From 30c3990d25293c01a380fc8715679e7f618a4cae Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Sun, 9 Apr 2017 18:16:20 -0700 Subject: Add test for builders --- crashboxd/src/main/scala/io/crashbox/ci/defs.scala | 2 +- .../test/scala/io/crashbox/ci/BuildStageSpec.scala | 122 ++++++++++++++++----- .../scala/io/crashbox/ci/DockerExecutorSpec.scala | 5 +- 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/crashboxd/src/main/scala/io/crashbox/ci/defs.scala b/crashboxd/src/main/scala/io/crashbox/ci/defs.scala index 24ed530..a17d713 100644 --- a/crashboxd/src/main/scala/io/crashbox/ci/defs.scala +++ b/crashboxd/src/main/scala/io/crashbox/ci/defs.scala @@ -1,6 +1,6 @@ package io.crashbox.ci -sealed trait Environment +trait Environment case class DockerEnvironment(image: String) extends Environment case class TaskDef[+E <: Environment](environment: E, script: String) diff --git a/crashboxd/src/test/scala/io/crashbox/ci/BuildStageSpec.scala b/crashboxd/src/test/scala/io/crashbox/ci/BuildStageSpec.scala index eb77c40..dd79f72 100644 --- a/crashboxd/src/test/scala/io/crashbox/ci/BuildStageSpec.scala +++ b/crashboxd/src/test/scala/io/crashbox/ci/BuildStageSpec.scala @@ -1,55 +1,123 @@ package io.crashbox.ci +import akka.NotUsed import akka.actor.ActorSystem import akka.stream.scaladsl.Keep import akka.stream.{ ClosedShape, KillSwitch } import akka.stream.scaladsl.{ GraphDSL, RunnableGraph, Sink, Source } import akka.stream.{ ActorMaterializer, FanInShape2 } -import java.io.{ ByteArrayOutputStream, File } +import java.io.{ ByteArrayOutputStream, File, OutputStream } import java.nio.file.Files import org.scalatest._ -import scala.concurrent.Await +import scala.concurrent.{ Await, Future } import scala.concurrent.duration._ +import Builder._ + class BuildStageSpec extends FlatSpec with Matchers with BeforeAndAfterAll { implicit val system = ActorSystem("crashboxd-buildstage") implicit val materializer = ActorMaterializer() - val executor = new DockerExecutor + import system.dispatcher + + case class DummyEnv() extends Environment { + val id = DummyId() + } + + case class DummyId() extends ExecutionId { + import DummyId._ + private var _state: State = Starting + def state = _state.synchronized{ _state} + def state_=(value: State) = _state.synchronized{_state = value} - override def beforeAll(): Unit = { - DockerUtil.ensureImage(executor.dockerClient) } + object DummyId { + sealed trait State + case object Starting extends State + case object Running extends State + case object Result extends State + case object Stopped extends State + } + + class DummyExecutor( + startDelay: Duration = 0.seconds, + resultDelay: Duration = 0.seconds, + stopDelay: Duration = 0.seconds + ) extends Executor[DummyEnv, DummyId] { + + override def start(env: DummyEnv, script: String, dir: File, out: OutputStream) = Future { + Thread.sleep(startDelay.toMillis) + env.id.state = DummyId.Running + env.id + } + + override def result(id: DummyId) = Future { + Thread.sleep(resultDelay.toMillis) + id.state = DummyId.Result + 0 + } + + override def stop(id: DummyId) = { + Thread.sleep(stopDelay.toMillis) + id.state = DummyId.Stopped + } - override def afterAll(): Unit = { - assert(executor.clean(), "Spawned containers were not removed") - system.terminate() } + def dummySource( + executor: DummyExecutor, + env: DummyEnv + ): Source[BuildState, NotUsed] = { + val stage = new BuildSource( + TaskId("dummy", 0), + TaskDef(env, ""), + executor, + new File("nonexistant"), + new ByteArrayOutputStream(0) + ) + Source.fromGraph(stage) + } - "BuildStage" should "have a test!" in { - IOUtil.withTemp{ (dir, out) => + "BuildStage" should "transition states and emit in the correct order" in { + val delay = 0.5.seconds - val taskDef = TaskDef(DockerEnvironment("crashbox"), "sleep 10; exit 0") - val resultSink = Sink.foreach[Builder.BuildState](x => println(x)) + val executor = new DummyExecutor(delay, delay, delay) + val env = new DummyEnv() - val stage = new BuildSource( - TaskId("build", 0), - taskDef, - executor, - dir, - out - ) - val src = Source.fromGraph(stage) + val taskId = TaskId("dummy", 0) + val taskDef = TaskDef(env, "dummy script") + val stage = new BuildSource( + taskId, + taskDef, + executor, + new File("nonexistant"), + new ByteArrayOutputStream(0) + ) + val source = Source.fromGraph(stage) + val eventFuture = source.toMat(Sink.seq)(Keep.right).run() - //val done = src.toMat(resultSink)(Keep.right).run() + Thread.sleep((delay / 2).toMillis) + assert(env.id.state == DummyId.Starting) + + Thread.sleep(delay.toMillis) + assert(env.id.state == DummyId.Running) + + Thread.sleep(delay.toMillis) + assert(env.id.state == DummyId.Result) + + Thread.sleep(delay.toMillis) + assert(env.id.state == DummyId.Stopped) + + + val expectedEvents = Seq( + TaskStarting(taskId, taskDef), + TaskRunning(taskId, env.id), + TaskFinished(taskId, 0) + ) + val events = Await.result(eventFuture, 10.seconds) + assert(events.toList === expectedEvents.toList) - //executor.start("crashbox", "sleep 10000", dir, out) - Thread.sleep(1000) - assert(executor.clean()) - //Await.ready(done, 30.seconds) - println("eot") - } } + } diff --git a/crashboxd/src/test/scala/io/crashbox/ci/DockerExecutorSpec.scala b/crashboxd/src/test/scala/io/crashbox/ci/DockerExecutorSpec.scala index 3c47edf..6794908 100644 --- a/crashboxd/src/test/scala/io/crashbox/ci/DockerExecutorSpec.scala +++ b/crashboxd/src/test/scala/io/crashbox/ci/DockerExecutorSpec.scala @@ -31,14 +31,11 @@ class DockerExecutorSpec val exec = new DockerExecutor override def beforeAll: Unit = { - sys.addShutdownHook { - println("------------------- fooooo") - exec.clean() - } DockerUtil.ensureImage(exec.dockerClient) } override def afterAll: Unit = { + exec.clean() // in case something goes wrong system.terminate() } -- cgit v1.2.3