/* * Copyright 2008 the original author or authors. * * 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 org.powermock.modules.junit4.common.internal.impl; import org.junit.Test; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.Filterable; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.manipulation.Sortable; import org.junit.runner.manipulation.Sorter; import org.junit.runner.notification.RunNotifier; import org.powermock.core.reporter.MockingFrameworkReporter; import org.powermock.core.spi.PowerMockTestListener; import org.powermock.core.spi.testresult.TestSuiteResult; import org.powermock.core.spi.testresult.impl.TestSuiteResultImpl; import org.powermock.modules.junit4.common.internal.JUnit4TestSuiteChunker; import org.powermock.modules.junit4.common.internal.PowerMockJUnitRunnerDelegate; import org.powermock.tests.utils.PowerMockTestNotifier; import org.powermock.tests.utils.TestChunk; import org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl; import org.powermock.tests.utils.impl.PowerMockTestNotifierImpl; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class JUnit4TestSuiteChunkerImpl extends AbstractTestSuiteChunkerImpl<PowerMockJUnitRunnerDelegate> implements JUnit4TestSuiteChunker, Filterable, Sortable { private static final Class<? extends Annotation> testMethodAnnotation = Test.class; private Description description; private final Class<? extends PowerMockJUnitRunnerDelegate> runnerDelegateImplementationType; public JUnit4TestSuiteChunkerImpl(Class<?> testClass, Class<? extends PowerMockJUnitRunnerDelegate> runnerDelegateImplementationType) throws Exception { super(testClass); if (testClass == null) { throw new IllegalArgumentException("You must supply a test class"); } if (runnerDelegateImplementationType == null) { throw new IllegalArgumentException("Runner delegate type cannot be null."); } this.runnerDelegateImplementationType = runnerDelegateImplementationType; try { createTestDelegators(testClass, getTestChunksEntries(testClass)); } catch (InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw new RuntimeException(cause); } } } @Override public void run(RunNotifier notifier) { List<TestChunk> chunkEntries = getTestChunks(); Iterator<TestChunk> iterator = chunkEntries.iterator(); if (delegates.size() != getChunkSize()) { throw new IllegalStateException("Internal error: There must be an equal number of suites and delegates."); } final Class<?> testClass = getTestClasses()[0]; final PowerMockTestListener[] powerMockTestListeners = (PowerMockTestListener[]) getPowerMockTestListenersLoadedByASpecificClassLoader( testClass, this.getClass().getClassLoader()); final Set<Method> allMethods = new LinkedHashSet<Method>(); for (TestChunk testChunk : getTestChunks()) { allMethods.addAll(testChunk.getTestMethodsToBeExecutedByThisClassloader()); } final Method[] allMethodsAsArray = allMethods.toArray(new Method[allMethods.size()]); final PowerMockTestNotifier powerMockTestNotifier = new PowerMockTestNotifierImpl(powerMockTestListeners); powerMockTestNotifier.notifyBeforeTestSuiteStarted(testClass, allMethodsAsArray); int failureCount = 0; int successCount = 0; int ignoreCount = 0; for (PowerMockJUnitRunnerDelegate delegate : delegates) { TestChunk next = iterator.next(); final ClassLoader key = next.getClassLoader(); PowerMockJUnit4RunListener powerMockListener = new PowerMockJUnit4RunListener(key, powerMockTestNotifier); notifier.addListener(powerMockListener); final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(key); try { MockingFrameworkReporter mockingFrameworkReporter = getMockingFrameworkReporter(); mockingFrameworkReporter.enable(); delegate.run(notifier); mockingFrameworkReporter.disable(); } finally { Thread.currentThread().setContextClassLoader(originalClassLoader); } final int failureCountForThisPowerMockListener = powerMockListener.getFailureCount(); final int ignoreCountForThisPowerMockListener = powerMockListener.getIgnoreCount(); failureCount += failureCountForThisPowerMockListener; ignoreCount += ignoreCountForThisPowerMockListener; successCount += delegate.getTestCount() - failureCountForThisPowerMockListener - ignoreCountForThisPowerMockListener; notifier.removeListener(powerMockListener); } final TestSuiteResult testSuiteResult = new TestSuiteResultImpl(failureCount, successCount, getTestCount(), ignoreCount); powerMockTestNotifier.notifyAfterTestSuiteEnded(testClass, allMethodsAsArray, testSuiteResult); } private MockingFrameworkReporter getMockingFrameworkReporter() {return getFrameworkReporterFactory().create();} @Override public boolean shouldExecuteTestForMethod(Class<?> testClass, Method potentialTestMethod) { return new JUnit4TestMethodChecker(testClass, potentialTestMethod).isTestMethod(); } @Override protected Class<? extends Annotation> testMethodAnnotation() { return testMethodAnnotation; } @Override protected PowerMockJUnitRunnerDelegate createDelegatorFromClassloader(ClassLoader classLoader, Class<?> testClass, final List<Method> methodsToTest) throws Exception { Set<String> methodNames = new HashSet<String>(); for (Method method : methodsToTest) { methodNames.add(method.getName()); } final Class<?> testClassLoadedByMockedClassLoader = Class.forName(testClass.getName(), false, classLoader); /* * Array classes cannot be loaded be classloader.loadClass(..) in JDK 6. * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6500212. */ final Class<?> powerMockTestListenerArrayType = Class.forName(PowerMockTestListener[].class.getName(), false, classLoader); final Class<?> delegateClass = Class.forName(runnerDelegateImplementationType.getName(), false, classLoader); Constructor<?> con = delegateClass.getConstructor(Class.class, String[].class, powerMockTestListenerArrayType); return (PowerMockJUnitRunnerDelegate) con.newInstance( testClassLoadedByMockedClassLoader, methodNames.toArray(new String[methodNames.size()]), getPowerMockTestListenersLoadedByASpecificClassLoader(testClass, classLoader)); } @Override public synchronized int getTestCount() { if (testCount == NOT_INITIALIZED) { testCount = 0; for (PowerMockJUnitRunnerDelegate delegate : delegates) { testCount += delegate.getTestCount(); } } return testCount; } @Override public Description getDescription() { if (description == null) { if (delegates.size() == 0) { /* * This happens if Test A extends Test B and B uses the @RunWith * annotation and there are no tests defined in class B. */ return Description.createTestDescription(this.getClass(), "no tests in this class"); } // Use the first delegator as the base for the description. PowerMockJUnitRunnerDelegate delegate = delegates.get(0); description = delegate.getDescription(); /* * Add the remaining descriptions of all the chunked delegators. We * do this to make sure that we avoid adding chunks as "Unrooted * tests". */ for (int i = 1; i < delegates.size(); i++) { // Get the method-level descriptions ArrayList<Description> children = delegates.get(i).getDescription().getChildren(); // Add all method-level descriptions to the main description. for (Description methodDescription : children) { description.addChild(methodDescription); } } } return description; } @Override public void filter(Filter filter) throws NoTestsRemainException { for (Object delegate : delegates) { if (delegate instanceof Filterable) { ((Filterable) delegate).filter(filter); } } } @Override public void sort(Sorter sorter) { for (Object delegate : delegates) { if (delegate instanceof Sortable) { ((Sortable) delegate).sort(sorter); } } } }