1package org.junit.experimental.max; 2 3import java.io.File; 4import java.io.FileInputStream; 5import java.io.FileOutputStream; 6import java.io.IOException; 7import java.io.ObjectInputStream; 8import java.io.ObjectOutputStream; 9import java.io.Serializable; 10import java.util.Comparator; 11import java.util.HashMap; 12import java.util.Map; 13 14import org.junit.runner.Description; 15import org.junit.runner.Result; 16import org.junit.runner.notification.Failure; 17import org.junit.runner.notification.RunListener; 18 19/** 20 * Stores a subset of the history of each test: 21 * <ul> 22 * <li>Last failure timestamp 23 * <li>Duration of last execution 24 * </ul> 25 */ 26public class MaxHistory implements Serializable { 27 private static final long serialVersionUID = 1L; 28 29 /** 30 * Loads a {@link MaxHistory} from {@code file}, or generates a new one that 31 * will be saved to {@code file}. 32 */ 33 public static MaxHistory forFolder(File file) { 34 if (file.exists()) { 35 try { 36 return readHistory(file); 37 } catch (CouldNotReadCoreException e) { 38 e.printStackTrace(); 39 file.delete(); 40 } 41 } 42 return new MaxHistory(file); 43 } 44 45 private static MaxHistory readHistory(File storedResults) 46 throws CouldNotReadCoreException { 47 try { 48 FileInputStream file = new FileInputStream(storedResults); 49 try { 50 ObjectInputStream stream = new ObjectInputStream(file); 51 try { 52 return (MaxHistory) stream.readObject(); 53 } finally { 54 stream.close(); 55 } 56 } finally { 57 file.close(); 58 } 59 } catch (Exception e) { 60 throw new CouldNotReadCoreException(e); 61 } 62 } 63 64 /* 65 * We have to use the f prefix until the next major release to ensure 66 * serialization compatibility. 67 * See https://github.com/junit-team/junit/issues/976 68 */ 69 private final Map<String, Long> fDurations = new HashMap<String, Long>(); 70 private final Map<String, Long> fFailureTimestamps = new HashMap<String, Long>(); 71 private final File fHistoryStore; 72 73 private MaxHistory(File storedResults) { 74 fHistoryStore = storedResults; 75 } 76 77 private void save() throws IOException { 78 ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream( 79 fHistoryStore)); 80 stream.writeObject(this); 81 stream.close(); 82 } 83 84 Long getFailureTimestamp(Description key) { 85 return fFailureTimestamps.get(key.toString()); 86 } 87 88 void putTestFailureTimestamp(Description key, long end) { 89 fFailureTimestamps.put(key.toString(), end); 90 } 91 92 boolean isNewTest(Description key) { 93 return !fDurations.containsKey(key.toString()); 94 } 95 96 Long getTestDuration(Description key) { 97 return fDurations.get(key.toString()); 98 } 99 100 void putTestDuration(Description description, long duration) { 101 fDurations.put(description.toString(), duration); 102 } 103 104 private final class RememberingListener extends RunListener { 105 private long overallStart = System.currentTimeMillis(); 106 107 private Map<Description, Long> starts = new HashMap<Description, Long>(); 108 109 @Override 110 public void testStarted(Description description) throws Exception { 111 starts.put(description, System.nanoTime()); // Get most accurate 112 // possible time 113 } 114 115 @Override 116 public void testFinished(Description description) throws Exception { 117 long end = System.nanoTime(); 118 long start = starts.get(description); 119 putTestDuration(description, end - start); 120 } 121 122 @Override 123 public void testFailure(Failure failure) throws Exception { 124 putTestFailureTimestamp(failure.getDescription(), overallStart); 125 } 126 127 @Override 128 public void testRunFinished(Result result) throws Exception { 129 save(); 130 } 131 } 132 133 private class TestComparator implements Comparator<Description> { 134 public int compare(Description o1, Description o2) { 135 // Always prefer new tests 136 if (isNewTest(o1)) { 137 return -1; 138 } 139 if (isNewTest(o2)) { 140 return 1; 141 } 142 // Then most recently failed first 143 int result = getFailure(o2).compareTo(getFailure(o1)); 144 return result != 0 ? result 145 // Then shorter tests first 146 : getTestDuration(o1).compareTo(getTestDuration(o2)); 147 } 148 149 private Long getFailure(Description key) { 150 Long result = getFailureTimestamp(key); 151 if (result == null) { 152 return 0L; // 0 = "never failed (that I know about)" 153 } 154 return result; 155 } 156 } 157 158 /** 159 * @return a listener that will update this history based on the test 160 * results reported. 161 */ 162 public RunListener listener() { 163 return new RememberingListener(); 164 } 165 166 /** 167 * @return a comparator that ranks tests based on the JUnit Max sorting 168 * rules, as described in the {@link MaxCore} class comment. 169 */ 170 public Comparator<Description> testComparator() { 171 return new TestComparator(); 172 } 173} 174