public class SampleSmallClassTest {
@Test
public void testStuff() {
// The boss says we should write tests.
// Doesn't seem that hard...
assertTrue(true);
}
}
$ gradle test --tests *testStuff jacocoTestReport
| Element | Missed Instructions | Coverage |
| doSomeMath(int, int) |
|
0% |
@Test
public void testStuffWithCoverage() {
// Need to get that coverage thing high...
for(int a = -5; a <= 5; a ++) {
for (int b = -5; b <= 5; b++) {
SampleSmallClass.doSomeMath(a, b);
}
}
}
$ gradle test --tests *testStuffWithCoverage jacocoTestReport
| Element | Missed Instructions | Coverage |
| doSomeMath(int, int) |
|
100% |
| Tool | Java | Ant | Maven | Gradle | CI | TestNG | Mocks* | Last Update |
|---|---|---|---|---|---|---|---|---|
| Simple Jester | 5-6 | N | N | N | N | N | ? | 2009 |
| javaLanche | 5-6 | N | N | N | N | N | ? | 2011 |
| Jumble | 4-6 | Y | N | N | N | Y | 3/6 | 2013 |
| Pitest | 5-8 | Y | Y | Y | Y | Y | 6/6 | Yesterday |
$ gradle test --tests *testStuffWithCoverage pitest
| Name | Line Coverage | Mutation Coverage |
| Total | 100% | 0% |
| SampleSmallClass.java |
|
|
public static int doSomeMath(int a, int b) {
long result = b;
result += a;
result -= b;
result -= a;
return (int) result;
}
public static int doSomeMath(int a, int b) {
long result = b;
result -= a;
result -= b;
result -= a;
return (int) result;
}
Replaced long addition with subtraction → SURVIVED
public static int doSomeMath(int a, int b) {
long result = b;
result += a;
result -= b;
result += a;
return (int) result;
}
Replaced long subtraction with addition → SURVIVED
public static int doSomeMath(int a, int b) {
long result = b;
result += a;
result -= b;
result -= a;
return ((int) result == 0 ? 1 : 0);
}
Replaced return value with (x == 0 ? 1 : 0) → SURVIVED
@Test
public void testProperly() {
final int expected = 0;
for(int a = -5; a <= 5; a ++) {
for(int b = -5; b <= 5; b ++) {
assertEquals(expected, doSomeMath(a, b));
}
}
}
$ gradle test --tests *testProperly pitest
| Name | Line Coverage | Mutation Coverage |
| Total | 100% | 100% |
| SampleSmallClass.java |
|
|
What we want to have:
if(isAllowed(action, user)) {
action.execute();
}
if(!isAllowed(action, user)) {
action.execute();
}
if(remainingHp <= 0) {
kill(player);
}
if(remainingHp < 0) {
kill(player);
}
if(cacheEnabled()) {
cache.store(result);
}
cache.store(result);
if(isValid(authData)) {
login(authData);
} else {
throw new GtfoException();
}
throw new GtfoException();
int sum = a + b;
int sum = a - b;
NB: Also affects field increments and decrements
private int total;
void add(double sample) {
total ++;
}
→
0: aload_0
1: dup
2: getfield
5: iconst_1
6: iadd # ← addition
7: putfield
int total = 0;
for(...) {
total ++;
}
int total = 0;
for(...) {
total --;
}
if(profit < 0) {
loss = -profit;
}
if(profit < 0) {
loss = profit;
}
int total = 0;
for(...) {
total ++;
}
int total = 0;
for(...) {
}
for(Listener l : listeners) {
l.onStuffHappened(stuff);
}
for(Listener l : listeners) {
}
double len = hypot(a, b);
double len = 0.0;
Product p = new Product(...); //... double result = calculate(p);
Product p = null;
//...
double result = calculate(p);
User findOwner(...) {
//...
return possibleOwner;
}
//...if(possibleOwner == null) throw new RuntimeException(); else return null;
void consume(Object obj) {
int tlr = (this.tlr =
(this.tlr * 1664525 +
1013904223));
//...
}
void consume(Object obj) {
int tlr = (this.tlr =
(this.tlr * 1664525 +
1013904224));
//...
}
void stop() {
this.shouldStop = true;
}
void stop() {
}
switch(condition) {
case 1: doFirst(); break;
case 2: doSecond(); break;
default: doDefault(); break;
}
switch(condition) {
case 1: doDefault(); break;
case 2: doDefault(); break;
default: doFirst(); break;
}
switch(condition) {
case 1: doFirst(); break;
case 2: doSecond(); break;
default: doDefault(); break;
}
doDefault();
A success story from TheLadders.com
$ svn checkout http://svn.apache.org/repos/asf/comm [...] $ cd commons-math3 $ patch commons-math3/pom.xml commons-math3-pom-patch.diff
$ mvn clean test [...] Tests run: 4901, Failures: 0, Errors: 0, Skipped: 43 [INFO] --------------------------------------------- [INFO] BUILD SUCCESS [INFO] --------------------------------------------- [INFO] Total time: 05:17 min
$ mvn clean test pitest:mutationCoverage [...] ====================================================== - Timings ====================================================== > scan classpath : < 1 second > coverage and dependency analysis : 27 minutes and 31 seconds > build mutation tests : 7 seconds > run mutation analysis : 3 hours, 47 minutes and 7 seconds ====================================================== > Total : 4 hours, 14 minutes and 46 seconds
$ mvn clean test pitest:mutationCoverage -Dpit.threads=2 [...] ====================================================== - Timings ====================================================== > scan classpath : < 1 second > coverage and dependency analysis : 27 minutes and 28 seconds > build mutation tests : 3 seconds > run mutation analysis : 2 hours, 2 minutes and 1 seconds ====================================================== > Total : 2 hours, 29 minutes and 33 seconds
Store the following info between runs:
Do not check a mutant, if in the previous run:
$ mvn ... pitest:mutationCoverage -P pit-history -Dpit.threads=4 [...] ====================================================== - Timings ====================================================== > scan classpath : < 1 second > coverage and dependency analysis : 7 minutes and 15 seconds > build mutation tests : 2 seconds > run mutation analysis : 55 minutes and 41 seconds ====================================================== > Total : 1 hours, 2 minutes and 59 seconds
o.a.c.math3.linear.MatrixUtils
public static <T extends FieldElement<T>> FieldMatrix<T>
createFieldIdentityMatrix(final Field<T> field, final int dimension) {
final T zero = field.getZero();
final T one = field.getOne();
final T[][] d = MathArrays.buildArray(field, dimension, dimension);
for (int row = 0; row < dimension; row++) {
final T[] dRow = d[row];
Arrays.fill(dRow, zero);
dRow[row] = one;
}
return new Array2DRowFieldMatrix<T>(field, d, false);
}
public static <T extends FieldElement<T>> FieldMatrix<T>
createFieldIdentityMatrix(final Field<T> field, final int dimension) {
final T zero = field.getZero();
final T one = field.getOne();
final T[][] d = MathArrays.buildArray(field, dimension, dimension);
for (int row = 0; row < dimension; row++) {
final T[] dRow = d[row];
dRow[row] = one;
}
return new Array2DRowFieldMatrix<T>(field, d, false);
}
200: removed call to java/util/Arrays::fill : SURVIVED
$ mvn ... pitest:mutationCoverage -P pit-history -Dpit.threads=4 [...] ====================================================== - Timings ====================================================== > scan classpath : < 1 second > coverage and dependency analysis : 7 minutes and 3 seconds > build mutation tests : 1 minutes and 1 seconds > run mutation analysis : 56 seconds ====================================================== > Total : 9 minutes and 0 seconds
public int evaluate(final int distance) {
if(distance == 0) {
throw new IllegalArgumentException("Distance should be non-zero");
}
int result = 0;
if(distance > 0) {
result += calculateDistanceAdjustment(distance);
}
return result;
}
public int evaluate(final int distance) {
if(distance == 0) {
throw new IllegalArgumentException("Distance should be non-zero");
}
int result = 0;
if(distance > 0) {
result += calculateDistanceAdjustment(distance);
}
return result;
}
11: changed conditional boundary → SURVIVED
public class Minitrue {
public static void rectify(String path) {
ensurePathIsSecure(path);
rmRf(path);
}
private static final void rmRf(String path) {
String command = "$ rm -rf " + path;
System.out.println("Executing: " + command);
exec(command);
}
// ...
}
@Test
public void testRectifySecurity() {
boolean rejected = false;
try {
Minitrue.rectify("/");
} catch(SecurityException e) {
rejected = true;
}
Assert.assertTrue(rejected);
Assert.assertFalse(Minitrue.isUnpath("/"));
}
stderr : PIT >> Running mutation [...]
mutator=VoidMethodCallMutator,
description=removed call to Minitrue::ensurePathIsSecure
stderr : PIT >> mutating method rectify
stderr : PIT >> 2 relevant test for rectify
stderr : PIT >> Running 1 units
stdout : Executing: rm -rf /