View Javadoc
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  		 * @since 5.1.9
64  		 */
65  		public RepeatedTestException(String message) {
66  			super(message);
67  		}
68  
69  		/**
70  		 * Constructor
71  		 *
72  		 * @param message
73  		 *            the error message
74  		 * @param cause
75  		 *            exception causing this exception
76  		 */
77  		public RepeatedTestException(String message, Throwable cause) {
78  			super(message, cause);
79  		}
80  	}
81  
82  	private static class RepeatStatement extends Statement {
83  
84  		private final int repetitions;
85  
86  		private boolean abortOnFailure;
87  
88  		private final Statement statement;
89  
90  		private RepeatStatement(int repetitions, boolean abortOnFailure,
91  				Statement statement) {
92  			this.repetitions = repetitions;
93  			this.abortOnFailure = abortOnFailure;
94  			this.statement = statement;
95  		}
96  
97  		@Override
98  		public void evaluate() throws Throwable {
99  			int failures = 0;
100 			for (int i = 0; i < repetitions; i++) {
101 				try {
102 					statement.evaluate();
103 				} catch (Throwable e) {
104 					failures += 1;
105 					RepeatedTestException ex = new RepeatedTestException(
106 							MessageFormat.format(
107 									"Repeated test failed when run for the {0}. time",
108 									Integer.valueOf(i + 1)),
109 							e);
110 					LOG.log(Level.SEVERE, ex.getMessage(), ex);
111 					if (abortOnFailure) {
112 						throw ex;
113 					}
114 				}
115 			}
116 			if (failures > 0) {
117 				RepeatedTestException e = new RepeatedTestException(
118 						MessageFormat.format(
119 								"Test failed {0} times out of {1} repeated executions",
120 								Integer.valueOf(failures),
121 								Integer.valueOf(repetitions)));
122 				LOG.log(Level.SEVERE, e.getMessage(), e);
123 				throw e;
124 			}
125 		}
126 	}
127 
128 	/** {@inheritDoc} */
129 	@Override
130 	public Statement apply(Statement statement, Description description) {
131 		Statement result = statement;
132 		Repeat repeat = description.getAnnotation(Repeat.class);
133 		if (repeat != null) {
134 			int n = repeat.n();
135 			boolean abortOnFailure = repeat.abortOnFailure();
136 			result = new RepeatStatement(n, abortOnFailure, statement);
137 		}
138 		return result;
139 	}
140 }