Commit 79e61212 authored by hzl's avatar hzl Committed by Commit bot

(Reland) Insert logcat as part of test result for android instrumentation tests.

In this cl, we are trying to add the logcat of each instrumentation
test into the corresponding test result object, so that when we are
checking the result of each test case, we would be able to see the
corresponding logcat for that test case at the same time.

The reason of reland is to fix the issue where environmental variables are not set.

BUG=631213

Review-Url: https://codereview.chromium.org/2545653002
Cr-Commit-Position: refs/heads/master@{#435550}
parent f3cfe772
# Copyright (c) 2016 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.
# Copyright 2016 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.
import os
import logging
import sys
from devil.android import logcat_monitor
from devil.utils import reraiser_thread
from pylib import constants
sys.path.insert(0, os.path.abspath(os.path.join(
constants.DIR_SOURCE_ROOT, 'tools', 'swarming_client')))
from libs.logdog import bootstrap # pylint: disable=import-error
class LogdogLogcatMonitor(logcat_monitor.LogcatMonitor):
"""Logcat monitor that writes logcat to a logdog stream.
The logdog stream client will return a url, where contains the logcat.
"""
def __init__(self, adb, stream_name, clear=True, filter_specs=None):
super(LogdogLogcatMonitor, self).__init__(adb, clear, filter_specs)
self._logcat_url = ''
self._logdog_stream = None
self._stream_client = None
self._stream_name = stream_name
try:
self._stream_client = bootstrap.ButlerBootstrap.probe().stream_client()
self._logdog_stream = self._stream_client.open_text(self._stream_name)
except bootstrap.NotBootstrappedError as e:
logging.exception(
'Error not bootstrapped. Failed to start logdog: %s', e)
except (KeyError, ValueError) as e:
logging.exception('Error when creating stream_client/stream: %s.', e)
def GetLogcatURL(self):
"""Return logcat url.
The default logcat url is '', if failed to create stream_client.
"""
return self._logcat_url
def Stop(self):
"""Stops the logcat monitor.
Close the logdog stream as well.
"""
super(LogdogLogcatMonitor, self)._StopRecording()
if self._logdog_stream:
try:
self._logcat_url = self._stream_client.get_viewer_url(self._stream_name)
except (KeyError, ValueError) as e:
logging.exception('Error cannot get viewer url: %s', e)
self._logdog_stream.close()
def Start(self):
"""Starts the logdog logcat monitor.
Clears the logcat if |clear| was set in |__init__|.
"""
if self._clear:
self._adb.Logcat(clear=True)
self._StartRecording()
def _StartRecording(self):
"""Starts recording logcat to file.
Write logcat to stream at the same time.
"""
def record_to_stream():
if self._logdog_stream:
for data in self._adb.Logcat(filter_specs=self._filter_specs,
logcat_format='threadtime'):
if self._stop_recording_event.isSet():
return
self._logdog_stream.write(data + '\n')
self._stop_recording_event.clear()
if not self._record_thread:
self._record_thread = reraiser_thread.ReraiserThread(record_to_stream)
self._record_thread.start()
def Close(self):
"""Override parent's close method."""
pass
def __del__(self):
"""Override parent's delete method."""
pass
......@@ -42,6 +42,7 @@ class BaseTestResult(object):
self._duration = duration
self._log = log
self._tombstones = None
self._logcat_url = None
def __str__(self):
return self._name
......@@ -95,6 +96,12 @@ class BaseTestResult(object):
def GetTombstones(self):
return self._tombstones
def SetLogcatUrl(self, logcat_url):
self._logcat_url = logcat_url
def GetLogcatUrl(self):
return self._logcat_url
class TestRunResults(object):
"""Set of results for a test run."""
......
......@@ -442,6 +442,9 @@ class InstrumentationTestInstance(test_instance.TestInstance):
self._store_tombstones = False
self._initializeTombstonesAttributes(args)
self._should_save_logcat = None
self._initializeLogAttributes(args)
def _initializeApkAttributes(self, args, error_func):
if args.apk_under_test:
apk_under_test_path = args.apk_under_test
......@@ -592,6 +595,9 @@ class InstrumentationTestInstance(test_instance.TestInstance):
def _initializeTombstonesAttributes(self, args):
self._store_tombstones = args.store_tombstones
def _initializeLogAttributes(self, args):
self._should_save_logcat = bool(args.json_results_file)
@property
def additional_apks(self):
return self._additional_apks
......@@ -624,6 +630,10 @@ class InstrumentationTestInstance(test_instance.TestInstance):
def flags(self):
return self._flags
@property
def should_save_logcat(self):
return self._should_save_logcat
@property
def package_info(self):
return self._package_info
......
......@@ -12,13 +12,13 @@ from devil.android import device_errors
from devil.android import flag_changer
from devil.utils import reraiser_thread
from pylib import valgrind_tools
from pylib.android import logdog_logcat_monitor
from pylib.base import base_test_result
from pylib.instrumentation import instrumentation_test_instance
from pylib.local.device import local_device_environment
from pylib.local.device import local_device_test_run
import tombstones
_TAG = 'test_runner_py'
TIMEOUT_ANNOTATIONS = [
......@@ -245,10 +245,19 @@ class LocalDeviceInstrumentationTestRun(
device.RunShellCommand(
['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name],
check_return=True)
logcat_url = None
time_ms = lambda: int(time.time() * 1e3)
start_ms = time_ms()
output = device.StartInstrumentation(
target, raw=True, extras=extras, timeout=timeout, retries=0)
if self._test_instance.should_save_logcat:
with logdog_logcat_monitor.LogdogLogcatMonitor(
device.adb,
'logcat_%s' % test_name.replace('#', '.')) as logmon:
output = device.StartInstrumentation(
target, raw=True, extras=extras, timeout=timeout, retries=0)
logcat_url = logmon.GetLogcatURL()
else:
output = device.StartInstrumentation(
target, raw=True, extras=extras, timeout=timeout, retries=0)
finally:
device.RunShellCommand(
['log', '-p', 'i', '-t', _TAG, 'END %s' % test_name],
......@@ -266,6 +275,8 @@ class LocalDeviceInstrumentationTestRun(
self._test_instance.ParseAmInstrumentRawOutput(output))
results = self._test_instance.GenerateTestResults(
result_code, result_bundle, statuses, start_ms, duration_ms)
for result in results:
result.SetLogcatUrl(logcat_url)
# Update the result name if the test used flags.
if flags:
......
......@@ -102,6 +102,7 @@ def GenerateResultsDict(test_run_results):
'losless_snippet': '',
'output_snippet_base64:': '',
'tombstones': r.GetTombstones() or '',
'logcat_url': r.GetLogcatUrl() or '',
}
iteration_data[r.GetName()].append(result_dict)
......
......@@ -67,9 +67,17 @@
../../third_party/catapult/devil/devil/utils/timeout_retry.py
../../third_party/catapult/devil/devil/utils/watchdog_timer.py
../../third_party/catapult/devil/devil/utils/zip_utils.py
../../tools/swarming_client/libs/__init__.py
../../tools/swarming_client/libs/logdog/__init__.py
../../tools/swarming_client/libs/logdog/bootstrap.py
../../tools/swarming_client/libs/logdog/stream.py
../../tools/swarming_client/libs/logdog/streamname.py
../../tools/swarming_client/libs/logdog/varint.py
../util/lib/common/unittest_util.py
devil_chromium.py
pylib/__init__.py
pylib/android/__init__.py
pylib/android/logdog_logcat_monitor.py
pylib/base/__init__.py
pylib/base/base_test_result.py
pylib/base/base_test_runner.py
......
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