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