aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepeatRule.java
blob: 30fffe9d94449f05712dbc7726fd260ee5952cc6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Copyright (C) 2016, Matthias Sohn <matthias.sohn@sap.com> and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package org.eclipse.jgit.junit;

import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

/**
 * {@link org.junit.rules.TestRule} which enables to run the same JUnit test
 * repeatedly. Add this rule to the test class
 *
 * <pre>
 * public class MyTest {
 * 	&#64;Rule
 * 	public RepeatRule repeatRule = new RepeatRule();
 * 	...
 * }
 * </pre>
 *
 * and annotate the test to be repeated with the
 * {@code @Repeat(n=<repetitions>)} annotation
 *
 * <pre>
 * &#64;Test
 * &#64;Repeat(n = 100)
 * public void test() {
 * 	...
 * }
 * </pre>
 *
 * then this test will be repeated 100 times. If any test execution fails test
 * repetition will be stopped.
 */
public class RepeatRule implements TestRule {

	private static final Logger LOG = Logger
			.getLogger(RepeatRule.class.getName());

	/**
	 * Exception thrown if repeated execution of a test annotated with
	 * {@code @Repeat} failed.
	 */
	public static class RepeatedTestException extends RuntimeException {
		private static final long serialVersionUID = 1L;

		/**
		 * Constructor
		 *
		 * @param message
		 *            the error message
		 * @since 5.1.9
		 */
		public RepeatedTestException(String message) {
			super(message);
		}

		/**
		 * Constructor
		 *
		 * @param message
		 *            the error message
		 * @param cause
		 *            exception causing this exception
		 */
		public RepeatedTestException(String message, Throwable cause) {
			super(message, cause);
		}
	}

	private static class RepeatStatement extends Statement {

		private final int repetitions;

		private boolean abortOnFailure;

		private final Statement statement;

		private RepeatStatement(int repetitions, boolean abortOnFailure,
				Statement statement) {
			this.repetitions = repetitions;
			this.abortOnFailure = abortOnFailure;
			this.statement = statement;
		}

		@Override
		public void evaluate() throws Throwable {
			int failures = 0;
			for (int i = 0; i < repetitions; i++) {
				try {
					statement.evaluate();
				} catch (Throwable e) {
					failures += 1;
					RepeatedTestException ex = new RepeatedTestException(
							MessageFormat.format(
									"Repeated test failed when run for the {0}. time",
									Integer.valueOf(i + 1)),
							e);
					LOG.log(Level.SEVERE, ex.getMessage(), ex);
					if (abortOnFailure) {
						throw ex;
					}
				}
			}
			if (failures > 0) {
				RepeatedTestException e = new RepeatedTestException(
						MessageFormat.format(
								"Test failed {0} times out of {1} repeated executions",
								Integer.valueOf(failures),
								Integer.valueOf(repetitions)));
				LOG.log(Level.SEVERE, e.getMessage(), e);
				throw e;
			}
		}
	}

	@Override
	public Statement apply(Statement statement, Description description) {
		Statement result = statement;
		Repeat repeat = description.getAnnotation(Repeat.class);
		if (repeat != null) {
			int n = repeat.n();
			boolean abortOnFailure = repeat.abortOnFailure();
			result = new RepeatStatement(n, abortOnFailure, statement);
		}
		return result;
	}
}