Skip to content

Fix #1965: add proper testing infrastructure for reporting tests #1966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/dotc/parsing/DocstringTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
70 changes: 70 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala
Original file line number Diff line number Diff line change
@@ -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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean asInstanceOf?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I should probably rename FailedReport => EmptyReport

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I don't understand the statement, this computes a Boolean and converts it to Unit regardless of it's value?

}

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the mutation stuff at the very top of the trait. BTW could you make it a class instead? (it feels wrong to have class → trait → class inheritance)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe DottyTest should be a trait instead?


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 =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertMessageCount

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

assertEquals(
expected,
messages.length
)
}
45 changes: 45 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
Original file line number Diff line number Diff line change
@@ -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")
}
}