def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m
def set_up_device(opt): if not opt.wifi_ssid or not opt.wifi_key or not opt.wifi_pass: raise ValueError('Missing --wifi options') mc = Marionette('localhost', opt.adb_port) for i in range(2): try: mc.start_session() break except socket.error: sh('adb forward tcp:%s tcp:%s' % (opt.adb_port, opt.adb_port)) if opt.shell: from pdb import set_trace set_trace() return # watch out! This is how gaiatest does it. mc.__class__ = type('Marionette', (Marionette, MarionetteTouchMixin), {}) device = GaiaDevice(mc) device.restart_b2g() apps = GaiaApps(mc) data_layer = GaiaData(mc) lockscreen = LockScreen(mc) mc.setup_touch() lockscreen.unlock() apps.kill_all() data_layer.enable_wifi() if opt.wifi_key == 'WPA-PSK': pass_key = 'psk' elif opt.wifi_key == 'WEP': pass_key = 'wep' else: assert 0, 'unknown key management' data = {'ssid': opt.wifi_ssid, 'keyManagement': opt.wifi_key, pass_key: opt.wifi_pass} data_layer.connect_to_wifi(data) mc.switch_to_frame() all_apps = set(a['manifest']['name'] for a in get_installed(apps)) if 'Marketplace Dev' not in all_apps: mc.execute_script( 'navigator.mozApps.install' '("https://marketplace-dev.allizom.org/manifest.webapp");') wait_for_element_displayed(mc, 'id', 'app-install-install-button') yes = mc.find_element('id', 'app-install-install-button') mc.tap(yes) wait_for_element_displayed(mc, 'id', 'system-banner') print 'Pushing payment prefs' sh('adb shell stop b2g') sh('adb push "%s" /data/local/user.js' % ( os.path.join(os.path.dirname(__file__), 'payment-prefs.js'))) sh('adb shell start b2g') print 'When your device reboots, Marketplace Dev will be installed'
def set_up_device(args): mc = get_marionette(args) device = GaiaDevice(mc) try: device.restart_b2g() except Exception: print ' ** Check to make sure you don\'t have desktop B2G running' raise apps = GaiaApps(mc) data_layer = GaiaData(mc) lockscreen = LockScreen(mc) mc.setup_touch() lockscreen.unlock() apps.kill_all() if args.wifi_ssid: print 'Configuring WiFi' if not args.wifi_key or not args.wifi_pass: args.error('Missing --wifi_key or --wifi_pass option') args.wifi_key = args.wifi_key.upper() data_layer.enable_wifi() if args.wifi_key == 'WPA-PSK': pass_key = 'psk' elif args.wifi_key == 'WEP': pass_key = 'wep' else: args.error('not sure what key to use for %r' % args.wifi_key) data = {'ssid': args.wifi_ssid, 'keyManagement': args.wifi_key, pass_key: args.wifi_pass} data_layer.connect_to_wifi(data) for manifest in args.apps: # There is probably a way easier way to do this by adb pushing # something. Send me a patch! mc.switch_to_frame() try: data = requests.get(manifest).json() app_name = data['name'] all_apps = set(a['manifest']['name'] for a in get_installed(apps)) if app_name not in all_apps: print 'Installing %s from %s' % (app_name, manifest) mc.execute_script('navigator.mozApps.install("%s");' % manifest) wait_for_element_displayed(mc, 'id', 'app-install-install-button') yes = mc.find_element('id', 'app-install-install-button') mc.tap(yes) # This still works but the id check broke. # See https://bugzilla.mozilla.org/show_bug.cgi?id=853878 wait_for_element_displayed(mc, 'id', 'system-banner') except Exception, exc: print ' ** installing manifest %s failed (maybe?)' % manifest print ' ** error: %s: %s' % (exc.__class__.__name__, exc) continue
def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error("couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element("id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception("Couldn't restart the device in time, even after 3 tries")
def install_apps(): mc = get_marionette(args) device = GaiaDevice(mc) try: device.restart_b2g() print 'Your device is rebooting.' except Exception: print ' ** Check to make sure you don\'t have desktop B2G running' raise apps = GaiaApps(mc) apps.kill_all() lockscreen = LockScreen(mc) lockscreen.unlock() if args.wifi_ssid: print 'Configuring WiFi' if not args.wifi_key or not args.wifi_pass: args.error('Missing --wifi_key or --wifi_pass option') args.wifi_key = args.wifi_key.upper() data_layer = GaiaData(mc) data_layer.enable_wifi() if args.wifi_key == 'WPA-PSK': pass_key = 'psk' elif args.wifi_key == 'WEP': pass_key = 'wep' else: args.error('not sure what key to use for %r' % args.wifi_key) data = { 'ssid': args.wifi_ssid, 'keyManagement': args.wifi_key, pass_key: args.wifi_pass } data_layer.connect_to_wifi(data) # disconnect marionette client because install_app would need it mc.client.close() # install apps one by one for manifest in args.apps: args.manifest = manifest args.app = None install_app(args)
def setup(self): if not self.serial or not self.port: logger.error("Fail to get device") raise DMError self.config_raptor() self.marionette and self.marionette.session and self.marionette.cleanup( ) self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial, port=self.port) self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.wait_for_port() self.marionette.start_session() self.device = GaiaDevice(marionette=self.marionette, manager=self.dm) self.apps = GaiaApps(self.marionette) self.data_layer = GaiaData(self.marionette) if self.flashed: self.device.wait_for_b2g_ready()
def install_apps(): mc = get_marionette(args) device = GaiaDevice(mc) try: device.restart_b2g() print 'Your device is rebooting.' except Exception: print ' ** Check to make sure you don\'t have desktop B2G running' raise apps = GaiaApps(mc) apps.kill_all() lockscreen = LockScreen(mc) lockscreen.unlock() if args.wifi_ssid: print 'Configuring WiFi' if not args.wifi_key or not args.wifi_pass: args.error('Missing --wifi_key or --wifi_pass option') args.wifi_key = args.wifi_key.upper() data_layer = GaiaData(mc) data_layer.enable_wifi() if args.wifi_key == 'WPA-PSK': pass_key = 'psk' elif args.wifi_key == 'WEP': pass_key = 'wep' else: args.error('not sure what key to use for %r' % args.wifi_key) data = {'ssid': args.wifi_ssid, 'keyManagement': args.wifi_key, pass_key: args.wifi_pass} data_layer.connect_to_wifi(data) # disconnect marionette client because install_app would need it mc.client.close() # install apps one by one for manifest in args.apps: args.manifest = manifest args.app = None install_app(args)
def setup(self): if not self.serial or not self.port: logger.error("Fail to get device") raise DMError self.marionette and self.marionette.session and self.marionette.cleanup() self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial, port=self.port) self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.start_session() self.device = GaiaDevice(marionette=self.marionette, manager=self.dm) self.apps = GaiaApps(self.marionette) self.data_layer = GaiaData(self.marionette) self.device.wait_for_b2g_ready()
def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error( "couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element( "id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception( "Couldn't restart the device in time, even after 3 tries")
def __init__(self, marionette, log_level='INFO'): self.marionette = marionette self.data_layer = GaiaData(self.marionette) self.device = GaiaDevice(self.marionette) dm = mozdevice.DeviceManagerADB() self.device.add_device_manager(dm) self.logger.setLevel(getattr(mozlog, log_level.upper())) if self.device.is_android_build: self.idb_dir = 'idb' for candidate in self.device.manager.listFiles( '/'.join([self.PERSISTENT_STORAGE_PATH, 'chrome'])): if re.match('\d.*idb', candidate): self.idb_dir = candidate break
def __init__(self, marionette, log_level='INFO', start_timeout=60, device_serial=None): self.marionette = marionette self.data_layer = GaiaData(self.marionette) dm = mozdevice.DeviceManagerADB(deviceSerial=device_serial) self.device = GaiaDevice(self.marionette, manager=dm) self.logger.setLevel(getattr(mozlog, log_level.upper())) self.start_timeout = start_timeout if self.device.is_android_build: self.idb_dir = 'idb' for candidate in self.device.file_manager.list_items( '/'.join([self.PERSISTENT_STORAGE_PATH, 'chrome'])): if re.match('\d.*idb', candidate): self.idb_dir = candidate break
class MtbfJobRunner(BaseActionRunner): serial = None marionette = None flash_params = { 'branch': 'mozilla-b2g34_v2_1-flame-kk-eng', 'build': '', 'build_id': '' } flashed = False def __init__(self, **kwargs): self.logger = logger BaseActionRunner.__init__(self) def setup(self): if not self.serial or not self.port: logger.error("Fail to get device") raise DMError self.config_raptor() self.marionette and self.marionette.session and self.marionette.cleanup( ) self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial, port=self.port) self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.wait_for_port() self.marionette.start_session() self.device = GaiaDevice(marionette=self.marionette, manager=self.dm) self.apps = GaiaApps(self.marionette) self.data_layer = GaiaData(self.marionette) if self.flashed: self.device.wait_for_b2g_ready() def adb_test(self): if not hasattr(self, 'serial') or os.system("ANDROID_SERIAL=" + self.serial + " adb shell ls") != 0: logger.error("Device not found or can't be controlled") return False return True @action(enabled=False) def add_7mobile_action(self): # workaround for waiting for boot self.marionette.wait_for_port() self.marionette.start_session() self.data_layer = GaiaData(self.marionette) self.data_layer.set_setting('ril.data.apnSettings', [[{ "carrier": "(7-Mobile) (MMS)", "apn": "opentalk", "mmsc": "http://mms", "mmsproxy": "210.241.199.199", "mmsport": "9201", "types": ["mms"] }, { "carrier": "(7-Mobile) (Internet)", "apn": "opentalk", "types": ["default", "supl"] }]]) return True @action(enabled=False) def change_memory(self): # This function only work in flame # TODO: use native adb/fastboot command to change memory? # Make sure it's in fastboot mode, TODO: leverage all fastboot command in one task function memory = 512 # default set 512 if 'MEM' in os.environ: memory = os.environ['MEM'] elif self.settings['change_memory'][ 'enabled'] and 'memory' in self.settings['change_memory']: memory = self.settings['change_memory']['memory'] if self.adb_test(): os.system("adb reboot bootloader") memory = 512 mem_str = str(memory) os.system("fastboot oem mem " + mem_str) # Preventing from async timing of fastboot os.system("fastboot reboot") self.device_obj.create_adb_forward(self.port) return True logger.error("Can't find device") self.marionette.wait_for_port() self.device_obj.create_adb_forward(self.port) return False @action(enabled=False) def config_raptor(self): settings = self.settings if 'config_raptor' in settings and settings['config_raptor']['config']: with open(os.path.expandvars( settings['config_raptor']['config'])) as conf: self.raptor = json.load(conf) self.raptor['path'] = settings['config_raptor']['config'] self.raptor['monitorJobFolder'] = settings['config_raptor'][ 'monitorJobFolder'] @action(enabled=True) def collect_memory_report(self): zip_utils.collect_about_memory( "mtbf_driver") # TODO: give a correct path for about memory folder def get_free_device(self): do = device_pool.get_device(self.serial) if do: # Record device serial and store dp instance self.serial = do.serial self.device_obj = do if do.create_adb_forward(): self.port = do.adb_forwarded_port logger.info("Device found, ANDROID_SERIAL= " + self.serial) return do logger.error("Port forwarding failed") raise DMError logger.warning( "No available device. Please retry after device released") # TODO: more handling for no available device def validate_flash_params(self): ## Using system environment variable as temporary solution TODO: use other way for input params ## Check if package(files)/folder exists and return, else raise exception if not 'FLASH_BASEDIR' in os.environ: raise AttributeError("No FLASH_BASEDIR set") basedir = os.environ['FLASH_BASEDIR'] if not 'FLASH_BUILDID' in os.environ: ## TODO: if latest/ exists, use latest as default logging.info("No build id set. search in base dir") buildid = "" flash_dir = basedir else: buildid = os.environ['FLASH_BUILDID'] # re-format build id based on pvt folder structure if '-' in buildid: buildid = buildid.replace("-", "") year = buildid[:4] month = buildid[4:6] datetime = '-'.join( [year, month] + [buildid[i + 6:i + 8] for i in range(0, len(buildid[6:]), 2)]) flash_dir = os.path.join(basedir, year, month, datetime) if not os.path.isdir(flash_dir): raise AttributeError("Flash directory " + flash_dir + " not exist") flash_files = glob.glob(os.path.join(flash_dir, '*')) flash_src = {} for flash_file in flash_files: logger.debug("Flash source found: [" + flash_file + "]") if os.path.isdir(flash_file): continue elif re.match("^b2g-[0-9]*.*\.tar\.gz$", flash_file): flash_src['gecko'] = flash_file elif "gaia.zip" == flash_file: flash_src['gaia'] = flash_file elif "symbol" in flash_file: flash_src['symbol'] = flash_file elif "zip" in flash_file and not ("gaia.zip" in flash_file): flash_src['image'] = flash_file return flash_src @action(enabled=True) def full_flash(self): flash_src = self.validate_flash_params() if self.flashed: logger.warning("Flash performed; skip flashing") return True if not flash_src: logger.warning("Invalid build folder/build_id, skip flashing") return False if not 'image' in flash_src: logger.warning("No available image for flash, skip flashing") return False try: self.temp_dir = tempfile.mkdtemp() logger.info('Create temporary folder:' + self.temp_dir) Decompressor().unzip(flash_src['image'], self.temp_dir) # set the permissions to rwxrwxr-x (509 in python's os.chmod) os.chmod( self.temp_dir + '/b2g-distro/flash.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) os.chmod( self.temp_dir + '/b2g-distro/load-config.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) os.system('cd ' + self.temp_dir + '/b2g-distro; ./flash.sh -f') # support NO_FTU environment for skipping FTU (e.g. monkey test) if 'NO_FTU' in os.environ and os.environ['NO_FTU'] == 'true': logger.log('The [NO_FTU] is [true].') os.system( "ANDROID_SERIAL=" + self.serial + 'adb wait-for-device && adb shell stop b2g; (RET=$(adb root); if ! case ${RET} in *"cannot"*) true;; *) false;; esac; then adb remount && sleep 5; else exit 1; fi; ./disable_ftu.py) || (echo "No root permission, cannot setup NO_FTU."); adb reboot;' ) finally: try: shutil.rmtree(self.temp_dir) # delete directory except OSError: logger.error('Can not remove temporary folder:' + self.temp_dir, level=Logger._LEVEL_WARNING) self.flashed = True @action(enabled=False) def shallow_flash(self): flash_src = self.validate_flash_params() if self.flashed: logger.warning("Flash performed; skip flashing") return True if not flash_src: logger.warning("Invalid build folder/build_id, skip flashing") return False if not 'gaia' in flash_src or not 'gecko' in flash_src: logger.warning("No gaia or gecko archive, skip flashing") return False cmd = 'flash_tool/shallow_flash.sh -y --gecko="' + flash_src[ 'gecko'] + '" --gaia="' + flash_src['gaia'] + '"' if _platform == 'darwin': cmd = cmd.replace('=', ' ') ret = os.system(cmd) if ret != 0: logger.info("Shallow flash ended abnormally") return False self.flashed = True os.system("ANDROID_SERIAL=" + self.serial + " adb wait-for-device") @action(enabled=True) def enable_certified_apps_debug(self): if self.serial: os.system( "ANDROID_SERIAL=" + self.serial + " flash_tool/enable_certified_apps_for_devtools.sh && adb wait-for-device" ) logger.debug("Successfully enabling certified apps for debugging") return True return False def release(self): device_pool.release() def start_monitoring(self): job = { 'name': 'mtbf', 'type': 'moz_minions.kevin.MtbfToRaptorMinion', 'serial': self.serial, 'job_info': { 'pid': os.getpid(), 'program': sys.argv[0], } } if hasattr(self, 'raptor'): raptor = self.raptor job['job_info'].update(self.raptor) if "monitorJobFolder" in self.raptor: dirpath = os.path.expandvars(self.raptor['monitorJobFolder']) else: dirpath = "/tmp/mtbf" if not os.path.isdir(dirpath): os.makedirs(dirpath) timestamp = time.strftime('%Y-%m-%d-%H-%M-%S+0000', time.gmtime()) filename = job['name'] + "_" + timestamp + ".json" self.monitor_conf = os.path.join(dirpath, filename) job['job_info']['conf'] = self.monitor_conf with open(self.monitor_conf, 'w') as fh: fh.write(json.dumps(job, indent=2, sort_keys=True)) def stop_monitoring(self): if hasattr(self, 'raptor'): os.remove(self.monitor_conf) self.monitor_conf = None def check_version(self): # FIXME: fix check version to use package import cmd = "cd flash_tool/ && NO_COLOR=TRUE ./check_versions.py | sed -e 's| \{2,\}||g' -e 's|\[0m||g'" if self.serial: cmd = "ANDROID_SERIAL=" + self.serial + " " + cmd os.system(cmd) @action(enabled=False) def patch_marionette(self): os.system( "M_PATH=/mnt/mtbf_shared/paul/ /mnt/mtbf_shared/paul/marionette_update.sh" ) import time time.sleep(10) self.device_obj.create_adb_forward(self.port) def mtbf_options(self): ## load mtbf parameters if not 'MTBF_TIME' in os.environ: logger.warning("MTBF_TIME is not set") if not 'MTBF_CONF' in os.environ: logger.warning("MTBF_CONF is not set") parser = self.parser.parser parser.add_argument("--testvars", help="Test variables for b2g") self.parse_options() # FIXME: make rootdir of testvars could be customized mtbf_testvars_dir = "/mnt/mtbf_shared/testvars" if not hasattr(self.options, 'testvars') or not self.options.testvars: testvars = os.path.join(mtbf_testvars_dir, "testvars_" + self.serial + ".json") logger.info("testvar is [" + testvars + "]") if os.path.exists(testvars): self.options.testvars = parser.testvars = testvars logger.info("testvar [" + testvars + "] found") else: raise AttributeError("testvars[" + testvars + "] doesn't exist") def remove_settings_opt(self): for e in sys.argv[1:]: if '--settings' in e: idx = sys.argv.index(e) sys.argv.remove(e) if len(sys.argv) > idx and not '--' in sys.argv[idx]: del sys.argv[idx] break @action(enabled=False) def mtbf_daily(self): parser = GaiaTestArguments() opts = [] for k, v in self.kwargs.iteritems(): opts.append("--" + k) opts.append(v) options, tests = parser.parse_args(sys.argv[1:] + opts) structured.commandline.add_logging_group(parser) logger = structured.commandline.setup_logging(options.logger_name, options, {"tbpl": sys.stdout}) options.logger = logger options.testvars = [self.options.testvars] runner = GaiaTestRunner(**vars(options)) runner.run_tests(["tests"]) @action(enabled=True) def run_mtbf(self): mtbf.main(testvars=self.options.testvars, **self.kwargs) def execute(self): self.marionette.cleanup() self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.wait_for_port() # run test runner here self.remove_settings_opt() self.kwargs = {} if self.port: self.kwargs['address'] = "localhost:" + str(self.port) logger.info("Using address[localhost:" + str(self.port) + "]") self.start_monitoring() self.mtbf_daily() self.run_mtbf() self.stop_monitoring() def pre_flash(self): pass def flash(self): self.shallow_flash() self.full_flash() # workaround for waiting for boot def post_flash(self): self.setup() self.check_version() self.change_memory() self.add_7mobile_action() self.enable_certified_apps_debug() self.patch_marionette() def output_crash_report_no_to_log(self, serial): if serial in CrashScan.get_current_all_dev_serials(): crash_result = CrashScan.get_crash_no_by_serial(serial) if crash_result['crashNo'] > 0: logger.error("CrashReportFound: device " + serial + " has " + str(crash_result['crashNo']) + " crashes.") else: logger.info( "CrashReportNotFound: No crash report found in device " + serial) else: logger.error("CrashReportAdbError: Can't find device in ADB list") def collect_report(self, serial): self.output_crash_report_no_to_log(serial) def run(self): try: if self.get_free_device(): self.mtbf_options() self.pre_flash() self.flash() self.device_obj.create_adb_forward() self.port = self.device_obj.adb_forwarded_port self.post_flash() self.execute() self.collect_report(self.serial) finally: self.release()
class B2GPopulate(object): PERSISTENT_STORAGE_PATH = '/data/local/storage/persistent' handler = mozlog.StreamHandler() handler.setFormatter(mozlog.MozFormatter(include_timestamp=True)) logger = mozlog.getLogger('B2GPopulate', handler) def __init__(self, marionette, log_level='INFO'): self.marionette = marionette self.data_layer = GaiaData(self.marionette) self.device = GaiaDevice(self.marionette) dm = mozdevice.DeviceManagerADB() self.device.add_device_manager(dm) self.logger.setLevel(getattr(mozlog, log_level.upper())) if self.device.is_android_build: self.idb_dir = 'idb' for candidate in self.device.manager.listFiles( '/'.join([self.PERSISTENT_STORAGE_PATH, 'chrome'])): if re.match('\d.*idb', candidate): self.idb_dir = candidate break def populate(self, call_count=None, contact_count=None, message_count=None, music_count=None, picture_count=None, video_count=None, event_count=None): restart = any([call_count, contact_count, message_count]) if restart: self.logger.debug('Stopping B2G') self.device.stop_b2g() if call_count is not None: self.populate_calls(call_count, restart=False) if contact_count is not None: self.populate_contacts(contact_count, restart=False) if event_count is not None: self.populate_events(event_count, restart=False) if message_count is not None: self.populate_messages(message_count, restart=False) if restart: self.start_b2g() if music_count > 0: self.populate_music(music_count) if picture_count > 0: self.populate_pictures(picture_count) if video_count > 0: self.populate_videos(video_count) def populate_calls(self, count, restart=True): # only allow preset db values for calls db_counts = DB_PRESET_COUNTS['call'] if not count in db_counts: raise InvalidCountError('call') self.logger.info('Populating %d calls' % count) db_counts.sort(reverse=True) for marker in db_counts: if count >= marker: key = 'communications.gaiamobile.org' local_id = json.loads(self.device.manager.pullFile( '/data/local/webapps/webapps.json'))[key]['localId'] db_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', 'dialerDb.zip'])) db_name = 'dialerDb-%d.sqlite' % marker self.logger.debug('Extracting %s from %s' % ( db_name, db_zip_name)) db = ZipFile(db_zip_name).extract(db_name) if restart: self.device.stop_b2g() destination = '/'.join([self.PERSISTENT_STORAGE_PATH, '%s+f+app+++%s' % (local_id, key), self.idb_dir, '2584670174dsitanleecreR.sqlite']) self.logger.debug('Pushing %s to %s' % (db, destination)) self.device.push_file(db, destination=destination) self.logger.debug('Removing %s' % db) os.remove(db) if restart: self.start_b2g() break def populate_contacts(self, count, restart=True, include_pictures=True): # only allow preset db values for contacts db_counts = DB_PRESET_COUNTS['contact'] if not count in db_counts: raise InvalidCountError('contact') self.device.manager.removeDir('/'.join([ self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir, '*csotncta*'])) self.logger.info('Populating %d contacts' % count) db_counts.sort(reverse=True) for marker in db_counts: if count >= marker: db_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', 'contactsDb.zip'])) db_name = 'contactsDb-%d.sqlite' % marker self.logger.debug('Extracting %s from %s' % ( db_name, db_zip_name)) db = ZipFile(db_zip_name).extract(db_name) if restart: self.device.stop_b2g() destination = '/'.join([ self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir, '3406066227csotncta.sqlite']) self.logger.debug('Pushing %s to %s' % (db, destination)) self.device.push_file(db, destination=destination) self.logger.debug('Removing %s' % db) os.remove(db) if marker > 0 and include_pictures: self.logger.debug('Adding contact pictures') pictures_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join( ['resources', 'contactsPictures.zip'])) temp = tempfile.mkdtemp() self.logger.debug('Extracting %s to %s' % ( pictures_zip_name, temp)) ZipFile(pictures_zip_name).extractall(temp) destination = '/'.join([ self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir, '3406066227csotncta']) self.logger.debug('Pushing %s to %s' % (temp, destination)) self.device.manager.pushDir(temp, destination) self.logger.debug('Removing %s' % temp) shutil.rmtree(temp) if restart: self.start_b2g() break def populate_events(self, count, restart=True): # only allow preset db values for events db_counts = DB_PRESET_COUNTS['event'] if not count in db_counts: raise InvalidCountError('event') self.logger.info('Populating %d events' % count) db_counts.sort(reverse=True) for marker in db_counts: if count >= marker: key = 'calendar.gaiamobile.org' local_id = json.loads(self.device.manager.pullFile( '/data/local/webapps/webapps.json'))[key]['localId'] db_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', 'calendarDb.zip'])) db_name = 'calendarDb-%d.sqlite' % marker self.logger.debug('Extracting %s from %s' % ( db_name, db_zip_name)) db = ZipFile(db_zip_name).extract(db_name) if restart: self.device.stop_b2g() destination = '/'.join([self.PERSISTENT_STORAGE_PATH, '%s+f+app+++%s' % (local_id, key), self.idb_dir, '125582036br2agd-nceal.sqlite']) self.logger.debug('Pushing %s to %s' % (db, destination)) self.device.push_file(db, destination=destination) self.logger.debug('Removing %s' % db) os.remove(db) if restart: self.start_b2g() break def populate_messages(self, count, restart=True): # only allow preset db values for messages db_counts = DB_PRESET_COUNTS['message'] if not count in db_counts: raise InvalidCountError('message') self.logger.info('Populating %d messages' % count) db_counts.sort(reverse=True) for marker in db_counts: if count >= marker: db_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', 'smsDb.zip'])) db_name = 'smsDb-%d.sqlite' % marker self.logger.debug('Extracting %s from %s' % ( db_name, db_zip_name)) db = ZipFile(db_zip_name).extract(db_name) if restart: self.device.stop_b2g() destination = '/'.join([ self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir, '226660312ssm.sqlite']) self.logger.debug('Pushing %s to %s' % (db, destination)) self.device.push_file(db, destination=destination) os.remove(db) if marker > 0: self.logger.debug('Adding message attachments') all_attachments_zip_name = pkg_resources.resource_filename( __name__, os.path.sep.join( ['resources', 'smsAttachments.zip'])) attachments_zip_name = 'smsAttachments-%d.zip' % marker self.logger.debug('Extracting %s from %s' % ( attachments_zip_name, all_attachments_zip_name)) attachments_zip = ZipFile( all_attachments_zip_name).extract(attachments_zip_name) temp = tempfile.mkdtemp() self.logger.debug('Extracting %s to %s' % ( attachments_zip, temp)) ZipFile(attachments_zip).extractall(temp) destination = '/'.join([ self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir, '226660312ssm']) self.logger.debug('Pushing %s to %s' % (temp, destination)) self.device.manager.pushDir(temp, destination) self.logger.debug('Removing %s' % temp) shutil.rmtree(temp) self.logger.debug('Removing %s' % attachments_zip) os.remove(attachments_zip) if restart: self.start_b2g() break def populate_music(self, count, source='MUS_0001.mp3', destination='sdcard', tracks_per_album=10): self.remove_media('music') import math from mutagen.easyid3 import EasyID3 music_file = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', source])) local_filename = music_file.rpartition(os.path.sep)[-1] # copy the mp3 file into a temp location with tempfile.NamedTemporaryFile() as local_copy: self.logger.debug('Creating copy of %s at %s' % ( music_file, local_copy.name)) local_copy.write(open(music_file).read()) music_file = local_copy.name mp3 = EasyID3(music_file) album_count = math.ceil(float(count) / tracks_per_album) self.logger.info('Populating %d music files (%d album%s)' % ( count, album_count, 's' if album_count > 1 else '')) for i in range(1, count + 1): album = math.ceil(float(i) / float(tracks_per_album)) track = i - ((album - 1) * tracks_per_album) mp3['title'] = 'Track %d' % track mp3['artist'] = 'Artist %d' % album mp3['album'] = 'Album %d' % album mp3['tracknumber'] = str(track) mp3.save() remote_filename = '_%s.'.join( iter(local_filename.split('.'))) % i remote_destination = os.path.join(destination, remote_filename) self.logger.debug('Pushing %s to %s' % ( music_file, remote_destination)) self.device.push_file(music_file, 1, remote_destination) def populate_pictures(self, count, source='IMG_0001.jpg', destination='sdcard/DCIM/100MZLLA'): self.populate_files('picture', source, count, destination) def populate_videos(self, count, source='VID_0001.3gp', destination='sdcard/DCIM/100MZLLA'): self.populate_files('video', source, count, destination) def populate_files(self, file_type, source, count, destination=''): self.remove_media(file_type) self.logger.info('Populating %d %s files' % (count, file_type)) source_file = pkg_resources.resource_filename( __name__, os.path.sep.join(['resources', source])) self.logger.debug('Pushing %d copies of %s to %s' % ( count, source_file, destination)) self.device.push_file(source_file, count, destination) def remove_media(self, file_type): if self.device.is_android_build: files = getattr(self.data_layer, '%s_files' % file_type) or [] if len(files) > 0: self.logger.info('Removing %d %s files' % ( len(files), file_type)) for filename in files: self.logger.debug('Removing %s' % filename) self.device.manager.removeFile(filename) # TODO Wait for files to be deleted time.sleep(5) files = getattr(self.data_layer, '%s_files' % file_type) or [] if not len(files) == 0: raise IncorrectCountError( '%s files' % file_type, 0, len(files)) def start_b2g(self): self.logger.debug('Starting B2G') self.device.start_b2g() self.data_layer = GaiaData(self.marionette)
class MtbfJobRunner(BaseActionRunner): serial = None marionette = None flash_params = { 'branch': 'mozilla-b2g34_v2_1-flame-kk-eng', 'build': '', 'build_id': '' } flashed = False def __init__(self, **kwargs): self.logger = logger BaseActionRunner.__init__(self) def setup(self): if not self.serial or not self.port: logger.error("Fail to get device") raise DMError self.marionette and self.marionette.session and self.marionette.cleanup() self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial, port=self.port) self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.start_session() self.device = GaiaDevice(marionette=self.marionette, manager=self.dm) self.apps = GaiaApps(self.marionette) self.data_layer = GaiaData(self.marionette) self.device.wait_for_b2g_ready() def adb_test(self): if not hasattr(self, 'serial') or os.system("ANDROID_SERIAL=" + self.serial + " adb shell ls") != 0: logger.error("Device not found or can't be controlled") return False return True @action(enabled=False) def add_7mobile_action(self): # workaround for waiting for boot self.marionette.start_session() self.data_layer = GaiaData(self.marionette) self.data_layer.set_setting('ril.data.apnSettings', [[ {"carrier": "(7-Mobile) (MMS)", "apn": "opentalk", "mmsc": "http://mms", "mmsproxy": "210.241.199.199", "mmsport": "9201", "types": ["mms"]}, {"carrier": "(7-Mobile) (Internet)", "apn": "opentalk", "types": ["default", "supl"]} ]]) return True @action(enabled=False) def change_memory(self): # This function only work in flame # TODO: use native adb/fastboot command to change memory? # Make sure it's in fastboot mode, TODO: leverage all fastboot command in one task function memory = 512 # default set 512 if 'MEM' in os.environ: memory = os.environ['MEM'] elif self.settings['change_memory']['enabled'] and 'memory' in self.settings['change_memory']: memory = self.settings['change_memory']['memory'] if self.adb_test(): os.system("adb reboot bootloader") memory = 512 mem_str = str(memory) os.system("fastboot oem mem " + mem_str) # Preventing from async timing of fastboot os.system("fastboot reboot") self.device_obj.create_adb_forward(self.port) return True logger.error("Can't find device") self.marionette.wait_for_port() self.device_obj.create_adb_forward(self.port) return False @action(enabled=True) def collect_memory_report(self): zip_utils.collect_about_memory("mtbf_driver") # TODO: give a correct path for about memory folder def get_free_device(self): do = device_pool.get_device(self.serial) if do: # Record device serial and store dp instance self.serial = do.serial self.device_obj = do if do.create_adb_forward(): self.port = do.adb_forwarded_port logger.info("Device found, ANDROID_SERIAL= " + self.serial) return do logger.error("Port forwarding failed") raise DMError logger.warning("No available device. Please retry after device released") # TODO: more handling for no available device def validate_flash_params(self): ## Using system environment variable as temporary solution TODO: use other way for input params ## Check if package(files)/folder exists and return, else raise exception if not 'FLASH_BASEDIR' in os.environ: raise AttributeError("No FLASH_BASEDIR set") basedir = os.environ['FLASH_BASEDIR'] if not 'FLASH_BUILDID' in os.environ: ## TODO: if latest/ exists, use latest as default logging.info("No build id set. search in base dir") buildid = "" flash_dir = basedir else: buildid = os.environ['FLASH_BUILDID'] # re-format build id based on pvt folder structure if '-' in buildid: buildid = buildid.replace("-", "") year = buildid[:4] month = buildid[4:6] datetime = '-'.join([year, month] + [buildid[i + 6:i + 8] for i in range(0, len(buildid[6:]), 2)]) flash_dir = os.path.join(basedir, year, month, datetime) if not os.path.isdir(flash_dir): raise AttributeError("Flash directory " + flash_dir + " not exist") flash_files = glob.glob(os.path.join(flash_dir, '*')) flash_src = {} for flash_file in flash_files: logger.debug("Flash source found: [" + flash_file + "]") if os.path.isdir(flash_file): continue elif re.match("^b2g-[0-9]*.*\.tar\.gz$", flash_file): flash_src['gecko'] = flash_file elif "gaia.zip" == flash_file: flash_src['gaia'] = flash_file elif "symbol" in flash_file: flash_src['symbol'] = flash_file elif "zip" in flash_file and not ("gaia.zip" in flash_file): flash_src['image'] = flash_file return flash_src @action(enabled=True) def full_flash(self): flash_src = self.validate_flash_params() if self.flashed: logger.warning("Flash performed; skip flashing") return True if not flash_src: logger.warning("Invalid build folder/build_id, skip flashing") return False if not 'image' in flash_src: logger.warning("No available image for flash, skip flashing") return False try: self.temp_dir = tempfile.mkdtemp() logger.info('Create temporary folder:' + self.temp_dir) Decompressor().unzip(flash_src['image'], self.temp_dir) # set the permissions to rwxrwxr-x (509 in python's os.chmod) os.chmod(self.temp_dir + '/b2g-distro/flash.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) os.chmod(self.temp_dir + '/b2g-distro/load-config.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) os.system('cd ' + self.temp_dir + '/b2g-distro; ./flash.sh -f') # support NO_FTU environment for skipping FTU (e.g. monkey test) if 'NO_FTU' in os.environ and os.environ['NO_FTU'] == 'true': logger.log('The [NO_FTU] is [true].') os.system("ANDROID_SERIAL=" + self.serial + 'adb wait-for-device && adb shell stop b2g; (RET=$(adb root); if ! case ${RET} in *"cannot"*) true;; *) false;; esac; then adb remount && sleep 5; else exit 1; fi; ./disable_ftu.py) || (echo "No root permission, cannot setup NO_FTU."); adb reboot;') finally: try: shutil.rmtree(self.temp_dir) # delete directory except OSError: logger.error('Can not remove temporary folder:' + self.temp_dir, level=Logger._LEVEL_WARNING) self.flashed = True @action(enabled=False) def shallow_flash(self): flash_src = self.validate_flash_params() if self.flashed: logger.warning("Flash performed; skip flashing") return True if not flash_src: logger.warning("Invalid build folder/build_id, skip flashing") return False if not 'gaia' in flash_src or not 'gecko' in flash_src: logger.warning("No gaia or gecko archive, skip flashing") return False cmd = 'flash_tool/shallow_flash.sh -y --gecko="' + flash_src['gecko'] + '" --gaia="' + flash_src['gaia'] + '"' if _platform == 'darwin': cmd = cmd.replace('=', ' ') ret = os.system(cmd) if ret != 0: logger.info("Shallow flash ended abnormally") return False self.flashed = True os.system("ANDROID_SERIAL=" + self.serial + " adb wait-for-device") @action(enabled=True) def enable_certified_apps_debug(self): if self.serial: os.system("ANDROID_SERIAL=" + self.serial + " flash_tool/enable_certified_apps_for_devtools.sh && adb wait-for-device") logger.debug("Successfully enabling certified apps for debugging") return True return False def release(self): device_pool.release() def check_version(self): # FIXME: fix check version to use package import cmd = "cd flash_tool/ && NO_COLOR=TRUE ./check_versions.py | sed -e 's| \{2,\}||g' -e 's|\[0m||g'" if self.serial: cmd = "ANDROID_SERIAL=" + self.serial + " " + cmd os.system(cmd) @action(enabled=False) def patch_marionette(self): os.system("M_PATH=/mnt/mtbf_shared/paul/ /mnt/mtbf_shared/paul/marionette_update.sh") import time time.sleep(10) self.device_obj.create_adb_forward(self.port) def mtbf_options(self): ## load mtbf parameters if not 'MTBF_TIME' in os.environ: logger.warning("MTBF_TIME is not set") if not 'MTBF_CONF' in os.environ: logger.warning("MTBF_CONF is not set") parser = self.parser.parser parser.add_argument("--testvars", help="Test variables for b2g") self.parse_options() # FIXME: make rootdir of testvars could be customized mtbf_testvars_dir = "/mnt/mtbf_shared/testvars" if not hasattr(self.options, 'testvars') or not self.options.testvars: testvars = os.path.join(mtbf_testvars_dir, "testvars_" + self.serial + ".json") logger.info("testvar is [" + testvars + "]") if os.path.exists(testvars): self.options.testvars = parser.testvars = testvars logger.info("testvar [" + testvars + "] found") else: raise AttributeError("testvars[" + testvars + "] doesn't exist") ## #TODO: finish parsing arguments for flashing ## parser.add_argument("--flashdir", help="directory for pulling build") ## parser.add_argument("--buildid", help="build id for pulling build") ## self.parse_options() ## if hasattr(self.parser.option, 'flashdir'): ## os.environ['FLASH_BASEDIR'] = self.parser.option.flashdir ## if hasattr(self.parser.option, 'buildid'): ## os.environ['FLASH_BUILDID'] = self.parser.option.buildid def remove_settings_opt(self): for e in sys.argv[1:]: if '--settings' in e: idx = sys.argv.index(e) sys.argv.remove(e) if len(sys.argv) > idx and not '--' in sys.argv[idx]: del sys.argv[idx] break @action(enabled=False) def mtbf_daily(self): parser = GaiaTestOptions() opts = [] for k, v in self.kwargs.iteritems(): opts.append("--" + k) opts.append(v) options, tests = parser.parse_args(sys.argv[1:] + opts) structured.commandline.add_logging_group(parser) logger = structured.commandline.setup_logging( options.logger_name, options, {"tbpl": sys.stdout}) options.logger = logger options.testvars = [self.options.testvars] runner = GaiaTestRunner(**vars(options)) runner.run_tests(["tests"]) @action(enabled=True) def run_mtbf(self): mtbf.main(testvars=self.options.testvars, **self.kwargs) def execute(self): self.marionette.cleanup() self.marionette = Marionette(device_serial=self.serial, port=self.port) self.marionette.wait_for_port() # run test runner here self.remove_settings_opt() self.kwargs = {} if self.port: self.kwargs['address'] = "localhost:" + str(self.port) logger.info("Using address[localhost:" + str(self.port) + "]") self.mtbf_daily() self.run_mtbf() def pre_flash(self): pass def flash(self): self.shallow_flash() self.full_flash() # workaround for waiting for boot def post_flash(self): self.setup() self.check_version() self.change_memory() self.add_7mobile_action() self.enable_certified_apps_debug() self.patch_marionette() def output_crash_report_no_to_log(self, serial): if serial in CrashScan.get_current_all_dev_serials(): crash_result = CrashScan.get_crash_no_by_serial(serial) if crash_result['crashNo'] > 0: logger.error("CrashReportFound: device " + serial + " has " + str(crash_result['crashNo']) + " crashes.") else: logger.info("CrashReportNotFound: No crash report found in device " + serial) else: logger.error("CrashReportAdbError: Can't find device in ADB list") def collect_report(self, serial): self.output_crash_report_no_to_log(serial) def run(self): try: if self.get_free_device(): self.mtbf_options() self.pre_flash() self.flash() self.device_obj.create_adb_forward() self.port = self.device_obj.adb_forwarded_port self.post_flash() self.execute() self.collect_report(self.serial) finally: self.release()
def do_login(args): mc = get_marionette(args) device = GaiaDevice(mc) apps = GaiaApps(mc) data_layer = GaiaData(mc) mc.setup_touch() _persona_frame_locator = ('css selector', "iframe") # Trusty UI on home screen _tui_container_locator = ('id', 'trustedui-frame-container') # Persona dialog _waiting_locator = ('css selector', 'body.waiting') _email_input_locator = ('id', 'authentication_email') _password_input_locator = ('id', 'authentication_password') _new_password = ('id', 'password') _verify_new_password = ('id', 'vpassword') _next_button_locator = ('css selector', 'button.start') _verify_start_button = ('css selector', 'button#verify_user') _returning_button_locator = ('css selector', 'button.returning') _sign_in_button_locator = ('id', 'signInButton') _this_session_only_button_locator = ('id', 'this_is_not_my_computer') # Switch to top level frame then Persona frame mc.switch_to_frame() wait_for_element_present(mc, *_tui_container_locator) trustyUI = mc.find_element(*_tui_container_locator) wait_for_condition( mc, lambda m: trustyUI.find_element(*_persona_frame_locator)) personaDialog = trustyUI.find_element(*_persona_frame_locator) mc.switch_to_frame(personaDialog) try: ready = mc.find_element(*_email_input_locator).is_displayed() except NoSuchElementException: ready = False if not ready: print 'Persona email input is not present.' print 'Are you on a new login screen?' return done = False while not done: username = raw_input('Persona username: '******'password: '******'Not a new account? Trying to log in to existing account' # Logging into an exisiting account: password_field = mc.find_element(*_password_input_locator) password_field.send_keys(password) wait_for_element_displayed(mc, *_returning_button_locator) mc.tap(mc.find_element(*_returning_button_locator)) #.click() print 'You should be logged in now'
def set_up_device(args): mc = get_marionette(args) device = GaiaDevice(mc) try: device.restart_b2g() except Exception: print ' ** Check to make sure you don\'t have desktop B2G running' raise apps = GaiaApps(mc) data_layer = GaiaData(mc) lockscreen = LockScreen(mc) lockscreen.unlock() apps.kill_all() if args.wifi_ssid: print 'Configuring WiFi' if not args.wifi_key or not args.wifi_pass: args.error('Missing --wifi_key or --wifi_pass option') args.wifi_key = args.wifi_key.upper() data_layer.enable_wifi() if args.wifi_key == 'WPA-PSK': pass_key = 'psk' elif args.wifi_key == 'WEP': pass_key = 'wep' else: args.error('not sure what key to use for %r' % args.wifi_key) data = {'ssid': args.wifi_ssid, 'keyManagement': args.wifi_key, pass_key: args.wifi_pass} data_layer.connect_to_wifi(data) for manifest in args.apps: # There is probably a way easier way to do this by adb pushing # something. Send me a patch! mc.switch_to_frame() try: data = requests.get(manifest).json() app_name = data['name'] all_apps = set(a['manifest']['name'] for a in get_installed(apps)) if app_name not in all_apps: print 'Installing %s from %s' % (app_name, manifest) mc.execute_script('navigator.mozApps.install("%s");' % manifest) wait_for_element_displayed(mc, 'id', 'app-install-install-button') mc.find_element('id', 'app-install-install-button').tap() wait_for_element_not_displayed(mc, 'id', 'app-install-install-button') # confirm that app got installed homescreen_frame = mc.find_element(*('css selector', 'div.homescreen iframe')) mc.switch_to_frame(homescreen_frame) _app_icon_locator = ('xpath', "//li[@class='icon']//span[text()='%s']" % app_name) try: mc.find_element(*_app_icon_locator) except NoSuchElementException: args.error('Error: app could not be installed.') except Exception, exc: print ' ** installing manifest %s failed (maybe?)' % manifest print ' ** error: %s: %s' % (exc.__class__.__name__, exc) continue
def run(self, script, address='localhost:2828', symbols=None, treeherder='https://treeherder.mozilla.org/', reset=False, **kwargs): try: host, port = address.split(':') except ValueError: raise ValueError('--address must be in the format host:port') # Check that Orangutan is installed self.adb_device = ADBDevice(self.device_serial) orng_path = posixpath.join('data', 'local', 'orng') if not self.adb_device.exists(orng_path): raise Exception('Orangutan not found! Please install it according ' 'to the documentation.') self.runner = B2GDeviceRunner(serial=self.device_serial, process_args={'stream': None}, symbols_path=symbols, logdir=self.temp_dir) if reset: self.runner.start() else: self.runner.device.connect() port = self.runner.device.setup_port_forwarding(remote_port=port) assert self.runner.device.wait_for_port(port), \ 'Timed out waiting for port!' marionette = Marionette(host=host, port=port) marionette.start_session() try: marionette.set_context(marionette.CONTEXT_CHROME) self.is_debug = marionette.execute_script( 'return Components.classes["@mozilla.org/xpcom/debug;1"].' 'getService(Components.interfaces.nsIDebug2).isDebugBuild;') marionette.set_context(marionette.CONTEXT_CONTENT) if reset: gaia_device = GaiaDevice(marionette) gaia_device.wait_for_b2g_ready(timeout=120) gaia_device.unlock() gaia_apps = GaiaApps(marionette) gaia_apps.kill_all() # TODO: Disable bluetooth, emergency calls, carrier, etc # Run Orangutan script remote_script = posixpath.join(self.adb_device.test_root, 'orng.script') self.adb_device.push(script, remote_script) self.start_time = time.time() # TODO: Kill remote process on keyboard interrupt self.adb_device.shell( '%s %s %s' % (orng_path, self.device_properties['input'], remote_script)) self.end_time = time.time() self.adb_device.rm(remote_script) except (MarionetteException, IOError): if self.runner.crashed: # Crash has been detected pass else: raise self.runner.check_for_crashes(test_name='b2gmonkey') # Report results to Treeherder required_envs = ['TREEHERDER_KEY', 'TREEHERDER_SECRET'] if all([os.environ.get(v) for v in required_envs]): self.post_to_treeherder(script, treeherder) else: self._logger.info( 'Results will not be posted to Treeherder. Please set the ' 'following environment variables to enable Treeherder ' 'reports: %s' % ', '.join([v for v in required_envs if not os.environ.get(v)]))
def set_up_device(args): mc = get_marionette(args) device = GaiaDevice(mc) try: device.restart_b2g() except Exception: print ' ** Check to make sure you don\'t have desktop B2G running' raise apps = GaiaApps(mc) data_layer = GaiaData(mc) lockscreen = LockScreen(mc) mc.setup_touch() lockscreen.unlock() apps.kill_all() if args.wifi_ssid: print 'Configuring WiFi' if not args.wifi_key or not args.wifi_pass: args.error('Missing --wifi_key or --wifi_pass option') args.wifi_key = args.wifi_key.upper() data_layer.enable_wifi() if args.wifi_key == 'WPA-PSK': pass_key = 'psk' elif args.wifi_key == 'WEP': pass_key = 'wep' else: args.error('not sure what key to use for %r' % args.wifi_key) data = { 'ssid': args.wifi_ssid, 'keyManagement': args.wifi_key, pass_key: args.wifi_pass } data_layer.connect_to_wifi(data) for manifest in args.apps: # There is probably a way easier way to do this by adb pushing # something. Send me a patch! mc.switch_to_frame() try: data = requests.get(manifest).json() app_name = data['name'] all_apps = set(a['manifest']['name'] for a in get_installed(apps)) if app_name not in all_apps: print 'Installing %s from %s' % (app_name, manifest) mc.execute_script('navigator.mozApps.install("%s");' % manifest) wait_for_element_displayed(mc, 'id', 'app-install-install-button') yes = mc.find_element('id', 'app-install-install-button') mc.tap(yes) # This still works but the id check broke. # See https://bugzilla.mozilla.org/show_bug.cgi?id=853878 wait_for_element_displayed(mc, 'id', 'system-banner') except Exception, exc: print ' ** installing manifest %s failed (maybe?)' % manifest print ' ** error: %s: %s' % (exc.__class__.__name__, exc) continue
def run(self, script, address='localhost:2828', symbols=None, treeherder='https://treeherder.mozilla.org/', reset=False, **kwargs): try: host, port = address.split(':') except ValueError: raise ValueError('--address must be in the format host:port') # Check that Orangutan is installed self.adb_device = ADBDevice(self.device_serial) orng_path = posixpath.join('data', 'local', 'orng') if not self.adb_device.exists(orng_path): raise Exception('Orangutan not found! Please install it according ' 'to the documentation.') self.runner = B2GDeviceRunner( serial=self.device_serial, process_args={'stream': None}, symbols_path=symbols, logdir=self.temp_dir) if reset: self.runner.start() else: self.runner.device.connect() port = self.runner.device.setup_port_forwarding(remote_port=port) assert self.runner.device.wait_for_port(port), \ 'Timed out waiting for port!' marionette = Marionette(host=host, port=port) marionette.start_session() try: marionette.set_context(marionette.CONTEXT_CHROME) self.is_debug = marionette.execute_script( 'return Components.classes["@mozilla.org/xpcom/debug;1"].' 'getService(Components.interfaces.nsIDebug2).isDebugBuild;') marionette.set_context(marionette.CONTEXT_CONTENT) if reset: gaia_device = GaiaDevice(marionette) gaia_device.wait_for_b2g_ready(timeout=120) gaia_device.unlock() gaia_apps = GaiaApps(marionette) gaia_apps.kill_all() # TODO: Disable bluetooth, emergency calls, carrier, etc # Run Orangutan script remote_script = posixpath.join(self.adb_device.test_root, 'orng.script') self.adb_device.push(script, remote_script) self.start_time = time.time() # TODO: Kill remote process on keyboard interrupt self.adb_device.shell('%s %s %s' % (orng_path, self.device_properties['input'], remote_script)) self.end_time = time.time() self.adb_device.rm(remote_script) except (MarionetteException, IOError): if self.runner.crashed: # Crash has been detected pass else: raise self.runner.check_for_crashes(test_name='b2gmonkey') # Report results to Treeherder required_envs = ['TREEHERDER_KEY', 'TREEHERDER_SECRET'] if all([os.environ.get(v) for v in required_envs]): self.post_to_treeherder(script, treeherder) else: self._logger.info( 'Results will not be posted to Treeherder. Please set the ' 'following environment variables to enable Treeherder ' 'reports: %s' % ', '.join([ v for v in required_envs if not os.environ.get(v)]))
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script("GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error("couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element("id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception("Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script("return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script( "GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error( "couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element( "id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception( "Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script( "return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False