summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormikecase <mikecase@chromium.org>2015-04-01 09:35:35 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-01 16:36:09 +0000
commitc638a07bf65457b24956785146d3429045a47fe2 (patch)
tree5c2d687bd70771755ea53318cbbf5c561240d35b
parentbd43a54c6b5dbda1e79c197760a03d9e44aaa0c8 (diff)
downloadchromium_src-c638a07bf65457b24956785146d3429045a47fe2.zip
chromium_src-c638a07bf65457b24956785146d3429045a47fe2.tar.gz
chromium_src-c638a07bf65457b24956785146d3429045a47fe2.tar.bz2
Add json output to Junit tests.
This change will allow us to get the test results information to our testrunner without having to parse stdout. This will fix the issue of junit tests not showing up on the test report step on our bots. BUG=465755 Review URL: https://codereview.chromium.org/1003463002 Cr-Commit-Position: refs/heads/master@{#323255}
-rw-r--r--build/android/pylib/junit/test_dispatcher.py17
-rw-r--r--build/android/pylib/junit/test_runner.py39
-rw-r--r--build/android/pylib/results/json_results.py66
-rwxr-xr-xbuild/android/test_runner.py8
-rw-r--r--testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java54
-rw-r--r--testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java89
-rw-r--r--testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java14
-rw-r--r--testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java8
8 files changed, 271 insertions, 24 deletions
diff --git a/build/android/pylib/junit/test_dispatcher.py b/build/android/pylib/junit/test_dispatcher.py
index b821b75..6e0d865 100644
--- a/build/android/pylib/junit/test_dispatcher.py
+++ b/build/android/pylib/junit/test_dispatcher.py
@@ -2,6 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from pylib import constants
+from pylib.base import base_test_result
+
def RunTests(tests, runner_factory):
"""Runs a set of java tests on the host.
@@ -11,9 +14,15 @@ def RunTests(tests, runner_factory):
def run(t):
runner = runner_factory(None, None)
runner.SetUp()
- result = runner.RunTest(t)
+ results_list, return_code = runner.RunTest(t)
runner.TearDown()
- return result == 0
-
- return (None, 0 if all(run(t) for t in tests) else 1)
+ return (results_list, return_code == 0)
+ test_run_results = base_test_result.TestRunResults()
+ exit_code = 0
+ for t in tests:
+ results_list, passed = run(t)
+ test_run_results.AddResults(results_list)
+ if not passed:
+ exit_code = constants.ERROR_EXIT_CODE
+ return (test_run_results, exit_code) \ No newline at end of file
diff --git a/build/android/pylib/junit/test_runner.py b/build/android/pylib/junit/test_runner.py
index b85967b..bf97412 100644
--- a/build/android/pylib/junit/test_runner.py
+++ b/build/android/pylib/junit/test_runner.py
@@ -2,10 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import json
import os
+import tempfile
from pylib import cmd_helper
from pylib import constants
+from pylib.base import base_test_result
+from pylib.results import json_results
class JavaTestRunner(object):
"""Runs java tests on the host."""
@@ -22,22 +26,25 @@ class JavaTestRunner(object):
def RunTest(self, _test):
"""Runs junit tests from |self._test_suite|."""
-
- command = ['java',
- '-Drobolectric.dependency.dir=%s' %
- os.path.join(constants.GetOutDirectory(), 'lib.java'),
- '-jar', os.path.join(constants.GetOutDirectory(), 'lib.java',
- '%s.jar' % self._test_suite)]
-
- if self._test_filter:
- command.extend(['-gtest-filter', self._test_filter])
- if self._package_filter:
- command.extend(['-package-filter', self._package_filter])
- if self._runner_filter:
- command.extend(['-runner-filter', self._runner_filter])
- if self._sdk_version:
- command.extend(['-sdk-version', self._sdk_version])
- return cmd_helper.RunCmd(command)
+ with tempfile.NamedTemporaryFile() as json_file:
+ command = ['java',
+ '-Drobolectric.dependency.dir=%s' %
+ os.path.join(constants.GetOutDirectory(), 'lib.java'),
+ '-jar', os.path.join(constants.GetOutDirectory(), 'lib.java',
+ '%s.jar' % self._test_suite),
+ '-json-results-file', json_file.name]
+ if self._test_filter:
+ command.extend(['-gtest-filter', self._test_filter])
+ if self._package_filter:
+ command.extend(['-package-filter', self._package_filter])
+ if self._runner_filter:
+ command.extend(['-runner-filter', self._runner_filter])
+ if self._sdk_version:
+ command.extend(['-sdk-version', self._sdk_version])
+ return_code = cmd_helper.RunCmd(command)
+ results_list = json_results.ParseResultsFromJson(
+ json.loads(json_file.read()))
+ return (results_list, return_code)
def TearDown(self):
pass
diff --git a/build/android/pylib/results/json_results.py b/build/android/pylib/results/json_results.py
index c34244e..65664e3 100644
--- a/build/android/pylib/results/json_results.py
+++ b/build/android/pylib/results/json_results.py
@@ -16,6 +16,38 @@ def GenerateResultsDict(test_run_result):
A results dict that mirrors the one generated by
base/test/launcher/test_results_tracker.cc:SaveSummaryAsJSON.
"""
+ # Example json output.
+ # {
+ # "global_tags": [],
+ # "all_tests": [
+ # "test1",
+ # "test2",
+ # ],
+ # "disabled_tests": [],
+ # "per_iteration_data": [
+ # {
+ # "test1": [
+ # {
+ # "status": "SUCCESS",
+ # "elapsed_time_ms": 1,
+ # "output_snippet": "",
+ # "output_snippet_base64": "",
+ # "losless_snippet": "",
+ # },
+ # ],
+ # "test2": [
+ # {
+ # "status": "FAILURE",
+ # "elapsed_time_ms": 12,
+ # "output_snippet": "",
+ # "output_snippet_base64": "",
+ # "losless_snippet": "",
+ # },
+ # ],
+ # },
+ # ],
+ # }
+
assert isinstance(test_run_result, base_test_result.TestRunResults)
def status_as_string(s):
@@ -71,3 +103,37 @@ def GenerateJsonResultsFile(test_run_result, file_path):
with open(file_path, 'w') as json_result_file:
json_result_file.write(json.dumps(GenerateResultsDict(test_run_result)))
+
+def ParseResultsFromJson(json_results):
+ """Creates a list of BaseTestResult objects from JSON.
+
+ Args:
+ json_results: A JSON dict in the format created by
+ GenerateJsonResultsFile.
+ """
+
+ def string_as_status(s):
+ if s == 'SUCCESS':
+ return base_test_result.ResultType.PASS
+ elif s == 'SKIPPED':
+ return base_test_result.ResultType.SKIP
+ elif s == 'FAILURE':
+ return base_test_result.ResultType.FAIL
+ elif s == 'CRASH':
+ return base_test_result.ResultType.CRASH
+ elif s == 'TIMEOUT':
+ return base_test_result.ResultType.TIMEOUT
+ else:
+ return base_test_result.ResultType.UNKNOWN
+
+ results_list = []
+ testsuite_runs = json_results['per_iteration_data']
+ for testsuite_run in testsuite_runs:
+ for test, test_runs in testsuite_run.iteritems():
+ results_list.extend(
+ [base_test_result.BaseTestResult(test,
+ string_as_status(tr['status']),
+ duration=tr['elapsed_time_ms'])
+ for tr in test_runs])
+ return results_list
+
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 368ac06..87f4cec 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -768,7 +768,13 @@ def _RunUIAutomatorTests(args, devices):
def _RunJUnitTests(args):
"""Subcommand of RunTestsCommand which runs junit tests."""
runner_factory, tests = junit_setup.Setup(args)
- _, exit_code = junit_dispatcher.RunTests(tests, runner_factory)
+ results, exit_code = junit_dispatcher.RunTests(tests, runner_factory)
+
+ report_results.LogFull(
+ results=results,
+ test_type='JUnit',
+ test_package=args.test_suite)
+
return exit_code
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java b/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java
new file mode 100644
index 0000000..3d501b3
--- /dev/null
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java
@@ -0,0 +1,54 @@
+// 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.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+/** A json RunListener that creates a Json file with test run information.
+ */
+public class JsonListener extends RunListener {
+
+ private final JsonLogger mJsonLogger;
+ private long mTestStartTimeMillis;
+ private boolean mCurrentTestPassed;
+
+ public JsonListener(JsonLogger jsonLogger) {
+ mJsonLogger = jsonLogger;
+ }
+
+ /** Called after all tests run.
+ */
+ @Override
+ public void testRunFinished(Result r) throws Exception {
+ mJsonLogger.writeJsonToFile();
+ }
+
+ /** Called when a test is about to start.
+ */
+ @Override
+ public void testStarted(Description d) throws Exception {
+ mCurrentTestPassed = true;
+ mTestStartTimeMillis = System.currentTimeMillis();
+ }
+
+ /** Called when a test has just finished.
+ */
+ @Override
+ public void testFinished(Description d) throws Exception {
+ long testElapsedTimeMillis = System.currentTimeMillis() - mTestStartTimeMillis;
+ mJsonLogger.addTestResultInfo(d, mCurrentTestPassed, testElapsedTimeMillis);
+ }
+
+ /** Called when a test fails.
+ */
+ @Override
+ public void testFailure(Failure f) throws Exception {
+ mCurrentTestPassed = false;
+ }
+}
+
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java b/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java
new file mode 100644
index 0000000..c253ba3
--- /dev/null
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java
@@ -0,0 +1,89 @@
+// 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.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.runner.Description;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Creates json file with junit test information. Format of the json file mirrors the
+ * json generated by build/android/pylib/results/json_results.py.
+ */
+public class JsonLogger {
+
+ private final JSONObject mBaseJsonObject;
+ private final JSONObject mBaseTestInfoJsonObject;
+ private final File mOutputFile;
+
+ public JsonLogger(File outputFile) {
+ mBaseJsonObject = new JSONObject();
+ mBaseTestInfoJsonObject = new JSONObject();
+ mOutputFile = outputFile;
+
+ try {
+ mBaseJsonObject.put("global_tags", new JSONArray());
+ mBaseJsonObject.put("all_tests", new JSONArray());
+ mBaseJsonObject.put("disabled_tests", new JSONArray());
+ mBaseJsonObject.put("per_iteration_data",
+ new JSONArray().put(mBaseTestInfoJsonObject));
+ } catch (JSONException e) {
+ System.err.println("Unable to create json output.");
+ }
+ }
+
+ /**
+ * Add the results of a test run to the json output.
+ */
+ public void addTestResultInfo(Description test, boolean passed, long elapsedTimeMillis) {
+ JSONObject testInfoJsonObject = new JSONObject();
+
+ try {
+ testInfoJsonObject.put("status", (passed ? "SUCCESS" : "FAILURE"));
+ testInfoJsonObject.put("elapsed_time_ms", elapsedTimeMillis);
+ testInfoJsonObject.put("output_snippet", "");
+ testInfoJsonObject.put("output_snippet_base64", "");
+ testInfoJsonObject.put("losless_snippet", "");
+
+ if (mBaseTestInfoJsonObject.optJSONArray(testName(test)) == null) {
+ mBaseTestInfoJsonObject.put(testName(test), new JSONArray());
+ mBaseJsonObject.getJSONArray("all_tests").put(testName(test));
+ }
+ mBaseTestInfoJsonObject.getJSONArray(testName(test)).put(testInfoJsonObject);
+ } catch (JSONException e) {
+ System.err.println("Unable to log test to json output: " + testName(test));
+ }
+ }
+
+ /**
+ * Writes the json output to a file.
+ */
+ public void writeJsonToFile() {
+ try {
+ PrintStream stream = new PrintStream(new FileOutputStream(mOutputFile));
+ try {
+ stream.print(mBaseJsonObject);
+ } finally {
+ try {
+ stream.close();
+ } catch (RuntimeException e) {
+ System.err.println("Unable to close output file: " + mOutputFile.getPath());
+ }
+ }
+ } catch (FileNotFoundException e) {
+ System.err.println("File not found: " + mOutputFile.getPath());
+ }
+ }
+
+ private String testName(Description test) {
+ return test.getClassName() + "#" + test.getMethodName();
+ }
+} \ No newline at end of file
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
index 9de7874..2837976 100644
--- a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java
@@ -4,6 +4,7 @@
package org.chromium.testing.local;
+import java.io.File;
import java.util.HashSet;
import java.util.Set;
@@ -15,6 +16,7 @@ public class JunitTestArgParser {
private final Set<String> mPackageFilters;
private final Set<Class<?>> mRunnerFilters;
private final Set<String> mGtestFilters;
+ private File mJsonOutput;
public static JunitTestArgParser parse(String[] args) {
@@ -38,6 +40,9 @@ public class JunitTestArgParser {
} else if ("gtest-filter".equals(argName)) {
// Read the command line argument after the flag.
parsed.addGtestFilter(args[++i]);
+ } else if ("json-results-file".equals(argName)) {
+ // Read the command line argument after the flag.
+ parsed.setJsonOutputFile(args[++i]);
} else {
System.out.println("Ignoring flag: \"" + argName + "\"");
}
@@ -60,6 +65,7 @@ public class JunitTestArgParser {
mPackageFilters = new HashSet<String>();
mRunnerFilters = new HashSet<Class<?>>();
mGtestFilters = new HashSet<String>();
+ mJsonOutput = null;
}
public Set<String> getPackageFilters() {
@@ -74,6 +80,10 @@ public class JunitTestArgParser {
return mGtestFilters;
}
+ public File getJsonOutputFile() {
+ return mJsonOutput;
+ }
+
private void addPackageFilter(String packageFilter) {
mPackageFilters.add(packageFilter);
}
@@ -86,5 +96,9 @@ public class JunitTestArgParser {
mGtestFilters.add(gtestFilter);
}
+ private void setJsonOutputFile(String path) {
+ mJsonOutput = new File(path);
+ }
+
}
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
index d9c468d..3a305fa 100644
--- a/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
+++ b/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java
@@ -76,10 +76,12 @@ public final class JunitTestMain {
JunitTestArgParser parser = JunitTestArgParser.parse(args);
JUnitCore core = new JUnitCore();
- GtestLogger logger = new GtestLogger(System.out);
- core.addListener(new GtestListener(logger));
+ GtestLogger gtestLogger = new GtestLogger(System.out);
+ core.addListener(new GtestListener(gtestLogger));
+ JsonLogger jsonLogger = new JsonLogger(parser.getJsonOutputFile());
+ core.addListener(new JsonListener(jsonLogger));
Class[] classes = findClassesFromClasspath();
- Request testRequest = Request.classes(new GtestComputer(logger), classes);
+ Request testRequest = Request.classes(new GtestComputer(gtestLogger), classes);
for (String packageFilter : parser.getPackageFilters()) {
testRequest = testRequest.filterWith(new PackageFilter(packageFilter));
}