From 4baedc7bf96bd06b36c4af241b7423d21b6ba424 Mon Sep 17 00:00:00 2001 From: Kush Chakraborty Date: Wed, 8 Mar 2017 15:30:59 +0000 Subject: Create ExperimentalTestRunner which is just the same as BazelTestRunner, but a testbed of upcoming changes, without breaking existing test targets. To use the alternate test runner a java test should add the tag "experimental_testrunner" and depend on "@bazel_tools//tools/jdk:ExperimentalTestRunner_deploy.jar" (instead of @bazel_tools//tools/jdk:TestRunner_deploy.jar) -- PiperOrigin-RevId: 149536298 MOS_MIGRATED_REVID=149536298 --- .../java/com/google/testing/junit/runner/BUILD | 40 ++++- .../testing/junit/runner/BazelTestRunner.java | 16 -- .../junit/runner/BazelTestRunnerModule.java | 36 ++++ .../junit/runner/ExperimentalTestRunner.java | 191 +++++++++++++++++++++ .../testing/junit/runner/ResultWriterFactory.java | 3 +- .../testing/junit/runner/StderrStreamFactory.java | 2 +- .../testing/junit/runner/StdoutStreamFactory.java | 2 +- 7 files changed, 269 insertions(+), 21 deletions(-) create mode 100644 src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunnerModule.java create mode 100644 src/java_tools/junitrunner/java/com/google/testing/junit/runner/ExperimentalTestRunner.java (limited to 'src/java_tools/junitrunner') diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BUILD b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BUILD index 1699cb882b..c5a8006794 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BUILD +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BUILD @@ -7,9 +7,41 @@ DEFAULT_VISIBILITY = [ package(default_visibility = ["//src:__subpackages__"]) +filegroup( + name = "common_runner_java_files", + srcs = glob( + ["*.java"], + exclude = + [ + "BazelTestRunner.java", + "ExperimentalTestRunner.java", + ], + ), +) + java_library( name = "test_runner", - srcs = glob(["*.java"]), + srcs = [ + "BazelTestRunner.java", + ":common_runner_java_files", + ], + data = ["//tools:test_sharding_compliant"], + deps = [ + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/model", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/sharding", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/util", + "//third_party:junit4", + ], +) + +java_library( + name = "experimental_test_runner", + srcs = [ + "ExperimentalTestRunner.java", + ":common_runner_java_files", + ], data = ["//tools:test_sharding_compliant"], deps = [ "//src/java_tools/junitrunner/java/com/google/testing/junit/runner/internal", @@ -27,6 +59,12 @@ java_binary( runtime_deps = [":test_runner"], ) +java_binary( + name = "ExperimentalRunner", + main_class = "com.google.testing.junit.runner.ExperimentalTestRunner", + runtime_deps = [":experimental_test_runner"], +) + filegroup( name = "srcs", testonly = 0, # All srcs should be not test only, overwrite package default. diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunner.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunner.java index 74a6826f88..66d7d91a67 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunner.java +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunner.java @@ -18,8 +18,6 @@ import com.google.testing.junit.runner.internal.StackTraces; import com.google.testing.junit.runner.junit4.JUnit4InstanceModules.Config; import com.google.testing.junit.runner.junit4.JUnit4InstanceModules.SuiteClass; import com.google.testing.junit.runner.junit4.JUnit4Runner; -import com.google.testing.junit.runner.model.AntXmlResultWriter; -import com.google.testing.junit.runner.model.XmlResultWriter; import java.io.PrintStream; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -200,18 +198,4 @@ public class BazelTestRunner { } } } - - static class BazelTestRunnerModule { - static XmlResultWriter resultWriter(AntXmlResultWriter impl) { - return impl; - } - - static PrintStream stdoutStream() { - return System.out; - } - - static PrintStream stderrStream() { - return System.err; - } - } } diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunnerModule.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunnerModule.java new file mode 100644 index 0000000000..a3c1c6f796 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/BazelTestRunnerModule.java @@ -0,0 +1,36 @@ +// Copyright 2017 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; + +import com.google.testing.junit.runner.model.AntXmlResultWriter; +import com.google.testing.junit.runner.model.XmlResultWriter; +import java.io.PrintStream; + +/** + * Originally a Dagger module extracted out of {@link BazelTestRunner}. + */ +class BazelTestRunnerModule { + static XmlResultWriter resultWriter(AntXmlResultWriter impl) { + return impl; + } + + static PrintStream stdoutStream() { + return System.out; + } + + static PrintStream stderrStream() { + return System.err; + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ExperimentalTestRunner.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ExperimentalTestRunner.java new file mode 100644 index 0000000000..f13915e8e9 --- /dev/null +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ExperimentalTestRunner.java @@ -0,0 +1,191 @@ +// Copyright 2017 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; + +import com.google.testing.junit.runner.internal.StackTraces; +import com.google.testing.junit.runner.junit4.JUnit4InstanceModules.Config; +import com.google.testing.junit.runner.junit4.JUnit4InstanceModules.SuiteClass; +import com.google.testing.junit.runner.junit4.JUnit4Runner; +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * For now the same as {@link BazelTestRunner} but intended to be a testbed to try out new features, + * without breeaking existing tests. + */ +public class ExperimentalTestRunner { + /** + * If no arguments are passed on the command line, use this System property to + * determine which test suite to run. + */ + static final String TEST_SUITE_PROPERTY_NAME = "bazel.test_suite"; + + private ExperimentalTestRunner() { + // utility class; should not be instantiated + } + + /** + * Takes as arguments the classes or packages to test. + * + *

To help just run one test or method in a suite, the test suite + * may be passed in via system properties (-Dbazel.test_suite). + * An empty args parameter means to run all tests in the suite. + * A non-empty args parameter means to run only the specified tests/methods. + * + *

Return codes: + *

+ */ + public static void main(String[] args) { + PrintStream stderr = System.err; + + String suiteClassName = System.getProperty(TEST_SUITE_PROPERTY_NAME); + System.out.println("WARNING: RUNNING EXPERIMENTAL TEST RUNNER"); + + if (args.length >= 1 && args[args.length - 1].equals("--persistent_test_runner")) { + System.err.println("Requested test strategy is currently unsupported."); + System.exit(1); + } + + if (!checkTestSuiteProperty(suiteClassName)) { + System.exit(2); + } + + int exitCode = runTestsInSuite(suiteClassName, args); + + System.err.printf("%nExperimentalTestRunner exiting with a return value of %d%n", exitCode); + System.err.println("JVM shutdown hooks (if any) will run now."); + System.err.println("The JVM will exit once they complete."); + System.err.println(); + + printStackTracesIfJvmExitHangs(stderr); + + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date shutdownTime = new Date(); + String formattedShutdownTime = format.format(shutdownTime); + System.err.printf("-- JVM shutdown starting at %s --%n%n", formattedShutdownTime); + System.exit(exitCode); + } + + /** + * Ensures that the bazel.test_suite in argument is not {@code null} or print error and + * explanation. + * + * @param testSuiteProperty system property to check + */ + private static boolean checkTestSuiteProperty(String testSuiteProperty) { + if (testSuiteProperty == null) { + System.err.printf( + "Error: The test suite Java system property %s is required but missing.%n", + TEST_SUITE_PROPERTY_NAME); + System.err.println(); + System.err.println("This property is set automatically when running with Bazel like such:"); + System.err.printf(" java -D%s=[test-suite-class] %s%n", + TEST_SUITE_PROPERTY_NAME, ExperimentalTestRunner.class.getName()); + System.err.printf(" java -D%s=[test-suite-class] -jar [deploy-jar]%n", + TEST_SUITE_PROPERTY_NAME); + System.err.println("E.g.:"); + System.err.printf(" java -D%s=org.example.testing.junit.runner.SmallTests %s%n", + TEST_SUITE_PROPERTY_NAME, ExperimentalTestRunner.class.getName()); + System.err.printf(" java -D%s=org.example.testing.junit.runner.SmallTests " + + "-jar SmallTests_deploy.jar%n", + TEST_SUITE_PROPERTY_NAME); + return false; + } + return true; + } + + private static int runTestsInSuite(String suiteClassName, String[] args) { + Class suite = getTestClass(suiteClassName); + + if (suite == null) { + // No class found corresponding to the system property passed in from Bazel + if (args.length == 0 && suiteClassName != null) { + System.err.printf("Class not found: [%s]%n", suiteClassName); + return 2; + } + } + + // TODO(kush): Use a new classloader for the following instantiation. + JUnit4Runner runner = + JUnit4Bazel.builder() + .suiteClass(new SuiteClass(suite)) + .config(new Config(args)) + .build() + .runner(); + return runner.run().wasSuccessful() ? 0 : 1; + } + + private static Class getTestClass(String name) { + if (name == null) { + return null; + } + + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * Prints out stack traces if the JVM does not exit quickly. This can help detect shutdown hooks + * that are preventing the JVM from exiting quickly. + * + * @param out Print stream to use + */ + private static void printStackTracesIfJvmExitHangs(final PrintStream out) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + sleepUninterruptibly(5); + out.println("JVM still up after five seconds. Dumping stack traces for all threads."); + StackTraces.printAll(out); + } + }, "ExperimentalTestRunner: Print stack traces if JVM exit hangs"); + + thread.setDaemon(true); + thread.start(); + } + + /** + * Invokes SECONDS.{@link TimeUnit#sleep(long) sleep(sleepForSeconds)} uninterruptibly. + */ + private static void sleepUninterruptibly(long sleepForSeconds) { + boolean interrupted = false; + try { + long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(sleepForSeconds); + while (true) { + try { + // TimeUnit.sleep() treats negative timeouts just like zero. + TimeUnit.NANOSECONDS.sleep(end - System.nanoTime()); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } +} diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ResultWriterFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ResultWriterFactory.java index ce8c70e790..34dcdf511c 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ResultWriterFactory.java +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/ResultWriterFactory.java @@ -32,8 +32,7 @@ public final class ResultWriterFactory implements Factory { @Override public XmlResultWriter get() { - XmlResultWriter xmlResultWriter = - BazelTestRunner.BazelTestRunnerModule.resultWriter(implSupplier.get()); + XmlResultWriter xmlResultWriter = BazelTestRunnerModule.resultWriter(implSupplier.get()); assert xmlResultWriter != null; return xmlResultWriter; } diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StderrStreamFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StderrStreamFactory.java index 22e45aeb84..be2303b793 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StderrStreamFactory.java +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StderrStreamFactory.java @@ -25,7 +25,7 @@ public enum StderrStreamFactory implements Factory { @Override public PrintStream get() { - PrintStream printStream = BazelTestRunner.BazelTestRunnerModule.stderrStream(); + PrintStream printStream = BazelTestRunnerModule.stderrStream(); assert printStream != null; return printStream; } diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StdoutStreamFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StdoutStreamFactory.java index 6e0d6463f3..57517d5309 100644 --- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StdoutStreamFactory.java +++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/StdoutStreamFactory.java @@ -25,7 +25,7 @@ public enum StdoutStreamFactory implements Factory { @Override public PrintStream get() { - PrintStream printStream = BazelTestRunner.BazelTestRunnerModule.stdoutStream(); + PrintStream printStream = BazelTestRunnerModule.stdoutStream(); assert printStream != null; return printStream; } -- cgit v1.2.3