From f891b224228f7c4939d09ac1849ad562d1298640 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Mon, 10 Apr 2017 16:26:14 +0200 Subject: Make sure that everything is dumped to log files --- .../dotty/tools/dotc/reporting/TestReporter.scala | 22 +++- .../test/dotty/tools/vulpix/ParallelTesting.scala | 142 ++++++++++++++------- .../test/dotty/tools/vulpix/SummaryReport.java | 13 +- 3 files changed, 120 insertions(+), 57 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index 5641240a7..efba2dc8f 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -23,6 +23,10 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M final def errors: Iterator[MessageContainer] = _errorBuf.iterator protected final val _messageBuf = mutable.ArrayBuffer.empty[String] + final def messages: Iterator[String] = _messageBuf.iterator + + private[this] var _didCrash = false + final def compilerCrashed: Boolean = _didCrash final def flushToFile(): Unit = _messageBuf @@ -33,7 +37,6 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M final def flushToStdErr(): Unit = _messageBuf .iterator - .map(_.replaceAll("\u001b\\[.*?m", "")) .foreach(System.err.println) final def inlineInfo(pos: SourcePosition): String = @@ -44,9 +47,17 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M } else "" - def echo(msg: String) = + def log(msg: String) = _messageBuf.append(msg) + def logStackTrace(thrown: Throwable): Unit = { + _didCrash = true + val sw = new java.io.StringWriter + val pw = new java.io.PrintWriter(sw) + thrown.printStackTrace(pw) + log(sw.toString) + } + /** Prints the message with the given position indication. */ def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = { val msg = messageAndPos(m.contained, m.pos, diagnosticLevel(m)) @@ -73,15 +84,14 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M _errorBuf.append(m) printMessageAndPos(m, extra) } - case w: Warning => - printMessageAndPos(w, extra) - case _ => + case m => + printMessageAndPos(m, extra) } } } object TestReporter { - private[this] lazy val logWriter = { + lazy val logWriter = { val df = new SimpleDateFormat("yyyy-MM-dd-HH:mm") val timestamp = df.format(new Date) new JFile("../testlogs").mkdirs() diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index e1babfb9c..82a15c4a4 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -183,12 +183,39 @@ trait ParallelTesting extends RunnerOrchestration { self => /** Each `Test` takes the `testSources` and performs the compilation and assertions * according to the implementing class "neg", "run" or "pos". */ - private abstract class Test(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean) { + private abstract class Test(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean) { test => protected final val realStdout = System.out protected final val realStderr = System.err + /** A runnable that logs its contents in a buffer */ + trait LoggedRunnable extends Runnable { + import TestReporter.logWriter + + /** Instances of `LoggedRunnable` implement this method instead of the + * `run` method + */ + def checkTestSource(): Unit + + private[this] val logBuffer = mutable.ArrayBuffer.empty[String] + def log(msg: String): Unit = logBuffer.append(msg) + + def logReporterContents(reporter: TestReporter): Unit = + reporter.messages.foreach(log) + + def echo(msg: String): Unit = { + log(msg) + test.echo(msg) + } + + final def run(): Unit = { + checkTestSource() + logBuffer.iterator.foreach(logWriter.println) + logWriter.flush() + } + } + /** Actual compilation run logic, the test behaviour is defined here */ - protected def compilationRunnable(testSource: TestSource): Runnable + protected def encapsulatedCompilation(testSource: TestSource): LoggedRunnable /** All testSources left after filtering out */ private val filteredSources = @@ -220,7 +247,7 @@ trait ParallelTesting extends RunnerOrchestration { self => protected[this] final def fail(): Unit = synchronized { _failed = true } def didFail: Boolean = _failed - protected def echoBuildInstructions(reporter: TestReporter, testSource: TestSource, err: Int, war: Int) = { + protected def logBuildInstructions(reporter: TestReporter, testSource: TestSource, err: Int, war: Int) = { val errorMsg = testSource.buildInstructions(reporter.errorCount, reporter.warningCount) addFailureInstruction(errorMsg) failTestSource(testSource) @@ -278,7 +305,9 @@ trait ParallelTesting extends RunnerOrchestration { self => */ protected def tryCompile(testSource: TestSource)(op: => Unit): Unit = try { - if (!isInteractive) realStdout.println(s"Testing ${testSource.title}") + val testing = s"Testing ${testSource.title}" + TestReporter.logWriter.println(testing) + if (!isInteractive) realStdout.println(testing) op } catch { case NonFatal(e) => { @@ -342,10 +371,17 @@ trait ParallelTesting extends RunnerOrchestration { self => } val allArgs = addOutDir(flags) - driver.process(allArgs ++ files.map(_.getAbsolutePath), reporter = reporter) - val javaFiles = files.filter(_.getName.endsWith(".java")).map(_.getAbsolutePath) - assert(compileWithJavac(javaFiles), s"java compilation failed for ${javaFiles.mkString(", ")}") + // Compile with a try to catch any StackTrace generated by the compiler: + try { + driver.process(allArgs ++ files.map(_.getAbsolutePath), reporter = reporter) + + val javaFiles = files.filter(_.getName.endsWith(".java")).map(_.getAbsolutePath) + assert(compileWithJavac(javaFiles), s"java compilation failed for ${javaFiles.mkString(", ")}") + } + catch { + case NonFatal(ex) => reporter.logStackTrace(ex) + } reporter } @@ -362,7 +398,7 @@ trait ParallelTesting extends RunnerOrchestration { self => if (isInteractive && !suppressAllOutput) pool.submit(createProgressMonitor) filteredSources.foreach { target => - pool.submit(compilationRunnable(target)) + pool.submit(encapsulatedCompilation(target)) } pool.shutdown() @@ -392,22 +428,25 @@ trait ParallelTesting extends RunnerOrchestration { self => private final class PosTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean) extends Test(testSources, times, threadLimit, suppressAllOutput) { - protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable { - def run(): Unit = tryCompile(testSource) { + protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable { + def checkTestSource(): Unit = tryCompile(testSource) { testSource match { case testSource @ JointCompilationSource(_, files, flags, outDir) => { val reporter = compile(testSource.sourceFiles, flags, false, outDir) registerCompletion(reporter.errorCount) - if (reporter.errorCount > 0) - echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + if (reporter.compilerCrashed || reporter.errorCount > 0) { + logReporterContents(reporter) + logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + } } case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => { val reporters = testSource.compilationGroups.map(files => compile(files, flags, false, outDir)) + val compilerCrashed = reporters.exists(_.compilerCrashed) val errorCount = reporters.foldLeft(0) { (acc, reporter) => if (reporter.errorCount > 0) - echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) acc + reporter.errorCount } @@ -416,8 +455,10 @@ trait ParallelTesting extends RunnerOrchestration { self => registerCompletion(errorCount) - if (errorCount > 0) - echoBuildInstructions(reporters.head, testSource, errorCount, warningCount) + if (compilerCrashed || errorCount > 0) { + reporters.foreach(logReporterContents) + logBuildInstructions(reporters.head, testSource, errorCount, warningCount) + } } } } @@ -483,9 +524,9 @@ trait ParallelTesting extends RunnerOrchestration { self => } } - protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable { - def run(): Unit = tryCompile(testSource) { - val (errorCount, warningCount, hasCheckFile, verifier: Function0[Unit]) = testSource match { + protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable { + def checkTestSource(): Unit = tryCompile(testSource) { + val (compilerCrashed, errorCount, warningCount, hasCheckFile, verifier: Function0[Unit]) = testSource match { case testSource @ JointCompilationSource(_, files, flags, outDir) => { val checkFile = files.flatMap { file => if (file.isDirectory) Nil @@ -498,33 +539,37 @@ trait ParallelTesting extends RunnerOrchestration { self => }.headOption val reporter = compile(testSource.sourceFiles, flags, false, outDir) - if (reporter.errorCount > 0) - echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + if (reporter.compilerCrashed || reporter.errorCount > 0) { + logReporterContents(reporter) + logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + } - (reporter.errorCount, reporter.warningCount, checkFile.isDefined, () => verifyOutput(checkFile.get, outDir, testSource, reporter.warningCount)) + (reporter.compilerCrashed, reporter.errorCount, reporter.warningCount, checkFile.isDefined, () => verifyOutput(checkFile.get, outDir, testSource, reporter.warningCount)) } case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => { val checkFile = new JFile(dir.getAbsolutePath.reverse.dropWhile(_ == '/').reverse + ".check") + val reporters = testSource.compilationGroups.map(compile(_, flags, false, outDir)) + val compilerCrashed = reporters.exists(_.compilerCrashed) val (errorCount, warningCount) = - testSource - .compilationGroups - .map(compile(_, flags, false, outDir)) - .foldLeft((0,0)) { case ((errors, warnings), reporter) => - if (reporter.errorCount > 0) - echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) + reporters.foldLeft((0,0)) { case ((errors, warnings), reporter) => + if (reporter.errorCount > 0) + logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount) - (errors + reporter.errorCount, warnings + reporter.warningCount) - } + (errors + reporter.errorCount, warnings + reporter.warningCount) + } - if (errorCount > 0) fail() + if (errorCount > 0) { + reporters.foreach(logReporterContents) + logBuildInstructions(reporters.head, testSource, errorCount, warningCount) + } - (errorCount, warningCount, checkFile.exists, () => verifyOutput(checkFile, outDir, testSource, warningCount)) + (compilerCrashed, errorCount, warningCount, checkFile.exists, () => verifyOutput(checkFile, outDir, testSource, warningCount)) } } - if (errorCount == 0 && hasCheckFile) verifier() - else if (errorCount == 0) { + if (!compilerCrashed && errorCount == 0 && hasCheckFile) verifier() + else if (!compilerCrashed && errorCount == 0) { if (Properties.testsNoRun) addNoRunWarning() else runMain(testSource.classPath) match { case Success(_) => // success! @@ -537,17 +582,12 @@ trait ParallelTesting extends RunnerOrchestration { self => failTestSource(testSource, Some("test timed out")) } } - else if (errorCount > 0) { + else { echo(s"\n Compilation failed for: '$testSource'") val buildInstr = testSource.buildInstructions(errorCount, warningCount) addFailureInstruction(buildInstr) failTestSource(testSource) } - else { - realStdout.println("Got a super weird error that I haven't handled yet") - realStdout.println("errorCount: " + errorCount) - realStdout.println("test: " + testSource.title + " " + testSource.name) - } registerCompletion(errorCount) } } @@ -555,8 +595,8 @@ trait ParallelTesting extends RunnerOrchestration { self => private final class NegTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean) extends Test(testSources, times, threadLimit, suppressAllOutput) { - protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable { - def run(): Unit = tryCompile(testSource) { + protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable { + def checkTestSource(): Unit = tryCompile(testSource) { // In neg-tests we allow two types of error annotations, // "nopos-error" which doesn't care about position and "error" which // has to be annotated on the correct line number. @@ -608,27 +648,39 @@ trait ParallelTesting extends RunnerOrchestration { self => } } - val (expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match { + val (compilerCrashed, expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match { case testSource @ JointCompilationSource(_, files, flags, outDir) => { val sourceFiles = testSource.sourceFiles val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles) val reporter = compile(sourceFiles, flags, true, outDir) val actualErrors = reporter.errorCount - (expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, reporter.errors), errorMap) + if (reporter.compilerCrashed || actualErrors > 0) + logReporterContents(reporter) + + (reporter.compilerCrashed, expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, reporter.errors), errorMap) } case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => { val compilationGroups = testSource.compilationGroups val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(compilationGroups.toArray.flatten) val reporters = compilationGroups.map(compile(_, flags, true, outDir)) + val compilerCrashed = reporters.exists(_.compilerCrashed) val actualErrors = reporters.foldLeft(0)(_ + _.errorCount) val errors = reporters.iterator.flatMap(_.errors) - (expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, errors), errorMap) + + if (actualErrors > 0) + reporters.foreach(logReporterContents) + + (compilerCrashed, expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, errors), errorMap) } } - if (expectedErrors != actualErrors) { + if (compilerCrashed) { + echo(s"Compiler crashed when compiling: ${testSource.title}") + failTestSource(testSource) + } + else if (expectedErrors != actualErrors) { echo { s"\nWrong number of errors encountered when compiling $testSource, expected: $expectedErrors, actual: $actualErrors\n" } diff --git a/compiler/test/dotty/tools/vulpix/SummaryReport.java b/compiler/test/dotty/tools/vulpix/SummaryReport.java index 23209eefc..b7aa423ff 100644 --- a/compiler/test/dotty/tools/vulpix/SummaryReport.java +++ b/compiler/test/dotty/tools/vulpix/SummaryReport.java @@ -2,6 +2,7 @@ package dotty.tools.vulpix; import org.junit.BeforeClass; import org.junit.AfterClass; +import java.util.Iterator; import java.util.ArrayDeque; import java.util.function.Supplier; import scala.Function0; @@ -75,7 +76,7 @@ public class SummaryReport { } @AfterClass public final static void teardown() { - rep.echo( + rep.log( "\n================================================================================" + "\nTest Report" + "\n================================================================================" + @@ -86,26 +87,26 @@ public class SummaryReport { startingMessages .stream() - .forEach(rep::echo); + .forEach(rep::log); failedTests .stream() .map(x -> " " + x) - .forEach(rep::echo); + .forEach(rep::log); // If we're compiling locally, we don't need reproduce instructions if (isInteractive) rep.flushToStdErr(); - rep.echo(""); + rep.log(""); reproduceInstructions .stream() - .forEach(rep::echo); + .forEach(rep::log); // If we're on the CI, we want everything if (!isInteractive) rep.flushToStdErr(); - if (failed > 0) rep.flushToFile(); + rep.flushToFile(); // Perform cleanup callback: if (cleanup != null) cleanup.get(); -- cgit v1.2.3