All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.powermock.modules.junit4.internal.impl.NotificationBuilder Maven / Gradle / Ivy
/*
* Copyright 2013 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.internal.impl;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.powermock.core.spi.testresult.Result;
import org.powermock.core.spi.testresult.TestMethodResult;
import org.powermock.tests.utils.PowerMockTestNotifier;
import org.powermock.tests.utils.impl.MockPolicyInitializerImpl;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Stateful class that, from information from JUnit and test-classes,
* can build and send notifications to PowerMockTestNotifier.
*/
class NotificationBuilder {
enum DetectedTestRunBehaviour {
PENDING,
START_FIRES_FIRST,
TEST_INSTANCE_CREATED_FIRST,
ALL_TESTINSTANCES_ARE_CREATED_FIRST,
TEST_INSTANCES_ARE_REUSED,
INCONSISTENT_BEHAVIOUR;
}
private static final Pattern methodDisplayNameRgx =
Pattern.compile("(^[^\\(\\[]++)|([^(]+(?=\\)))");
private final Method[] testMethods;
private final List> pendingTestInstances;
private final PowerMockTestNotifier powerMockTestNotifier;
private DetectedTestRunBehaviour behaviour = DetectedTestRunBehaviour.PENDING;
private Description currentDescription;
private Object currentTestInstance;
private String testClassName;
private Object latestTestInstance;
private Method latestMethod;
private static final Object[] unsupportedMethodArgs = {};
private final Map> methodsPerInstance =
new IdentityHashMap>() {
@Override
public List get(Object key) {
if (!containsKey(key)) {
put(key, new LinkedList());
}
return super.get(key);
}
};
private final Map ongoingTestRuns =
new IdentityHashMap();
private class OngoingTestRun implements TestMethodResult {
final Description testDescription;
final Object testInstance;
final Method testMethod;
private Result result;
OngoingTestRun(Description testDescription, Object testInstance) {
this.testDescription = testDescription;
this.testInstance = testInstance;
this.testMethod = determineTestMethod(testDescription);
pendingTestInstances.remove(testInstance);
Class> testClass = testClass();
new MockPolicyInitializerImpl(testClass).initialize(testClass.getClassLoader());
powerMockTestNotifier.notifyBeforeTestMethod(
testInstance, testMethod, unsupportedMethodArgs);
ongoingTestRuns.put(testDescription, this);
}
final Class> testClass() {
if (null == testClassName) {
return testInstance.getClass();
} else {
try {
return Class.forName(testClassName, false,
testInstance.getClass().getClassLoader());
} catch (ClassNotFoundException ex) {
return testInstance.getClass();
}
}
}
void report(Result result) {
if (null != this.result && Result.SUCCESSFUL == result
|| this.result == result) {
/* Already notified - ignore this duplication */
return;
} else if (null != this.result) {
new IllegalStateException(
"Will report an unexpected result-notification " + result
+ " after previously received notification " + this.result)
.printStackTrace();
}
this.result = result;
powerMockTestNotifier.notifyAfterTestMethod(
testInstance, testMethod, unsupportedMethodArgs, this);
}
@Override
public Result getResult() {
return this.result;
}
}
public NotificationBuilder(Method[] testMethods,
PowerMockTestNotifier notifier,
List> pendingTestInstances) {
this.testMethods = testMethods;
this.pendingTestInstances = pendingTestInstances;
this.powerMockTestNotifier = notifier;
}
private Method determineTestMethod(Description d) {
Matcher matchMethodName = methodDisplayNameRgx
.matcher(d.getDisplayName());
matchMethodName.find();
String methodName = matchMethodName.group();
boolean latestTestMethodCanBeRepeated = false;
for (Method m : testMethods) {
if (m.getName().equals(methodName)) {
if (m == latestMethod) {
latestTestMethodCanBeRepeated = true;
} else {
return latestMethod = m;
}
}
}
if (latestTestMethodCanBeRepeated) {
return latestMethod;
} else {
new IllegalArgumentException(
"Unable to determine method-name from description="
+ d + "; - ignored").printStackTrace();
return null;
}
}
private Class> reloadParamType(
Class> testClass, Class> typeToReload) {
if (typeToReload.isPrimitive()
|| testClass.getClassLoader() == typeToReload.getClassLoader()) {
return typeToReload;
} else if (typeToReload.isArray()) {
Class> newComponentType = reloadParamType(
testClass, typeToReload.getComponentType());
if (newComponentType == typeToReload.getComponentType()) {
return typeToReload;
} else {
return Array.newInstance(newComponentType, 0).getClass();
}
} else {
try {
return Class.forName(typeToReload.getName(),
true, testClass.getClassLoader());
} catch (ClassNotFoundException ex) {
throw new Error(ex);
}
}
}
private Method reloadMethod(Class> testClass, Method m) {
if (testClass.getClassLoader() == m.getDeclaringClass().getClassLoader()) {
return m;
} else if (!m.getDeclaringClass().getName()
.equals(testClass.getName())) {
return reloadMethod(testClass.getSuperclass(), m);
}
Class[] paramTypes = m.getParameterTypes();
for (int i = 0; i < paramTypes.length; ++i) {
paramTypes[i] = reloadParamType(testClass, paramTypes[i]);
}
try {
return testClass.getDeclaredMethod(m.getName(), paramTypes);
} catch (NoSuchMethodException ex) {
throw new Error(ex);
}
}
void testSuiteStarted(Class> testClass) {
for (int i = 0; i < testMethods.length; ++i) {
testMethods[i] = reloadMethod(testClass, testMethods[i]);
}
powerMockTestNotifier.notifyBeforeTestSuiteStarted(testClass, testMethods);
this.testClassName = testClass.getName();
}
void testStartHasBeenFired(Description d) {
OngoingTestRun oldTestRun = ongoingTestRuns.get(d);
if (null != oldTestRun && null != oldTestRun.getResult()) {
throw new IllegalStateException(
"Fired testrun is already running: " + d);
}
currentDescription = d;
switch (behaviour) {
case PENDING:
behaviour = DetectedTestRunBehaviour.START_FIRES_FIRST;
case START_FIRES_FIRST:
return;
case TEST_INSTANCE_CREATED_FIRST:
if (currentTestInstance == latestTestInstance) {
behaviour = DetectedTestRunBehaviour.TEST_INSTANCES_ARE_REUSED;
}
case TEST_INSTANCES_ARE_REUSED:
latestTestInstance = currentTestInstance;
methodsPerInstance.get(currentTestInstance).add(
new OngoingTestRun(d, currentTestInstance).testMethod);
return;
case ALL_TESTINSTANCES_ARE_CREATED_FIRST:
System.err.println(
"Notifications are not supported when all test-instances are created first!");
return;
default:
throw new AssertionError();
}
}
void testInstanceCreated(Object newTestInstance) {
switch (behaviour) {
case PENDING:
behaviour = DetectedTestRunBehaviour.TEST_INSTANCE_CREATED_FIRST;
currentTestInstance = newTestInstance;
return ;
case TEST_INSTANCE_CREATED_FIRST:
if (methodsPerInstance.isEmpty()) {
behaviour = DetectedTestRunBehaviour.ALL_TESTINSTANCES_ARE_CREATED_FIRST;
} else if (currentTestInstance == latestTestInstance) {
currentTestInstance = newTestInstance;
} else {
behaviour = DetectedTestRunBehaviour.INCONSISTENT_BEHAVIOUR;
}
return;
case ALL_TESTINSTANCES_ARE_CREATED_FIRST:
case INCONSISTENT_BEHAVIOUR:
System.err.println(
"Notifications are not supported for behaviour " + behaviour);
return;
case START_FIRES_FIRST:
currentTestInstance = latestTestInstance = newTestInstance;
latestMethod = determineTestMethod(currentDescription);
methodsPerInstance.get(newTestInstance).add(
new OngoingTestRun(currentDescription, newTestInstance).testMethod);
return;
default:
throw new AssertionError("Unknown behaviour: " + behaviour);
}
}
void testIgnored(Description d) {
if (!notify(d, Result.IGNORED)
&& DetectedTestRunBehaviour.TEST_INSTANCE_CREATED_FIRST == behaviour
&& currentTestInstance != latestTestInstance) {
/*
* Workaround for some bad behaviour in JUnit-4.4 default runner,
* which creates a test-instance first, even for a test that is ignored!!
*/
currentTestInstance = latestTestInstance;
}
}
void assumptionFailed(Description d) {
notify(d, Result.IGNORED);
}
void failure(Failure f) {
notify(f.getDescription(), Result.FAILED);
}
void testFinished(Description d) {
notify(d, Result.SUCCESSFUL);
}
/**
* @return true if notification concerns an ongoing testrun; otherwise false
* when there is no test launched for the specified description
*/
private boolean notify(Description d, Result result) {
OngoingTestRun testRun = ongoingTestRuns.get(d);
if (null == testRun) {
// System.err.println("Notification not enabled for " + d);
return false;
} else {
testRun.report(result);
return true;
}
}
}