/* * sbt * Copyright 2011 - 2018, Lightbend, Inc. * Copyright 2008 - 2010, Mark Harrah * Licensed under Apache License 2.0 (see LICENSE) */ package testpkg import java.io.{ InputStream, OutputStream, PrintStream } import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit, TimeoutException } import sbt.internal.client.NetworkClient import sbt.internal.util.Util import scala.collection.mutable object ClientTest extends AbstractServerTest { override val testDirectory: String = "client" object NullInputStream extends InputStream { override def read(): Int = { try this.synchronized(this.wait) catch { case _: InterruptedException => } -1 } } val NullPrintStream = new PrintStream(_ => {}, false) class CachingPrintStream extends { val cos = new CachingOutputStream } with PrintStream(cos, true) { def lines = cos.lines } class CachingOutputStream extends OutputStream { private val byteBuffer = new mutable.ArrayBuffer[Byte] override def write(i: Int) = Util.ignoreResult(byteBuffer += i.toByte) def lines = new String(byteBuffer.toArray, "UTF-8").linesIterator.toSeq } class FixedInputStream(keys: Char*) extends InputStream { var i = 0 override def read(): Int = { if (i < keys.length) { val res = keys(i).toInt i += 1 res } else -1 } } private[this] def background[R](f: => R): R = { val result = new LinkedBlockingQueue[R] val thread = new Thread("client-bg-thread") { setDaemon(true) start() override def run(): Unit = result.put(f) } result.poll(1, TimeUnit.MINUTES) match { case null => thread.interrupt() thread.join(5000) throw new TimeoutException case r => r } } private def client(args: String*): Int = background( NetworkClient.client( testPath.toFile, args.toArray, NullInputStream, NullPrintStream, NullPrintStream, false ) ) def clientWithStdoutLines(args: String*): (Int, Seq[String]) = { val out = new CachingPrintStream val exitCode = background( NetworkClient.client( testPath.toFile, args.toArray, NullInputStream, out, NullPrintStream, false ) ) (exitCode, out.lines) } // This ensures that the completion command will send a tab that triggers // sbt to call definedTestNames or discoveredMainClasses if there hasn't // been a necessary compilation def tabs = new FixedInputStream('\t', '\t') private def complete(completionString: String): Seq[String] = { val cps = new CachingPrintStream background( NetworkClient.complete( testPath.toFile, Array(s"--completions=sbtn $completionString"), false, tabs, cps ) ) cps.lines } test("exit success") { c => assert(client("willSucceed") == 0) } test("exit failure") { _ => assert(client("willFail") == 1) } test("two commands") { _ => assert(client("compile;willSucceed") == 0) } test("two commands with failing second") { _ => assert(client("compile;willFail") == 1) } test("two commands with leading failure") { _ => assert(client("willFail;willSucceed") == 1) } test("three commands") { _ => assert(client("compile;clean;willSucceed") == 0) } test("three commands with middle failure") { _ => assert(client("compile;willFail;willSucceed") == 1) } test("compi completions") { _ => val expected = Vector( "compile", "compile:", "compileAnalysisFile", "compileAnalysisFilename", "compileAnalysisTargetRoot", "compileEarly", "compileIncSetup", "compileIncremental", "compileJava", "compileOrder", "compileOutputs", "compileProgress", "compileScalaBackend", "compileSplit", "compilerCache", "compilers", ) assert(complete("compi").toVector == expected) } test("testOnly completions") { _ => val testOnlyExpected = Vector( "testOnly", "testOnly/", "testOnly::", "testOnly;", ) assert(complete("testOnly") == testOnlyExpected) val testOnlyOptionsExpected = Vector("--", ";", "test.pkg.FooSpec") assert(complete("testOnly ") == testOnlyOptionsExpected) } test("quote with semi") { _ => assert(complete("\"compile; fooB") == Vector("compile; fooBar")) } }