diff options
Diffstat (limited to 'opendc-simulator/opendc-simulator-network')
9 files changed, 632 insertions, 0 deletions
diff --git a/opendc-simulator/opendc-simulator-network/build.gradle.kts b/opendc-simulator/opendc-simulator-network/build.gradle.kts new file mode 100644 index 00000000..f8931053 --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 = "Library for simulating datacenter network components" + +plugins { + `kotlin-library-conventions` + `testing-conventions` + `jacoco-conventions` +} + +dependencies { + api(platform(projects.opendcPlatform)) + api(projects.opendcSimulator.opendcSimulatorFlow) + implementation(projects.opendcSimulator.opendcSimulatorCore) + + testImplementation(libs.slf4j.simple) +} diff --git a/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkLink.kt b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkLink.kt new file mode 100644 index 00000000..67562640 --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkLink.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +/** + * A physical bi-directional communication link between two [SimNetworkPort]s. + * + * @param left The first port of the link. + * @param right The second port of the link. + */ +public class SimNetworkLink(public val left: SimNetworkPort, public val right: SimNetworkPort) { + /** + * Determine whether the specified [port] participates in this network link. + */ + public operator fun contains(port: SimNetworkPort): Boolean = port == left || port == right + + /** + * Obtain the opposite port to which the specified [port] is connected through this link. + */ + public fun opposite(port: SimNetworkPort): SimNetworkPort { + return when (port) { + left -> right + right -> left + else -> throw IllegalArgumentException("Invalid port given") + } + } + + override fun toString(): String = "SimNetworkLink[left=$left,right=$right]" +} diff --git a/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkPort.kt b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkPort.kt new file mode 100644 index 00000000..4b66d5cf --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkPort.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import org.opendc.simulator.flow.FlowConsumer +import org.opendc.simulator.flow.FlowSource + +/** + * A network port allows network devices to be connected to network through links. + */ +public abstract class SimNetworkPort { + /** + * A flag to indicate that the network port is connected to another port. + */ + public val isConnected: Boolean + get() = _link != null + + /** + * The network link which connects this port to another port. + */ + public val link: SimNetworkLink? + get() = _link + private var _link: SimNetworkLink? = null + + /** + * Connect this port to the specified [port]. + */ + public fun connect(port: SimNetworkPort) { + require(port !== this) { "Circular reference" } + check(!isConnected) { "Port already connected" } + check(!port.isConnected) { "Target port already connected" } + + val link = SimNetworkLink(this, port) + _link = link + port._link = link + + // Start bi-directional flow channel between the two ports + try { + provider.startConsumer(port.createConsumer()) + port.provider.startConsumer(createConsumer()) + } catch (e: Throwable) { + disconnect() + throw e + } + } + + /** + * Disconnect the current network link if it exists. + */ + public fun disconnect() { + val link = _link ?: return + val opposite = link.opposite(this) + _link = null + opposite._link = null + + provider.cancel() + opposite.provider.cancel() + } + + /** + * Create a [FlowSource] which generates the outgoing traffic of this port. + */ + protected abstract fun createConsumer(): FlowSource + + /** + * The [FlowConsumer] which processes the ingoing traffic of this port. + */ + protected abstract val provider: FlowConsumer + + override fun toString(): String = "SimNetworkPort[isConnected=$isConnected]" +} diff --git a/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSink.kt b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSink.kt new file mode 100644 index 00000000..4b0d7bbd --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSink.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import org.opendc.simulator.flow.* + +/** + * A network sink which discards all received traffic and does not generate any traffic itself. + */ +public class SimNetworkSink( + engine: FlowEngine, + public val capacity: Double +) : SimNetworkPort() { + override fun createConsumer(): FlowSource = object : FlowSource { + override fun onPull(conn: FlowConnection, now: Long, delta: Long): Long = Long.MAX_VALUE + + override fun toString(): String = "SimNetworkSink.Consumer" + } + + override val provider: FlowConsumer = FlowSink(engine, capacity) + + override fun toString(): String = "SimNetworkSink[capacity=$capacity]" +} diff --git a/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitch.kt b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitch.kt new file mode 100644 index 00000000..7dc249ab --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitch.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +/** + * A network device connects devices on a network by switching the traffic over its ports. + */ +public interface SimNetworkSwitch { + /** + * The ports of the switch. + */ + public val ports: List<SimNetworkPort> +} diff --git a/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtual.kt b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtual.kt new file mode 100644 index 00000000..6667c80c --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/main/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtual.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import org.opendc.simulator.flow.* +import org.opendc.simulator.flow.mux.MaxMinFlowMultiplexer + +/** + * A [SimNetworkSwitch] that can support new networking ports on demand. + */ +public class SimNetworkSwitchVirtual(private val engine: FlowEngine) : SimNetworkSwitch { + /** + * The ports of this switch. + */ + override val ports: List<Port> + get() = _ports + private val _ports = mutableListOf<Port>() + + /** + * The [MaxMinFlowMultiplexer] to actually perform the switching. + */ + private val mux = MaxMinFlowMultiplexer(engine) + + /** + * Open a new port on the switch. + */ + public fun newPort(): Port { + val port = Port() + _ports.add(port) + return port + } + + /** + * A port on the network switch. + */ + public inner class Port : SimNetworkPort(), AutoCloseable { + /** + * A flag to indicate that this virtual port was removed from the switch. + */ + private var isClosed: Boolean = false + + override val provider: FlowConsumer + get() = _provider + private val _provider = mux.newInput() + + private val _source = mux.newOutput() + + override fun createConsumer(): FlowSource = _source + + override fun close() { + isClosed = true + mux.removeInput(_provider) + _ports.remove(this) + } + } +} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt new file mode 100644 index 00000000..3480c9df --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import io.mockk.mockk +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * Test suite for [SimNetworkLink] class. + */ +class SimNetworkLinkTest { + @Test + fun testContainsLeft() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertTrue(left in link) + } + + @Test + fun testContainsRight() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertTrue(right in link) + } + + @Test + fun testContainsNone() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + val none = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertFalse(none in link) + } + + @Test + fun testOppositeLeft() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertEquals(right, link.opposite(left)) + } + + @Test + fun testOppositeRight() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertEquals(left, link.opposite(right)) + } + + @Test + fun testOppositeNone() { + val left = mockk<SimNetworkPort>() + val right = mockk<SimNetworkPort>() + val none = mockk<SimNetworkPort>() + + val link = SimNetworkLink(left, right) + assertThrows<IllegalArgumentException> { link.opposite(none) } + } +} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt new file mode 100644 index 00000000..14d22162 --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows +import org.opendc.simulator.core.runBlockingSimulation +import org.opendc.simulator.flow.* +import org.opendc.simulator.flow.source.FixedFlowSource + +/** + * Test suite for the [SimNetworkSink] class. + */ +class SimNetworkSinkTest { + @Test + fun testInitialState() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + + assertFalse(sink.isConnected) + assertNull(sink.link) + assertEquals(100.0, sink.capacity) + } + + @Test + fun testDisconnectIdempotent() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + + assertDoesNotThrow { sink.disconnect() } + assertFalse(sink.isConnected) + } + + @Test + fun testConnectCircular() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + + assertThrows<IllegalArgumentException> { + sink.connect(sink) + } + } + + @Test + fun testConnectAlreadyConnectedTarget() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val source = mockk<SimNetworkPort>(relaxUnitFun = true) + every { source.isConnected } returns true + + assertThrows<IllegalStateException> { + sink.connect(source) + } + } + + @Test + fun testConnectAlreadyConnected() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val source1 = Source(engine) + + val source2 = mockk<SimNetworkPort>(relaxUnitFun = true) + + every { source2.isConnected } returns false + + sink.connect(source1) + assertThrows<IllegalStateException> { + sink.connect(source2) + } + } + + @Test + fun testConnect() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val source = spyk(Source(engine)) + val consumer = source.consumer + + sink.connect(source) + + assertTrue(sink.isConnected) + assertTrue(source.isConnected) + + verify { source.createConsumer() } + verify { consumer.onStart(any(), any()) } + } + + @Test + fun testDisconnect() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val source = spyk(Source(engine)) + val consumer = source.consumer + + sink.connect(source) + sink.disconnect() + + assertFalse(sink.isConnected) + assertFalse(source.isConnected) + + verify { consumer.onStop(any(), any(), any()) } + } + + private class Source(engine: FlowEngine) : SimNetworkPort() { + val consumer = spyk(FixedFlowSource(Double.POSITIVE_INFINITY, utilization = 0.8)) + + public override fun createConsumer(): FlowSource = consumer + + override val provider: FlowConsumer = FlowSink(engine, 0.0) + } +} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt new file mode 100644 index 00000000..62e54ffb --- /dev/null +++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.network + +import io.mockk.spyk +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.opendc.simulator.core.runBlockingSimulation +import org.opendc.simulator.flow.* +import org.opendc.simulator.flow.source.FixedFlowSource + +/** + * Test suite for the [SimNetworkSwitchVirtual] class. + */ +class SimNetworkSwitchVirtualTest { + @Test + fun testConnect() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val source = spyk(Source(engine)) + val switch = SimNetworkSwitchVirtual(engine) + val consumer = source.consumer + + switch.newPort().connect(sink) + switch.newPort().connect(source) + + assertTrue(sink.isConnected) + assertTrue(source.isConnected) + + verify { source.createConsumer() } + verify { consumer.onStart(any(), any()) } + } + + @Test + fun testConnectClosedPort() = runBlockingSimulation { + val engine = FlowEngine(coroutineContext, clock) + val sink = SimNetworkSink(engine, capacity = 100.0) + val switch = SimNetworkSwitchVirtual(engine) + + val port = switch.newPort() + port.close() + + assertThrows<IllegalStateException> { + port.connect(sink) + } + } + + private class Source(engine: FlowEngine) : SimNetworkPort() { + val consumer = spyk(FixedFlowSource(Double.POSITIVE_INFINITY, utilization = 0.8)) + + public override fun createConsumer(): FlowSource = consumer + + override val provider: FlowConsumer = FlowSink(engine, 0.0) + } +} |
