From 4a8b32d288ba3ee986ecef7933fa77554d34e762 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 4 May 2021 19:19:47 +0200 Subject: harness: Split harness into separate modules This change splits the OpenDC Experiment Harness into separate modules. This prevents users from pulling in unnecessary dependencies when depending on the harness API. --- .../runner/console/ConsoleExperimentReporter.kt | 79 ---------- .../opendc/harness/runner/console/ConsoleRunner.kt | 99 ------------- .../junit5/JUnitExperimentExecutionListener.kt | 162 --------------------- .../harness/runner/junit5/OpenDCTestEngine.kt | 92 ------------ 4 files changed, 432 deletions(-) delete mode 100644 opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleExperimentReporter.kt delete mode 100644 opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleRunner.kt delete mode 100644 opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/JUnitExperimentExecutionListener.kt delete mode 100644 opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/OpenDCTestEngine.kt (limited to 'opendc-harness/src/main/kotlin/org/opendc/harness/runner') diff --git a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleExperimentReporter.kt b/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleExperimentReporter.kt deleted file mode 100644 index 2db74ef4..00000000 --- a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleExperimentReporter.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.harness.runner.console - -import me.tongfei.progressbar.ProgressBar -import me.tongfei.progressbar.ProgressBarBuilder -import mu.KotlinLogging -import org.opendc.harness.api.Trial -import org.opendc.harness.engine.ExperimentExecutionListener - -/** - * A reporter that reports the experiment progress to the console. - */ -public class ConsoleExperimentReporter : ExperimentExecutionListener, AutoCloseable { - /** - * The active [Trial]s. - */ - private val trials: MutableSet = mutableSetOf() - - /** - * The total number of runs. - */ - private var total = 0 - - /** - * The logger for this reporter. - */ - private val logger = KotlinLogging.logger {} - - /** - * The progress bar to keep track of the progress. - */ - private val pb: ProgressBar = ProgressBarBuilder() - .setTaskName("") - .setInitialMax(1) - .build() - - override fun trialFinished(trial: Trial, throwable: Throwable?) { - trials -= trial - - pb.stepTo(total - trials.size.toLong()) - if (trials.isEmpty()) { - pb.close() - } - - if (throwable != null) { - logger.warn(throwable) { "Trial $trial failed" } - } - } - - override fun trialStarted(trial: Trial) { - trials += trial - pb.maxHint((++total).toLong()) - } - - override fun close() { - pb.close() - } -} diff --git a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleRunner.kt b/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleRunner.kt deleted file mode 100644 index ae221c7f..00000000 --- a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/console/ConsoleRunner.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.harness.runner.console - -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.parameters.options.default -import com.github.ajalt.clikt.parameters.options.multiple -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.types.int -import mu.KotlinLogging -import org.opendc.harness.engine.ExperimentEngineLauncher -import org.opendc.harness.engine.discovery.DiscoveryProvider -import org.opendc.harness.engine.discovery.DiscoveryRequest -import org.opendc.harness.engine.discovery.DiscoverySelector -import org.opendc.harness.engine.scheduler.ThreadPoolExperimentScheduler - -/** - * The logger for this experiment runner. - */ -private val logger = KotlinLogging.logger {} - -/** - * The command line interface for the console experiment runner. - */ -public class ConsoleRunner : CliktCommand(name = "opendc-harness") { - /** - * The number of repeats per scenario. - */ - private val repeats by option("-r", "--repeats", help = "Number of repeats per scenario") - .int() - .default(1) - - /** - * The selected experiments to run by name. - */ - private val experiments by option("-e", "--experiments", help = "Names of experiments to explore") - .multiple(emptyList()) - - /** - * The maximum number of worker threads to use. - */ - private val parallelism by option("-p", "--parallelism", help = "Maximum number of concurrent simulation runs") - .int() - .default(Runtime.getRuntime().availableProcessors()) - - override fun run() { - logger.info { "Starting OpenDC Console Experiment Runner" } - - val discovery = DiscoveryProvider.createComposite() - val experiments = discovery.discover( - DiscoveryRequest( - selectors = experiments.map { DiscoverySelector.Name(it) } - ) - ) - - val reporter = ConsoleExperimentReporter() - val scheduler = ThreadPoolExperimentScheduler(parallelism) - - try { - ExperimentEngineLauncher() - .withListener(reporter) - .withRepeats(repeats) - .withScheduler(scheduler) - .runBlocking(experiments) - } catch (e: Throwable) { - logger.error(e) { "Failed to finish experiments" } - } finally { - reporter.close() - scheduler.close() - } - - logger.info { "Finished all experiments. Exiting." } - } -} - -/** - * Main entry point of the experiment runner. - */ -public fun main(args: Array): Unit = ConsoleRunner().main(args) diff --git a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/JUnitExperimentExecutionListener.kt b/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/JUnitExperimentExecutionListener.kt deleted file mode 100644 index 9e2b629d..00000000 --- a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/JUnitExperimentExecutionListener.kt +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.harness.runner.junit5 - -import org.junit.platform.engine.* -import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor -import org.junit.platform.engine.support.descriptor.ClassSource -import org.junit.platform.engine.support.descriptor.EngineDescriptor -import org.opendc.harness.api.ExperimentDefinition -import org.opendc.harness.api.Scenario -import org.opendc.harness.api.Trial -import org.opendc.harness.engine.ExperimentExecutionListener -import java.util.* - -/** - * An [ExperimentExecutionListener] that notifies JUnit platform of the progress of the experiment trials. - */ -public class JUnitExperimentExecutionListener( - private val listener: EngineExecutionListener, - private val root: EngineDescriptor -) : ExperimentExecutionListener { - /** - * The current active experiments. - */ - private val experiments = mutableMapOf() - - /** - * The current active scenarios. - */ - private val scenarios = mutableMapOf() - - /** - * The current active trials. - */ - private val trials = mutableMapOf() - - override fun experimentStarted(experiment: ExperimentDefinition) { - val descriptor = experiment.toDescriptor(root) - root.addChild(descriptor) - experiments[experiment] = descriptor - - listener.dynamicTestRegistered(descriptor) - listener.executionStarted(descriptor) - } - - override fun experimentFinished(experiment: ExperimentDefinition, throwable: Throwable?) { - val descriptor = experiments.remove(experiment) - - if (throwable != null) { - listener.executionFinished(descriptor, TestExecutionResult.failed(throwable)) - } else { - listener.executionFinished(descriptor, TestExecutionResult.successful()) - } - } - - override fun scenarioStarted(scenario: Scenario) { - val parent = experiments[scenario.experiment] ?: return - val descriptor = scenario.toDescriptor(parent) - parent.addChild(descriptor) - scenarios[scenario] = descriptor - - listener.dynamicTestRegistered(descriptor) - listener.executionStarted(descriptor) - } - - override fun scenarioFinished(scenario: Scenario, throwable: Throwable?) { - val descriptor = scenarios.remove(scenario) - - if (throwable != null) { - listener.executionFinished(descriptor, TestExecutionResult.failed(throwable)) - } else { - listener.executionFinished(descriptor, TestExecutionResult.successful()) - } - } - - override fun trialStarted(trial: Trial) { - val parent = scenarios[trial.scenario] ?: return - val descriptor = trial.toDescriptor(parent) - parent.addChild(descriptor) - trials[trial] = descriptor - - listener.dynamicTestRegistered(descriptor) - listener.executionStarted(descriptor) - } - - override fun trialFinished(trial: Trial, throwable: Throwable?) { - val descriptor = trials.remove(trial) - - if (throwable != null) { - listener.executionFinished(descriptor, TestExecutionResult.failed(throwable)) - } else { - listener.executionFinished(descriptor, TestExecutionResult.successful()) - } - } - - /** - * Create a [TestDescriptor] for an [ExperimentDefinition]. - */ - private fun ExperimentDefinition.toDescriptor(parent: TestDescriptor): TestDescriptor { - return object : AbstractTestDescriptor(parent.uniqueId.append("experiment", name), name) { - override fun getType(): TestDescriptor.Type = TestDescriptor.Type.CONTAINER - - override fun mayRegisterTests(): Boolean = true - - override fun toString(): String = "ExperimentDescriptor" - - override fun getSource(): Optional { - val cls = meta["class.name"] as? String - return if (cls != null) - Optional.of(ClassSource.from(cls)) - else - Optional.empty() - } - } - } - - /** - * Create a [TestDescriptor] for a [Scenario]. - */ - private fun Scenario.toDescriptor(parent: TestDescriptor): TestDescriptor { - return object : AbstractTestDescriptor(parent.uniqueId.append("scenario", id.toString()), "Scenario $id") { - override fun getType(): TestDescriptor.Type = TestDescriptor.Type.CONTAINER_AND_TEST - - override fun mayRegisterTests(): Boolean = true - - override fun toString(): String = "ScenarioDescriptor" - } - } - - /** - * Create a [TestDescriptor] for a [Trial]. - */ - private fun Trial.toDescriptor(parent: TestDescriptor): TestDescriptor { - return object : AbstractTestDescriptor(parent.uniqueId.append("repeat", repeat.toString()), "Repeat $repeat") { - override fun getType(): TestDescriptor.Type = TestDescriptor.Type.TEST - - override fun mayRegisterTests(): Boolean = false - - override fun toString(): String = "TrialDescriptor" - } - } -} diff --git a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/OpenDCTestEngine.kt b/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/OpenDCTestEngine.kt deleted file mode 100644 index ab7367b8..00000000 --- a/opendc-harness/src/main/kotlin/org/opendc/harness/runner/junit5/OpenDCTestEngine.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.harness.runner.junit5 - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow -import mu.KotlinLogging -import org.junit.platform.engine.* -import org.junit.platform.engine.discovery.ClassNameFilter -import org.junit.platform.engine.discovery.ClassSelector -import org.junit.platform.engine.discovery.MethodSelector -import org.junit.platform.engine.support.descriptor.EngineDescriptor -import org.opendc.harness.api.ExperimentDefinition -import org.opendc.harness.engine.ExperimentEngineLauncher -import org.opendc.harness.engine.discovery.DiscoveryFilter -import org.opendc.harness.engine.discovery.DiscoveryProvider -import org.opendc.harness.engine.discovery.DiscoveryRequest -import org.opendc.harness.engine.discovery.DiscoverySelector -import java.util.* - -/** - * A [TestEngine] implementation that is able to run experiments defined using the harness. - */ -public class OpenDCTestEngine : TestEngine { - /** - * The logging instance for this engine. - */ - private val logger = KotlinLogging.logger {} - - override fun getId(): String = "opendc-harness" - - override fun getGroupId(): Optional = Optional.of("org.opendc") - - override fun getArtifactId(): Optional = Optional.of("opendc-harness") - - override fun discover(request: EngineDiscoveryRequest, uniqueId: UniqueId): TestDescriptor { - // IntelliJ will pass a [MethodSelector] to run just a single method inside a file. In that - // case, no experiments should be discovered, since we support only experiments by class. - if (request.getSelectorsByType(MethodSelector::class.java).isNotEmpty()) { - return ExperimentEngineDescriptor(uniqueId, emptyFlow()) - } - - val classNames = request.getSelectorsByType(ClassSelector::class.java).map { DiscoverySelector.Meta("class.name", it.className) } - val classNameFilters = request.getFiltersByType(ClassNameFilter::class.java).map { DiscoveryFilter.Name(it.toPredicate()) } - - val discovery = DiscoveryProvider.createComposite() - val definitions = discovery.discover(DiscoveryRequest(classNames, classNameFilters)) - - return ExperimentEngineDescriptor(uniqueId, definitions) - } - - override fun execute(request: ExecutionRequest) { - logger.debug { "JUnit ExecutionRequest[${request::class.java.name}] [configurationParameters=${request.configurationParameters}; rootTestDescriptor=${request.rootTestDescriptor}]" } - val root = request.rootTestDescriptor as ExperimentEngineDescriptor - val listener = request.engineExecutionListener - - listener.executionStarted(root) - - try { - ExperimentEngineLauncher() - .withListener(JUnitExperimentExecutionListener(listener, root)) - .runBlocking(root.experiments) - listener.executionFinished(root, TestExecutionResult.successful()) - } catch (e: Throwable) { - listener.executionFinished(root, TestExecutionResult.failed(e)) - } - } - - private class ExperimentEngineDescriptor(id: UniqueId, val experiments: Flow) : EngineDescriptor(id, "opendc") { - override fun mayRegisterTests(): Boolean = true - } -} -- cgit v1.2.3