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

com.nordstrom.automation.junit.RetryHandler Maven / Gradle / Ivy

There is a newer version: 17.1.1
Show newest version
package com.nordstrom.automation.junit;

import static com.nordstrom.automation.junit.LifecycleHooks.invoke;

import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.nordstrom.automation.junit.JUnitConfig.JUnitSettings;

/**
 * This class provided the utility methods used by the JUnit Foundation automatic retry feature.
 */
public class RetryHandler {

    private static final ServiceLoader retryAnalyzerLoader;
    private static final Logger LOGGER = LoggerFactory.getLogger(RetryHandler.class);
    
    static {
        retryAnalyzerLoader = ServiceLoader.load(JUnitRetryAnalyzer.class);
    }
    
    private RetryHandler() {
        throw new AssertionError("RetryHandler is a static utility class that cannot be instantiated");
    }
    
    /**
     * Run the specified method, retrying on failure.
     * 
     * @param runner JUnit test runner
     * @param method test method to be run
     * @param notifier run notifier through which events are published
     * @param maxRetry maximum number of retry attempts
     */
    static void runChildWithRetry(Object runner, final FrameworkMethod method, RunNotifier notifier, int maxRetry) {
        boolean doRetry = false;
        Statement statement = invoke(runner, "methodBlock", method);
        Description description = invoke(runner, "describeChild", method);
        AtomicInteger count = new AtomicInteger(maxRetry);
        
        do {
            EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
            
            eachNotifier.fireTestStarted();
            try {
                statement.evaluate();
                doRetry = false;
            } catch (AssumptionViolatedException thrown) {
                doRetry = doRetry(method, thrown, count);
                if (doRetry) {
                    description = RetriedTest.proxyFor(description, thrown);
                    eachNotifier.fireTestIgnored();
                } else {
                    eachNotifier.addFailedAssumption(thrown);
                }
            } catch (Throwable thrown) {
                doRetry = doRetry(method, thrown, count);
                if (doRetry) {
                    description = RetriedTest.proxyFor(description, thrown);
                    eachNotifier.fireTestIgnored();
                } else {
                    eachNotifier.addFailure(thrown);
                }
            } finally {
                eachNotifier.fireTestFinished();
            }
        } while (doRetry);
    }
    
    /**
     * Determine if the indicated failure should be retried.
     * 
     * @param method failed test method
     * @param thrown exception for this failed test
     * @param retryCounter retry counter (remaining attempts)
     * @return {@code true} if failed test should be retried; otherwise {@code false}
     */
    static boolean doRetry(FrameworkMethod method, Throwable thrown, AtomicInteger retryCounter) {
        boolean doRetry = false;
        if ((retryCounter.decrementAndGet() > -1) && isRetriable(method, thrown)) {
            LOGGER.warn("### RETRY ### {}", method);
            doRetry = true;
        }
        return doRetry;
    }

    /**
     * Get the configured maximum retry count for failed tests ({@link JUnitSetting#MAX_RETRY MAX_RETRY}).
     * 

* NOTE: If the specified method or the class that declares it are marked with the {@code @NoRetry} * annotation, this method returns zero (0). * * @param runner JUnit test runner * @param method test method for which retry is being considered * @return maximum retry attempts that will be made if the specified method fails */ static int getMaxRetry(Object runner, final FrameworkMethod method) { int maxRetry = 0; // determine if retry is disabled for this method NoRetry noRetryOnMethod = method.getAnnotation(NoRetry.class); // determine if retry is disabled for the class that declares this method NoRetry noRetryOnClass = method.getDeclaringClass().getAnnotation(NoRetry.class); // if method isn't ignored or excluded from retry attempts if (Boolean.FALSE.equals(invoke(runner, "isIgnored", method)) && (noRetryOnMethod == null) && (noRetryOnClass == null)) { // get configured maximum retry count maxRetry = JUnitConfig.getConfig().getInteger(JUnitSettings.MAX_RETRY.key(), Integer.valueOf(0)); } return maxRetry; } /** * Determine if the specified failed test should be retried. * * @param method failed test method * @param thrown exception for this failed test * @return {@code true} if test should be retried; otherwise {@code false} */ static boolean isRetriable(final FrameworkMethod method, final Throwable thrown) { synchronized(retryAnalyzerLoader) { for (JUnitRetryAnalyzer analyzer : retryAnalyzerLoader) { if (analyzer.retry(method, thrown)) { return true; } } } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy