From 9dab4d7b3921cd48199d773c7dc4bae0f2273223 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 18 Mar 2021 16:25:00 +0100 Subject: simulator: Re-design consumer interface to support capacity negotiation This change re-designs the SimResourceConsumer interface to support in the future capacity negotiation. This basically means that the consumer will be informed directly when not enough capacity is available, instead of after the deadline specified by the consumer. --- .../simulator/resources/SimResourceContextTest.kt | 115 ++++++--------- .../resources/SimResourceForwarderTest.kt | 112 +++++++++++++-- .../simulator/resources/SimResourceSourceTest.kt | 159 +++++++++------------ .../resources/SimResourceSwitchExclusiveTest.kt | 52 ++++--- .../resources/SimResourceSwitchMaxMinTest.kt | 19 +-- .../simulator/resources/SimWorkConsumerTest.kt | 74 ++++++++++ 6 files changed, 320 insertions(+), 211 deletions(-) create mode 100644 simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt (limited to 'simulator/opendc-simulator/opendc-simulator-resources/src/test') diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt index e7642dc1..0bc87473 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt @@ -22,11 +22,10 @@ package org.opendc.simulator.resources +import io.mockk.* import kotlinx.coroutines.* import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue import org.opendc.simulator.utils.DelayControllerClockAdapter /** @@ -45,28 +44,15 @@ class SimResourceContextTest { val resource = SimCpu(4200.0) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(10.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit val context = object : SimAbstractResourceContext(resource, clock, consumer) { - override fun onIdle(deadline: Long) { - } + override fun onIdle(deadline: Long) {} - override fun onConsume(work: Double, limit: Double, deadline: Long) { - } + override fun onConsume(work: Double, limit: Double, deadline: Long) {} - override fun onFinish() { - } - - override fun onFailure(cause: Throwable) { - } + override fun onFinish(cause: Throwable?) {} } context.flush() @@ -77,36 +63,20 @@ class SimResourceContextTest { val clock = DelayControllerClockAdapter(this) val resource = SimCpu(4200.0) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(10.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } - - var counter = 0 - val context = object : SimAbstractResourceContext(resource, clock, consumer) { - override fun onIdle(deadline: Long) { - } - - override fun onConsume(work: Double, limit: Double, deadline: Long) { - counter++ - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit - override fun onFinish() { - } - - override fun onFailure(cause: Throwable) { - } - } + val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { + override fun onIdle(deadline: Long) {} + override fun onFinish(cause: Throwable?) {} + override fun onConsume(work: Double, limit: Double, deadline: Long) {} + }) context.start() delay(1) // Delay 1 ms to prevent hitting the fast path context.flush(isIntermediate = true) - assertEquals(2, counter) + + verify(exactly = 2) { context.onConsume(any(), any(), any()) } } @Test @@ -114,33 +84,14 @@ class SimResourceContextTest { val clock = DelayControllerClockAdapter(this) val resource = SimCpu(4200.0) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Idle(10) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } - - var counter = 0 - var isFinished = false - val context = object : SimAbstractResourceContext(resource, clock, consumer) { - override fun onIdle(deadline: Long) { - counter++ - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit - override fun onConsume(work: Double, limit: Double, deadline: Long) { - } - - override fun onFinish() { - isFinished = true - } - - override fun onFailure(cause: Throwable) { - } - } + val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { + override fun onIdle(deadline: Long) {} + override fun onFinish(cause: Throwable?) {} + override fun onConsume(work: Double, limit: Double, deadline: Long) {} + }) context.start() delay(5) @@ -149,8 +100,26 @@ class SimResourceContextTest { context.flush(isIntermediate = true) assertAll( - { assertEquals(1, counter) }, - { assertTrue(isFinished) } + { verify(exactly = 1) { context.onIdle(any()) } }, + { verify(exactly = 1) { context.onFinish(null) } } ) } + + @Test + fun testDoubleStart() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val resource = SimCpu(4200.0) + + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit + + val context = object : SimAbstractResourceContext(resource, clock, consumer) { + override fun onIdle(deadline: Long) {} + override fun onFinish(cause: Throwable?) {} + override fun onConsume(work: Double, limit: Double, deadline: Long) {} + } + + context.start() + assertThrows { context.start() } + } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt index ced1bd98..b1b959ba 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt @@ -22,10 +22,16 @@ package org.opendc.simulator.resources +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.yield +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.utils.TimerScheduler @@ -53,14 +59,15 @@ internal class SimResourceForwarderTest { } forwarder.consume(object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Exit - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { + override fun onNext( + ctx: SimResourceContext, + capacity: Double, + remainingWork: Double + ): SimResourceCommand { return SimResourceCommand.Exit } }) + forwarder.close() scheduler.close() } @@ -78,15 +85,100 @@ internal class SimResourceForwarderTest { } forwarder.consume(object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } + var isFirst = true - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit + override fun onNext( + ctx: SimResourceContext, + capacity: Double, + remainingWork: Double + ): SimResourceCommand { + return if (isFirst) { + isFirst = false + SimResourceCommand.Consume(10.0, 1.0) + } else { + SimResourceCommand.Exit + } } }) forwarder.close() } + + @Test + fun testState() = runBlockingTest { + val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val consumer = object : SimResourceConsumer { + override fun onNext( + ctx: SimResourceContext, + capacity: Double, + remainingWork: Double + ): SimResourceCommand = SimResourceCommand.Exit + } + + assertEquals(SimResourceState.Pending, forwarder.state) + + forwarder.startConsumer(consumer) + assertEquals(SimResourceState.Active, forwarder.state) + + assertThrows { forwarder.startConsumer(consumer) } + + forwarder.cancel() + assertEquals(SimResourceState.Pending, forwarder.state) + + forwarder.close() + assertEquals(SimResourceState.Stopped, forwarder.state) + } + + @Test + fun testCancelPendingDelegate() = runBlockingTest { + val forwarder = SimResourceForwarder(SimCpu(1000.0)) + + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Exit + + forwarder.startConsumer(consumer) + forwarder.cancel() + + verify(exactly = 0) { consumer.onFinish(any(), null) } + } + + @Test + fun testCancelStartedDelegate() = runBlockingTest { + val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) + + source.startConsumer(forwarder) + yield() + forwarder.startConsumer(consumer) + yield() + forwarder.cancel() + + verify(exactly = 1) { consumer.onStart(any()) } + verify(exactly = 1) { consumer.onFinish(any(), null) } + } + + @Test + fun testCancelPropagation() = runBlockingTest { + val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) + + source.startConsumer(forwarder) + yield() + forwarder.startConsumer(consumer) + yield() + source.cancel() + + verify(exactly = 1) { consumer.onStart(any()) } + verify(exactly = 1) { consumer.onFinish(any(), null) } + } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt index 4f7825fc..18f18ded 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt @@ -22,6 +22,8 @@ package org.opendc.simulator.resources +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.* import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runBlockingTest @@ -46,15 +48,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1000 * ctx.resource.speed, ctx.resource.speed) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, provider.resource.speed)) + .andThen(SimResourceCommand.Exit) try { val res = mutableListOf() @@ -76,15 +73,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1000 * ctx.resource.speed, 2 * ctx.resource.speed) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, 2 * provider.resource.speed)) + .andThen(SimResourceCommand.Exit) try { val res = mutableListOf() @@ -111,13 +103,12 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { + override fun onStart(ctx: SimResourceContext) { ctx.interrupt() - return SimResourceCommand.Exit } - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() + override fun onNext(ctx: SimResourceContext, capacity: Double, remainingWork: Double): SimResourceCommand { + return SimResourceCommand.Exit } } @@ -137,14 +128,19 @@ class SimResourceSourceTest { lateinit var resCtx: SimResourceContext val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { + var isFirst = true + override fun onStart(ctx: SimResourceContext) { resCtx = ctx - return SimResourceCommand.Consume(4.0, 1.0) } - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { + override fun onNext(ctx: SimResourceContext, capacity: Double, remainingWork: Double): SimResourceCommand { assertEquals(0.0, remainingWork) - return SimResourceCommand.Exit + return if (isFirst) { + isFirst = false + SimResourceCommand.Consume(4.0, 1.0) + } else { + SimResourceCommand.Exit + } } } @@ -168,15 +164,9 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - throw IllegalStateException() - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onStart(any()) } + .throws(IllegalStateException()) try { assertThrows { @@ -194,15 +184,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1.0, 1.0)) + .andThenThrows(IllegalStateException()) try { assertThrows { @@ -220,15 +205,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1.0, 1.0)) + .andThenThrows(IllegalStateException()) try { assertThrows { @@ -249,15 +229,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1.0, 1.0)) + .andThenThrows(IllegalStateException()) try { assertThrows { @@ -276,15 +251,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - throw IllegalStateException() - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Consume(1.0, 1.0)) + .andThenThrows(IllegalStateException()) try { launch { provider.consume(consumer) } @@ -304,15 +274,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Idle(ctx.clock.millis() + 500) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Idle(clock.millis() + 500)) + .andThen(SimResourceCommand.Exit) try { provider.consume(consumer) @@ -332,15 +297,10 @@ class SimResourceSourceTest { val scheduler = TimerScheduler(coroutineContext, clock) val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Idle() - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Idle()) + .andThenThrows(IllegalStateException()) try { provider.consume(consumer) @@ -351,4 +311,25 @@ class SimResourceSourceTest { } } } + + @Test + fun testIncorrectDeadline() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } + .returns(SimResourceCommand.Idle(2)) + .andThen(SimResourceCommand.Exit) + + try { + delay(10) + + assertThrows { provider.consume(consumer) } + } finally { + scheduler.close() + provider.close() + } + } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt index ca6558bf..354dab93 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt @@ -22,6 +22,8 @@ package org.opendc.simulator.resources +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch @@ -34,7 +36,6 @@ import org.junit.jupiter.api.assertThrows import org.opendc.simulator.resources.consumer.SimTraceConsumer import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.utils.TimerScheduler -import java.lang.IllegalStateException /** * Test suite for the [SimResourceSwitchExclusive] class. @@ -67,7 +68,7 @@ internal class SimResourceSwitchExclusiveTest { ), ) - val switch = SimResourceSwitchExclusive(coroutineContext) + val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) switch.addInput(source) @@ -98,17 +99,10 @@ internal class SimResourceSwitchExclusiveTest { val scheduler = TimerScheduler(coroutineContext, clock) val duration = 5 * 60L * 1000 - val workload = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(duration / 1000.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val workload = mockk>(relaxUnitFun = true) + every { workload.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit - val switch = SimResourceSwitchExclusive(coroutineContext) + val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) switch.addInput(source) @@ -134,16 +128,27 @@ internal class SimResourceSwitchExclusiveTest { val duration = 5 * 60L * 1000 val workload = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(duration / 1000.0, 1.0) + var isFirst = true + + override fun onStart(ctx: SimResourceContext) { + isFirst = true } - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit + override fun onNext( + ctx: SimResourceContext, + capacity: Double, + remainingWork: Double + ): SimResourceCommand { + return if (isFirst) { + isFirst = false + SimResourceCommand.Consume(duration / 1000.0, 1.0) + } else { + SimResourceCommand.Exit + } } } - val switch = SimResourceSwitchExclusive(coroutineContext) + val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) switch.addInput(source) @@ -169,17 +174,10 @@ internal class SimResourceSwitchExclusiveTest { val scheduler = TimerScheduler(coroutineContext, clock) val duration = 5 * 60L * 1000 - val workload = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(duration.toDouble(), 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val workload = mockk>(relaxUnitFun = true) + every { workload.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit - val switch = SimResourceSwitchExclusive(coroutineContext) + val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) switch.addInput(source) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt index 698c1700..e8f5a13c 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt @@ -22,6 +22,8 @@ package org.opendc.simulator.resources +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -47,22 +49,15 @@ internal class SimResourceSwitchMaxMinTest { fun testSmoke() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val switch = SimResourceSwitchMaxMin(clock, coroutineContext) + val switch = SimResourceSwitchMaxMin(clock) val sources = List(2) { SimResourceSource(SimCpu(2000.0), clock, scheduler) } sources.forEach { switch.addInput(it) } val provider = switch.addOutput(SimCpu(1000.0)) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext): SimResourceCommand { - return SimResourceCommand.Consume(1.0, 1.0) - } - - override fun onNext(ctx: SimResourceContext, remainingWork: Double): SimResourceCommand { - return SimResourceCommand.Exit - } - } + val consumer = mockk>(relaxUnitFun = true) + every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(1.0, 1.0) andThen SimResourceCommand.Exit try { provider.consume(consumer) @@ -112,7 +107,7 @@ internal class SimResourceSwitchMaxMinTest { ), ) - val switch = SimResourceSwitchMaxMin(clock, coroutineContext, listener) + val switch = SimResourceSwitchMaxMin(clock, listener) val provider = switch.addOutput(SimCpu(3200.0)) try { @@ -180,7 +175,7 @@ internal class SimResourceSwitchMaxMinTest { ) ) - val switch = SimResourceSwitchMaxMin(clock, coroutineContext, listener) + val switch = SimResourceSwitchMaxMin(clock, listener) val providerA = switch.addOutput(SimCpu(3200.0)) val providerB = switch.addOutput(SimCpu(3200.0)) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt new file mode 100644 index 00000000..b05195f7 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt @@ -0,0 +1,74 @@ +/* + * 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.resources + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.opendc.simulator.resources.consumer.SimWorkConsumer +import org.opendc.simulator.utils.DelayControllerClockAdapter +import org.opendc.utils.TimerScheduler + +/** + * A test suite for the [SimWorkConsumer] class. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimWorkConsumerTest { + data class SimCpu(val speed: Double) : SimResource { + override val capacity: Double + get() = speed + } + + @Test + fun testSmoke() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val provider = SimResourceSource(SimCpu(1.0), clock, scheduler) + + val consumer = SimWorkConsumer(1.0, 1.0) + + try { + provider.consume(consumer) + assertEquals(1000, currentTime) + } finally { + provider.close() + } + } + + @Test + fun testUtilization() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val provider = SimResourceSource(SimCpu(1.0), clock, scheduler) + + val consumer = SimWorkConsumer(1.0, 0.5) + + try { + provider.consume(consumer) + assertEquals(2000, currentTime) + } finally { + provider.close() + } + } +} -- cgit v1.2.3 From f616b720406250b1415593ff04c9d910b1fda54c Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 22 Mar 2021 12:13:23 +0100 Subject: simulator: Expose capacity and remaining work outside consumer callback This change changes the consumer and context interfaces to expose the provider capacity and remaining work via the context instance as opposed to only via the callback. This simplifies aggregation of resources. --- .../simulator/resources/SimResourceContextTest.kt | 8 ++++---- .../resources/SimResourceForwarderTest.kt | 18 ++++++---------- .../simulator/resources/SimResourceSourceTest.kt | 24 +++++++++++----------- .../resources/SimResourceSwitchExclusiveTest.kt | 8 +++----- .../resources/SimResourceSwitchMaxMinTest.kt | 2 +- 5 files changed, 26 insertions(+), 34 deletions(-) (limited to 'simulator/opendc-simulator/opendc-simulator-resources/src/test') diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt index 0bc87473..5d4eb46d 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt @@ -45,7 +45,7 @@ class SimResourceContextTest { val resource = SimCpu(4200.0) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit val context = object : SimAbstractResourceContext(resource, clock, consumer) { override fun onIdle(deadline: Long) {} @@ -64,7 +64,7 @@ class SimResourceContextTest { val resource = SimCpu(4200.0) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { override fun onIdle(deadline: Long) {} @@ -85,7 +85,7 @@ class SimResourceContextTest { val resource = SimCpu(4200.0) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { override fun onIdle(deadline: Long) {} @@ -111,7 +111,7 @@ class SimResourceContextTest { val resource = SimCpu(4200.0) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit val context = object : SimAbstractResourceContext(resource, clock, consumer) { override fun onIdle(deadline: Long) {} diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt index b1b959ba..47794bdd 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt @@ -60,9 +60,7 @@ internal class SimResourceForwarderTest { forwarder.consume(object : SimResourceConsumer { override fun onNext( - ctx: SimResourceContext, - capacity: Double, - remainingWork: Double + ctx: SimResourceContext ): SimResourceCommand { return SimResourceCommand.Exit } @@ -88,9 +86,7 @@ internal class SimResourceForwarderTest { var isFirst = true override fun onNext( - ctx: SimResourceContext, - capacity: Double, - remainingWork: Double + ctx: SimResourceContext ): SimResourceCommand { return if (isFirst) { isFirst = false @@ -109,9 +105,7 @@ internal class SimResourceForwarderTest { val forwarder = SimResourceForwarder(SimCpu(1000.0)) val consumer = object : SimResourceConsumer { override fun onNext( - ctx: SimResourceContext, - capacity: Double, - remainingWork: Double + ctx: SimResourceContext ): SimResourceCommand = SimResourceCommand.Exit } @@ -134,7 +128,7 @@ internal class SimResourceForwarderTest { val forwarder = SimResourceForwarder(SimCpu(1000.0)) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Exit forwarder.startConsumer(consumer) forwarder.cancel() @@ -150,7 +144,7 @@ internal class SimResourceForwarderTest { val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) + every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) source.startConsumer(forwarder) yield() @@ -170,7 +164,7 @@ internal class SimResourceForwarderTest { val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Idle(10) + every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) source.startConsumer(forwarder) yield() diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt index 18f18ded..c0ed8c9e 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt @@ -49,7 +49,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, provider.resource.speed)) .andThen(SimResourceCommand.Exit) @@ -74,7 +74,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, 2 * provider.resource.speed)) .andThen(SimResourceCommand.Exit) @@ -107,7 +107,7 @@ class SimResourceSourceTest { ctx.interrupt() } - override fun onNext(ctx: SimResourceContext, capacity: Double, remainingWork: Double): SimResourceCommand { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { return SimResourceCommand.Exit } } @@ -133,8 +133,8 @@ class SimResourceSourceTest { resCtx = ctx } - override fun onNext(ctx: SimResourceContext, capacity: Double, remainingWork: Double): SimResourceCommand { - assertEquals(0.0, remainingWork) + override fun onNext(ctx: SimResourceContext): SimResourceCommand { + assertEquals(0.0, ctx.remainingWork) return if (isFirst) { isFirst = false SimResourceCommand.Consume(4.0, 1.0) @@ -185,7 +185,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -206,7 +206,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -230,7 +230,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -252,7 +252,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -275,7 +275,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle(clock.millis() + 500)) .andThen(SimResourceCommand.Exit) @@ -298,7 +298,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle()) .andThenThrows(IllegalStateException()) @@ -319,7 +319,7 @@ class SimResourceSourceTest { val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } + every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle(2)) .andThen(SimResourceCommand.Exit) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt index 354dab93..dc90a43e 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt @@ -100,7 +100,7 @@ internal class SimResourceSwitchExclusiveTest { val duration = 5 * 60L * 1000 val workload = mockk>(relaxUnitFun = true) - every { workload.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit + every { workload.onNext(any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) @@ -135,9 +135,7 @@ internal class SimResourceSwitchExclusiveTest { } override fun onNext( - ctx: SimResourceContext, - capacity: Double, - remainingWork: Double + ctx: SimResourceContext ): SimResourceCommand { return if (isFirst) { isFirst = false @@ -175,7 +173,7 @@ internal class SimResourceSwitchExclusiveTest { val duration = 5 * 60L * 1000 val workload = mockk>(relaxUnitFun = true) - every { workload.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit + every { workload.onNext(any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit val switch = SimResourceSwitchExclusive() val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt index e8f5a13c..8b989334 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt @@ -57,7 +57,7 @@ internal class SimResourceSwitchMaxMinTest { val provider = switch.addOutput(SimCpu(1000.0)) val consumer = mockk>(relaxUnitFun = true) - every { consumer.onNext(any(), any(), any()) } returns SimResourceCommand.Consume(1.0, 1.0) andThen SimResourceCommand.Exit + every { consumer.onNext(any()) } returns SimResourceCommand.Consume(1.0, 1.0) andThen SimResourceCommand.Exit try { provider.consume(consumer) -- cgit v1.2.3 From 3718c385f84b463ac799080bb5603e0011adcd7d Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 22 Mar 2021 16:45:13 +0100 Subject: simulator: Remove generic resource constraint from resource model This change removes the generic resource constraint (e.g., SimResource) and replaces it by a simple capacity property. In the future, users should handle the resource properties on a higher level. This change simplifies compositions of consumers and providers by not requiring a translation from resource to capacity. --- .../resources/SimResourceAggregatorMaxMinTest.kt | 158 +++++++++++++++++++++ .../simulator/resources/SimResourceContextTest.kt | 38 +++-- .../resources/SimResourceForwarderTest.kt | 50 +++---- .../simulator/resources/SimResourceSourceTest.kt | 83 ++++++----- .../resources/SimResourceSwitchExclusiveTest.kt | 43 +++--- .../resources/SimResourceSwitchMaxMinTest.kt | 31 ++-- .../simulator/resources/SimWorkConsumerTest.kt | 13 +- 7 files changed, 274 insertions(+), 142 deletions(-) create mode 100644 simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt (limited to 'simulator/opendc-simulator/opendc-simulator-resources/src/test') diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt new file mode 100644 index 00000000..3dffc7bf --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt @@ -0,0 +1,158 @@ +/* + * 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.resources + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.yield +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.assertThrows +import org.opendc.simulator.resources.consumer.SimWorkConsumer +import org.opendc.simulator.utils.DelayControllerClockAdapter +import org.opendc.utils.TimerScheduler + +/** + * Test suite for the [SimResourceAggregatorMaxMin] class. + */ +@OptIn(ExperimentalCoroutinesApi::class) +internal class SimResourceAggregatorMaxMinTest { + @Test + fun testSingleCapacity() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = SimWorkConsumer(1.0, 0.5) + val usage = mutableListOf() + val job = launch { sources[0].speed.toList(usage) } + + try { + aggregator.output.consume(consumer) + yield() + + assertAll( + { assertEquals(1000, currentTime) }, + { assertEquals(listOf(0.0, 0.5, 0.0), usage) } + ) + } finally { + aggregator.output.close() + job.cancel() + } + } + + @Test + fun testDoubleCapacity() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = SimWorkConsumer(2.0, 1.0) + val usage = mutableListOf() + val job = launch { sources[0].speed.toList(usage) } + + try { + aggregator.output.consume(consumer) + yield() + assertAll( + { assertEquals(1000, currentTime) }, + { assertEquals(listOf(0.0, 1.0, 0.0), usage) } + ) + } finally { + aggregator.output.close() + job.cancel() + } + } + + @Test + fun testOvercommit() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = mockk(relaxUnitFun = true) + every { consumer.onNext(any()) } + .returns(SimResourceCommand.Consume(4.0, 4.0, 1000)) + .andThen(SimResourceCommand.Exit) + + try { + aggregator.output.consume(consumer) + yield() + assertEquals(1000, currentTime) + + verify(exactly = 2) { consumer.onNext(any()) } + } finally { + aggregator.output.close() + } + } + + @Test + fun testException() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = mockk(relaxUnitFun = true) + every { consumer.onNext(any()) } + .returns(SimResourceCommand.Consume(1.0, 1.0)) + .andThenThrows(IllegalStateException()) + + try { + assertThrows { aggregator.output.consume(consumer) } + yield() + assertEquals(SimResourceState.Pending, sources[0].state) + } finally { + aggregator.output.close() + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt index 5d4eb46d..c6988ed9 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt @@ -33,25 +33,18 @@ import org.opendc.simulator.utils.DelayControllerClockAdapter */ @OptIn(ExperimentalCoroutinesApi::class) class SimResourceContextTest { - data class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - @Test fun testFlushWithoutCommand() = runBlockingTest { val clock = DelayControllerClockAdapter(this) - val resource = SimCpu(4200.0) - - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit - val context = object : SimAbstractResourceContext(resource, clock, consumer) { - override fun onIdle(deadline: Long) {} + val context = object : SimAbstractResourceContext(clock, consumer) { + override val capacity: Double = 4200.0 + override fun onIdle(deadline: Long) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} - override fun onFinish(cause: Throwable?) {} } @@ -61,12 +54,13 @@ class SimResourceContextTest { @Test fun testIntermediateFlush() = runBlockingTest { val clock = DelayControllerClockAdapter(this) - val resource = SimCpu(4200.0) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit - val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { + val context = spyk(object : SimAbstractResourceContext(clock, consumer) { + override val capacity: Double = 4200.0 + override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} @@ -82,12 +76,13 @@ class SimResourceContextTest { @Test fun testIntermediateFlushIdle() = runBlockingTest { val clock = DelayControllerClockAdapter(this) - val resource = SimCpu(4200.0) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit - val context = spyk(object : SimAbstractResourceContext(resource, clock, consumer) { + val context = spyk(object : SimAbstractResourceContext(clock, consumer) { + override val capacity: Double = 4200.0 + override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} @@ -100,7 +95,7 @@ class SimResourceContextTest { context.flush(isIntermediate = true) assertAll( - { verify(exactly = 1) { context.onIdle(any()) } }, + { verify(exactly = 2) { context.onIdle(any()) } }, { verify(exactly = 1) { context.onFinish(null) } } ) } @@ -108,12 +103,13 @@ class SimResourceContextTest { @Test fun testDoubleStart() = runBlockingTest { val clock = DelayControllerClockAdapter(this) - val resource = SimCpu(4200.0) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit - val context = object : SimAbstractResourceContext(resource, clock, consumer) { + val context = object : SimAbstractResourceContext(clock, consumer) { + override val capacity: Double = 4200.0 + override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt index 47794bdd..f68450ff 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt @@ -40,28 +40,20 @@ import org.opendc.utils.TimerScheduler */ @OptIn(ExperimentalCoroutinesApi::class) internal class SimResourceForwarderTest { - - data class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - @Test fun testExitImmediately() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val forwarder = SimResourceForwarder() val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + val source = SimResourceSource(2000.0, clock, scheduler) launch { source.consume(forwarder) source.close() } - forwarder.consume(object : SimResourceConsumer { - override fun onNext( - ctx: SimResourceContext - ): SimResourceCommand { + forwarder.consume(object : SimResourceConsumer { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { return SimResourceCommand.Exit } }) @@ -72,22 +64,20 @@ internal class SimResourceForwarderTest { @Test fun testExit() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val forwarder = SimResourceForwarder() val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + val source = SimResourceSource(2000.0, clock, scheduler) launch { source.consume(forwarder) source.close() } - forwarder.consume(object : SimResourceConsumer { + forwarder.consume(object : SimResourceConsumer { var isFirst = true - override fun onNext( - ctx: SimResourceContext - ): SimResourceCommand { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { return if (isFirst) { isFirst = false SimResourceCommand.Consume(10.0, 1.0) @@ -102,11 +92,9 @@ internal class SimResourceForwarderTest { @Test fun testState() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) - val consumer = object : SimResourceConsumer { - override fun onNext( - ctx: SimResourceContext - ): SimResourceCommand = SimResourceCommand.Exit + val forwarder = SimResourceForwarder() + val consumer = object : SimResourceConsumer { + override fun onNext(ctx: SimResourceContext): SimResourceCommand = SimResourceCommand.Exit } assertEquals(SimResourceState.Pending, forwarder.state) @@ -125,9 +113,9 @@ internal class SimResourceForwarderTest { @Test fun testCancelPendingDelegate() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val forwarder = SimResourceForwarder() - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Exit forwarder.startConsumer(consumer) @@ -138,12 +126,12 @@ internal class SimResourceForwarderTest { @Test fun testCancelStartedDelegate() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val forwarder = SimResourceForwarder() val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + val source = SimResourceSource(2000.0, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) source.startConsumer(forwarder) @@ -158,12 +146,12 @@ internal class SimResourceForwarderTest { @Test fun testCancelPropagation() = runBlockingTest { - val forwarder = SimResourceForwarder(SimCpu(1000.0)) + val forwarder = SimResourceForwarder() val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val source = SimResourceSource(SimCpu(2000.0), clock, scheduler) + val source = SimResourceSource(2000.0, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) source.startConsumer(forwarder) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt index c0ed8c9e..1279c679 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt @@ -37,20 +37,16 @@ import org.opendc.utils.TimerScheduler */ @OptIn(ExperimentalCoroutinesApi::class) class SimResourceSourceTest { - data class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - @Test fun testSpeed() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } - .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, provider.resource.speed)) + .returns(SimResourceCommand.Consume(1000 * capacity, capacity)) .andThen(SimResourceCommand.Exit) try { @@ -60,7 +56,7 @@ class SimResourceSourceTest { provider.consume(consumer) job.cancel() - assertEquals(listOf(0.0, provider.resource.speed, 0.0), res) { "Speed is reported correctly" } + assertEquals(listOf(0.0, capacity, 0.0), res) { "Speed is reported correctly" } } finally { scheduler.close() provider.close() @@ -71,11 +67,12 @@ class SimResourceSourceTest { fun testSpeedLimit() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } - .returns(SimResourceCommand.Consume(1000 * provider.resource.speed, 2 * provider.resource.speed)) + .returns(SimResourceCommand.Consume(1000 * capacity, 2 * capacity)) .andThen(SimResourceCommand.Exit) try { @@ -85,7 +82,7 @@ class SimResourceSourceTest { provider.consume(consumer) job.cancel() - assertEquals(listOf(0.0, provider.resource.speed, 0.0), res) { "Speed is reported correctly" } + assertEquals(listOf(0.0, capacity, 0.0), res) { "Speed is reported correctly" } } finally { scheduler.close() provider.close() @@ -100,14 +97,15 @@ class SimResourceSourceTest { fun testIntermediateInterrupt() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = object : SimResourceConsumer { - override fun onStart(ctx: SimResourceContext) { + val consumer = object : SimResourceConsumer { + override fun onStart(ctx: SimResourceContext) { ctx.interrupt() } - override fun onNext(ctx: SimResourceContext): SimResourceCommand { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { return SimResourceCommand.Exit } } @@ -124,16 +122,17 @@ class SimResourceSourceTest { fun testInterrupt() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) - lateinit var resCtx: SimResourceContext + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) + lateinit var resCtx: SimResourceContext - val consumer = object : SimResourceConsumer { + val consumer = object : SimResourceConsumer { var isFirst = true - override fun onStart(ctx: SimResourceContext) { + override fun onStart(ctx: SimResourceContext) { resCtx = ctx } - override fun onNext(ctx: SimResourceContext): SimResourceCommand { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { assertEquals(0.0, ctx.remainingWork) return if (isFirst) { isFirst = false @@ -162,9 +161,10 @@ class SimResourceSourceTest { fun testFailure() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onStart(any()) } .throws(IllegalStateException()) @@ -182,9 +182,10 @@ class SimResourceSourceTest { fun testExceptionPropagationOnNext() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -203,9 +204,10 @@ class SimResourceSourceTest { fun testConcurrentConsumption() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -227,9 +229,10 @@ class SimResourceSourceTest { fun testClosedConsumption() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -249,9 +252,10 @@ class SimResourceSourceTest { fun testCloseDuringConsumption() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Consume(1.0, 1.0)) .andThenThrows(IllegalStateException()) @@ -272,9 +276,10 @@ class SimResourceSourceTest { fun testIdle() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle(clock.millis() + 500)) .andThen(SimResourceCommand.Exit) @@ -295,9 +300,10 @@ class SimResourceSourceTest { runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle()) .andThenThrows(IllegalStateException()) @@ -316,9 +322,10 @@ class SimResourceSourceTest { fun testIncorrectDeadline() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(4200.0), clock, scheduler) + val capacity = 4200.0 + val provider = SimResourceSource(capacity, clock, scheduler) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } .returns(SimResourceCommand.Idle(2)) .andThen(SimResourceCommand.Exit) diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt index dc90a43e..edd60502 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchExclusiveTest.kt @@ -42,11 +42,6 @@ import org.opendc.utils.TimerScheduler */ @OptIn(ExperimentalCoroutinesApi::class) internal class SimResourceSwitchExclusiveTest { - class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - /** * Test a trace workload. */ @@ -68,12 +63,12 @@ internal class SimResourceSwitchExclusiveTest { ), ) - val switch = SimResourceSwitchExclusive() - val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) + val switch = SimResourceSwitchExclusive() + val source = SimResourceSource(3200.0, clock, scheduler) switch.addInput(source) - val provider = switch.addOutput(SimCpu(3200.0)) + val provider = switch.addOutput(3200.0) val job = launch { source.speed.toList(speed) } try { @@ -99,15 +94,15 @@ internal class SimResourceSwitchExclusiveTest { val scheduler = TimerScheduler(coroutineContext, clock) val duration = 5 * 60L * 1000 - val workload = mockk>(relaxUnitFun = true) + val workload = mockk(relaxUnitFun = true) every { workload.onNext(any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit - val switch = SimResourceSwitchExclusive() - val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) + val switch = SimResourceSwitchExclusive() + val source = SimResourceSource(3200.0, clock, scheduler) switch.addInput(source) - val provider = switch.addOutput(SimCpu(3200.0)) + val provider = switch.addOutput(3200.0) try { provider.consume(workload) @@ -127,16 +122,14 @@ internal class SimResourceSwitchExclusiveTest { val scheduler = TimerScheduler(coroutineContext, clock) val duration = 5 * 60L * 1000 - val workload = object : SimResourceConsumer { + val workload = object : SimResourceConsumer { var isFirst = true - override fun onStart(ctx: SimResourceContext) { + override fun onStart(ctx: SimResourceContext) { isFirst = true } - override fun onNext( - ctx: SimResourceContext - ): SimResourceCommand { + override fun onNext(ctx: SimResourceContext): SimResourceCommand { return if (isFirst) { isFirst = false SimResourceCommand.Consume(duration / 1000.0, 1.0) @@ -146,12 +139,12 @@ internal class SimResourceSwitchExclusiveTest { } } - val switch = SimResourceSwitchExclusive() - val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) + val switch = SimResourceSwitchExclusive() + val source = SimResourceSource(3200.0, clock, scheduler) switch.addInput(source) - val provider = switch.addOutput(SimCpu(3200.0)) + val provider = switch.addOutput(3200.0) try { provider.consume(workload) @@ -172,15 +165,15 @@ internal class SimResourceSwitchExclusiveTest { val scheduler = TimerScheduler(coroutineContext, clock) val duration = 5 * 60L * 1000 - val workload = mockk>(relaxUnitFun = true) + val workload = mockk(relaxUnitFun = true) every { workload.onNext(any()) } returns SimResourceCommand.Consume(duration / 1000.0, 1.0) andThen SimResourceCommand.Exit - val switch = SimResourceSwitchExclusive() - val source = SimResourceSource(SimCpu(3200.0), clock, scheduler) + val switch = SimResourceSwitchExclusive() + val source = SimResourceSource(3200.0, clock, scheduler) switch.addInput(source) - switch.addOutput(SimCpu(3200.0)) - assertThrows { switch.addOutput(SimCpu(3200.0)) } + switch.addOutput(3200.0) + assertThrows { switch.addOutput(3200.0) } } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt index 8b989334..5f4fd187 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSwitchMaxMinTest.kt @@ -40,23 +40,18 @@ import org.opendc.utils.TimerScheduler */ @OptIn(ExperimentalCoroutinesApi::class) internal class SimResourceSwitchMaxMinTest { - class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - @Test fun testSmoke() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val switch = SimResourceSwitchMaxMin(clock) + val switch = SimResourceSwitchMaxMin(clock) - val sources = List(2) { SimResourceSource(SimCpu(2000.0), clock, scheduler) } + val sources = List(2) { SimResourceSource(2000.0, clock, scheduler) } sources.forEach { switch.addInput(it) } - val provider = switch.addOutput(SimCpu(1000.0)) + val provider = switch.addOutput(1000.0) - val consumer = mockk>(relaxUnitFun = true) + val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Consume(1.0, 1.0) andThen SimResourceCommand.Exit try { @@ -76,13 +71,13 @@ internal class SimResourceSwitchMaxMinTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val listener = object : SimResourceSwitchMaxMin.Listener { + val listener = object : SimResourceSwitchMaxMin.Listener { var totalRequestedWork = 0L var totalGrantedWork = 0L var totalOvercommittedWork = 0L override fun onSliceFinish( - switch: SimResourceSwitchMaxMin, + switch: SimResourceSwitchMaxMin, requestedWork: Long, grantedWork: Long, overcommittedWork: Long, @@ -108,10 +103,10 @@ internal class SimResourceSwitchMaxMinTest { ) val switch = SimResourceSwitchMaxMin(clock, listener) - val provider = switch.addOutput(SimCpu(3200.0)) + val provider = switch.addOutput(3200.0) try { - switch.addInput(SimResourceSource(SimCpu(3200.0), clock, scheduler)) + switch.addInput(SimResourceSource(3200.0, clock, scheduler)) provider.consume(workload) yield() } finally { @@ -135,13 +130,13 @@ internal class SimResourceSwitchMaxMinTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val listener = object : SimResourceSwitchMaxMin.Listener { + val listener = object : SimResourceSwitchMaxMin.Listener { var totalRequestedWork = 0L var totalGrantedWork = 0L var totalOvercommittedWork = 0L override fun onSliceFinish( - switch: SimResourceSwitchMaxMin, + switch: SimResourceSwitchMaxMin, requestedWork: Long, grantedWork: Long, overcommittedWork: Long, @@ -176,11 +171,11 @@ internal class SimResourceSwitchMaxMinTest { ) val switch = SimResourceSwitchMaxMin(clock, listener) - val providerA = switch.addOutput(SimCpu(3200.0)) - val providerB = switch.addOutput(SimCpu(3200.0)) + val providerA = switch.addOutput(3200.0) + val providerB = switch.addOutput(3200.0) try { - switch.addInput(SimResourceSource(SimCpu(3200.0), clock, scheduler)) + switch.addInput(SimResourceSource(3200.0, clock, scheduler)) coroutineScope { launch { providerA.consume(workloadA) } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt index b05195f7..4d6b19ee 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimWorkConsumerTest.kt @@ -35,18 +35,13 @@ import org.opendc.utils.TimerScheduler */ @OptIn(ExperimentalCoroutinesApi::class) internal class SimWorkConsumerTest { - data class SimCpu(val speed: Double) : SimResource { - override val capacity: Double - get() = speed - } - @Test fun testSmoke() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(1.0), clock, scheduler) + val provider = SimResourceSource(1.0, clock, scheduler) - val consumer = SimWorkConsumer(1.0, 1.0) + val consumer = SimWorkConsumer(1.0, 1.0) try { provider.consume(consumer) @@ -60,9 +55,9 @@ internal class SimWorkConsumerTest { fun testUtilization() = runBlockingTest { val clock = DelayControllerClockAdapter(this) val scheduler = TimerScheduler(coroutineContext, clock) - val provider = SimResourceSource(SimCpu(1.0), clock, scheduler) + val provider = SimResourceSource(1.0, clock, scheduler) - val consumer = SimWorkConsumer(1.0, 0.5) + val consumer = SimWorkConsumer(1.0, 0.5) try { provider.consume(consumer) -- cgit v1.2.3 From b5ac4b4f0c9a9e0c4b2ee744f8184adbe8e8d76a Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 22 Mar 2021 21:00:41 +0100 Subject: simulator: Add support for signaling dynamic capacity changes This change adds support for dynamically changing the capacity of resources and propagating this change to consumers. --- .../resources/SimResourceAggregatorMaxMinTest.kt | 56 ++++++++++++++++++++-- .../simulator/resources/SimResourceContextTest.kt | 16 ++----- .../resources/SimResourceForwarderTest.kt | 26 ++++++++-- .../simulator/resources/SimResourceSourceTest.kt | 25 ++++++++++ 4 files changed, 105 insertions(+), 18 deletions(-) (limited to 'simulator/opendc-simulator/opendc-simulator-resources/src/test') diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt index 3dffc7bf..de864c1c 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMinTest.kt @@ -25,11 +25,9 @@ package org.opendc.simulator.resources import io.mockk.every import io.mockk.mockk import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.* import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.launch import kotlinx.coroutines.test.runBlockingTest -import kotlinx.coroutines.yield import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll @@ -155,4 +153,56 @@ internal class SimResourceAggregatorMaxMinTest { aggregator.output.close() } } + + @Test + fun testAdjustCapacity() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = SimWorkConsumer(4.0, 1.0) + try { + coroutineScope { + launch { aggregator.output.consume(consumer) } + delay(1000) + sources[0].capacity = 0.5 + } + yield() + assertEquals(2334, currentTime) + } finally { + aggregator.output.close() + } + } + + @Test + fun testFailOverCapacity() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + + val aggregator = SimResourceAggregatorMaxMin(clock) + val sources = listOf( + SimResourceSource(1.0, clock, scheduler), + SimResourceSource(1.0, clock, scheduler) + ) + sources.forEach(aggregator::addInput) + + val consumer = SimWorkConsumer(1.0, 0.5) + try { + coroutineScope { + launch { aggregator.output.consume(consumer) } + delay(500) + sources[0].capacity = 0.5 + } + yield() + assertEquals(1000, currentTime) + } finally { + aggregator.output.close() + } + } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt index c6988ed9..030a0f6b 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceContextTest.kt @@ -40,9 +40,7 @@ class SimResourceContextTest { val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit - val context = object : SimAbstractResourceContext(clock, consumer) { - override val capacity: Double = 4200.0 - + val context = object : SimAbstractResourceContext(4200.0, clock, consumer) { override fun onIdle(deadline: Long) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} override fun onFinish(cause: Throwable?) {} @@ -58,9 +56,7 @@ class SimResourceContextTest { val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Consume(10.0, 1.0) andThen SimResourceCommand.Exit - val context = spyk(object : SimAbstractResourceContext(clock, consumer) { - override val capacity: Double = 4200.0 - + val context = spyk(object : SimAbstractResourceContext(4200.0, clock, consumer) { override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} @@ -80,9 +76,7 @@ class SimResourceContextTest { val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit - val context = spyk(object : SimAbstractResourceContext(clock, consumer) { - override val capacity: Double = 4200.0 - + val context = spyk(object : SimAbstractResourceContext(4200.0, clock, consumer) { override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} @@ -107,9 +101,7 @@ class SimResourceContextTest { val consumer = mockk(relaxUnitFun = true) every { consumer.onNext(any()) } returns SimResourceCommand.Idle(10) andThen SimResourceCommand.Exit - val context = object : SimAbstractResourceContext(clock, consumer) { - override val capacity: Double = 4200.0 - + val context = object : SimAbstractResourceContext(4200.0, clock, consumer) { override fun onIdle(deadline: Long) {} override fun onFinish(cause: Throwable?) {} override fun onConsume(work: Double, limit: Double, deadline: Long) {} diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt index f68450ff..143dbca9 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceForwarderTest.kt @@ -24,14 +24,14 @@ package org.opendc.simulator.resources import io.mockk.every import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import kotlinx.coroutines.test.runBlockingTest -import kotlinx.coroutines.yield import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.opendc.simulator.resources.consumer.SimWorkConsumer import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.utils.TimerScheduler @@ -163,4 +163,24 @@ internal class SimResourceForwarderTest { verify(exactly = 1) { consumer.onStart(any()) } verify(exactly = 1) { consumer.onFinish(any(), null) } } + + @Test + fun testAdjustCapacity() = runBlockingTest { + val forwarder = SimResourceForwarder() + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val source = SimResourceSource(1.0, clock, scheduler) + + val consumer = spyk(SimWorkConsumer(2.0, 1.0)) + source.startConsumer(forwarder) + + coroutineScope { + launch { forwarder.consume(consumer) } + delay(1000) + source.capacity = 0.5 + } + + assertEquals(3000, currentTime) + verify(exactly = 1) { consumer.onCapacityChanged(any(), true) } + } } diff --git a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt index 1279c679..58e19421 100644 --- a/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-resources/src/test/kotlin/org/opendc/simulator/resources/SimResourceSourceTest.kt @@ -24,11 +24,14 @@ package org.opendc.simulator.resources import io.mockk.every import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify import kotlinx.coroutines.* import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals +import org.opendc.simulator.resources.consumer.SimWorkConsumer import org.opendc.simulator.utils.DelayControllerClockAdapter import org.opendc.utils.TimerScheduler @@ -63,6 +66,28 @@ class SimResourceSourceTest { } } + @Test + fun testAdjustCapacity() = runBlockingTest { + val clock = DelayControllerClockAdapter(this) + val scheduler = TimerScheduler(coroutineContext, clock) + val provider = SimResourceSource(1.0, clock, scheduler) + + val consumer = spyk(SimWorkConsumer(2.0, 1.0)) + + try { + coroutineScope { + launch { provider.consume(consumer) } + delay(1000) + provider.capacity = 0.5 + } + assertEquals(3000, currentTime) + verify(exactly = 1) { consumer.onCapacityChanged(any(), true) } + } finally { + scheduler.close() + provider.close() + } + } + @Test fun testSpeedLimit() = runBlockingTest { val clock = DelayControllerClockAdapter(this) -- cgit v1.2.3