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 * @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 * @Test 37 * @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