• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.testing.local;
6 
7 import org.json.JSONException;
8 import org.json.JSONObject;
9 import org.junit.runner.Computer;
10 import org.junit.runner.JUnitCore;
11 import org.junit.runner.Request;
12 import org.junit.runner.Result;
13 import org.junit.runner.RunWith;
14 
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.nio.file.Files;
19 import java.nio.file.Paths;
20 import java.util.ArrayList;
21 import java.util.Enumeration;
22 import java.util.List;
23 import java.util.jar.JarEntry;
24 import java.util.jar.JarFile;
25 import java.util.regex.Pattern;
26 
27 /**
28  *  Runs tests based on JUnit from the classpath on the host JVM based on the
29  *  provided filter configurations.
30  */
31 public final class JunitTestMain {
32     private static final int CLASS_SUFFIX_LEN = ".class".length();
33     private static final Pattern COLON = Pattern.compile(":");
34     private static final Pattern FORWARD_SLASH = Pattern.compile("/");
35 
JunitTestMain()36     private JunitTestMain() {}
37 
38     /** Finds all test classes on the class path annotated with RunWith. */
findClassesFromClasspath()39     public static Class[] findClassesFromClasspath() {
40         String[] jarPaths = COLON.split(System.getProperty("java.class.path"));
41         List<Class> classes = new ArrayList<Class>();
42         for (String jp : jarPaths) {
43             // Do not look at android.jar.
44             if (jp.contains("third_party/android_sdk")) {
45                 continue;
46             }
47             try {
48                 JarFile jf = new JarFile(jp);
49                 for (Enumeration<JarEntry> eje = jf.entries(); eje.hasMoreElements(); ) {
50                     JarEntry je = eje.nextElement();
51                     String cn = je.getName();
52                     // Skip classes in common libraries.
53                     if (cn.startsWith("androidx.") || cn.startsWith("junit")) {
54                         continue;
55                     }
56                     // Skip nested classes and classes that do not end with "Test".
57                     // That tests end with "Test" is enforced by TestClassNameCheck ErrorProne
58                     // check.
59                     if (cn.contains("$") || !cn.endsWith("Test.class")) {
60                         continue;
61                     }
62                     cn = cn.substring(0, cn.length() - CLASS_SUFFIX_LEN);
63                     cn = FORWARD_SLASH.matcher(cn).replaceAll(".");
64                     Class<?> c = classOrNull(cn);
65                     if (c != null && c.isAnnotationPresent(RunWith.class)) {
66                         classes.add(c);
67                     }
68                 }
69                 jf.close();
70             } catch (IOException e) {
71                 System.err.println("Error while reading classes from " + jp);
72             }
73         }
74         return classes.toArray(new Class[0]);
75     }
76 
classOrNull(String className)77     private static Class<?> classOrNull(String className) {
78         try {
79             // Do not initialize classes (clinit) yet, Android methods are all
80             // stubs until robolectric loads the real implementations.
81             return Class.forName(
82                     className, /* initialize= */ false, JunitTestMain.class.getClassLoader());
83         } catch (ClassNotFoundException e) {
84             System.err.println("Class not found: " + className);
85         } catch (NoClassDefFoundError e) {
86             System.err.println("Class definition not found: " + className);
87         } catch (Exception e) {
88             System.err.println("Other exception while reading class: " + className);
89         }
90         return null;
91     }
92 
listTestMain(JunitTestArgParser parser)93     private static Result listTestMain(JunitTestArgParser parser)
94             throws FileNotFoundException, JSONException {
95         JUnitCore core = new JUnitCore();
96         TestListComputer computer = new TestListComputer();
97         Class[] classes = findClassesFromClasspath();
98         Request testRequest = Request.classes(computer, classes);
99         for (String packageFilter : parser.mPackageFilters) {
100             testRequest = testRequest.filterWith(new PackageFilter(packageFilter));
101         }
102         for (Class<?> runnerFilter : parser.mRunnerFilters) {
103             testRequest = testRequest.filterWith(new RunnerFilter(runnerFilter));
104         }
105         for (String gtestFilter : parser.mGtestFilters) {
106             testRequest = testRequest.filterWith(new GtestFilter(gtestFilter));
107         }
108         Result ret = core.run(testRequest);
109         computer.writeJson(new File(parser.mJsonConfig));
110         return ret;
111     }
112 
runTestsMain(JunitTestArgParser parser)113     private static Result runTestsMain(JunitTestArgParser parser) throws Exception {
114         String data = new String(Files.readAllBytes(Paths.get(parser.mJsonConfig)));
115         JSONObject jsonConfig = new JSONObject(data);
116         ChromiumAndroidConfigurer.setJsonConfig(jsonConfig);
117         Class[] classes = ConfigFilter.classesFromConfig(jsonConfig);
118 
119         JUnitCore core = new JUnitCore();
120         GtestLogger gtestLogger = new GtestLogger(System.out);
121         core.addListener(new GtestListener(gtestLogger));
122         JsonLogger jsonLogger = new JsonLogger(new File(parser.mJsonOutput));
123         core.addListener(new JsonListener(jsonLogger));
124         Computer computer = new GtestComputer(gtestLogger);
125 
126         Request testRequest =
127                 Request.classes(computer, classes).filterWith(new ConfigFilter(jsonConfig));
128         return core.run(testRequest);
129     }
130 
main(String[] args)131     public static void main(String[] args) throws Exception {
132         // Causes test names to have the sdk version as a [suffix].
133         // This enables sharding by SDK version.
134         System.setProperty("robolectric.alwaysIncludeVariantMarkersInTestName", "true");
135 
136         JunitTestArgParser parser = JunitTestArgParser.parse(args);
137         Result r = parser.mListTests ? listTestMain(parser) : runTestsMain(parser);
138         System.exit(r.wasSuccessful() ? 0 : 1);
139     }
140 }
141