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  		 */
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  		 */
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  
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
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
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 }