From f4506b747bcc0fee03ccc6aa2abda02d3c6ff3ee Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 25 Feb 2013 14:55:23 -0600 Subject: make the test method hiding stuff a bit less fragile. hopefully. --- src/main/scala/org/perl8/test/TestMore.scala | 59 ++++++++++++++--------- src/test/scala/org/perl8/test/ExtensionTest.scala | 12 ++--- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/main/scala/org/perl8/test/TestMore.scala b/src/main/scala/org/perl8/test/TestMore.scala index 7d5ab7b..8f60cf3 100644 --- a/src/main/scala/org/perl8/test/TestMore.scala +++ b/src/main/scala/org/perl8/test/TestMore.scala @@ -9,7 +9,6 @@ class TestMore (plan: Option[Plan] = None) extends Test with DelayedInit { this(Some(plan)) def delayedInit (body: => Unit) { - level = 0 todo = NoMessage builder = new TestBuilder(plan, "", NoMessage) testBody = () => body @@ -123,17 +122,38 @@ class TestMore (plan: Option[Plan] = None) extends Test with DelayedInit { ok(success, name) } + protected def ignoreFrame (frame: StackTraceElement): Boolean = { + val className = frame.getClassName + val methodName = frame.getMethodName + + // ignore everything in this class, except the hideTestMethod call which we + // use as a stack trace marker + (className == "org.perl8.test.TestMore" && + methodName != "hideTestMethod") || + // when you call a method in a class when the method is defined in a + // trait, it calls a stub which calls the real definition in the trait. + // the trait is represented under the hood as a class with the same name + // as the trait, except with $class appended. this is a gross reliance on + // implementation details that could change at any moment, but i don't + // really see any better options. + """\$class$""".r.findFirstIn(className).nonEmpty + } + private def failed (desc: Message) { - val stack = Thread.currentThread.getStackTrace.drop(1) - def findIdx (level: Int, start: Int): Int = { - val idx = stack.indexWhere({ frame => - frame.getFileName != "TestMore.scala" - }, start) + val stack = Thread.currentThread.getStackTrace.drop(1).filter { frame => + !ignoreFrame(frame) + } + val idx = stack.lastIndexWhere { frame => + frame.getClassName == "org.perl8.test.TestMore" && + frame.getMethodName == "hideTestMethod" + } + val caller = idx match { + case -1 => stack.headOption + // one level to jump out of hideTestMethod and one level to jump out of + // the method that called hideTestMethod + case i => stack.drop(i + 2).headOption - if (level == 0) { idx } else { findIdx(level - 1, idx + 1) } } - val idx = findIdx(level, 0) - val caller = stack.drop(idx).headOption val (file, line) = caller match { case Some(frame) => (frame.getFileName, frame.getLineNumber) case None => ("", "") @@ -149,20 +169,13 @@ class TestMore (plan: Option[Plan] = None) extends Test with DelayedInit { builder.diag(message + trace) } - def withLevel[T] (newLevel: Int)(body: => T): T = { - val oldLevel = level - try { - // XXX "+4" is something of a hack, not sure how stable it will be - level += newLevel + 4 - body - } - finally { - level = oldLevel - } + // this just adds a method call with a known name to the stack trace, so + // that we can detect it later + def hideTestMethod[T] (body: => T): T = { + body } - private var level: Int = _ - private var todo: Message = _ - private var builder: TestBuilder = _ - private var testBody: () => Unit = _ + private var todo: Message = _ + private var builder: TestBuilder = _ + private var testBody: () => Unit = _ } diff --git a/src/test/scala/org/perl8/test/ExtensionTest.scala b/src/test/scala/org/perl8/test/ExtensionTest.scala index bf1c17f..5a9c1ac 100644 --- a/src/test/scala/org/perl8/test/ExtensionTest.scala +++ b/src/test/scala/org/perl8/test/ExtensionTest.scala @@ -5,18 +5,14 @@ import java.io.ByteArrayOutputStream import org.perl8.test.tap.Consumer trait NumberZero { this: TestMore => - def is_zero (i: Int, desc: String): Boolean = { - withLevel(1) { - is(i, 0, desc) - } + def is_zero (i: Int, desc: String): Boolean = hideTestMethod { + is(i, 0, desc) } } trait NumberZeroWrapped extends NumberZero { this: TestMore => - def isZero (i: Int): Boolean = { - withLevel(1) { - is_zero(i, "the number is zero") - } + def isZero (i: Int): Boolean = hideTestMethod { + is_zero(i, "the number is zero") } } -- cgit v1.2.3-54-g00ecf