diff --git a/src/main/scala/com/fortysevendeg/lambdatest/package.scala b/src/main/scala/com/fortysevendeg/lambdatest/package.scala index cc3d3b5..91aeacf 100644 --- a/src/main/scala/com/fortysevendeg/lambdatest/package.scala +++ b/src/main/scala/com/fortysevendeg/lambdatest/package.scala @@ -30,7 +30,13 @@ package object lambdatest { x.foldLeft[LambdaAct](LambdaAct(List()))((a, b) ⇒ a + b) } - private def pos(offset: Int = 0): String = { + /** + * Get a source position string. + * + * @param offset stack offset (default 0). + * @return a string representing the source position of the caller. + */ + def srcPos(offset: Int = 0): String = { val f = new Exception("foo").getStackTrace.apply(2 + offset) s"${f.getFileName} Line ${f.getLineNumber}" } @@ -40,7 +46,7 @@ package object lambdatest { * * @param name the name for the test. * @param body the test to be run. - * @param parallel an option to run top level actions in parallel. + * @param parallel an option to run top level actions in parallel (default false). * @param reporter an option to specify an alternate reporter. * @param change an option to change the options for the run. */ @@ -60,19 +66,20 @@ package object lambdatest { * @param test the boolean. * @param info a string to be reported. * @param showOk an option that if false supresses the output for success. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ - def assert(test: ⇒ Boolean, info: ⇒ String = "", showOk: Boolean = true): LambdaAct = { - val p = pos() + def assert(test: ⇒ Boolean, info: ⇒ String = "", + showOk: Boolean = true, pos: String = srcPos()): LambdaAct = { SingleLambdaAct(t ⇒ try { if (test) { - if (showOk) t.success(info, p) else t + if (showOk) t.success(info, pos) else t } else { - t.fail(info, p) + t.fail(info, pos) } } catch { case ex: Exception ⇒ - t.unExpected(ex, p) + t.unExpected(ex, pos) }) } @@ -84,22 +91,23 @@ package object lambdatest { * @param b the second value. * @param info a string to be reported. * @param showOk an option that if false supresses the output for success. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ - def assertEq[T](a: ⇒ T, b: ⇒ T, info: ⇒ String = "", showOk: Boolean = true): LambdaAct = { - val p = pos() + def assertEq[T](a: ⇒ T, b: ⇒ T, info: ⇒ String = "", + showOk: Boolean = true, pos: String = srcPos()): LambdaAct = { SingleLambdaAct(t ⇒ try { val a1 = a val info0 = if (info == "") "" else s" ($info)" val info1 = s"[$a1] $info" if (a1 == b) { - if (showOk) t.success(info1, p) else t + if (showOk) t.success(info1, pos) else t } else { - t.fail(s"[$a1 != $b] $info", p) + t.fail(s"[$a1 != $b] $info", pos) } } catch { case ex: Exception ⇒ - t.unExpected(ex, p) + t.unExpected(ex, pos) }) } @@ -111,26 +119,27 @@ package object lambdatest { * @param showOk an option that if false supresses the output for success. * @param check a function that further checks the exception. It returns None if the the check passes and * Some(msg) if the check fails (where msg desribes the failure). + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ def assertEx( test: ⇒ Unit, info: ⇒ String = "", check: Exception ⇒ Option[String] = (ex: Exception) ⇒ None, - showOk: Boolean = true + showOk: Boolean = true, + pos: String = srcPos() ): LambdaAct = { - val p = pos() val info1 = if (info == "") "" else s" ($info)" SingleLambdaAct(t ⇒ try { test - t.fail(s"Expected exception not raised$info1", p) + t.fail(s"Expected exception not raised$info1", pos) } catch { case ex: Exception ⇒ check(ex) match { case None ⇒ - if (showOk) t.success(info, p) else t + if (showOk) t.success(info, pos) else t case Some(s) ⇒ - t.fail(s"Exception fails check ($s$info1)", p) + t.fail(s"Exception fails check ($s$info1)", pos) } }) } @@ -145,10 +154,13 @@ package object lambdatest { * @param params * @param showOk an option that if false supresees the output for success. * @param prop the ScalaCheck property to be checked. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ - def assertSC(params: Test.Parameters = Test.Parameters.default, showOk: Boolean = true)(prop: org.scalacheck.Prop): LambdaAct = { - val p = pos() + def assertSC( + params: Test.Parameters = Test.Parameters.default, + showOk: Boolean = true, pos: String = srcPos() + )(prop: org.scalacheck.Prop): LambdaAct = { val resP = Promise[Test.Result] object cb extends TestCallback { override def onTestResult(name: String, res: Test.Result) = { @@ -168,10 +180,10 @@ package object lambdatest { SingleLambdaAct(t ⇒ try { val param = params.withTestCallback(cb) Test.check(param, prop) - cb.out(t, p) + cb.out(t, pos) } catch { case ex: Exception ⇒ - t.unExpected(ex, p) + t.unExpected(ex, pos) }) } @@ -179,50 +191,62 @@ package object lambdatest { * A compund action that defines a single test. * * @param name the name of the test. - * @param parallel if true, run top level actions in body in parallel. + * @param parallel if true, run top level actions in body in parallel (default false). * @param tags tags for this test (default empty). + * @param pos the source position (usually defaulted). * @param body the actions inside the test. * @return the LambdaAct. */ - def test(name: String, parallel: Boolean = false, tags: Set[String] = Set.empty[String])(body: ⇒ LambdaAct): LambdaAct = { - val p = pos() - SingleLambdaAct(t ⇒ if (t.options.checkTags(tags)) t.test(name, body, parallel, p) else t) + def test(name: String, parallel: Boolean = false, + tags: Set[String] = Set.empty[String], + pos: String = srcPos())(body: ⇒ LambdaAct): LambdaAct = { + SingleLambdaAct(t ⇒ if (t.options.checkTags(tags)) t.test(name, body, parallel, pos) else t) } /** * A compund action that defines a labeled block of code. * * @param name the name of the label. Default, no label line output and body is not nested. - * @param parallel if true, run top level actions in body in parallel. + * @param parallel if true, run top level actions in body in parallel (default false). * @param tags tags for this label (default empty). + * @param pos the source position (usually defaulted). * @param body the actions inside the label. * @return the LambdaAct. */ - def label(name: String = "", parallel: Boolean = false, tags: Set[String] = Set.empty[String])(body: ⇒ LambdaAct): LambdaAct = { - val p = pos() - SingleLambdaAct(t ⇒ if (t.options.checkTags(tags)) t.label(name, body, parallel, p) else t) + def label(name: String = "", parallel: Boolean = false, + tags: Set[String] = Set.empty[String], pos: String = srcPos())(body: ⇒ LambdaAct): LambdaAct = { + SingleLambdaAct(t ⇒ if (t.options.checkTags(tags)) t.label(name, body, parallel, pos) else t) } - def nest(body: ⇒ LambdaAct): LambdaAct = { - val p = pos() - SingleLambdaAct(t ⇒ t.label("", body, false, p)) + /** + * An action for nesting other actions. + * Nest does not increase indentation of the + * output if its body. + * Nest is useful for functional testing. + * + * @param body the nested actions. + * @param pos the source position (usually defaulted). + * @return the LambdaAct. + */ + def nest(body: ⇒ LambdaAct, pos: String = srcPos()): LambdaAct = { + SingleLambdaAct(t ⇒ t.label("", body, false, pos)) } /** * An action that executed the code in its body * * @param body the Scala code to be executed. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ - def exec[T](body: ⇒ Unit): LambdaAct = { + def exec[T](body: ⇒ Unit, pos: String = srcPos()): LambdaAct = { SingleLambdaAct(t ⇒ { - val p1 = pos() try { body t } catch { case ex: Exception ⇒ - t.unExpected(ex, p1) + t.unExpected(ex, pos) } }) } @@ -231,17 +255,17 @@ package object lambdatest { * Changes the options within its body. * * @param change a function to change the options. + * @param pos the source position (usually defaulted). * @param body the actions inside. * @return the LambdaAct. */ - def changeOptions(change: LambdaOptions ⇒ LambdaOptions)(body: ⇒ LambdaAct): LambdaAct = { - val p = pos() + def changeOptions(change: LambdaOptions ⇒ LambdaOptions, pos: String = srcPos())(body: ⇒ LambdaAct): LambdaAct = { SingleLambdaAct(t ⇒ try { val t1 = t.changeOptions(change) body.eval(t1) } catch { case ex: Exception ⇒ - t.unExpected(ex, p) + t.unExpected(ex, pos) }) } @@ -263,10 +287,10 @@ package object lambdatest { * * @param info a string to be reported * @param body the actions to be timed. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ - def timer(info: String)(body: ⇒ LambdaAct): LambdaAct = { - val p = pos() + def timer(info: String, pos: String = srcPos())(body: ⇒ LambdaAct): LambdaAct = { SingleLambdaAct { t ⇒ val time0 = System.nanoTime() @@ -287,6 +311,7 @@ package object lambdatest { * @param mean the assertion fails if the mean exceeds this value. * @param max the assertion fails if the max exceeds this value. * @param timeout the maximum time to wait for the body execution to complete. + * @param pos the source position (usually defaulted). * @param body the code to be timed. * @return the LambdaAct. */ @@ -296,9 +321,9 @@ package object lambdatest { repeat: Int = 100, mean: FiniteDuration = 10 millis, max: FiniteDuration = 15 millis, - timeout: FiniteDuration = 1 second + timeout: FiniteDuration = 1 second, + pos: String = srcPos() )(body: ⇒ Unit): LambdaAct = { - val p = pos() import scala.concurrent.ExecutionContext.Implicits.global SingleLambdaAct { @@ -324,15 +349,15 @@ package object lambdatest { }] $info" if (meanMicros.micros > mean) { - t.fail(s"exceeds mean $info1 $meanMicros.micros $mean", p) + t.fail(s"exceeds mean $info1 $meanMicros.micros $mean", pos) } else if (maxMicros.micros > max) { - t.fail(s"exceeds max $info1", p) + t.fail(s"exceeds max $info1", pos) } else { - t.success(s"$info1", p) + t.success(s"$info1", pos) } } catch { - case ex: TimeoutException ⇒ t.fail(s"timeout: $info", p) - case ex: Exception ⇒ t.unExpected(ex, p) + case ex: TimeoutException ⇒ t.fail(s"timeout: $info", pos) + case ex: Exception ⇒ t.unExpected(ex, pos) } } } @@ -344,15 +369,16 @@ package object lambdatest { * @param info a string to be reported. * @param max the assertion fails if the body takes longer than this. * @param timeout the maximum time to wait for the body execution to complete. + * @param pos the source position (usually defaulted). * @return the LambdaAct. */ def assertTiming(body: ⇒ LambdaAct)( info: String, max: FiniteDuration = 100 millis, - timeout: FiniteDuration = 1 second + timeout: FiniteDuration = 1 second, + pos: String = srcPos() ): LambdaAct = { import scala.concurrent.ExecutionContext.Implicits.global - val p = pos() SingleLambdaAct { case t ⇒ @@ -365,14 +391,14 @@ package object lambdatest { val part: Int = ((elapsed / max) * 100).toInt val times = s"$elapsed $part%" r match { - case Failure(ex) ⇒ t.fail(s"exceeded timeout [$times] $info", p) + case Failure(ex) ⇒ t.fail(s"exceeded timeout [$times] $info", pos) case Success(Success(t1: LambdaState)) ⇒ if (micros.micros > max) { - t1.fail(s"exceeded max [$times] $info", p) + t1.fail(s"exceeded max [$times] $info", pos) } else { - t1.success(s"[$times] $info", p) + t1.success(s"[$times] $info", pos) } - case Success(Failure(ex: Throwable)) ⇒ t.unExpected(ex, p) + case Success(Failure(ex: Throwable)) ⇒ t.unExpected(ex, pos) } } }