@@ -281,10 +281,12 @@ trait ParallelTesting extends RunnerOrchestration { self =>
281
281
private final def onComplete (testSource : TestSource , reportersOrCrash : Try [Seq [TestReporter ]], logger : LoggedRunnable ): Unit =
282
282
reportersOrCrash match {
283
283
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 _ =>
288
290
}
289
291
290
292
/**
@@ -391,6 +393,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
391
393
/** Number of failed tests */
392
394
def failureCount : Int = _failureCount
393
395
396
+ private var _skipCount = 0
397
+ protected final def registerSkip (): Unit = synchronized { _skipCount += 1 }
398
+ def skipCount : Int = _skipCount
399
+
394
400
protected def logBuildInstructions (testSource : TestSource , reporters : Seq [TestReporter ]) = {
395
401
val (errCount, warnCount) = countErrorsAndWarnings(reporters)
396
402
val errorMsg = testSource.buildInstructions(errCount, warnCount)
@@ -464,13 +470,13 @@ trait ParallelTesting extends RunnerOrchestration { self =>
464
470
val toolArgs = toolArgsFor(files.toList.map(_.toPath), getCharsetFromEncodingOpt(flags0))
465
471
466
472
val spec = raw " (\d+)(\+)? " .r
467
- val testFilter = toolArgs.get(ToolName .Test ) match
473
+ val testIsFiltered = toolArgs.get(ToolName .Test ) match
468
474
case Some (" -jvm" :: spec(n, more) :: Nil ) =>
469
475
if more == " +" then isJavaAtLeast(n) else javaSpecVersion == n
470
476
case Some (args) => throw new IllegalStateException (args.mkString(" unknown test option: " , " , " , " " ))
471
477
case None => true
472
478
473
- def scalacOptions = toolArgs.get (ToolName .Scalac ).getOrElse( Nil )
479
+ def scalacOptions = toolArgs.getOrElse (ToolName .Scalac , Nil )
474
480
475
481
val flags = flags0
476
482
.and(scalacOptions : _* )
@@ -509,7 +515,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
509
515
510
516
val allArgs = flags.all
511
517
512
- if testFilter then
518
+ if testIsFiltered then
513
519
// If a test contains a Java file that cannot be parsed by Dotty's Java source parser, its
514
520
// name must contain the string "JAVA_ONLY".
515
521
val dottyFiles = files.filterNot(_.getName.contains(" JAVA_ONLY" )).map(_.getPath)
@@ -523,6 +529,9 @@ trait ParallelTesting extends RunnerOrchestration { self =>
523
529
echo(s " \n java compilation failed: \n ${ javaErrors.get }" )
524
530
fail(failure = JavaCompilationFailure (javaErrors.get))
525
531
}
532
+ else
533
+ registerSkip()
534
+ reporter.setSkip()
526
535
end if
527
536
528
537
reporter
@@ -724,7 +733,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
724
733
}
725
734
726
735
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()
728
737
else runMain(testSource.runClassPath, testSource.allToolArgs) match {
729
738
case Success (output) => checkFile match {
730
739
case Some (file) if file.exists => diffTest(testSource, file, output.linesIterator.toList, reporters, logger)
@@ -748,7 +757,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
748
757
extends Test (testSources, times, threadLimit, suppressAllOutput) {
749
758
override def suppressErrors = true
750
759
751
- override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = {
760
+ override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] =
752
761
def compilerCrashed = reporters.exists(_.compilerCrashed)
753
762
lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
754
763
lazy val actualErrors = reporters.foldLeft(0 )(_ + _.errorCount)
@@ -772,20 +781,21 @@ trait ParallelTesting extends RunnerOrchestration { self =>
772
781
else if ! errorMap.isEmpty then s " \n Expected error(s) have {<error position>=<unreported error>}: $errorMap"
773
782
else null
774
783
}
775
- }
784
+ end maybeFailureMessage
776
785
777
786
override def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit =
778
787
checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger))
779
788
780
789
def reporterOutputLines (reporters : Seq [TestReporter ]): List [String ] =
781
790
reporters.flatMap(_.consoleOutput.split(" \n " )).toList
782
791
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.
786
796
//
787
797
// 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`
789
799
def getErrorMapAndExpectedCount (files : Seq [JFile ]): (HashMap [String , Integer ], Int ) =
790
800
val comment = raw " //( *)(nopos-|anypos-)?error " .r
791
801
val errorMap = new HashMap [String , Integer ]()
@@ -950,8 +960,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
950
960
* ===============
951
961
* Since this is a parallel test suite, it is essential to be able to
952
962
* 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.
955
964
*/
956
965
final class CompilationTest private (
957
966
private [ParallelTesting ] val targets : List [TestSource ],
@@ -969,6 +978,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
969
978
def this (targets : List [TestSource ]) =
970
979
this (targets, 1 , true , None , false , false )
971
980
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
+
972
989
/** Creates a "pos" test run, which makes sure that all tests pass
973
990
* compilation without generating errors and that they do not crash the
974
991
* compiler
@@ -981,31 +998,29 @@ trait ParallelTesting extends RunnerOrchestration { self =>
981
998
if (! shouldFail && test.didFail) {
982
999
fail(s " Expected no errors when compiling, failed for the following reason(s): \n ${reasonsForFailure(test)}\n " )
983
1000
}
984
- else if (shouldFail && ! test.didFail) {
1001
+ else if (shouldFail && ! test.didFail && test.skipCount == 0 ) {
985
1002
fail(" Pos test should have failed, but didn't" )
986
1003
}
987
1004
988
1005
this
989
1006
}
990
1007
991
1008
/** 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.
994
1011
*/
995
- def checkExpectedErrors ()(implicit summaryReport : SummaryReporting ): this .type = {
1012
+ def checkExpectedErrors ()(implicit summaryReport : SummaryReporting ): this .type =
996
1013
val test = new NegTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
997
1014
998
1015
cleanup()
999
1016
1000
- if ( shouldFail && ! test.didFail) {
1017
+ if shouldFail && ! test.didFail && test.skipCount == 0 then
1001
1018
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
1004
1020
fail(" Neg test should have failed, but did not" )
1005
- }
1006
1021
1007
1022
this
1008
- }
1023
+ end checkExpectedErrors
1009
1024
1010
1025
/** Creates a "fuzzy" test run, which makes sure that each test compiles (or not) without crashing */
1011
1026
def checkNoCrash ()(implicit summaryReport : SummaryReporting ): this .type = {
@@ -1030,12 +1045,10 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1030
1045
1031
1046
cleanup()
1032
1047
1033
- if ( ! shouldFail && test.didFail) {
1048
+ if ! shouldFail && test.didFail then
1034
1049
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
1037
1051
fail(" Run test should have failed, but did not" )
1038
- }
1039
1052
1040
1053
this
1041
1054
}
@@ -1160,35 +1173,32 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1160
1173
}
1161
1174
}
1162
1175
1163
- object CompilationTest {
1176
+ object CompilationTest :
1164
1177
1165
1178
/** 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 =
1180
1193
assert(tests.nonEmpty)
1181
- def aggregate (test1 : CompilationTest , test2 : CompilationTest ) = {
1194
+ def aggregate (test1 : CompilationTest , test2 : CompilationTest ) =
1182
1195
require(test1.times == test2.times, " can't combine tests that are meant to be benchmark compiled" )
1183
1196
require(test1.shouldDelete == test2.shouldDelete, " can't combine tests that differ on deleting output" )
1184
1197
require(test1.shouldFail == test2.shouldFail, " can't combine tests that have different expectations on outcome" )
1185
1198
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
1188
1200
tests.reduce(aggregate)
1189
- }
1190
-
1191
- }
1201
+ end CompilationTest
1192
1202
1193
1203
/** Create out directory for directory `d` */
1194
1204
def createOutputDirsForDir (d : JFile , sourceDir : JFile , outDir : String ): JFile = {
0 commit comments