From e1103d15574106f05e91e203aafcb2267117b6d3 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sun, 28 Jun 2020 14:14:05 -0700 Subject: [PATCH] Fix TestServer for windows There were two issues with the server tests on windows 1) Sometimes client connections fail on the first attempt but will succeed with retries 2) It is possible for named pipes to leak which causes subsequent tests to be unable to run 3) ClientTest did not handle lines with carriage returns correctly Issue #1 is fixed with retries. Issue #2 is fixed by using a unique temp directory for each test run. Issue #3 was fixed by simplifying the output stream cache to only cache the bytes returned and then letting scala do the line splitting for us in linesIterator. After these changes, all of the server tests work on appveyor. --- .appveyor.yml | 2 +- .../src/test/scala/testpkg/ClientTest.scala | 12 +++------ .../src/test/scala/testpkg/TestServer.scala | 25 ++++++++++++++----- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f9277ae44..0c3f595a1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -155,4 +155,4 @@ for: - '%USERPROFILE%\.sbt' test_script: - - sbt "scripted actions/* classloader-cache/* nio/* watch/*" "serverTestProj/testOnly serverTestProj/testOnly testpkg.HandshakeTest" + - sbt "scripted actions/* classloader-cache/* nio/* watch/*" "serverTestProj/test" diff --git a/server-test/src/test/scala/testpkg/ClientTest.scala b/server-test/src/test/scala/testpkg/ClientTest.scala index dc85e05ef..145f12e0f 100644 --- a/server-test/src/test/scala/testpkg/ClientTest.scala +++ b/server-test/src/test/scala/testpkg/ClientTest.scala @@ -27,15 +27,9 @@ object ClientTest extends AbstractServerTest { def lines = cos.lines } class CachingOutputStream extends OutputStream { - private val lineBuffer = new mutable.ArrayBuffer[String] - private var byteBuffer = new mutable.ArrayBuffer[Byte] - override def write(i: Int) = { - if (i == 10) { - lineBuffer += new String(byteBuffer.toArray) - byteBuffer = new mutable.ArrayBuffer[Byte] - } else Util.ignoreResult(byteBuffer += i.toByte) - } - def lines = lineBuffer.toVector + 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 diff --git a/server-test/src/test/scala/testpkg/TestServer.scala b/server-test/src/test/scala/testpkg/TestServer.scala index 86c0d5e56..b5a45abdb 100644 --- a/server-test/src/test/scala/testpkg/TestServer.scala +++ b/server-test/src/test/scala/testpkg/TestServer.scala @@ -8,7 +8,8 @@ package testpkg import java.io.{ File, IOException } -import java.nio.file.Path +import java.net.Socket +import java.nio.file.{ Files, Path } import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit } import java.util.concurrent.atomic.AtomicBoolean @@ -37,10 +38,11 @@ trait AbstractServerTest extends TestSuite[Unit] { } override def setupSuite(): Unit = { - temp = targetDir / "test-server" / testDirectory - if (temp.exists) { - IO.delete(temp) - } + val base = Files.createTempDirectory( + Files.createDirectories(targetDir.toPath.resolve("test-server")), + "server-test" + ) + temp = base.toFile val classpath = sys.props.get("sbt.server.classpath") match { case Some(s: String) => s.split(java.io.File.pathSeparator).map(file) case _ => throw new IllegalStateException("No server classpath was specified.") @@ -191,8 +193,19 @@ case class TestServer( hostLog(s"wait $waitDuration until the server is ready to respond") waitForPortfile(waitDuration) + @tailrec + private def connect(attempt: Int): Socket = { + val res = try Some(ClientSocket.socket(portfile)._1) + catch { case _: IOException if attempt < 10 => None } + res match { + case Some(s) => s + case _ => + Thread.sleep(100) + connect(attempt + 1) + } + } // make connection to the socket described in the portfile - val (sk, _) = ClientSocket.socket(portfile) + val sk = connect(0) val out = sk.getOutputStream val in = sk.getInputStream private val lines = new LinkedBlockingQueue[String]