@@ -12,7 +12,7 @@ import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors
12
12
13
13
import scala .collection .mutable
14
14
import scala .io .Source
15
- import scala .util .{Random , Try }
15
+ import scala .util .{Random , Try , Failure => TryFailure , Success => TrySuccess }
16
16
import scala .util .control .NonFatal
17
17
import scala .util .matching .Regex
18
18
@@ -142,7 +142,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
142
142
fromTasty : Boolean = false ,
143
143
decompilation : Boolean = false
144
144
) extends TestSource {
145
- def sourceFiles : Array [JFile ] = files.filter(isSourceFile).toArray
145
+ def sourceFiles : Array [JFile ] = files.filter(isSourceFile)
146
146
147
147
override def toString () = outDir.toString
148
148
}
@@ -180,23 +180,27 @@ trait ParallelTesting extends RunnerOrchestration { self =>
180
180
}
181
181
.toList.sortBy(_._1).map(_._2.filter(isSourceFile).sorted)
182
182
183
- def sourceFiles : Array [JFile ] = compilationGroups.flatten.filter(isSourceFile). toArray
183
+ def sourceFiles : Array [JFile ] = compilationGroups.flatten.toArray
184
184
}
185
185
186
186
private trait CompilationLogic { this : Test =>
187
- val suppressErrors = false
187
+ def suppressErrors = false
188
188
189
- final def compileTestSource (testSource : TestSource ): Either [Throwable , List [TestReporter ]] =
189
+ /**
190
+ * Compiles the test source.
191
+ * @return The reporters containing the results of all the compilation runs for this test source.
192
+ */
193
+ private final def compileTestSource (testSource : TestSource ): Try [List [TestReporter ]] =
190
194
Try (testSource match {
191
195
case testSource @ JointCompilationSource (name, files, flags, outDir, fromTasty, decompilation) =>
192
196
val reporter =
193
- if (fromTasty) compileFromTasty( flags, suppressErrors, outDir)
194
- else compile(testSource.sourceFiles, flags, suppressErrors, outDir)
197
+ if (fromTasty) compileFromTasty(flags, suppressErrors, outDir)
198
+ else compile(testSource.sourceFiles, flags, suppressErrors, outDir)
195
199
List (reporter)
196
200
197
201
case testSource @ SeparateCompilationSource (_, dir, flags, outDir) =>
198
202
testSource.compilationGroups.map(files => compile(files, flags, suppressErrors, outDir)) // TODO? only `compile` option?
199
- }).toEither
203
+ })
200
204
201
205
final def countErrorsAndWarnings (reporters : Seq [TestReporter ]): (Int , Int ) =
202
206
reporters.foldLeft((0 , 0 )) { case ((err, warn), r) => (err + r.errorCount, warn + r.warningCount) }
@@ -205,14 +209,22 @@ trait ParallelTesting extends RunnerOrchestration { self =>
205
209
final def countWarnings (reporters : Seq [TestReporter ]) = countErrorsAndWarnings(reporters)._2
206
210
final def reporterFailed (r : TestReporter ) = r.compilerCrashed || r.errorCount > 0
207
211
212
+ /**
213
+ * For a given test source, returns a check file against which the result of the test run
214
+ * should be compared. Is used by implementations of this trait.
215
+ */
208
216
final def checkFile (testSource : TestSource ): Option [JFile ] = (testSource match {
209
217
case ts : JointCompilationSource =>
210
- ts.files.filter(f => ! f.isDirectory).map { f => new JFile (f.getAbsolutePath.replaceFirst(" \\ .scala$" , " .check" )) }.headOption
218
+ ts.files.collectFirst { case f if ! f.isDirectory => new JFile (f.getAbsolutePath.replaceFirst(" \\ .scala$" , " .check" )) }
211
219
212
220
case ts : SeparateCompilationSource =>
213
221
Option (new JFile (ts.dir.getAbsolutePath + " .check" ))
214
222
}).filter(_.exists)
215
223
224
+ /**
225
+ * Checks if the given actual lines are the same as the ones in the check file.
226
+ * If not, fails the test.
227
+ */
216
228
final def diffTest (testSource : TestSource , checkFile : JFile , actual : List [String ]) = {
217
229
val expected = Source .fromFile(checkFile, " UTF-8" ).getLines().toList
218
230
for (msg <- diffMessage(testSource.title, actual, expected)) {
@@ -222,6 +234,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
222
234
}
223
235
}
224
236
237
+ /** Entry point: runs the test */
225
238
final def encapsulatedCompilation (testSource : TestSource ) = new LoggedRunnable { self =>
226
239
def checkTestSource (): Unit = tryCompile(testSource) {
227
240
val reportersOrCrash = compileTestSource(testSource)
@@ -230,17 +243,34 @@ trait ParallelTesting extends RunnerOrchestration { self =>
230
243
}
231
244
}
232
245
233
- final def onComplete (testSource : TestSource , reportersOrCrash : Either [Throwable , Seq [TestReporter ]], logger : LoggedRunnable ): Unit =
234
- reportersOrCrash.fold(
235
- exn => onFailure(testSource, Nil , logger, Some (s " Fatal compiler crash when compiling: ${testSource.title}: \n ${exn.getMessage}\n ${exn.getStackTrace.mkString(" \n " )}" ))
236
- , reporters => testFailed(testSource, reporters).fold
237
- ( onSuccess(testSource, reporters, logger ) )
238
- (msg => onFailure(testSource, reporters, logger, Option (msg).filter(_.nonEmpty)) ) )
246
+ /** This callback is executed once the compilation of this test source finished */
247
+ final def onComplete (testSource : TestSource , reportersOrCrash : Try [Seq [TestReporter ]], logger : LoggedRunnable ): Unit =
248
+ reportersOrCrash match {
249
+ case TryFailure (exn) => onFailure(testSource, Nil , logger, Some (s " Fatal compiler crash when compiling: ${testSource.title}: \n ${exn.getMessage}\n ${exn.getStackTrace.mkString(" \n " )}" ))
250
+ case TrySuccess (reporters) => maybeFailureMessage(testSource, reporters) match {
251
+ case Some (msg) => onFailure(testSource, reporters, logger, Option (msg).filter(_.nonEmpty))
252
+ case None => onSuccess(testSource, reporters, logger)
253
+ }
254
+ }
239
255
240
- def testFailed (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] =
241
- Option (reporters.exists(reporterFailed)).filter(identity).map(_ => s " Compilation failed for: ' ${testSource.title}' " )
256
+ /**
257
+ * Based on the reporters obtained after the compilation, determines if this test has failed.
258
+ * If it has, returns a Some with an error message. Otherwise, returns None.
259
+ * As the conditions of failure are different for different test types, this method should be
260
+ * overridden by the concrete implementations of this trait.
261
+ */
262
+ def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] =
263
+ if (reporters.exists(reporterFailed)) Some (s " Compilation failed for: ' ${testSource.title}' " )
264
+ else None
242
265
266
+ /**
267
+ * If the test has compiled successfully, this callback will be called. You can still fail the test from this callback.
268
+ */
243
269
def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit = ()
270
+
271
+ /**
272
+ * If the test failed to compile or the compiler crashed, this callback will be called.
273
+ */
244
274
def onFailure (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable , message : Option [String ]): Unit = {
245
275
message.foreach(echo)
246
276
reporters.filter(reporterFailed).foreach(logger.logReporterContents)
@@ -623,9 +653,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
623
653
624
654
private def verifyOutput (checkFile : Option [JFile ], dir : JFile , testSource : TestSource , warnings : Int ) = {
625
655
if (Properties .testsNoRun) addNoRunWarning()
626
- else runMain(testSource.runClassPath) match {
627
- case Success (_) if ! checkFile.isDefined || ! checkFile.get.exists =>
628
- case Success (output) => checkFile.foreach(diffTest(testSource, _, output.linesIterator.toList))
656
+ else runMain(testSource.runClassPath) match {
657
+ case Success (output) => checkFile match {
658
+ case Some (file) if file.exists => diffTest(testSource, file, output.linesIterator.toList)
659
+ case _ =>
660
+ }
629
661
case Failure (output) =>
630
662
echo(s " Test ' ${testSource.title}' failed with output: " )
631
663
echo(output)
@@ -642,20 +674,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
642
674
643
675
private final class NegTest (testSources : List [TestSource ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
644
676
extends Test (testSources, times, threadLimit, suppressAllOutput) {
645
- override val suppressErrors = true
677
+ override def suppressErrors = true
646
678
647
- override def testFailed (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = {
648
- val compilerCrashed = reporters.exists(_.compilerCrashed)
649
- val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles)
650
- val actualErrors = reporters.foldLeft(0 )(_ + _.errorCount)
651
- val hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
679
+ override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = {
680
+ def compilerCrashed = reporters.exists(_.compilerCrashed)
681
+ lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles)
682
+ lazy val actualErrors = reporters.foldLeft(0 )(_ + _.errorCount)
683
+ def hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
652
684
653
- if (compilerCrashed ) Some (s " Compiler crashed when compiling: ${testSource.title}" )
654
- else if (actualErrors == 0 ) Some (s " \n No errors found when compiling neg test $testSource" )
685
+ if (compilerCrashed) Some (s " Compiler crashed when compiling: ${testSource.title}" )
686
+ else if (actualErrors == 0 ) Some (s " \n No errors found when compiling neg test $testSource" )
655
687
else if (expectedErrors != actualErrors) Some (s " \n Wrong number of errors encountered when compiling $testSource, expected: $expectedErrors, actual: $actualErrors" )
656
- else if (hasMissingAnnotations ) Some (s " \n Errors found on incorrect row numbers when compiling $testSource" )
657
- else if (! errorMap.isEmpty ) Some (s " \n Expected error(s) have {<error position>=<unreported error>}: $errorMap" )
658
- else None
688
+ else if (hasMissingAnnotations) Some (s " \n Errors found on incorrect row numbers when compiling $testSource" )
689
+ else if (! errorMap.isEmpty) Some (s " \n Expected error(s) have {<error position>=<unreported error>}: $errorMap" )
690
+ else None
659
691
}
660
692
661
693
override def onSuccess (testSource : TestSource , reporters : Seq [TestReporter ], logger : LoggedRunnable ): Unit =
@@ -717,8 +749,8 @@ trait ParallelTesting extends RunnerOrchestration { self =>
717
749
718
750
private final class NoCrashTest (testSources : List [TestSource ], times : Int , threadLimit : Option [Int ], suppressAllOutput : Boolean )(implicit summaryReport : SummaryReporting )
719
751
extends Test (testSources, times, threadLimit, suppressAllOutput) {
720
- override val suppressErrors = true
721
- override def testFailed (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = None
752
+ override def suppressErrors = true
753
+ override def maybeFailureMessage (testSource : TestSource , reporters : Seq [TestReporter ]): Option [String ] = None
722
754
}
723
755
724
756
def diffMessage (sourceTitle : String , outputLines : Seq [String ], checkLines : Seq [String ]): Option [String ] = {
0 commit comments