def onerror(error): logger.info("Failed to get the profile.") if os.path.exists(downloaded_archive): try: os.remove(downloaded_archive) except Exception: logger.error("Could not remove the file")
def dump_logs(self): logger.info("Dumping Android logs") try: logcat = self.device.get_logcat() if logcat: # local path, not using posixpath logfile = os.path.join(self.archive, "logcat.log") logger.info("Writing logcat at %s" % logfile) with open(logfile, "wb") as f: for line in logcat: f.write(line.encode("utf8", errors="replace") + b"\n") else: logger.info("logcat came back empty") except Exception: logger.error("Could not extract the logcat", exc_info=True)
def _retries(callable, onerror=None): retries = 0 pause = RETRY_PAUSE while retries < RETRIES: try: return callable() except Exception as e: if onerror is not None: onerror(e) logger.info("Failed, retrying") retries += 1 time.sleep(pause) pause *= 1.5 # If we reach that point, it means all attempts failed logger.error("All attempt failed") raise RetriesError()
async def execute_async_script(session, script, *args): # switch to the right context if needed current_context = await session._request(url="/moz/context", method="GET") if current_context != "chrome": logger.info("Switching to chrome context") await session._request(url="/moz/context", method="POST", data={"context": "chrome"}) switch_back = True else: switch_back = False logger.info("Setting up script timeout") await session._request(url="/timeouts", method="POST", data={"script": _SCRIPT_TIMEOUT}) try: attempts = 0 while True: logger.info("Running triggerSync()") try: return await session._request( url="/execute/async", method="POST", data={ "script": script, "args": list(args) }, ) except Exception as e: attempts += 1 logger.error("The script failed.", exc_info=True) if attempts > 2: return { "result": 1, "result_message": str(e), "result_exc": e, "logs": {}, } finally: if switch_back: await session._request(url="/moz/context", method="POST", data={"context": current_context})
async def switch(self): if self._mobile: return try: if self.handles is None: self.handles = await self.session.get_window_handles() self.current = 0 except Exception: logger.error("Could not get window handles") return handle = self.handles[self.current] if self.current == len(self.handles) - 1: self.current = 0 else: self.current += 1 try: await self.session.switch_to_window(handle) except Exception: logger.error("Could not switch to handle %s" % str(handle))
def prepare(self, profile, logfile): self._set_adb_logger(logfile) try: # See android_emulator_pgo.py run_tests for more # details on why test_root must be /sdcard/test_root # for android pgo due to Android 4.3. self.device = ADBDeviceFactory(verbose=self.verbose, logger_name="adb", test_root="/sdcard/test_root") except Exception: logger.error("Cannot initialize device") raise device = self.device self.profile = profile # checking that the app is installed if not device.is_app_installed(self.app_name): raise Exception("%s is not installed" % self.app_name) # debug flag logger.info("Setting %s as the debug app on the phone" % self.app_name) device.shell( "am set-debug-app --persistent %s" % self.app_name, stdout_callback=logger.info, ) # creating the profile on the device logger.info("Creating the profile on the device") remote_test_root = posixpath.join(device.test_root, "condprof") remote_profile = posixpath.join(remote_test_root, "profile") logger.info("The profile on the phone will be at %s" % remote_profile) device.rm(remote_test_root, force=True, recursive=True) device.mkdir(remote_test_root) device.rm(remote_profile, force=True, recursive=True) logger.info("Pushing %s on the phone" % self.profile) device.push(profile, remote_profile) device.chmod(remote_profile, recursive=True) self.profile = profile self.remote_profile = remote_profile # creating the yml file yml_data = { "args": ["-marionette", "-profile", self.remote_profile], "prefs": DEFAULT_PREFS, "env": { "LOG_VERBOSE": 1, "R_LOG_LEVEL": 6, "MOZ_LOG": "" }, } yml_name = "%s-geckoview-config.yaml" % self.app_name yml_on_host = posixpath.join(tempfile.mkdtemp(), yml_name) write_yml_file(yml_on_host, yml_data) tmp_on_device = posixpath.join("/data", "local", "tmp") if not device.exists(tmp_on_device): raise IOError("%s does not exists on the device" % tmp_on_device) yml_on_device = posixpath.join(tmp_on_device, yml_name) try: device.rm(yml_on_device, force=True, recursive=True) device.push(yml_on_host, yml_on_device) device.chmod(yml_on_device, recursive=True) except Exception: logger.info( "could not create the yaml file on device. Permission issue?") raise # command line 'extra' args not used with geckoview apps; instead we use # an on-device config.yml file intent = "android.intent.action.VIEW" device.stop_application(self.app_name) if self.fennec: # XXX does the Fennec app picks up the YML file ? extra_args = [ "-profile", self.remote_profile, "--es", "env0", "LOG_VERBOSE=1", "--es", "env1", "R_LOG_LEVEL=6", "--es", "env2", "MOZ_WEBRENDER=0", ] device.launch_fennec( self.app_name, extra_args=extra_args, url="about:blank", fail_if_running=False, ) else: device.launch_application(self.app_name, self.activity, intent, extras=None, url="about:blank") if not device.process_exist(self.app_name): raise Exception("Could not start %s" % self.app_name) logger.info("Creating socket forwarding on port %d" % self.marionette_port) device.forward( local="tcp:%d" % self.marionette_port, remote="tcp:%d" % self.marionette_port, ) # we don't have a clean way for now to check that GV or Fenix # is ready to handle our tests. So here we just wait 30s logger.info("Sleeping for 30s") time.sleep(30)
def display_error(self, scenario, customization): logger.error("%s x %s failed." % (scenario, customization), exc_info=True) if self.strict: raise
def get_profile( target_dir, platform, scenario, customization="default", task_id=None, download_cache=True, repo="mozilla-central", ): """Extract a conditioned profile in the target directory. If task_id is provided, will grab the profile from that task. when not provided (default) will grab the latest profile. """ # XXX assert values params = { "platform": platform, "scenario": scenario, "customization": customization, "task_id": task_id, "repo": repo, } logger.info("Getting conditioned profile with arguments: %s" % params) filename = ARTIFACT_NAME % params if task_id is None: url = TC_LINK % params + filename else: url = DIRECT_LINK % params + filename logger.info("preparing download dir") if not download_cache: download_dir = tempfile.mkdtemp() else: # using a cache dir in the user home dir download_dir = os.path.expanduser(CONDPROF_CACHE) if not os.path.exists(download_dir): os.makedirs(download_dir) downloaded_archive = os.path.join(download_dir, filename) logger.info("Downloaded archive path: %s" % downloaded_archive) retries = 0 while retries < RETRIES: try: logger.info("Getting %s" % url) try: archive = download_file(url, target=downloaded_archive) except ArchiveNotFound: raise ProfileNotFoundError(url) try: with tarfile.open(archive, "r:gz") as tar: logger.info("Extracting the tarball content in %s" % target_dir) size = len(list(tar)) with progress.Bar(expected_size=size) as bar: def _extract(self, *args, **kw): if not TASK_CLUSTER: bar.show(bar.last_progress + 1) return self.old(*args, **kw) tar.old = tar.extract tar.extract = functools.partial(_extract, tar) tar.extractall(target_dir) except (OSError, tarfile.ReadError) as e: logger.info("Failed to extract the tarball") if download_cache and os.path.exists(archive): logger.info("Removing cached file to attempt a new download") os.remove(archive) raise ProfileNotFoundError(str(e)) finally: if not download_cache: shutil.rmtree(download_dir) logger.info("Success, we have a profile to work with") return target_dir except Exception: logger.info("Failed to get the profile.") retries += 1 if os.path.exists(downloaded_archive): try: os.remove(downloaded_archive) except Exception: logger.error("Could not remove the file") time.sleep(RETRY_PAUSE) # If we reach that point, it means all attempts failed logger.error("All attempt failed") raise ProfileNotFoundError(url)
def get_browser_version(self): try: return get_version(self.firefox) except Exception: logger.error("Could not get Firefox version", exc_info=True) return "unknown"
async def build_profile(self, device, headless): scenario = self.scenario profile = self.env.profile customization_data = self.customization_data scenario_func = scenarii[scenario] if scenario in customization_data.get("scenario", {}): options = customization_data["scenario"][scenario] logger.info("Loaded options for that scenario %s" % str(options)) else: options = {} # Adding general options options["platform"] = self.env.target_platform if not self.force_new: try: custom_name = customization_data["name"] get_profile(profile, self.env.target_platform, scenario, custom_name) except ProfileNotFoundError: # XXX we'll use a fresh profile for now fresh_profile(profile, customization_data) else: fresh_profile(profile, customization_data) logger.info("Updating profile located at %r" % profile) metadata = Metadata(profile) logger.info("Starting the Gecko app...") adb_logs = self._log_filename("adb") self.env.prepare(logfile=adb_logs) geckodriver_logs = self._log_filename("geckodriver") logger.info("Writing geckodriver logs in %s" % geckodriver_logs) step = START try: firefox_instance = Firefox(**self.env.get_browser_args(headless)) step = INIT_GECKODRIVER with open(geckodriver_logs, "w") as glog: geckodriver = self.env.get_geckodriver(log_file=glog) step = START_SESSION async with get_session(geckodriver, firefox_instance) as session: step = START_SCENARIO self.env.check_session(session) logger.info("Running the %s scenario" % scenario) metadata.update(await scenario_func(session, options)) logger.info("%s scenario done." % scenario) await close_extra_windows(session) except Exception: logger.error("%s scenario broke!" % scenario) if step == START: logger.info("Could not initialize the browser") elif step == INIT_GECKODRIVER: logger.info("Could not initialize Geckodriver") elif step == START_SESSION: logger.info("Could not start the session, check %s first" % geckodriver_logs) else: logger.info( "Could not run the scenario, probably a faulty scenario") raise finally: self.env.stop_browser() for logfile in (adb_logs, geckodriver_logs): if os.path.exists(logfile): obfuscate_file(logfile) self.env.collect_profile() # writing metadata metadata.write( name=self.scenario, customization=self.customization_data["name"], version=self.env.get_browser_version(), platform=self.env.target_platform, ) logger.info("Profile at %s.\nDone." % profile) return metadata