@@ -281,10 +281,12 @@ trait ParallelTesting extends RunnerOrchestration { self =>
281281 private final def onComplete (testSource : TestSource , reportersOrCrash : Try [Seq [TestReporter ]], logger : LoggedRunnable ): Unit =
282282 reportersOrCrash match {
283283 case TryFailure (exn) => onFailure(testSource, Nil , logger, Some (s " Fatal compiler crash when compiling: ${testSource.title}: \n ${exn.getMessage}${exn.getStackTrace.map(" \n\t at " + _).mkString}" ))
284- case TrySuccess (reporters) => maybeFailureMessage(testSource, reporters) match {
285- case Some (msg) => onFailure(testSource, reporters, logger, Option (msg).filter(_.nonEmpty))
286- case None => onSuccess(testSource, reporters, logger)
287- }
284+ case TrySuccess (reporters) if ! reporters.exists(_.skipped) =>
285+ maybeFailureMessage(testSource, reporters) match {
286+ case Some (msg) => onFailure(testSource, reporters, logger, Option (msg).filter(_.nonEmpty))
287+ case None => onSuccess(testSource, reporters, logger)
288+ }
289+ case _ =>
288290 }
289291
290292 /**
@@ -391,6 +393,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
391393 /** Number of failed tests */
392394 def failureCount : Int = _failureCount
393395
396+ private var _skipCount = 0
397+ protected final def registerSkip (): Unit = synchronized { _skipCount += 1 }
398+ def skipCount : Int = _skipCount
399+
394400 protected def logBuildInstructions (testSource : TestSource , reporters : Seq [TestReporter ]) = {
395401 val (errCount, warnCount) = countErrorsAndWarnings(reporters)
396402 val errorMsg = testSource.buildInstructions(errCount, warnCount)
@@ -464,13 +470,13 @@ trait ParallelTesting extends RunnerOrchestration { self =>
464470 val toolArgs = toolArgsFor(files.toList.map(_.toPath), getCharsetFromEncodingOpt(flags0))
465471
466472 val spec = raw " (\d+)(\+)? " .r
467- val testFilter = toolArgs.get(ToolName .Test ) match
473+ val testIsFiltered = toolArgs.get(ToolName .Test ) match
468474 case Some (" -jvm" :: spec(n, more) :: Nil ) =>
469475 if more == " +" then isJavaAtLeast(n) else javaSpecVersion == n
470476 case Some (args) => throw new IllegalStateException (args.mkString(" unknown test option: " , " , " , " " ))
471477 case None => true
472478
473- def scalacOptions = toolArgs.get (ToolName .Scalac ).getOrElse( Nil )
479+ def scalacOptions = toolArgs.getOrElse (ToolName .Scalac , Nil )
474480
475481 val flags = flags0
476482 .and(scalacOptions : _* )
@@ -509,7 +515,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
509515
510516 val allArgs = flags.all
511517
512- if testFilter then
518+ if testIsFiltered then
513519 // If a test contains a Java file that cannot be parsed by Dotty's Java source parser, its
514520 // name must contain the string "JAVA_ONLY".
515521 val dottyFiles = files.filterNot(_.getName.contains(" JAVA_ONLY" )).map(_.getPath)
@@ -523,6 +529,9 @@ trait ParallelTesting extends RunnerOrchestration { self =>
523529 echo(s " \n java compilation failed: \n ${ javaErrors.get }" )
524530 fail(failure = JavaCompilationFailure (javaErrors.get))
525531 }
532+ else
533+ registerSkip()
534+ reporter.setSkip()
526535 end if
527536
528537 reporter
@@ -724,7 +733,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
724733 }
725734
726735 private def verifyOutput (checkFile : Option [JFile ], dir : JFile , testSource : TestSource , warnings : Int , reporters : Seq [TestReporter ], logger : LoggedRunnable ) = {
727- if ( Properties .testsNoRun) addNoRunWarning()
736+ if Properties .testsNoRun then addNoRunWarning()
728737 else runMain(testSource.runClassPath, testSource.allToolArgs) match {
729738 case Success (output) => checkFile match {
730739 case Some (file) if file.exists => diffTest(testSource, file, output.linesIterator.toList, reporters, logger)
@@ -748,7 +757,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
748757 extends Test (testSources, times, threadLimit, suppressAllOutput) {
749758 override def suppressErrors = true
750759
751- override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = {
760+ override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] =
752761 def compilerCrashed = reporters.exists(_.compilerCrashed)
753762 lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
754763 lazy val actualErrors = reporters.foldLeft(0 )(_ + _.errorCount)
@@ -772,20 +781,21 @@ trait ParallelTesting extends RunnerOrchestration { self =>
772781 else if ! errorMap.isEmpty then s " \n Expected error(s) have {<error position>=<unreported error>}: $errorMap"
773782 else null
774783 }
775- }
784+ end maybeFailureMessage
776785
777786 override def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit =
778787 checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger))
779788
780789 def reporterOutputLines (reporters : Seq [TestReporter ]): List [String ] =
781790 reporters.flatMap(_.consoleOutput.split(" \n " )).toList
782791
783- // In neg-tests we allow two types of error annotations,
784- // "nopos-error" which doesn't care about position and "error" which
785- // has to be annotated on the correct line number.
792+ // In neg-tests we allow two or three types of error annotations.
793+ // Normally, `// error` must be annotated on the correct line number.
794+ // `// nopos-error` allows for an error reported without a position.
795+ // `// anypos-error` allows for an error reported with a position that can't be annotated in the check file.
786796 //
787797 // We collect these in a map `"file:row" -> numberOfErrors`, for
788- // nopos errors we save them in `"file" -> numberOfNoPosErrors`
798+ // nopos and anypos errors we save them in `"file" -> numberOfNoPosErrors`
789799 def getErrorMapAndExpectedCount (files : Seq [JFile ]): (HashMap [String , Integer ], Int ) =
790800 val comment = raw " //( *)(nopos-|anypos-)?error " .r
791801 val errorMap = new HashMap [String , Integer ]()
@@ -950,8 +960,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
950960 * ===============
951961 * Since this is a parallel test suite, it is essential to be able to
952962 * compose tests to take advantage of the concurrency. This is done using
953- * the `+` function. This function will make sure that tests being combined
954- * are compatible according to the `require`s in `+`.
963+ * `aggregateTests` in the companion, which will ensure that aggregation is allowed.
955964 */
956965 final class CompilationTest private (
957966 private [ParallelTesting ] val targets : List [TestSource ],
@@ -969,6 +978,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
969978 def this (targets : List [TestSource ]) =
970979 this (targets, 1 , true , None , false , false )
971980
981+ def copy (targets : List [TestSource ],
982+ times : Int = times,
983+ shouldDelete : Boolean = shouldDelete,
984+ threadLimit : Option [Int ] = threadLimit,
985+ shouldFail : Boolean = shouldFail,
986+ shouldSuppressOutput : Boolean = shouldSuppressOutput): CompilationTest =
987+ CompilationTest (targets, times, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput)
988+
972989 /** Creates a "pos" test run, which makes sure that all tests pass
973990 * compilation without generating errors and that they do not crash the
974991 * compiler
@@ -981,31 +998,29 @@ trait ParallelTesting extends RunnerOrchestration { self =>
981998 if (! shouldFail && test.didFail) {
982999 fail(s " Expected no errors when compiling, failed for the following reason(s): \n ${reasonsForFailure(test)}\n " )
9831000 }
984- else if (shouldFail && ! test.didFail) {
1001+ else if (shouldFail && ! test.didFail && test.skipCount == 0 ) {
9851002 fail(" Pos test should have failed, but didn't" )
9861003 }
9871004
9881005 this
9891006 }
9901007
9911008 /** Creates a "neg" test run, which makes sure that each test generates the
992- * correct amount of errors at the correct positions. It also makes sure
993- * that none of these tests crash the compiler
1009+ * correct number of errors at the correct positions. It also makes sure
1010+ * that none of these tests crashes the compiler.
9941011 */
995- def checkExpectedErrors ()(implicit summaryReport : SummaryReporting ): this .type = {
1012+ def checkExpectedErrors ()(implicit summaryReport : SummaryReporting ): this .type =
9961013 val test = new NegTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
9971014
9981015 cleanup()
9991016
1000- if ( shouldFail && ! test.didFail) {
1017+ if shouldFail && ! test.didFail && test.skipCount == 0 then
10011018 fail(s " Neg test shouldn't have failed, but did. Reasons: \n ${ reasonsForFailure(test) }" )
1002- }
1003- else if (! shouldFail && test.didFail) {
1019+ else if ! shouldFail && test.didFail then
10041020 fail(" Neg test should have failed, but did not" )
1005- }
10061021
10071022 this
1008- }
1023+ end checkExpectedErrors
10091024
10101025 /** Creates a "fuzzy" test run, which makes sure that each test compiles (or not) without crashing */
10111026 def checkNoCrash ()(implicit summaryReport : SummaryReporting ): this .type = {
@@ -1030,12 +1045,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
10301045
10311046 cleanup()
10321047
1033- if ( ! shouldFail && test.didFail) {
1048+ if ! shouldFail && test.didFail then
10341049 fail(s " Run test failed, but should not, reasons: \n ${ reasonsForFailure(test) }" )
1035- }
1036- else if (shouldFail && ! test.didFail) {
1050+ else if shouldFail && ! test.didFail && test.skipCount == 0 then
10371051 fail(" Run test should have failed, but did not" )
1038- }
10391052
10401053 this
10411054 }
@@ -1160,35 +1173,32 @@ trait ParallelTesting extends RunnerOrchestration { self =>
11601173 }
11611174 }
11621175
1163- object CompilationTest {
1176+ object CompilationTest :
11641177
11651178 /** Compose test targets from `tests`
1166- *
1167- * It does this, only if the two tests are compatible. Otherwise it throws
1168- * an `IllegalArgumentException`.
1169- *
1170- * Grouping tests together like this allows us to take advantage of the
1171- * concurrency offered by this test suite as each call to an executing
1172- * method (`pos()` / `checkExpectedErrors()`/ `run()`) will spin up a thread pool with the
1173- * maximum allowed level of concurrency. Doing this for only a few targets
1174- * does not yield any real benefit over sequential compilation.
1175- *
1176- * As such, each `CompilationTest` should contain as many targets as
1177- * possible.
1178- */
1179- def aggregateTests (tests : CompilationTest * ): CompilationTest = {
1179+ *
1180+ * It does this, only if all the tests are mutally compatible.
1181+ * Otherwise it throws an `IllegalArgumentException`.
1182+ *
1183+ * Grouping tests together like this allows us to take advantage of the
1184+ * concurrency offered by this test suite, as each call to an executing
1185+ * method (`pos()` / `checkExpectedErrors()`/ `run()`) will spin up a thread pool with the
1186+ * maximum allowed level of concurrency. Doing this for only a few targets
1187+ * does not yield any real benefit over sequential compilation.
1188+ *
1189+ * As such, each `CompilationTest` should contain as many targets as
1190+ * possible.
1191+ */
1192+ def aggregateTests (tests : CompilationTest * ): CompilationTest =
11801193 assert(tests.nonEmpty)
1181- def aggregate (test1 : CompilationTest , test2 : CompilationTest ) = {
1194+ def aggregate (test1 : CompilationTest , test2 : CompilationTest ) =
11821195 require(test1.times == test2.times, " can't combine tests that are meant to be benchmark compiled" )
11831196 require(test1.shouldDelete == test2.shouldDelete, " can't combine tests that differ on deleting output" )
11841197 require(test1.shouldFail == test2.shouldFail, " can't combine tests that have different expectations on outcome" )
11851198 require(test1.shouldSuppressOutput == test2.shouldSuppressOutput, " can't combine tests that both suppress and don't suppress output" )
1186- new CompilationTest (test1.targets ++ test2.targets, test1.times, test1.shouldDelete, test1.threadLimit, test1.shouldFail, test1.shouldSuppressOutput)
1187- }
1199+ test1.copy(test1.targets ++ test2.targets) // what if thread limit differs? currently threads are limited on aggregate only
11881200 tests.reduce(aggregate)
1189- }
1190-
1191- }
1201+ end CompilationTest
11921202
11931203 /** Create out directory for directory `d` */
11941204 def createOutputDirsForDir (d : JFile , sourceDir : JFile , outDir : String ): JFile = {
0 commit comments