/* * Copyright 2016 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.tests.utils.impl; import org.powermock.core.classloader.annotations.PowerMockListener; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.reporter.MockingFrameworkReporterFactory; import org.powermock.core.spi.PowerMockTestListener; import org.powermock.reflect.Whitebox; import org.powermock.tests.utils.RunnerTestSuiteChunker; import org.powermock.tests.utils.TestChunk; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; /** * Abstract base class for test suite chunking, i.e. a suite is chunked into * several smaller pieces which are ran with different classloaders. A chunk is * defined by the {@link PrepareForTest} annotation and whichever test-method * annotation the actual implementation-class specifies by overriding the * method {@link #testMethodAnnotation()}. This to make sure that you * can byte-code manipulate classes in tests without impacting on other tests. * */ public abstract class AbstractTestSuiteChunkerImpl<T> extends AbstractCommonTestSuiteChunkerImpl implements RunnerTestSuiteChunker { /* * The classes listed in this set has been chunked and its delegates has * been created. */ protected final Set<Class<?>> delegatesCreatedForTheseClasses = new LinkedHashSet<Class<?>>(); // A list of junit delegates. protected final List<T> delegates = new ArrayList<T>(); protected volatile int testCount = NOT_INITIALIZED; protected AbstractTestSuiteChunkerImpl(Class<?> testClass) throws Exception { super(testClass); } protected AbstractTestSuiteChunkerImpl(Class<?>... testClasses) throws Exception { super(testClasses); } protected Object getPowerMockTestListenersLoadedByASpecificClassLoader(Class<?> clazz, ClassLoader classLoader) { try { int defaultListenerSize = DEFAULT_TEST_LISTENERS_SIZE; Class<?> annotationEnablerClass = null; try { annotationEnablerClass = Class.forName("org.powermock.api.extension.listener.AnnotationEnabler", false, classLoader); } catch (ClassNotFoundException e) { // Annotation enabler wasn't found in class path defaultListenerSize = 0; } final Class<?> powerMockTestListenerType = Class.forName(PowerMockTestListener.class.getName(), false, classLoader); Object testListeners = null; if (clazz.isAnnotationPresent(PowerMockListener.class)) { PowerMockListener annotation = clazz.getAnnotation(PowerMockListener.class); final Class<? extends PowerMockTestListener>[] powerMockTestListeners = annotation.value(); if (powerMockTestListeners.length > 0) { testListeners = Array.newInstance(powerMockTestListenerType, powerMockTestListeners.length + defaultListenerSize); for (int i = 0; i < powerMockTestListeners.length; i++) { String testListenerClassName = powerMockTestListeners[i].getName(); final Class<?> listenerTypeLoadedByClassLoader = Class.forName(testListenerClassName, false, classLoader); Array.set(testListeners, i, Whitebox.newInstance(listenerTypeLoadedByClassLoader)); } } } else { testListeners = Array.newInstance(powerMockTestListenerType, defaultListenerSize); } // Add default annotation enabler listener if (annotationEnablerClass != null) { Array.set(testListeners, Array.getLength(testListeners) - 1, Whitebox.newInstance(annotationEnablerClass)); } return testListeners; } catch (ClassNotFoundException e) { throw new IllegalStateException("PowerMock internal error: Failed to load class.", e); } } /** * {@inheritDoc} */ public final void createTestDelegators(Class<?> testClass, List<TestChunk> chunks) throws Exception { for (TestChunk chunk : chunks) { ClassLoader classLoader = chunk.getClassLoader(); List<Method> methodsToTest = chunk.getTestMethodsToBeExecutedByThisClassloader(); T runnerDelegator = createDelegatorFromClassloader(classLoader, testClass, methodsToTest); delegates.add(runnerDelegator); } delegatesCreatedForTheseClasses.add(testClass); } protected abstract T createDelegatorFromClassloader(ClassLoader classLoader, Class<?> testClass, final List<Method> methodsToTest) throws Exception; /** * Get the internal test index for a junit runner delegate based on the * "real" original test index. For example, the test may need to run a * single test, for example the test with index 3. However since PowerMock * may have chunked the test suite to use many classloaders and junit * delegators the index (3) must be mapped to an internal representation for * the specific junit runner delegate. This is what this method does. I.e. * it will iterate through all junit runner delegates and see if they * contain the test with index 3, in the internal index of this test * delegator is returned. * * @param originalTestIndex * The original test index as seen by the test runner. * @return The internal test index as seen by PowerMock or <code>-1</code> * if no index was found. * */ public int getInternalTestIndex(int originalTestIndex) { Set<Entry<Integer, List<Integer>>> delegatorEntrySet = testAtDelegateMapper.entrySet(); for (Entry<Integer, List<Integer>> entry : delegatorEntrySet) { final List<Integer> testIndexesForThisDelegate = entry.getValue(); final int internalIndex = testIndexesForThisDelegate.indexOf(originalTestIndex); if (internalIndex != INTERNAL_INDEX_NOT_FOUND) { return internalIndex; } } return INTERNAL_INDEX_NOT_FOUND; } /** * Get the junit runner delegate that handles the test at index * <code>testIndex</code>. Throws a {@link RuntimeException} if a delegator * is not found for the specific test index. * * @param testIndex * The test index that a delegator should hold. * @return The index for of the junit runner delegate as seen by JTestRack. */ public int getDelegatorIndex(int testIndex) { int delegatorIndex = -1; Set<Entry<Integer, List<Integer>>> entrySet = testAtDelegateMapper.entrySet(); for (Entry<Integer, List<Integer>> entry : entrySet) { // If the delegator contains the test case, return the index of the // delegator. if (entry.getValue().contains(testIndex)) { delegatorIndex = entry.getKey(); break; } } if (delegatorIndex == -1) { throw new RuntimeException("Internal error: Failed to find the delgator index."); } return delegatorIndex; } public Class<?>[] getTestClasses() { return testClasses; } @SuppressWarnings("unchecked") protected MockingFrameworkReporterFactory getFrameworkReporterFactory() { Class<MockingFrameworkReporterFactory> mockingFrameworkReporterFactoryClass; try { ClassLoader classLoader = this.getClass().getClassLoader(); mockingFrameworkReporterFactoryClass = (Class<MockingFrameworkReporterFactory>) classLoader.loadClass("org.powermock.api.extension.reporter.MockingFrameworkReporterFactoryImpl"); } catch (ClassNotFoundException e) { throw new IllegalStateException( "Extension API internal error: org.powermock.api.extension.reporter.MockingFrameworkReporterFactoryImpl could not be located in classpath."); } return Whitebox.newInstance(mockingFrameworkReporterFactoryClass); } }