@@ -226,14 +226,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
226226 Try (testSource match {
227227 case testSource @ JointCompilationSource (name, files, flags, outDir, fromTasty, decompilation) =>
228228 val reporter =
229- if (fromTasty) compileFromTasty(flags, suppressErrors, outDir)
230- else compile(testSource.sourceFiles, flags, suppressErrors, outDir)
229+ if (fromTasty) compileFromTasty(flags, outDir)
230+ else compile(testSource.sourceFiles, flags, outDir)
231231 List (reporter)
232232
233233 case testSource @ SeparateCompilationSource (_, dir, flags, outDir) =>
234234 testSource.compilationGroups.map { (group, files) =>
235235 if group.compiler.isEmpty then
236- compile(files, flags, suppressErrors, outDir)
236+ compile(files, flags, outDir)
237237 else
238238 compileWithOtherCompiler(group.compiler, files, flags, outDir)
239239 }
@@ -469,7 +469,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
469469 registerCompletion()
470470 throw e
471471
472- protected def compile (files0 : Array [JFile ], flags0 : TestFlags , suppressErrors : Boolean , targetDir : JFile ): TestReporter = {
472+ protected def compile (files0 : Array [JFile ], flags0 : TestFlags , targetDir : JFile ): TestReporter = {
473473 import scala .util .Properties .*
474474
475475 def flattenFiles (f : JFile ): Array [JFile ] =
@@ -634,7 +634,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
634634
635635 reporter
636636
637- protected def compileFromTasty (flags0 : TestFlags , suppressErrors : Boolean , targetDir : JFile ): TestReporter = {
637+ protected def compileFromTasty (flags0 : TestFlags , targetDir : JFile ): TestReporter = {
638638 val tastyOutput = new JFile (targetDir.getPath + " _from-tasty" )
639639 tastyOutput.mkdir()
640640 val flags = flags0 and (" -d" , tastyOutput.getPath) and " -from-tasty"
@@ -653,6 +653,12 @@ trait ParallelTesting extends RunnerOrchestration { self =>
653653 private def mkLogLevel = if suppressErrors || suppressAllOutput then ERROR + 1 else ERROR
654654 private def mkReporter = TestReporter .reporter(realStdout, logLevel = mkLogLevel)
655655
656+ protected def diffCheckfile (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ) =
657+ checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger))
658+
659+ private def reporterOutputLines (reporters : Seq [TestReporter ]): List [String ] =
660+ reporters.flatMap(_.consoleOutput.split(" \n " )).toList
661+
656662 private [ParallelTesting ] def executeTestSuite (): this .type = {
657663 assert(testSourcesCompleted == 0 , " not allowed to re-use a `CompileRun`" )
658664 if filteredSources.nonEmpty then
@@ -717,6 +723,80 @@ trait ParallelTesting extends RunnerOrchestration { self =>
717723 private final class PosTest (testSources : List [TestSource ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
718724 extends Test (testSources, times, threadLimit, suppressAllOutput)
719725
726+ private final class WarnTest (testSources : List [TestSource ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
727+ extends Test (testSources, times, threadLimit, suppressAllOutput):
728+ override def suppressErrors = true
729+ override def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit =
730+ diffCheckfile(testSource, reporters, logger)
731+
732+ override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] =
733+ lazy val (map, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
734+ lazy val obtCount = reporters.foldLeft(0 )(_ + _.warningCount)
735+ lazy val (expected, unexpected) = getMissingExpectedWarnings(map, reporters.iterator.flatMap(_.diagnostics))
736+ def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty
737+ def showDiagnostics = " -> following the diagnostics:\n " +
738+ reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s " ${e.pos.line + 1 }: ${e.message}" )).mkString(" at " , " \n at " , " " )
739+ Option :
740+ if reporters.exists(_.compilerCrashed) then s " Compiler crashed when compiling: ${testSource.title}"
741+ else if reporters.exists(_.errorCount > 0 ) then
742+ s """ Compilation failed for: ${testSource.title}
743+ | $showDiagnostics
744+ | """ .stripMargin.trim.linesIterator.mkString(" \n " , " \n " , " " )
745+ else if obtCount == 0 then s " \n No warnings found when compiling warn test $testSource"
746+ else if expCount == 0 then s " \n No warning expected/defined in $testSource -- use // warn "
747+ else if expCount != obtCount then
748+ s """ |Wrong number of warnings encountered when compiling $testSource
749+ |expected: $expCount, actual: $obtCount
750+ | ${expected.mkString(" Unfulfilled expectations:\n " , " \n " , " " )}
751+ | ${unexpected.mkString(" Unexpected warnings:\n " , " \n " , " " )}
752+ | $showDiagnostics
753+ | """ .stripMargin.trim.linesIterator.mkString(" \n " , " \n " , " " )
754+ else if hasMissingAnnotations then s " \n Warnings found on incorrect row numbers when compiling $testSource\n $showDiagnostics"
755+ else if ! map.isEmpty then s " \n Expected warnings(s) have {<warning position>=<unreported warning>}: $map"
756+ else null
757+ end maybeFailureMessage
758+
759+ def getWarnMapAndExpectedCount (files : Seq [JFile ]): (HashMap [String , Integer ], Int ) =
760+ val comment = raw " //( *)warn " .r
761+ val map = new HashMap [String , Integer ]()
762+ var count = 0
763+ def bump (key : String ): Unit =
764+ map.get(key) match
765+ case null => map.put(key, 1 )
766+ case n => map.put(key, n+ 1 )
767+ count += 1
768+ files.filter(isSourceFile).foreach { file =>
769+ Using (Source .fromFile(file, StandardCharsets .UTF_8 .name)) { source =>
770+ source.getLines.zipWithIndex.foreach { case (line, lineNbr) =>
771+ comment.findAllMatchIn(line).foreach { _ =>
772+ bump(s " ${file.getPath}: ${lineNbr+ 1 }" )
773+ }
774+ }
775+ }.get
776+ }
777+ (map, count)
778+
779+ def getMissingExpectedWarnings (map : HashMap [String , Integer ], reporterWarnings : Iterator [Diagnostic ]): (List [String ], List [String ]) =
780+ val unexpected, unpositioned = ListBuffer .empty[String ]
781+ def relativize (path : String ): String = path.split(JFile .separatorChar).dropWhile(_ != " tests" ).mkString(JFile .separator)
782+ def seenAt (key : String ): Boolean =
783+ map.get(key) match
784+ case null => false
785+ case 1 => map.remove(key) ; true
786+ case n => map.put(key, n - 1 ) ; true
787+ def sawDiagnostic (d : Diagnostic ): Unit =
788+ val srcpos = d.pos.nonInlined
789+ if srcpos.exists then
790+ val key = s " ${relativize(srcpos.source.file.toString())}: ${srcpos.line + 1 }"
791+ if ! seenAt(key) then unexpected += key
792+ else
793+ unpositioned += relativize(srcpos.source.file.toString())
794+
795+ reporterWarnings.foreach(sawDiagnostic)
796+
797+ (map.asScala.keys.toList, (unexpected ++ unpositioned).toList)
798+ end getMissingExpectedWarnings
799+
720800 private final class RewriteTest (testSources : List [TestSource ], checkFiles : Map [JFile , JFile ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
721801 extends Test (testSources, times, threadLimit, suppressAllOutput) {
722802 private def verifyOutput (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ) = {
@@ -808,10 +888,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
808888 end maybeFailureMessage
809889
810890 override def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit =
811- checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger))
812-
813- def reporterOutputLines (reporters : Seq [TestReporter ]): List [String ] =
814- reporters.flatMap(_.consoleOutput.split(" \n " )).toList
891+ diffCheckfile(testSource, reporters, logger)
815892
816893 // In neg-tests we allow two or three types of error annotations.
817894 // Normally, `// error` must be annotated on the correct line number.
@@ -1014,20 +1091,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
10141091 * compilation without generating errors and that they do not crash the
10151092 * compiler
10161093 */
1017- def checkCompile ()(implicit summaryReport : SummaryReporting ): this .type = {
1018- val test = new PosTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
1019-
1020- cleanup()
1094+ def checkCompile ()(implicit summaryReport : SummaryReporting ): this .type =
1095+ checkPass(new PosTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput), " Pos" )
10211096
1022- if (! shouldFail && test.didFail) {
1023- fail(s " Expected no errors when compiling, failed for the following reason(s): \n ${reasonsForFailure(test)}\n " )
1024- }
1025- else if (shouldFail && ! test.didFail && test.skipCount == 0 ) {
1026- fail(" Pos test should have failed, but didn't" )
1027- }
1028-
1029- this
1030- }
1097+ def checkWarnings ()(implicit summaryReport : SummaryReporting ): this .type =
1098+ checkPass(new WarnTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput), " Warn" )
10311099
10321100 /** Creates a "neg" test run, which makes sure that each test generates the
10331101 * correct number of errors at the correct positions. It also makes sure
@@ -1047,35 +1115,16 @@ trait ParallelTesting extends RunnerOrchestration { self =>
10471115 end checkExpectedErrors
10481116
10491117 /** Creates a "fuzzy" test run, which makes sure that each test compiles (or not) without crashing */
1050- def checkNoCrash ()(implicit summaryReport : SummaryReporting ): this .type = {
1051- val test = new NoCrashTest (targets, times, threadLimit, shouldSuppressOutput).executeTestSuite()
1052-
1053- cleanup()
1054-
1055- if (test.didFail) {
1056- fail(" Fuzzy test shouldn't have crashed, but did" )
1057- }
1058-
1059- this
1060- }
1118+ def checkNoCrash ()(implicit summaryReport : SummaryReporting ): this .type =
1119+ checkFail(new NoCrashTest (targets, times, threadLimit, shouldSuppressOutput), " Fuzzy" )
10611120
10621121 /** Creates a "run" test run, which is a superset of "pos". In addition to
10631122 * making sure that all tests pass compilation and that they do not crash
10641123 * the compiler; it also makes sure that all tests can run with the
10651124 * expected output
10661125 */
1067- def checkRuns ()(implicit summaryReport : SummaryReporting ): this .type = {
1068- val test = new RunTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
1069-
1070- cleanup()
1071-
1072- if ! shouldFail && test.didFail then
1073- fail(s " Run test failed, but should not, reasons: \n ${ reasonsForFailure(test) }" )
1074- else if shouldFail && ! test.didFail && test.skipCount == 0 then
1075- fail(" Run test should have failed, but did not" )
1076-
1077- this
1078- }
1126+ def checkRuns ()(implicit summaryReport : SummaryReporting ): this .type =
1127+ checkPass(new RunTest (targets, times, threadLimit, shouldFail || shouldSuppressOutput), " Run" )
10791128
10801129 /** Tests `-rewrite`, which makes sure that the rewritten files still compile
10811130 * and agree with the expected result (if specified).
@@ -1100,15 +1149,34 @@ trait ParallelTesting extends RunnerOrchestration { self =>
11001149 target.copy(dir = copyToDir(outDir, dir))
11011150 }
11021151
1103- val test = new RewriteTest (copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
1152+ val test = new RewriteTest (copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput)
1153+
1154+ checkFail(test, " Rewrite" )
1155+ }
1156+
1157+ private def checkPass (test : Test , desc : String ): this .type =
1158+ test.executeTestSuite()
1159+
1160+ cleanup()
1161+
1162+ if ! shouldFail && test.didFail then
1163+ fail(s " $desc test failed, but should not, reasons: \n ${reasonsForFailure(test)}" )
1164+ else if shouldFail && ! test.didFail && test.skipCount == 0 then
1165+ fail(s " $desc test should have failed, but didn't " )
1166+
1167+ this
1168+
1169+ private def checkFail (test : Test , desc : String ): this .type =
1170+ test.executeTestSuite()
11041171
11051172 cleanup()
11061173
1107- if test.didFail then
1108- fail(" Rewrite test failed" )
1174+ if shouldFail && ! test.didFail && test.skipCount == 0 then
1175+ fail(s " $desc test shouldn't have failed, but did. Reasons: \n ${reasonsForFailure(test)}" )
1176+ else if ! shouldFail && test.didFail then
1177+ fail(s " $desc test failed " )
11091178
11101179 this
1111- }
11121180
11131181 /** Deletes output directories and files */
11141182 private def cleanup (): this .type = {
0 commit comments