Beispiel #1
0
def launch_control_center():
    profile_path = os.path.join(get_core_args().workdir, 'cc_profile')
    fx_path = PathManager.get_local_firefox_path()
    if fx_path is None:
        logger.error(
            'Can\'t find local Firefox installation, aborting Iris run.')
        return False, None

    args = ['http://127.0.0.1:%s' % get_core_args().port]
    process_args = {'stream': None}
    profile = MozProfile(profile=profile_path,
                         preferences=Settings.default_fx_prefs)
    fx_runner = FirefoxRunner(binary=fx_path,
                              profile=profile,
                              cmdargs=args,
                              process_args=process_args)
    fx_runner.start()
    server = LocalWebServer(get_core_args().workdir, get_core_args().port)
    server.stop()
    time.sleep(Settings.DEFAULT_UI_DELAY)

    if OSHelper.is_mac():
        type(text='q', modifier=KeyModifier.CMD)
    elif OSHelper.is_windows():
        type(text='w', modifier=[KeyModifier.CTRL, KeyModifier.SHIFT])
    else:
        type(text='q', modifier=KeyModifier.CTRL)

    try:
        fx_runner.stop()
    except Exception as e:
        logger.debug('Error stopping fx_runner')
        logger.debug(e)

    return server.result
Beispiel #2
0
def launch_control_center():
    profile_path = os.path.join(PathManager.get_working_dir(), "cc_profile")
    fx_path = PathManager.get_local_firefox_path()
    if fx_path is None:
        logger.error("Can't find local Firefox installation, aborting Iris run.")
        return False, None

    args = ["http://127.0.0.1:%s" % get_core_args().port]
    process_args = {"stream": None}
    profile = MozProfile(profile=profile_path, preferences=get_fx_prefs())
    if OSHelper.is_windows():
        process = subprocess.Popen(
            [
                fx_path,
                "-no-remote",
                "-new-tab",
                args,
                "--wait-for-browser",
                "-foreground",
                "-profile",
                profile.profile,
            ],
            shell=False,
        )

    else:
        fx_runner = FirefoxRunner(
            binary=fx_path, profile=profile, cmdargs=args, process_args=process_args
        )
        fx_runner.start()
    logger.debug("Launching web server for directory %s" % PathManager.get_working_dir())
    server = LocalWebServer(PathManager.get_working_dir(), get_core_args().port)
    server.stop()
    time.sleep(Settings.DEFAULT_UI_DELAY)

    if OSHelper.is_mac():
        type(text="q", modifier=KeyModifier.CMD)
    elif OSHelper.is_windows():
        type(text="w", modifier=[KeyModifier.CTRL, KeyModifier.SHIFT])
    else:
        type(text="q", modifier=KeyModifier.CTRL)
    if OSHelper.is_windows():
        if process.pid is not None:
            try:
                logger.debug("Closing Firefox process ID: %s" % process.pid)
                process = psutil.Process(process.pid)
                for proc in process.children(recursive=True):
                    proc.kill()
                process.kill()
            except psutil.NoSuchProcess:
                pass
    else:
        try:
            fx_runner.stop()
        except Exception as e:
            logger.debug("Error stopping fx_runner")
            logger.debug(e)

    return server.result
Beispiel #3
0
def launch_control_center():
    profile_path = os.path.join(get_core_args().workdir, 'cc_profile')
    fx_path = PathManager.get_local_firefox_path()
    if fx_path is None:
        logger.error(
            'Can\'t find local Firefox installation, aborting Iris run.')
        return False, None

    args = ['http://127.0.0.1:%s' % get_core_args().port]
    process_args = {'stream': None}
    profile = MozProfile(profile=profile_path,
                         preferences=Settings.default_fx_prefs)
    if OSHelper.is_windows():
        process = subprocess.Popen([
            fx_path, '-no-remote', '-new-tab', args, '--wait-for-browser',
            '-foreground', '-profile', profile.profile
        ],
                                   shell=False)

    else:
        fx_runner = FirefoxRunner(binary=fx_path,
                                  profile=profile,
                                  cmdargs=args,
                                  process_args=process_args)
        fx_runner.start()

    server = LocalWebServer(get_core_args().workdir, get_core_args().port)
    server.stop()
    time.sleep(Settings.DEFAULT_UI_DELAY)

    if OSHelper.is_mac():
        type(text='q', modifier=KeyModifier.CMD)
    elif OSHelper.is_windows():
        type(text='w', modifier=[KeyModifier.CTRL, KeyModifier.SHIFT])
    else:
        type(text='q', modifier=KeyModifier.CTRL)
    if OSHelper.is_windows():
        if process.pid is not None:
            try:
                logger.debug('Closing Firefox process ID: %s' % process.pid)
                process = psutil.Process(process.pid)
                for proc in process.children(recursive=True):
                    proc.kill()
                process.kill()
            except psutil.NoSuchProcess:
                pass
    else:
        try:
            fx_runner.stop()
        except Exception as e:
            logger.debug('Error stopping fx_runner')
            logger.debug(e)

    return server.result
Beispiel #4
0
class FirefoxBrowser(Browser):
    used_ports = set()
    init_timeout = 60

    def __init__(self, logger, binary, prefs_root, debug_info=None,
                 symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                 ca_certificate_path=None, e10s=False):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.marionette_port = None
        self.runner = None
        self.debug_info = debug_info
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary
        self.e10s = e10s

    def start(self):
        self.marionette_port = get_free_port(2828, exclude=self.used_ports)
        self.used_ports.add(self.marionette_port)

        env = os.environ.copy()
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"

        locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(locations=locations,
                                      preferences=preferences)
        self.profile.set_preferences({"marionette.defaultPrefs.enabled": True,
                                      "marionette.defaultPrefs.port": self.marionette_port,
                                      "dom.disable_open_during_load": False,
                                      "network.dns.localDomains": ",".join(hostnames),
                                      "network.proxy.type": 0,
                                      "places.history.enabled": False})
        if self.e10s:
            self.profile.set_preferences({"browser.tabs.remote.autostart": True})

        # Bug 1262954: winxp + e10s, disable hwaccel
        if (self.e10s and platform.system() in ("Windows", "Microsoft") and
            '5.1' in platform.version()):
            self.profile.set_preferences({"layers.acceleration.disabled": True})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        debug_args, cmd = browser_command(self.binary, [cmd_arg("marionette"), "about:blank"],
                                          self.debug_info)

        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd[0],
                                    cmdargs=cmd[1:],
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")

        self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            preferences = Preferences.read_prefs(prefs_path)
        else:
            self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
            preferences = []

        return preferences

    def stop(self):
        self.logger.debug("Stopping browser")
        if self.runner is not None:
            try:
                self.runner.stop()
            except OSError:
                # This can happen on Windows if the process is already dead
                pass

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        self.logger.process_output(self.pid(),
                                   line.decode("utf8", "replace"),
                                   command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def log_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        mozcrash.log_crashes(self.logger,
                             dump_dir,
                             symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process,
                             test=test)

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary)
        if mozinfo.isMac:
            env_var = "DYLD_LIBRARY_PATH"
        elif mozinfo.isUnix:
            env_var = "LD_LIBRARY_PATH"
        else:
            env_var = "PATH"


        env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
                        if env_var in env else certutil_dir).encode(
                                sys.getfilesystemencoding() or 'utf-8', 'replace')

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output("certutil",
                                       subprocess.check_output(cmd,
                                                               env=env,
                                                               stderr=subprocess.STDOUT),
                                       " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,",
                 "-n", "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
Beispiel #5
0
class B2GDesktopReftest(RefTest):
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert (self.marionette.wait_for_port())
        self.marionette.start_session()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, test_path, options):
        reftestlist = self.getManifestPath(test_path)
        if not reftestlist.startswith('file://'):
            reftestlist = 'file://%s' % reftestlist

        self.profile = self.create_profile(options,
                                           reftestlist,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = {
            'processOutputLine': [self._on_output],
            'onTimeout': [self._on_timeout],
            'kill_on_timeout': False
        }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(
            options.app,
            ignore_window_size=options.ignoreWindowSize,
            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d", os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s", self.last_test,
                     -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, reftestlist, profile_to_clone=None):
        profile = RefTest.createReftestProfile(
            self, options, reftestlist, profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        prefs[
            "b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html"
        prefs[
            "b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp"
        prefs["browser.tabs.remote"] = False
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs[
            "network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        prefs["reftest.uri"] = "%s" % reftestlist
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self,
                           app,
                           ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])
        return cmd, args

    def _on_output(self, line):
        print(line)
        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)
Beispiel #6
0
class FirefoxBrowser(Browser):
    used_ports = set()
    init_timeout = 70
    shutdown_timeout = 70

    def __init__(self,
                 logger,
                 binary,
                 prefs_root,
                 test_type,
                 extra_prefs=None,
                 debug_info=None,
                 symbols_path=None,
                 stackwalk_binary=None,
                 certutil_binary=None,
                 ca_certificate_path=None,
                 e10s=False,
                 lsan_dir=None,
                 stackfix_dir=None,
                 binary_args=None,
                 timeout_multiplier=None,
                 leak_check=False,
                 asan=False,
                 stylo_threads=1,
                 chaos_mode_flags=None,
                 config=None,
                 browser_channel="nightly",
                 headless=None,
                 **kwargs):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.test_type = test_type
        self.extra_prefs = extra_prefs
        self.marionette_port = None
        self.runner = None
        self.debug_info = debug_info
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary
        self.e10s = e10s
        self.binary_args = binary_args
        self.config = config
        if stackfix_dir:
            self.stack_fixer = get_stack_fixer_function(
                stackfix_dir, self.symbols_path)
        else:
            self.stack_fixer = None

        if timeout_multiplier:
            self.init_timeout = self.init_timeout * timeout_multiplier

        self.asan = asan
        self.lsan_dir = lsan_dir
        self.lsan_allowed = None
        self.lsan_max_stack_depth = None
        self.mozleak_allowed = None
        self.mozleak_thresholds = None
        self.leak_check = leak_check
        self.leak_report_file = None
        self.lsan_handler = None
        self.stylo_threads = stylo_threads
        self.chaos_mode_flags = chaos_mode_flags
        self.browser_channel = browser_channel
        self.headless = headless

    def settings(self, test):
        return {
            "check_leaks": self.leak_check and not test.leaks,
            "lsan_allowed": test.lsan_allowed,
            "lsan_max_stack_depth": test.lsan_max_stack_depth,
            "mozleak_allowed": self.leak_check and test.mozleak_allowed,
            "mozleak_thresholds": self.leak_check and test.mozleak_threshold
        }

    def start(self, group_metadata=None, **kwargs):
        if group_metadata is None:
            group_metadata = {}

        self.group_metadata = group_metadata
        self.lsan_allowed = kwargs.get("lsan_allowed")
        self.lsan_max_stack_depth = kwargs.get("lsan_max_stack_depth")
        self.mozleak_allowed = kwargs.get("mozleak_allowed")
        self.mozleak_thresholds = kwargs.get("mozleak_thresholds")

        if self.marionette_port is None:
            self.marionette_port = get_free_port(2828, exclude=self.used_ports)
            self.used_ports.add(self.marionette_port)

        if self.asan:
            self.lsan_handler = mozleak.LSANLeaks(
                self.logger,
                scope=group_metadata.get("scope", "/"),
                allowed=self.lsan_allowed,
                maxNumRecordedFrames=self.lsan_max_stack_depth)

        env = test_environment(xrePath=os.path.dirname(self.binary),
                               debugger=self.debug_info is not None,
                               log=self.logger,
                               lsanPath=self.lsan_dir)

        env["STYLO_THREADS"] = str(self.stylo_threads)
        if self.chaos_mode_flags is not None:
            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
        if self.headless:
            env["MOZ_HEADLESS"] = "1"

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(preferences=preferences)
        self.profile.set_preferences({
            "marionette.port":
            self.marionette_port,
            "network.dns.localDomains":
            ",".join(self.config.domains_set),
            "dom.file.createInChild":
            True,
            # TODO: Remove preferences once Firefox 64 is stable (Bug 905404)
            "network.proxy.type":
            0,
            "places.history.enabled":
            False,
            "network.preload":
            True,
        })
        if self.e10s:
            self.profile.set_preferences(
                {"browser.tabs.remote.autostart": True})

        if self.test_type == "reftest":
            self.profile.set_preferences(
                {"layout.interruptible-reflow.enabled": False})

        if self.leak_check:
            self.leak_report_file = os.path.join(
                self.profile.profile, "runtests_leaks_%s.log" % os.getpid())
            if os.path.exists(self.leak_report_file):
                os.remove(self.leak_report_file)
            env["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
        else:
            self.leak_report_file = None

        # Bug 1262954: winxp + e10s, disable hwaccel
        if (self.e10s and platform.system() in ("Windows", "Microsoft")
                and '5.1' in platform.version()):
            self.profile.set_preferences(
                {"layers.acceleration.disabled": True})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        args = self.binary_args[:] if self.binary_args else []
        args += [cmd_arg("marionette"), "about:blank"]

        debug_args, cmd = browser_command(self.binary, args, self.debug_info)

        self.runner = FirefoxRunner(
            profile=self.profile,
            binary=cmd[0],
            cmdargs=cmd[1:],
            env=env,
            process_class=ProcessHandler,
            process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")

        self.runner.start(debug_args=debug_args,
                          interactive=self.debug_info
                          and self.debug_info.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs = Preferences()

        pref_paths = []

        profiles = os.path.join(self.prefs_root, 'profiles.json')
        if os.path.isfile(profiles):
            with open(profiles, 'r') as fh:
                for name in json.load(fh)['web-platform-tests']:
                    if self.browser_channel in (None, 'nightly'):
                        pref_paths.append(
                            os.path.join(self.prefs_root, name, 'user.js'))
                    elif name != 'unittest-features':
                        pref_paths.append(
                            os.path.join(self.prefs_root, name, 'user.js'))
        else:
            # Old preference files used before the creation of profiles.json (remove when no longer supported)
            legacy_pref_paths = (
                os.path.join(
                    self.prefs_root,
                    'prefs_general.js'),  # Used in Firefox 60 and below
                os.path.join(self.prefs_root, 'common',
                             'user.js'),  # Used in Firefox 61
            )
            for path in legacy_pref_paths:
                if os.path.isfile(path):
                    pref_paths.append(path)

        for path in pref_paths:
            if os.path.exists(path):
                prefs.add(Preferences.read_prefs(path))
            else:
                self.logger.warning("Failed to find base prefs file in %s" %
                                    path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()

    def stop(self, force=False):
        if self.runner is not None and self.runner.is_running():
            try:
                # For Firefox we assume that stopping the runner prompts the
                # browser to shut down. This allows the leak log to be written
                for clean, stop_f in [
                    (True, lambda: self.runner.wait(self.shutdown_timeout)),
                    (False, lambda: self.runner.stop(signal.SIGTERM)),
                    (False, lambda: self.runner.stop(signal.SIGKILL))
                ]:
                    if not force or not clean:
                        retcode = stop_f()
                        if retcode is not None:
                            self.logger.info(
                                "Browser exited with return code %s" % retcode)
                            break
            except OSError:
                # This can happen on Windows if the process is already dead
                pass
        self.process_leaks()
        self.logger.debug("stopped")

    def process_leaks(self):
        self.logger.info("PROCESS LEAKS %s" % self.leak_report_file)
        if self.lsan_handler:
            self.lsan_handler.process()
        if self.leak_report_file is not None:
            mozleak.process_leak_log(self.leak_report_file,
                                     leak_thresholds=self.mozleak_thresholds,
                                     ignore_missing_leaks=["gmplugin"],
                                     log=self.logger,
                                     stack_fixer=self.stack_fixer,
                                     scope=self.group_metadata.get("scope"),
                                     allowed=self.mozleak_allowed)

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        if "GLib-GObject-CRITICAL" in line:
            return
        if line:
            data = line.decode("utf8", "replace")
            if self.stack_fixer:
                data = self.stack_fixer(data)
            if self.lsan_handler:
                data = self.lsan_handler.log(data)
            if data is not None:
                self.logger.process_output(self.pid(),
                                           data,
                                           command=" ".join(
                                               self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self, force=False):
        self.stop(force)

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def check_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        return bool(
            mozcrash.log_crashes(self.logger,
                                 dump_dir,
                                 symbols_path=self.symbols_path,
                                 stackwalk_binary=self.stackwalk_binary,
                                 process=process,
                                 test=test))

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""
        if self.certutil_binary is None:
            self.logger.info(
                "--certutil-binary not supplied; Firefox will not check certificates"
            )
            return

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary or self.certutil_binary)
        if mozinfo.isMac:
            env_var = "DYLD_LIBRARY_PATH"
        elif mozinfo.isUnix:
            env_var = "LD_LIBRARY_PATH"
        else:
            env_var = "PATH"

        env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
                        if env_var in env else certutil_dir).encode(
                            sys.getfilesystemencoding() or 'utf-8', 'replace')

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output(
                "certutil",
                subprocess.check_output(cmd, env=env,
                                        stderr=subprocess.STDOUT),
                " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,", "-n",
                 "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
Beispiel #7
0
class B2GDesktopReftest(RefTest):
    build_type = "desktop"
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert(self.marionette.wait_for_port())
        self.marionette.start_session()
        if self.build_type == "mulet":
            self._wait_for_homescreen(timeout=300)
            self._unlockScreen()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, tests, options):
        manifests = self.resolver.resolveManifests(options, tests)

        self.profile = self.create_profile(options, manifests,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = { 'processOutputLine': [self._on_output],
                      'onTimeout': [self._on_timeout],
                      'kill_on_timeout': False }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(options.app,
                            ignore_window_size=options.ignoreWindowSize,
                            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d",
                     os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s",
                         self.last_test, -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, manifests, profile_to_clone=None):
        profile = RefTest.createReftestProfile(self, options, manifests,
                                               profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        if not self.build_type == "mulet":
            # FIXME: With Mulet we can't set this values since Gaia won't launch
            prefs["b2g.system_startup_url"] = \
                    "app://test-container.gaiamobile.org/index.html"
            prefs["b2g.system_manifest_url"] = \
                    "app://test-container.gaiamobile.org/manifest.webapp"
        # Make sure we disable system updates
        prefs["app.update.enabled"] = False
        prefs["app.update.url"] = ""
        prefs["app.update.url.override"] = ""
        # Disable webapp updates
        prefs["webapps.update.enabled"] = False
        # Disable tiles also
        prefs["browser.newtabpage.directory.source"] = ""
        prefs["browser.newtabpage.directory.ping"] = ""
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999
        # Disable periodic updates of service workers
        prefs["dom.serviceWorkers.periodic-updates.enabled"] = False

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self, app, ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])

        if self.build_type == "mulet":
            args += ['-chrome', 'chrome://b2g/content/shell.html']
        return cmd, args

    def _on_output(self, line):
        sys.stdout.write("%s\n" % line)
        sys.stdout.flush()

        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)
Beispiel #8
0
class TestRunnerManager(threading.Thread):
    """Thread that owns a single TestRunner process and any processes required
    by the TestRunner (e.g. the Firefox binary)"""

    init_lock = threading.Lock()

    def __init__(self, server_url, firefox_binary, run_info, tests_queue,
                 stop_flag, runner_cls=TestharnessTestRunner,
                 marionette_port=None, process_cls=FirefoxProcess):
        self.http_server_url = server_url
        self.firefox_binary = firefox_binary
        self.tests_queue = tests_queue
        self.run_info = run_info
        self.stop_flag = stop_flag
        self.command_pipe = None
        self.firefox_runner = None
        self.test_runner_proc = None
        self.runner_cls = runner_cls
        self.marionette_port = marionette_port
        self.process_cls = process_cls
        threading.Thread.__init__(self)
        #This is started in the actual new thread
        self.logger = None
        #This may not really be what we want
        self.daemon = True
        self.setup_fail_count = 0
        self.max_setup_fails = 5
        self.init_timer = None

    def run(self):
        self.logger = structuredlog.getOutputLogger("WPT")
        self.init()
        while True:
            commands = {"test_ended":self.test_ended,
                        "setup_succeeded": self.setup_succeeded,
                        "setup_failed": self.setup_failed}
            has_data = self.command_pipe.poll(1)
            if has_data:
                command, data = self.command_pipe.recv()
                if commands[command](*data) is Stop:
                    break
            else:
                if self.stop_flag.is_set():
                    self.stop_runner(graceful=True)
                    break
                elif not self.test_runner_proc.is_alive():
                    #This happens when we run out of tests;
                    #We ask the runner to stop, it shuts itself
                    #down and then we end up here
                    #An alternate implementation strategy would be to have the
                    #runner signal that it is done just before it terminates
                    self.firefox_runner.stop()
                    break

    def init(self):
        #It seems that this lock is helpful to prevent some race that otherwise
        #sometimes stops the spawned processes initalising correctly, and
        #leaves this thread hung
        with self.init_lock:
            def init_failed():
                self.logger.error("Init failed")
                self.setup_failed()

            #TODO: make this timeout configurable
            self.init_timer = threading.Timer(30, self.setup_failed)
            self.init_timer.start()

            self.command_pipe, remote_end = Pipe()

            self.start_firefox()
            self.start_test_runner(remote_end)

    def start_firefox(self):
        env = os.environ.copy()
        env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'

        profile = Profile()
        profile.set_preferences({"marionette.defaultPrefs.enabled": True,
                                 "marionette.defaultPrefs.port": self.marionette_port,
                                 "dom.disable_open_during_load": False,
                                 "dom.max_script_run_time": 0})

        self.firefox_runner = FirefoxRunner(profile,
                                            self.firefox_binary,
                                            cmdargs=["--marionette"],
                                            env=env,
                                            kp_kwargs = {"processOutputLine":[self.on_output]},
                                            process_class=self.process_cls)
        self.logger.debug("Starting Firefox")
        self.firefox_runner.start()
        self.logger.debug("Firefox Started")

    def start_test_runner(self, remote_connection):
        self.test_runner_proc = Process(target=start_runner,
                                        args=(self.runner_cls,
                                              self.http_server_url,
                                              self.marionette_port,
                                              remote_connection))
        self.logger.debug("Starting test runner")
        self.test_runner_proc.start()
        self.logger.debug("Test runner started")

    def send_message(self, command, *args):
        self.command_pipe.send((command, args))

    def stop_runner(self, graceful=True):
        self.logger.debug("Stopping runner")
        if graceful:
            self.test_runner_proc.join(10)
            if self.test_runner_proc.is_alive():
                graceful = False
        self.firefox_runner.stop()
        if not graceful:
            self.test_runner_proc.terminate()
        self.logger.flush()
        self.command_pipe.close()

    def start_next_test(self):
        try:
            test = self.tests_queue.get(False)
        except Empty:
            logger.debug("No more tests")
            self.send_message("stop")
        else:
            self.logger.test_start(test.id)
            self.send_message("run_test", test)

    def test_ended(self, test, results):
        #It would be nice to move this down into the runner
        file_result, test_results = results
        for result in test_results:
            if test.disabled(self.run_info, result.name):
                continue
            expected = test.expected_status(self.run_info, result.name)
            self.logger.test_status(test.id,
                                    result.name,
                                    result.status,
                                    message=result.message,
                                    unexpected=expected != result.status)

        expected = test.expected_status(self.run_info)
        status = file_result.status if file_result.status != "EXTERNAL-TIMEOUT" else "TIMEOUT"
        self.logger.test_end(test.id,
                             status,
                             message=file_result.message,
                             unexpected=expected != status)
        #Restarting after a timeout is quite wasteful, but it seems otherwise we can get
        #results from the timed-out test back when we are waiting for the results of a
        #later test
        if file_result.status in ("CRASH", "EXTERNAL-TIMEOUT"):
            self.restart_runner()
        else:
            self.start_next_test()

    def setup_succeeded(self):
        self.init_timer.cancel()
        self.setup_fail_count = 0
        self.start_next_test()

    def setup_failed(self):
        self.init_timer.cancel()
        self.send_message("stop")
        self.setup_fail_count += 1
        if self.setup_fail_count < self.max_setup_fails:
            self.restart_runner()
        else:
            return Stop

    def restart_runner(self):
        self.stop_runner(graceful=False)
        self.init()

    def on_output(self, line):
        self.logger.process_output(line,
                                   self.firefox_runner.process_handler.pid,
                                   command=" ".join(self.firefox_runner.command))
class MuletReftest(RefTest):
    build_type = "mulet"
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert (self.marionette.wait_for_port())
        self.marionette.start_session()
        if self.build_type == "mulet":
            self._wait_for_homescreen(timeout=300)
            self._unlockScreen()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, tests, options):
        manifests = self.resolver.resolveManifests(options, tests)

        self.profile = self.create_profile(options,
                                           manifests,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = {
            'processOutputLine': [self._on_output],
            'onTimeout': [self._on_timeout],
            'kill_on_timeout': False
        }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(
            options.app,
            ignore_window_size=options.ignoreWindowSize,
            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d", os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s", self.last_test,
                     -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, manifests, profile_to_clone=None):
        profile = RefTest.createReftestProfile(
            self, options, manifests, profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        if not self.build_type == "mulet":
            # FIXME: With Mulet we can't set this values since Gaia won't launch
            prefs["b2g.system_startup_url"] = \
                    "app://test-container.gaiamobile.org/index.html"
            prefs["b2g.system_manifest_url"] = \
                    "app://test-container.gaiamobile.org/manifest.webapp"
        # Make sure we disable system updates
        prefs["app.update.enabled"] = False
        prefs["app.update.url"] = ""
        prefs["app.update.url.override"] = ""
        # Disable webapp updates
        prefs["webapps.update.enabled"] = False
        # Disable tiles also
        prefs["browser.newtabpage.directory.source"] = ""
        prefs["browser.newtabpage.directory.ping"] = ""
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs[
            "network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self,
                           app,
                           ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])

        if self.build_type == "mulet":
            args += ['-chrome', 'chrome://b2g/content/shell.html']
        return cmd, args

    def _on_output(self, line):
        sys.stdout.write("%s\n" % line)
        sys.stdout.flush()

        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)

    def _unlockScreen(self):
        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
        self.marionette.import_script(
            os.path.abspath(
                os.path.join(__file__, os.path.pardir, "gaia_lock_screen.js")))
        self.marionette.switch_to_frame()
        self.marionette.execute_async_script('GaiaLockScreen.unlock()')

    def _wait_for_homescreen(self, timeout):
        log.info("Waiting for home screen to load")
        Wait(self.marionette, timeout).until(
            expected.element_present(By.CSS_SELECTOR,
                                     '#homescreen[loading-state=false]'))
Beispiel #10
0
class FirefoxBrowser(Browser):
    used_ports = set()

    def __init__(self, logger, binary, prefs_root, debug_args=None, interactive=None,
                 symbols_path=None, stackwalk_binary=None):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.marionette_port = None
        self.used_ports.add(self.marionette_port)
        self.runner = None
        self.debug_args = debug_args
        self.interactive = interactive
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary

    def start(self):
        self.marionette_port = get_free_port(2828, exclude=self.used_ports)

        env = os.environ.copy()
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"

        locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))

        preferences = self.load_prefs()

        ports = {"http": "8000",
                 "https": "8443",
                 "ws": "8888"}

        self.profile = FirefoxProfile(locations=locations,
                                      proxy=ports,
                                      preferences=preferences)
        self.profile.set_preferences({"marionette.defaultPrefs.enabled": True,
                                      "marionette.defaultPrefs.port": self.marionette_port,
                                      "dom.disable_open_during_load": False})

        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=self.binary,
                                    cmdargs=[cmd_arg("marionette"), "about:blank"],
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")
        self.runner.start(debug_args=self.debug_args, interactive=self.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            preferences = Preferences.read_prefs(prefs_path)
        else:
            self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
            preferences = []

        return preferences

    def stop(self):
        self.logger.debug("Stopping browser")
        if self.runner is not None:
            try:
                self.runner.stop()
            except OSError:
                # This can happen on Windows if the process is already dead
                pass

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        self.logger.process_output(self.pid(),
                                   line.decode("utf8", "replace"),
                                   command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def log_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")
        mozcrash.log_crashes(self.logger, dump_dir, symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process, test=test)
Beispiel #11
0
class B2GDesktopReftest(RefTest):
    def __init__(self, marionette):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette = marionette
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        assert(self.marionette.wait_for_port())
        self.marionette.start_session()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, test_path, options):
        reftestlist = self.getManifestPath(test_path)
        if not reftestlist.startswith('file://'):
            reftestlist = 'file://%s' % reftestlist

        self.profile = self.create_profile(options, reftestlist,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = { 'processOutputLine': [self._on_output],
                      'onTimeout': [self._on_timeout],
                      'kill_on_timeout': False }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(options.app,
                            ignore_window_size=options.ignoreWindowSize)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    symbols_path=options.symbolsPath,
                                    kp_kwargs=kp_kwargs)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d",
                     os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s",
                         self.last_test, -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, reftestlist, profile_to_clone=None):
        profile = RefTest.createReftestProfile(self, options, reftestlist,
                                               profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        prefs["browser.homescreenURL"] = "app://test-container.gaiamobile.org/index.html"
        prefs["browser.manifestURL"] = "app://test-container.gaiamobile.org/manifest.webapp"
        prefs["browser.tabs.remote"] = False
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        prefs["reftest.uri"] = "%s" % reftestlist
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self, app, ignore_window_size=False):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])
        return cmd, args

    def _on_output(self, line):
        print(line)
        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)
class MuletReftest(RefTest):
    build_type = "mulet"
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert(self.marionette.wait_for_port())
        self.marionette.start_session()
        if self.build_type == "mulet":
            self._wait_for_homescreen(timeout=300)
            self._unlockScreen()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, tests, options):
        manifests = self.resolver.resolveManifests(options, tests)

        self.profile = self.create_profile(options, manifests,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)

        self._populate_logger(options)
        outputHandler = OutputHandler(self.log, options.utilityPath, symbolsPath=options.symbolsPath)

        kp_kwargs = { 'processOutputLine': [outputHandler],
                      'onTimeout': [self._on_timeout],
                      'kill_on_timeout': False }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        self.log.info("%s | Running tests: start." % os.path.basename(__file__))
        cmd, args = self.build_command_line(options.app,
                            ignore_window_size=options.ignoreWindowSize,
                            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            self.log.info("%s | Application pid: %d" % (
                     os.path.basename(__file__),
                     self.runner.process_handler.pid))

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            self.log.testFail("%s | application terminated with exit code %s" % (
                         self.last_test, status))
        elif status < 0:
            self.log.info("%s | application killed with signal %s" % (
                         self.last_test, -status))

        self.log.info("%s | Running tests: end." % os.path.basename(__file__))
        return status

    def create_profile(self, options, manifests, profile_to_clone=None):
        profile = RefTest.createReftestProfile(self, options, manifests,
                                               profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        if not self.build_type == "mulet":
            # FIXME: With Mulet we can't set this values since Gaia won't launch
            prefs["b2g.system_startup_url"] = \
                    "app://test-container.gaiamobile.org/index.html"
            prefs["b2g.system_manifest_url"] = \
                    "app://test-container.gaiamobile.org/manifest.webapp"
        # Make sure we disable system updates
        prefs["app.update.enabled"] = False
        prefs["app.update.url"] = ""
        # Disable webapp updates
        prefs["webapps.update.enabled"] = False
        # Disable tiles also
        prefs["browser.newtabpage.directory.source"] = ""
        prefs["browser.newtabpage.directory.ping"] = ""
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self, app, ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])

        if self.build_type == "mulet":
            args += ['-chrome', 'chrome://b2g/content/shell.html']
        return cmd, args

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        self.log.testFail(msg % (self.last_test, self.timeout))
        self.log.error("Force-terminating active process(es).");

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)

    def _unlockScreen(self):
        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
        self.marionette.import_script(os.path.abspath(
            os.path.join(__file__, os.path.pardir, "gaia_lock_screen.js")))
        self.marionette.switch_to_frame()
        self.marionette.execute_async_script('GaiaLockScreen.unlock()')

    def _wait_for_homescreen(self, timeout):
        self.log.info("Waiting for home screen to load")
        Wait(self.marionette, timeout).until(expected.element_present(
            By.CSS_SELECTOR, '#homescreen[loading-state=false]'))
Beispiel #13
0
        # Give last two lines of file a chance to write and send log file to fb_logs
        sleep(1)
        filename = logfile.name
        logfile.close()

        # Send log file to couchdb
        self.log.info("Sending log file to couchdb at '" + self.couchURI + "'")
        try:
            fb_logs.main(["--log", filename, "--database", self.databasename, "--couch", self.couchURI,
                             "--changeset", utils.get_changeset(self.appdir), "--section", self.section])
        except Exception:
            self.log.error("Log file not sent to couchdb at server: '" + self.couchURI + "' and database: '" + self.databasename)
            self.log.error(traceback.format_exc())

        # Cleanup
        mozRunner.stop()
        self.log.debug("Exiting - Status successful")
        self.cleanup()


# Called from the command line
def cli(argv=sys.argv[1:]):
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("--appname", dest="binary",
                      help="Firefox binary path")

    parser.add_option("--profile-path", dest="profile",
                      help="The profile to use when running Firefox")

    parser.add_option("-s", "--serverpath", dest="serverpath",
                      help="The http server containing the Firebug tests")
Beispiel #14
0
class FirefoxBrowser(Browser):
    used_ports = set()

    def __init__(self, logger, binary, prefs_root, debug_args=None, interactive=None,
                 symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                 ca_certificate_path=None):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.marionette_port = None
        self.used_ports.add(self.marionette_port)
        self.runner = None
        self.debug_args = debug_args
        self.interactive = interactive
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary

    def start(self):
        self.marionette_port = get_free_port(2828, exclude=self.used_ports)

        env = os.environ.copy()
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"

        locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))

        preferences = self.load_prefs()

        ports = {"http": "8000",
                 "https": "8443",
                 "ws": "8888"}

        self.profile = FirefoxProfile(locations=locations,
                                      proxy=ports,
                                      preferences=preferences)
        self.profile.set_preferences({"marionette.defaultPrefs.enabled": True,
                                      "marionette.defaultPrefs.port": self.marionette_port,
                                      "dom.disable_open_during_load": False})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=self.binary,
                                    cmdargs=[cmd_arg("marionette"), "about:blank"],
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")
        self.runner.start(debug_args=self.debug_args, interactive=self.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            preferences = Preferences.read_prefs(prefs_path)
        else:
            self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
            preferences = []

        return preferences

    def stop(self):
        self.logger.debug("Stopping browser")
        if self.runner is not None:
            try:
                self.runner.stop()
            except OSError:
                # This can happen on Windows if the process is already dead
                pass

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        self.logger.process_output(self.pid(),
                                   line.decode("utf8", "replace"),
                                   command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def log_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        mozcrash.log_crashes(self.logger,
                             dump_dir,
                             symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process,
                             test=test)

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary)
        env["LD_LIBRARY_PATH"] = certutil_dir
        env["PATH"] = os.path.pathsep.join([certutil_dir, env["PATH"]])

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output("certutil",
                                       subprocess.check_output(cmd,
                                                               env=env,
                                                               stderr=subprocess.STDOUT),
                                       " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,",
                 "-n", "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
class FirefoxBrowser(Browser):
    init_timeout = 70
    shutdown_timeout = 70

    def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                 symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                 ca_certificate_path=None, e10s=False, lsan_dir=None, stackfix_dir=None,
                 binary_args=None, timeout_multiplier=None, leak_check=False, asan=False,
                 stylo_threads=1, chaos_mode_flags=None, config=None, browser_channel="nightly", headless=None, **kwargs):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.test_type = test_type
        self.extra_prefs = extra_prefs
        self.marionette_port = None
        self.runner = None
        self.debug_info = debug_info
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary
        self.e10s = e10s
        self.binary_args = binary_args
        self.config = config
        if stackfix_dir:
            self.stack_fixer = get_stack_fixer_function(stackfix_dir,
                                                        self.symbols_path)
        else:
            self.stack_fixer = None

        if timeout_multiplier:
            self.init_timeout = self.init_timeout * timeout_multiplier

        self.asan = asan
        self.lsan_dir = lsan_dir
        self.lsan_allowed = None
        self.lsan_max_stack_depth = None
        self.mozleak_allowed = None
        self.mozleak_thresholds = None
        self.leak_check = leak_check
        self.leak_report_file = None
        self.lsan_handler = None
        self.stylo_threads = stylo_threads
        self.chaos_mode_flags = chaos_mode_flags
        self.browser_channel = browser_channel
        self.headless = headless

    def settings(self, test):
        return {"check_leaks": self.leak_check and not test.leaks,
                "lsan_allowed": test.lsan_allowed,
                "lsan_max_stack_depth": test.lsan_max_stack_depth,
                "mozleak_allowed": self.leak_check and test.mozleak_allowed,
                "mozleak_thresholds": self.leak_check and test.mozleak_threshold}

    def start(self, group_metadata=None, **kwargs):
        if group_metadata is None:
            group_metadata = {}

        self.group_metadata = group_metadata
        self.lsan_allowed = kwargs.get("lsan_allowed")
        self.lsan_max_stack_depth = kwargs.get("lsan_max_stack_depth")
        self.mozleak_allowed = kwargs.get("mozleak_allowed")
        self.mozleak_thresholds = kwargs.get("mozleak_thresholds")

        if self.marionette_port is None:
            self.marionette_port = get_free_port()

        if self.asan:
            self.lsan_handler = mozleak.LSANLeaks(self.logger,
                                                  scope=group_metadata.get("scope", "/"),
                                                  allowed=self.lsan_allowed,
                                                  maxNumRecordedFrames=self.lsan_max_stack_depth)

        env = test_environment(xrePath=os.path.dirname(self.binary),
                               debugger=self.debug_info is not None,
                               log=self.logger,
                               lsanPath=self.lsan_dir)

        env["STYLO_THREADS"] = str(self.stylo_threads)
        if self.chaos_mode_flags is not None:
            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
        if self.headless:
            env["MOZ_HEADLESS"] = "1"

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(preferences=preferences)
        self.profile.set_preferences({
            "marionette.port": self.marionette_port,
            "network.dns.localDomains": ",".join(self.config.domains_set),
            "dom.file.createInChild": True,
            # TODO: Remove preferences once Firefox 64 is stable (Bug 905404)
            "network.proxy.type": 0,
            "places.history.enabled": False,
            "network.preload": True,
        })
        if self.e10s:
            self.profile.set_preferences({"browser.tabs.remote.autostart": True})

        if self.test_type == "reftest":
            self.profile.set_preferences({"layout.interruptible-reflow.enabled": False})

        if self.leak_check:
            self.leak_report_file = os.path.join(self.profile.profile, "runtests_leaks_%s.log" % os.getpid())
            if os.path.exists(self.leak_report_file):
                os.remove(self.leak_report_file)
            env["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
        else:
            self.leak_report_file = None

        # Bug 1262954: winxp + e10s, disable hwaccel
        if (self.e10s and platform.system() in ("Windows", "Microsoft") and
            '5.1' in platform.version()):
            self.profile.set_preferences({"layers.acceleration.disabled": True})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        args = self.binary_args[:] if self.binary_args else []
        args += [cmd_arg("marionette"), "about:blank"]

        debug_args, cmd = browser_command(self.binary,
                                          args,
                                          self.debug_info)

        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd[0],
                                    cmdargs=cmd[1:],
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")

        self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs = Preferences()

        pref_paths = []

        profiles = os.path.join(self.prefs_root, 'profiles.json')
        if os.path.isfile(profiles):
            with open(profiles, 'r') as fh:
                for name in json.load(fh)['web-platform-tests']:
                    if self.browser_channel in (None, 'nightly'):
                        pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))
                    elif name != 'unittest-features':
                        pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))
        else:
            # Old preference files used before the creation of profiles.json (remove when no longer supported)
            legacy_pref_paths = (
                os.path.join(self.prefs_root, 'prefs_general.js'),   # Used in Firefox 60 and below
                os.path.join(self.prefs_root, 'common', 'user.js'),  # Used in Firefox 61
            )
            for path in legacy_pref_paths:
                if os.path.isfile(path):
                    pref_paths.append(path)

        for path in pref_paths:
            if os.path.exists(path):
                prefs.add(Preferences.read_prefs(path))
            else:
                self.logger.warning("Failed to find base prefs file in %s" % path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()

    def stop(self, force=False):
        if self.runner is not None and self.runner.is_running():
            try:
                # For Firefox we assume that stopping the runner prompts the
                # browser to shut down. This allows the leak log to be written
                for clean, stop_f in [(True, lambda: self.runner.wait(self.shutdown_timeout)),
                                      (False, lambda: self.runner.stop(signal.SIGTERM)),
                                      (False, lambda: self.runner.stop(signal.SIGKILL))]:
                    if not force or not clean:
                        retcode = stop_f()
                        if retcode is not None:
                            self.logger.info("Browser exited with return code %s" % retcode)
                            break
            except OSError:
                # This can happen on Windows if the process is already dead
                pass
        self.process_leaks()
        self.logger.debug("stopped")

    def process_leaks(self):
        self.logger.info("PROCESS LEAKS %s" % self.leak_report_file)
        if self.lsan_handler:
            self.lsan_handler.process()
        if self.leak_report_file is not None:
            mozleak.process_leak_log(
                self.leak_report_file,
                leak_thresholds=self.mozleak_thresholds,
                ignore_missing_leaks=["gmplugin"],
                log=self.logger,
                stack_fixer=self.stack_fixer,
                scope=self.group_metadata.get("scope"),
                allowed=self.mozleak_allowed
            )

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        if "GLib-GObject-CRITICAL" in line:
            return
        if line:
            data = line.decode("utf8", "replace")
            if self.stack_fixer:
                data = self.stack_fixer(data)
            if self.lsan_handler:
                data = self.lsan_handler.log(data)
            if data is not None:
                self.logger.process_output(self.pid(),
                                           data,
                                           command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self, force=False):
        self.stop(force)

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def check_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        return bool(mozcrash.log_crashes(self.logger,
                                         dump_dir,
                                         symbols_path=self.symbols_path,
                                         stackwalk_binary=self.stackwalk_binary,
                                         process=process,
                                         test=test))

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""
        if self.certutil_binary is None:
            self.logger.info("--certutil-binary not supplied; Firefox will not check certificates")
            return

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary or self.certutil_binary)
        if mozinfo.isMac:
            env_var = "DYLD_LIBRARY_PATH"
        elif mozinfo.isUnix:
            env_var = "LD_LIBRARY_PATH"
        else:
            env_var = "PATH"


        env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
                        if env_var in env else certutil_dir).encode(
                            sys.getfilesystemencoding() or 'utf-8', 'replace')

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output("certutil",
                                       subprocess.check_output(cmd,
                                                               env=env,
                                                               stderr=subprocess.STDOUT),
                                       " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,",
                 "-n", "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
class FirefoxBrowser(Browser):
    used_ports = set()
    init_timeout = 60
    shutdown_timeout = 60

    def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                 symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                 ca_certificate_path=None, e10s=False, stackfix_dir=None,
                 binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1,
                 chaos_mode_flags=None, config=None):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.test_type = test_type
        self.extra_prefs = extra_prefs
        self.marionette_port = None
        self.runner = None
        self.debug_info = debug_info
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary
        self.e10s = e10s
        self.binary_args = binary_args
        self.config = config
        if stackfix_dir:
            self.stack_fixer = get_stack_fixer_function(stackfix_dir,
                                                        self.symbols_path)
        else:
            self.stack_fixer = None

        if timeout_multiplier:
            self.init_timeout = self.init_timeout * timeout_multiplier

        self.leak_report_file = None
        self.leak_check = leak_check
        self.stylo_threads = stylo_threads
        self.chaos_mode_flags = chaos_mode_flags

    def settings(self, test):
        return {"check_leaks": self.leak_check and not test.leaks}

    def start(self, **kwargs):
        if self.marionette_port is None:
            self.marionette_port = get_free_port(2828, exclude=self.used_ports)
            self.used_ports.add(self.marionette_port)

        env = os.environ.copy()
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
        env["STYLO_THREADS"] = str(self.stylo_threads)
        if self.chaos_mode_flags is not None:
            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(preferences=preferences)
        self.profile.set_preferences({"marionette.port": self.marionette_port,
                                      "dom.disable_open_during_load": False,
                                      "network.dns.localDomains": ",".join(self.config.domains_set),
                                      "network.proxy.type": 0,
                                      "places.history.enabled": False,
                                      "dom.send_after_paint_to_content": True,
                                      "network.preload": True})
        if self.e10s:
            self.profile.set_preferences({"browser.tabs.remote.autostart": True})

        if self.test_type == "reftest":
            self.profile.set_preferences({"layout.interruptible-reflow.enabled": False})

        if self.leak_check and kwargs.get("check_leaks", True):
            self.leak_report_file = os.path.join(self.profile.profile, "runtests_leaks.log")
            if os.path.exists(self.leak_report_file):
                os.remove(self.leak_report_file)
            env["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
        else:
            self.leak_report_file = None

        # Bug 1262954: winxp + e10s, disable hwaccel
        if (self.e10s and platform.system() in ("Windows", "Microsoft") and
            '5.1' in platform.version()):
            self.profile.set_preferences({"layers.acceleration.disabled": True})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        debug_args, cmd = browser_command(self.binary,
                                          self.binary_args if self.binary_args else [] +
                                          [cmd_arg("marionette"), "about:blank"],
                                          self.debug_info)

        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd[0],
                                    cmdargs=cmd[1:],
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")

        self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs = Preferences()

        pref_paths = []
        prefs_general = os.path.join(self.prefs_root, 'prefs_general.js')
        if os.path.isfile(prefs_general):
            # Old preference file used in Firefox 60 and earlier (remove when no longer supported)
            pref_paths.append(prefs_general)

        profiles = os.path.join(self.prefs_root, 'profiles.json')
        if os.path.isfile(profiles):
            with open(profiles, 'r') as fh:
                for name in json.load(fh)['web-platform-tests']:
                    pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))

        for path in pref_paths:
            if os.path.exists(path):
                prefs.add(Preferences.read_prefs(path))
            else:
                self.logger.warning("Failed to find base prefs file in %s" % path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()

    def stop(self, force=False):
        if self.runner is not None and self.runner.is_running():
            try:
                # For Firefox we assume that stopping the runner prompts the
                # browser to shut down. This allows the leak log to be written
                for clean, stop_f in [(True, lambda: self.runner.wait(self.shutdown_timeout)),
                                      (False, lambda: self.runner.stop(signal.SIGTERM)),
                                      (False, lambda: self.runner.stop(signal.SIGKILL))]:
                    if not force or not clean:
                        retcode = stop_f()
                        if retcode is not None:
                            self.logger.info("Browser exited with return code %s" % retcode)
                            break
            except OSError:
                # This can happen on Windows if the process is already dead
                pass
        self.logger.debug("stopped")

    def process_leaks(self):
        self.logger.debug("PROCESS LEAKS %s" % self.leak_report_file)
        if self.leak_report_file is None:
            return
        mozleak.process_leak_log(
            self.leak_report_file,
            leak_thresholds={
                "default": 0,
                "tab": 10000,  # See dependencies of bug 1051230.
                # GMP rarely gets a log, but when it does, it leaks a little.
                "geckomediaplugin": 20000,
            },
            ignore_missing_leaks=["geckomediaplugin"],
            log=self.logger,
            stack_fixer=self.stack_fixer
        )

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        if "GLib-GObject-CRITICAL" in line:
            return
        if line:
            data = line.decode("utf8", "replace")
            if self.stack_fixer:
                data = self.stack_fixer(data)
            self.logger.process_output(self.pid(),
                                      data,
                                      command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()
        self.process_leaks()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def check_for_crashes(self):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        return bool(mozcrash.check_for_crashes(dump_dir,
                                               symbols_path=self.symbols_path,
                                               stackwalk_binary=self.stackwalk_binary,
                                               quiet=True))

    def log_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        mozcrash.log_crashes(self.logger,
                             dump_dir,
                             symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process,
                             test=test)

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""
        if self.certutil_binary is None:
            self.logger.info("--certutil-binary not supplied; Firefox will not check certificates")
            return

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary)
        if mozinfo.isMac:
            env_var = "DYLD_LIBRARY_PATH"
        elif mozinfo.isUnix:
            env_var = "LD_LIBRARY_PATH"
        else:
            env_var = "PATH"


        env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
                        if env_var in env else certutil_dir).encode(
                            sys.getfilesystemencoding() or 'utf-8', 'replace')

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output("certutil",
                                       subprocess.check_output(cmd,
                                                               env=env,
                                                               stderr=subprocess.STDOUT),
                                       " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,",
                 "-n", "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
Beispiel #17
0
        # Send log file to couchdb
        self.log.info("Sending log file to couchdb at '" + self.couchURI + "'")
        try:
            fb_logs.main([
                "--log", filename, "--database", self.databasename, "--couch",
                self.couchURI, "--changeset",
                utils.get_changeset(self.appdir), "--section", self.section
            ])
        except Exception:
            self.log.error("Log file not sent to couchdb at server: '" +
                           self.couchURI + "' and database: '" +
                           self.databasename)
            self.log.error(traceback.format_exc())

        # Cleanup
        mozRunner.stop()
        self.log.debug("Exiting - Status successful")
        self.cleanup()


# Called from the command line
def cli(argv=sys.argv[1:]):
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("--appname", dest="binary", help="Firefox binary path")

    parser.add_option("--profile-path",
                      dest="profile",
                      help="The profile to use when running Firefox")

    parser.add_option("-s",
                      "--serverpath",
Beispiel #18
0
class FirefoxBrowser(Browser):
    used_ports = set()

    def __init__(self,
                 logger,
                 binary,
                 prefs_root,
                 debug_args=None,
                 interactive=None,
                 symbols_path=None,
                 stackwalk_binary=None):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.marionette_port = None
        self.used_ports.add(self.marionette_port)
        self.runner = None
        self.debug_args = debug_args
        self.interactive = interactive
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary

    def start(self):
        self.marionette_port = get_free_port(2828, exclude=self.used_ports)

        env = os.environ.copy()
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"

        locations = ServerLocations(
            filename=os.path.join(here, "server-locations.txt"))

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(locations=locations,
                                      proxy=True,
                                      preferences=preferences)
        self.profile.set_preferences({
            "marionette.defaultPrefs.enabled": True,
            "marionette.defaultPrefs.port": self.marionette_port,
            "dom.disable_open_during_load": False
        })

        self.runner = FirefoxRunner(
            profile=self.profile,
            binary=self.binary,
            cmdargs=[cmd_arg("marionette"), "about:blank"],
            env=env,
            process_class=ProcessHandler,
            process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")
        self.runner.start(debug_args=self.debug_args,
                          interactive=self.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            preferences = Preferences.read_prefs(prefs_path)
        else:
            self.logger.warning("Failed to find base prefs file in %s" %
                                prefs_path)
            preferences = []

        return preferences

    def stop(self):
        self.logger.debug("Stopping browser")
        if self.runner is not None:
            try:
                self.runner.stop()
            except OSError:
                # This can happen on Windows if the process is already dead
                pass

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        self.logger.process_output(self.pid(),
                                   line.decode("utf8", "replace"),
                                   command=" ".join(self.runner.command))

    def is_alive(self):
        return self.runner.is_running()

    def cleanup(self):
        self.stop()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def log_crash(self, logger, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")
        mozcrash.log_crashes(logger,
                             dump_dir,
                             symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process,
                             test=test)
Beispiel #19
0
class FirefoxBrowser(Browser):
    used_ports = set()
    init_timeout = 60
    shutdown_timeout = 60

    def __init__(self,
                 logger,
                 binary,
                 prefs_root,
                 test_type,
                 extra_prefs=None,
                 debug_info=None,
                 symbols_path=None,
                 stackwalk_binary=None,
                 certutil_binary=None,
                 ca_certificate_path=None,
                 e10s=False,
                 stackfix_dir=None,
                 binary_args=None,
                 timeout_multiplier=None,
                 leak_check=False,
                 stylo_threads=1,
                 chaos_mode_flags=None):
        Browser.__init__(self, logger)
        self.binary = binary
        self.prefs_root = prefs_root
        self.test_type = test_type
        self.extra_prefs = extra_prefs
        self.marionette_port = None
        self.runner = None
        self.debug_info = debug_info
        self.profile = None
        self.symbols_path = symbols_path
        self.stackwalk_binary = stackwalk_binary
        self.ca_certificate_path = ca_certificate_path
        self.certutil_binary = certutil_binary
        self.e10s = e10s
        self.binary_args = binary_args
        if stackfix_dir:
            self.stack_fixer = get_stack_fixer_function(
                stackfix_dir, self.symbols_path)
        else:
            self.stack_fixer = None

        if timeout_multiplier:
            self.init_timeout = self.init_timeout * timeout_multiplier

        self.leak_report_file = None
        self.leak_check = leak_check
        self.stylo_threads = stylo_threads
        self.chaos_mode_flags = chaos_mode_flags

    def settings(self, test):
        return {"check_leaks": self.leak_check and not test.leaks}

    def start(self, **kwargs):
        if self.marionette_port is None:
            self.marionette_port = get_free_port(2828, exclude=self.used_ports)
            self.used_ports.add(self.marionette_port)

        env = os.environ.copy()
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
        env["STYLO_THREADS"] = str(self.stylo_threads)
        if self.chaos_mode_flags is not None:
            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)

        locations = ServerLocations(
            filename=os.path.join(here, "server-locations.txt"))

        preferences = self.load_prefs()

        self.profile = FirefoxProfile(locations=locations,
                                      preferences=preferences)
        self.profile.set_preferences({
            "marionette.port":
            self.marionette_port,
            "dom.disable_open_during_load":
            False,
            "network.dns.localDomains":
            ",".join(hostnames),
            "network.proxy.type":
            0,
            "places.history.enabled":
            False,
            "dom.send_after_paint_to_content":
            True,
            "network.preload":
            True
        })
        if self.e10s:
            self.profile.set_preferences(
                {"browser.tabs.remote.autostart": True})

        if self.test_type == "reftest":
            self.profile.set_preferences(
                {"layout.interruptible-reflow.enabled": False})

        if self.leak_check and kwargs.get("check_leaks", True):
            self.leak_report_file = os.path.join(self.profile.profile,
                                                 "runtests_leaks.log")
            if os.path.exists(self.leak_report_file):
                os.remove(self.leak_report_file)
            env["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
        else:
            self.leak_report_file = None

        # Bug 1262954: winxp + e10s, disable hwaccel
        if (self.e10s and platform.system() in ("Windows", "Microsoft")
                and '5.1' in platform.version()):
            self.profile.set_preferences(
                {"layers.acceleration.disabled": True})

        if self.ca_certificate_path is not None:
            self.setup_ssl()

        debug_args, cmd = browser_command(
            self.binary, self.binary_args if self.binary_args else [] +
            [cmd_arg("marionette"), "about:blank"], self.debug_info)

        self.runner = FirefoxRunner(
            profile=self.profile,
            binary=cmd[0],
            cmdargs=cmd[1:],
            env=env,
            process_class=ProcessHandler,
            process_args={"processOutputLine": [self.on_output]})

        self.logger.debug("Starting Firefox")

        self.runner.start(debug_args=debug_args,
                          interactive=self.debug_info
                          and self.debug_info.interactive)
        self.logger.debug("Firefox Started")

    def load_prefs(self):
        prefs = Preferences()

        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            prefs.add(Preferences.read_prefs(prefs_path))
        else:
            self.logger.warning("Failed to find base prefs file in %s" %
                                prefs_path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()

    def stop(self, force=False):
        if self.runner is not None and self.runner.is_running():
            try:
                # For Firefox we assume that stopping the runner prompts the
                # browser to shut down. This allows the leak log to be written
                for clean, stop_f in [
                    (True, lambda: self.runner.wait(self.shutdown_timeout)),
                    (False, lambda: self.runner.stop(signal.SIGTERM)),
                    (False, lambda: self.runner.stop(signal.SIGKILL))
                ]:
                    if not force or not clean:
                        retcode = stop_f()
                        if retcode is not None:
                            self.logger.info(
                                "Browser exited with return code %s" % retcode)
                            break
            except OSError:
                # This can happen on Windows if the process is already dead
                pass
        self.logger.debug("stopped")

    def process_leaks(self):
        self.logger.debug("PROCESS LEAKS %s" % self.leak_report_file)
        if self.leak_report_file is None:
            return
        mozleak.process_leak_log(
            self.leak_report_file,
            leak_thresholds={
                "default": 0,
                "tab": 10000,  # See dependencies of bug 1051230.
                # GMP rarely gets a log, but when it does, it leaks a little.
                "geckomediaplugin": 20000,
            },
            ignore_missing_leaks=["geckomediaplugin"],
            log=self.logger,
            stack_fixer=self.stack_fixer)

    def pid(self):
        if self.runner.process_handler is None:
            return None

        try:
            return self.runner.process_handler.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the firefox process to the log"""
        data = line.decode("utf8", "replace")
        if self.stack_fixer:
            data = self.stack_fixer(data)
        self.logger.process_output(self.pid(),
                                   data,
                                   command=" ".join(self.runner.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()
        self.process_leaks()

    def executor_browser(self):
        assert self.marionette_port is not None
        return ExecutorBrowser, {"marionette_port": self.marionette_port}

    def check_for_crashes(self):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        return bool(
            mozcrash.check_for_crashes(dump_dir,
                                       symbols_path=self.symbols_path,
                                       stackwalk_binary=self.stackwalk_binary,
                                       quiet=True))

    def log_crash(self, process, test):
        dump_dir = os.path.join(self.profile.profile, "minidumps")

        mozcrash.log_crashes(self.logger,
                             dump_dir,
                             symbols_path=self.symbols_path,
                             stackwalk_binary=self.stackwalk_binary,
                             process=process,
                             test=test)

    def setup_ssl(self):
        """Create a certificate database to use in the test profile. This is configured
        to trust the CA Certificate that has signed the web-platform.test server
        certificate."""
        if self.certutil_binary is None:
            self.logger.info(
                "--certutil-binary not supplied; Firefox will not check certificates"
            )
            return

        self.logger.info("Setting up ssl")

        # Make sure the certutil libraries from the source tree are loaded when using a
        # local copy of certutil
        # TODO: Maybe only set this if certutil won't launch?
        env = os.environ.copy()
        certutil_dir = os.path.dirname(self.binary)
        if mozinfo.isMac:
            env_var = "DYLD_LIBRARY_PATH"
        elif mozinfo.isUnix:
            env_var = "LD_LIBRARY_PATH"
        else:
            env_var = "PATH"

        env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
                        if env_var in env else certutil_dir).encode(
                            sys.getfilesystemencoding() or 'utf-8', 'replace')

        def certutil(*args):
            cmd = [self.certutil_binary] + list(args)
            self.logger.process_output(
                "certutil",
                subprocess.check_output(cmd, env=env,
                                        stderr=subprocess.STDOUT),
                " ".join(cmd))

        pw_path = os.path.join(self.profile.profile, ".crtdbpw")
        with open(pw_path, "w") as f:
            # Use empty password for certificate db
            f.write("\n")

        cert_db_path = self.profile.profile

        # Create a new certificate db
        certutil("-N", "-d", cert_db_path, "-f", pw_path)

        # Add the CA certificate to the database and mark as trusted to issue server certs
        certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,", "-n",
                 "web-platform-tests", "-i", self.ca_certificate_path)

        # List all certs in the database
        certutil("-L", "-d", cert_db_path)
Beispiel #20
0
class FirefoxThread(Thread):
    def __init__(self, binary, marionette_port = 2828):
        Thread.__init__(self)
        self.binary = binary
        self.marionette_port = marionette_port
        self.logger = FirefoxThreadLogger(None)
        self._firefoxRunningEvent = Event()
        
        
    def run(self):
        '''
        Starts Firefox thread with Marionette turned on.
        '''
        
        self.profile = FirefoxProfile()
        self.profile.set_preferences({"marionette.defaultPrefs.enabled" : True,
                                      "marionette.defaultPrefs.port": 2828,
                                      "browser.startup.page": 0,
                                      "browser.startup.homepage": "about:blank",
                                      })
        self.runner = FirefoxRunner(profile = self.profile,
                                    binary = self.binary,
                                    kp_kwargs = {'processOutputLine' : [self.logger]})

        self.runner.start()
        self._firefoxRunningEvent.set()
        self.runner.wait()
        
    def stop(self):
        '''
        Stops Firefox/Nightly. To be called by external thread.
        '''
        self.runner.stop()
        
    def getPID(self):
        '''
        This is called by external threads, and blocks until PID is available in FirefoxRunner,
        which is shortly after start() has been called. 
        '''
        self._firefoxRunningEvent.wait()
        return self.runner.process_handler.proc.pid
        
        
    def waitForMarionettePortOpenReady(self, timeout):
        '''
        This method can be run by an external thread. Returns True when the port is open, or False on timeout.
        It's active waiting with 1 sec heartbeat, if you know better solution please mail me.
        
        Originally taken from:
        https://github.com/mozilla/marionette_client/blob/master/marionette/emulator.py#L246
        '''
        starttime = datetime.datetime.now()
        while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect(('127.0.0.1', self.marionette_port))
                data = sock.recv(16)
                sock.close()
                if '"from"' in data:
                    return True
            except:
                #import traceback
                #print traceback.format_exc()
                pass
            
            time.sleep(1)
        print ''
        return False