Commit 1ad64324 authored by mikecase's avatar mikecase Committed by Commit bot

Add new GN junit binary template and new GYP java binary template.

Adds a new junit_binary template for gyp which makes it easy to
make new Junit test suites by setting a lot of the options and
necessary dependencies for you.

Also, adds a new java_binary.gypi file which uses the
create_java_binary_script.py similiar to how the java_binary
GN template works when main_class is specified. This lets
GYP and GN work similiar when building a junit binary, so
our test runner can run them the same way.

Finally, because of the slightly different way were now running
the junit binary, the java classpath contains all of the jars
instead of just the test jar (before we were running
java -jar test_jar.jar which set the classpath to just be
test_jar.jar). Therefore, we can simplify things and just look
in the classpath for the paths to the Robolectric dependencies
when running Robolectric junit tests.

BUG=

Review URL: https://codereview.chromium.org/1057783002

Cr-Commit-Position: refs/heads/master@{#324093}
parent 9a4f22db
...@@ -27,11 +27,10 @@ class JavaTestRunner(object): ...@@ -27,11 +27,10 @@ class JavaTestRunner(object):
def RunTest(self, _test): def RunTest(self, _test):
"""Runs junit tests from |self._test_suite|.""" """Runs junit tests from |self._test_suite|."""
with tempfile.NamedTemporaryFile() as json_file: with tempfile.NamedTemporaryFile() as json_file:
command = ['java', java_script = os.path.join(
'-Drobolectric.dependency.dir=%s' % constants.GetOutDirectory(), 'bin', self._test_suite)
os.path.join(constants.GetOutDirectory(), 'lib.java'), command = [java_script,
'-jar', os.path.join(constants.GetOutDirectory(), 'lib.java', '-test-jars', self._test_suite + '.jar',
'%s.jar' % self._test_suite),
'-json-results-file', json_file.name] '-json-results-file', json_file.name]
if self._test_filter: if self._test_filter:
command.extend(['-gtest-filter', self._test_filter]) command.extend(['-gtest-filter', self._test_filter])
......
...@@ -776,6 +776,64 @@ template("java_binary") { ...@@ -776,6 +776,64 @@ template("java_binary") {
} }
} }
# Declare a Junit executable target
#
# This target creates an executable from java code for running as a junit test
# suite. The executable will be in the output folder's /bin/ directory.
#
# Variables
# deps: Specifies the dependencies of this target. Java targets in this list
# will be included in the executable (and the javac classpath).
#
# java_files: List of .java files included in this library.
# srcjar_deps: List of srcjar dependencies. The .java files in the srcjars
# will be added to java_files and be included in this library.
# srcjars: List of srcjars to be included in this library, together with the
# ones obtained from srcjar_deps.
#
# chromium_code: If true, extra analysis warning/errors will be enabled.
#
# Example
# junit_binary("foo") {
# java_files = [ "org/chromium/foo/FooTest.java" ]
# deps = [ ":bar_java" ]
# }
template("junit_binary") {
set_sources_assignment_filter([])
java_binary(target_name) {
bypass_platform_checks = true
main_class = "org.chromium.testing.local.JunitTestMain"
testonly = true
if (defined(invoker.DEPRECATED_java_in_dir)) {
DEPRECATED_java_in_dir = invoker.DEPRECATED_java_in_dir
}
if (defined(invoker.chromium_code)) {
chromium_code = invoker.chromium_code
}
deps = [
"//testing/android/junit:junit_test_support",
"//third_party/junit",
"//third_party/mockito:mockito_java",
"//third_party/robolectric:robolectric_java",
"//third_party/robolectric:android-all-4.3_r2-robolectric-0",
]
if (defined(invoker.deps)) {
deps += invoker.deps
}
if (defined(invoker.java_files)) {
java_files = invoker.java_files
}
if (defined(invoker.srcjar_deps)) {
srcjar_deps = invoker.srcjar_deps
}
if (defined(invoker.srcjars)) {
srcjars = invoker.srcjars
}
}
}
# Declare an java library target # Declare an java library target
# #
# Variables # Variables
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
# found in the LICENSE file. # found in the LICENSE file.
# This file is meant to be included into a target to provide a rule to build # This file is meant to be included into a target to provide a rule to build
# a JAR file for use on a host in a consistent manner. # a JAR file for use on a host in a consistent manner. If a main class is
# specified, this file will also generate an executable to run the jar in the
# output folder's /bin/ directory.
# #
# To use this, create a gyp target with the following form: # To use this, create a gyp target with the following form:
# { # {
...@@ -69,9 +71,9 @@ ...@@ -69,9 +71,9 @@
'java_sources!': ['<!@(find <@(excluded_src_paths) -name "*.java")'] 'java_sources!': ['<!@(find <@(excluded_src_paths) -name "*.java")']
}], }],
['"<(jar_excluded_classes)" != ""', { ['"<(jar_excluded_classes)" != ""', {
'extra_options': ['--excluded-classes=<(jar_excluded_classes)'] 'extra_options': ['--jar-excluded-classes=<(jar_excluded_classes)']
}], }],
['">(main_class)" != ""', { ['main_class != ""', {
'extra_options': ['--main-class=>(main_class)'] 'extra_options': ['--main-class=>(main_class)']
}] }]
], ],
...@@ -97,6 +99,33 @@ ...@@ -97,6 +99,33 @@
'^@(java_sources)', '^@(java_sources)',
], ],
}, },
],
'conditions': [
['main_class != ""', {
'actions': [
{
'action_name': 'create_java_binary_script_<(_target_name)',
'message': 'Creating java binary script <(_target_name)',
'variables': {
'output': '<(PRODUCT_DIR)/bin/<(_target_name)',
},
'inputs': [
'<(DEPTH)/build/android/gyp/create_java_binary_script.py',
'<(jar_path)',
],
'outputs': [
'<(output)',
],
'action': [
'python', '<(DEPTH)/build/android/gyp/create_java_binary_script.py',
'--classpath=>(input_jars_paths)',
'--jar-path=<(jar_path)',
'--output=<(output)',
'--main-class=>(main_class)',
]
}
]
}]
] ]
} }
...@@ -453,15 +453,13 @@ android_apk("chrome_shell_test_apk") { ...@@ -453,15 +453,13 @@ android_apk("chrome_shell_test_apk") {
} }
# GYP: //chrome/chrome_tests.gypi:chrome_junit_tests # GYP: //chrome/chrome_tests.gypi:chrome_junit_tests
java_binary("chrome_junit_tests") { junit_binary("chrome_junit_tests") {
testonly = true java_files =
java_files = [ "android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java" ] [ "junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java" ]
main_class = "org.chromium.testing.local.JunitTestMain"
deps = [ deps = [
":chrome_java", ":chrome_java",
"//base:base_java", "//base:base_java",
"//base:base_java_test_support", "//base:base_java_test_support",
"//testing/android/junit:junit_test_support",
] ]
} }
......
...@@ -180,15 +180,12 @@ android_library("content_javatests") { ...@@ -180,15 +180,12 @@ android_library("content_javatests") {
} }
# GYP: //content/content_tests.gypi:content_junit_tests # GYP: //content/content_tests.gypi:content_junit_tests
java_binary("content_junit_tests") { junit_binary("content_junit_tests") {
testonly = true
java_files = [ "junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java" ] java_files = [ "junit/src/org/chromium/content/browser/input/GamepadMappingsTest.java" ]
main_class = "org.chromium.testing.local.JunitTestMain"
deps = [ deps = [
":content_java", ":content_java",
"//base:base_java", "//base:base_java",
"//base:base_java_test_support", "//base:base_java_test_support",
"//testing/android/junit:junit_test_support",
] ]
} }
# TODO(GYP): content_icudata # TODO(GYP): content_icudata
...@@ -14,16 +14,11 @@ java_library("junit_test_support") { ...@@ -14,16 +14,11 @@ java_library("junit_test_support") {
"//third_party/junit", "//third_party/junit",
"//third_party/mockito:mockito_java", "//third_party/mockito:mockito_java",
"//third_party/robolectric:robolectric_java", "//third_party/robolectric:robolectric_java",
"//third_party/robolectric:android-all-4.3_r2-robolectric-0",
] ]
} }
# GYP: //testing/android/junit_test.gyp:junit_unit_tests # GYP: //testing/android/junit_test.gyp:junit_unit_tests
java_binary("junit_unittests") { junit_binary("junit_unittests") {
testonly = true
deps = [
":junit_test_support",
"//third_party/junit",
]
main_class = "org.chromium.testing.local.JunitTestMain"
DEPRECATED_java_in_dir = "javatests/src" DEPRECATED_java_in_dir = "javatests/src"
} }
...@@ -7,16 +7,20 @@ package org.chromium.testing.local; ...@@ -7,16 +7,20 @@ package org.chromium.testing.local;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
/** /**
* Parses command line arguments for JunitTestMain. * Parses command line arguments for JunitTestMain.
*/ */
public class JunitTestArgParser { public class JunitTestArgParser {
private static final Pattern COLON = Pattern.compile(":");
private final Set<String> mPackageFilters; private final Set<String> mPackageFilters;
private final Set<Class<?>> mRunnerFilters; private final Set<Class<?>> mRunnerFilters;
private final Set<String> mGtestFilters; private final Set<String> mGtestFilters;
private File mJsonOutput; private File mJsonOutput;
private String[] mTestJars;
public static JunitTestArgParser parse(String[] args) { public static JunitTestArgParser parse(String[] args) {
...@@ -43,6 +47,9 @@ public class JunitTestArgParser { ...@@ -43,6 +47,9 @@ public class JunitTestArgParser {
} else if ("json-results-file".equals(argName)) { } else if ("json-results-file".equals(argName)) {
// Read the command line argument after the flag. // Read the command line argument after the flag.
parsed.setJsonOutputFile(args[++i]); parsed.setJsonOutputFile(args[++i]);
} else if ("test-jars".equals(argName)) {
// Read the command line argument after the flag.
parsed.setTestJars(args[++i]);
} else { } else {
System.out.println("Ignoring flag: \"" + argName + "\""); System.out.println("Ignoring flag: \"" + argName + "\"");
} }
...@@ -84,6 +91,10 @@ public class JunitTestArgParser { ...@@ -84,6 +91,10 @@ public class JunitTestArgParser {
return mJsonOutput; return mJsonOutput;
} }
public String[] getTestJars() {
return mTestJars;
}
private void addPackageFilter(String packageFilter) { private void addPackageFilter(String packageFilter) {
mPackageFilters.add(packageFilter); mPackageFilters.add(packageFilter);
} }
...@@ -100,5 +111,7 @@ public class JunitTestArgParser { ...@@ -100,5 +111,7 @@ public class JunitTestArgParser {
mJsonOutput = new File(path); mJsonOutput = new File(path);
} }
} private void setTestJars(String jars) {
mTestJars = COLON.split(jars);
}
}
\ No newline at end of file
...@@ -9,8 +9,9 @@ import org.junit.runner.Request; ...@@ -9,8 +9,9 @@ import org.junit.runner.Request;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.LinkedList; import java.util.List;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -32,10 +33,19 @@ public final class JunitTestMain { ...@@ -32,10 +33,19 @@ public final class JunitTestMain {
/** /**
* Finds all classes on the class path annotated with RunWith. * Finds all classes on the class path annotated with RunWith.
*/ */
public static Class[] findClassesFromClasspath() { public static Class[] findClassesFromClasspath(String[] testJars) {
String[] jarPaths = COLON.split(System.getProperty("java.class.path")); String[] jarPaths = COLON.split(System.getProperty("java.class.path"));
LinkedList<Class> classes = new LinkedList<Class>(); List<String> testJarPaths = new ArrayList<String>(testJars.length);
for (String jp : jarPaths) { for (String testJar: testJars) {
for (String jarPath: jarPaths) {
if (jarPath.endsWith(testJar)) {
testJarPaths.add(jarPath);
break;
}
}
}
List<Class> classes = new ArrayList<Class>();
for (String jp : testJarPaths) {
try { try {
JarFile jf = new JarFile(jp); JarFile jf = new JarFile(jp);
for (Enumeration<JarEntry> eje = jf.entries(); eje.hasMoreElements();) { for (Enumeration<JarEntry> eje = jf.entries(); eje.hasMoreElements();) {
...@@ -48,7 +58,7 @@ public final class JunitTestMain { ...@@ -48,7 +58,7 @@ public final class JunitTestMain {
cn = FORWARD_SLASH.matcher(cn).replaceAll("."); cn = FORWARD_SLASH.matcher(cn).replaceAll(".");
Class<?> c = classOrNull(cn); Class<?> c = classOrNull(cn);
if (c != null && c.isAnnotationPresent(RunWith.class)) { if (c != null && c.isAnnotationPresent(RunWith.class)) {
classes.push(c); classes.add(c);
} }
} }
jf.close(); jf.close();
...@@ -80,8 +90,9 @@ public final class JunitTestMain { ...@@ -80,8 +90,9 @@ public final class JunitTestMain {
core.addListener(new GtestListener(gtestLogger)); core.addListener(new GtestListener(gtestLogger));
JsonLogger jsonLogger = new JsonLogger(parser.getJsonOutputFile()); JsonLogger jsonLogger = new JsonLogger(parser.getJsonOutputFile());
core.addListener(new JsonListener(jsonLogger)); core.addListener(new JsonListener(jsonLogger));
Class[] classes = findClassesFromClasspath(); Class[] classes = findClassesFromClasspath(parser.getTestJars());
Request testRequest = Request.classes(new GtestComputer(gtestLogger), classes); Request testRequest = Request.classes(new GtestComputer(gtestLogger), classes);
for (String packageFilter : parser.getPackageFilters()) { for (String packageFilter : parser.getPackageFilters()) {
testRequest = testRequest.filterWith(new PackageFilter(packageFilter)); testRequest = testRequest.filterWith(new PackageFilter(packageFilter));
} }
......
...@@ -8,13 +8,10 @@ import org.junit.runners.model.InitializationError; ...@@ -8,13 +8,10 @@ import org.junit.runners.model.InitializationError;
import org.robolectric.AndroidManifest; import org.robolectric.AndroidManifest;
import org.robolectric.DependencyResolver; import org.robolectric.DependencyResolver;
import org.robolectric.LocalDependencyResolver;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.SdkConfig; import org.robolectric.SdkConfig;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.io.File;
/** /**
* A custom Robolectric Junit4 Test Runner. This test runner will load the * A custom Robolectric Junit4 Test Runner. This test runner will load the
* "real" android jars from a local directory rather than use Maven to fetch * "real" android jars from a local directory rather than use Maven to fetch
...@@ -32,13 +29,7 @@ public class LocalRobolectricTestRunner extends RobolectricTestRunner { ...@@ -32,13 +29,7 @@ public class LocalRobolectricTestRunner extends RobolectricTestRunner {
@Override @Override
protected final DependencyResolver getJarResolver() { protected final DependencyResolver getJarResolver() {
String dependencyDir = System.getProperty("robolectric.dependency.dir"); return new RobolectricClasspathDependencyResolver();
if (dependencyDir == null) {
throw new IllegalStateException("robolectric.dependency.dir not specified. Make sure"
+ " you are setting the robolectric.dependency.dir system property to the"
+ " directory containing Robolectric's runtime dependency jars.");
}
return new LocalDependencyResolver(new File(dependencyDir));
} }
@Override @Override
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.testing.local;
import org.robolectric.DependencyJar;
import org.robolectric.DependencyResolver;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Pattern;
/**
* A Robolectric dependency resolver that looks for the Robolectric dependencies
* in the Java classpath.
*/
public class RobolectricClasspathDependencyResolver implements DependencyResolver {
private static final Pattern COLON = Pattern.compile(":");
private final String[] mClassPathJars;
/**
* Creates a {@link ClasspathDependencyResolver}.
*/
public RobolectricClasspathDependencyResolver() {
mClassPathJars = COLON.split(System.getProperty("java.class.path"));
}
/**
* Returns the {@link URL} for a Robolectric dependency. It looks through the jars
* in the classpath to find the dependency's filepath.
*/
@Override
public URL getLocalArtifactUrl(DependencyJar dependency) {
// Jar filenames are constructed identically to how they are built in Robolectric's
// own LocalDependencyResolver.
String dependencyJar = dependency.getArtifactId() + "-" + dependency.getVersion() + "."
+ dependency.getType();
for (String jarPath : mClassPathJars) {
if (jarPath.endsWith(dependencyJar)) {
return fileToUrl(new File(jarPath));
}
}
throw new IllegalStateException(
String.format("Robolectric jar %s was not found in classpath.", dependencyJar));
}
/**
* Returns the {@link URL} for a list of Robolectric dependencies.
*/
@Override
public URL[] getLocalArtifactUrls(DependencyJar... dependencies) {
URL[] urls = new URL[dependencies.length];
for (int i = 0; i < dependencies.length; i++) {
urls[i] = getLocalArtifactUrl(dependencies[i]);
}
return urls;
}
private static URL fileToUrl(File file) {
try {
return file.toURI().toURL();
} catch (MalformedURLException e) {
throw new IllegalArgumentException(
String.format("File \"%s\" cannot be represented as a URL: %s", file, e));
}
}
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment