Last active
December 13, 2015 18:59
-
-
Save stillalex/4959583 to your computer and use it in GitHub Desktop.
Utility class that diffs 2 maven test run logs and outputs the percentage difference in the run times on test classes that are found in both files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.Option.option2Iterable | |
import scala.io.Source | |
/** | |
* Utility class that diffs 2 maven test run logs and outputs the percentage difference in the run times on test classes that are found in both files | |
* | |
* How to run | |
* | |
* - run first test on the reference | |
* mvn clean install > before.log | |
* | |
* - change the setup and run again the maven test suite | |
* mvn clean install > after.log | |
* | |
* - Use the 2 produced logs as an input for the DiffMvnTest | |
* scala DiffMvnTest.scala before.log after.log | |
* | |
* and you'll get a report that shows you the percentage difference between tests that are in both log files | |
* org.apache.jackrabbit.oak.api.QueryTest,1(0.06),1(0.03),-50% | |
* | |
* This means | |
* - the name of the test is 'org.apache.jackrabbit.oak.api.QueryTest' | |
* - the first run had 1 test that executed in 0.06 sec | |
* - the second run had 1 test that executed in 0.03 sec | |
* - the percentage difference is -50% | |
* | |
* Under the hood the program greps the input file for output like the following: | |
* | |
* .... | |
* Running org.apache.jackrabbit.mk.tests.MkConcurrentAddNodes1CommitTest | |
* [possible sysout garbage in between] | |
* Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.562 sec | |
* .... | |
* | |
* Next it will build a map of known test cases which it then will try to merge with the map build using the second input file. | |
* | |
* | |
* Tested with Scala 2.10 | |
* | |
*/ | |
object DiffMvnTest { | |
def diff(file1: String, file2: String) = { | |
val m1 = aggregate(file1).toMap; | |
val m2 = aggregate(file2).toMap; | |
val diffs = for (i1 ← m1; i2 ← m2.get(i1._1)) yield { | |
val delta = if (i2._5 != 0 && i1._2._5 != 0) { | |
(i2._5 * 100 / i1._2._5).toInt - 100; | |
} else { | |
0 | |
} | |
new Tuple4(i1._1, i1._2, i2, delta); | |
} | |
diffs | |
} | |
def aggregate(file: String) = { | |
val firstToken = "Running "; | |
val secondToken = "Tests run: "; | |
grep(Source.fromFile(file).getLines(), firstToken, secondToken).map(p ⇒ { | |
val test = p._1.substring(firstToken.length()); | |
// Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.562 sec | |
val fragments = p._2.split(", "); | |
val tests = fragments(0).substring("Tests run: ".length()).toInt; | |
val failures = fragments(1).substring("Failures: ".length()).toInt; | |
val errors = fragments(2).substring("Errors: ".length()).toInt; | |
val skipped = fragments(3).substring("Skipped: ".length()).toInt; | |
val timeRaw = fragments(4).substring("Time elapsed: ".length(), fragments(4).indexOf(" sec")).toDouble; | |
val time = (math floor timeRaw * 100) / 100; | |
val results = (tests, failures, errors, skipped, time); | |
new Pair(test, results); | |
}); | |
} | |
def grep(lines: Iterator[String], firstToken: String, secondToken: String): Iterator[Pair[String, String]] = { | |
for (x ← lines; l ← findFirst(lines, firstToken); p ← findFirst(lines, secondToken)) | |
yield new Pair(l, p); | |
} | |
def findFirst(lines: Iterator[String], starts: String): Option[String] = { | |
for (l ← lines if l.startsWith(starts)) { | |
return Some(l); | |
} | |
return None; | |
} | |
// -------------- print methods ----------------------- | |
def asPlainText(file1: String, file2: String) = { | |
println("Test Name, First Run, Second Run, %Diff"); | |
diff(file1, file2).toSeq.sortBy(e ⇒ (-e._4, e._1)).foreach(i ⇒ { | |
println(i._1 + "," + i._2._1 + "(" + i._2._5 + ")," + i._3._1 + "(" + i._3._5 + ")," + i._4 + "%"); | |
}); | |
} | |
def asJiraTable(file1: String, file2: String) = { | |
println("||Test Name||First Run||Second Run,||%Diff||"); | |
diff(file1, file2).toSeq.sortBy(e ⇒ (-e._4, e._1)).foreach(i ⇒ { | |
println("|" + i._1 + "|" + i._2._1 + "(" + i._2._5 + ")|" + i._3._1 + "(" + i._3._5 + ")|" + i._4 + "%|"); | |
}); | |
} | |
def main(args: Array[String]) { | |
if (args.length != 2 && args.length != 3) { | |
println("To run"); | |
println(" scala DiffMvnTest.scala filename1 filename2 [1]"); | |
println(""); | |
println("DiffMvnTest args:"); | |
println(" filename1, filename2 are paths to the input files (logs generated by running 'mvn test > l.log'"); | |
println(" [1] is an optional flag controlling the output formatting - use '1' for "); | |
println(" - use '1' for a JIRA style table"); | |
println(" - use '0' or nothing for plain text output"); | |
return; | |
} | |
if (args.length == 3 && "1".equals(args(2))) { | |
asJiraTable(args(0), args(1)); | |
} else { | |
asPlainText(args(0), args(1)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment