@@ -226,14 +226,14 @@ trait ParallelTesting extends RunnerOrchestration { self =>
226
226
Try (testSource match {
227
227
case testSource @ JointCompilationSource (name, files, flags, outDir, fromTasty, decompilation) =>
228
228
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)
231
231
List (reporter)
232
232
233
233
case testSource @ SeparateCompilationSource (_, dir, flags, outDir) =>
234
234
testSource.compilationGroups.map { (group, files) =>
235
235
if group.compiler.isEmpty then
236
- compile(files, flags, suppressErrors, outDir)
236
+ compile(files, flags, outDir)
237
237
else
238
238
compileWithOtherCompiler(group.compiler, files, flags, outDir)
239
239
}
@@ -469,7 +469,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
469
469
registerCompletion()
470
470
throw e
471
471
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 = {
473
473
import scala .util .Properties .*
474
474
475
475
def flattenFiles (f : JFile ): Array [JFile ] =
@@ -634,7 +634,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
634
634
635
635
reporter
636
636
637
- protected def compileFromTasty (flags0 : TestFlags , suppressErrors : Boolean , targetDir : JFile ): TestReporter = {
637
+ protected def compileFromTasty (flags0 : TestFlags , targetDir : JFile ): TestReporter = {
638
638
val tastyOutput = new JFile (targetDir.getPath + " _from-tasty" )
639
639
tastyOutput.mkdir()
640
640
val flags = flags0 and (" -d" , tastyOutput.getPath) and " -from-tasty"
@@ -653,6 +653,12 @@ trait ParallelTesting extends RunnerOrchestration { self =>
653
653
private def mkLogLevel = if suppressErrors || suppressAllOutput then ERROR + 1 else ERROR
654
654
private def mkReporter = TestReporter .reporter(realStdout, logLevel = mkLogLevel)
655
655
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
+
656
662
private [ParallelTesting ] def executeTestSuite (): this .type = {
657
663
assert(testSourcesCompleted == 0 , " not allowed to re-use a `CompileRun`" )
658
664
if filteredSources.nonEmpty then
@@ -717,6 +723,80 @@ trait ParallelTesting extends RunnerOrchestration { self =>
717
723
private final class PosTest (testSources : List [TestSource ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
718
724
extends Test (testSources, times, threadLimit, suppressAllOutput)
719
725
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
+
720
800
private final class RewriteTest (testSources : List [TestSource ], checkFiles : Map [JFile , JFile ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
721
801
extends Test (testSources, times, threadLimit, suppressAllOutput) {
722
802
private def verifyOutput (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ) = {
@@ -808,10 +888,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
808
888
end maybeFailureMessage
809
889
810
890
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)
815
892
816
893
// In neg-tests we allow two or three types of error annotations.
817
894
// Normally, `// error` must be annotated on the correct line number.
@@ -1014,20 +1091,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1014
1091
* compilation without generating errors and that they do not crash the
1015
1092
* compiler
1016
1093
*/
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" )
1021
1096
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" )
1031
1099
1032
1100
/** Creates a "neg" test run, which makes sure that each test generates the
1033
1101
* correct number of errors at the correct positions. It also makes sure
@@ -1047,35 +1115,16 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1047
1115
end checkExpectedErrors
1048
1116
1049
1117
/** 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" )
1061
1120
1062
1121
/** Creates a "run" test run, which is a superset of "pos". In addition to
1063
1122
* making sure that all tests pass compilation and that they do not crash
1064
1123
* the compiler; it also makes sure that all tests can run with the
1065
1124
* expected output
1066
1125
*/
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" )
1079
1128
1080
1129
/** Tests `-rewrite`, which makes sure that the rewritten files still compile
1081
1130
* and agree with the expected result (if specified).
@@ -1100,15 +1149,34 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1100
1149
target.copy(dir = copyToDir(outDir, dir))
1101
1150
}
1102
1151
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()
1104
1171
1105
1172
cleanup()
1106
1173
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 " )
1109
1178
1110
1179
this
1111
- }
1112
1180
1113
1181
/** Deletes output directories and files */
1114
1182
private def cleanup (): this .type = {
0 commit comments