Skip to content

Instantly share code, notes, and snippets.

@stillalex
Last active December 13, 2015 18:59
Show Gist options
  • Save stillalex/4959583 to your computer and use it in GitHub Desktop.
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
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