Skip to content

Add idempotency check on bootstrapped dotty #2756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class CompilationTests extends ParallelTesting {
// compile with bootstrapped library on cp:
defaultOutputDir + "lib/src/:" +
// as well as bootstrapped compiler:
defaultOutputDir + "dotty1/dotty1/:" +
defaultOutputDir + "dotty1/dotty/:" +
Jars.dottyInterfaces
)

Expand Down Expand Up @@ -241,15 +241,19 @@ class CompilationTests extends ParallelTesting {

def dotty1 = {
compileList(
"dotty1",
"dotty",
compilerSources ++ backendSources ++ backendJvmSources,
opt)
}

def dotty2 =
compileShallowFilesInDir("../compiler/src/dotty", opt)
def dotty2 = {
compileList(
"dotty",
compilerSources ++ backendSources ++ backendJvmSources,
opt)
}

{
val tests = {
lib.keepOutput :: dotty1.keepOutput :: {
dotty2 +
compileShallowFilesInDir("../compiler/src/dotty/tools", opt) +
Expand All @@ -266,21 +270,25 @@ class CompilationTests extends ParallelTesting {
compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/util", opt) +
compileList("shallow-backend", backendSources, opt) +
compileList("shallow-backend-jvm", backendJvmSources, opt)
} :: Nil
}.map(_.checkCompile()).foreach(_.delete())
}.keepOutput :: Nil
}.map(_.checkCompile())

assert(new java.io.File("../out/dotty1/dotty/").exists)
assert(new java.io.File("../out/dotty2/dotty/").exists)
compileList("idempotency", List("../tests/idempotency/BootstrapChecker.scala", "../tests/idempotency/IdempotencyCheck.scala"), defaultOptions).checkRuns()

tests.foreach(_.delete())
}

/** Add a `z` so that they run last. TODO: Only run them selectively? */
@Test def zBytecodeIdempotency: Unit = {
val opt = defaultOptions.and("-YemitTasty")

def idempotency1() = {
compileList("dotty1", compilerSources ++ backendSources ++ backendJvmSources, opt) +
compileDir("../collection-strawman/src/main", opt) +
compileFilesInDir("../tests/pos", opt)
}
def idempotency2() = {
compileList("dotty1", compilerSources ++ backendSources ++ backendJvmSources, opt) +
compileDir("../collection-strawman/src/main", opt) +
compileFilesInDir("../tests/pos", opt)
}
Expand All @@ -290,7 +298,7 @@ class CompilationTests extends ParallelTesting {
assert(new java.io.File("../out/idempotency1/").exists)
assert(new java.io.File("../out/idempotency2/").exists)

compileFile("../tests/idempotency/IdempotencyCheck.scala", defaultOptions).checkRuns()
compileList("idempotency", List("../tests/idempotency/Checker.scala", "../tests/idempotency/IdempotencyCheck.scala"), defaultOptions).checkRuns()

tests.delete()
}
Expand Down
5 changes: 5 additions & 0 deletions tests/idempotency/BootstrapChecker.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

object Test {
def main(args: Array[String]): Unit =
IdempotencyCheck.checkIdempotency("../out/dotty")
}
5 changes: 5 additions & 0 deletions tests/idempotency/Checker.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

object Test {
def main(args: Array[String]): Unit =
IdempotencyCheck.checkIdempotency("../out/idempotency")
}
42 changes: 20 additions & 22 deletions tests/idempotency/IdempotencyCheck.scala
Original file line number Diff line number Diff line change
@@ -1,64 +1,62 @@

import java.nio.file.{Files, Path, Paths}
import java.nio.file.{ Files => JFiles, Path => JPath, Paths => JPaths }
import java.util.stream.{ Stream => JStream }

import scala.collection.JavaConverters._

object Test {

def main(args: Array[String]): Unit = checkIdempotency()

object IdempotencyCheck {
val blacklisted = Set(
// Bridges on collections in different order. Second one in scala2 order.
// No fix needed. Bridges on collections in different order. Second one in scala2 order.
"pos/Map/scala/collection/immutable/Map",
"pos/Map/scala/collection/immutable/AbstractMap",
"pos/t1203a/NodeSeq",
"pos/i2345/Whatever"
)

def checkIdempotency(): Unit = {
def checkIdempotency(dirPrefix: String): Unit = {
var failed = 0
var total = 0

val groupedBytecodeFiles: List[(Path, Path, Path, Path)] = {
val groupedBytecodeFiles: List[(JPath, JPath, JPath, JPath)] = {
val bytecodeFiles = {
def bytecodeFiles(paths: JStream[Path]): List[Path] = {
def bytecodeFiles(paths: JStream[JPath]): List[JPath] = {
def isBytecode(file: String) = file.endsWith(".class") || file.endsWith(".tasty")
paths.iterator.asScala.filter(path => isBytecode(path.toString)).toList
}
val compilerDir1 = Paths.get("../out/idempotency1")
val compilerDir2 = Paths.get("../out/idempotency2")
bytecodeFiles(Files.walk(compilerDir1)) ++ bytecodeFiles(Files.walk(compilerDir2))
val compilerDir1 = JPaths.get(dirPrefix + 1)
val compilerDir2 = JPaths.get(dirPrefix + 2)
bytecodeFiles(JFiles.walk(compilerDir1)) ++ bytecodeFiles(JFiles.walk(compilerDir2))
}
val groups = bytecodeFiles.groupBy(f => f.toString.substring("../out/idempotencyN/".length, f.toString.length - 6))
val groups = bytecodeFiles.groupBy(f => f.toString.substring(dirPrefix.length + 1, f.toString.length - 6))

groups.filterNot(x => blacklisted(x._1)).valuesIterator.flatMap { g =>
def pred(f: Path, i: Int, isTasty: Boolean) =
f.toString.contains("idempotency" + i) && f.toString.endsWith(if (isTasty) ".tasty" else ".class")
def pred(f: JPath, i: Int, isTasty: Boolean) =
f.toString.contains(dirPrefix + i) && f.toString.endsWith(if (isTasty) ".tasty" else ".class")
val class1 = g.find(f => pred(f, 1, isTasty = false))
val class2 = g.find(f => pred(f, 2, isTasty = false))
val tasty1 = g.find(f => pred(f, 1, isTasty = true))
val tasty2 = g.find(f => pred(f, 2, isTasty = true))
assert(class1.isDefined, "Could not find class in idempotency1 for " + class2)
assert(class2.isDefined, "Could not find class in idempotency2 for " + class1)
assert(class1.isDefined, s"Could not find class in ${dirPrefix + 1} for $class2")
assert(class2.isDefined, s"Could not find class in ${dirPrefix + 2} for $class1")
if (tasty1.isEmpty || tasty2.isEmpty) Nil
else List(Tuple4(class1.get, tasty1.get, class2.get, tasty2.get))
}.toList
}

for ((class1, tasty1, class2, tasty2) <- groupedBytecodeFiles) {
total += 1
val bytes1 = Files.readAllBytes(class1)
val bytes2 = Files.readAllBytes(class2)
val bytes1 = JFiles.readAllBytes(class1)
val bytes2 = JFiles.readAllBytes(class2)
if (!java.util.Arrays.equals(bytes1, bytes2)) {
failed += 1
val tastyBytes1 = Files.readAllBytes(tasty1)
val tastyBytes2 = Files.readAllBytes(tasty2)
val tastyBytes1 = JFiles.readAllBytes(tasty1)
val tastyBytes2 = JFiles.readAllBytes(tasty2)
if (java.util.Arrays.equals(tastyBytes1, tastyBytes2))
println(s"Idempotency test failed between $class1 and $class1 (same tasty)")
else
println(s"Idempotency test failed between $tasty1 and $tasty2")
/* Dump bytes to console, could be useful if issue only appears in CI.
* Create the .class locally with Files.write(path, Array[Byte](...)) with the printed array
* Create the .class locally with JFiles.write(path, Array[Byte](...)) with the printed array
*/
// println(bytes1.mkString("Array[Byte](", ",", ")"))
// println(bytes2.mkString("Array[Byte](", ",", ")"))
Expand Down