| Index: build/android/pylib/remote/device/remote_device_test_run.py
|
| diff --git a/build/android/pylib/remote/device/remote_device_test_run.py b/build/android/pylib/remote/device/remote_device_test_run.py
|
| index ec29b554464dc4850d10a7421eb568fd0875e30c..e5af1f1b345e217e405d012e6703c3521947885c 100644
|
| --- a/build/android/pylib/remote/device/remote_device_test_run.py
|
| +++ b/build/android/pylib/remote/device/remote_device_test_run.py
|
| @@ -7,6 +7,7 @@
|
| import json
|
| import logging
|
| import os
|
| +import random
|
| import re
|
| import shutil
|
| import string
|
| @@ -14,10 +15,10 @@ import tempfile
|
| import time
|
| import zipfile
|
|
|
| +from devil.utils import reraiser_thread
|
| from devil.utils import zip_utils
|
| from pylib.base import base_test_result
|
| from pylib.base import test_run
|
| -from pylib.remote.device import appurify_constants
|
| from pylib.remote.device import appurify_sanitized
|
| from pylib.remote.device import remote_device_helper
|
|
|
| @@ -28,13 +29,13 @@ _SHORT_MSG_RE = re.compile('shortMsg=(.*)$')
|
| class RemoteDeviceTestRun(test_run.TestRun):
|
| """Run tests on a remote device."""
|
|
|
| - _TEST_RUN_KEY = 'test_run'
|
| - _TEST_RUN_ID_KEY = 'test_run_id'
|
| -
|
| - WAIT_TIME = 5
|
| + WAIT_TIME = 10
|
| COMPLETE = 'complete'
|
| HEARTBEAT_INTERVAL = 300
|
|
|
| + _TEST_RUN_KEY = 'test_run'
|
| + _TEST_RUN_IDS_KEY = 'test_run_ids'
|
| +
|
| def __init__(self, env, test_instance):
|
| """Constructor.
|
|
|
| @@ -43,80 +44,105 @@ class RemoteDeviceTestRun(test_run.TestRun):
|
| test_instance: The test that will be run.
|
| """
|
| super(RemoteDeviceTestRun, self).__init__(env, test_instance)
|
| + self._app_id = None
|
| + self._appurify_configs = {
|
| + 'network': env.network_config,
|
| + 'pcap': env.pcap_config,
|
| + 'profiler': env.profiler_config,
|
| + 'videocapture': env.videocapture_config,
|
| + }
|
| + self._device_ids = None
|
| self._env = env
|
| self._test_instance = test_instance
|
| - self._app_id = ''
|
| - self._test_id = ''
|
| - self._results = ''
|
| - self._test_run_id = ''
|
| - self._results_temp_dir = None
|
| + self._test_ids = []
|
| + self._test_run_ids = []
|
| +
|
| + def _GetAppPath(self):
|
| + raise NotImplementedError
|
| +
|
| + def _GetTestFramework(self):
|
| + raise NotImplementedError
|
| +
|
| + def _SetupTestShards(self, num_shards):
|
| + raise NotImplementedError
|
|
|
| #override
|
| def SetUp(self):
|
| """Set up a test run."""
|
| if self._env.trigger:
|
| - self._TriggerSetUp()
|
| + num_shards = 1
|
| + if self._ShouldShard():
|
| + num_shards = min(self._env.num_shards, len(self._env.device_ids))
|
| + # TODO(mikecase): Change to always run with the number of shards passed.
|
| + # Will need some shards to wait for new devices to become available.
|
| + if num_shards < self._env.num_shards:
|
| + logging.warning(
|
| + 'Requested to shard across %s devices, only %s availible.',
|
| + self._env.num_shards, num_shards)
|
| + logging.info('Sharding test run across %s device(s).', num_shards)
|
| +
|
| + self._test_ids = self._SetupTestShards(num_shards)
|
| +
|
| elif self._env.collect:
|
| assert isinstance(self._env.collect, basestring), (
|
| 'File for storing test_run_id must be a string.')
|
| with open(self._env.collect, 'r') as persisted_data_file:
|
| persisted_data = json.loads(persisted_data_file.read())
|
| - self._env.LoadFrom(persisted_data)
|
| - self.LoadFrom(persisted_data)
|
| + self._LoadFrom(persisted_data)
|
|
|
| - def _TriggerSetUp(self):
|
| - """Set up the triggering of a test run."""
|
| - raise NotImplementedError
|
| + # pylint: disable=no-self-use
|
| + def _ShouldShard(self):
|
| + return False
|
| +
|
| + def _Trigger(self, device_id, test_id):
|
| + with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| + logging.WARNING):
|
| + test_start_response = appurify_sanitized.api.tests_run(
|
| + access_token=self._env.token, device_type_id=device_id,
|
| + app_id=self._app_id, test_id=test_id)
|
| + remote_device_helper.TestHttpResponse(test_start_response,
|
| + 'Unable to run test.')
|
| + return test_start_response.json()['response']['test_run_id']
|
|
|
| - #override
|
| def RunTests(self):
|
| """Run the test."""
|
| +
|
| if self._env.trigger:
|
| - with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| - logging.WARNING):
|
| - test_start_res = appurify_sanitized.api.tests_run(
|
| - self._env.token, self._env.device_type_id, self._app_id,
|
| - self._test_id)
|
| - remote_device_helper.TestHttpResponse(
|
| - test_start_res, 'Unable to run test.')
|
| - self._test_run_id = test_start_res.json()['response']['test_run_id']
|
| - logging.info('Test run id: %s', self._test_run_id)
|
| + def trigger(device_id, test_id):
|
| + test_run_id = self._Trigger(device_id, test_id)
|
| + self._test_run_ids.append(test_run_id)
|
| +
|
| + self._app_id = self._UploadAppToDevice(self._GetAppPath())
|
| + self._device_ids = random.sample(
|
| + self._env.device_ids, len(self._test_ids))
|
| +
|
| + reraiser_thread.RunAsync(
|
| + funcs=[trigger]*len(self._test_ids),
|
| + args=zip(self._device_ids, self._test_ids),
|
| + names=['trigger test %s' % test_id for test_id in self._test_ids])
|
|
|
| if self._env.collect:
|
| - current_status = ''
|
| - timeout_counter = 0
|
| - heartbeat_counter = 0
|
| - while self._GetTestStatus(self._test_run_id) != self.COMPLETE:
|
| - if self._results['detailed_status'] != current_status:
|
| - logging.info('Test status: %s', self._results['detailed_status'])
|
| - current_status = self._results['detailed_status']
|
| - timeout_counter = 0
|
| - heartbeat_counter = 0
|
| - if heartbeat_counter > self.HEARTBEAT_INTERVAL:
|
| - logging.info('Test status: %s', self._results['detailed_status'])
|
| - heartbeat_counter = 0
|
| -
|
| - timeout = self._env.timeouts.get(
|
| - current_status, self._env.timeouts['unknown'])
|
| - if timeout_counter > timeout:
|
| - raise remote_device_helper.RemoteDeviceError(
|
| - 'Timeout while in %s state for %s seconds'
|
| - % (current_status, timeout),
|
| - is_infra_error=True)
|
| - time.sleep(self.WAIT_TIME)
|
| - timeout_counter += self.WAIT_TIME
|
| - heartbeat_counter += self.WAIT_TIME
|
| - self._DownloadTestResults(self._env.results_path)
|
| -
|
| - if self._results['results']['exception']:
|
| - raise remote_device_helper.RemoteDeviceError(
|
| - self._results['results']['exception'], is_infra_error=True)
|
| + parsed_results = base_test_result.TestRunResults()
|
| +
|
| + def collect(test_run_id):
|
| + with tempfile.NamedTemporaryFile(
|
| + prefix='results-', suffix='.zip') as download_results_file:
|
| + test_output = self._Collect(test_run_id=test_run_id)
|
| + self._DownloadTestResults(test_output, download_results_file.name)
|
| + parsed_results.AddTestRunResults(self._ParseTestResults(
|
| + test_output, download_results_file.name))
|
| +
|
| + reraiser_thread.RunAsync(
|
| + funcs=[collect]*len(self._test_run_ids),
|
| + args=[[run_id] for run_id in self._test_run_ids],
|
| + names=['collect test %s' % run_id for run_id in self._test_run_ids])
|
|
|
| - return self._ParseTestResults()
|
| + return parsed_results
|
|
|
| #override
|
| def TearDown(self):
|
| """Tear down the test run."""
|
| +
|
| if self._env.collect:
|
| self._CollectTearDown()
|
| elif self._env.trigger:
|
| @@ -124,20 +150,47 @@ class RemoteDeviceTestRun(test_run.TestRun):
|
| 'File for storing test_run_id must be a string.')
|
| with open(self._env.trigger, 'w') as persisted_data_file:
|
| persisted_data = {}
|
| - self.DumpTo(persisted_data)
|
| - self._env.DumpTo(persisted_data)
|
| + self._DumpTo(persisted_data)
|
| persisted_data_file.write(json.dumps(persisted_data))
|
|
|
| + def _Collect(self, test_run_id):
|
| + current_status = ''
|
| + timeout_counter = 0
|
| + heartbeat_counter = 0
|
| + results = self._CheckTestResults(test_run_id)
|
| + while results['status'] != self.COMPLETE:
|
| + if results['detailed_status'] != current_status:
|
| + logging.info('Test status: %s', results['detailed_status'])
|
| + current_status = results['detailed_status']
|
| + timeout_counter = 0
|
| + heartbeat_counter = 0
|
| + if heartbeat_counter > self.HEARTBEAT_INTERVAL:
|
| + logging.info('Test status: %s', results['detailed_status'])
|
| + heartbeat_counter = 0
|
| +
|
| + timeout = self._env.timeouts.get(
|
| + current_status, self._env.timeouts['unknown'])
|
| + if timeout_counter > timeout:
|
| + raise remote_device_helper.RemoteDeviceError(
|
| + 'Timeout while in %s state for %s seconds'
|
| + % (current_status, timeout),
|
| + is_infra_error=True)
|
| + time.sleep(self.WAIT_TIME)
|
| + timeout_counter += self.WAIT_TIME
|
| + heartbeat_counter += self.WAIT_TIME
|
| + results = self._CheckTestResults(test_run_id)
|
| + if results['results']['exception']:
|
| + raise remote_device_helper.RemoteDeviceError(
|
| + results['results']['exception'],
|
| + is_infra_error=True)
|
| + return results
|
| +
|
| def _CollectTearDown(self):
|
| - if self._GetTestStatus(self._test_run_id) != self.COMPLETE:
|
| - with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| - logging.WARNING):
|
| - test_abort_res = appurify_sanitized.api.tests_abort(
|
| - self._env.token, self._test_run_id, reason='Test runner exiting.')
|
| - remote_device_helper.TestHttpResponse(test_abort_res,
|
| - 'Unable to abort test.')
|
| - if self._results_temp_dir:
|
| - shutil.rmtree(self._results_temp_dir)
|
| + if self._test_run_ids:
|
| + reraiser_thread.RunAsync(
|
| + funcs=[self._AbortTestRun]*len(self._test_run_ids),
|
| + args=[[run_id] for run_id in self._test_run_ids],
|
| + names=['aborting test %s' % run_id for run_id in self._test_run_ids])
|
|
|
| def __enter__(self):
|
| """Set up the test run when used as a context manager."""
|
| @@ -148,121 +201,76 @@ class RemoteDeviceTestRun(test_run.TestRun):
|
| """Tear down the test run when used as a context manager."""
|
| self.TearDown()
|
|
|
| - def DumpTo(self, persisted_data):
|
| - test_run_data = {
|
| - self._TEST_RUN_ID_KEY: self._test_run_id,
|
| + def _PackageTest(self, dest_path, test_apk_path, data_deps, extra_apks=None):
|
| + packaged_data_deps = []
|
| + packaged_extra_apks = []
|
| + packaged_test_path = os.path.basename(test_apk_path)
|
| +
|
| + with zipfile.ZipFile(dest_path, 'w') as zip_file:
|
| + zip_file.write(test_apk_path, packaged_test_path, zipfile.ZIP_DEFLATED)
|
| + for h, _ in data_deps:
|
| + if os.path.isdir(h):
|
| + zip_utils.WriteToZipFile(zip_file, h, '.')
|
| + packaged_data_deps.extend(os.listdir(h))
|
| + else:
|
| + zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h))
|
| + packaged_data_deps.append(os.path.basename(h))
|
| + for a in extra_apks or ():
|
| + zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a))
|
| + packaged_extra_apks.append(os.path.basename(a))
|
| +
|
| + configs = {
|
| + 'host_test': packaged_test_path,
|
| + 'sdcard_files': ','.join(packaged_data_deps),
|
| }
|
| + if packaged_extra_apks:
|
| + configs['additional_apks'] = ','.join(packaged_extra_apks)
|
| + return configs
|
| +
|
| + def _DumpTo(self, persisted_data):
|
| + """Dump test run information to dict."""
|
| + test_run_data = {self._TEST_RUN_IDS_KEY: self._test_run_ids}
|
| persisted_data[self._TEST_RUN_KEY] = test_run_data
|
|
|
| - def LoadFrom(self, persisted_data):
|
| + def _LoadFrom(self, persisted_data):
|
| + """Load test run information."""
|
| test_run_data = persisted_data[self._TEST_RUN_KEY]
|
| - self._test_run_id = test_run_data[self._TEST_RUN_ID_KEY]
|
| + self._test_run_ids = test_run_data[self._TEST_RUN_IDS_KEY]
|
|
|
| - def _ParseTestResults(self):
|
| + def _ParseTestResults(self, test_output, results_zip):
|
| raise NotImplementedError
|
|
|
| - def _GetTestByName(self, test_name):
|
| - """Gets test_id for specific test.
|
| -
|
| - Args:
|
| - test_name: Test to find the ID of.
|
| - """
|
| - with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| - logging.WARNING):
|
| - test_list_res = appurify_sanitized.api.tests_list(self._env.token)
|
| - remote_device_helper.TestHttpResponse(test_list_res,
|
| - 'Unable to get tests list.')
|
| - for test in test_list_res.json()['response']:
|
| - if test['test_type'] == test_name:
|
| - return test['test_id']
|
| - raise remote_device_helper.RemoteDeviceError(
|
| - 'No test found with name %s' % (test_name))
|
| -
|
| - def _DownloadTestResults(self, results_path):
|
| + def _DownloadTestResults(self, results, download_path):
|
| """Download the test results from remote device service.
|
|
|
| - Downloads results in temporary location, and then copys results
|
| - to results_path if results_path is not set to None.
|
| -
|
| - Args:
|
| - results_path: Path to download appurify results zipfile.
|
| -
|
| Returns:
|
| Path to downloaded file.
|
| """
|
| -
|
| - if self._results_temp_dir is None:
|
| - self._results_temp_dir = tempfile.mkdtemp()
|
| - logging.info('Downloading results to %s.', self._results_temp_dir)
|
| - with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| - logging.WARNING):
|
| - appurify_sanitized.utils.wget(self._results['results']['url'],
|
| - self._results_temp_dir + '/results')
|
| - if results_path:
|
| - logging.info('Copying results to %s', results_path)
|
| - if not os.path.exists(os.path.dirname(results_path)):
|
| - os.makedirs(os.path.dirname(results_path))
|
| - shutil.copy(self._results_temp_dir + '/results', results_path)
|
| - return self._results_temp_dir + '/results'
|
| -
|
| - def _GetTestStatus(self, test_run_id):
|
| - """Checks the state of the test, and sets self._results
|
| + logging.info('Downloading results to %s.', download_path)
|
| + with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| + logging.WARNING):
|
| + appurify_sanitized.utils.wget(
|
| + url=results['results']['url'], path=download_path)
|
| + if self._env.results_dir:
|
| + logging.info('Copying results to %s from %s',
|
| + self._env.results_dir, download_path)
|
| + if not os.path.exists(os.path.dirname(self._env.results_dir)):
|
| + os.makedirs(self._env.results_dir)
|
| + shutil.copy(download_path, self._env.results_dir)
|
| +
|
| + def _CheckTestResults(self, test_run_id):
|
| + """Checks the state of the test.
|
|
|
| Args:
|
| test_run_id: Id of test on on remote service.
|
| """
|
| -
|
| with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| logging.WARNING):
|
| - test_check_res = appurify_sanitized.api.tests_check_result(
|
| - self._env.token, test_run_id)
|
| - remote_device_helper.TestHttpResponse(test_check_res,
|
| + test_check_response = appurify_sanitized.api.tests_check_result(
|
| + access_token=self._env.token, test_run_id=test_run_id)
|
| + remote_device_helper.TestHttpResponse(test_check_response,
|
| 'Unable to get test status.')
|
| - self._results = test_check_res.json()['response']
|
| - return self._results['status']
|
| -
|
| - def _AmInstrumentTestSetup(self, app_path, test_path, runner_package,
|
| - environment_variables, extra_apks=None):
|
| - config = {'runner': runner_package}
|
| - if environment_variables:
|
| - config['environment_vars'] = ','.join(
|
| - '%s=%s' % (k, v) for k, v in environment_variables.iteritems())
|
| -
|
| - self._app_id = self._UploadAppToDevice(app_path)
|
| -
|
| - data_deps = self._test_instance.GetDataDependencies()
|
| - if data_deps:
|
| - with tempfile.NamedTemporaryFile(suffix='.zip') as test_with_deps:
|
| - sdcard_files = []
|
| - additional_apks = []
|
| - host_test = os.path.basename(test_path)
|
| - with zipfile.ZipFile(test_with_deps.name, 'w') as zip_file:
|
| - zip_file.write(test_path, host_test, zipfile.ZIP_DEFLATED)
|
| - for h, _ in data_deps:
|
| - if os.path.isdir(h):
|
| - zip_utils.WriteToZipFile(zip_file, h, '.')
|
| - sdcard_files.extend(os.listdir(h))
|
| - else:
|
| - zip_utils.WriteToZipFile(zip_file, h, os.path.basename(h))
|
| - sdcard_files.append(os.path.basename(h))
|
| - for a in extra_apks or ():
|
| - zip_utils.WriteToZipFile(zip_file, a, os.path.basename(a))
|
| - additional_apks.append(os.path.basename(a))
|
| -
|
| - config['sdcard_files'] = ','.join(sdcard_files)
|
| - config['host_test'] = host_test
|
| - if additional_apks:
|
| - config['additional_apks'] = ','.join(additional_apks)
|
| - self._test_id = self._UploadTestToDevice(
|
| - 'robotium', test_with_deps.name, app_id=self._app_id)
|
| - else:
|
| - self._test_id = self._UploadTestToDevice('robotium', test_path)
|
| -
|
| - logging.info('Setting config: %s', config)
|
| - appurify_configs = {}
|
| - if self._env.network_config:
|
| - appurify_configs['network'] = self._env.network_config
|
| - self._SetTestConfig('robotium', config, **appurify_configs)
|
| + return test_check_response.json()['response']
|
|
|
| def _UploadAppToDevice(self, app_path):
|
| """Upload app to device."""
|
| @@ -273,118 +281,112 @@ class RemoteDeviceTestRun(test_run.TestRun):
|
| logging.WARNING):
|
| upload_results = appurify_sanitized.api.apps_upload(
|
| self._env.token, apk_src, 'raw', name=self._test_instance.suite)
|
| - remote_device_helper.TestHttpResponse(
|
| - upload_results, 'Unable to upload %s.' % app_path)
|
| + remote_device_helper.TestHttpResponse(upload_results,
|
| + 'Unable to upload %s.' % app_path)
|
| return upload_results.json()['response']['app_id']
|
|
|
| - def _UploadTestToDevice(self, test_type, test_path, app_id=None):
|
| - """Upload test to device
|
| - Args:
|
| - test_type: Type of test that is being uploaded. Ex. uirobot, gtest..
|
| - """
|
| - logging.info('Uploading %s to remote service.', test_path)
|
| + def _UploadTestToDevice(self, test_path):
|
| + """Upload test to device."""
|
| + logging.info('Shard uploading %s to remote service.', test_path)
|
| with open(test_path, 'rb') as test_src:
|
| with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| logging.WARNING):
|
| - upload_results = appurify_sanitized.api.tests_upload(
|
| - self._env.token, test_src, 'raw', test_type, app_id=app_id)
|
| - remote_device_helper.TestHttpResponse(upload_results,
|
| - 'Unable to upload %s.' % test_path)
|
| - return upload_results.json()['response']['test_id']
|
| -
|
| - def _SetTestConfig(self, runner_type, runner_configs,
|
| - network=appurify_constants.NETWORK.WIFI_1_BAR,
|
| - pcap=0, profiler=0, videocapture=0):
|
| - """Generates and uploads config file for test.
|
| - Args:
|
| - runner_configs: Configs specific to the runner you are using.
|
| - network: Config to specify the network environment the devices running
|
| - the tests will be in.
|
| - pcap: Option to set the recording the of network traffic from the device.
|
| - profiler: Option to set the recording of CPU, memory, and network
|
| - transfer usage in the tests.
|
| - videocapture: Option to set video capture during the tests.
|
| + upload_results_response = appurify_sanitized.api.tests_upload(
|
| + access_token=self._env.token, test_source=test_src,
|
| + test_source_type='raw', test_type=self._GetTestFramework(),
|
| + app_id=self._app_id)
|
| + remote_device_helper.TestHttpResponse(
|
| + upload_results_response, 'Unable to upload %s.' % test_path)
|
| + return upload_results_response.json()['response']['test_id']
|
|
|
| - """
|
| + def _UploadTestConfigToDevice(
|
| + self, test_id, framework_configs, appurify_configs):
|
| + """Generates and uploads config file for test."""
|
| logging.info('Generating config file for test.')
|
| + config_data = ['[appurify]']
|
| + config_data.extend(
|
| + '%s=%s' % (k, v) for k, v in appurify_configs.iteritems())
|
| + config_data.append('[%s]' % self._GetTestFramework())
|
| + config_data.extend(
|
| + '%s=%s' % (k, v) for k, v in framework_configs.iteritems())
|
| with tempfile.TemporaryFile() as config:
|
| - config_data = [
|
| - '[appurify]',
|
| - 'network=%s' % network,
|
| - 'pcap=%s' % pcap,
|
| - 'profiler=%s' % profiler,
|
| - 'videocapture=%s' % videocapture,
|
| - '[%s]' % runner_type
|
| - ]
|
| - config_data.extend(
|
| - '%s=%s' % (k, v) for k, v in runner_configs.iteritems())
|
| config.write(''.join('%s\n' % l for l in config_data))
|
| config.flush()
|
| config.seek(0)
|
| with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| logging.WARNING):
|
| - config_response = appurify_sanitized.api.config_upload(
|
| - self._env.token, config, self._test_id)
|
| - remote_device_helper.TestHttpResponse(
|
| - config_response, 'Unable to upload test config.')
|
| -
|
| - def _LogLogcat(self, level=logging.CRITICAL):
|
| - """Prints out logcat downloaded from remote service.
|
| - Args:
|
| - level: logging level to print at.
|
| -
|
| - Raises:
|
| - KeyError: If appurify_results/logcat.txt file cannot be found in
|
| - downloaded zip.
|
| - """
|
| - zip_file = self._DownloadTestResults(None)
|
| - with zipfile.ZipFile(zip_file) as z:
|
| - try:
|
| - logcat = z.read('appurify_results/logcat.txt')
|
| - printable_logcat = ''.join(c for c in logcat if c in string.printable)
|
| - for line in printable_logcat.splitlines():
|
| - logging.log(level, line)
|
| - except KeyError:
|
| - logging.error('No logcat found.')
|
| -
|
| - def _LogAdbTraceLog(self):
|
| - zip_file = self._DownloadTestResults(None)
|
| - with zipfile.ZipFile(zip_file) as z:
|
| - adb_trace_log = z.read('adb_trace.log')
|
| - for line in adb_trace_log.splitlines():
|
| - logging.critical(line)
|
| -
|
| - def _DidDeviceGoOffline(self):
|
| - zip_file = self._DownloadTestResults(None)
|
| - with zipfile.ZipFile(zip_file) as z:
|
| - adb_trace_log = z.read('adb_trace.log')
|
| - if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()):
|
| - return True
|
| - return False
|
| + config_upload_response = appurify_sanitized.api.config_upload(
|
| + access_token=self._env.token, config_src=config, test_id=test_id)
|
| + remote_device_helper.TestHttpResponse(config_upload_response,
|
| + 'Unable to upload test config.')
|
| +
|
| + def _AbortTestRun(self, test_run_id):
|
| + logging.info('Aborting test run %s.', test_run_id)
|
| + if self._CheckTestResults(test_run_id)['status'] != self.COMPLETE:
|
| + with appurify_sanitized.SanitizeLogging(self._env.verbose_count,
|
| + logging.WARNING):
|
| + test_abort_response = appurify_sanitized.api.tests_abort(
|
| + access_token=self._env.token, test_run_id=test_run_id,
|
| + reason='Test runner exiting.')
|
| + remote_device_helper.TestHttpResponse(test_abort_response,
|
| + 'Unable to abort test.')
|
|
|
| - def _DetectPlatformErrors(self, results):
|
| - if not self._results['results']['pass']:
|
| - crash_msg = None
|
| - for line in self._results['results']['output'].splitlines():
|
| - m = _LONG_MSG_RE.search(line)
|
| - if m:
|
| - crash_msg = m.group(1)
|
| - break
|
| - m = _SHORT_MSG_RE.search(line)
|
| - if m:
|
| - crash_msg = m.group(1)
|
| - if crash_msg:
|
| - self._LogLogcat()
|
| +def DetectPlatformErrors(results, test_output, results_zip):
|
| + if not test_output['results']['pass']:
|
| + crash_msg = None
|
| + for line in test_output['results']['output'].splitlines():
|
| + m = _LONG_MSG_RE.search(line)
|
| + if m:
|
| + crash_msg = m.group(1)
|
| + break
|
| + m = _SHORT_MSG_RE.search(line)
|
| + if m:
|
| + crash_msg = m.group(1)
|
| + if crash_msg:
|
| + _LogLogcat(results_zip)
|
| + results.AddResult(base_test_result.BaseTestResult(
|
| + crash_msg, base_test_result.ResultType.CRASH))
|
| + elif _DidDeviceGoOffline(results_zip):
|
| + _LogLogcat(results_zip)
|
| + _LogAdbTraceLog(results_zip)
|
| + raise remote_device_helper.RemoteDeviceError(
|
| + 'Remote service unable to reach device.',
|
| + is_infra_error=True)
|
| + else:
|
| + # Remote service is reporting a failure, but no failure in results obj.
|
| + if results.DidRunPass():
|
| results.AddResult(base_test_result.BaseTestResult(
|
| - crash_msg, base_test_result.ResultType.CRASH))
|
| - elif self._DidDeviceGoOffline():
|
| - self._LogLogcat()
|
| - self._LogAdbTraceLog()
|
| - raise remote_device_helper.RemoteDeviceError(
|
| - 'Remote service unable to reach device.', is_infra_error=True)
|
| - else:
|
| - # Remote service is reporting a failure, but no failure in results obj.
|
| - if results.DidRunPass():
|
| - results.AddResult(base_test_result.BaseTestResult(
|
| - 'Remote service detected error.',
|
| - base_test_result.ResultType.UNKNOWN))
|
| + 'Remote service detected error.',
|
| + base_test_result.ResultType.UNKNOWN))
|
| +
|
| +def _LogLogcat(results_zip, level=logging.CRITICAL):
|
| + """Prints out logcat downloaded from remote service.
|
| + Args:
|
| + results_zip: path to zipfile containing the results files.
|
| + level: logging level to print at.
|
| +
|
| + Raises:
|
| + KeyError: If appurify_results/logcat.txt file cannot be found in
|
| + downloaded zip.
|
| + """
|
| + with zipfile.ZipFile(results_zip) as z:
|
| + try:
|
| + logcat = z.read('appurify_results/logcat.txt')
|
| + printable_logcat = ''.join(c for c in logcat if c in string.printable)
|
| + for line in printable_logcat.splitlines():
|
| + logging.log(level, line)
|
| + except KeyError:
|
| + logging.error('No logcat found.')
|
| +
|
| +def _LogAdbTraceLog(results_zip):
|
| + with zipfile.ZipFile(results_zip) as z:
|
| + adb_trace_log = z.read('adb_trace.log')
|
| + for line in adb_trace_log.splitlines():
|
| + logging.critical(line)
|
| +
|
| +def _DidDeviceGoOffline(results_zip):
|
| + with zipfile.ZipFile(results_zip) as z:
|
| + adb_trace_log = z.read('adb_trace.log')
|
| + if any(_DEVICE_OFFLINE_RE.search(l) for l in adb_trace_log.splitlines()):
|
| + return True
|
| + return False
|
|
|