// Copyright 2010 The Bazel Authors. All Rights Reserved. // // 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 com.google.testing.junit.runner.junit4; import com.google.testing.junit.junit4.runner.RegExTestCaseFilter; import com.google.testing.junit.junit4.runner.SuiteTrimmingFilter; import com.google.testing.junit.runner.internal.Stdout; import com.google.testing.junit.runner.internal.junit4.CancellableRequestFactory; import com.google.testing.junit.runner.model.TestSuiteModel; import com.google.testing.junit.runner.util.GoogleTestSecurityManager; import com.google.testing.junit.runner.util.Supplier; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Set; import javax.annotation.Nullable; import javax.inject.Inject; import org.junit.internal.runners.ErrorReportingRunner; import org.junit.runner.Description; import org.junit.runner.JUnitCore; import org.junit.runner.Request; import org.junit.runner.Result; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; /** * Main entry point for running JUnit4 tests.
*/
public class JUnit4Runner {
private final Request request;
private final CancellableRequestFactory requestFactory;
private final Supplier
*
* Note that this is carefully written to avoid running into potential
* problems with the way runners implement filtering. The JavaDoc for
* {@link org.junit.runner.manipulation.Filterable} states that tests that
* don't match the filter should be removed, which implies if you apply two
* filters, you will always get an intersection of the two. Unfortunately, the
* filtering implementation of {@link org.junit.runners.ParentRunner} does not
* do this, and instead uses a "last applied filter wins" strategy.
*
* We work around potential problems by ensuring that if we apply a second
* filter, the filter is more restrictive than the first. We also assume that
* if filtering fails, the request will have a runner that is a
* {@link ErrorReportingRunner}. Luckily, we can cover this with tests to make
* sure we don't break if JUnit changes in the future.
*
* @param request Request to filter
* @param shardingFilter Sharding filter to use; {@link Filter#ALL} to not do sharding
* @param testIncludeFilterRegexp String denoting a regular expression with which
* to filter tests. Only test descriptions that match this regular
* expression will be run. If {@code null}, tests will not be filtered.
* @param testExcludeFilterRegexp String denoting a regular expression with which
* to filter tests. Only test descriptions that do not match this regular
* expression will be run. If {@code null}, tests will not be filtered.
* @return Filtered request (may be a request that delegates to
* {@link ErrorReportingRunner}
*/
private static Request applyFilters(Request request, Filter shardingFilter,
@Nullable String testIncludeFilterRegexp, @Nullable String testExcludeFilterRegexp) {
// Allow the user to specify a filter on the command line
boolean allowNoTests = false;
Filter filter = Filter.ALL;
if (testIncludeFilterRegexp != null) {
filter = RegExTestCaseFilter.include(testIncludeFilterRegexp);
}
if (testExcludeFilterRegexp != null) {
Filter excludeFilter = RegExTestCaseFilter.exclude(testExcludeFilterRegexp);
filter = filter.intersect(excludeFilter);
}
if (testIncludeFilterRegexp != null || testExcludeFilterRegexp != null) {
try {
request = applyFilter(request, filter);
} catch (NoTestsRemainException e) {
return createErrorReportingRequestForFilterError(filter);
}
/*
* If you filter a sharded test to run one test, we don't want all the
* shards but one to fail.
*/
allowNoTests = (shardingFilter != Filter.ALL);
}
// Sharding
if (shardingFilter != Filter.ALL) {
filter = filter.intersect(shardingFilter);
}
if (filter != Filter.ALL) {
try {
request = applyFilter(request, filter);
} catch (NoTestsRemainException e) {
if (allowNoTests) {
return Request.runner(new NoOpRunner());
} else {
return createErrorReportingRequestForFilterError(filter);
}
}
}
return request;
}
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
private static Request createErrorReportingRequestForFilterError(Filter filter) {
ErrorReportingRunner runner = new ErrorReportingRunner(Filter.class, new Exception(
String.format("No tests found matching %s", filter.describe())));
return Request.runner(runner);
}
private void checkJUnitRunnerApiVersion() {
config.getJUnitRunnerApiVersion();
}
private void installSecurityManager() {
previousSecurityManager = System.getSecurityManager();
GoogleTestSecurityManager newSecurityManager = new GoogleTestSecurityManager();
System.setSecurityManager(newSecurityManager);
// set field after call to setSecurityManager() in case that call fails
googleTestSecurityManager = newSecurityManager;
}
private void disableSecurityManager() {
if (googleTestSecurityManager != null) {
GoogleTestSecurityManager.uninstallIfInstalled();
System.setSecurityManager(previousSecurityManager);
}
}
static class NoOpRunner extends Runner {
@Override
public Description getDescription() {
return Description.createTestDescription(getClass(), "nothingToDo");
}
@Override
public void run(RunNotifier notifier) {
}
}
/**
* A simple initializer which can be used to provide additional initialization logic in custom
* runners.
*
* Initializers will be run in unspecified order. If an exception is thrown it will not be
* deemed recoverable and will cause the runner to error-out.
*/
public interface Initializer {
void initialize();
}
}