Beispiel #1
0
 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")
Beispiel #2
0
 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)
Beispiel #3
0
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()
Beispiel #4
0
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})
Beispiel #5
0
    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))
Beispiel #6
0
    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)
Beispiel #7
0
 def display_error(self, scenario, customization):
     logger.error("%s x %s failed." % (scenario, customization),
                  exc_info=True)
     if self.strict:
         raise
Beispiel #8
0
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)
Beispiel #9
0
 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"
Beispiel #10
0
    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