@@ -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)
@@ -419,6 +421,32 @@ trait ParallelTesting extends RunnerOrchestration { self =>
419
421
reporter
420
422
}
421
423
424
+ protected def decompile (flags0 : TestFlags , suppressErrors : Boolean , targetDir : JFile ): TestReporter = {
425
+ val decompilationOutput = new JFile (targetDir.getPath)
426
+ decompilationOutput.mkdir()
427
+ val flags = flags0 and (" -d" , decompilationOutput.getAbsolutePath) and " -decompile"
428
+
429
+ def hasTastyFileToClassName (f : JFile ): String =
430
+ targetDir.toPath.relativize(f.toPath).toString.dropRight(" .hasTasty" .length).replace('/' , '.' )
431
+ val classes = flattenFiles(targetDir).filter(isHasTastyFile).map(hasTastyFileToClassName)
432
+
433
+ val reporter =
434
+ TestReporter .reporter(realStdout, logLevel =
435
+ if (suppressErrors || suppressAllOutput) ERROR + 1 else ERROR )
436
+
437
+ val driver = decompiler.Main
438
+
439
+ // Compile with a try to catch any StackTrace generated by the compiler:
440
+ try {
441
+ driver.process(flags.all ++ classes, reporter = reporter)
442
+ }
443
+ catch {
444
+ case NonFatal (ex) => reporter.logStackTrace(ex)
445
+ }
446
+
447
+ reporter
448
+ }
449
+
422
450
private [ParallelTesting ] def executeTestSuite (): this .type = {
423
451
assert(_testSourcesCompleted == 0 , " not allowed to re-use a `CompileRun`" )
424
452
@@ -471,9 +499,46 @@ trait ParallelTesting extends RunnerOrchestration { self =>
471
499
protected def encapsulatedCompilation (testSource : TestSource ) = new LoggedRunnable {
472
500
def checkTestSource (): Unit = tryCompile(testSource) {
473
501
testSource match {
474
- case testSource @ JointCompilationSource (_ , files, flags, outDir, fromTasty) =>
502
+ case testSource @ JointCompilationSource (name , files, flags, outDir, fromTasty, decompilation ) =>
475
503
val reporter =
476
- if (fromTasty) compileFromTasty(flags, false , outDir)
504
+ if (decompilation) {
505
+ val rep = decompile(flags, false , outDir)
506
+
507
+ val checkFileOpt = files.flatMap { file =>
508
+ if (file.isDirectory) Nil
509
+ else {
510
+ val fname = file.getAbsolutePath.reverse.dropWhile(_ != '.' ).reverse + " decompiled"
511
+ val checkFile = new JFile (fname)
512
+ if (checkFile.exists) List (checkFile)
513
+ else Nil
514
+ }
515
+ }.headOption
516
+ checkFileOpt match {
517
+ case Some (checkFile) =>
518
+ val stripTrailingWhitespaces = " (.*\\ S|)\\ s+" .r
519
+ val output = Source .fromFile(outDir + " .decompiled" ).getLines().map {line =>
520
+ stripTrailingWhitespaces.unapplySeq(line).map(_.head).getOrElse(line)
521
+ }.mkString(" \n " )
522
+
523
+ checkDiff(output, checkFile, testSource, 0 ) match {
524
+ case Some (diff) =>
525
+ echo(diff)
526
+ addFailureInstruction(diff)
527
+
528
+ // Print build instructions to file and summary:
529
+ val buildInstr = testSource.buildInstructions(0 , rep.warningCount)
530
+ addFailureInstruction(buildInstr)
531
+
532
+ // Fail target:
533
+ failTestSource(testSource)
534
+ case None =>
535
+ }
536
+ case _ =>
537
+ }
538
+
539
+ rep
540
+ }
541
+ else if (fromTasty) compileFromTasty(flags, false , outDir)
477
542
else compile(testSource.sourceFiles, flags, false , outDir)
478
543
registerCompletion(reporter.errorCount)
479
544
@@ -523,39 +588,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
523
588
if (Properties .testsNoRun) addNoRunWarning()
524
589
else runMain(testSource.runClassPath) match {
525
590
case Success (_) if ! checkFile.isDefined || ! checkFile.get.exists => // success!
526
- case Success (output) => {
527
- val outputLines = output.lines.toArray :+ DiffUtil .EOF
528
- val checkLines : Array [String ] = Source .fromFile(checkFile.get).getLines().toArray :+ DiffUtil .EOF
529
- val sourceTitle = testSource.title
530
-
531
- def linesMatch =
532
- outputLines
533
- .zip(checkLines)
534
- .forall { case (x, y) => x == y }
535
-
536
- if (outputLines.length != checkLines.length || ! linesMatch) {
537
- // Print diff to files and summary:
538
- val expectedSize = DiffUtil .EOF .length max checkLines.map(_.length).max
539
- val diff = outputLines.padTo(checkLines.length, " " ).zip(checkLines.padTo(outputLines.length, " " )).map { case (act, exp) =>
540
- DiffUtil .mkColoredLineDiff(exp, act, expectedSize)
541
- }.mkString(" \n " )
542
-
543
- val msg =
544
- s """ |Output from ' $sourceTitle' did not match check file.
545
- |Diff (expected on the left, actual right):
546
- | """ .stripMargin + diff + " \n "
547
- echo(msg)
548
- addFailureInstruction(msg)
549
-
550
- // Print build instructions to file and summary:
551
- val buildInstr = testSource.buildInstructions(0 , warnings)
552
- addFailureInstruction(buildInstr)
553
-
554
- // Fail target:
555
- failTestSource(testSource)
591
+ case Success (output) =>
592
+ checkDiff(output, checkFile.get, testSource, warnings) match {
593
+ case Some (msg) =>
594
+ echo(msg)
595
+ addFailureInstruction(msg)
596
+
597
+ // Print build instructions to file and summary:
598
+ val buildInstr = testSource.buildInstructions(0 , warnings)
599
+ addFailureInstruction(buildInstr)
600
+
601
+ // Fail target:
602
+ failTestSource(testSource)
603
+ case None =>
556
604
}
557
- }
558
-
559
605
case Failure (output) =>
560
606
echo(s " Test ' ${testSource.title}' failed with output: " )
561
607
echo(output)
@@ -570,7 +616,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
570
616
protected def encapsulatedCompilation (testSource : TestSource ) = new LoggedRunnable {
571
617
def checkTestSource (): Unit = tryCompile(testSource) {
572
618
val (compilerCrashed, errorCount, warningCount, verifier : Function0 [Unit ]) = testSource match {
573
- case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty) =>
619
+ case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty, decompilation ) =>
574
620
val checkFile = files.flatMap { file =>
575
621
if (file.isDirectory) Nil
576
622
else {
@@ -679,7 +725,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
679
725
}
680
726
681
727
val (compilerCrashed, expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match {
682
- case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty) =>
728
+ case testSource @ JointCompilationSource (_, files, flags, outDir, fromTasty, decompilation ) =>
683
729
val sourceFiles = testSource.sourceFiles
684
730
val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles)
685
731
val reporter = compile(sourceFiles, flags, true , outDir)
@@ -733,6 +779,31 @@ trait ParallelTesting extends RunnerOrchestration { self =>
733
779
}
734
780
}
735
781
782
+ private def checkDiff (output : String , checkFile : JFile , testSource : TestSource , warnings : Int ): Option [String ] = {
783
+ val outputLines = output.lines.toArray :+ DiffUtil .EOF
784
+ val checkLines : Array [String ] = Source .fromFile(checkFile).getLines().toArray :+ DiffUtil .EOF
785
+ val sourceTitle = testSource.title
786
+
787
+ def linesMatch =
788
+ outputLines
789
+ .zip(checkLines)
790
+ .forall { case (x, y) => x == y }
791
+
792
+ if (outputLines.length != checkLines.length || ! linesMatch) {
793
+ // Print diff to files and summary:
794
+ val expectedSize = DiffUtil .EOF .length max checkLines.map(_.length).max
795
+ val diff = outputLines.padTo(checkLines.length, " " ).zip(checkLines.padTo(outputLines.length, " " )).map { case (act, exp) =>
796
+ DiffUtil .mkColoredLineDiff(exp, act, expectedSize)
797
+ }.mkString(" \n " )
798
+
799
+ val msg =
800
+ s """ |Output from ' $sourceTitle' did not match check file.
801
+ |Diff (expected on the left, actual right):
802
+ | """ .stripMargin + diff + " \n "
803
+ Some (msg)
804
+ } else None
805
+ }
806
+
736
807
/** The `CompilationTest` is the main interface to `ParallelTesting`, it
737
808
* can be instantiated via one of the following methods:
738
809
*
@@ -964,7 +1035,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
964
1035
*/
965
1036
def copyToTarget (): CompilationTest = new CompilationTest (
966
1037
targets.map {
967
- case target @ JointCompilationSource (_, files, _, outDir, _) =>
1038
+ case target @ JointCompilationSource (_, files, _, outDir, _, _ ) =>
968
1039
target.copy(files = files.map(copyToDir(outDir,_)))
969
1040
case target @ SeparateCompilationSource (_, dir, _, outDir) =>
970
1041
target.copy(dir = copyToDir(outDir, dir))
@@ -1080,34 +1151,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1080
1151
new CompilationTest (target)
1081
1152
}
1082
1153
1083
- /** Compiles a single file from the string path `f` using the supplied flags
1084
- *
1085
- * Tests in the first part of the tuple must be executed before the second.
1086
- * Both testsRequires explicit delete().
1087
- */
1088
- def compileTasty (f : String , flags : TestFlags )(implicit testGroup : TestGroup ): (CompilationTest , CompilationTest ) = {
1089
- val sourceFile = new JFile (f)
1090
- val parent = sourceFile.getParentFile
1091
- val outDir =
1092
- defaultOutputDir + testGroup + " /" +
1093
- sourceFile.getName.substring(0 , sourceFile.getName.lastIndexOf('.' )) + " /"
1094
-
1095
- require(
1096
- sourceFile.exists && ! sourceFile.isDirectory &&
1097
- (parent ne null ) && parent.exists && parent.isDirectory,
1098
- s " Source file: $f, didn't exist "
1099
- )
1100
- val tastySource = createOutputDirsForFile(sourceFile, parent, outDir)
1101
- val target = JointCompilationSource (
1102
- testGroup.name,
1103
- Array (sourceFile),
1104
- flags.withClasspath(tastySource.getPath) and " -from-tasty" ,
1105
- tastySource,
1106
- fromTasty = true
1107
- )
1108
- (compileFile(f, flags).keepOutput, new CompilationTest (target).keepOutput)
1109
- }
1110
-
1111
1154
/** Compiles a directory `f` using the supplied `flags`. This method does
1112
1155
* deep compilation, that is - it compiles all files and subdirectories
1113
1156
* contained within the directory `f`.
@@ -1213,7 +1256,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1213
1256
* Both testsRequires explicit delete().
1214
1257
*/
1215
1258
def compileTastyInDir (f : String , flags0 : TestFlags , blacklist : Set [String ] = Set .empty)(
1216
- implicit testGroup : TestGroup ): (CompilationTest , CompilationTest ) = {
1259
+ implicit testGroup : TestGroup ): (CompilationTest , CompilationTest , CompilationTest ) = {
1217
1260
val outDir = defaultOutputDir + testGroup + " /"
1218
1261
val flags = flags0 and " -Yretain-trees"
1219
1262
val sourceDir = new JFile (f)
@@ -1228,9 +1271,22 @@ trait ParallelTesting extends RunnerOrchestration { self =>
1228
1271
}
1229
1272
// TODO add SeparateCompilationSource from tasty?
1230
1273
1274
+ val targets2 =
1275
+ files
1276
+ .filter(f => dotty.tools.io.File (f.toPath).changeExtension(" decompiled" ).exists)
1277
+ .map { f =>
1278
+ val classpath = createOutputDirsForFile(f, sourceDir, outDir)
1279
+ JointCompilationSource (testGroup.name, Array (f), flags.withClasspath(classpath.getPath), classpath, decompilation = true )
1280
+ }
1281
+
1231
1282
// Create a CompilationTest and let the user decide whether to execute a pos or a neg test
1232
1283
val generateClassFiles = compileFilesInDir(f, flags0, blacklist)
1233
- (generateClassFiles.keepOutput, new CompilationTest (targets).keepOutput)
1284
+
1285
+ (
1286
+ generateClassFiles.keepOutput,
1287
+ new CompilationTest (targets).keepOutput,
1288
+ new CompilationTest (targets2).keepOutput
1289
+ )
1234
1290
}
1235
1291
1236
1292
0 commit comments