Skip to content

Commit ad4c4ac

Browse files
authored
Test can be skipped (#15463)
2 parents 28a851d + c9f692a commit ad4c4ac

File tree

7 files changed

+65
-50
lines changed

7 files changed

+65
-50
lines changed

compiler/test/dotty/tools/dotc/reporting/TestReporter.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
3232
private var _didCrash = false
3333
final def compilerCrashed: Boolean = _didCrash
3434

35+
private var _skip: Boolean = false
36+
final def setSkip(): Unit = _skip = true
37+
final def skipped: Boolean = _skip
38+
3539
protected final def inlineInfo(pos: SourcePosition)(using Context): String =
3640
if (pos.exists) {
3741
if (pos.outer.exists)

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

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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\tat " + _).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"\njava 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"\nExpected 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 = {

tests/neg-with-compiler/t12290/SCALA_ONLY_Invalid1.java renamed to tests/neg/t12290/SCALA_ONLY_Invalid1.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// note that scala's java parser doesn't care about the platform version
12
class SCALA_ONLY_Invalid1 {
23

34
public static final String badOpeningDelimiter = """non-whitespace // error
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)