/*
* Copyright 2016 Google Inc. 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 org.inferred.freebuilder.processor.util.testing;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import org.junit.rules.ExpectedException;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Optimized test runner for tests using {@link BehaviorTester}.
*
* <p>Pre-runs all tests once with no compilation, to determine which can use the same compiler.
* If two tests add the same processors and do not give conflicting java files, they will be
* run back-to-back with the same compiler instance. This means <b>all tests are run twice</b>.
* Any tests which do not compile the same code every time (e.g. random or time-based code) will
* flake with this runner.
*
* <p>Tests that provide <em>non-compilable</em> test code <b>must</b> use
* {@link ExpectedException} to catch the {@link CompilationException}, as this runner uses the
* resulting pre-run error message to mark the test as <b>unmergeable</b>. (If we did not do this,
* all tests using the same compiler would fail with the same compilation exception!)
*
* <p>This runner adds an "Introspection" test to the suite to show how much time the initial
* pre-run is taking. If no tests can share a compiler, the "Introspection" test will fail. This may
* indicate your processor does not have suitable equals and hashCode implementations.
*/
public class BehaviorTestRunner extends BlockJUnit4ClassRunner {
/**
* Annotates a public {@link BehaviorTester} field that will potentially be shared between
* multiple unit tests.
*/
@Retention(RUNTIME)
@Target(FIELD)
public @interface Shared {}
private final SharedBehaviorTesting testing;
public BehaviorTestRunner(Class<?> cls) throws InitializationError {
super(cls);
// JDK-8 bug: Cannot use `super::` if the superclass method is protected; see
// https://bugs.openjdk.java.net/browse/JDK-8139836
testing = new SharedBehaviorTesting(
notifier -> super.childrenInvoker(notifier),
(method, notifier) -> super.runChild(method, notifier),
() -> super.createTest(),
super::getDescription,
super::getTestClass,
Description::createTestDescription);
}
@Override
public Description getDescription() {
return testing.getDescription();
}
@Override
protected Statement childrenInvoker(RunNotifier notifier) {
return testing.childrenInvoker(notifier);
}
@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
testing.runChild(method, notifier);
}
@Override
public Object createTest() throws Exception {
return testing.createTest();
}
}