| 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(); |
| } |
| } |