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 return new MaxHistory(file); 42 } 43 44 private static MaxHistory readHistory(File storedResults) 45 throws CouldNotReadCoreException { 46 try { 47 FileInputStream file= new FileInputStream(storedResults); 48 try { 49 ObjectInputStream stream= new ObjectInputStream(file); 50 try { 51 return (MaxHistory) stream.readObject(); 52 } finally { 53 stream.close(); 54 } 55 } finally { 56 file.close(); 57 } 58 } catch (Exception e) { 59 throw new CouldNotReadCoreException(e); 60 } 61 } 62 63 private final Map<String, Long> fDurations= new HashMap<String, Long>(); 64 65 private final Map<String, Long> fFailureTimestamps= new HashMap<String, Long>(); 66 67 private final File fHistoryStore; 68 69 private MaxHistory(File storedResults) { 70 fHistoryStore= storedResults; 71 } 72 73 private void save() throws IOException { 74 ObjectOutputStream stream= new ObjectOutputStream(new FileOutputStream( 75 fHistoryStore)); 76 stream.writeObject(this); 77 stream.close(); 78 } 79 80 Long getFailureTimestamp(Description key) { 81 return fFailureTimestamps.get(key.toString()); 82 } 83 84 void putTestFailureTimestamp(Description key, long end) { 85 fFailureTimestamps.put(key.toString(), end); 86 } 87 88 boolean isNewTest(Description key) { 89 return !fDurations.containsKey(key.toString()); 90 } 91 92 Long getTestDuration(Description key) { 93 return fDurations.get(key.toString()); 94 } 95 96 void putTestDuration(Description description, long duration) { 97 fDurations.put(description.toString(), duration); 98 } 99 100 private final class RememberingListener extends RunListener { 101 private long overallStart= System.currentTimeMillis(); 102 103 private Map<Description, Long> starts= new HashMap<Description, Long>(); 104 105 @Override 106 public void testStarted(Description description) throws Exception { 107 starts.put(description, System.nanoTime()); // Get most accurate 108 // possible time 109 } 110 111 @Override 112 public void testFinished(Description description) throws Exception { 113 long end= System.nanoTime(); 114 long start= starts.get(description); 115 putTestDuration(description, end - start); 116 } 117 118 @Override 119 public void testFailure(Failure failure) throws Exception { 120 putTestFailureTimestamp(failure.getDescription(), overallStart); 121 } 122 123 @Override 124 public void testRunFinished(Result result) throws Exception { 125 save(); 126 } 127 } 128 129 private class TestComparator implements Comparator<Description> { 130 public int compare(Description o1, Description o2) { 131 // Always prefer new tests 132 if (isNewTest(o1)) 133 return -1; 134 if (isNewTest(o2)) 135 return 1; 136 // Then most recently failed first 137 int result= getFailure(o2).compareTo(getFailure(o1)); 138 return result != 0 ? result 139 // Then shorter tests first 140 : getTestDuration(o1).compareTo(getTestDuration(o2)); 141 } 142 143 private Long getFailure(Description key) { 144 Long result= getFailureTimestamp(key); 145 if (result == null) 146 return 0L; // 0 = "never failed (that I know about)" 147 return result; 148 } 149 } 150 151 /** 152 * @return a listener that will update this history based on the test 153 * results reported. 154 */ 155 public RunListener listener() { 156 return new RememberingListener(); 157 } 158 159 /** 160 * @return a comparator that ranks tests based on the JUnit Max sorting 161 * rules, as described in the {@link MaxCore} class comment. 162 */ 163 public Comparator<Description> testComparator() { 164 return new TestComparator(); 165 } 166} 167