diff options
Diffstat (limited to 'src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal')
7 files changed, 434 insertions, 0 deletions
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/BUILD b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/BUILD index 7047b3bc08..7e3fa4d644 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/BUILD +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/BUILD @@ -1,11 +1,29 @@ package(default_visibility = ["//src:__subpackages__"]) +# Internal code for the JUnit runner that does not depend on JUnit 4. +# Code used by the JUnit runner that does depend on JUnit 4 +# lives in the ":junit4" rule. java_library( name = "internal", srcs = glob(["*.java"]), deps = [ "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/util", "//third_party:jsr330_inject", + ], +) + +# Internal code for the JUnit runner that depends on JUnit 4. +# Code used by the JUnit runner that doesn't depend on JUnit 4 +# lives in the "internal" rule. +java_library( + name = "junit4", + srcs = glob(["junit4/*.java"]), + deps = [ + ":internal", + "//src/java_tools/junitrunner/java/com/google/testing/junit/junit4:runner", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/model", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/util", + "//third_party:jsr330_inject", "//third_party:junit4", ], ) diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/Xml.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/Xml.java new file mode 100644 index 0000000000..2734eb1405 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/Xml.java @@ -0,0 +1,31 @@ +// Copyright 2012 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.internal; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.inject.Qualifier; + +/** + * Binding annotation that indicates that the given {@code String} or stream + * represents XML. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Qualifier +public @interface Xml { +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/CancellableRequestFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/CancellableRequestFactory.java new file mode 100644 index 0000000000..74ee5de8df --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/CancellableRequestFactory.java @@ -0,0 +1,130 @@ +// Copyright 2012 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.internal.junit4; + +import com.google.testing.junit.junit4.runner.MemoizingRequest; +import com.google.testing.junit.junit4.runner.RunNotifierWrapper; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.junit.runner.Description; +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runner.notification.StoppedByUserException; + +/** + * Creates requests that can be cancelled. + */ +@Singleton +public class CancellableRequestFactory { + private boolean requestCreated; + private volatile ThreadSafeRunNotifier currentNotifier; + private volatile boolean cancelRequested = false; + + @Inject + public CancellableRequestFactory() {} + + /** + * Creates a request that can be cancelled. Can only be called once. + * + * @param delegate request to wrap + */ + public Request createRequest(Request delegate) { + if (requestCreated) { + throw new IllegalStateException("a request was already created"); + } + return new MemoizingRequest(delegate) { + @Override + protected Runner createRunner(Request delegate) { + return new CancellableRunner(delegate.getRunner()); + } + }; + } + + /** + * Cancels the request created by this request factory. + */ + public void cancelRun() { + cancelRequested = true; + RunNotifier notifier = currentNotifier; + if (notifier != null) { + notifier.pleaseStop(); + } + } + + + private class CancellableRunner extends Runner { + private final Runner delegate; + + public CancellableRunner(Runner delegate) { + this.delegate = delegate; + } + + @Override + public Description getDescription() { + return delegate.getDescription(); + } + + @Override + public void run(RunNotifier notifier) { + currentNotifier = new ThreadSafeRunNotifier(notifier); + if (cancelRequested) { + currentNotifier.pleaseStop(); + } + + try { + delegate.run(currentNotifier); + } catch (StoppedByUserException e) { + if (cancelRequested) { + throw new RuntimeException("Test run interrupted", e); + } + throw e; + } + } + } + + + private static class ThreadSafeRunNotifier extends RunNotifierWrapper { + private volatile boolean stopRequested; + + public ThreadSafeRunNotifier(RunNotifier delegate) { + super(delegate); + } + + /** + * {@inheritDoc}<p> + * + * The implementation is almost an exact copy of the version in + * {@code RunNotifier} but is thread-safe. + */ + @Override + public void fireTestStarted(Description description) throws StoppedByUserException { + if (stopRequested) { + throw new StoppedByUserException(); + } + getDelegate().fireTestStarted(description); + } + + /** + * {@inheritDoc}<p> + * + * This method is thread-safe. + */ + @Override + public void pleaseStop() { + stopRequested = true; + } + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestNameListener.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestNameListener.java new file mode 100644 index 0000000000..b2a2e57d58 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestNameListener.java @@ -0,0 +1,55 @@ +// Copyright 2011 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.internal.junit4; + +import com.google.testing.junit.runner.util.TestNameProvider; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.junit.runner.Description; +import org.junit.runner.notification.RunListener; + +/** + * A listener to get the name of a JUnit4 test. + */ +@Singleton +public class JUnit4TestNameListener extends RunListener { + private final ThreadLocal<Description> runningTest = new ThreadLocal<>(); + private final SettableCurrentRunningTest currentRunningTest; + + @Inject + public JUnit4TestNameListener(SettableCurrentRunningTest currentRunningTest) { + this.currentRunningTest = currentRunningTest; + } + + @Override + public void testRunStarted(Description description) throws Exception { + currentRunningTest.setGlobalTestNameProvider(new TestNameProvider() { + @Override + public Description get() { + return runningTest.get(); + } + }); + } + + @Override + public void testStarted(Description description) throws Exception { + runningTest.set(description); + } + + @Override + public void testFinished(Description description) throws Exception { + runningTest.set(null); + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestStackTraceListener.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestStackTraceListener.java new file mode 100644 index 0000000000..d1ef844b11 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestStackTraceListener.java @@ -0,0 +1,55 @@ +// Copyright 2015 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.internal.junit4; + +import com.google.testing.junit.runner.internal.SignalHandlers; +import com.google.testing.junit.runner.internal.StackTraces; +import com.google.testing.junit.runner.internal.Stderr; +import java.io.PrintStream; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.junit.runner.Description; +import org.junit.runner.notification.RunListener; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * A listener than dumps all stack traces when the test receives a SIGTERM. + */ +@Singleton +public class JUnit4TestStackTraceListener extends RunListener { + private final SignalHandlers signalHandlers; + private final PrintStream errPrintStream; + + @Inject + public JUnit4TestStackTraceListener( + SignalHandlers signalHandlers, @Stderr PrintStream errPrintStream) { + this.signalHandlers = signalHandlers; + this.errPrintStream = errPrintStream; + } + + @Override + public void testRunStarted(Description description) throws Exception { + signalHandlers.installHandler(new Signal("TERM"), new WriteStackTraceSignalHandler()); + } + + private class WriteStackTraceSignalHandler implements SignalHandler { + @Override + public void handle(Signal signal) { + errPrintStream.println("Dumping stack traces for all threads\n"); + StackTraces.printAll(errPrintStream); + } + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestXmlListener.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestXmlListener.java new file mode 100644 index 0000000000..6d607256c9 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/JUnit4TestXmlListener.java @@ -0,0 +1,120 @@ +// Copyright 2012 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.internal.junit4; + +import com.google.testing.junit.runner.internal.SignalHandlers; +import com.google.testing.junit.runner.internal.Stderr; +import com.google.testing.junit.runner.internal.Xml; +import com.google.testing.junit.runner.model.TestSuiteModel; +import com.google.testing.junit.runner.util.Supplier; +import java.io.OutputStream; +import java.io.PrintStream; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.junit.Ignore; +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +/** + * A listener that writes the test output as XML. + */ +@Singleton +public class JUnit4TestXmlListener extends RunListener { + private final Supplier<TestSuiteModel> modelSupplier; + private final CancellableRequestFactory requestFactory; + private final SignalHandlers signalHandlers; + private final OutputStream xmlStream; + private final PrintStream errPrintStream; + private volatile TestSuiteModel model; + + @Inject + public JUnit4TestXmlListener(Supplier<TestSuiteModel> modelSupplier, + CancellableRequestFactory requestFactory, SignalHandlers signalHandlers, + @Xml OutputStream xmlStream, @Stderr PrintStream errPrintStream) { + this.modelSupplier = modelSupplier; + this.requestFactory = requestFactory; + this.signalHandlers = signalHandlers; + this.xmlStream = xmlStream; + this.errPrintStream = errPrintStream; + } + + @Override + public void testRunStarted(Description description) throws Exception { + model = modelSupplier.get(); + signalHandlers.installHandler(new Signal("TERM"), new WriteXmlSignalHandler()); + } + + @Override + public void testStarted(Description description) throws Exception { + model.testStarted(description); + } + + @Override + public void testAssumptionFailure(Failure failure) { + model.testSkipped(failure.getDescription()); + } + + @Override + public void testFailure(Failure failure) throws Exception { + model.testFailure(failure.getDescription(), failure.getException()); + } + + @Override + public void testIgnored(Description description) throws Exception { + // TODO(bazel-team) There's a known issue in the JUnit4 ParentRunner that + // fires testIgnored on test suites that are being skipped due to an + // assumption failure. + if (isSuiteAssumptionFailure(description)) { + model.testSkipped(description); + } else { + model.testSuppressed(description); + } + } + + private boolean isSuiteAssumptionFailure(Description description) { + return description.isSuite() && description.getAnnotation(Ignore.class) == null; + } + + @Override + public void testFinished(Description description) throws Exception { + model.testFinished(description); + } + + @Override + public void testRunFinished(Result result) throws Exception { + model.writeAsXml(xmlStream); + } + + private class WriteXmlSignalHandler implements SignalHandler { + + @Override + public void handle(Signal signal) { + try { + errPrintStream.printf("%nReceived %s; writing test XML%n", signal.toString()); + requestFactory.cancelRun(); + model.testRunInterrupted(); + model.writeAsXml(xmlStream); + errPrintStream.println("Done writing test XML"); + } catch (Exception e) { + errPrintStream.println("Could not write test XML"); + e.printStackTrace(errPrintStream); + } + } + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/SettableCurrentRunningTest.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/SettableCurrentRunningTest.java new file mode 100644 index 0000000000..28894ba2e6 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal/junit4/SettableCurrentRunningTest.java @@ -0,0 +1,25 @@ +// Copyright 2015 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.internal.junit4; + +import com.google.testing.junit.runner.util.CurrentRunningTest; +import com.google.testing.junit.runner.util.TestNameProvider; + +/** + * A {@link CurrentRunningTest} variant that allows to set the testNameProvider via a method call. + */ +public abstract class SettableCurrentRunningTest extends CurrentRunningTest { + protected abstract void setGlobalTestNameProvider(TestNameProvider provider); +} |