blob: ab7443f38535d0acd8c5f4c6eb8cd41dabd4263d [file] [log] [blame]
package org.junit.experimental.max;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
/**
* Stores a subset of the history of each test:
* <ul>
* <li>Last failure timestamp
* <li>Duration of last execution
* </ul>
*/
public class MaxHistory implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Loads a {@link MaxHistory} from {@code file}, or generates a new one that
* will be saved to {@code file}.
*/
public static MaxHistory forFolder(File file) {
if (file.exists()) {
try {
return readHistory(file);
} catch (CouldNotReadCoreException e) {
e.printStackTrace();
file.delete();
}
}
return new MaxHistory(file);
}
private static MaxHistory readHistory(File storedResults)
throws CouldNotReadCoreException {
try {
FileInputStream file = new FileInputStream(storedResults);
try {
ObjectInputStream stream = new ObjectInputStream(file);
try {
return (MaxHistory) stream.readObject();
} finally {
stream.close();
}
} finally {
file.close();
}
} catch (Exception e) {
throw new CouldNotReadCoreException(e);
}
}
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
* See https://github.com/junit-team/junit4/issues/976
*/
private final Map<String, Long> fDurations = new HashMap<String, Long>();
private final Map<String, Long> fFailureTimestamps = new HashMap<String, Long>();
private final File fHistoryStore;
private MaxHistory(File storedResults) {
fHistoryStore = storedResults;
}
private void save() throws IOException {
ObjectOutputStream stream = null;
try {
stream = new ObjectOutputStream(new FileOutputStream(fHistoryStore));
stream.writeObject(this);
} finally {
if (stream != null) {
stream.close();
}
}
}
Long getFailureTimestamp(Description key) {
return fFailureTimestamps.get(key.toString());
}
void putTestFailureTimestamp(Description key, long end) {
fFailureTimestamps.put(key.toString(), end);
}
boolean isNewTest(Description key) {
return !fDurations.containsKey(key.toString());
}
Long getTestDuration(Description key) {
return fDurations.get(key.toString());
}
void putTestDuration(Description description, long duration) {
fDurations.put(description.toString(), duration);
}
private final class RememberingListener extends RunListener {
private long overallStart = System.currentTimeMillis();
private Map<Description, Long> starts = new HashMap<Description, Long>();
@Override
public void testStarted(Description description) throws Exception {
starts.put(description, System.nanoTime()); // Get most accurate
// possible time
}
@Override
public void testFinished(Description description) throws Exception {
long end = System.nanoTime();
long start = starts.get(description);
putTestDuration(description, end - start);
}
@Override
public void testFailure(Failure failure) throws Exception {
putTestFailureTimestamp(failure.getDescription(), overallStart);
}
@Override
public void testRunFinished(Result result) throws Exception {
save();
}
}
private class TestComparator implements Comparator<Description> {
public int compare(Description o1, Description o2) {
// Always prefer new tests
if (isNewTest(o1)) {
return -1;
}
if (isNewTest(o2)) {
return 1;
}
// Then most recently failed first
int result = getFailure(o2).compareTo(getFailure(o1));
return result != 0 ? result
// Then shorter tests first
: getTestDuration(o1).compareTo(getTestDuration(o2));
}
private Long getFailure(Description key) {
Long result = getFailureTimestamp(key);
if (result == null) {
return 0L; // 0 = "never failed (that I know about)"
}
return result;
}
}
/**
* @return a listener that will update this history based on the test
* results reported.
*/
public RunListener listener() {
return new RememberingListener();
}
/**
* @return a comparator that ranks tests based on the JUnit Max sorting
* rules, as described in the {@link MaxCore} class comment.
*/
public Comparator<Description> testComparator() {
return new TestComparator();
}
}