From 14bfa1ab9a7ce95e99f44449b56f52ec4b6439e0 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 10 Feb 2017 14:35:00 +0100 Subject: [PATCH 1/3] Fix #1965: add proper testing infrastructure for reporting tests --- .../tools/dotc/parsing/DocstringTest.scala | 2 +- .../dotc/reporting/ErrorMessagesTest.scala | 70 +++++++++++++++++++ .../dotc/reporting/ErrorMessagesTests.scala | 45 ++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala create mode 100644 compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala diff --git a/compiler/test/dotty/tools/dotc/parsing/DocstringTest.scala b/compiler/test/dotty/tools/dotc/parsing/DocstringTest.scala index 30e885f70fa5..a67b58cc6e07 100644 --- a/compiler/test/dotty/tools/dotc/parsing/DocstringTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/DocstringTest.scala @@ -27,7 +27,7 @@ trait DocstringTest extends DottyTest { def checkFrontend(source: String)(docAssert: PartialFunction[Tree[Untyped], Unit]) = { checkCompile("frontend", source) { (_, ctx) => - implicit val c = ctx + implicit val c: Context = ctx (docAssert orElse defaultAssertion)(ctx.compilationUnit.untpdTree) } } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala new file mode 100644 index 000000000000..807e3a40aee7 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala @@ -0,0 +1,70 @@ +package dotty.tools +package dotc +package reporting + +import diagnostic._ +import core.Contexts.Context + +import scala.collection.mutable + +import org.junit.Assert._ + +trait ErrorMessagesTest extends DottyTest { + + class Report(messages: List[Message], ictx: Context) { + def expect(f: (Context, List[Message]) => Unit): Unit = { + f(ictx, messages) + } + + def expectNoErrors: Unit = this.isInstanceOf[FailedReport] + } + + class FailedReport extends Report(Nil, null) { + override def expect(f: (Context, List[Message]) => Unit) = + fail("""| + |Couldn't capture errors from compiled sources, this can happen if + |there are no errors or the compiler crashes.""".stripMargin) + } + + class CapturingReporter extends Reporter + with UniqueMessagePositions with HideNonSensicalMessages { + private[this] val buffer = new mutable.ListBuffer[Message] + private[this] var capturedContext: Context = _ + + def doReport(m: MessageContainer)(implicit ctx: Context) = { + capturedContext = ctx + buffer append m.contained + } + + def toReport: Report = + if (capturedContext eq null) + new FailedReport + else { + val xs = buffer.reverse.toList + buffer.clear() + + val ctx = capturedContext + capturedContext = null + + new Report(xs, ctx) + } + } + + ctx = freshReporter(ctx) + + private def freshReporter(ctx: Context) = + ctx.fresh.setReporter(new CapturingReporter) + + def checkMessages(source: String): Report = { + checkCompile("frontend", source) { (_,ictx) => () } + val rep = ctx.reporter.asInstanceOf[CapturingReporter].toReport + ctx = freshReporter(ctx) + rep + } + + def messageCount(expected: Int, messages: List[Message]): Unit = + assertEquals( + expected, + messages.length + ) +} diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala new file mode 100644 index 000000000000..eb7b944b13aa --- /dev/null +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -0,0 +1,45 @@ +package dotty.tools +package dotc +package reporting + +import core.Contexts.Context +import diagnostic.messages._ + +import org.junit.Assert._ +import org.junit.Test + +class ErrorMessagesTests extends ErrorMessagesTest { + // In the case where there are no errors, we can do "expectNoErrors" in the + // `Report` + @Test def noErrors = + checkMessages("""class Foo""") + .expectNoErrors + + @Test def typeMismatch = + checkMessages { + """ + |object Foo { + | def bar: String = 1 + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + val defn = ictx.definitions + + // Assert that we only got one error message + messageCount(1, messages) + + // Pattern match out the expected error + val TypeMismatch(found, expected, _, _) :: Nil = messages + + // The type of the right hand side will actually be the constant 1, + // therefore we check if it "derivesFrom" `IntClass` + assert(found.derivesFrom(defn.IntClass), s"found was: $found") + + // The expected type is `scala.String` which we dealias to + // `java.lang.String` and compare with `=:=` to `defn.StringType` which + // is a type reference to `java.lang.String` + assert(expected.dealias =:= defn.StringType, s"expected was: $expected") + } +} From f8c3993ca6e78edf2f9bf5842bce78c028578081 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 10 Feb 2017 15:34:33 +0100 Subject: [PATCH 2/3] Make DottyTest a Trait --- compiler/test/dotty/tools/DottyTest.scala | 2 +- compiler/test/dotty/tools/DottyTypeStealer.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/test/dotty/tools/DottyTest.scala b/compiler/test/dotty/tools/DottyTest.scala index bd6b1cfa4dbb..39ff014c8f71 100644 --- a/compiler/test/dotty/tools/DottyTest.scala +++ b/compiler/test/dotty/tools/DottyTest.scala @@ -14,7 +14,7 @@ import dotc.Compiler import dotc.core.Phases.Phase -class DottyTest extends ContextEscapeDetection{ +trait DottyTest extends ContextEscapeDetection { dotc.parsing.Scanners // initialize keywords diff --git a/compiler/test/dotty/tools/DottyTypeStealer.scala b/compiler/test/dotty/tools/DottyTypeStealer.scala index 819f19d25ca0..ff6e67e41614 100644 --- a/compiler/test/dotty/tools/DottyTypeStealer.scala +++ b/compiler/test/dotty/tools/DottyTypeStealer.scala @@ -7,14 +7,14 @@ import dotc.core.Contexts.Context import dotc.core.Decorators._ import dotc.core.Types.Type -object DottyTypeStealer { +object DottyTypeStealer extends DottyTest { def stealType(source: String, typeStrings: String*): (Context, List[Type]) = { val dummyName = "x_x_x" val vals = typeStrings.zipWithIndex.map{case (s, x)=> s"val ${dummyName}$x: $s = ???"}.mkString("\n") val gatheredSource = s" ${source}\n object A$dummyName {$vals}" var scontext : Context = null var tp: List[Type] = null - new DottyTest().checkCompile("frontend",gatheredSource) { + checkCompile("frontend",gatheredSource) { (tree, context) => implicit val ctx = context val findValDef: (List[ValDef], tpd.Tree) => List[ValDef] = From 07384caa0b6c732e180f172402023ea47f3e31ae Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Fri, 10 Feb 2017 15:35:13 +0100 Subject: [PATCH 3/3] Address reviews for #1966 --- .../dotc/reporting/ErrorMessagesTest.scala | 20 ++++++++++--------- .../dotc/reporting/ErrorMessagesTests.scala | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala index 807e3a40aee7..67a09b9bf520 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala @@ -11,15 +11,22 @@ import org.junit.Assert._ trait ErrorMessagesTest extends DottyTest { + ctx = freshReporter(ctx) + + private def freshReporter(ctx: Context) = + ctx.fresh.setReporter(new CapturingReporter) + + class Report(messages: List[Message], ictx: Context) { def expect(f: (Context, List[Message]) => Unit): Unit = { f(ictx, messages) } - def expectNoErrors: Unit = this.isInstanceOf[FailedReport] + def expectNoErrors: Unit = + assert(this.isInstanceOf[EmptyReport], "errors found when not expected") } - class FailedReport extends Report(Nil, null) { + class EmptyReport extends Report(Nil, null) { override def expect(f: (Context, List[Message]) => Unit) = fail("""| |Couldn't capture errors from compiled sources, this can happen if @@ -38,7 +45,7 @@ trait ErrorMessagesTest extends DottyTest { def toReport: Report = if (capturedContext eq null) - new FailedReport + new EmptyReport else { val xs = buffer.reverse.toList buffer.clear() @@ -50,11 +57,6 @@ trait ErrorMessagesTest extends DottyTest { } } - ctx = freshReporter(ctx) - - private def freshReporter(ctx: Context) = - ctx.fresh.setReporter(new CapturingReporter) - def checkMessages(source: String): Report = { checkCompile("frontend", source) { (_,ictx) => () } val rep = ctx.reporter.asInstanceOf[CapturingReporter].toReport @@ -62,7 +64,7 @@ trait ErrorMessagesTest extends DottyTest { rep } - def messageCount(expected: Int, messages: List[Message]): Unit = + def assertMessageCount(expected: Int, messages: List[Message]): Unit = assertEquals( expected, messages.length diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index eb7b944b13aa..59391cf90dc7 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -28,7 +28,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { val defn = ictx.definitions // Assert that we only got one error message - messageCount(1, messages) + assertMessageCount(1, messages) // Pattern match out the expected error val TypeMismatch(found, expected, _, _) :: Nil = messages