From 8e42c6f340b2a35bfb5c08df116a1e88de052eb7 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Sat, 3 Dec 2016 22:36:23 -0800 Subject: Add test suite --- .../scala/ch/jodersky/flow/PseudoTerminal.scala | 43 ++++++++++++++++++ .../scala/ch/jodersky/flow/SerialManagerSpec.scala | 38 ++++++++++++++++ .../ch/jodersky/flow/SerialOperatorSpec.scala | 53 ++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 flow-core/src/test/scala/ch/jodersky/flow/PseudoTerminal.scala create mode 100644 flow-core/src/test/scala/ch/jodersky/flow/SerialManagerSpec.scala create mode 100644 flow-core/src/test/scala/ch/jodersky/flow/SerialOperatorSpec.scala (limited to 'flow-core/src/test/scala/ch/jodersky/flow') 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) + + } + + } + +} -- cgit v1.2.3