Package net.grinder.scriptengine.groovy.junit

Source Code of net.grinder.scriptengine.groovy.junit.GrinderRunner

/*
* Licensed 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 net.grinder.scriptengine.groovy.junit;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.grinder.engine.process.JUnitThreadContextInitializer;
import net.grinder.engine.process.JUnitThreadContextUpdater;
import net.grinder.scriptengine.exception.AbstractExceptionProcessor;
import net.grinder.scriptengine.groovy.GroovyExceptionProcessor;
import net.grinder.scriptengine.groovy.junit.annotation.AfterProcess;
import net.grinder.scriptengine.groovy.junit.annotation.AfterThread;
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess;
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread;
import net.grinder.scriptengine.groovy.junit.annotation.Repeat;
import net.grinder.scriptengine.groovy.junit.annotation.RunRate;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.internal.runners.model.MultipleFailureException;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.rules.MethodRule;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runner.notification.StoppedByUserException;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;

/**
* Grinder JUnit Runner. Grinder JUnit Runner is the custom {@link Runner} which lets the user can
* run the Grinder script in the JUnit context.
*
* This runner has a little bit different characteristic from conventional JUnit test.
* <ul>
* <li>All Test annotated tests are executed with a single instance.</li>
* <li>{@link BeforeProcess} and {@link AfterProcess} annotated methods are executed per each
* process.</li>
* <li>{@link BeforeThread} and {@link AfterThread} annotated methods are executed per each thread.</li>
* <li>{@link Repeat} annotated
* </ul>
*
* In addition, it contains a little different behavior from generic grinder test script.
* <ul>
* <li>It only initiates only 1 process and 1 thread.</li>
* <li>Each <code>&#064;test</code> annotated method are independent to run. So one failure from one
* method doesn't block the other methods' runs</li>
* </ul>
*
* @author JunHo Yoon
* @author Mavlarn
* @see BeforeProcess
* @see BeforeThread
* @see AfterThread
* @see AfterProcess
* @see Repeat
* @since 1.0
*/
public class GrinderRunner extends BlockJUnit4ClassRunner {
  private JUnitThreadContextInitializer threadContextInitializer;
  private JUnitThreadContextUpdater threadContextUpdater;
  private TestObjectFactory testTargetFactory;
  private PerThreadStatement finalPerThreadStatement;
  private AbstractExceptionProcessor exceptionProcessor = new GroovyExceptionProcessor();
  private boolean enableRateRunner = true;
  private Map<FrameworkMethod, Statement> frameworkMethodCache = new HashMap<FrameworkMethod, Statement>();

  /**
   * Constructor.
   *
   * @param klass klass
   * @throws InitializationError class initialization error.
   */
  public GrinderRunner(Class<?> klass) throws InitializationError {
    super(klass);
    this.testTargetFactory = new TestObjectFactory() {
      @Override
      public TestClass getTestClass() {
        return GrinderRunner.this.getTestClass();
      }

      @Override
      public Object createTest() throws Exception {
        return GrinderRunner.this.createTest();
      }
    };

    initializeGrinderContext();
  }

  /**
   * Constructor.
   *
   * @param klass  klass
   * @param runner runner class
   * @throws InitializationError class initialization error.
   */
  public GrinderRunner(Class<?> klass, final Object runner) throws InitializationError {
    super(klass);
    this.testTargetFactory = new TestObjectFactory() {
      @Override
      public TestClass getTestClass() {
        return GrinderRunner.this.getTestClass();
      }

      @Override
      public Object createTest() throws Exception {
        return runner;
      }
    };

    initializeGrinderContext();
  }


  protected void initializeGrinderContext() {
    this.threadContextInitializer = new JUnitThreadContextInitializer();
    this.threadContextInitializer.initialize();
    this.threadContextUpdater = threadContextInitializer.getThreadContextUpdater();
    this.finalPerThreadStatement = new PerThreadStatement() {
      @Override
      void before() {
        attachWorker();
      }

      @Override
      void after() {
        detachWorker();
      }
    };
  }

  @Override
  protected List<FrameworkMethod> getChildren() {
    return super.getChildren();
  }

  @Override
  public void run(RunNotifier notifier) {
    registerRunNotifierListener(notifier);
    Description description = getDescription();
    enableRateRunner = isRateRunnerEnabled();
    EachTestNotifier testNotifier = new EachTestNotifier(notifier, description);
    try {
      Statement statement = classBlock(notifier);
      statement.evaluate();
    } catch (AssumptionViolatedException e) {
      testNotifier.fireTestIgnored();
    } catch (StoppedByUserException e) {
      throw e;
    } catch (Throwable e) {
      testNotifier.addFailure(e);
    }

  }

  /**
   * Check if the rate runner should be enabled.
   *
   * @return true if enabled;
   */
  protected boolean isRateRunnerEnabled() {
    Description description = getDescription();
    return description.testCount() > 1 && isRepeatRunnerEnabled();
  }

  private boolean isRepeatRunnerEnabled() {
    Annotation[] annotations = getTestClass().getAnnotations();
    boolean repeatAnnotation = false;
    for (Annotation each : annotations) {
      if (each.annotationType().equals(Repeat.class)) {
        repeatAnnotation = true;
      }
    }
    return repeatAnnotation;
  }

  @Override
  protected Statement classBlock(RunNotifier notifier) {
    Statement statement = childrenInvoker(notifier);
    statement = withRepeat(statement);
    statement = withBeforeThread(statement);
    statement = withBeforeProcess(statement);
    statement = withAfterThread(statement);
    statement = withAfterProcess(statement);
    return statement;
  }

  protected Statement withRepeat(Statement statement) {
    Annotation[] annotations = getTestClass().getAnnotations();
    int repetition = 1;
    for (Annotation each : annotations) {
      if (each.annotationType().equals(Repeat.class)) {
        repetition = ((Repeat) each).value();
      }
    }
    return new RepetitionStatement(statement, repetition, threadContextUpdater);
  }

  @SuppressWarnings("deprecation")
  protected Statement methodBlock(FrameworkMethod method) {
    Statement statement = frameworkMethodCache.get(method);
    if (statement != null) {
      return statement;
    }
    Object testObject = testTargetFactory.getTestObject();
    statement = methodInvoker(method, testObject);
    statement = possiblyExpectingExceptions(method, testObject, statement);
    statement = withPotentialTimeout(method, testObject, statement);
    statement = withBefores(method, testObject, statement);
    statement = withAfters(method, testObject, statement);
    statement = withRules(method, testObject, statement);
    if (enableRateRunner) {
      statement = withRunRate(method, testObject, statement);
    }
    frameworkMethodCache.put(method, statement);
    return statement;
  }

  protected Statement withRunRate(FrameworkMethod method, @SuppressWarnings("UnusedParameters") Object target, Statement statement) {
    RunRate runRate = method.getAnnotation(RunRate.class);
    return runRate == null ? statement : new RunRateStatement(statement, runRate.value());
  }

  private Statement withRules(FrameworkMethod method, Object target, Statement statement) {
    Statement result = statement;
    for (MethodRule each : getTestClass().getAnnotatedFieldValues(target, Rule.class, MethodRule.class)) {
      result = each.apply(result, method, target);
    }
    return result;
  }

  /**
   * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this
   * class and superclasses before executing {@code statement}; if any throws an Exception, stop
   * execution and pass the exception on.
   *
   * @param statement statement
   * @return wrapped statement
   */
  protected Statement withBeforeProcess(Statement statement) {
    TestClass testClass = getTestClass();
    List<FrameworkMethod> befores = testClass.getAnnotatedMethods(BeforeProcess.class);
    befores.addAll(testClass.getAnnotatedMethods(BeforeClass.class));
    return befores.isEmpty() ? statement : new RunBefores(statement, befores, null);
  }

  /**
   * Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
   * and superclasses before executing {@code statement}; all AfterClass methods are always
   * executed: exceptions thrown by previous steps are combined, if necessary, with exceptions
   * from AfterClass methods into a {@link MultipleFailureException}.
   *
   * @param statement statement
   * @return wrapped statement
   */
  protected Statement withAfterProcess(Statement statement) {
    TestClass testClass = getTestClass();
    List<FrameworkMethod> afters = testClass.getAnnotatedMethods(AfterProcess.class);
    afters.addAll(testClass.getAnnotatedMethods(AfterClass.class));
    return afters.isEmpty() ? statement : new RunAfters(statement, afters, null);
  }

  protected Statement withAfterThread(Statement statement) {
    List<FrameworkMethod> afterThreads = getTestClass().getAnnotatedMethods(AfterThread.class);
    return new RunAfterThreads(statement, afterThreads, testTargetFactory, finalPerThreadStatement);
  }

  protected Statement withBeforeThread(Statement statement) {
    List<FrameworkMethod> beforeThreads = getTestClass().getAnnotatedMethods(BeforeThread.class);
    return new RunBeforeThreads(statement, beforeThreads, testTargetFactory, finalPerThreadStatement);
  }

  protected void registerRunNotifierListener(RunNotifier notifier) {
    notifier.addFirstListener(new RunListener() {
      @Override
      public void testStarted(Description description) throws Exception {

      }

      @Override
      public void testRunStarted(Description description) throws Exception {
        attachWorker();
      }

      @Override
      public void testRunFinished(Result result) throws Exception {
        detachWorker();
      }

      @Override
      public void testFailure(Failure failure) throws Exception {
        Throwable exception = failure.getException();
        Throwable filtered = exceptionProcessor.filterException(exception);
        if (exception != filtered) {
          exception.initCause(filtered);
        }
      }
    });
  }

  void attachWorker() {
    this.threadContextInitializer.attachWorkerThreadContext();
  }

  void detachWorker() {
    this.threadContextInitializer.detachWorkerThreadContext();
  }
}
TOP

Related Classes of net.grinder.scriptengine.groovy.junit.GrinderRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.