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

patterntesting.runtime.junit.SmokeRunner Maven / Gradle / Ivy

Go to download

PatternTesting Runtime (patterntesting-rt) is the runtime component for the PatternTesting framework. It provides the annotations and base classes for the PatternTesting testing framework (e.g. patterntesting-check, patterntesting-concurrent or patterntesting-exception) but can be also used standalone for classpath monitoring or profiling. It uses AOP and AspectJ to perform this feat.

There is a newer version: 2.4.0
Show newest version
/*
 * $Id: SmokeRunner.java,v 1.30 2014/04/28 14:56:48 oboehm Exp $
 *
 * Copyright (c) 2010 by Oliver Boehm
 *
 * 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 orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 29.03.2010 by oliver ([email protected])
 */

package patterntesting.runtime.junit;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

import org.apache.commons.lang.StringUtils;
import org.junit.*;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.MultipleFailureException;
import org.junit.internal.runners.statements.*;
import org.junit.runner.Description;
import org.junit.runner.notification.*;
import org.junit.runners.*;
import org.junit.runners.model.*;
import org.slf4j.*;

import patterntesting.runtime.junit.internal.*;

/**
 * This is the eXtended Runner for JUnit 4 which handles the SmokeTest and
 * other annotations. In previous (intermediate) version it was called
 * "XRunner". But because this may be a little bit confusing name it was
 * renamed to "SmokeRunner" for the final version.
 * 
* It can be used together with the {@code @RunWith} annotation of JUnit 4. * * @author oliver * @since 1.0 (29.03.2010) */ @SuppressWarnings("deprecation") public class SmokeRunner extends ParentRunner { //public class SmokeRunner extends BlockJUnit4ClassRunner { private static final Logger log = LoggerFactory.getLogger(SmokeRunner.class); private final SmokeFilter xfilter = new SmokeFilter(); /** * Creates a SmokeRunner to run klass methods in parallel. * * @param klass the test class to run * @throws InitializationError if the test class is malformed */ public SmokeRunner(final Class klass) throws InitializationError { super(klass); } /** * The ParentRunner allows us no access to the filter. So we added this * method here (e.g. needed by the ParallelRunner). * * @return the SmokeFilter */ protected final SmokeFilter getFilter() { return this.xfilter; } // /** // * If we extends from BlockJUnit4CLassRunner we must overwrite this method // * here because we also support JUnit3 tests. Otherwise we would get an // * InitializationExcption. // */ // @Override // protected void collectInitializationErrors(List errors) { // } /** * Returns the methods that run tests. Default implementation * returns all methods annotated with {@code @Test} on this * class and superclasses that are not overridden. * * @return the methods that run tests */ @Override protected List getChildren() { TestClass testClass = this.getTestClass(); Class javaClass = testClass.getJavaClass(); if (ProfiledStatement.isTestCaseClass(testClass)) { return getJUnit3TestMethods(javaClass); } return testClass.getAnnotatedMethods(Test.class); } // /** // * This method is private in ParentRunner so we copied it and adapted it // * to our needs. It is not used by SmokeRunner but by the derived classes // * ParallelRunner and ParallelProxyRunner (where this class here is the // * common super class). // * // * @return the filtered children list // */ // protected final List getFilteredChildren() { // ArrayList filtered = new ArrayList(); // for (FrameworkMethod each : getChildren()) { // if (this.getFilter().shouldRun(describeChild(each))) { // filtered.add(each); // } // } // return filtered; // } /** * Create a Description of a single test named * name in the class clazz. Generally, this will * be a leaf Description. (see also * {@link BlockJUnit4ClassRunner}) * * @param child * the name of the test (a method name for test annotated with * {@link org.junit.Test}) * @return the description * @see org.junit.runners.ParentRunner#describeChild(java.lang.Object) */ @Override protected Description describeChild(final FrameworkMethod child) { return Description.createTestDescription(getTestClass().getJavaClass(), child.getName(), child.getAnnotations()); } /** * Checks the annotation of the method marked as "@BeforeClass" and add * (or filters out) the beforeClass method (needed to solve. * * @param statement the statement(s) before * @return the statement(s) after with the BeforeClass method * {@link "https://sourceforge.net/tracker/?func=detail&aid=3034823&group_id=48833&atid=454317"}). * @see org.junit.runners.ParentRunner#withBeforeClasses(org.junit.runners.model.Statement) */ @Override protected Statement withBeforeClasses(final Statement statement) { List filtered = filter(BeforeClass.class); if (filtered.isEmpty()) { return statement; } return new RunBefores(statement, filtered, null); } /** * Checks the annotation of the method marked as "@AfterClass" and add * (or filters out) the afterClass method (needed to solve. * * @param statement the statement(s) before * @return the statement(s) after with the AfterClass method * {@link "https://sourceforge.net/tracker/?func=detail&aid=3034823&group_id=48833&atid=454317"}). * @see org.junit.runners.ParentRunner#withAfterClasses(org.junit.runners.model.Statement) */ @Override protected Statement withAfterClasses(final Statement statement) { List filtered = filter(AfterClass.class); if (filtered.isEmpty()) { return statement; } return new RunAfters(statement, filtered, null); } private List filter(final Class annotationClass) { List methods = this.getTestClass().getAnnotatedMethods(annotationClass); return filter(methods); } private List filter(final List methods) { List filtered = new ArrayList(); for (FrameworkMethod fm : methods) { if (!this.xfilter.shouldBeIgnored(describeChild(fm))) { filtered.add(fm); } else if (log.isTraceEnabled()) { log.trace(fm.getMethod() + " is filtered out"); } } return filtered; } // /** // * Unfortunately sending a "fireTestIgnored" to the notifier does not work // * as expected. Neither the test class is displayed as "ignored" in the // * JUnit GUI of Eclipse nor the methods are displayed as "ignored". // * But more critical is that the count of the method is not correct - e.g. // * for the IntegrationTestClassTest with two methods the following is // * shown in the GUI: // * // * Runs: 0/2 // * // * That means: 0 methods of 2 methods are executed. But the correct display // * should be: // * // * Runs: 0/0 // * // * The workaround here is to send no "fireTestIgnored" for the class but for // * the single methods. So the result is then "Runs: 2/2" - not nice but the // * count is correct after running it. // * // * @param notifier the notifier // * @see org.junit.runners.ParentRunner#run(org.junit.runner.notification.RunNotifier) // */ // @Override // public void run(final RunNotifier notifier) { // if (this.shouldBeIgnored()) { // EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription()); // testNotifier.fireTestIgnored(); // } else { // super.run(notifier); // } // } // // /** // * Here we handle the annotations like {@code @TestIntegration} or // * {@code @Broken} for the whole class. // * // * @return true if test should be ignored // */ // private boolean shouldBeIgnored() { // TestClass testClass = this.getTestClass(); // if (runSmokeTests) { // return !hasSmokeTests(testClass); // } // Annotation[] annotations = testClass.getAnnotations(); // for (int i = 0; i < annotations.length; i++) { // Annotation a = annotations[i]; // if (a.annotationType().equals(IntegrationTest.class) // && isIntegrationTest((IntegrationTest) a)) { // return true; // } // } // return this.xfilter.shouldBeIgnored(describeTestClass()); // } // // private final Description describeTestClass() { // return Description.createSuiteDescription(getTestClass().getName(), getTestClass() // .getAnnotations()); // } // // private boolean isIntegrationTest(final IntegrationTest it) { // if (Environment.integrationTestEnabled) { // log.trace("all tests including integration tests will be executed"); // return false; // } // log.info(this.getTestClass().getName() + " SKIPPED because " // + it.value() + " (can be activated with '-D" // + Environment.INTEGRATION_TEST + "')"); // return true; // } // // /** // * Looks for the given test class if it has the SmokeClass annotation or // * one of its test methods have it. // * // * @param testClass the test class // * @return true if smoke tests are found // */ // private static boolean hasSmokeTests(final TestClass testClass) { // if (testClass.getJavaClass().getAnnotation(SmokeTest.class) != null) { // return true; // } // List smokeTests = testClass // .getAnnotatedMethods(SmokeTest.class); // return !smokeTests.isEmpty(); // } /** * Runs the test corresponding to {@code child}, which can be assumed to be * an element of the list returned by {@link ParentRunner#getChildren()}. * Subclasses are responsible for making sure that relevant test events are * reported through {@code notifier} * * @param method the method * @param notifier the notifier */ @Override @SuppressWarnings("all") protected void runChild(final FrameworkMethod method, final RunNotifier notifier) { Description description = describeChild(method); try { if (shouldBeIgnored(method)) { notifier.fireTestIgnored(description); return; } } catch (IllegalArgumentException iae) { notifier.fireTestStarted(description); fireTestAssumptionFailed(notifier, description, iae); notifier.fireTestFinished(description); return; } notifier.fireTestStarted(description); Statement stmt = methodBlock(method); try { stmt.evaluate(); } catch (AssumptionViolatedException ex) { fireTestAssumptionFailed(notifier, description, ex); } catch (Throwable e) { addFailure(notifier, e, description); } finally { logStatement(stmt); notifier.fireTestFinished(description); } } /** * In JUnit 4.5 and newer we can use the fireTestAssumptionFailed(..) * of the {@link RunNotifier} class. But JUnit 4.4 does not provide this * method. *
* We could map it to the {@link RunNotifier#fireTestFailure(Failure)} * method - but this does not work for JUnit 4.5 (some internal JUnit tests * will fail if you try that). * We could compile with JUnit 4.5, run the tests with JUnit 4.4 and see * what will happen. Perhaps we can catch the exception and call the * {@link RunNotifier#fireTestFailure(Failure)} method. * We could also give up because the architecture of JUnit has changed too * much between 4.4 and 4.5 - this is, what we do now. * * @param notifier the notifier * @param description the description * @param ex the ex * @since 1.2.20 */ protected static void fireTestAssumptionFailed(final RunNotifier notifier, final Description description, final Exception ex) { notifier.fireTestAssumptionFailed(new Failure(description, ex)); //notifier.fireTestFailure(new Failure(description, ex)); } /** * We will give a subclass (like e.g. ParallelRunner) the chance to report * the statement with its own logger. * * @param stmt the stmt to be logged */ protected void logStatement(final Statement stmt) { log.info("{}", stmt); } /** * Here we handle annotations like {@code @Ignore} where the execution of * the test method should be ignored. * * @param method the test method * @return true or false */ protected final boolean shouldBeIgnored(final FrameworkMethod method) { Ignore ignore = method.getAnnotation(Ignore.class); if (ignore != null) { if (log.isDebugEnabled()) { String reason = ignore.value(); if (StringUtils.isNotEmpty(reason)) { reason = " (" + reason + ")"; } log.debug(this.getTestClass().getName() + "." + method.getName() + " ignored" + reason); } return true; } return this.xfilter.shouldBeIgnored(describeChild(method)); } /** * Should be run. * * @param method the method * @return true, if successful */ protected final boolean shouldBeRun(final FrameworkMethod method) { return !this.xfilter.shouldBeIgnored(describeChild(method)); } /** * This method was inspired from an internal JUnit class * (EachTestNotifier#addFailure(Throwable)). * * @param notifier the notifier * @param targetException the target exception * @param description the description */ protected final void addFailure(final RunNotifier notifier, final Throwable targetException, final Description description) { if (targetException instanceof MultipleFailureException) { MultipleFailureException mfe = (MultipleFailureException) targetException; for (Throwable each : mfe.getFailures()) { addFailure(notifier, each, description); } return; } notifier.fireTestFailure(new Failure(description, targetException)); } /** * Creates a RunStatement for the given test method. * * @param method the test method * @return a created RunStatement */ protected Statement methodBlock(final FrameworkMethod method) { return new ProfiledStatement(this.getTestClass(), method); } /** * Here we look after public void methods with "test" as prefix and with no * arguments. *
* NOTE: This method is public because it is also needed by * patterntesting.concurrent.junit.JUnit3Executor * * @param testClass the test class * @return a list of public methods starting with prefix "test" */ public static List getJUnit3TestMethods(final Class testClass) { List children = new ArrayList(); Method[] methods = testClass.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; int mod = method.getModifiers(); if (Modifier.isPrivate(mod) || Modifier.isStatic(mod)) { continue; } if (method.getParameterTypes().length > 0) { continue; } Class returnType = method.getReturnType(); if (!returnType.toString().equalsIgnoreCase("void")) { continue; } String name = method.getName(); if (name.startsWith("test")) { if (Modifier.isPublic(mod)) { FrameworkMethod child = new FrameworkMethod(method); children.add(child); } else { log.warn(method + " isn't public"); } } } return children; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy