Permalink
Browse files

Consolidate random port finding for specs

  • Loading branch information...
1 parent 08a35bb commit 2c14ee50435cc61c9b9d0006a6ec469ea3a564c0 @dchenbecker dchenbecker committed Apr 17, 2013
@@ -29,27 +29,35 @@ import org.streum.configrity.io.BlockFormat
import blueeyes.akka_testing.FutureMatchers
+import com.weiglewilczek.slf4s.Logging
+
import org.specs2.mutable.Specification
import org.specs2.specification.{Step, Fragments}
import org.specs2.time.TimeConversions._
import scalaz._
import scalaz.syntax.monad._
-class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpRequestMatchers with FutureMatchers {
+class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpRequestMatchers with FutureMatchers with PortFinder with Logging {
private val configPattern = """server { port = %d sslPort = %d }"""
val duration: org.specs2.time.Duration = 5000.milliseconds
- private val port = 8585
+ private val (port, sslPort) = findUnusedPorts(1000, 2) match {
+ case Some(p :: sp :: Nil) => (p, sp)
+ case _ => throw new Exception("Failed to find 2 unused ports for testing")
+ }
+
+ logger.info("Running HttpServerNettySpec with config " + configPattern.format(port, sslPort))
+
private var stop: Option[Stoppable] = None
private var config: Configuration = null
override def is = args(sequential = true) ^ super.is
override def map(fs: => Fragments) = {
def startStep = Step {
- val conf = Configuration.parse(configPattern.format(port, port + 1), BlockFormat)
+ val conf = Configuration.parse(configPattern.format(port, sslPort), BlockFormat)
val sampleServer = (new SampleServer).server(conf, defaultFutureDispatch)
config = sampleServer.config
@@ -58,8 +66,8 @@ class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpR
} getOrElse {
sys.error("No handlers available in service being started; aborting test.")
}
- }
-
+ }
+
def stopStep = Step {
TestEngineService.dataFile.delete
stop.foreach(Stoppable.stop(_, duration))
@@ -107,20 +115,20 @@ class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpR
}
"return NotFound when accessing a nonexistent URI" in {
- Await.result(client.post("/foo/foo/adCode.html")("foo"), duration) must beLike {
+ Await.result(client.post("/foo/foo/adCode.html")("foo"), duration) must beLike {
case HttpResponse(status, _, content, _) => status.code must_== NotFound
}
}
"return InternalServerError when handling request crashes" in {
- Await.result(client.get("/error"), duration) must beLike {
+ Await.result(client.get("/error"), duration) must beLike {
case HttpResponse(status, _, content, _) => status.code must_== InternalServerError
}
}
"return Http error when handling request throws HttpException" in {
- Await.result(client.get("/http/error"), duration) must beLike {
- case HttpResponse(status, _, content, _) => status.code must_== BadRequest
+ Await.result(client.get("/http/error"), duration) must beLike {
+ case HttpResponse(status, _, content, _) => status.code must_== BadRequest
}
}
@@ -129,7 +137,7 @@ class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpR
be_==(TestEngineService.content)
}
}
-
+
"return huge content" in {
Await.result(client.get[ByteChunk]("/huge"), 10.seconds) must beLike {
case HttpResponse(status, _, Some(content), _) =>
@@ -173,7 +181,7 @@ class HttpServerNettySpec extends Specification with TestAkkaDefaults with HttpR
private def client = new LocalClient(config).protocol("http").host("localhost").port(port).path("/sample/v1")
- private def sslClient = new LocalClient(config).protocol("https").host("localhost").port(port + 1).path("/sample/v1")
+ private def sslClient = new LocalClient(config).protocol("https").host("localhost").port(sslPort).path("/sample/v1")
}
class SampleServer extends BlueEyesServer with TestEngineService with TestAkkaDefaults {
@@ -1,5 +1,7 @@
package blueeyes.persistence.mongo
+import blueeyes.util.PortFinder
+
import com.mongodb.{Mongo => TGMongo, MongoOptions, ServerAddress}
import org.slf4j.LoggerFactory
@@ -8,7 +10,7 @@ import org.apache.commons.io.FileUtils
import com.google.common.io.Files
import java.io.{File, InputStream, IOException}
-import java.net.{InetSocketAddress, ServerSocket, URL}
+import java.net.URL
import java.util.concurrent.TimeUnit
import akka.dispatch.Await
@@ -18,16 +20,18 @@ import org.specs2.mutable._
import org.specs2.specification.{Fragments, Step}
import scala.sys.process._
-import scala.util.Random
import scalaz.effect.IO
-trait RealMongoSpecSupport extends Specification {
+trait RealMongoSpecSupport extends Specification with PortFinder {
private[this] var mongoImpl: Mongo = _
private[this] var realMongoImpl: TGMongo = _
lazy val mongoLogger = LoggerFactory.getLogger("blueeyes.persistence.mongo.RealMongoSpecSupport")
+ /** The max number of random ports to try */
+ def portTries = 1000
+
// Calling destroy on a process causes an IOException on the thread that reads its output
val processStreamLogger = (in: InputStream) =>
try { BasicIO.processFully(s => mongoLogger.debug(s))(in) } catch { case _: IOException => () }
@@ -50,42 +54,6 @@ trait RealMongoSpecSupport extends Specification {
private final val mongoDir = new File(workDir, "mongo")
private final val dataDir = new File(workDir, "mongo-data")
- /** The minimum random port number that can be used by the underlying mongo instance */
- def portRangeStart = 50000
-
- /** The size of the random port range to be used by the underlying mongo instance */
- def portRangeSize = 10000
-
- /** The max number of random ports to try */
- def portTries = 1000
-
- // Shamlessly borrowed from Camel:
- // http://svn.apache.org/viewvc/camel/trunk/components/camel-test/src/main/java/org/apache/camel/test/AvailablePortFinder.java?view=markup#l130
- private def isAvailable(port: Int): Boolean = {
- var ss: ServerSocket = null
- try {
- ss = new ServerSocket();
- ss.setReuseAddress(true);
- ss.bind(new InetSocketAddress(port))
- true
- } catch { case t: Exception =>
- false
- } finally {
- if (ss != null) {
- try {
- ss.close();
- } catch {
- case t => t.printStackTrace
- }
- }
- }
- }
-
- private def findUnusedPort(tries: Int): Option[Int] = {
- def randomPortStream: Stream[Int] = (Random.nextInt(portRangeSize) + portRangeStart) #:: randomPortStream
- randomPortStream.take(tries).find(isAvailable)
- }
-
def startup(): Unit = {
def mkDirs(d: File) = IO {
if (! (d.isDirectory || d.mkdirs())) {
@@ -103,23 +71,23 @@ trait RealMongoSpecSupport extends Specification {
} else {
"http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.2.0.tgz"
})
-
+
FileUtils.copyURLToFile(url, downloadFile)
}
mongoLogger.debug("Unpacking mongo")
- Process("tar",
- Seq("-C",
- targetDir.getCanonicalPath,
- "--strip-components",
- "1",
- "-x",
- "-v",
- "-z",
+ Process("tar",
+ Seq("-C",
+ targetDir.getCanonicalPath,
+ "--strip-components",
+ "1",
+ "-x",
+ "-v",
+ "-z",
"-f", downloadFile.getCanonicalPath)).!!(ProcessLogger(s => mongoLogger.debug(s)))
}
}
-
+
val io: IO[Unit] = for {
_ <- mkDirs(downloadDir)
_ <- mkDirs(mongoDir)
@@ -131,9 +99,9 @@ trait RealMongoSpecSupport extends Specification {
case Some(port) =>
mongoLogger.info("Starting mongo on port " + port)
mongoProcess = Some({
- val proc = Process(new File(mongoDir, "bin/mongod").getCanonicalPath, Seq("--port", port.toString,
+ val proc = Process(new File(mongoDir, "bin/mongod").getCanonicalPath, Seq("--port", port.toString,
"--dbpath", dataDir.getCanonicalPath,
- "--nojournal",
+ "--nojournal",
"--nounixsocket",
"--maxConns", maxMongoConns.toString,
"--nohttpinterface",
@@ -172,8 +140,8 @@ trait RealMongoSpecSupport extends Specification {
}
def shutdown(): Unit = {
- IO {
- Await.result(mongo.asInstanceOf[RealMongo].close, Timeout(60, TimeUnit.SECONDS).duration)
+ IO {
+ Await.result(mongo.asInstanceOf[RealMongo].close, Timeout(60, TimeUnit.SECONDS).duration)
}.ensuring {
IO {
mongoProcess.foreach { process =>
@@ -0,0 +1,46 @@
+package blueeyes.util
+
+import java.net.{InetSocketAddress, ServerSocket}
+
+import scala.util.Random
+
+import scalaz.syntax.std.boolean._
+
+trait PortFinder {
+ /** The minimum random port number that can be used by the underlying mongo instance */
+ def portRangeStart = 50000
+
+ /** The size of the random port range to be used by the underlying mongo instance */
+ def portRangeSize = 10000
+
+ // Shamlessly borrowed from Camel:
+ // http://svn.apache.org/viewvc/camel/trunk/components/camel-test/src/main/java/org/apache/camel/test/AvailablePortFinder.java?view=markup#l130
+ protected def isAvailable(port: Int): Boolean = {
+ var ss: ServerSocket = null
+ try {
+ ss = new ServerSocket();
+ ss.setReuseAddress(true);
+ ss.bind(new InetSocketAddress(port))
+ true
+ } catch { case t: Exception =>
+ false
+ } finally {
+ if (ss != null) {
+ try {
+ ss.close();
+ } catch {
+ case t => t.printStackTrace
+ }
+ }
+ }
+ }
+
+ protected def findUnusedPort(tries: Int): Option[Int] = findUnusedPorts(tries, 1).map(_.head)
+
+ protected def findUnusedPorts(tries: Int, count: Int): Option[List[Int]] = {
+ def randomPortStream: Stream[Int] = (Random.nextInt(portRangeSize) + portRangeStart) #:: randomPortStream
+ val result = randomPortStream.take(tries).filter(isAvailable).take(count)
+
+ (result.size == count).option(result.toList)
+ }
+}

0 comments on commit 2c14ee5

Please sign in to comment.