diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2018-10-28 16:08:22 +0100 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2019-05-06 18:19:43 +0200 |
| commit | 783a021a1cb6641d0494067b44fdc02f41630493 (patch) | |
| tree | 692e2d0227e910b3139c242a4bb559ef0cfdbec1 /odcsim-core/src/test | |
| parent | decb8fb5297c7772f5319a47c784d44bf8bdbe9c (diff) | |
test: Create initial test suite for API
This change creates the initial conformance test suite for the API
design and tries to specify most of the requirements and assumptions
made for implementors.
Diffstat (limited to 'odcsim-core/src/test')
3 files changed, 325 insertions, 3 deletions
diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt new file mode 100644 index 00000000..023d3efd --- /dev/null +++ b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * A test suite for the [ActorPath] class. + */ +@DisplayName("ActorPath") +class ActorPathTest { + /** + * Test whether an [ActorPath.Root] may only start with a slash. + */ + @Test + fun `root node may only start with a slash`() { + ActorPath.Root() // Assert slash at start + assertThrows<IllegalArgumentException> { ActorPath.Root("abc/") } + } + + /** + * Test whether an [ActorPath.Child] disallows names with a slash. + */ + @Test + fun `child node should not allow name with a slash`() { + assertThrows<IllegalArgumentException> { ActorPath.Child(ActorPath.Root(), "/") } + } + + /** + * Test whether a root node can have a custom name. + */ + @Test + fun `root node can have a custom name`() { + val name = "user" + assertEquals(name, ActorPath.Root(name).name) + } + + /** + * Test whether a child node can be created on a root. + */ + @Test + fun `child node can be created on a root`() { + val root = ActorPath.Root(name = "/user") + val child = root.child("child") + + assertEquals(root, child.parent) + assertEquals("child", child.name) + } + + /** + * Test whether a child node can be created on a child. + */ + @Test + fun `child node can be created on a child`() { + val root = ActorPath.Root(name = "/user").child("child") + val child = root.child("child") + + assertEquals(root, child.parent) + assertEquals("child", child.name) + } +} diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt index a374548c..266f9fba 100644 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt +++ b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt @@ -24,6 +24,10 @@ package com.atlarge.odcsim +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + /** * A conformance test suite for implementors of the [ActorSystemFactory] interface. */ @@ -32,4 +36,31 @@ abstract class ActorSystemFactoryTest { * Create an [ActorSystemFactory] instance to test. */ abstract fun createFactory(): ActorSystemFactory + + /** + * Test whether the factory will create an [ActorSystem] with correct name. + */ + @Test + fun `should create a system with correct name`() { + val factory = createFactory() + val name = "test" + val system = factory(object : Behavior<Unit> {}, name) + + assertEquals(name, system.name) + } + + /** + * Test whether the factory will create an [ActorSystem] with valid root behavior. + */ + @Test + fun `should create a system with correct root behavior`() { + val factory = createFactory() + val system = factory(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + throw UnsupportedOperationException() + } + }, "test") + + assertThrows<UnsupportedOperationException> { system.run(until = 10.0) } + } } diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt index 62df356b..71bc645a 100644 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt +++ b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt @@ -25,11 +25,12 @@ package com.atlarge.odcsim import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Assumptions.assumeTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import java.lang.IllegalArgumentException const val DELTA: Double = 0.0001 @@ -54,6 +55,16 @@ abstract class ActorSystemTest { } /** + * Test whether the created [ActorSystem] has a path. + */ + @Test + fun `should have a path`() { + val system = factory(object : Behavior<Unit> {}, "test") + + assertTrue(system.path is ActorPath.Root) + } + + /** * Test whether creating an [ActorSystem] sets the initial time at 0. */ @Test @@ -94,8 +105,202 @@ abstract class ActorSystemTest { val until = 10.0 val system = factory(object : Behavior<Unit> {}, name = "test") - assumeTrue(Math.abs(system.time) < DELTA) system.run(until = until) assertTrue(Math.abs(system.time - until) < DELTA) } + + /** + * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. + */ + @Test + fun `should order messages at the instant by insertion time`() { + val behavior = object : Behavior<Int> { + override fun receive(ctx: ActorContext<Int>, msg: Int): Behavior<Int> { + assertEquals(1, msg) + return object : Behavior<Int> { + override fun receive(ctx: ActorContext<Int>, msg: Int): Behavior<Int> { + assertEquals(2, msg) + return this + } + } + } + } + val system = factory(behavior, name = "test") + system.send(1, after = 1.0) + system.send(2, after = 1.0) + system.run(until = 10.0) + } + + @Nested + @DisplayName("ActorRef") + inner class ActorRefTest { + /** + * Test whether an [ActorSystem] disallows sending messages in the past. + */ + @Test + fun `should disallow messages in the past`() { + val system = factory(object : Behavior<Unit> {}, name = "test") + assertThrows<IllegalArgumentException> { system.send(Unit, after = -1.0) } + } + } + + + @Nested + @DisplayName("Actor") + inner class Actor { + /** + * Test whether the pre-start time of the root actor is at 0. + */ + @Test + fun `should pre-start at t=0 if root`() { + val behavior = object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + assertTrue(Math.abs(ctx.time) < DELTA) + return this + } + } + + val system = factory(behavior, "test") + system.run() + } + + /** + * Test whether a child actor can be created from an actor. + */ + @Test + fun `should allow spawning of child actors`() { + val behavior = object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + throw UnsupportedOperationException("b") + } + } + + val system = factory(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + if (signal is PreStart) { + val ref = ctx.spawn(behavior, "child") + assertEquals("child", ref.path.name) + } + + return this + } + }, name = "test") + + assertThrows<UnsupportedOperationException> { system.run(until = 10.0) } + } + + /** + * Test whether a child actor can be stopped from an actor. + */ + @Test + fun `should allow stopping of child actors`() { + val system = factory(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + if (signal is PreStart) { + val ref = ctx.spawn(object : Behavior<Unit> { + override fun receive(ctx: ActorContext<Unit>, msg: Unit): Behavior<Unit> { + throw UnsupportedOperationException() + } + }, "child") + assertTrue(ctx.stop(ref)) + assertEquals("child", ref.path.name) + } + + return this + } + }, name = "test") + + system.run(until = 10.0) + } + + /** + * Test whether only the parent of a child can terminate it. + */ + @Test + fun `should only be able to terminate child actors`() { + val system = factory(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + val child1 = ctx.spawn(object : Behavior<Unit> {}, "child-1") + ctx.spawn(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + assertFalse(ctx.stop(child1)) + return this + } + }, "child-2") + return this + } + }, name = "test") + system.run() + } + + /** + * Test whether terminating an already terminated child fails. + */ + @Test + fun `should not be able to stop an already terminated child`() { + val system = factory(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + val child = ctx.spawn(object : Behavior<Unit> {}, "child") + ctx.stop(child) + assertFalse(ctx.stop(child)) + return this + } + }, name = "test") + system.run() + } + + /** + * Test whether termination of a child also results in termination of its children. + */ + @Test + fun `should terminate children of child when terminating it`() { + val system = factory(object : Behavior<ActorRef<Unit>> { + lateinit var child: ActorRef<Unit> + + override fun receive(ctx: ActorContext<ActorRef<Unit>>, msg: ActorRef<Unit>): Behavior<ActorRef<Unit>> { + assertTrue(ctx.stop(child)) + msg.send(Unit) // This actor should be stopped now and not receive the message anymore + return this + } + + override fun receiveSignal(ctx: ActorContext<ActorRef<Unit>>, signal: Signal): Behavior<ActorRef<Unit>> { + val root = ctx.self + child = ctx.spawn(object : Behavior<Unit> { + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + if (signal is PreStart) { + val child = ctx.spawn(object : Behavior<Unit> { + override fun receive(ctx: ActorContext<Unit>, msg: Unit): Behavior<Unit> { + throw IllegalStateException() + } + }, "child") + root.send(child) + } + return this + } + }, "child") + return this + } + }, name = "test") + system.run() + } + + /** + * Test whether the reference to the actor itself is valid. + */ + @Test + fun `should have reference to itself`() { + val behavior = object : Behavior<Unit> { + override fun receive(ctx: ActorContext<Unit>, msg: Unit): Behavior<Unit> { + throw UnsupportedOperationException() + } + override fun receiveSignal(ctx: ActorContext<Unit>, signal: Signal): Behavior<Unit> { + ctx.self.send(Unit) + return this + } + } + + val system = factory(behavior, "test") + assertThrows<UnsupportedOperationException> { system.run() } + } + } } |
