Skip to content

Commit

Permalink
chore(alchemist-grid): add working directory and contextually refacto…
Browse files Browse the repository at this point in the history
…r tests
  • Loading branch information
kelvin-olaiya committed Sep 13, 2023
1 parent e895897 commit 359530e
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import it.unibo.alchemist.model.times.DoubleTime
import it.unibo.alchemist.proto.ClusterMessages
import it.unibo.alchemist.proto.SimulationMessage
import it.unibo.alchemist.proto.SimulationMessage.Assignment
import it.unibo.alchemist.proto.SimulationMessage.SimulationConfiguration
import it.unibo.alchemist.proto.SimulationMessage.SimulationResult
import it.unibo.alchemist.proto.SimulationMessage.SimulationStatus
import java.io.ByteArrayInputStream
Expand Down Expand Up @@ -191,10 +192,8 @@ class ClusterRegistry(
@Suppress("UNCHECKED_CAST")
override fun <T, P : Position<P>> simulationByJobId(jobID: UUID): Simulation<T, P> {
val job = getJob(jobID)
val simulationID = job.simulationID
val config = storage.get(KEYS.SIMULATIONS.make(simulationID)).first().bytes
val simulationConfig = SimulationMessage.SimulationConfiguration.parseFrom(config)
// save dependencies
val simulationID = UUID.fromString(job.simulationID)
val simulationConfig = getSimulationConfiguration(simulationID)
val environment: Environment<T, P> = deserializeObject(job.environment) as Environment<T, P>
val exports = deserializeObject(job.exports) as List<DistributedExporter<T, P>>
exports.forEach {
Expand All @@ -211,6 +210,11 @@ class ClusterRegistry(
return SimulationMessage.Simulation.parseFrom(job)
}

private fun getSimulationConfiguration(simulationID: UUID): SimulationConfiguration {
val config = storage.get(KEYS.SIMULATIONS.make(simulationID)).first().bytes
return SimulationConfiguration.parseFrom(config)
}

override fun jobStatus(jobID: UUID): Pair<JobStatus, UUID> {
val status = getSimulationStatus(jobID)
return status.status.toJobStatus to UUID.fromString(status.serverID)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2010-2023, Danilo Pianini and contributors
* listed, for each module, in the respective subproject's build.gradle.kts file.
*
* This file is part of Alchemist, and is distributed under the terms of the
* GNU General Public License, with a linking exception,
* as described in the file LICENSE in the Alchemist distribution's top directory.
*/

package it.unibo.alchemist.boundary.grid.utils

import org.apache.commons.io.FileUtils
import java.io.File
import java.net.URL
import java.nio.charset.StandardCharsets
import java.nio.file.Files

/**
* Manages a temporary working directory.
*/
class WorkingDirectory : AutoCloseable {

private val directory = Files.createTempDirectory("alchemist").toFile()

/**
* The temporary directory url.
*/
val url: URL get() = directory.toURI().toURL()

/**
* Get the folder file content.
*/
fun getFileContent(filename: String): String {
val file = File(getFileAbsolutePath(filename))
return FileUtils.readFileToString(file, StandardCharsets.UTF_8)
}

/**
* An absolute path for a given filename in this directory.
*/
fun getFileAbsolutePath(filename: String): String = "${directory.absolutePath}${File.separator}$filename"

/**
* Writes multiple files inside the directory
*/
fun writeFiles(files: Map<String, ByteArray>) {
files.forEach { (name, content) ->
val file = File(getFileAbsolutePath(name))
if (file.parentFile.exists() || file.parentFile.mkdirs()) {
FileUtils.writeByteArrayToFile(file, content)
} else {
throw IllegalStateException("Could not create directory structure for $file")
}
}
}

override fun close() {
FileUtils.deleteDirectory(directory)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package it.unibo.alchemist.test

import io.kotest.assertions.nondeterministic.eventually
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.ints.shouldBeExactly
import io.kotest.matchers.collections.shouldHaveSize
import it.unibo.alchemist.boundary.grid.cluster.ClusterImpl
import it.unibo.alchemist.test.utils.DistributionTestUtils.awaitServerJoin
import it.unibo.alchemist.test.utils.DistributionTestUtils.getDockerExtension
Expand All @@ -31,7 +31,7 @@ class ClusterTest : StringSpec({
startServers(serverConfigFile, SERVERS_TO_LAUNCH).use {
val cluster = ClusterImpl(registry)
eventually(10.seconds) {
cluster.nodes.size shouldBeExactly SERVERS_TO_LAUNCH
cluster.nodes shouldHaveSize SERVERS_TO_LAUNCH
}
}
}
Expand All @@ -43,7 +43,7 @@ class ClusterTest : StringSpec({
awaitServerJoin(cluster, SERVERS_TO_LAUNCH, 10.seconds)
serverToShutdown.close()
eventually(10.seconds) {
cluster.nodes.size shouldBeExactly SERVERS_TO_LAUNCH - 1
cluster.nodes shouldHaveSize SERVERS_TO_LAUNCH - 1
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class DistributionTest : StringSpec({
registry.simulationAssignments(simulationID).keys.size shouldBeExactly SERVERS_TO_LAUNCH
servers.first().close()
eventually(15.seconds) {
registry.simulationAssignments(simulationID).keys.size shouldBeExactly SERVERS_TO_LAUNCH - 1
registry.simulationAssignments(simulationID).keys shouldHaveSize SERVERS_TO_LAUNCH - 1
registry.assignedJobs(registry.nodes.first().serverID) shouldHaveSize BATCH_SIZE
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
package it.unibo.alchemist.test

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.ints.shouldBeExactly
import io.kotest.matchers.maps.shouldHaveSize
import io.kotest.matchers.shouldBe
import it.unibo.alchemist.test.utils.DistributionTestUtils.getSimulationContext
import org.kaikikm.threadresloader.ResourceLoader
Expand All @@ -24,7 +24,7 @@ class SimulationContextTest : StringSpec({

"Simulation dependencies are correctly loaded" {
val simulationContext = getSimulationContext(YAML_CONFIG_PATH)
simulationContext.dependencies.size shouldBeExactly 2
simulationContext.dependencies shouldHaveSize 2
simulationContext.dependencies[DEPENDENCY_FILE_PATH] shouldBe Files.readAllBytes(
Path.of(ResourceLoader.getResource(DEPENDENCY_FILE_PATH).toURI()),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ package it.unibo.alchemist.test

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import it.unibo.alchemist.boundary.grid.cluster.ClusterNode
import it.unibo.alchemist.boundary.grid.cluster.AlchemistClusterNode
import it.unibo.alchemist.boundary.grid.cluster.DispatchStrategyFactory
import it.unibo.alchemist.boundary.grid.simulation.SimulationInitializer
import java.util.UUID
import java.util.stream.Collectors
import java.util.stream.Stream
Expand All @@ -36,15 +35,6 @@ class TestDispatchStrategy : StringSpec({
companion object {
const val NUM_OF_SERVERS = 2L

fun serverFromUUID(uuid: UUID) = object : ClusterNode {
override val serverID: UUID
get() = uuid
override val metadata: Map<String, String>
get() = throw NotImplementedError()

override fun submitJob(simulationID: UUID, parameters: SimulationInitializer): UUID {
throw NotImplementedError()
}
}
fun serverFromUUID(uuid: UUID) = AlchemistClusterNode(uuid, mapOf())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2010-2023, Danilo Pianini and contributors
* listed, for each module, in the respective subproject's build.gradle.kts file.
*
* This file is part of Alchemist, and is distributed under the terms of the
* GNU General Public License, with a linking exception,
* as described in the file LICENSE in the Alchemist distribution's top directory.
*/

package it.unibo.alchemist.test

import io.kotest.assertions.withClue
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.booleans.shouldBeFalse
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.maps.shouldHaveSize
import io.kotest.matchers.nulls.shouldNotBeNull
import it.unibo.alchemist.boundary.LoadAlchemist
import it.unibo.alchemist.boundary.Loader
import it.unibo.alchemist.boundary.grid.simulation.SimulationConfigImpl
import it.unibo.alchemist.boundary.grid.utils.WorkingDirectory
import it.unibo.alchemist.model.Time
import org.kaikikm.threadresloader.ResourceLoader
import java.io.File
import java.net.URL

class TestWorkingDirectory : StringSpec({
"Files should be correctly written in working directory" {
val resource = "config/00-dependencies.yml"
val yaml = ResourceLoader.getResource(resource)
withClue("Yaml configuration should exits") {
yaml.shouldNotBeNull()
}
val loader: Loader = getLoader(yaml)
val simulationConfiguration = SimulationConfigImpl(loader, 0, Time.INFINITY)
simulationConfiguration.dependencies shouldHaveSize 2
val test: File
WorkingDirectory().use { wd ->
test = File(wd.getFileAbsolutePath("nothing")).parentFile
test.exists().shouldBeTrue()
wd.writeFiles(simulationConfiguration.dependencies)
val newFile = File(wd.getFileAbsolutePath("test.txt"))
if (newFile.exists() || newFile.createNewFile()) {
ResourceLoader.addURL(wd.url)
ResourceLoader.getResource("test.txt").shouldNotBeNull()
} else {
error("File was not written in working directory")
}
}
test.exists().shouldBeFalse()
}
}) {
companion object {
private fun getLoader(yaml: URL) = LoadAlchemist.from(yaml)
}
}
4 changes: 1 addition & 3 deletions alchemist-grid/src/test/resources/config/00-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ incarnation: sapere

launcher:
type: DistributedExecution
parameters:
variables: []
distributedConfigPath: config/dependencies_test.txt
parameters: ["../distribution-config.yml", ["horizontalEnd", "verticalEnd"]]

remote-dependencies:
- config/dependencies_test.txt
Expand Down

0 comments on commit 359530e

Please sign in to comment.