 Chromium Code Reviews
 Chromium Code Reviews Issue 1415533007:
  [Android] Add sharding for AMP instrumentation tests.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1415533007:
  [Android] Add sharding for AMP instrumentation tests.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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..b7c389c4438b9370c2286a4a57306ee296297e77 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 parallelizer | 
| 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,17 +44,45 @@ 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 = None | 
| + self._base_tempfile_dir = '' | 
| + 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_package = None | 
| + self._shard_framework_configs = None | 
| + self._test_run_ids = [] | 
| + | 
| + # pylint: disable=no-self-use | 
| + def _GetAdditionalApks(self): | 
| + return NotImplementedError | 
| + | 
| + def _GetAppPath(self): | 
| + raise NotImplementedError | 
| + | 
| + def _GetTestPath(self): | 
| + raise NotImplementedError | 
| + | 
| + def _GetTestRunnerName(self): | 
| + raise NotImplementedError | 
| + | 
| + # pylint: disable=no-self-use | 
| + def _GetTestFramework(self): | 
| + raise NotImplementedError | 
| + | 
| + # pylint: disable=no-self-use | 
| + def _GetFrameworkConfigs(self): | 
| + return NotImplementedError | 
| + | 
| + # pylint: disable=unused-argument | 
| + def _GetShardEnvironmentVars(self, num_shards): | 
| + return NotImplementedError | 
| #override | 
| def SetUp(self): | 
| """Set up a test run.""" | 
| + self._base_tempfile_dir = tempfile.mkdtemp() | 
| if self._env.trigger: | 
| self._TriggerSetUp() | 
| elif self._env.collect: | 
| @@ -61,62 +90,110 @@ class RemoteDeviceTestRun(test_run.TestRun): | 
| '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 _ShouldShard(self): | 
| + raise NotImplementedError | 
| def _TriggerSetUp(self): | 
| """Set up the triggering of a test run.""" | 
| - raise NotImplementedError | 
| + self._app_id = self._UploadAppToDevice(self._GetAppPath()) | 
| + | 
| + 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 devices availible.', | 
| + self._env.num_shards, num_shards) | 
| + else: | 
| + num_shards = 1 | 
| + logging.critical('Sharding test run across %s device(s).', num_shards) | 
| 
jbudorick
2015/10/26 22:23:30
This is info, not critical.
 
mikecase (-- gone --)
2015/10/27 01:27:43
Done.
 | 
| + self._device_ids = random.sample(self._env.device_ids, num_shards) | 
| + | 
| + shard_env_vars = (self._GetShardEnvironmentVars(num_shards) | 
| 
jbudorick
2015/10/26 22:23:30
I don't think this is structured properly. Most of
 
mikecase (-- gone --)
2015/10/27 01:27:43
Yeah, but it even gets messier if you want some of
 | 
| + or [{} for _ in range(num_shards)]) | 
| 
jbudorick
2015/10/26 22:23:30
:/ this is ugly. Maybe [{}] * num_shards
also: wh
 
mikecase (-- gone --)
2015/10/27 01:27:43
Done.
 | 
| + framework_configs = self._GetFrameworkConfigs() or {} | 
| + | 
| + if self._GetTestPath(): | 
| + self._test_package, test_package_configs = self._PackageTest( | 
| + test_path=self._GetTestPath(), | 
| + extra_apks=self._GetAdditionalApks()) | 
| + else: | 
| + test_package_configs = {} | 
| + framework_configs.update(test_package_configs) | 
| + | 
| + self._appurify_configs = { | 
| + 'network': self._env.network_config, | 
| + 'pcap': self._env.pcap_config, | 
| + 'profiler': self._env.profiler_config, | 
| + 'videocapture': self._env.videocapture_config, | 
| + } | 
| + | 
| + self._shard_framework_configs = [] | 
| + for env_vars in shard_env_vars: | 
| + shard_framework_config = dict(framework_configs) | 
| + shard_framework_config['environment_vars'] = ','.join( | 
| + '%s=%s' % (k, v) for k, v in env_vars.iteritems()) | 
| + self._shard_framework_configs.append(shard_framework_config) | 
| + | 
| + def _Trigger(self, device_id, test_id): | 
| + with appurify_sanitized.SanitizeLogging( | 
| + verbose_count=self._env.verbose_count, | 
| + level=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( | 
| + response=test_start_response, | 
| + error_msg='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) | 
| - 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) | 
| + def trigger(trigger_args): | 
| 
jbudorick
2015/10/26 22:23:30
these should be defined in their respective if blo
 | 
| + device_id = trigger_args[0] | 
| + configs = trigger_args[1] | 
| + test_id = self._UploadTestToDevice() | 
| + self._UploadTestConfigToDevice( | 
| + test_id=test_id, | 
| + framework_configs=configs, | 
| + appurify_configs=self._appurify_configs) | 
| + test_run_id = self._Trigger( | 
| + device_id=device_id, | 
| + test_id=test_id) | 
| + self._test_run_ids.append(test_run_id) | 
| + | 
| + def collect(test_run_id, parsed_results): | 
| + download_results_file = tempfile.NamedTemporaryFile( | 
| + suffix='.zip', | 
| + dir=self._base_tempfile_dir, | 
| + delete=False) | 
| + 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=test_output, | 
| + results_zip=download_results_file.name)) | 
| - return self._ParseTestResults() | 
| + if self._env.trigger: | 
| + parallelizer.SyncParallelizer( | 
| + zip(self._device_ids, self._shard_framework_configs)).pMap(trigger) | 
| 
jbudorick
2015/10/26 22:23:30
O_O
I... I don't know how I feel about this.
 
mikecase (-- gone --)
2015/10/27 01:27:43
Ack
 | 
| + if self._env.collect: | 
| + parsed_results = base_test_result.TestRunResults() | 
| + parallelizer.SyncParallelizer( | 
| + self._test_run_ids).pMap(collect, parsed_results) | 
| 
jbudorick
2015/10/26 22:23:30
or, unsurprisingly, this.
 
mikecase (-- gone --)
2015/10/27 01:27:43
Ack
 | 
| + return parsed_results | 
| #override | 
| def TearDown(self): | 
| """Tear down the test run.""" | 
| + if self._base_tempfile_dir: | 
| + shutil.rmtree(self._base_tempfile_dir) | 
| + | 
| if self._env.collect: | 
| self._CollectTearDown() | 
| elif self._env.trigger: | 
| @@ -124,20 +201,46 @@ 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( | 
| + message='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( | 
| + message=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) | 
| + def abort_test_run(test_run_id): | 
| 
rnephew (Reviews Here)
2015/10/26 20:42:18
Is there a reason you can't just pass self._AbortT
 
mikecase (-- gone --)
2015/10/27 01:27:43
Hmmmmmmmmmm, it seems to work your way. I assumed
 | 
| + self._AbortTestRun(test_run_id) | 
| + if self._test_run_ids: | 
| + parallelizer.SyncParallelizer(self._test_run_ids).pMap(abort_test_run) | 
| def __enter__(self): | 
| """Set up the test run when used as a context manager.""" | 
| @@ -148,121 +251,87 @@ 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 _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. | 
| """ | 
| + logging.info('Downloading results to %s.', download_path) | 
| + with appurify_sanitized.SanitizeLogging( | 
| + verbose_count=self._env.verbose_count, | 
| + level=logging.WARNING): | 
| + appurify_sanitized.utils.wget( | 
| + url=results['results']['url'], | 
| + path=download_path) | 
| - 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 | 
| + 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( | 
| + verbose_count=self._env.verbose_count, | 
| + level=logging.WARNING): | 
| + test_check_response = appurify_sanitized.api.tests_check_result( | 
| + access_token=self._env.token, | 
| + test_run_id=test_run_id) | 
| + remote_device_helper.TestHttpResponse( | 
| + response=test_check_response, | 
| + error_msg='Unable to get test status.') | 
| + return test_check_response.json()['response'] | 
| + | 
| + def _PackageTest(self, test_path, extra_apks): | 
| + """Packages the test and dependencies into a zip. | 
| - 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, | 
| - '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) | 
| - | 
| + Returns: | 
| + A tuple of the path to the packaged test and a dictionary containing | 
| + configs about how the test was packaged. | 
| + """ | 
| + package_configs = {} | 
| 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) | 
| + test_package = tempfile.NamedTemporaryFile( | 
| + suffix='.zip', | 
| + delete=False, | 
| + dir=self._base_tempfile_dir) | 
| 
jbudorick
2015/10/26 22:23:30
oh. This is how you're deleting the temporary file
 
mikecase (-- gone --)
2015/10/27 01:27:43
Ack. All my temporary files are put into _base_tem
 | 
| + sdcard_files = [] | 
| + additional_apks = [] | 
| + host_test = os.path.basename(test_path) | 
| + with zipfile.ZipFile(test_package.name, 'w') as zip_file: | 
| + zip_file.write(self._GetTestPath(), 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)) | 
| + | 
| + package_configs['sdcard_files'] = ','.join(sdcard_files) | 
| + package_configs['host_test'] = host_test | 
| + if additional_apks: | 
| + package_configs['additional_apks'] = ','.join(additional_apks) | 
| + return (test_package.name, package_configs) | 
| 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 (self._GetTestPath(), package_configs) | 
| def _UploadAppToDevice(self, app_path): | 
| """Upload app to device.""" | 
| @@ -277,68 +346,102 @@ class RemoteDeviceTestRun(test_run.TestRun): | 
| 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) | 
| - 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. | 
| + def _UploadTestToDevice(self): | 
| + """Upload test to device.""" | 
| + logging.info('Shard uploading %s to remote service.', self._test_package) | 
| + with open(self._test_package, 'rb') as test_src: | 
| + with appurify_sanitized.SanitizeLogging( | 
| + verbose_count=self._env.verbose_count, | 
| + level=logging.WARNING): | 
| + upload_results_response = appurify_sanitized.api.tests_upload( | 
| + access_token=self._env.token, | 
| 
jbudorick
2015/10/26 22:23:30
You sure added a lot of kwarg labels. Are they rea
 
mikecase (-- gone --)
2015/10/27 01:27:43
Added them because it made reading the code far ea
 
jbudorick
2015/10/27 01:30:32
to be fair, you were also outvoted 2-1 :P
 | 
| + test_source=test_src, | 
| + test_source_type='raw', | 
| + test_type=self._GetTestFramework(), | 
| + app_id=self._app_id) | 
| + remote_device_helper.TestHttpResponse( | 
| + response=upload_results_response, | 
| + error_msg='Unable to upload %s.' % self._test_package) | 
| + 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) | 
| + with appurify_sanitized.SanitizeLogging( | 
| + verbose_count=self._env.verbose_count, | 
| + level=logging.WARNING): | 
| + config_upload_response = appurify_sanitized.api.config_upload( | 
| + access_token=self._env.token, | 
| + config_src=config, | 
| + test_id=test_id) | 
| + remote_device_helper.TestHttpResponse( | 
| + response=config_upload_response, | 
| + error_msg='Unable to upload test config.') | 
| + | 
| + def _DetectPlatformErrors(self, 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: | 
| + self._LogLogcat(results_zip) | 
| + results.AddResult(base_test_result.BaseTestResult( | 
| + crash_msg, base_test_result.ResultType.CRASH)) | 
| + elif self._DidDeviceGoOffline(results_zip): | 
| + self._LogLogcat(results_zip) | 
| + self._LogAdbTraceLog(results_zip) | 
| + raise remote_device_helper.RemoteDeviceError( | 
| + message='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)) | 
| + | 
| + def _AbortTestRun(self, test_run_id): | 
| + if self._CheckTestResults(test_run_id)['status'] != self.COMPLETE: | 
| + with appurify_sanitized.SanitizeLogging( | 
| + verbose_count=self._env.verbose_count, | 
| + level=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( | 
| - config_response, 'Unable to upload test config.') | 
| + response=test_abort_response, | 
| + error_msg='Unable to abort test.') | 
| - def _LogLogcat(self, level=logging.CRITICAL): | 
| + # pylint: disable=no-self-use | 
| + def _LogLogcat(self, results_zip, level=logging.CRITICAL): | 
| 
jbudorick
2015/10/26 22:23:30
methods that don't use self should usually be move
 
mikecase (-- gone --)
2015/10/27 01:27:43
Will do. Was kinda unsure since only this class re
 | 
| """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. | 
| """ | 
| - zip_file = self._DownloadTestResults(None) | 
| - with zipfile.ZipFile(zip_file) as z: | 
| + 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) | 
| @@ -347,44 +450,17 @@ class RemoteDeviceTestRun(test_run.TestRun): | 
| except KeyError: | 
| logging.error('No logcat found.') | 
| - def _LogAdbTraceLog(self): | 
| - zip_file = self._DownloadTestResults(None) | 
| - with zipfile.ZipFile(zip_file) as z: | 
| + # pylint: disable=no-self-use | 
| + def _LogAdbTraceLog(self, 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(self): | 
| - zip_file = self._DownloadTestResults(None) | 
| - with zipfile.ZipFile(zip_file) as z: | 
| + # pylint: disable=no-self-use | 
| + def _DidDeviceGoOffline(self, 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 | 
| - | 
| - 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() | 
| - 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)) |