Skip to content

Commit d916343

Browse files
authored
Merge pull request #2811 from dotty-staging/fix/javac-utf8
Fix #2791: add reasons for failure to vulpix
2 parents f561301 + 64d101b commit d916343

File tree

3 files changed

+84
-26
lines changed

3 files changed

+84
-26
lines changed

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
242242
_errorCount += errors
243243
}
244244

245-
private[this] var _failed = false
245+
sealed trait Failure
246+
case class JavaCompilationFailure(reason: String) extends Failure
247+
case class TimeoutFailure(title: String) extends Failure
248+
case object Generic extends Failure
249+
250+
private[this] var _failures = Set.empty[Failure]
246251
/** Fail the current test */
247-
protected[this] final def fail(): Unit = synchronized { _failed = true }
248-
def didFail: Boolean = _failed
252+
protected[this] final def fail(failure: Failure = Generic): Unit = synchronized {
253+
_failures = _failures + failure
254+
}
255+
def didFail: Boolean = _failures.nonEmpty
256+
257+
/** A set of the different failures */
258+
def failureReasons: Set[Failure] = _failures
249259

250260
protected def logBuildInstructions(reporter: TestReporter, testSource: TestSource, err: Int, war: Int) = {
251261
val errorMsg = testSource.buildInstructions(reporter.errorCount, reporter.warningCount)
@@ -260,10 +270,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
260270

261271
/** The test sources that failed according to the implementing subclass */
262272
private[this] val failedTestSources = mutable.ArrayBuffer.empty[String]
263-
protected final def failTestSource(testSource: TestSource, reason: Option[String] = None) = synchronized {
264-
val extra = reason.map(" with reason: " + _).getOrElse("")
273+
protected final def failTestSource(testSource: TestSource, reason: Failure = Generic) = synchronized {
274+
val extra = reason match {
275+
case TimeoutFailure(title) => s", test '$title' timed out"
276+
case JavaCompilationFailure(msg) => s", java test sources failed to compile with: \n$msg"
277+
case Generic => ""
278+
}
265279
failedTestSources.append(testSource.title + s" failed" + extra)
266-
fail()
280+
fail(reason)
267281
}
268282

269283
/** Prints to `System.err` if we're not suppressing all output */
@@ -342,12 +356,17 @@ trait ParallelTesting extends RunnerOrchestration { self =>
342356
def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) {
343357
val fullArgs = Array(
344358
"javac",
359+
"-encoding", "UTF-8",
345360
"-classpath",
346361
s".:${Jars.scalaLibrary}:${targetDir.getAbsolutePath}"
347362
) ++ flags.takeRight(2) ++ fs
348363

349-
Runtime.getRuntime.exec(fullArgs).waitFor() == 0
350-
} else true
364+
val process = Runtime.getRuntime.exec(fullArgs)
365+
val output = Source.fromInputStream(process.getErrorStream).mkString
366+
367+
if (process.waitFor() != 0) Some(output)
368+
else None
369+
} else None
351370

352371
val reporter =
353372
TestReporter.reporter(realStdout, logLevel =
@@ -377,7 +396,12 @@ trait ParallelTesting extends RunnerOrchestration { self =>
377396
driver.process(allArgs ++ files.map(_.getAbsolutePath), reporter = reporter)
378397

379398
val javaFiles = files.filter(_.getName.endsWith(".java")).map(_.getAbsolutePath)
380-
assert(compileWithJavac(javaFiles), s"java compilation failed for ${javaFiles.mkString(", ")}")
399+
val javaErrors = compileWithJavac(javaFiles)
400+
401+
if (javaErrors.isDefined) {
402+
echo(s"\njava compilation failed: \n${ javaErrors.get }")
403+
fail(failure = JavaCompilationFailure(javaErrors.get))
404+
}
381405
}
382406
catch {
383407
case NonFatal(ex) => reporter.logStackTrace(ex)
@@ -522,7 +546,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
522546

523547
case Timeout =>
524548
echo("failed because test " + testSource.title + " timed out")
525-
failTestSource(testSource, Some("test timed out"))
549+
failTestSource(testSource, TimeoutFailure(testSource.title))
526550
}
527551
}
528552

@@ -803,15 +827,16 @@ trait ParallelTesting extends RunnerOrchestration { self =>
803827
private[ParallelTesting] val times: Int,
804828
private[ParallelTesting] val shouldDelete: Boolean,
805829
private[ParallelTesting] val threadLimit: Option[Int],
806-
private[ParallelTesting] val shouldFail: Boolean
830+
private[ParallelTesting] val shouldFail: Boolean,
831+
private[ParallelTesting] val shouldSuppressOutput: Boolean
807832
) {
808833
import org.junit.Assert.fail
809834

810835
private[ParallelTesting] def this(target: TestSource) =
811-
this(List(target), 1, true, None, false)
836+
this(List(target), 1, true, None, false, false)
812837

813838
private[ParallelTesting] def this(targets: List[TestSource]) =
814-
this(targets, 1, true, None, false)
839+
this(targets, 1, true, None, false, false)
815840

816841
/** Compose test targets from `this` with `other`
817842
*
@@ -831,18 +856,19 @@ trait ParallelTesting extends RunnerOrchestration { self =>
831856
require(other.times == times, "can't combine tests that are meant to be benchmark compiled")
832857
require(other.shouldDelete == shouldDelete, "can't combine tests that differ on deleting output")
833858
require(other.shouldFail == shouldFail, "can't combine tests that have different expectations on outcome")
834-
new CompilationTest(targets ++ other.targets, times, shouldDelete, threadLimit, shouldFail)
859+
require(other.shouldSuppressOutput == shouldSuppressOutput, "can't combine tests that both suppress and don't suppress output")
860+
new CompilationTest(targets ++ other.targets, times, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput)
835861
}
836862

837863
/** Creates a "pos" test run, which makes sure that all tests pass
838864
* compilation without generating errors and that they do not crash the
839865
* compiler
840866
*/
841867
def checkCompile()(implicit summaryReport: SummaryReporting): this.type = {
842-
val test = new PosTest(targets, times, threadLimit, shouldFail).executeTestSuite()
868+
val test = new PosTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
843869

844870
if (!shouldFail && test.didFail) {
845-
fail(s"Expected no errors when compiling, but found: ${test.errorCount}")
871+
fail(s"Expected no errors when compiling, failed for the following reason(s):\n${ reasonsForFailure(test) }")
846872
}
847873
else if (shouldFail && !test.didFail) {
848874
fail("Pos test should have failed, but didn't")
@@ -856,10 +882,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
856882
* that none of these tests crash the compiler
857883
*/
858884
def checkExpectedErrors()(implicit summaryReport: SummaryReporting): this.type = {
859-
val test = new NegTest(targets, times, threadLimit, shouldFail).executeTestSuite()
885+
val test = new NegTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
860886

861887
if (!shouldFail && test.didFail) {
862-
fail("Neg test shouldn't have failed, but did")
888+
fail(s"Neg test shouldn't have failed, but did. Reasons:\n${ reasonsForFailure(test) }")
863889
}
864890
else if (shouldFail && !test.didFail) {
865891
fail("Neg test should have failed, but did not")
@@ -874,10 +900,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
874900
* expected output
875901
*/
876902
def checkRuns()(implicit summaryReport: SummaryReporting): this.type = {
877-
val test = new RunTest(targets, times, threadLimit, shouldFail).executeTestSuite()
903+
val test = new RunTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
878904

879905
if (!shouldFail && test.didFail) {
880-
fail("Run test failed, but should not")
906+
fail(s"Run test failed, but should not, reasons:\n${ reasonsForFailure(test) }")
881907
}
882908
else if (shouldFail && !test.didFail) {
883909
fail("Run test should have failed, but did not")
@@ -892,6 +918,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
892918
this
893919
}
894920

921+
/** Extract `Failure` set and render from `Test` */
922+
private[this] def reasonsForFailure(test: Test): String = {
923+
val errors =
924+
if (test.errorCount == 0) ""
925+
else s"\n - encountered ${test.errorCount} error(s)"
926+
927+
errors + test.failureReasons.collect {
928+
case test.TimeoutFailure(title) =>
929+
s" - test '$title' timed out"
930+
case test.JavaCompilationFailure(msg) =>
931+
s" - java compilation failed with:\n${ msg.lines.map(" " + _).mkString("\n") }"
932+
}.mkString("\n")
933+
}
934+
895935
/** Copies `file` to `dir` - taking into account if `file` is a directory,
896936
* and if so copying recursively
897937
*/
@@ -913,21 +953,21 @@ trait ParallelTesting extends RunnerOrchestration { self =>
913953
case target @ SeparateCompilationSource(_, dir, _, outDir) =>
914954
target.copy(dir = copyToDir(outDir, dir))
915955
},
916-
times, shouldDelete, threadLimit, shouldFail
956+
times, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput
917957
)
918958

919959
/** Builds a `CompilationTest` which performs the compilation `i` times on
920960
* each target
921961
*/
922962
def times(i: Int): CompilationTest =
923-
new CompilationTest(targets, i, shouldDelete, threadLimit, shouldFail)
963+
new CompilationTest(targets, i, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput)
924964

925965
/** Builds a `Compilationtest` which passes the verbose flag and logs the
926966
* classpath
927967
*/
928968
def verbose: CompilationTest = new CompilationTest(
929969
targets.map(t => t.withFlags("-verbose", "-Ylog-classpath")),
930-
times, shouldDelete, threadLimit, shouldFail
970+
times, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput
931971
)
932972

933973
/** Builds a `CompilationTest` which keeps the generated output files
@@ -937,20 +977,27 @@ trait ParallelTesting extends RunnerOrchestration { self =>
937977
* part which depends on the first
938978
*/
939979
def keepOutput: CompilationTest =
940-
new CompilationTest(targets, times, false, threadLimit, shouldFail)
980+
new CompilationTest(targets, times, false, threadLimit, shouldFail, shouldSuppressOutput)
941981

942982
/** Builds a `CompilationTest` with a limited level of concurrency with
943983
* maximum `i` threads
944984
*/
945985
def limitThreads(i: Int): CompilationTest =
946-
new CompilationTest(targets, times, shouldDelete, Some(i), shouldFail)
986+
new CompilationTest(targets, times, shouldDelete, Some(i), shouldFail, shouldSuppressOutput)
947987

948988
/** Builds a `CompilationTest` where the executed test is expected to fail
949989
*
950990
* This behaviour is mainly needed for the tests that test the test suite.
951991
*/
952992
def expectFailure: CompilationTest =
953-
new CompilationTest(targets, times, shouldDelete, threadLimit, true)
993+
new CompilationTest(targets, times, shouldDelete, threadLimit, true, shouldSuppressOutput)
994+
995+
/** Builds a `CompilationTest` where all output is suppressed
996+
*
997+
* This behaviour is mainly needed for the tests that test the test suite.
998+
*/
999+
def suppressAllOutput: CompilationTest =
1000+
new CompilationTest(targets, times, shouldDelete, threadLimit, shouldFail, true)
9541001

9551002
/** Delete all output files generated by this `CompilationTest` */
9561003
def delete(): Unit = targets.foreach(t => delete(t.outDir))

compiler/test/dotty/tools/vulpix/VulpixTests.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,10 @@ class VulpixTests extends ParallelTesting {
7373

7474
@Test def deadlock: Unit =
7575
compileFile("../tests/partest-test/deadlock.scala", defaultOptions).expectFailure.checkRuns()
76+
77+
@Test def badJava: Unit =
78+
try compileFile("../tests/partest-test/BadJava.java", defaultOptions).suppressAllOutput.checkCompile()
79+
catch {
80+
case ae: AssertionError => assert(ae.getMessage.contains("java compilation failed"))
81+
}
7682
}

tests/partest-test/BadJava.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public interface BadJava {
2+
default void foo() {
3+
return 1;
4+
}
5+
}

0 commit comments

Comments
 (0)