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 * @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 * @Test 372d4d6448SMatthias Sohn * @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