All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.testutils.junit.RetryRule Maven / Gradle / Ivy

There is a newer version: 1.5.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.flink.testutils.junit;

import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A rule to retry failed tests for a fixed number of times.
 *
 * 

Add the {@link RetryRule} to your test and annotate tests with {@link RetryOnFailure}. * *

 * public class YourTest {
 *
 *     {@literal @}Rule
 *     public RetryRule retryRule = new RetryRule();
 *
 *     {@literal @}Test
 *     {@literal @}RetryOnFailure(times=1)
 *     public void yourTest() {
 *         // This will be retried 1 time (total runs 2) before failing the test.
 *         throw new Exception("Failing test");
 *     }
 * }
 * 
*/ public class RetryRule implements TestRule { public static final Logger LOG = LoggerFactory.getLogger(RetryRule.class); @Override public Statement apply(Statement statement, Description description) { RetryOnFailure retryOnFailure = description.getAnnotation(RetryOnFailure.class); RetryOnException retryOnException = description.getAnnotation(RetryOnException.class); // sanity check that we don't use expected exceptions with the RetryOnX annotations if (retryOnFailure != null || retryOnException != null) { Test test = description.getAnnotation(Test.class); if (test.expected() != Test.None.class) { throw new IllegalArgumentException("You cannot combine the RetryOnFailure " + "annotation with the Test(expected) annotation."); } } // sanity check that we don't use both annotations if (retryOnFailure != null && retryOnException != null) { throw new IllegalArgumentException( "You cannot combine the RetryOnFailure and RetryOnException annotations."); } if (retryOnFailure != null) { return new RetryOnFailureStatement(retryOnFailure.times(), statement); } else if (retryOnException != null) { return new RetryOnExceptionStatement(retryOnException.times(), retryOnException.exception(), statement); } else { return statement; } } /** * Retries a test in case of a failure. */ private static class RetryOnFailureStatement extends Statement { private final int timesOnFailure; private int currentRun; private final Statement statement; private RetryOnFailureStatement(int timesOnFailure, Statement statement) { if (timesOnFailure < 0) { throw new IllegalArgumentException("Negatives number of retries on failure"); } this.timesOnFailure = timesOnFailure; this.statement = statement; } /** * Retry a test in case of a failure. * * @throws Throwable */ @Override public void evaluate() throws Throwable { for (currentRun = 0; currentRun <= timesOnFailure; currentRun++) { try { statement.evaluate(); break; // success } catch (Throwable t) { LOG.warn(String.format("Test run failed (%d/%d).", currentRun, timesOnFailure + 1), t); // Throw the failure if retried too often if (currentRun == timesOnFailure) { throw t; } } } } } /** * Retries a test in case of a failure. */ private static class RetryOnExceptionStatement extends Statement { private final Class exceptionClass; private final int timesOnFailure; private final Statement statement; private int currentRun; private RetryOnExceptionStatement(int timesOnFailure, Class exceptionClass, Statement statement) { if (timesOnFailure < 0) { throw new IllegalArgumentException("Negatives number of retries on failure"); } if (exceptionClass == null) { throw new NullPointerException("exceptionClass"); } this.exceptionClass = (exceptionClass); this.timesOnFailure = timesOnFailure; this.statement = statement; } /** * Retry a test in case of a failure with a specific exception. * * @throws Throwable */ @Override public void evaluate() throws Throwable { for (currentRun = 0; currentRun <= timesOnFailure; currentRun++) { try { statement.evaluate(); break; // success } catch (Throwable t) { LOG.warn(String.format("Test run failed (%d/%d).", currentRun, timesOnFailure + 1), t); if (!exceptionClass.isAssignableFrom(t.getClass()) || currentRun >= timesOnFailure) { // Throw the failure if retried too often, or if it is the wrong exception throw t; } } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy