diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2020-01-19 21:02:22 +0100 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2020-01-19 21:13:37 +0100 |
| commit | 4d13f702c87bc195d8edbd19c5cd6567ecfd2af4 (patch) | |
| tree | 3262880c18204adce76e3d4a8091d1b24b2b4c9a /odcsim/odcsim-engine-tests | |
| parent | ce952cf3f27c154e06cfa56ca1ad7db9ba3eac7c (diff) | |
refactor: Introduce revised API design for 2.x
This change introduces the revised API design for version 2.0 of the
OpenDC simulator. This version drops built-in support for Java and
instead opts to build on Kotlin coroutines to simplify the API surface.
During development of and experimentation with the previous API for version
2.x, we found that the design based on Akka Typed was too limiting and caused
too much boilerplate for the models we needed to implement. Essential
patterns such as request-response were found to be hard to implement
with only a single mailbox. Moveover, limiting each actor's mailbox to a
single type hindered composition and often resulted in unchecked casts
or the type being changed to `Any`, eliminating the type-safety of the
API.
In this revised API design, a simulation is now represented as the interplay of
logical processes that communicate via multiple message passing channels.
We use Kotlin coroutines to describe the behavior of the processes.
The API has been design from the start to take into account
distributed/parallel simulations by disallowing messages from arbitrary
processes, which was possible in the previous design. Instead, the
'communication graph' is known during runtime as procsses must register
themselves before being able to send/receive messages to/from channels.
We are still figuring out process/channel identity and supervision. Currently,
all logical processes run on a single level, instead of being
hierachical. However, this might change in the future.
Diffstat (limited to 'odcsim/odcsim-engine-tests')
3 files changed, 0 insertions, 506 deletions
diff --git a/odcsim/odcsim-engine-tests/build.gradle.kts b/odcsim/odcsim-engine-tests/build.gradle.kts deleted file mode 100644 index e68070cc..00000000 --- a/odcsim/odcsim-engine-tests/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -description = "Conformance suite for implementors of the odcsim API" - -/* Build configuration */ -plugins { - `kotlin-library-convention` -} - -/* Project configuration */ -repositories { - jcenter() -} - -dependencies { - api(project(":odcsim:odcsim-api")) - - implementation(kotlin("stdlib")) - implementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") -} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt deleted file mode 100644 index 5e735e68..00000000 --- a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt +++ /dev/null @@ -1,391 +0,0 @@ -/* - * 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.ActorRef -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.coroutines.dsl.timeout -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.ignore -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.receiveSignal -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.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<Unit>(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether creating an [ActorSystem] sets the initial time at 0. - */ - @Test - fun `should start at t=0`() { - val system = factory(empty<Unit>(), name = "test") - - assertEquals(.0, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] does not accept invalid points in time. - */ - @Test - fun `should not accept negative instants for running`() { - val system = factory(empty<Unit>(), name = "test") - assertThrows<IllegalArgumentException> { system.run(-10.0) } - system.terminate() - } - - /** - * 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<Unit>(), name = "test") - - system.run(until = until) - system.run(until = until - 0.5) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * 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<Unit>(), name = "test") - - system.run(until = until) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * 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<Int> { 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) - system.terminate() - } - - /** - * 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<Unit> { _ -> - 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) - system.terminate() - } - - /** - * 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`() { - val system = factory(setup<Unit> { TODO() }, name = "test") - system.terminate() - } - - @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<Unit>(), name = "test") - assertThrows<IllegalArgumentException> { system.send(Unit, after = -1.0) } - system.terminate() - } - } - - @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<Unit> { ctx -> - assertEquals(.0, ctx.time, DELTA) - ignore() - } - - val system = factory(behavior, "test") - system.run() - system.terminate() - } - - /** - * 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<Unit> { spawned = true; empty() } - - val system = factory(setup<Unit> { ctx -> - val ref = ctx.spawn(behavior, "child") - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - assertTrue(spawned) - system.terminate() - } - - /** - * Test whether a child actor can be stopped from an actor. - */ - @Test - fun `should allow stopping of child actors`() { - val system = factory(setup<Unit> { ctx -> - val ref = ctx.spawn(receiveMessage<Unit> { throw UnsupportedOperationException() }, "child") - ctx.stop(ref) - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - system.terminate() - } - - /** - * 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<Unit> { ctx1 -> - val child1 = ctx1.spawn(ignore<Unit>(), "child-1") - ctx1.spawn(setup<Unit> { ctx2 -> - ctx2.stop(child1) - ignore() - }, "child-2") - - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * Test whether stopping a child is idempotent. - */ - @Test - fun `should be able to stop a child twice`() { - val system = factory(setup<Unit> { ctx -> - val child = ctx.spawn(ignore<Unit>(), "child") - ctx.stop(child) - ctx.stop(child) - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * 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<ActorRef<Unit>> { ctx -> - val root = ctx.self - val child = ctx.spawn(setup<Unit> { - val child = it.spawn(receiveMessage<Unit> { - throw IllegalStateException("DELIBERATE") - }, "child") - ctx.send(root, child) - ignore() - }, "child") - - receiveMessage { msg -> - ctx.stop(child) - ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore - stopped() - } - }, name = "test") - - system.run() - system.terminate() - } - - /** - * Test whether [same] works correctly. - */ - @Test - fun `should keep same behavior on same`() { - var counter = 0 - - val behavior = setup<Unit> { ctx -> - counter++ - ctx.send(ctx.self, Unit) - receiveMessage { - counter++ - same() - } - } - - val system = factory(behavior, "test") - system.run() - assertEquals(2, counter) - system.terminate() - } - - /** - * Test whether the reference to the actor itself is valid. - */ - @Test - fun `should have reference to itself`() { - var flag = false - val behavior: Behavior<Unit> = setup { ctx -> - ctx.send(ctx.self, Unit) - receiveMessage { flag = true; same() } - } - - val system = factory(behavior, "test") - system.run() - assertTrue(flag) - system.terminate() - } - - /** - * Test whether we can start an actor with the [stopped] behavior. - */ - @Test - fun `should start with stopped behavior`() { - val system = factory(stopped<Unit>(), "test") - system.run() - system.terminate() - } - - /** - * 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<Unit> { - counter++ - throw IllegalArgumentException("STAGED") - }, "test") - - system.send(Unit) - system.send(Unit) - - system.run() - assertEquals(1, counter) - system.terminate() - } - - /** - * Test whether an actor can watch for termination. - */ - @Test - fun `should watch for termination`() { - var received = false - val system = factory(setup<Nothing> { ctx -> - val child = ctx.spawn(suspending<Nothing> { - timeout(50.0) - stopped() - }, "child") - ctx.watch(child) - - receiveSignal { _, signal -> - when (signal) { - is Terminated -> { - received = true - stopped() - } - else -> - same() - } - } - }, "test") - - system.run() - system.terminate() - assertTrue(received) - } - } - - companion object { - private const val DELTA: Double = 0.0001 - } -} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt deleted file mode 100644 index 565f4f4c..00000000 --- a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 com.atlarge.odcsim.stopped -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test - -/** - * 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<Unit>(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether the factory will create an [ActorSystem] with valid root behavior. - */ - @Test - fun `should create a system with correct root behavior`() { - var flag = false - val factory = createFactory() - val system = factory(setup<Unit> { - flag = true - stopped() - }, "test") - - system.run(until = 10.0) - system.terminate() - assertTrue(flag) - } -} |
