xref: /JGit/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java (revision 2161c1e5e4a53b3c56256389b3fc388b3c51d82d)
1 /*
2  * Copyright (C) 2016, Matthias Sohn <matthias.sohn@sap.com> and others
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Distribution License v. 1.0 which is available at
6  * https://www.eclipse.org/org/documents/edl-v10.php.
7  *
8  * SPDX-License-Identifier: BSD-3-Clause
9  */
10 package org.eclipse.jgit.junit;
11 
12 import java.text.MessageFormat;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
15 
16 import org.junit.rules.TestRule;
17 import org.junit.runner.Description;
18 import org.junit.runners.model.Statement;
19 
20 /**
21  * {@link org.junit.rules.TestRule} which enables to run the same JUnit test
22  * repeatedly. Add this rule to the test class
23  *
24  * <pre>
25  * public class MyTest {
26  * 	&#64;Rule
27  * 	public RepeatRule repeatRule = new RepeatRule();
28  * 	...
29  * }
30  * </pre>
31  *
32  * and annotate the test to be repeated with the
33  * {@code @Repeat(n=<repetitions>)} annotation
34  *
35  * <pre>
36  * &#64;Test
37  * &#64;Repeat(n = 100)
38  * public void test() {
39  * 	...
40  * }
41  * </pre>
42  *
43  * then this test will be repeated 100 times. If any test execution fails test
44  * repetition will be stopped.
45  */
46 public class RepeatRule implements TestRule {
47 
48 	private static final Logger LOG = Logger
49 			.getLogger(RepeatRule.class.getName());
50 
51 	/**
52 	 * Exception thrown if repeated execution of a test annotated with
53 	 * {@code @Repeat} failed.
54 	 */
55 	public static class RepeatedTestException extends RuntimeException {
56 		private static final long serialVersionUID = 1L;
57 
58 		/**
59 		 * Constructor
60 		 *
61 		 * @param message
62 		 *            the error message
63 		 */
RepeatedTestException(String message)64 		public RepeatedTestException(String message) {
65 			super(message);
66 		}
67 
68 		/**
69 		 * Constructor
70 		 *
71 		 * @param message
72 		 *            the error message
73 		 * @param cause
74 		 *            exception causing this exception
75 		 */
RepeatedTestException(String message, Throwable cause)76 		public RepeatedTestException(String message, Throwable cause) {
77 			super(message, cause);
78 		}
79 	}
80 
81 	private static class RepeatStatement extends Statement {
82 
83 		private final int repetitions;
84 
85 		private boolean abortOnFailure;
86 
87 		private final Statement statement;
88 
RepeatStatement(int repetitions, boolean abortOnFailure, Statement statement)89 		private RepeatStatement(int repetitions, boolean abortOnFailure,
90 				Statement statement) {
91 			this.repetitions = repetitions;
92 			this.abortOnFailure = abortOnFailure;
93 			this.statement = statement;
94 		}
95 
96 		@Override
evaluate()97 		public void evaluate() throws Throwable {
98 			int failures = 0;
99 			for (int i = 0; i < repetitions; i++) {
100 				try {
101 					statement.evaluate();
102 				} catch (Throwable e) {
103 					failures += 1;
104 					RepeatedTestException ex = new RepeatedTestException(
105 							MessageFormat.format(
106 									"Repeated test failed when run for the {0}. time",
107 									Integer.valueOf(i + 1)),
108 							e);
109 					LOG.log(Level.SEVERE, ex.getMessage(), ex);
110 					if (abortOnFailure) {
111 						throw ex;
112 					}
113 				}
114 			}
115 			if (failures > 0) {
116 				RepeatedTestException e = new RepeatedTestException(
117 						MessageFormat.format(
118 								"Test failed {0} times out of {1} repeated executions",
119 								Integer.valueOf(failures),
120 								Integer.valueOf(repetitions)));
121 				LOG.log(Level.SEVERE, e.getMessage(), e);
122 				throw e;
123 			}
124 		}
125 	}
126 
127 	/** {@inheritDoc} */
128 	@Override
apply(Statement statement, Description description)129 	public Statement apply(Statement statement, Description description) {
130 		Statement result = statement;
131 		Repeat repeat = description.getAnnotation(Repeat.class);
132 		if (repeat != null) {
133 			int n = repeat.n();
134 			boolean abortOnFailure = repeat.abortOnFailure();
135 			result = new RepeatStatement(n, abortOnFailure, statement);
136 		}
137 		return result;
138 	}
139 }
140