/*
* Copyright 2010 Henry Coles
*
* 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.pitest.junit.adapter;
import static org.pitest.util.Unchecked.translateCheckedException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.internal.runners.ErrorReportingRunner;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runners.model.RunnerBuilder;
import org.pitest.functional.Option;
import org.pitest.testapi.AbstractTestUnit;
import org.pitest.testapi.ResultCollector;
import org.pitest.testapi.foreignclassloader.Events;
import org.pitest.util.ClassLoaderDetectionStrategy;
import org.pitest.util.IsolationUtils;
import org.pitest.util.Log;
import org.pitest.util.Unchecked;
public class AdaptedJUnitTestUnit extends AbstractTestUnit {
private static final Logger LOG = Log.getLogger();
private final ClassLoaderDetectionStrategy loaderDetection;
private final Class<?> clazz;
private final Option<Filter> filter;
public AdaptedJUnitTestUnit(final Class<?> clazz, final Option<Filter> filter) {
this(IsolationUtils.loaderDetectionStrategy(), clazz, filter);
}
AdaptedJUnitTestUnit(final ClassLoaderDetectionStrategy loaderDetection,
final Class<?> clazz, final Option<Filter> filter) {
super(new org.pitest.testapi.Description(createName(clazz, filter), clazz));
this.loaderDetection = loaderDetection;
this.clazz = clazz;
this.filter = filter;
}
private static String createName(final Class<?> clazz,
final Option<Filter> filter) {
if (filter.hasSome()) {
return filter.value().describe();
} else {
return clazz.getName();
}
}
@Override
public void execute(final ClassLoader loader, final ResultCollector rc) {
final Runner runner = createRunner(this.clazz);
checkForErrorRunner(runner);
filterIfRequired(rc, runner);
try {
if (this.loaderDetection.fromDifferentLoader(runner.getClass(), loader)) {
executeInDifferentClassLoader(loader, rc, runner);
} else {
final CustomRunnerExecutor nativeCe = new CustomRunnerExecutor(
this.getDescription(), runner, rc);
nativeCe.run();
}
} catch (final Exception e) {
LOG.log(Level.SEVERE, "Error while running adapter JUnit fixture "
+ this.clazz + " with filter " + this.filter, e);
throw translateCheckedException(e);
}
}
private void checkForErrorRunner(final Runner runner) {
if (runner instanceof ErrorReportingRunner) {
LOG.warning("JUnit error for class " + this.clazz + " : "
+ runner.getDescription());
}
}
private void filterIfRequired(final ResultCollector rc, final Runner runner) {
if (this.filter.hasSome()) {
if (!(runner instanceof Filterable)) {
LOG.warning("Not able to filter " + runner.getDescription()
+ ". Mutation may have prevented JUnit from constructing test");
return;
}
final Filterable f = (Filterable) runner;
try {
f.filter(this.filter.value());
} catch (final NoTestsRemainException e1) {
rc.notifySkipped(this.getDescription());
return;
}
}
}
public static Runner createRunner(final Class<?> clazz) {
final RunnerBuilder builder = createRunnerBuilder();
try {
return builder.runnerForClass(clazz);
} catch (final Throwable ex) {
LOG.log(Level.SEVERE, "Error while creating runner for " + clazz, ex);
throw translateCheckedException(ex);
}
}
private static RunnerBuilder createRunnerBuilder() {
return new AllDefaultPossibilitiesBuilder(true);
}
private void executeInDifferentClassLoader(final ClassLoader loader,
final ResultCollector rc, final Runner runner)
throws IllegalAccessException, InvocationTargetException {
// must jump through hoops to run in different class loader
// when even our framework classes may be duplicated
// translate everything via strings
final ForeignClassLoaderCustomRunnerExecutor ce = new ForeignClassLoaderCustomRunnerExecutor(
runner);
@SuppressWarnings("unchecked")
Callable<List<String>> foreignCe = (Callable<List<String>>) IsolationUtils
.cloneForLoader(ce, loader);
try {
final List<String> q = foreignCe.call();
convertStringsToResults(rc, q);
} catch (Exception ex) {
throw Unchecked.translateCheckedException(ex);
}
}
private void convertStringsToResults(final ResultCollector rc,
final List<String> q) {
Events.applyEvents(q, rc, this.getDescription());
}
@Override
public String toString() {
return "AdaptedJUnitTestUnit [clazz=" + this.clazz + ", filter="
+ this.filter + "]";
}
}