Home | History | Annotate | Download | only in max
      1 package org.junit.experimental.max;
      2 
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.FileOutputStream;
      6 import java.io.IOException;
      7 import java.io.ObjectInputStream;
      8 import java.io.ObjectOutputStream;
      9 import java.io.Serializable;
     10 import java.util.Comparator;
     11 import java.util.HashMap;
     12 import java.util.Map;
     13 
     14 import org.junit.runner.Description;
     15 import org.junit.runner.Result;
     16 import org.junit.runner.notification.Failure;
     17 import 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  */
     26 public 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