From 164fa3764ef7b75f04a07aa1dbf8b36dd98d7fb9 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 25 Apr 2019 14:19:58 +0200 Subject: test: Move implementation test suite into separate package This change moves the implementation conformance test suite from the test sources of `odcsim-core` to a new `odcsim-engine-tests` module in order to simplify Gradle configuration. --- odcsim-core/build.gradle | 15 - .../com/atlarge/odcsim/ActorSystemFactoryTest.kt | 62 ---- .../kotlin/com/atlarge/odcsim/ActorSystemTest.kt | 349 -------------------- odcsim-engine-omega/build.gradle | 2 +- .../engine/omega/OmegaActorSystemFactoryTest.kt | 6 +- .../odcsim/engine/omega/OmegaActorSystemTest.kt | 7 +- odcsim-engine-tests/build.gradle | 39 +++ .../odcsim/engine/tests/ActorSystemContract.kt | 362 +++++++++++++++++++++ .../engine/tests/ActorSystemFactoryContract.kt | 65 ++++ settings.gradle | 1 + 10 files changed, 475 insertions(+), 433 deletions(-) delete mode 100644 odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt delete mode 100644 odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt create mode 100644 odcsim-engine-tests/build.gradle create mode 100644 odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt create mode 100644 odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt diff --git a/odcsim-core/build.gradle b/odcsim-core/build.gradle index 83dfabec..a0cd8450 100644 --- a/odcsim-core/build.gradle +++ b/odcsim-core/build.gradle @@ -39,18 +39,3 @@ dependencies { testImplementation "org.junit.platform:junit-platform-launcher:$junit_platform_version" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0" } - -/* Create configuration for test suite used by implementors */ -task testJar(type: Jar, dependsOn: testClasses) { - from sourceSets.test.output -} - -configurations { - tests -} - -artifacts { - tests testJar -} - - diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt deleted file mode 100644 index 11652606..00000000 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemFactoryTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.Test -import org.junit.jupiter.api.assertThrows - -/** - * A conformance test suite for implementors of the [ActorSystemFactory] interface. - */ -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(empty(), 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(setup { throw UnsupportedOperationException() }, "test") - - assertThrows { 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 deleted file mode 100644 index e9cc3886..00000000 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorSystemTest.kt +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -const val DELTA: Double = 0.0001 - -/** - * A conformance test suite for implementors of the [ActorSystem] interface. - */ -abstract class ActorSystemTest { - /** - * An [ActorSystemFactory] provided by implementors to create the [ActorSystem] to be tested. - */ - abstract val factory: ActorSystemFactory - - /** - * Test whether the created [ActorSystem] has the correct name. - */ - @Test - fun `should have a name`() { - val name = "test" - val system = factory(empty(), name) - - assertEquals(name, system.name) - } - - /** - * Test whether the created [ActorSystem] has a path. - */ - @Test - fun `should have a path`() { - val system = factory(empty(), "test") - - assertTrue(system.path is ActorPath.Root) - } - - /** - * Test whether creating an [ActorSystem] sets the initial time at 0. - */ - @Test - fun `should start at t=0`() { - val system = factory(empty(), name = "test") - - assertTrue(Math.abs(system.time) < DELTA) - } - - /** - * Test whether an [ActorSystem] does not accept invalid points in time. - */ - @Test - fun `should not accept negative instants for running`() { - val system = factory(empty(), name = "test") - assertThrows { system.run(-10.0) } - } - - /** - * Test whether an [ActorSystem] will not jump backward in time when asking to run until a specified instant - * that has already occurred. - */ - @Test - fun `should not jump backward in time`() { - val until = 10.0 - val system = factory(empty(), name = "test") - - system.run(until = until) - system.run(until = until - 0.5) - 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 jump forward in time`() { - val until = 10.0 - val system = factory(empty(), name = "test") - - 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 = receiveMessage { msg -> - assertEquals(1, msg) - receiveMessage { - assertEquals(2, it) - ignore() - } - } - val system = factory(behavior, name = "test") - system.send(1, after = 1.0) - system.send(2, after = 1.0) - system.run(until = 10.0) - } - - /** - * Test whether an [ActorSystem] will not process messages in the queue after the deadline. - */ - @Test - fun `should not process messages after deadline`() { - var counter = 0 - val behavior = receiveMessage { _ -> - counter++ - same() - } - val system = factory(behavior, name = "test") - system.send(Unit, after = 3.0) - system.send(Unit, after = 1.0) - system.run(until = 2.0) - assertEquals(1, counter) - } - - /** - * Test whether an [ActorSystem] will not initialize the root actor if the system has not been run yet. - */ - @Test - fun `should not initialize root actor if not run`() { - factory(setup { TODO() }, name = "test") - } - - @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(empty(), name = "test") - assertThrows { 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 = setup { ctx -> - assertTrue(Math.abs(ctx.time) < DELTA) - ignore() - } - - 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`() { - var spawned = false - val behavior = setup { spawned = true; empty() } - - val system = factory(setup { ctx -> - val ref = ctx.spawn(behavior, "child") - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - assertTrue(spawned) - } - - /** - * Test whether a child actor can be stopped from an actor. - */ - @Test - fun `should allow stopping of child actors`() { - val system = factory(setup { ctx -> - val ref = ctx.spawn(receiveMessage { throw UnsupportedOperationException() }, "child") - assertTrue(ctx.stop(ref)) - assertEquals("child", ref.path.name) - - ignore() - }, 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(setup { ctx1 -> - val child1 = ctx1.spawn(ignore(), "child-1") - ctx1.spawn(setup { ctx2 -> - assertFalse(ctx2.stop(child1)) - ignore() - }, "child-2") - - ignore() - }, 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(setup { ctx -> - val child = ctx.spawn(ignore(), "child") - ctx.stop(child) - assertFalse(ctx.stop(child)) - ignore() - }, 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(setup> { ctx -> - val root = ctx.self - val child = ctx.spawn(setup { - val child = ctx.spawn(receiveMessage { - throw IllegalStateException("DELIBERATE") - }, "child") - ctx.send(root, child) - ignore() - }, "child") - - receiveMessage { msg -> - assertTrue(ctx.stop(child)) - ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore - stopped() - } - }, name = "test") - - system.run() - } - - /** - * Test whether [same] works correctly. - */ - @Test - fun `should keep same behavior on same`() { - var counter = 0 - - val behavior = setup { ctx -> - counter++ - ctx.send(ctx.self, Unit) - receiveMessage { - counter++ - same() - } - } - - val system = factory(behavior, "test") - system.run() - assertEquals(2, counter) - } - - /** - * Test whether the reference to the actor itself is valid. - */ - @Test - fun `should have reference to itself`() { - var flag = false - val behavior: Behavior = setup { ctx -> - ctx.send(ctx.self, Unit) - receiveMessage { flag = true; same() } - } - - val system = factory(behavior, "test") - system.run() - assertTrue(flag) - } - - /** - * Test whether we cannot start an actor with the [same] behavior. - */ - @Test - fun `should not start with same behavior`() { - val system = factory(same(), "test") - assertThrows { system.run() } - } - - /** - * Test whether we can start an actor with the [stopped] behavior. - */ - @Test - fun `should start with stopped behavior`() { - val system = factory(stopped(), "test") - system.run() - } - - - /** - * Test whether an actor that is crashed cannot receive more messages. - */ - @Test - fun `should stop if it crashes`() { - var counter = 0 - val system = factory(receiveMessage { - counter++ - throw IllegalArgumentException("STAGED") - }, "test") - - system.send(Unit) - system.send(Unit) - - system.run() - assertEquals(1, counter) - } - } -} diff --git a/odcsim-engine-omega/build.gradle b/odcsim-engine-omega/build.gradle index f60dbe89..cefd3e20 100644 --- a/odcsim-engine-omega/build.gradle +++ b/odcsim-engine-omega/build.gradle @@ -37,7 +37,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib" implementation "io.github.microutils:kotlin-logging:1.6.20" - testCompile project(path: ':odcsim-core', configuration: 'tests') + testApi project(':odcsim-engine-tests') testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_jupiter_version" testImplementation "org.junit.platform:junit-platform-launcher:$junit_platform_version" diff --git a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt index 01574b7f..4e195e6e 100644 --- a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt +++ b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt @@ -25,13 +25,13 @@ package com.atlarge.odcsim.engine.omega import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.ActorSystemFactoryTest +import com.atlarge.odcsim.engine.tests.ActorSystemFactoryContract import org.junit.jupiter.api.DisplayName /** - * The [ActorSystemFactoryTest] suite for the Omega engine implementation. + * The [ActorSystemFactory] test suite for the Omega engine implementation. */ @DisplayName("OmegaActorSystemFactory") -class OmegaActorSystemFactoryTest : ActorSystemFactoryTest() { +class OmegaActorSystemFactoryTest : ActorSystemFactoryContract() { override fun createFactory(): ActorSystemFactory = OmegaActorSystemFactory() } diff --git a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt index ef7a5258..dc310d47 100644 --- a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt +++ b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt @@ -24,13 +24,14 @@ package com.atlarge.odcsim.engine.omega -import com.atlarge.odcsim.ActorSystemTest +import com.atlarge.odcsim.ActorSystem +import com.atlarge.odcsim.engine.tests.ActorSystemContract import org.junit.jupiter.api.DisplayName /** - * The [ActorSystemTest] suite for the Omega engine implementation. + * The [ActorSystem] test suite for the Omega engine implementation. */ @DisplayName("OmegaActorSystem") -class OmegaActorSystemTest : ActorSystemTest() { +class OmegaActorSystemTest : ActorSystemContract() { override val factory = OmegaActorSystemFactory() } diff --git a/odcsim-engine-tests/build.gradle b/odcsim-engine-tests/build.gradle new file mode 100644 index 00000000..da568319 --- /dev/null +++ b/odcsim-engine-tests/build.gradle @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2019 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. + */ + +/* Build configuration */ +apply from: '../gradle/kotlin.gradle' +apply plugin: 'java-library' + +/* Project configuration */ +repositories { + jcenter() +} + +dependencies { + api project(':odcsim-core') + + implementation "org.jetbrains.kotlin:kotlin-stdlib" + implementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" +} diff --git a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt new file mode 100644 index 00000000..eb6d38f6 --- /dev/null +++ b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt @@ -0,0 +1,362 @@ +/* + * MIT License + * + * Copyright (c) 2019 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.engine.tests + +import com.atlarge.odcsim.ActorPath +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.ignore +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.stopped +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.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * A conformance test suite for implementors of the [ActorSystem] interface. + */ +abstract class ActorSystemContract { + /** + * An [ActorSystemFactory] provided by implementors to create the [ActorSystem] to be tested. + */ + abstract val factory: ActorSystemFactory + + /** + * Test whether the created [ActorSystem] has the correct name. + */ + @Test + fun `should have a name`() { + val name = "test" + val system = factory(empty(), name) + + assertEquals(name, system.name) + } + + /** + * Test whether the created [ActorSystem] has a path. + */ + @Test + fun `should have a path`() { + val system = factory(empty(), "test") + + assertTrue(system.path is ActorPath.Root) + } + + /** + * Test whether creating an [ActorSystem] sets the initial time at 0. + */ + @Test + fun `should start at t=0`() { + val system = factory(empty(), name = "test") + + assertEquals(.0, system.time, DELTA) + } + + /** + * Test whether an [ActorSystem] does not accept invalid points in time. + */ + @Test + fun `should not accept negative instants for running`() { + val system = factory(empty(), name = "test") + assertThrows { system.run(-10.0) } + } + + /** + * Test whether an [ActorSystem] will not jump backward in time when asking to run until a specified instant + * that has already occurred. + */ + @Test + fun `should not jump backward in time`() { + val until = 10.0 + val system = factory(empty(), name = "test") + + system.run(until = until) + system.run(until = until - 0.5) + assertEquals(until, system.time, DELTA) + } + + /** + * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. + */ + @Test + fun `should jump forward in time`() { + val until = 10.0 + val system = factory(empty(), name = "test") + + system.run(until = until) + assertEquals(until, system.time, 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 = receiveMessage { msg -> + assertEquals(1, msg) + receiveMessage { + assertEquals(2, it) + ignore() + } + } + val system = factory(behavior, name = "test") + system.send(1, after = 1.0) + system.send(2, after = 1.0) + system.run(until = 10.0) + } + + /** + * Test whether an [ActorSystem] will not process messages in the queue after the deadline. + */ + @Test + fun `should not process messages after deadline`() { + var counter = 0 + val behavior = receiveMessage { _ -> + counter++ + same() + } + val system = factory(behavior, name = "test") + system.send(Unit, after = 3.0) + system.send(Unit, after = 1.0) + system.run(until = 2.0) + assertEquals(1, counter) + } + + /** + * Test whether an [ActorSystem] will not initialize the root actor if the system has not been run yet. + */ + @Test + fun `should not initialize root actor if not run`() { + factory(setup { TODO() }, name = "test") + } + + + @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(empty(), name = "test") + assertThrows { 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 = setup { ctx -> + assertEquals(.0, ctx.time, DELTA) + ignore() + } + + 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`() { + var spawned = false + val behavior = setup { spawned = true; empty() } + + val system = factory(setup { ctx -> + val ref = ctx.spawn(behavior, "child") + assertEquals("child", ref.path.name) + ignore() + }, name = "test") + + system.run(until = 10.0) + assertTrue(spawned) + } + + /** + * Test whether a child actor can be stopped from an actor. + */ + @Test + fun `should allow stopping of child actors`() { + val system = factory(setup { ctx -> + val ref = ctx.spawn(receiveMessage { throw UnsupportedOperationException() }, "child") + assertTrue(ctx.stop(ref)) + assertEquals("child", ref.path.name) + + ignore() + }, 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(setup { ctx1 -> + val child1 = ctx1.spawn(ignore(), "child-1") + ctx1.spawn(setup { ctx2 -> + assertFalse(ctx2.stop(child1)) + ignore() + }, "child-2") + + ignore() + }, 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(setup { ctx -> + val child = ctx.spawn(ignore(), "child") + ctx.stop(child) + assertFalse(ctx.stop(child)) + ignore() + }, 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(setup> { ctx -> + val root = ctx.self + val child = ctx.spawn(setup { + val child = ctx.spawn(receiveMessage { + throw IllegalStateException("DELIBERATE") + }, "child") + ctx.send(root, child) + ignore() + }, "child") + + receiveMessage { msg -> + assertTrue(ctx.stop(child)) + ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore + stopped() + } + }, name = "test") + + system.run() + } + + /** + * Test whether [same] works correctly. + */ + @Test + fun `should keep same behavior on same`() { + var counter = 0 + + val behavior = setup { ctx -> + counter++ + ctx.send(ctx.self, Unit) + receiveMessage { + counter++ + same() + } + } + + val system = factory(behavior, "test") + system.run() + assertEquals(2, counter) + } + + /** + * Test whether the reference to the actor itself is valid. + */ + @Test + fun `should have reference to itself`() { + var flag = false + val behavior: Behavior = setup { ctx -> + ctx.send(ctx.self, Unit) + receiveMessage { flag = true; same() } + } + + val system = factory(behavior, "test") + system.run() + assertTrue(flag) + } + + /** + * Test whether we cannot start an actor with the [Behavior.Companion.same] behavior. + */ + @Test + fun `should not start with same behavior`() { + val system = factory(same(), "test") + assertThrows { system.run() } + } + + /** + * Test whether we can start an actor with the [Behavior.Companion.stopped] behavior. + */ + @Test + fun `should start with stopped behavior`() { + val system = factory(stopped(), "test") + system.run() + } + + + /** + * Test whether an actor that is crashed cannot receive more messages. + */ + @Test + fun `should stop if it crashes`() { + var counter = 0 + val system = factory(receiveMessage { + counter++ + throw IllegalArgumentException("STAGED") + }, "test") + + system.send(Unit) + system.send(Unit) + + system.run() + assertEquals(1, counter) + } + } + + companion object { + private const val DELTA: Double = 0.0001 + } +} diff --git a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt new file mode 100644 index 00000000..55d70a84 --- /dev/null +++ b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2019 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.engine.tests + +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.setup +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. + */ +abstract class ActorSystemFactoryContract { + /** + * 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(empty(), 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(setup { throw UnsupportedOperationException() }, "test") + + assertThrows { system.run(until = 10.0) } + } +} diff --git a/settings.gradle b/settings.gradle index a7a78f90..9ae9a5ab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,4 +24,5 @@ rootProject.name = 'opendc-simulator' include 'odcsim-core' +include 'odcsim-engine-tests' include 'odcsim-engine-omega' -- cgit v1.2.3