From 81d873a25985e76c64c2ae2693c2b670120d13a3 Mon Sep 17 00:00:00 2001 From: Jon Pretty Date: Tue, 27 Feb 2024 21:46:52 +0100 Subject: [PATCH] Code cleanup --- src/bench/benchmark.scala | 25 ++++++----- src/core/macros.scala | 83 +++++++++++++++++++++++-------------- src/core/probably.scala | 79 +++++++++++++++++++---------------- src/core/tolerance.scala | 4 +- src/coverage/coverage.scala | 15 ++++++- 5 files changed, 124 insertions(+), 82 deletions(-) diff --git a/src/bench/benchmark.scala b/src/bench/benchmark.scala index d3e10e2..2caf70d 100644 --- a/src/bench/benchmark.scala +++ b/src/bench/benchmark.scala @@ -22,15 +22,18 @@ import anticipation.* import scala.collection.mutable as scm extension [TestType](test: Test[TestType]) - inline def benchmark - [DurationType, ReportType] - (confidence: Optional[Benchmark.Percentiles] = Unset, iterations: Optional[Int] = Unset, - duration: Optional[DurationType] = Unset, warmup: Optional[DurationType] = Unset, - baseline: Optional[Baseline] = Unset) - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Benchmark], - specificDuration: SpecificDuration[DurationType] = timeInterfaces.long, - genericDuration: GenericDuration[DurationType] = timeInterfaces.long) - : Unit = + inline def benchmark[DurationType, ReportType] + ( confidence: Optional[Benchmark.Percentiles] = Unset, + iterations: Optional[Int] = Unset, + duration: Optional[DurationType] = Unset, + warmup: Optional[DurationType] = Unset, + baseline: Optional[Baseline] = Unset ) + ( using runner: Runner[ReportType], + inc: Inclusion[ReportType, Benchmark], + specificDuration: SpecificDuration[DurationType] = timeInterfaces.long, + genericDuration: GenericDuration[DurationType] = timeInterfaces.long ) + : Unit = + val action = test.action var end = System.currentTimeMillis + warmup.or(SpecificDuration(10000L)).milliseconds val times: scm.ArrayBuffer[Long] = scm.ArrayBuffer() @@ -61,7 +64,7 @@ extension [TestType](test: Test[TestType]) val variance: Double = (times.map { t => (mean - t)*(mean - t) }.sum)/count val stdDev: Double = math.sqrt(variance) - val benchmark = Benchmark(total, times.size, min.toDouble, mean, max.toDouble, stdDev, - confidence.or(95), baseline) + val benchmark = + Benchmark(total, times.size, min.toDouble, mean, max.toDouble, stdDev, confidence.or(95), baseline) inc.include(runner.report, test.id, benchmark) diff --git a/src/core/macros.scala b/src/core/macros.scala index bc98592..d99f9e2 100644 --- a/src/core/macros.scala +++ b/src/core/macros.scala @@ -26,13 +26,16 @@ import dotty.tools.dotc.util as dtdu import scala.quoted.* object Probably: - protected def general - [TestType: Type, ReportType: Type, ResultType: Type] - (test: Expr[Test[TestType]], predicate: Expr[TestType => Boolean], runner: Expr[Runner[ReportType]], - inc: Expr[Inclusion[ReportType, Outcome]], inc2: Expr[Inclusion[ReportType, DebugInfo]], - action: Expr[TestRun[TestType] => ResultType]) + protected def general[TestType: Type, ReportType: Type, ResultType: Type] + ( test: Expr[Test[TestType]], + predicate: Expr[TestType => Boolean], + runner: Expr[Runner[ReportType]], + inc: Expr[Inclusion[ReportType, Outcome]], + inc2: Expr[Inclusion[ReportType, DebugInfo]], + action: Expr[TestRun[TestType] => ResultType]) (using Quotes) - : Expr[ResultType] = + : Expr[ResultType] = + import quotes.reflect.* val exp: Option[Expr[Any]] = predicate.asTerm match @@ -53,49 +56,67 @@ object Probably: val contrast = '{Contrast.general[t | TestType]} //val contrast = Expr.summon[Contrast[t | TestType]].getOrElse('{Contrast.general[t | TestType]}) - '{ assertion[t | TestType, TestType, ReportType, ResultType]($runner, $test, $predicate, $action, $contrast, Some($expr), $inc, - $inc2, $debug) } + '{ + assertion[t | TestType, TestType, ReportType, ResultType] + ($runner, $test, $predicate, $action, $contrast, Some($expr), $inc, $inc2, $debug) + } case _ => - '{ assertion[TestType, TestType, ReportType, ResultType]($runner, $test, $predicate, $action, Contrast.nothing[TestType], None, $inc, - $inc2, TextConversion.any) } + '{ + assertion[TestType, TestType, ReportType, ResultType] + ($runner, $test, $predicate, $action, Contrast.nothing[TestType], None, $inc, $inc2, + TextConversion.any) + } - def check - [TestType: Type, ReportType: Type] - (test: Expr[Test[TestType]], predicate: Expr[TestType => Boolean], runner: Expr[Runner[ReportType]], - inc: Expr[Inclusion[ReportType, Outcome]], inc2: Expr[Inclusion[ReportType, DebugInfo]]) + def check[TestType: Type, ReportType: Type] + ( test: Expr[Test[TestType]], + predicate: Expr[TestType => Boolean], + runner: Expr[Runner[ReportType]], + inc: Expr[Inclusion[ReportType, Outcome]], + inc2: Expr[Inclusion[ReportType, DebugInfo]] ) (using Quotes) - : Expr[TestType] = + : Expr[TestType] = + general[TestType, ReportType, TestType](test, predicate, runner, inc, inc2, '{ (t: TestRun[TestType]) => t.get }) - def assert - [TestType: Type, ReportType: Type] - (test: Expr[Test[TestType]], predicate: Expr[TestType => Boolean], runner: Expr[Runner[ReportType]], - inc: Expr[Inclusion[ReportType, Outcome]], inc2: Expr[Inclusion[ReportType, DebugInfo]]) + def assert[TestType: Type, ReportType: Type] + ( test: Expr[Test[TestType]], + predicate: Expr[TestType => Boolean], + runner: Expr[Runner[ReportType]], + inc: Expr[Inclusion[ReportType, Outcome]], + inc2: Expr[Inclusion[ReportType, DebugInfo]] ) (using Quotes) : Expr[Unit] = general[TestType, ReportType, Unit](test, predicate, runner, inc, inc2, '{ (t: TestRun[TestType]) => () }) - def aspire - [TestType: Type, ReportType: Type] - (test: Expr[Test[TestType]], runner: Expr[Runner[ReportType]], inc: Expr[Inclusion[ReportType, Outcome]], - inc2: Expr[Inclusion[ReportType, DebugInfo]]) + def aspire[TestType: Type, ReportType: Type] + ( test: Expr[Test[TestType]], + runner: Expr[Runner[ReportType]], + inc: Expr[Inclusion[ReportType, Outcome]], + inc2: Expr[Inclusion[ReportType, DebugInfo]] ) (using Quotes) - : Expr[Unit] = + : Expr[Unit] = + general[TestType, ReportType, Unit](test, '{ _ => true }, runner, inc, inc2, '{ (t: TestRun[TestType]) => () }) def succeed: Any => Boolean = (value: Any) => true - def assertion - [TestType, TestType2 <: TestType, ReportType, ResultType] - (runner: Runner[ReportType], test: Test[TestType2], predicate: TestType2 => Boolean, result: TestRun[TestType2] => ResultType, - contrast: Contrast[TestType], exp: Option[TestType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo], display: Debug[TestType]): ResultType = + def assertion[TestType, TestType2 <: TestType, ReportType, ResultType] + ( runner: Runner[ReportType], + test: Test[TestType2], + predicate: TestType2 => Boolean, + result: TestRun[TestType2] => ResultType, + contrast: Contrast[TestType], + exp: Option[TestType], + inc: Inclusion[ReportType, Outcome], + inc2: Inclusion[ReportType, DebugInfo], + display: Debug[TestType] ) + : ResultType = + runner.run(test).pipe: run => val outcome = run match case TestRun.Throws(err, duration, map) => - val exception: Exception = - try err() catch case exc: Exception => exc + val exception: Exception = try err() catch case exc: Exception => exc if !map.isEmpty then inc2.include(runner.report, test.id, DebugInfo.Captures(map)) Outcome.Throws(exception, duration) diff --git a/src/core/probably.scala b/src/core/probably.scala index d2da345..0f1427c 100644 --- a/src/core/probably.scala +++ b/src/core/probably.scala @@ -30,21 +30,21 @@ import scala.collection.mutable as scm given realm: Realm = realm"probably" -extension [T](inline value: T)(using inline test: TestContext) - inline def inspect: T = ${Probably.inspect('value, 'test)} +extension [ValueType](inline value: ValueType)(using inline test: TestContext) + inline def inspect: ValueType = ${Probably.inspect('value, 'test)} package testContexts: given threadLocal: TestContext = new TestContext(): private val delegate: Option[TestContext] = Option(Runner.testContextThreadLocal.get()).map(_.nn).flatten - override def capture[T](name: Text, value: T)(using Debug[T]): T = - delegate.map(_.capture[T](name, value)).getOrElse(value) + override def capture[ValueType](name: Text, value: ValueType)(using Debug[ValueType]): ValueType = + delegate.map(_.capture[ValueType](name, value)).getOrElse(value) @annotation.capability class TestContext(): private[probably] val captured: scm.ArrayBuffer[(Text, Text)] = scm.ArrayBuffer() - def capture[T](name: Text, value: T)(using Debug[T]): T = + def capture[ValueType](name: Text, value: ValueType)(using Debug[ValueType]): ValueType = captured.append(name -> value.debug) value @@ -56,7 +56,10 @@ case class TestId(name: Text, suite: Optional[TestSuite], codepoint: Codepoint): import textMetrics.uniform lazy val id: Text = (suite.hashCode ^ name.hashCode).hex.pad(6, Rtl, '0').take(6, Rtl) lazy val ids: List[Text] = id :: suite.let(_.id.ids).or(Nil) - def apply[T](ctx: TestContext ?=> T): Test[T] = Test[T](this, ctx(using _)) + + def apply[ResultType](ctx: TestContext ?=> ResultType): Test[ResultType] = + Test[ResultType](this, ctx(using _)) + def depth: Int = suite.let(_.id.depth).or(0) + 1 class TestSuite(val name: Text, val parent: Optional[TestSuite] = Unset)(using codepoint: Codepoint): @@ -131,53 +134,59 @@ class Runner[ReportType]()(using reporter: TestReporter[ReportType]): def complete(): Unit = reporter.complete(report) -case class Test[+Return](id: TestId, action: TestContext => Return) +case class Test[+ReturnType](id: TestId, action: TestContext => ReturnType) def test[ReportType](name: Text)(using suite: TestSuite, codepoint: Codepoint): TestId = TestId(name, suite, codepoint) -def suite - [ReportType](name: Text)(using suite: TestSuite, runner: Runner[ReportType]) - (block: TestSuite ?=> Unit): Unit = +def suite[ReportType](name: Text)(using suite: TestSuite, runner: Runner[ReportType]) + (block: TestSuite ?=> Unit) + : Unit = + runner.suite(TestSuite(name, suite), block) extension [TestType](test: Test[TestType]) - inline def aspire[ReportType] - (inline pred: TestType => Boolean) + inline def aspire[ReportType](inline predicate: TestType => Boolean) (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], inc2: Inclusion[ReportType, DebugInfo]) : Unit = ${Probably.aspire[TestType, ReportType]('test, 'runner, 'inc, 'inc2)} inline def assert[ReportType] - (inline pred: TestType => Boolean) - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo]) - : Unit = - ${Probably.assert[TestType, ReportType]('test, 'pred, 'runner, 'inc, 'inc2)} + (inline predicate: TestType => Boolean) + ( using runner: Runner[ReportType], + inclusion: Inclusion[ReportType, Outcome], + inclusion2: Inclusion[ReportType, DebugInfo] ) + : Unit = + ${Probably.assert[TestType, ReportType]('test, 'predicate, 'runner, 'inclusion, 'inclusion2)} inline def check[ReportType] - (inline pred: TestType => Boolean) - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo]) - : TestType = - ${Probably.check[TestType, ReportType]('test, 'pred, 'runner, 'inc, 'inc2)} + (inline predicate: TestType => Boolean) + ( using runner: Runner[ReportType], + inclusion: Inclusion[ReportType, Outcome], + inclusion2: Inclusion[ReportType, DebugInfo] ) + : TestType = + ${Probably.check[TestType, ReportType]('test, 'predicate, 'runner, 'inclusion, 'inclusion2)} inline def assert[ReportType]() - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo]) - : Unit = - ${Probably.assert[TestType, ReportType]('test, '{Probably.succeed}, 'runner, 'inc, 'inc2)} + ( using runner: Runner[ReportType], + inclusion: Inclusion[ReportType, Outcome], + inclusion2: Inclusion[ReportType, DebugInfo] ) + : Unit = + ${Probably.assert[TestType, ReportType]('test, '{Probably.succeed}, 'runner, 'inclusion, 'inclusion2)} inline def check[ReportType]() - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo]) - : TestType = - ${Probably.check[TestType, ReportType]('test, '{Probably.succeed}, 'runner, 'inc, 'inc2)} + ( using runner: Runner[ReportType], + inclusion: Inclusion[ReportType, Outcome], + inclusion2: Inclusion[ReportType, DebugInfo] ) + : TestType = + + ${Probably.check[TestType, ReportType]('test, '{Probably.succeed}, 'runner, 'inclusion, 'inclusion2)} - inline def matches[ReportType] - (inline pf: PartialFunction[TestType, Any]) - (using runner: Runner[ReportType], inc: Inclusion[ReportType, Outcome], - inc2: Inclusion[ReportType, DebugInfo]) - : Unit = + inline def matches[ReportType](inline pf: PartialFunction[TestType, Any]) + ( using runner: Runner[ReportType], + inc: Inclusion[ReportType, Outcome], + inc2: Inclusion[ReportType, DebugInfo] ) + : Unit = + assert[ReportType](pf.isDefinedAt(_)) diff --git a/src/core/tolerance.scala b/src/core/tolerance.scala index e66b4ce..8405b24 100644 --- a/src/core/tolerance.scala +++ b/src/core/tolerance.scala @@ -19,11 +19,9 @@ package probably import rudiments.* case class Tolerance(value: Double, tolerance: Double): - def covers(right: Double): Boolean = - value >= (value - tolerance) && value <= (value + tolerance) + def covers(right: Double): Boolean = value >= (value - tolerance) && value <= (value + tolerance) extension (value: Double) @targetName("plusOrMinus") infix def +/- (tolerance: Double): Tolerance = Tolerance(value, tolerance) - infix def meets (tolerance: Tolerance): Boolean = tolerance.covers(value) diff --git a/src/coverage/coverage.scala b/src/coverage/coverage.scala index 7f2f8a6..984aef3 100644 --- a/src/coverage/coverage.scala +++ b/src/coverage/coverage.scala @@ -31,8 +31,19 @@ object Juncture: given Ordering[Juncture] = Ordering.by[Juncture, Int](_.start).orElseBy(-_.end) case class Juncture - (id: Int, path: Text, className: Text, methodName: Text, start: Int, end: Int, lineNo: Int, - symbolName: Text, treeName: Text, branch: Boolean, ignored: Boolean, code: List[Text]): + ( id: Int, + path: Text, + className: Text, + methodName: Text, + start: Int, + end: Int, + lineNo: Int, + symbolName: Text, + treeName: Text, + branch: Boolean, + ignored: Boolean, + code: List[Text] ): + def contains(right: Juncture): Boolean = (right.start >= start && right.end <= end && !(right.start == start && right.end == end)) || treeName == t"DefDef" && right.treeName != t"DefDef" && className == right.className &&