@@ -22,6 +22,7 @@ import dotc.reporting.diagnostic.MessageContainer
22
22
import dotc .interfaces .Diagnostic .ERROR
23
23
import dotc .util .DiffUtil
24
24
import dotc .{ Driver , Compiler }
25
+ import dotc .decompiler
25
26
26
27
/** A parallel testing suite whose goal is to integrate nicely with JUnit
27
28
*
@@ -47,7 +48,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
47
48
/** A test source whose files or directory of files is to be compiled
48
49
* in a specific way defined by the `Test`
49
50
*/
50
- private sealed trait TestSource { self =>
51
+ protected sealed trait TestSource { self =>
51
52
def name : String
52
53
def outDir : JFile
53
54
def flags : TestFlags
@@ -133,7 +134,8 @@ trait ParallelTesting extends RunnerOrchestration { self =>
133
134
files : Array [JFile ],
134
135
flags : TestFlags ,
135
136
outDir : JFile ,
136
- fromTasty : Boolean = false
137
+ fromTasty : Boolean = false ,
138
+ decompilation : Boolean = false
137
139
) extends TestSource {
138
140
def sourceFiles : Array [JFile ] = files.filter(isSourceFile)
139
141
@@ -215,7 +217,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
215
217
private val filteredSources =
216
218
if (! testFilter.isDefined) testSources
217
219
else testSources.filter {
218
- case JointCompilationSource (_, files, _, _, _) =>
220
+ case JointCompilationSource (_, files, _, _, _, _ ) =>
219
221
files.exists(file => file.getAbsolutePath.contains(testFilter.get))
220
222
case SeparateCompilationSource (_, dir, _, _) =>
221
223
dir.getAbsolutePath.contains(testFilter.get)
@@ -422,6 +424,32 @@ trait ParallelTesting extends RunnerOrchestration { self =>
422
424
reporter
423
425
}
424
426
427
+ protected def decompile (flags0 : TestFlags , suppressErrors : Boolean , targetDir : JFile ): TestReporter = {
428
+ val decompilationOutput = new JFile (targetDir.getPath)
429
+ decompilationOutput.mkdir()
430
+ val flags = flags0 and (" -d" , decompilationOutput.getAbsolutePath) and " -decompile"
431
+
432
+ def hasTastyFileToClassName (f : JFile ): String =
433
+ targetDir.toPath.relativize(f.toPath).toString.dropRight(" .hasTasty" .length).replace('/' , '.' )
434
+ val classes = flattenFiles(targetDir).filter(isHasTastyFile).map(hasTastyFileToClassName)
435
+
436
+ val reporter =
437
+ TestReporter .reporter(realStdout, logLevel =
438
+ if (suppressErrors || suppressAllOutput) ERROR + 1 else ERROR )
439
+
440
+ val driver = decompiler.Main
441
+
442
+ // Compile with a try to catch any StackTrace generated by the compiler:
443
+ try {
444
+ driver.process(flags.all ++ classes, reporter = reporter)
445
+ }
446
+ catch {
447
+ case NonFatal (ex) => reporter.logStackTrace(ex)
448
+ }
449
+
450
+ reporter
451
+ }
452
+
425
453
private [ParallelTesting ] def executeTestSuite (): this .type = {
426
454
assert(_testSourcesCompleted == 0 , " not allowed to re-use a `CompileRun`" )
427
455
@@ -474,9 +502,52 @@ trait ParallelTesting extends RunnerOrchestration { self =>
474
502
protected def encapsulatedCompilation (testSource : TestSource ) = new LoggedRunnable {
475
503
def checkTestSource (): Unit = tryCompile(testSource) {
476
504
testSource match {
477
- case testSource @ JointCompilationSource (_ , files, flags, outDir, fromTasty) =>
505
+ case testSource @ JointCompilationSource (name , files, flags, outDir, fromTasty, decompilation ) =>
478
506
val reporter =
479
- if (fromTasty) compileFromTasty(flags, false , outDir)
507
+ if (decompilation) {
508
+ val rep = decompile(flags, false , outDir)
509
+
510
+ val checkFileOpt = files.flatMap { file =>
511
+ if (file.isDirectory) Nil
512
+ else {
513
+ val fname = file.getAbsolutePath.reverse.dropWhile(_ != '.' ).reverse + " decompiled"
514
+ val checkFile = new JFile (fname)
515
+ if (checkFile.exists) List (checkFile)
516
+ else Nil
517
+ }
518
+ }.headOption
519
+ checkFileOpt match {
520
+ case Some (checkFile) =>
521
+ val stripTrailingWhitespaces = " (.*\\ S|)\\ s+" .r
522
+ val output = Source .fromFile(outDir + " .decompiled" ).getLines().map {line =>
523
+ stripTrailingWhitespaces.unapplySeq(line).map(_.head).getOrElse(line)
524
+ }.mkString(" \n " )
525
+ .replaceFirst(" @scala\\ .annotation\\ .internal\\ .SourceFile\\ ([^\\ )]+\\ )( |\\ n )" , " " ) // FIXME: should not be printed in the decompiler
526
+
527
+ checkDiff(output, checkFile, testSource, 0 ) match {
528
+ case Some (diff) =>
529
+ println(" Expected:" )
530
+ println(checkFile)
531
+ println(" Actual output;" )
532
+ println(output)
533
+ println(" Diff;" )
534
+ echo(diff)
535
+ addFailureInstruction(diff)
536
+
537
+ // Print build instructions to file and summary:
538
+ val buildInstr = testSource.buildInstructions(0 , rep.warningCount)
539
+ addFailureInstruction(buildInstr)
540
+
541
+ // Fail target:
542
+ failTestSource(testSource)
543
+ case None =>
544
+ }
545
+ case _ =>
546
+ }
547
+
548
+ rep
549
+ }
550
+ else if (fromTasty) compileFromTasty(flags, false , outDir)
480
551
else compile(testSource.sourceFiles, flags, false , outDir)
481
552
registerCompletion(reporter.errorCount)
482
553
@@ -526,39 +597,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
526
597
if (Properties .testsNoRun) addNoRunWarning()
527
598
else runMain(testSource.runClassPath) match {
528
599
case Success (_) if ! checkFile.isDefined || ! checkFile.get.exists => // success!
529
- case Success (output) => {
530
- val outputLines = output.lines.toArray :+ DiffUtil .EOF
531
- val checkLines : Array [String ] = Source .fromFile(checkFile.get).getLines().toArray :+ DiffUtil .EOF
532
- val sourceTitle = testSource.title
533
-
534
- def linesMatch =
535
- outputLines
536
- .zip(checkLines)
537
- .forall { case (x, y) => x == y }
538
-
539
- if (outputLines.length != checkLines.length || ! linesMatch) {
540
- // Print diff to files and summary:
541
- val expectedSize = DiffUtil .EOF .length max checkLines.map(_.length).max
542
- val diff = outputLines.padTo(checkLines.length, " " ).zip(checkLines.padTo(outputLines.length, " " )).map { case (act, exp) =>
543
- DiffUtil .mkColoredLineDiff(exp, act, expectedSize)
544
- }.mkString(" \n " )
545
-
546
- val msg =
547
- s """ |Output from ' $sourceTitle' did not match check file.
548
- |Diff (expected on the left, actual right):
549
- | """ .stripMargin + diff + " \n "
550
- echo(msg)
551
- addFailureInstruction(msg)
552
-
553
- // Print build instructions to file and summary:
554
- val buildInstr = testSource.buildInstructions(0 , warnings)
555
- addFailureInstruction(buildInstr)
556
-
557
- // Fail target:
558
- failTestSource(testSource)
600
+ case Success (output) =>
601
+ checkDiff(output, checkFile.get, testSource, warnings) match {
602
+ case Some (msg) =>
603
+ echo(msg)
604
+ addFailureInstruction(msg)
605
+
606
+ // Print build instructions to file and summary:
607
+ val buildInstr = testSource.buildInstructions(0 , warnings)
608
+ addFailureInstruction(buildInstr)
609
+
610
+ // Fail target:
611
+ failTestSource(testSource)
612
+ case None =>
559
613
}
560
- }
561
-
562
614
case Failure (output) =>
563
615
echo(s " Test ' ${testSource.title}' failed with output: " )
564
616
echo(output)
@@ -573,7 +625,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
573
625
protected def encapsulatedCompilation (testSource : TestSource ) = new LoggedRunnable {
574
626
def checkTestSource (): Unit = tryCompile(testSource) {
575
627
val (compilerCrashed, errorCount, warningCount, verifier : Function0 [Unit ]) = testSource match {
576
- case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty) =>
628
+ case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty, decompilation ) =>
577
629
val checkFile = files.flatMap { file =>
578
630
if (file.isDirectory) Nil
579
631
else {
@@ -682,7 +734,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
682
734
}
683
735
684
736
val (compilerCrashed, expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match {
685
- case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty) =>
737
+ case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty, decompilation ) =>
686
738
val sourceFiles = testSource.sourceFiles
687
739
val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles)
688
740
val reporter = compile(sourceFiles, flags, true , outDir)
@@ -736,6 +788,31 @@ trait ParallelTesting extends RunnerOrchestration { self =>
736
788
}
737
789
}
738
790
791
+ private def checkDiff (output : String , checkFile : JFile , testSource : TestSource , warnings : Int ): Option [String ] = {
792
+ val outputLines = output.lines.toArray :+ DiffUtil .EOF
793
+ val checkLines : Array [String ] = Source .fromFile(checkFile).getLines().toArray :+ DiffUtil .EOF
794
+ val sourceTitle = testSource.title
795
+
796
+ def linesMatch =
797
+ outputLines
798
+ .zip(checkLines)
799
+ .forall { case (x, y) => x == y }
800
+
801
+ if (outputLines.length != checkLines.length || ! linesMatch) {
802
+ // Print diff to files and summary:
803
+ val expectedSize = DiffUtil .EOF .length max checkLines.map(_.length).max
804
+ val diff = outputLines.padTo(checkLines.length, " " ).zip(checkLines.padTo(outputLines.length, " " )).map { case (act, exp) =>
805
+ DiffUtil .mkColoredLineDiff(exp, act, expectedSize)
806
+ }.mkString(" \n " )
807
+
808
+ val msg =
809
+ s """ |Output from ' $sourceTitle' did not match check file.
810
+ |Diff (expected on the left, actual right):
811
+ | """ .stripMargin + diff + " \n "
812
+ Some (msg)
813
+ } else None
814
+ }
815
+
739
816
/** The `CompilationTest` is the main interface to `ParallelTesting`, it
740
817
* can be instantiated via one of the following methods:
741
818
*
@@ -967,7 +1044,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
967
1044
*/
968
1045
def copyToTarget (): CompilationTest = new CompilationTest (
969
1046
targets.map {
970
- case target @ JointCompilationSource (_, files, _, outDir, _) =>
1047
+ case target @ JointCompilationSource (_, files, _, outDir, _, _ ) =>
971
1048
target.copy(files = files.map(copyToDir(outDir,_)))
972
1049
case target @ SeparateCompilationSource (_, dir, _, outDir) =>
973
1050
target.copy(dir = copyToDir(outDir, dir))
@@ -1083,34 +1160,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1083
1160
new CompilationTest (target)
1084
1161
}
1085
1162
1086
- /** Compiles a single file from the string path `f` using the supplied flags
1087
- *
1088
- * Tests in the first part of the tuple must be executed before the second.
1089
- * Both testsRequires explicit delete().
1090
- */
1091
- def compileTasty (f : String , flags : TestFlags )(implicit testGroup : TestGroup ): (CompilationTest , CompilationTest ) = {
1092
- val sourceFile = new JFile (f)
1093
- val parent = sourceFile.getParentFile
1094
- val outDir =
1095
- defaultOutputDir + testGroup + " /" +
1096
- sourceFile.getName.substring(0 , sourceFile.getName.lastIndexOf('.' )) + " /"
1097
-
1098
- require(
1099
- sourceFile.exists && ! sourceFile.isDirectory &&
1100
- (parent ne null ) && parent.exists && parent.isDirectory,
1101
- s " Source file: $f, didn't exist "
1102
- )
1103
- val tastySource = createOutputDirsForFile(sourceFile, parent, outDir)
1104
- val target = JointCompilationSource (
1105
- testGroup.name,
1106
- Array (sourceFile),
1107
- flags.withClasspath(tastySource.getPath) and " -from-tasty" ,
1108
- tastySource,
1109
- fromTasty = true
1110
- )
1111
- (compileFile(f, flags).keepOutput, new CompilationTest (target).keepOutput)
1112
- }
1113
-
1114
1163
/** Compiles a directory `f` using the supplied `flags`. This method does
1115
1164
* deep compilation, that is - it compiles all files and subdirectories
1116
1165
* contained within the directory `f`.
@@ -1216,7 +1265,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1216
1265
* Both testsRequires explicit delete().
1217
1266
*/
1218
1267
def compileTastyInDir (f : String , flags0 : TestFlags , blacklist : Set [String ] = Set .empty)(
1219
- implicit testGroup : TestGroup ): (CompilationTest , CompilationTest ) = {
1268
+ implicit testGroup : TestGroup ): (CompilationTest , CompilationTest , CompilationTest ) = {
1220
1269
val outDir = defaultOutputDir + testGroup + " /"
1221
1270
val flags = flags0 and " -Yretain-trees"
1222
1271
val sourceDir = new JFile (f)
@@ -1231,9 +1280,22 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1231
1280
}
1232
1281
// TODO add SeparateCompilationSource from tasty?
1233
1282
1283
+ val targets2 =
1284
+ files
1285
+ .filter(f => dotty.tools.io.File (f.toPath).changeExtension(" decompiled" ).exists)
1286
+ .map { f =>
1287
+ val classpath = createOutputDirsForFile(f, sourceDir, outDir)
1288
+ JointCompilationSource (testGroup.name, Array (f), flags.withClasspath(classpath.getPath), classpath, decompilation = true )
1289
+ }
1290
+
1234
1291
// Create a CompilationTest and let the user decide whether to execute a pos or a neg test
1235
1292
val generateClassFiles = compileFilesInDir(f, flags0, blacklist)
1236
- (generateClassFiles.keepOutput, new CompilationTest (targets).keepOutput)
1293
+
1294
+ (
1295
+ generateClassFiles.keepOutput,
1296
+ new CompilationTest (targets).keepOutput,
1297
+ new CompilationTest (targets2).keepOutput
1298
+ )
1237
1299
}
1238
1300
1239
1301
0 commit comments