/*
* 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.core.testlisteners;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Core static utility to help modules, such as PowerMockRunner, that needs to
* communicate with some 3rd-party framework in order to properly fire
* events to PowerMockTestListener instances.
*/
public class GlobalNotificationBuildSupport {
public interface Callback {
void suiteClassInitiated(Class<?> testClass);
void testInstanceCreated(Object testInstance);
}
private static final Map<String, Callback> testSuiteCallbacks =
new ConcurrentHashMap<String, Callback>();
private static final Map<Class<?>, Callback> initiatedTestSuiteClasses =
new ConcurrentHashMap<Class<?>, Callback>();
private static final ThreadLocal<Class<?>> pendingInitiatedTestClass =
new ThreadLocal<Class<?>>();
public static void prepareTestSuite(
String testClassName, Callback callback) {
if (testSuiteCallbacks.containsKey(testClassName)) {
throw new IllegalStateException(
"Needs to wait for concurrent test-suite execution to start!");
} else {
testSuiteCallbacks.put(testClassName, callback);
Class<?> initiatedTestClass = pendingInitiatedTestClass.get();
if (null != initiatedTestClass
&& initiatedTestClass.getName().equals(testClassName)) {
System.err.println("Detected late test-suite preparation of "
+ "already initiated test-" + initiatedTestClass);
testClassInitiated(initiatedTestClass);
}
}
}
public static void testClassInitiated(Class<?> testClass) {
if (!initiatedTestSuiteClasses.containsKey(testClass)) {
Callback callback = testSuiteCallbacks.get(testClass.getName());
if (null == callback) {
pendingInitiatedTestClass.set(testClass);
} else {
initiatedTestSuiteClasses.put(testClass, callback);
callback.suiteClassInitiated(testClass);
pendingInitiatedTestClass.set(null);
}
}
}
private static int countInitializersInTrace(final String className) {
int initializerCount = 0;
for (StackTraceElement ste : new Throwable().getStackTrace()) {
if ("<init>".equals(ste.getMethodName())
&& className.equals(ste.getClassName())
&& 2 <= ++initializerCount) {
return 2;
}
}
return initializerCount;
}
public static void testInstanceCreated(Object testInstance) {
for (Class<?> c = testInstance.getClass(); Object.class != c; c = c.getSuperclass()) {
Callback callback = initiatedTestSuiteClasses.get(c);
if (null != callback) {
if (1 == countInitializersInTrace(c.getName())) {
callback.testInstanceCreated(testInstance);
}
return;
}
}
}
public static void closeTestSuite(Class<?> testClass) {
Callback callback = initiatedTestSuiteClasses.remove(testClass);
if (null != callback
&& !initiatedTestSuiteClasses.values().contains(callback)) {
testSuiteCallbacks.values().remove(callback);
}
}
public static void closePendingTestSuites(Callback callback) {
testSuiteCallbacks.values().remove(callback);
initiatedTestSuiteClasses.values()
.removeAll(java.util.Collections.singleton(callback));
}
}