|
| 1 | +import scala.sys.process._ |
| 2 | +import System.{ getenv, getProperty } |
| 3 | +import java.io.{ File, PrintWriter } |
| 4 | + |
| 5 | +object Bisect extends App { |
| 6 | + val minRev = 1183 |
| 7 | + val maxRev = 1241 |
| 8 | + |
| 9 | + def file(path: String) = new File(path) |
| 10 | + implicit def fileImplicits(f: File) = new { |
| 11 | + def /(subPath: String) = new File(f, subPath) |
| 12 | + } |
| 13 | + val homeDir = file(getProperty("user.home")) |
| 14 | + val dyncallDir = homeDir / "src/dyncall" |
| 15 | + val bridjDir = homeDir / "nativelibs4java/Runtime/BridJ" |
| 16 | + |
| 17 | + def svnUpdate(dir: File, rev: Int): Unit = { |
| 18 | + Process(Seq("svn", "update", "-r" + rev), Some(dir)) !! |
| 19 | + |
| 20 | + //println("Svn tree '" + dir + "' updated to revision " + rev); |
| 21 | + //if (ret != 0) |
| 22 | + // throw new RuntimeException("svn update returned " + ret) |
| 23 | + } |
| 24 | + def svnLog(dir: File, rev: Int): String = { |
| 25 | + Process(Seq("svn", "log", "-r" + rev), Some(dir)) !! |
| 26 | + } |
| 27 | + def svnDiff(dir: File, from: Int, to: Int): String = { |
| 28 | + Process(Seq("svn", "diff", "-r" + from + ":" + to), Some(dir)) !! |
| 29 | + } |
| 30 | + |
| 31 | + def concatLogger = { |
| 32 | + val builder = new StringBuilder |
| 33 | + (ProcessLogger(s => builder.append(s).append('\n')), () => builder.toString) |
| 34 | + } |
| 35 | + |
| 36 | + sealed trait TestResult { def describe: String } |
| 37 | + case class BuildFailure(out: String) extends TestResult { override def describe = "Broken" } |
| 38 | + case class TestFailure(out: String) extends TestResult { override def describe = "Failure" } |
| 39 | + object TestSuccess extends TestResult { override def describe = "Success" } |
| 40 | + |
| 41 | + def test(rev: Int): TestResult = { |
| 42 | + print("Updating to rev " + rev + "... ") |
| 43 | + svnUpdate(dyncallDir, rev) |
| 44 | + print("Building... ") |
| 45 | + |
| 46 | + val ret = { |
| 47 | + val (buildLog, buildOut) = concatLogger |
| 48 | + val buildRet: Int = |
| 49 | + ( |
| 50 | + Process(Seq("sh", "CleanNative"), Some(bridjDir)) #&& |
| 51 | + Process(Seq("sh", "BuildNative"), Some(bridjDir)) |
| 52 | + ) ! (buildLog) |
| 53 | + |
| 54 | + if (buildRet != 0) { |
| 55 | + BuildFailure(buildOut()) |
| 56 | + } else { |
| 57 | + print("Testing... ") |
| 58 | + val (testLog, testOut) = concatLogger |
| 59 | + val testRet: Int = |
| 60 | + Process(Seq("mvn", "test", "-o", "-Dtest=CallTest"), Some(bridjDir)) ! (testLog) |
| 61 | + |
| 62 | + if (testRet == 0) |
| 63 | + TestSuccess |
| 64 | + else |
| 65 | + TestFailure(testOut()) |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + println(ret.describe + ".") |
| 70 | + |
| 71 | + ret |
| 72 | + //println("Res = " + res) |
| 73 | + } |
| 74 | + |
| 75 | + import scala.annotation.tailrec |
| 76 | + |
| 77 | + @tailrec |
| 78 | + case class BisectRes(firstFailedTest: Int, firstBrokenBuild: Option[Int] = None) |
| 79 | + |
| 80 | + def bisect(fromOkRev: Int, toBadRev: Int): BisectRes = { |
| 81 | + if (toBadRev <= fromOkRev + 1) |
| 82 | + BisectRes(toBadRev) |
| 83 | + else { |
| 84 | + var pick = (fromOkRev + toBadRev) / 2 |
| 85 | + if (pick == fromOkRev) |
| 86 | + pick += 1 |
| 87 | + |
| 88 | + test(pick) match { |
| 89 | + case TestSuccess => |
| 90 | + bisect(pick, toBadRev) |
| 91 | + case _: TestFailure => |
| 92 | + bisect(fromOkRev, pick) |
| 93 | + case _: BuildFailure => |
| 94 | + val BisectRes(failed, broken) = |
| 95 | + bisect(pick, toBadRev) |
| 96 | + |
| 97 | + BisectRes(failed, Some(pick)) |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + |
| 103 | + // sbt "run 1183 1241" |
| 104 | + object SomeInt { |
| 105 | + def unapply(s: String) = |
| 106 | + try Some(s.toInt) catch { case _ => None } |
| 107 | + } |
| 108 | + args.toList match { |
| 109 | + case SomeInt(fromRev) :: SomeInt(toRev) :: xargs => |
| 110 | + val (logFile, diffFile) = xargs match { |
| 111 | + case l :: d :: Nil => (l, d) |
| 112 | + case Nil => ("bisect.log", "bisect.diff") |
| 113 | + } |
| 114 | + println("Bisect from rev " + fromRev + " to rev " + toRev) |
| 115 | + assert(test(fromRev) == TestSuccess, "Does not build at rev " + fromRev) |
| 116 | + assert(test(toRev) != TestSuccess, "Already builds at rev " + toRev) |
| 117 | + val bisection = bisect(fromRev, toRev) |
| 118 | + //val bisection = BisectRes(1236,Some(1226)) |
| 119 | + val (revisions, (diffFrom, diffTo)) = bisection match { |
| 120 | + case BisectRes(failed, Some(broken)) => |
| 121 | + println("Tests fail at revision " + failed + " but build broken since revision " + broken) |
| 122 | + (broken to failed, (broken - 1, failed)) |
| 123 | + case BisectRes(failed, None) => |
| 124 | + println("Faulty revision is " + failed) |
| 125 | + (Seq(failed), (failed - 1, failed)) |
| 126 | + } |
| 127 | + |
| 128 | + val logOut = new PrintWriter(file(logFile)) |
| 129 | + for (rev <- revisions) { |
| 130 | + val log = svnLog(dyncallDir, rev) |
| 131 | + println(log) |
| 132 | + logOut.println(log) |
| 133 | + } |
| 134 | + logOut.close |
| 135 | + |
| 136 | + val diffOut = new PrintWriter(file(diffFile)) |
| 137 | + val diff = svnDiff(dyncallDir, diffFrom, diffTo) |
| 138 | + println(diff) |
| 139 | + diffOut.println(diff) |
| 140 | + diffOut.close |
| 141 | + } |
| 142 | +} |
0 commit comments