aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt1
-rw-r--r--flow-core/build.sbt2
-rw-r--r--flow-core/src/test/scala/ch/jodersky/flow/PseudoTerminal.scala43
-rw-r--r--flow-core/src/test/scala/ch/jodersky/flow/SerialManagerSpec.scala38
-rw-r--r--flow-core/src/test/scala/ch/jodersky/flow/SerialOperatorSpec.scala53
-rw-r--r--flow-stream/build.sbt2
-rw-r--r--flow-stream/src/test/scala/ch/jodersky/flow/stream/SerialSpec.scala51
-rw-r--r--project/Dependencies.scala4
-rw-r--r--project/FlowBuild.scala5
9 files changed, 195 insertions, 4 deletions
diff --git a/build.sbt b/build.sbt
index 91efbd3..2f895fb 100644
--- a/build.sbt
+++ b/build.sbt
@@ -37,3 +37,4 @@ scalacOptions in (ScalaUnidoc, doc) ++= Seq(
siteMappings ++= (mappings in (ScalaUnidoc, packageDoc)).value.map{ case (file, path) =>
(file, "api/" + path)
}
+fork := true
diff --git a/flow-core/build.sbt b/flow-core/build.sbt
index 487afe0..40eb978 100644
--- a/flow-core/build.sbt
+++ b/flow-core/build.sbt
@@ -3,5 +3,7 @@ import flow.{FlowBuild, Dependencies}
FlowBuild.commonSettings
libraryDependencies += Dependencies.akkaActor
+libraryDependencies += Dependencies.akkaTestKit
+libraryDependencies += Dependencies.scalatest
target in javah := (baseDirectory in ThisBuild).value / "flow-native" / "src" / "include"
diff --git a/flow-core/src/test/scala/ch/jodersky/flow/PseudoTerminal.scala b/flow-core/src/test/scala/ch/jodersky/flow/PseudoTerminal.scala
new file mode 100644
index 0000000..8e891ae
--- /dev/null
+++ b/flow-core/src/test/scala/ch/jodersky/flow/PseudoTerminal.scala
@@ -0,0 +1,43 @@
+package ch.jodersky.flow
+
+import java.io.{File, IOException}
+import java.nio.file.Files
+
+import scala.concurrent.duration._
+import scala.sys.process._
+import scala.util.control.NonFatal
+
+trait PseudoTerminal {
+
+ final val SetupTimeout = 100.milliseconds
+
+ def withEcho[A](action: (String, SerialSettings) => A): A = {
+ val dir = Files.createTempDirectory("flow-pty").toFile
+ val pty = new File(dir, "pty")
+
+ val socat = try {
+ val s = Seq(
+ "socat",
+ "-d -d",
+ s"exec:cat,pty,raw,b115200,echo=0",
+ s"pty,raw,b115200,echo=0,link=${pty.getAbsolutePath}"
+ ).run(ProcessLogger(println(_)), false)
+ Thread.sleep(SetupTimeout.toMillis) // allow ptys to set up
+ s
+ } catch {
+ case NonFatal(ex) =>
+ throw new IOException(
+ "Error running echo service, make sure the program 'socat' is installed", ex)
+ }
+
+ try {
+ val result = action(pty.getAbsolutePath, SerialSettings(baud = 115200))
+ Thread.sleep(SetupTimeout.toMillis) // allow for async cleanup before destroying ptys
+ result
+ } finally {
+ socat.destroy()
+ dir.delete()
+ }
+ }
+
+}
diff --git a/flow-core/src/test/scala/ch/jodersky/flow/SerialManagerSpec.scala b/flow-core/src/test/scala/ch/jodersky/flow/SerialManagerSpec.scala
new file mode 100644
index 0000000..59af305
--- /dev/null
+++ b/flow-core/src/test/scala/ch/jodersky/flow/SerialManagerSpec.scala
@@ -0,0 +1,38 @@
+package ch.jodersky.flow
+
+import akka.actor.ActorSystem
+import akka.io.IO
+import akka.testkit.{ImplicitSender, TestKit}
+import org.scalatest._
+
+class SerialManagerSpec
+ extends TestKit(ActorSystem("serial-manager"))
+ with ImplicitSender
+ with WordSpecLike
+ with Matchers
+ with BeforeAndAfterAll
+ with PseudoTerminal {
+
+ override def afterAll {
+ TestKit.shutdownActorSystem(system)
+ }
+
+ "Serial manager" should {
+ val manager = IO(Serial)
+
+ "open an existing port" in {
+ withEcho{ case (port, settings) =>
+ manager ! Serial.Open(port, settings)
+ expectMsgType[Serial.Opened]
+ }
+ }
+
+ "fail opening a non-existing port" in {
+ val cmd = Serial.Open("nonexistent", SerialSettings(115200))
+ manager ! cmd
+ assert(expectMsgType[Serial.CommandFailed].command == cmd)
+ }
+
+ }
+
+}
diff --git a/flow-core/src/test/scala/ch/jodersky/flow/SerialOperatorSpec.scala b/flow-core/src/test/scala/ch/jodersky/flow/SerialOperatorSpec.scala
new file mode 100644
index 0000000..4a6cf1e
--- /dev/null
+++ b/flow-core/src/test/scala/ch/jodersky/flow/SerialOperatorSpec.scala
@@ -0,0 +1,53 @@
+package ch.jodersky.flow
+
+import scala.concurrent.duration._
+
+import akka.actor.{ActorRef, ActorSystem}
+import akka.testkit.{ImplicitSender, TestKit}
+import akka.util.ByteString
+import org.scalatest._
+
+case class Ack(n: Int) extends Serial.Event
+
+class SerialOperatorSpec
+ extends TestKit(ActorSystem("serial-operator"))
+ with ImplicitSender
+ with WordSpecLike
+ with Matchers
+ with BeforeAndAfterAll
+ with SequentialNestedSuiteExecution
+ with PseudoTerminal {
+
+ override def afterAll {
+ TestKit.shutdownActorSystem(system)
+ }
+
+ def withEchoOp[A](action: ActorRef => A): A = {
+ withEcho { case (port, settings) =>
+ val connection = SerialConnection.open(port, settings)
+ val operator = system.actorOf(SerialOperator.apply(connection, 1024, testActor))
+ action(operator)
+ }
+ }
+
+ "Serial operator" should {
+
+ "follow the correct protocol" in withEchoOp { op =>
+ expectMsgType[Serial.Opened]
+
+ val data = ByteString("hello world".getBytes("utf-8"))
+ op ! Serial.Write(data)
+ expectMsg(Serial.Received(data))
+
+ op ! Serial.Write(data, n => Ack(n))
+ expectMsg(Serial.Received(data))
+ expectMsg(Ack(data.length))
+
+ op ! Serial.Close
+ expectMsg(Serial.Closed)
+
+ }
+
+ }
+
+}
diff --git a/flow-stream/build.sbt b/flow-stream/build.sbt
index 183c20f..f14190f 100644
--- a/flow-stream/build.sbt
+++ b/flow-stream/build.sbt
@@ -4,4 +4,4 @@ FlowBuild.commonSettings
libraryDependencies += Dependencies.akkaActor
libraryDependencies += Dependencies.akkaStream
-
+libraryDependencies += Dependencies.scalatest
diff --git a/flow-stream/src/test/scala/ch/jodersky/flow/stream/SerialSpec.scala b/flow-stream/src/test/scala/ch/jodersky/flow/stream/SerialSpec.scala
new file mode 100644
index 0000000..1a1ebdc
--- /dev/null
+++ b/flow-stream/src/test/scala/ch/jodersky/flow/stream/SerialSpec.scala
@@ -0,0 +1,51 @@
+package ch.jodersky.flow
+package stream
+
+import scala.concurrent.Await
+import scala.concurrent.duration._
+
+import akka.actor.ActorSystem
+import akka.stream.ActorMaterializer
+import akka.stream.scaladsl.{Keep, Sink, Source}
+import akka.util.ByteString
+import org.scalatest._
+
+class SerialSpec extends WordSpec with BeforeAndAfterAll with PseudoTerminal {
+
+ implicit val system = ActorSystem("flow-test")
+ implicit val materializer = ActorMaterializer()
+
+ override def afterAll {
+ system.terminate()
+ }
+
+ "Serial stream" should {
+ val data = ByteString(("hello world").getBytes("utf-8"))
+
+ "receive the same data it sends in an echo test" in {
+ withEcho { case (port, settings) =>
+ val graph = Source.single(data)
+ .via(Serial().open(port, settings)) // send to echo pty
+ .scan(ByteString.empty)(_ ++ _) // received elements could potentially be split by OS
+ .dropWhile(_ != data)
+ .toMat(Sink.head)(Keep.right)
+
+ Await.result(graph.run(), 2.seconds)
+ }
+ }
+
+ "fail if the underlying pty fails" in {
+ val result = withEcho { case (port, settings) =>
+ Source.single(data)
+ .via(Serial().open(port, settings))
+ .toMat(Sink.last)(Keep.right)
+ .run()}
+
+ intercept[StreamSerialException] {
+ Await.result(result, 10.seconds)
+ }
+ }
+
+ }
+
+}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index b697f6d..f02b039 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -5,7 +5,9 @@ import sbt._
object Dependencies {
val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.4.14"
-
val akkaStream ="com.typesafe.akka" %% "akka-stream" % "2.4.14"
+ val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % "2.4.14" % "test"
+ val scalatest = "org.scalatest" %% "scalatest" % "3.0.1" % "test"
+
}
diff --git a/project/FlowBuild.scala b/project/FlowBuild.scala
index dcd4cdd..19042b6 100644
--- a/project/FlowBuild.scala
+++ b/project/FlowBuild.scala
@@ -42,14 +42,15 @@ object FlowBuild extends Build {
aggregate(core, native, stream)
lazy val core = (project in file("flow-core")).
- settings(name:= "flow-core")
+ settings(name:= "flow-core").
+ dependsOn(native % "test->runtime")
lazy val native = (project in file("flow-native")).
settings(name:= "flow-native")
lazy val stream = (project in file("flow-stream")).
settings(name:= "flow-stream").
- dependsOn(core)
+ dependsOn(core, core % "test->test", native % "test->runtime")
lazy val samplesTerminal = (project in file("flow-samples") / "terminal").
dependsOn(core, native % Runtime)