xref: /JGit/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java (revision 2161c1e5e4a53b3c56256389b3fc388b3c51d82d)
12d4d6448SMatthias Sohn /*
25c5f7c6bSMatthias Sohn  * Copyright (C) 2016, Matthias Sohn <matthias.sohn@sap.com> and others
32d4d6448SMatthias Sohn  *
45c5f7c6bSMatthias Sohn  * This program and the accompanying materials are made available under the
55c5f7c6bSMatthias Sohn  * terms of the Eclipse Distribution License v. 1.0 which is available at
65c5f7c6bSMatthias Sohn  * https://www.eclipse.org/org/documents/edl-v10.php.
72d4d6448SMatthias Sohn  *
85c5f7c6bSMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
92d4d6448SMatthias Sohn  */
102d4d6448SMatthias Sohn package org.eclipse.jgit.junit;
112d4d6448SMatthias Sohn 
122d4d6448SMatthias Sohn import java.text.MessageFormat;
132d4d6448SMatthias Sohn import java.util.logging.Level;
142d4d6448SMatthias Sohn import java.util.logging.Logger;
152d4d6448SMatthias Sohn 
162d4d6448SMatthias Sohn import org.junit.rules.TestRule;
172d4d6448SMatthias Sohn import org.junit.runner.Description;
182d4d6448SMatthias Sohn import org.junit.runners.model.Statement;
192d4d6448SMatthias Sohn 
202d4d6448SMatthias Sohn /**
21a90b75b4SMatthias Sohn  * {@link org.junit.rules.TestRule} which enables to run the same JUnit test
22a90b75b4SMatthias Sohn  * repeatedly. Add this rule to the test class
232d4d6448SMatthias Sohn  *
242d4d6448SMatthias Sohn  * <pre>
252d4d6448SMatthias Sohn  * public class MyTest {
262d4d6448SMatthias Sohn  * 	&#64;Rule
272d4d6448SMatthias Sohn  * 	public RepeatRule repeatRule = new RepeatRule();
282d4d6448SMatthias Sohn  * 	...
292d4d6448SMatthias Sohn  * }
302d4d6448SMatthias Sohn  * </pre>
312d4d6448SMatthias Sohn  *
322d4d6448SMatthias Sohn  * and annotate the test to be repeated with the
332d4d6448SMatthias Sohn  * {@code @Repeat(n=<repetitions>)} annotation
342d4d6448SMatthias Sohn  *
352d4d6448SMatthias Sohn  * <pre>
362d4d6448SMatthias Sohn  * &#64;Test
372d4d6448SMatthias Sohn  * &#64;Repeat(n = 100)
382d4d6448SMatthias Sohn  * public void test() {
392d4d6448SMatthias Sohn  * 	...
402d4d6448SMatthias Sohn  * }
412d4d6448SMatthias Sohn  * </pre>
422d4d6448SMatthias Sohn  *
432d4d6448SMatthias Sohn  * then this test will be repeated 100 times. If any test execution fails test
442d4d6448SMatthias Sohn  * repetition will be stopped.
452d4d6448SMatthias Sohn  */
462d4d6448SMatthias Sohn public class RepeatRule implements TestRule {
472d4d6448SMatthias Sohn 
48*14a157dfSDavid Pursehouse 	private static final Logger LOG = Logger
492d4d6448SMatthias Sohn 			.getLogger(RepeatRule.class.getName());
502d4d6448SMatthias Sohn 
51376c20f4SMatthias Sohn 	/**
52376c20f4SMatthias Sohn 	 * Exception thrown if repeated execution of a test annotated with
53376c20f4SMatthias Sohn 	 * {@code @Repeat} failed.
54376c20f4SMatthias Sohn 	 */
552d4d6448SMatthias Sohn 	public static class RepeatedTestException extends RuntimeException {
562d4d6448SMatthias Sohn 		private static final long serialVersionUID = 1L;
572d4d6448SMatthias Sohn 
58376c20f4SMatthias Sohn 		/**
59376c20f4SMatthias Sohn 		 * Constructor
60376c20f4SMatthias Sohn 		 *
61376c20f4SMatthias Sohn 		 * @param message
62376c20f4SMatthias Sohn 		 *            the error message
63376c20f4SMatthias Sohn 		 */
RepeatedTestException(String message)64ba5d13edSMatthias Sohn 		public RepeatedTestException(String message) {
65ba5d13edSMatthias Sohn 			super(message);
66ba5d13edSMatthias Sohn 		}
67ba5d13edSMatthias Sohn 
68376c20f4SMatthias Sohn 		/**
69376c20f4SMatthias Sohn 		 * Constructor
70376c20f4SMatthias Sohn 		 *
71376c20f4SMatthias Sohn 		 * @param message
72376c20f4SMatthias Sohn 		 *            the error message
73376c20f4SMatthias Sohn 		 * @param cause
74376c20f4SMatthias Sohn 		 *            exception causing this exception
75376c20f4SMatthias Sohn 		 */
RepeatedTestException(String message, Throwable cause)762d4d6448SMatthias Sohn 		public RepeatedTestException(String message, Throwable cause) {
772d4d6448SMatthias Sohn 			super(message, cause);
782d4d6448SMatthias Sohn 		}
792d4d6448SMatthias Sohn 	}
802d4d6448SMatthias Sohn 
812d4d6448SMatthias Sohn 	private static class RepeatStatement extends Statement {
822d4d6448SMatthias Sohn 
832d4d6448SMatthias Sohn 		private final int repetitions;
842d4d6448SMatthias Sohn 
85ba5d13edSMatthias Sohn 		private boolean abortOnFailure;
86ba5d13edSMatthias Sohn 
872d4d6448SMatthias Sohn 		private final Statement statement;
882d4d6448SMatthias Sohn 
RepeatStatement(int repetitions, boolean abortOnFailure, Statement statement)89ba5d13edSMatthias Sohn 		private RepeatStatement(int repetitions, boolean abortOnFailure,
90ba5d13edSMatthias Sohn 				Statement statement) {
912d4d6448SMatthias Sohn 			this.repetitions = repetitions;
92ba5d13edSMatthias Sohn 			this.abortOnFailure = abortOnFailure;
932d4d6448SMatthias Sohn 			this.statement = statement;
942d4d6448SMatthias Sohn 		}
952d4d6448SMatthias Sohn 
962d4d6448SMatthias Sohn 		@Override
evaluate()972d4d6448SMatthias Sohn 		public void evaluate() throws Throwable {
98ba5d13edSMatthias Sohn 			int failures = 0;
992d4d6448SMatthias Sohn 			for (int i = 0; i < repetitions; i++) {
1002d4d6448SMatthias Sohn 				try {
1012d4d6448SMatthias Sohn 					statement.evaluate();
1022d4d6448SMatthias Sohn 				} catch (Throwable e) {
103ba5d13edSMatthias Sohn 					failures += 1;
1042d4d6448SMatthias Sohn 					RepeatedTestException ex = new RepeatedTestException(
1052d4d6448SMatthias Sohn 							MessageFormat.format(
1062d4d6448SMatthias Sohn 									"Repeated test failed when run for the {0}. time",
1072d4d6448SMatthias Sohn 									Integer.valueOf(i + 1)),
1082d4d6448SMatthias Sohn 							e);
1092d4d6448SMatthias Sohn 					LOG.log(Level.SEVERE, ex.getMessage(), ex);
110ba5d13edSMatthias Sohn 					if (abortOnFailure) {
1112d4d6448SMatthias Sohn 						throw ex;
1122d4d6448SMatthias Sohn 					}
1132d4d6448SMatthias Sohn 				}
1142d4d6448SMatthias Sohn 			}
115ba5d13edSMatthias Sohn 			if (failures > 0) {
116ba5d13edSMatthias Sohn 				RepeatedTestException e = new RepeatedTestException(
117ba5d13edSMatthias Sohn 						MessageFormat.format(
118ba5d13edSMatthias Sohn 								"Test failed {0} times out of {1} repeated executions",
119ba5d13edSMatthias Sohn 								Integer.valueOf(failures),
120ba5d13edSMatthias Sohn 								Integer.valueOf(repetitions)));
121ba5d13edSMatthias Sohn 				LOG.log(Level.SEVERE, e.getMessage(), e);
122ba5d13edSMatthias Sohn 				throw e;
123ba5d13edSMatthias Sohn 			}
124ba5d13edSMatthias Sohn 		}
1252d4d6448SMatthias Sohn 	}
1262d4d6448SMatthias Sohn 
127a90b75b4SMatthias Sohn 	/** {@inheritDoc} */
1282d4d6448SMatthias Sohn 	@Override
apply(Statement statement, Description description)1292d4d6448SMatthias Sohn 	public Statement apply(Statement statement, Description description) {
1302d4d6448SMatthias Sohn 		Statement result = statement;
1312d4d6448SMatthias Sohn 		Repeat repeat = description.getAnnotation(Repeat.class);
1322d4d6448SMatthias Sohn 		if (repeat != null) {
1332d4d6448SMatthias Sohn 			int n = repeat.n();
134ba5d13edSMatthias Sohn 			boolean abortOnFailure = repeat.abortOnFailure();
135ba5d13edSMatthias Sohn 			result = new RepeatStatement(n, abortOnFailure, statement);
1362d4d6448SMatthias Sohn 		}
1372d4d6448SMatthias Sohn 		return result;
1382d4d6448SMatthias Sohn 	}
1392d4d6448SMatthias Sohn }
140