diff options
author | 2017-03-08 15:30:59 +0000 | |
---|---|---|
committer | 2017-03-09 10:27:29 +0000 | |
commit | 4baedc7bf96bd06b36c4af241b7423d21b6ba424 (patch) | |
tree | c17272f6e4f49c539f708b0b79517b543622f76e | |
parent | cb8a5e25382cb67a80449ac1fb78ae9680650b92 (diff) |
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
12 files changed, 301 insertions, 24 deletions
@@ -148,6 +148,7 @@ genrule( "//src/java_tools/singlejar:SingleJar_deploy.jar", "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass:GenClass_deploy.jar", "//src/java_tools/junitrunner/java/com/google/testing/junit/runner:Runner_deploy.jar", + "//src/java_tools/junitrunner/java/com/google/testing/junit/runner:ExperimentalRunner_deploy.jar", "//src/java_tools/junitrunner/java/com/google/testing/coverage:embedded_tools", "//third_party/ijar", "//third_party/java/apkbuilder:embedded_tools", diff --git a/src/create_embedded_tools.sh b/src/create_embedded_tools.sh index 56ddf8dda2..d814f17e29 100755 --- a/src/create_embedded_tools.sh +++ b/src/create_embedded_tools.sh @@ -48,6 +48,7 @@ for i in $*; do *javac7.jar) OUTPUT_PATH=third_party/java/jdk/langtools/javac7.jar ;; *SingleJar_deploy.jar) OUTPUT_PATH=tools/jdk/SingleJar_deploy.jar ;; *GenClass_deploy.jar) OUTPUT_PATH=tools/jdk/GenClass_deploy.jar ;; + *ExperimentalRunner_deploy.jar) OUTPUT_PATH=tools/jdk/ExperimentalTestRunner_deploy.jar ;; *Runner_deploy.jar) OUTPUT_PATH=tools/jdk/TestRunner_deploy.jar ;; *ijar.exe) OUTPUT_PATH=tools/jdk/ijar/ijar.exe ;; *ijar) OUTPUT_PATH=tools/jdk/ijar/ijar ;; 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. + * + * <p>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. + * + * <p>Return codes: + * <ul> + * <li>Test runner failure, bad arguments, etc.: exit code of 2</li> + * <li>Normal test failure: exit code of 1</li> + * <li>All tests pass: exit code of 0</li> + * </ul> + */ + 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<XmlResultWriter> { @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<PrintStream> { @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<PrintStream> { @Override public PrintStream get() { - PrintStream printStream = BazelTestRunner.BazelTestRunnerModule.stdoutStream(); + PrintStream printStream = BazelTestRunnerModule.stdoutStream(); assert printStream != null; return printStream; } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java index 2946b297a1..87a02bb547 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java @@ -60,6 +60,8 @@ public class BazelJavaRuleClasses { PackageNameConstraint.ANY_SEGMENT, "java", "javatests"); protected static final String JUNIT_TESTRUNNER = "//tools/jdk:TestRunner_deploy.jar"; + protected static final String EXPERIMENTAL_TESTRUNNER = + "//tools/jdk:ExperimentalTestRunner_deploy.jar"; public static final ImplicitOutputsFunction JAVA_BINARY_IMPLICIT_OUTPUTS = fromFunctions( @@ -391,6 +393,17 @@ public class BazelJavaRuleClasses { : null; } })) + .add( + attr("$experimental_testsupport", LABEL) + .value( + new Attribute.ComputedDefault("use_testrunner") { + @Override + public Object getDefault(AttributeMap rule) { + return rule.get("use_testrunner", Type.BOOLEAN) + ? env.getToolsLabel(EXPERIMENTAL_TESTRUNNER) + : null; + } + })) /* <!-- #BLAZE_RULE($base_java_binary).ATTRIBUTE(deploy_manifest_lines) --> A list of lines to add to the <code>META-INF/manifest.mf</code> file generated for the <code>*_deploy.jar</code> target. The contents of this attribute are <em>not</em> subject diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java index f5fa1ff7ae..d4156ba4f3 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java @@ -80,6 +80,11 @@ public class BazelJavaSemantics implements JavaSemantics { private static final String JACOCO_COVERAGE_RUNNER_MAIN_CLASS = "com.google.testing.coverage.JacocoCoverageRunner"; + private static final String BAZEL_TEST_RUNNER_MAIN_CLASS = + "com.google.testing.junit.runner.BazelTestRunner"; + private static final String EXPERIMENTAL_TEST_RUNNER_MAIN_CLASS = + "com.google.testing.junit.runner.ExperimentalTestRunner"; + private static final String EXPERIMENTAL_TESTRUNNER_TAG = "experimental_testrunner"; private BazelJavaSemantics() { } @@ -120,13 +125,20 @@ public class BazelJavaSemantics implements JavaSemantics { if (mainClass.isEmpty()) { if (ruleContext.attributes().get("use_testrunner", Type.BOOLEAN) && !useLegacyJavaTest(ruleContext)) { - return "com.google.testing.junit.runner.BazelTestRunner"; + return useExperimentalTestRunner(ruleContext) + ? EXPERIMENTAL_TEST_RUNNER_MAIN_CLASS + : BAZEL_TEST_RUNNER_MAIN_CLASS; } mainClass = JavaCommon.determinePrimaryClass(ruleContext, sources); } return mainClass; } + private static boolean useExperimentalTestRunner(RuleContext ruleContext) { + List<String> tags = ruleContext.attributes().get("tags", Type.STRING_LIST); + return tags.contains(EXPERIMENTAL_TESTRUNNER_TAG); + } + private void checkMainClass(RuleContext ruleContext, ImmutableList<Artifact> sources) { boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN); String mainClass = getMainClassInternal(ruleContext, sources); @@ -294,7 +306,9 @@ public class BazelJavaSemantics implements JavaSemantics { boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN); if (createExecutable && ruleContext.attributes().get("use_testrunner", Type.BOOLEAN)) { - return Iterables.getOnlyElement(ruleContext.getPrerequisites("$testsupport", Mode.TARGET)); + String testSupport = + useExperimentalTestRunner(ruleContext) ? "$experimental_testsupport" : "$testsupport"; + return Iterables.getOnlyElement(ruleContext.getPrerequisites(testSupport, Mode.TARGET)); } else { return null; } diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java index 088fa53c13..76de5f80c4 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java @@ -111,7 +111,7 @@ public final class BazelAnalysisMock extends AnalysisMock { "filegroup(name='jacoco-blaze-agent', srcs = [])", "exports_files(['JavaBuilder_deploy.jar','SingleJar_deploy.jar','TestRunner_deploy.jar',", " 'JavaBuilderCanary_deploy.jar', 'ijar', 'GenClass_deploy.jar',", - " 'turbine_deploy.jar'])"); + " 'turbine_deploy.jar','ExperimentalTestRunner_deploy.jar'])"); ImmutableList<String> androidBuildContents = createAndroidBuildContents(); |