# Copyright 2017 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. from . import default """iOS flavor, used for running code on iOS.""" class iOSFlavor(default.DefaultFlavor): def __init__(self, m, app_name): super(iOSFlavor, self).__init__(m, app_name) self.device_dirs = default.DeviceDirs( bin_dir='[unused]', dm_dir='dm', perf_data_dir='perf', resource_dir='resources', images_dir='images', lotties_dir='lotties', skp_dir='skps', svg_dir='svgs', mskp_dir='mskp', tmp_dir='tmp', texttraces_dir='') @property def env(self): return { 'IOS_BUNDLE_ID': 'com.google.%s' % self.app_name, 'IOS_MOUNT_POINT': self.m.vars.workdir.join('mnt_iosdevice'), } def context(self): return self.m.context(env=self.env) def _run(self, title, *cmd, **kwargs): def sleep(attempt): self.m.python.inline('sleep before attempt %d' % attempt, """ import time time.sleep(2) """) # pragma: nocover return self.m.run.with_retry(self.m.step, title, 3, cmd=list(cmd), between_attempts_fn=sleep, **kwargs) def install(self): with self.context(): self._install() def _install(self): # We assume a single device is connected. # Pair the device. try: self.m.run(self.m.step, 'check if device is paired', cmd=['idevicepair', 'validate'], infra_step=True, abort_on_failure=True, fail_build_on_failure=False) except self.m.step.StepFailure: # pragma: nocover self._run('pair device', 'idevicepair', 'pair') # pragma: nocover # Mount developer image. image_info = self._run('list mounted image', 'ideviceimagemounter', '--list', stdout=self.m.raw_io.output()) image_info_out = ( image_info.stdout.decode('utf-8').strip() if image_info.stdout else '') if 'ImageSignature' not in image_info_out: image_pkgs = self.m.file.glob_paths('locate ios-dev-image package', self.m.path['start_dir'], 'ios-dev-image*', test_data=['ios-dev-image-13.2']) if len(image_pkgs) != 1: raise Exception('glob for ios-dev-image* returned %s' % image_pkgs) # pragma: nocover image_pkg = image_pkgs[0] contents = self.m.file.listdir( 'locate image and signature', image_pkg, test_data=['DeveloperDiskImage.dmg', 'DeveloperDiskImage.dmg.signature']) image = None sig = None for f in contents: if str(f).endswith('.dmg'): image = f if str(f).endswith('.dmg.signature'): sig = f if not image or not sig: raise Exception('%s does not contain *.dmg and *.dmg.signature' % image_pkg) # pragma: nocover self._run('mount developer image', 'ideviceimagemounter', image, sig) # Install app (necessary before copying any resources to the device). if self.app_name: app_package = self.host_dirs.bin_dir.join('%s.app' % self.app_name) def uninstall_app(attempt): # If app ID changes, upgrade will fail, so try uninstalling. self.m.run(self.m.step, 'uninstall %s' % self.app_name, cmd=['ideviceinstaller', '-U', 'com.google.%s' % self.app_name], infra_step=True, # App may not be installed. abort_on_failure=False, fail_build_on_failure=False) num_attempts = 2 self.m.run.with_retry(self.m.step, 'install %s' % self.app_name, num_attempts, cmd=['ideviceinstaller', '-i', app_package], between_attempts_fn=uninstall_app, infra_step=True) def step(self, name, cmd, **kwargs): app_name = cmd[0] bundle_id = 'com.google.%s' % app_name args = [bundle_id] + [str(ele) for ele in cmd[1:]] success = False with self.context(): try: self.m.run(self.m.step, name, cmd=['idevicedebug', 'run'] + args) success = True finally: if not success: self.m.run(self.m.python, '%s with full debug output' % name, script=self.module.resource('ios_debug_cmd.py'), args=args) def _run_ios_script(self, script, first, *rest): with self.context(): full = self.m.path['start_dir'].join( 'skia', 'platform_tools', 'ios', 'bin', 'ios_' + script) self.m.run(self.m.step, name = '%s %s' % (script, first), cmd = [full, first] + list(rest), infra_step=True) def copy_file_to_device(self, host, device): self._run_ios_script('push_file', host, device) def copy_directory_contents_to_device(self, host, device): self._run_ios_script('push_if_needed', host, device) def copy_directory_contents_to_host(self, device, host): self._run_ios_script('pull_if_needed', device, host) def remove_file_on_device(self, path): self._run_ios_script('rm', path) def create_clean_device_dir(self, path): self._run_ios_script('rm', path) self._run_ios_script('mkdir', path) def read_file_on_device(self, path, **kwargs): with self.context(): full = self.m.path['start_dir'].join( 'skia', 'platform_tools', 'ios', 'bin', 'ios_cat_file') rv = self.m.run(self.m.step, name = 'cat_file %s' % path, cmd = [full, path], stdout=self.m.raw_io.output(), infra_step=True, **kwargs) return rv.stdout.decode('utf-8').rstrip() if rv and rv.stdout else None