Commit 43aaf3b8 authored by erikchen's avatar erikchen Committed by Commit Bot

Propagate Android test results through exceptions.

Previously, Android test results were propagated after all tests finished
running. In the event of an Exception, none of the test results would be written
to file. This CL slightly changes the control flow of TestRun.RunTests().
Instead of returning a results array, it now receives a results array as input
and modifies the array as it receives test results. This allows the json_writer
context TearDown() to serialize the partial test results.

This CL updates local_device_test_run.py to have the correct flow. The flow of
local_device_perf_test_run.py and local_machine_junit_test_run.py are left
untouched -- they can be modified in the future if it's deemed necessary. This
minimizes the size of this CL.

Bug: 895027
Change-Id: I60fe78da993de366d678252b84a91aaff044c10b
Reviewed-on: https://chromium-review.googlesource.com/c/1305396
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarJohn Budorick <jbudorick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603599}
parent f2d4ff7a
...@@ -24,11 +24,12 @@ class TestRun(object): ...@@ -24,11 +24,12 @@ class TestRun(object):
def SetUp(self): def SetUp(self):
raise NotImplementedError raise NotImplementedError
def RunTests(self): def RunTests(self, results):
"""Runs Tests and returns test results. """Runs Tests and populates |results|.
Returns: Args:
Should return list of |base_test_result.TestRunResults| objects. results: An array that should be populated with
|base_test_result.TestRunResults| objects.
""" """
raise NotImplementedError raise NotImplementedError
......
...@@ -412,7 +412,7 @@ class LocalDevicePerfTestRun(local_device_test_run.LocalDeviceTestRun): ...@@ -412,7 +412,7 @@ class LocalDevicePerfTestRun(local_device_test_run.LocalDeviceTestRun):
return sorted(devices) return sorted(devices)
#override #override
def RunTests(self): def RunTests(self, results):
def run_no_devices_tests(): def run_no_devices_tests():
if not self._no_device_tests: if not self._no_device_tests:
return [] return []
...@@ -451,7 +451,12 @@ class LocalDevicePerfTestRun(local_device_test_run.LocalDeviceTestRun): ...@@ -451,7 +451,12 @@ class LocalDevicePerfTestRun(local_device_test_run.LocalDeviceTestRun):
host_test_results, device_test_results = reraiser_thread.RunAsync( host_test_results, device_test_results = reraiser_thread.RunAsync(
[run_no_devices_tests, run_devices_tests]) [run_no_devices_tests, run_devices_tests])
return host_test_results + device_test_results # Ideally, results would be populated as early as possible, so that in the
# event of an exception or timeout, the caller will still have partially
# populated results. This looks like it can be done prior to dispatching
# tests, but will hold off on making this change unless it looks like it
# might provide utility.
results.extend(host_test_results + device_test_results)
# override # override
def TestPackage(self): def TestPackage(self):
...@@ -480,12 +485,16 @@ class OutputJsonList(LocalDevicePerfTestRun): ...@@ -480,12 +485,16 @@ class OutputJsonList(LocalDevicePerfTestRun):
pass pass
# override # override
def RunTests(self): def RunTests(self, results):
result_type = self._test_instance.OutputJsonList() result_type = self._test_instance.OutputJsonList()
result = base_test_result.TestRunResults() result = base_test_result.TestRunResults()
result.AddResult( result.AddResult(
base_test_result.BaseTestResult('OutputJsonList', result_type)) base_test_result.BaseTestResult('OutputJsonList', result_type))
return [result]
# Ideally, results would be populated as early as possible, so that in the
# event of an exception or timeout, the caller will still have partially
# populated results.
results.append(result)
# override # override
def _CreateShards(self, _tests): def _CreateShards(self, _tests):
...@@ -502,12 +511,16 @@ class PrintStep(LocalDevicePerfTestRun): ...@@ -502,12 +511,16 @@ class PrintStep(LocalDevicePerfTestRun):
pass pass
# override # override
def RunTests(self): def RunTests(self, results):
result_type = self._test_instance.PrintTestOutput() result_type = self._test_instance.PrintTestOutput()
result = base_test_result.TestRunResults() result = base_test_result.TestRunResults()
result.AddResult( result.AddResult(
base_test_result.BaseTestResult('PrintStep', result_type)) base_test_result.BaseTestResult('PrintStep', result_type))
return [result]
# Ideally, results would be populated as early as possible, so that in the
# event of an exception or timeout, the caller will still have partially
# populated results.
results.append(result)
# override # override
def _CreateShards(self, _tests): def _CreateShards(self, _tests):
......
...@@ -54,7 +54,7 @@ class LocalDeviceTestRun(test_run.TestRun): ...@@ -54,7 +54,7 @@ class LocalDeviceTestRun(test_run.TestRun):
self._tools = {} self._tools = {}
#override #override
def RunTests(self): def RunTests(self, results):
tests = self._GetTests() tests = self._GetTests()
exit_now = threading.Event() exit_now = threading.Event()
...@@ -115,7 +115,6 @@ class LocalDeviceTestRun(test_run.TestRun): ...@@ -115,7 +115,6 @@ class LocalDeviceTestRun(test_run.TestRun):
try: try:
with signal_handler.SignalHandler(signal.SIGTERM, stop_tests): with signal_handler.SignalHandler(signal.SIGTERM, stop_tests):
tries = 0 tries = 0
results = []
while tries < self._env.max_tries and tests: while tries < self._env.max_tries and tests:
logging.info('STARTING TRY #%d/%d', tries + 1, self._env.max_tries) logging.info('STARTING TRY #%d/%d', tries + 1, self._env.max_tries)
if tries > 0 and self._env.recover_devices: if tries > 0 and self._env.recover_devices:
...@@ -144,6 +143,11 @@ class LocalDeviceTestRun(test_run.TestRun): ...@@ -144,6 +143,11 @@ class LocalDeviceTestRun(test_run.TestRun):
t, base_test_result.ResultType.NOTRUN) t, base_test_result.ResultType.NOTRUN)
for t in test_names if not t.endswith('*')) for t in test_names if not t.endswith('*'))
# As soon as we know the names of the tests, we populate |results|.
# The tests in try_results will have their results updated by
# try_results.AddResult() as they are run.
results.append(try_results)
try: try:
if self._ShouldShard(): if self._ShouldShard():
tc = test_collection.TestCollection(self._CreateShards(tests)) tc = test_collection.TestCollection(self._CreateShards(tests))
...@@ -160,8 +164,6 @@ class LocalDeviceTestRun(test_run.TestRun): ...@@ -160,8 +164,6 @@ class LocalDeviceTestRun(test_run.TestRun):
base_test_result.ResultType.TIMEOUT, base_test_result.ResultType.TIMEOUT,
log=_SIGTERM_TEST_LOG)) log=_SIGTERM_TEST_LOG))
raise raise
finally:
results.append(try_results)
tries += 1 tries += 1
tests = self._GetTestsToRetry(tests, try_results) tests = self._GetTestsToRetry(tests, try_results)
...@@ -174,8 +176,6 @@ class LocalDeviceTestRun(test_run.TestRun): ...@@ -174,8 +176,6 @@ class LocalDeviceTestRun(test_run.TestRun):
except TestsTerminated: except TestsTerminated:
pass pass
return results
def _GetTestsToRetry(self, tests, try_results): def _GetTestsToRetry(self, tests, try_results):
def is_failure_result(test_result): def is_failure_result(test_result):
......
...@@ -29,7 +29,7 @@ class LocalMachineJunitTestRun(test_run.TestRun): ...@@ -29,7 +29,7 @@ class LocalMachineJunitTestRun(test_run.TestRun):
pass pass
#override #override
def RunTests(self): def RunTests(self, results):
with tempfile_ext.NamedTemporaryDirectory() as temp_dir: with tempfile_ext.NamedTemporaryDirectory() as temp_dir:
json_file_path = os.path.join(temp_dir, 'results.json') json_file_path = os.path.join(temp_dir, 'results.json')
...@@ -119,8 +119,7 @@ class LocalMachineJunitTestRun(test_run.TestRun): ...@@ -119,8 +119,7 @@ class LocalMachineJunitTestRun(test_run.TestRun):
test_run_results = base_test_result.TestRunResults() test_run_results = base_test_result.TestRunResults()
test_run_results.AddResults(results_list) test_run_results.AddResults(results_list)
results.append(test_run_results)
return [test_run_results]
#override #override
def TearDown(self): def TearDown(self):
......
...@@ -844,12 +844,18 @@ def RunTestsInPlatformMode(args): ...@@ -844,12 +844,18 @@ def RunTestsInPlatformMode(args):
lambda: collections.defaultdict(int)) lambda: collections.defaultdict(int))
iteration_count = 0 iteration_count = 0
for _ in repetitions: for _ in repetitions:
raw_results = test_run.RunTests() # raw_results will be populated with base_test_result.TestRunResults by
# test_run.RunTests(). It is immediately added to all_raw_results so
# that in the event of an exception, all_raw_results will already have
# the up-to-date results and those can be written to disk.
raw_results = []
all_raw_results.append(raw_results)
test_run.RunTests(raw_results)
if not raw_results: if not raw_results:
all_raw_results.pop()
continue continue
all_raw_results.append(raw_results)
iteration_results = base_test_result.TestRunResults() iteration_results = base_test_result.TestRunResults()
for r in reversed(raw_results): for r in reversed(raw_results):
iteration_results.AddTestRunResults(r) iteration_results.AddTestRunResults(r)
......
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