From f139e5a9c18f40083ffbf46827284dab4b62cb9a Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 4 Dec 2009 21:31:03 -0500 Subject: [PATCH] Changes to assist with scripted testing of sbt 0.6.x series --- project/build.properties | 4 +- scripted/ScriptRunner.scala | 30 +++++++++++--- scripted/StatementHandler.scala | 8 ++++ tasks/standard/Compile.scala | 1 + util/io/IPC.scala | 72 +++++++++++++++++++++++++++++++++ util/io/OpenResource.scala | 2 +- 6 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 util/io/IPC.scala diff --git a/project/build.properties b/project/build.properties index ba5c7a046..bdbebb96c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,7 +1,5 @@ -#Project properties -#Sat Nov 14 17:25:10 EST 2009 project.organization=org.scala-tools.sbt project.name=xsbt sbt.version=0.5.6 -project.version=0.6.4 +project.version=0.6.5-p1 scala.version=2.7.5 diff --git a/scripted/ScriptRunner.scala b/scripted/ScriptRunner.scala index 64be5c23b..b59a14205 100644 --- a/scripted/ScriptRunner.scala +++ b/scripted/ScriptRunner.scala @@ -13,9 +13,9 @@ class ScriptRunner def apply(statements: List[(StatementHandler, Statement)]) { val states = new HashMap[StatementHandler, Any] - def processStatement(handler: StatementHandler, statement: Statement) - { - val state = states.getOrElseUpdate(handler, handler.initialState).asInstanceOf[handler.State] + def processStatement(handler: StatementHandler, statement: Statement) + { + val state = states(handler).asInstanceOf[handler.State] val nextState = try { Right( handler(statement.command, statement.arguments, state) ) } catch { case e: Exception => Left(e) } @@ -23,7 +23,12 @@ class ScriptRunner { case Left(err) => if(statement.successExpected) - throw new TestException(statement, "Command failed", err) + { + err match { + case t: TestFailed => throw new TestException(statement, "Command failed: " + t.getMessage, null) + case _ => throw new TestException(statement, "Command failed", err) + } + } else () case Right(s) => @@ -33,7 +38,20 @@ class ScriptRunner throw new TestException(statement, "Command succeeded but failure was expected", null) } } - statements.foreach { case (handler, _) => states(handler) = handler.initialState } - statements foreach( Function.tupled(processStatement) ) + val handlers = Set() ++ statements.map(_._1) + + try + { + handlers.foreach { handler => states(handler) = handler.initialState } + statements foreach( Function.tupled(processStatement) ) + } + finally + { + for(handler <- handlers; state <- states.get(handler)) + { + try { handler.finish(state.asInstanceOf[handler.State]) } + catch { case e: Exception => () } + } + } } } \ No newline at end of file diff --git a/scripted/StatementHandler.scala b/scripted/StatementHandler.scala index 36cdec006..9b339f037 100644 --- a/scripted/StatementHandler.scala +++ b/scripted/StatementHandler.scala @@ -5,6 +5,7 @@ trait StatementHandler type State def initialState: State def apply(command: String, arguments: List[String], state: State): State + def finish(state: State): Unit } trait BasicStatementHandler extends StatementHandler @@ -13,4 +14,11 @@ trait BasicStatementHandler extends StatementHandler final def initialState = () final def apply(command: String, arguments: List[String], state: Unit): Unit= apply(command, arguments) def apply(command: String, arguments: List[String]): Unit + def finish(state: Unit) = () +} + +/** Use when a stack trace is not useful */ +final class TestFailed(msg: String) extends RuntimeException(msg) +{ + override def fillInStackTrace = this } \ No newline at end of file diff --git a/tasks/standard/Compile.scala b/tasks/standard/Compile.scala index 9d3175d66..0b1210cfd 100644 --- a/tasks/standard/Compile.scala +++ b/tasks/standard/Compile.scala @@ -114,6 +114,7 @@ class StandardCompile(val sources: Task[Set[File]], val classpath: Task[Set[File def jarDependency(jar: File, source: File) { tracking.use(source, jar) } def classDependency(clazz: File, source: File) { tracking.dependency(source, clazz) } def generatedClass(source: File, clazz: File) { tracking.product(source, clazz) } + def api(source: File, api: xsbti.api.Source) = () } } diff --git a/util/io/IPC.scala b/util/io/IPC.scala new file mode 100644 index 000000000..d0ccf7b03 --- /dev/null +++ b/util/io/IPC.scala @@ -0,0 +1,72 @@ +/* sbt -- Simple Build Tool + * Copyright 2009 Mark Harrah + */ +package xsbt + +import java.io.{BufferedReader, BufferedWriter, InputStream, InputStreamReader, OutputStreamWriter, OutputStream} +import java.net.{InetAddress, ServerSocket, Socket} + +object IPC +{ + private val portMin = 1025 + private val portMax = 65536 + private val loopback = InetAddress.getByName(null) // loopback + + def client[T](port: Int)(f: IPC => T): T = + ipc(new Socket(loopback, port))(f) + + def pullServer[T](f: Server => T): T = + { + val server = makeServer + try { f(new Server(server)) } + finally { server.close() } + } + def makeServer: ServerSocket = + { + val random = new java.util.Random + def nextPort = random.nextInt(portMax - portMin + 1) + portMin + def createServer(attempts: Int): ServerSocket = + if(attempts > 0) + try { new ServerSocket(nextPort, 1, loopback) } + catch { case _: Exception => createServer(attempts - 1) } + else + error("Could not connect to socket: maximum attempts exceeded") + createServer(10) + } + def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f) + def server[T](port: Int)(f: IPC => Option[T]): T = + serverImpl(new ServerSocket(port, 1, loopback), f) + private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = + { + def listen(): T = + { + ipc(server.accept())(f) match + { + case Some(done) => done + case None => listen() + } + } + + try { listen() } + finally { server.close() } + } + private def ipc[T](s: Socket)(f: IPC => T): T = + try { f(new IPC(s)) } + finally { s.close() } + + final class Server private[IPC](s: ServerSocket) extends NotNull + { + def port = s.getLocalPort + def close() = s.close() + def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f) + } +} +final class IPC private(s: Socket) extends NotNull +{ + def port = s.getLocalPort + private val in = new BufferedReader(new InputStreamReader(s.getInputStream)) + private val out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream)) + + def send(s: String) = { out.write(s); out.newLine(); out.flush() } + def receive: String = in.readLine() +} \ No newline at end of file diff --git a/util/io/OpenResource.scala b/util/io/OpenResource.scala index 4ce10d3ed..d7a052a28 100644 --- a/util/io/OpenResource.scala +++ b/util/io/OpenResource.scala @@ -59,7 +59,7 @@ object OpenResource def resource[Source, T <: Closeable](openF: Source => T): OpenResource[Source,T] = resource(openF, _.close) - def resource[Source, T <: Closeable](openF: Source => T, closeF: T => Unit): OpenResource[Source,T] = + def resource[Source, T](openF: Source => T, closeF: T => Unit): OpenResource[Source,T] = new OpenResource[Source,T] { def open(s: Source) = openF(s)