def marionette(request, timeout):
    """Return a marionette instance"""
    m = Marionette(bin=request.config.option.bin)
    m.start_session()
    m.set_prefs({'signon.rememberSignons': False})
    request.addfinalizer(m.delete_session)
    m.set_search_timeout(timeout)
    return m
Esempio n. 2
0
 def setup(self):
     I18nSetup.locale_path = os.path.join(site.getsitepackages()[0], 'OWDTestToolkit/locale')
     if not I18nSetup.configured:
         marionette = Marionette()
         marionette.start_session()
         # Configure the translation to be used based on the previously retrieved language. Should
         # the translation is not found, the fallback will prevent a failure
         lang = marionette.execute_script("""return window.navigator.language;""")
         translation = gettext.translation('default', I18nSetup.locale_path, languages=[lang], fallback=True)
         I18nSetup._ = translation.ugettext
         I18nSetup.configured = True
     return I18nSetup._
Esempio n. 3
0
 def get_new_emulator(self):
     self.extra_emulator_index += 1
     if len(self.marionette.extra_emulators) == self.extra_emulator_index:
         qemu  = Marionette(emulator=self.marionette.emulator.arch,
                            emulatorBinary=self.marionette.emulator.binary,
                            homedir=self.marionette.homedir,
                            baseurl=self.marionette.baseurl,
                            noWindow=self.marionette.noWindow,
                            gecko_path=self.marionette.gecko_path)
         qemu.start_session()
         self.marionette.extra_emulators.append(qemu)
     else:
         qemu = self.marionette.extra_emulators[self.extra_emulator_index]
     return qemu
Esempio n. 4
0
def _wait_browser_ready(browser):
    '''waits for browser to become ready'''
    for line in iter(browser.stdout.readline, ''):
        if 'Bootstrapped 100%: Done' in line:
            break
    client = Marionette('localhost', port=2828)
    connected = False
    while not connected:
        try:
            client.start_session()
            connected = True
        except socket.error:
            time.sleep(.1)
    client.close()
Esempio n. 5
0
 def get_new_emulator(self):
     self.extra_emulator_index += 1
     if len(self.marionette.extra_emulators) == self.extra_emulator_index:
         qemu = Marionette(emulator=self.marionette.emulator.arch,
                           emulatorBinary=self.marionette.emulator.binary,
                           homedir=self.marionette.homedir,
                           baseurl=self.marionette.baseurl,
                           noWindow=self.marionette.noWindow,
                           gecko_path=self.marionette.gecko_path)
         qemu.start_session()
         self.marionette.extra_emulators.append(qemu)
     else:
         qemu = self.marionette.extra_emulators[self.extra_emulator_index]
     return qemu
Esempio n. 6
0
def workerProc(arg):
    client = Marionette('localhost', port=2828)
    client.start_session()
    client.navigate('https://www.mercari.com/sell/')
    time.sleep(5)
    #first open the excel file to grab information
    #photos will be stored in photos file
    num_rows = arg
    items = load_excel("Mercari.xlsx", num_rows)

    for item in items:
        title = client.find_element(By.XPATH, "/html/body/div[1]/main/div[3]/div/div[2]/div[3]/div[1]/div[2]/input")
        title.send_keys(item[0])
        textarea = client.find_element(By.TAG_NAME, 'textarea')
        textarea.send_keys(item[1])
        zipcode = client.find_element(By.XPATH, "/html/body/div[1]/main/div[3]/div/div[2]/div[6]/div[1]/div[2]/input")
        zipcode.send_keys(item[8])
        price = client.find_element(By.XPATH, "/html/body/div[1]/main/div[3]/div/div[2]/div[7]/div/div[2]/input")
        price.send_keys(item[10])
        brand = client.find_element(By.XPATH, "/html/body/div[1]/main/div[3]/div/div[2]/div[7]/div/div[2]/input")
        brand.send_keys(item[6])
        category(client, item[2], item[3], item[4])

        if item[6] >= 0:
            submit_size(client, item[6])

        time.sleep(1)
        #determineSize(client, 1)
        selectCondition(client, item[7])
        ship_selfpaid(client)


        for i in range(item[10]):
            path = os.path.dirname(os.path.realpath("photos\img" + str(i) + ".jpg"))
            fileUpload(client, getPID(), path + "\img" + str(i) + ".jpg", i)
        submit_button(client)
        time.sleep(2)
        client.navigate('https://www.mercari.com/sell/')
Esempio n. 7
0
    def freeze(self, request, queryset):
        with open(os.path.join(settings.BASE_DIR, 'build',
                               'freeze.bundle.js')) as f:
            freeze_script = f.read()

        client = None
        try:
            client = Marionette(bin=settings.FIREFOX_BIN, headless=True)
            client.start_session()

            for webpage in queryset:
                print('Freezing {}...'.format(webpage.url))
                client.navigate(webpage.url)
                results = client.execute_async_script(
                    freeze_script,
                    script_timeout=1000 * 60 * 5,
                )
                webpage.frozen_html.save(webpage.url,
                                         ContentFile(results['html']))
                webpage.save()
        finally:
            if client:
                client.cleanup()
Esempio n. 8
0
def _open_with_timeout(browser,
                       page,
                       timeout=config.DURATION_LIMIT,
                       burst_wait=3,
                       bridge=None):
    '''navigates browser to url while capturing the packet dump, aborts
    after timeout. If bridge, that is the IP address of the connected
    bridge, just capture traffic to there (need to set this by hand)
    '''
    client = Marionette('localhost', port=2828, socket_timeout=(timeout))
    try:
        client.start_session()
        client.set_page_load_timeout((timeout) * 1000)
    except socket.timeout:
        _kill(browser)
        raise

    (url, domain) = _normalize_url(page)

    (tshark_process, file_name) = _open_packet_dump(domain, bridge)
    #    thread = threading.Thread(target=client.navigate, args=(url,))
    thread = threading.Thread(target=_navigate_or_fail,
                              args=(client, url, file_name))
    thread.daemon = True
    thread.start()

    # todo: this is code duplication for both _open functions
    start = time.time()
    while thread.is_alive():
        time.sleep(.1)
        # will this still happen after the timeout is set like above?
        if time.time() - start > timeout + 30:
            _handle_exception("aborted after timeout2", file_name, client)
            _kill(browser, tshark_process)
            raise SystemExit("download aborted after timeout")
    time.sleep(burst_wait)
    _kill(browser, tshark_process)
Esempio n. 9
0
class CommonTestCase(unittest.TestCase):

    __metaclass__ = MetaParameterized
    match_re = None
    failureException = AssertionError
    pydebugger = None

    def __init__(self, methodName, **kwargs):
        unittest.TestCase.__init__(self, methodName)
        self.loglines = []
        self.duration = 0
        self.expected = kwargs.pop("expected", "pass")
        self.logger = get_default_logger()
        self.profile = FirefoxProfile()
        self.binary = kwargs.pop("binary", None)

    def _enter_pm(self):
        if self.pydebugger:
            self.pydebugger.post_mortem(sys.exc_info()[2])

    def _addSkip(self, result, reason):
        addSkip = getattr(result, "addSkip", None)
        if addSkip is not None:
            addSkip(self, reason)
        else:
            warnings.warn("TestResult has no addSkip method, skips not reported", RuntimeWarning, 2)
            result.addSuccess(self)

    def run(self, result=None):
        # Bug 967566 suggests refactoring run, which would hopefully
        # mean getting rid of this inner function, which only sits
        # here to reduce code duplication:
        def expected_failure(result, exc_info):
            addExpectedFailure = getattr(result, "addExpectedFailure", None)
            if addExpectedFailure is not None:
                addExpectedFailure(self, exc_info)
            else:
                warnings.warn("TestResult has no addExpectedFailure method, " "reporting as passes", RuntimeWarning)
                result.addSuccess(self)

        self.start_time = time.time()
        orig_result = result
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, "startTestRun", None)
            if startTestRun is not None:
                startTestRun()

        result.startTest(self)

        testMethod = getattr(self, self._testMethodName)
        if getattr(self.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False):
            # If the class or method was skipped.
            try:
                skip_why = getattr(self.__class__, "__unittest_skip_why__", "") or getattr(
                    testMethod, "__unittest_skip_why__", ""
                )
                self._addSkip(result, skip_why)
            finally:
                result.stopTest(self)
            self.stop_time = time.time()
            return
        try:
            success = False
            try:
                if self.expected == "fail":
                    try:
                        self.setUp()
                    except Exception:
                        raise _ExpectedFailure(sys.exc_info())
                else:
                    self.setUp()
            except SkipTest as e:
                self._addSkip(result, str(e))
            except KeyboardInterrupt:
                raise
            except _ExpectedFailure as e:
                expected_failure(result, e.exc_info)
            except:
                self._enter_pm()
                result.addError(self, sys.exc_info())
            else:
                try:
                    if self.expected == "fail":
                        try:
                            testMethod()
                        except:
                            raise _ExpectedFailure(sys.exc_info())
                        raise _UnexpectedSuccess
                    else:
                        testMethod()
                except self.failureException:
                    self._enter_pm()
                    result.addFailure(self, sys.exc_info())
                except KeyboardInterrupt:
                    raise
                except _ExpectedFailure as e:
                    expected_failure(result, e.exc_info)
                except _UnexpectedSuccess:
                    addUnexpectedSuccess = getattr(result, "addUnexpectedSuccess", None)
                    if addUnexpectedSuccess is not None:
                        addUnexpectedSuccess(self)
                    else:
                        warnings.warn(
                            "TestResult has no addUnexpectedSuccess method, reporting as failures", RuntimeWarning
                        )
                        result.addFailure(self, sys.exc_info())
                except SkipTest as e:
                    self._addSkip(result, str(e))
                except:
                    self._enter_pm()
                    result.addError(self, sys.exc_info())
                else:
                    success = True
                try:
                    if self.expected == "fail":
                        try:
                            self.tearDown()
                        except:
                            raise _ExpectedFailure(sys.exc_info())
                    else:
                        self.tearDown()
                except KeyboardInterrupt:
                    raise
                except _ExpectedFailure as e:
                    expected_failure(result, e.exc_info)
                except:
                    self._enter_pm()
                    result.addError(self, sys.exc_info())
                    success = False
            # Here we could handle doCleanups() instead of calling cleanTest directly
            self.cleanTest()

            if success:
                result.addSuccess(self)

        finally:
            result.stopTest(self)
            if orig_result is None:
                stopTestRun = getattr(result, "stopTestRun", None)
                if stopTestRun is not None:
                    stopTestRun()

    @classmethod
    def match(cls, filename):
        """
        Determines if the specified filename should be handled by this
        test class; this is done by looking for a match for the filename
        using cls.match_re.
        """
        if not cls.match_re:
            return False
        m = cls.match_re.match(filename)
        return m is not None

    @classmethod
    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, testvars):
        """
        Adds all the tests in the specified file to the specified suite.
        """
        raise NotImplementedError

    @property
    def test_name(self):
        if hasattr(self, "jsFile"):
            return os.path.basename(self.jsFile)
        else:
            return "%s.py %s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName)

    def id(self):
        # TBPL starring requires that the "test name" field of a failure message
        # not differ over time. The test name to be used is passed to
        # mozlog via the test id, so this is overriden to maintain
        # consistency.
        return self.test_name

    def setUp(self):
        # Convert the marionette weakref to an object, just for the
        # duration of the test; this is deleted in tearDown() to prevent
        # a persistent circular reference which in turn would prevent
        # proper garbage collection.
        self.start_time = time.time()
        self.pingServer = PingServer()
        self.pingServer.start()
        self.marionette = Marionette(bin=self.binary, profile=self.profile)
        if self.marionette.session is None:
            self.marionette.start_session()
        if self.marionette.timeout is not None:
            self.marionette.timeouts(self.marionette.TIMEOUT_SEARCH, self.marionette.timeout)
            self.marionette.timeouts(self.marionette.TIMEOUT_SCRIPT, self.marionette.timeout)
            self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, self.marionette.timeout)
        else:
            self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, 30000)

    def tearDown(self):
        self.marionette.cleanup()
        self.pingServer.stop()

    def cleanTest(self):
        self._deleteSession()

    def _deleteSession(self):
        if hasattr(self, "start_time"):
            self.duration = time.time() - self.start_time
        if hasattr(self.marionette, "session"):
            if self.marionette.session is not None:
                try:
                    self.loglines.extend(self.marionette.get_logs())
                except Exception, inst:
                    self.loglines = [["Error getting log: %s" % inst]]
                try:
                    self.marionette.delete_session()
                except (socket.error, MarionetteException, IOError):
                    # Gecko has crashed?
                    self.marionette.session = None
                    try:
                        self.marionette.client.close()
                    except socket.error:
                        pass
        self.marionette = None
Esempio n. 10
0
def run_tests(firefox_path=None):
    basedir = os.path.dirname(__file__)

    if sys.platform == 'darwin' and os.path.isdir(firefox_path):
        firefox_path = os.path.join(firefox_path,
                                    'Contents', 'MacOS', 'firefox')

    driver = Marionette(app='fxdesktop', bin=firefox_path, gecko_log='-',
                        prefs={'xpinstall.signatures.required': False})
    driver.start_session()

    try:
        build1 = tempfile.NamedTemporaryFile(mode='wb', suffix='.xpi',
                                             delete=False)
        build2 = tempfile.NamedTemporaryFile(mode='wb', suffix='.xpi',
                                             delete=False)
        try:
            jpm_build(basedir, build1.name)
            jpm_build(os.path.join(basedir, 'testhelper'), build2.name)

            addons = Addons(driver)
            addons.install(build1.name, temp=True)
            addons.install(build2.name, temp=True)
        finally:
            os.unlink(build1.name)
            os.unlink(build2.name)

        driver.expected = expected
        driver.keys = Keys

        class restore_url:
            def __enter__(self):
                self.url = driver.get_url()

            def __exit__(self, type, value, traceback):
                driver.navigate('about:blank')
                driver.navigate(self.url)
        driver.restore_url = restore_url

        def wait_until(method):
            Wait(driver, default_timeout).until(lambda d: method())
        driver.wait_until = wait_until

        def accept_alert():
            driver.switch_to_alert().accept()
        driver.accept_alert = accept_alert

        max_timestamp = {'value': 0}

        def get_urls():
            result = []
            prefix = '[testhelper] Loading: '
            new_timestamp = max_timestamp['value']
            with driver.using_context(driver.CONTEXT_CHROME):
                messages = driver.execute_script(
                    'return ' +
                    'Cc["@mozilla.org/consoleservice;1"]' +
                    '.getService(Ci.nsIConsoleService).getMessageArray()' +
                    '.map(m => m instanceof Ci.nsIScriptError ? ' +
                    '[m.timeStamp, m.errorMessage] : [null, null])'
                )
            for timestamp, message in messages:
                if timestamp <= max_timestamp['value']:
                    continue
                if not message.startswith(prefix):
                    continue
                if timestamp > new_timestamp:
                    new_timestamp = timestamp
                result.append(message[len(prefix):])
            max_timestamp['value'] = new_timestamp
            return result
        driver.get_urls = get_urls

        def close_windows(keep):
            for h in [h for h in driver.chrome_window_handles if h != keep]:
                driver.switch_to_window(h)
                driver.close_chrome_window()
            driver.switch_to_window(keep)
        driver.close_windows = close_windows

        def close_background_tabs():
            current_tab = driver.current_window_handle
            for h in [h for h in driver.window_handles if h != current_tab]:
                driver.switch_to_window(h)
                driver.close()
            driver.switch_to_window(current_tab)
        driver.close_background_tabs = close_background_tabs

        def wait_for_load():
            code = 'return document.readyState == "complete";'
            driver.wait_until(lambda: driver.execute_script(code))
        driver.wait_for_load = wait_for_load

        def click(self):
            action = Actions(driver)
            action.click(self)
            action.perform()
        HTMLElement.click = click

        def middle_click(self):
            action = Actions(driver)
            action.middle_click(self)
            action.perform()
        HTMLElement.middle_click = middle_click

        def context_click(self):
            action = Actions(driver)
            action.context_click(self)
            action.perform()
        HTMLElement.context_click = context_click

        testdir = os.path.join(basedir, 'tests')
        for filename in os.listdir(testdir):
            if filename.startswith('.') or not filename.endswith('.py'):
                continue
            filepath = os.path.join(testdir, filename)
            globals = {}
            execfile(filepath, globals)
            globals['run'](driver)
    finally:
        driver.cleanup()
                    " /tmp/foo" + str(timestamp) + "\"",
                    shell=True)
    shutil.copy("firefox_prefs.js", "/tmp/foo" + str(timestamp) + "/prefs.js")

    time.sleep(1)

    # Launch Firefox with the new profile
    p = subprocess.Popen([
        FIREFOX_PATH + " -profile /tmp/foo" + str(timestamp) +
        " -marionette -devtools"
    ],
                         shell=True,
                         preexec_fn=os.setsid)

    client = Marionette('localhost', port=2828)
    client.start_session()

    addons = Addons(client)
    addons.install(os.getcwd() + "/har-export-trigger-0.6.1.xpi", temp=True)

    for run in range(1, TIMES + 1):
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d+%H-%M-%S.%f")
        unixtimestamp = int(time.time())
        try:
            hostname = URL_TO_FETCH.split('/')[2]
        except IndexError:
            hostname = URL_TO_FETCH
            URL_TO_FETCH = "http://" + hostname

        print("Run " + str(run) + "/" + str(TIMES) + " - Fetching " +
              URL_TO_FETCH + " at " + timestamp)
Esempio n. 12
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDevice, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = '10.0.2.2'
        PORT = 8888

        PATH_MAPPINGS = {
            '/js-input/webkit/PerformanceTests':
            'third_party/webkit/PerformanceTests',
        }

        dirs = self.query_abs_dirs()
        topsrcdir = os.path.join(dirs['abs_work_dir'], 'src')
        adb = self.query_exe('adb')

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(port=PORT,
                         docroot=os.path.join(topsrcdir, "build", "pgo"),
                         path_mappings=path_mappings)
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [
            os.path.join(profile_data_dir, profile, 'user.js')
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        # Enforce 1proc. This isn't in one of the user.js files because those
        # are shared with desktop, which wants e10s. We can't interpolate
        # because the formatting code only works for strings, and this is a
        # bool pref.
        prefs["browser.tabs.remote.autostart"] = False

        outputdir = self.config.get('output_directory', '/sdcard/pgo_profile')
        jarlog = posixpath.join(outputdir, 'en-US.log')
        profdata = posixpath.join(outputdir, 'default_%p_random_%m.profraw')

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        adbdevice = ADBDevice(adb=adb, device='emulator-5554')
        adbdevice.mkdir(outputdir)

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app='fennec',
                package_name=app,
                adb_path=adb,
                bin="target.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if 'Speedometer/index.html' in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                if not adbdevice.process_exist(app):
                    break
                time.sleep(2)
            else:
                raise Exception("Android App (%s) never quit" % app)

            # Pull all the profraw files and en-US.log
            adbdevice.pull(outputdir, '/builds/worker/workspace/')
        except ADBTimeoutError:
            self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError',
                       EXIT_STATUS_DICT[TBPL_RETRY])

        profraw_files = glob.glob('/builds/worker/workspace/*.profraw')
        if not profraw_files:
            self.fatal(
                'Could not find any profraw files in /builds/worker/workspace')
        merge_cmd = [
            os.path.join(os.environ['MOZ_FETCHES_DIR'],
                         'clang/bin/llvm-profdata'),
            'merge',
            '-o',
            '/builds/worker/workspace/merged.profdata',
        ] + profraw_files
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                'INFRA-ERROR: Failed to merge profile data. Corrupt profile?',
                EXIT_STATUS_DICT[TBPL_RETRY])

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            'tar',
            '-acvf',
            '/builds/worker/artifacts/profdata.tar.xz',
            '-C',
            '/builds/worker/workspace',
            'merged.profdata',
            'en-US.log',
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
Esempio n. 13
0
class current_frame():

    filename_screenshot = ""
    filename_htmldump = ""

    def main(self, LOGDIR):
        #
        # The first variable is the log directory.
        #
        ucount = 0
        self.marionette = Marionette(host='localhost', port=2828)
        self.marionette.start_session()
        self.marionette.set_search_timeout(1000)

        #
        # Now loop through all the iframes, gathering details about each one.
        #
        print ""
        print "Iframe for 'top level' () ..."
        self.filename_screenshot = LOGDIR + "top_level" + ".png"
        self.filename_htmldump = LOGDIR + "top_level" + ".html"
        self.marionette.switch_to_frame()
        self.record_frame()

        frames = self.marionette.find_elements("tag name", "iframe")
        for fnum in range(len(frames)):

            #
            # App name is usually in the "src" attribute, so it's worth a shot..
            #
            frame_src = frames[fnum].get_attribute("src")

            if frame_src != "":
                startpos = frame_src.index('/') + 2
                stoppos = frame_src.index('.')
                appname = frame_src[startpos:stoppos]
                filename = appname
            else:
                ucount = ucount + 1
                appname = "(unknown)"
                filename = "unknown_" + str(ucount)

            #
            # Because we call this script sometimes when we hit a Marionette issue,
            # these filenames may already exist (and we'd overwrite them!), so
            # add 'DEBUG_' to the start of the filename.
            #
            filename = "DEBUG_" + filename

            filename_details = LOGDIR + filename + "_iframe_details.txt"
            self.filename_screenshot = LOGDIR + filename + ".png"
            self.filename_htmldump = LOGDIR + filename + ".html"

            #
            # This iframe gives me problems sometimes, so I'm ignoring it for now.
            #
            if appname == "costcontrol":
                continue

            print ""
            print "Iframe for app \"" + appname + "\" ..."

            #
            # Record the iframe details (pretty verbose, but 'execute_script'
            # wasn't letting me use 'for' loops in js for some reason).
            #
            print "    |_ iframe details saved to : " + filename_details
            f = open(filename_details, 'w')
            f.write("Attributes for this iframe ...\n")
            num_attribs = self.marionette.execute_script("return document.getElementsByTagName('iframe')["
                                                         + str(fnum) + "].attributes.length;")
            for i in range(num_attribs):
                attrib_name = self.marionette.execute_script("return document.getElementsByTagName('iframe')["
                                                             + str(fnum) + "].attributes[" + str(i) + "].nodeName;")
                attrib_value = self.marionette.execute_script("return document.getElementsByTagName('iframe')["
                                                              + str(fnum) + "].attributes[" + str(i) + "].nodeValue;")

                f.write("    |_ " + attrib_name.ljust(20) + ": \"" + attrib_value + "\"\n")
            f.close()

            #
            # Switch to this frame.
            #
            self.marionette.switch_to_frame(fnum)

            if appname == "keyboard":
                #
                # Take the html dump and save it to the file.
                #
                print "    |_ html dump saved to      : " + self.filename_htmldump
                f = open(self.filename_htmldump, 'w')
                f.write(self.marionette.page_source.encode('utf8', 'ignore'))
                f.close()
            else:
                self.record_frame()

            self.marionette.switch_to_frame()

    def record_frame(self):
        #
        # Take the screenshot and save it to the file.
        #
        print "    |_ screenshot saved to     : " + self.filename_screenshot
        screenshot = self.marionette.screenshot()
        with open(self.filename_screenshot, 'w') as f:
            f.write(base64.decodestring(screenshot))
        f.close()

        #
        # Take the html dump and save it to the file.
        #
        print "    |_ html dump saved to      : " + self.filename_htmldump
        f = open(self.filename_htmldump, 'w')
        f.write(self.marionette.page_source.encode('utf8', 'ignore'))
        f.close()
Esempio n. 14
0
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]'))
Esempio n. 15
0
class GCli(object):

    def __init__(self):
        self.commands = {
            'connectwifi': {
                'function': self.connect_to_wifi,
                'args': [
                    {'name': 'ssid',
                     'help': 'SSID of the network to connect to'},
                    {'name': '--security',
                     'choices': ['WPA-PSK', 'WEP'],
                     'help': 'Security model of the network'},
                    {'name': '--password',
                     'help': 'Password to access the network'}],
                'help': 'Connect to a WiFi network'},
            'disablewifi': {
                'function': self.disable_wifi,
                'help': 'Disable WiFi'},
            'enablewifi': {
                'function': self.enable_wifi,
                'help': 'Enable WiFi'},
            'forgetallwifi': {
                'function': self.forget_all_wifi_networks,
                'help': 'Forget all WiFi networks'},
            'getknownwifi': {
                'function': self.known_wifi_networks,
                'help': 'Show known WiFi networks'},
            'getsetting': {
                'function': self.get_setting,
                'args': [{
                    'name': 'name',
                    'help': 'Name of the setting to retrieve'}],
                'help': 'Show the current value of a setting'},
            'holdhome': {
                'function': self.hold_home,
                'help': 'Simulate holding the home button'},
            'holdsleep': {
                'function': self.hold_sleep,
                'help': 'Simulate holding the sleep button'},
            'home': {
                'function': self.home,
                'help': 'Simulate pressing the home button'},
            'killapps': {
                'function': self.kill_all_apps,
                'help': 'Kill all running apps'},
            'launchapp': {
                'function': self.launch_app,
                'args': [
                    {'name': 'name',
                     'nargs': argparse.REMAINDER,
                     'help': 'Name of app to launch'}],
                'help': 'Launch an application'},
            'listallapps': {
                'function': self.list_all_apps,
                'help': 'List all apps'},
            'listrunningapps': {
                'function': self.list_running_apps,
                'help': 'List the running apps'},
            'lock': {
                'function': self.lock,
                'help': 'Lock screen'},
            'screenshot': {
                'function': self.screenshot,
                'help': 'Take a screenshot'},
            'sendsms': {
                'function': self.send_sms,
                'args': [
                    {'name': 'number',
                     'help': 'Phone number of recipient'},
                    {'name': 'message',
                     'help': 'Message content'}],
                'help': 'Send an SMS'},
            'setsetting': {
                'function': self.set_setting,
                'args': [
                    {'name': 'name',
                     'help': 'Name of setting to change'},
                    {'name': 'value',
                     'help': 'New value for setting'}],
                'help': 'Change the value of a setting'},
            'sleep': {
                'function': self.sleep,
                'help': 'Enter sleep mode'},
            'unlock': {
                'function': self.unlock,
                'help': 'Unlock screen'},
            'volume': {
                'function': self.volume,
                'args': [
                    {'name': 'direction',
                     'choices': ['down', 'up'],
                     'help': 'Direction to change the volume'}],
                'help': 'Change the volume'},
            'wake': {
                'function': self.wake,
                'help': 'Wake from sleep mode'}}

        self.parser = argparse.ArgumentParser()
        self.add_options(self.parser)
        self.add_commands(self.parser)

    def run(self, args=sys.argv[1:]):
        args = self.parser.parse_args()

        host, port = args.address.split(':')
        self.marionette = Marionette(host=host, port=int(port))
        self.marionette.start_session()

        self.apps = gaiatest.GaiaApps(self.marionette)
        self.data_layer = gaiatest.GaiaData(self.marionette)
        self.device = gaiatest.GaiaDevice(self.marionette)

        ret = args.func(args)
        if ret is None:
            ret = 0

        self.marionette.delete_session()

        sys.exit(ret)

    def add_options(self, parser):
        parser.add_argument(
            '--address',
            default='localhost:2828',
            help='Address (host:port) of running Gecko instance to connect to '
                 '(default: %(default)s)')

    def add_commands(self, parser):
        subparsers = parser.add_subparsers(
            title='Commands', metavar='<command>')
        for (name, props) in sorted(self.commands.iteritems()):
            subparser = subparsers.add_parser(name, help=props['help'])
            if props.get('args'):
                for arg in props['args']:
                    kwargs = {k: v for k, v in arg.items() if k is not 'name'}
                    subparser.add_argument(arg['name'], **kwargs)
            subparser.set_defaults(func=props['function'])

    def connect_to_wifi(self, args):
        network = {
            'ssid': args.ssid,
            'keyManagement': args.security or 'NONE'}
        if args.security == 'WEP':
            network['wep'] = args.password
        elif args.security == 'WPA-PSK':
            network['psk'] = args.password
        self.data_layer.connect_to_wifi(network)

    def disable_wifi(self, args):
        self.data_layer.disable_wifi()

    def enable_wifi(self, args):
        self.data_layer.enable_wifi()

    def forget_all_wifi_networks(self, args):
        self.data_layer.forget_all_networks()

    def get_setting(self, args):
        print '%s: %s' % (
            args.name,
            self.data_layer.get_setting(args.name))

    def home(self, args):
        self.device.touch_home_button()

    def hold_home(self, args):
        self.device.hold_home_button()

    def hold_sleep(self, args):
        self.marionette.execute_script(
            "window.wrappedJSObject.dispatchEvent(new Event('holdsleep'));")

    def kill_all_apps(self, args):
        self.apps.kill_all()

    def known_wifi_networks(self, args):
        networks = [n for n in self.data_layer.known_networks if 'ssid' in n]
        if len(networks) > 0:
            for i, network in enumerate(networks):
                print '%s: %s' % (i + 1, network['ssid'])
        else:
            print 'No known networks.'

    def launch_app(self, args):
        for name in args.name:
            self.apps.launch(name)

    def list_all_apps(self, args):
        for i, app in enumerate(sorted(self.apps.installed_apps,
                                       key=lambda a: a.name.lower())):
            print '%d: %s' % (i + 1, app.name)

    def list_running_apps(self, args):
        for i, app in enumerate(sorted(self.apps.running_apps,
                                       key=lambda a: a.name.lower())):
            print '%d: %s' % (i + 1, app.name)

    def lock(self, args):
        self.device.lock()

    def screenshot(self, args):
        self.marionette.execute_script(
            "window.wrappedJSObject.dispatchEvent(new Event('volumedown+sleep'));")

    def send_sms(self, args):
        self.data_layer.send_sms(args.number, args.message)

    def set_setting(self, args):
        self.data_layer.set_setting(args.name, args.value)

    def sleep(self, args):
        self.marionette.execute_script(
            "window.wrappedJSObject.dispatchEvent(new Event('sleep'));")

    def unlock(self, args):
        self.device.unlock()

    def volume(self, args):
        self.marionette.execute_script(
            "window.wrappedJSObject.dispatchEvent(new Event('volume%s'));" %
            args.direction)

    def wake(self, args):
        self.marionette.execute_script(
            "window.wrappedJSObject.dispatchEvent(new Event('wake'));")
Esempio n. 16
0
def marionette(request):
    marionette = Marionette(bin=request.config.getoption('firefox_path'))
    marionette.start_session()
    request.node._marionette = marionette
    yield marionette
    marionette.cleanup()
Esempio n. 17
0
class Puppet:
    def __init__(self, binary: str, profile: str):
        self.__has_marionette = False
        self.__auto_download = False
        self.__download_dir = ""

        if not Path(binary).is_file():
            return

        if not Path(profile).is_dir():
            return

        # geckodriver の log ファイル出力を抑止する
        NO_LOG = "-"
        self.marionette = Marionette(bin=binary,
                                     gecko_log=NO_LOG,
                                     profile=profile)
        # start_session しないと quit もできない
        self.marionette.start_session()
        self.__has_marionette = True

    @property
    def has_marionette(self):
        return self.__has_marionette

    @property
    def auto_download(self):
        return self.__auto_download

    def __activate_auto_download(self):
        # 一度有効にすると同セッション内では無効にできない

        # firefox52 では MIME_TYPES.rdf, firefox60 では handlers.json に
        # ファイルダウンロード時の動作設定が記述されている(text/plain はプログラムで開く、など)
        # 自動ダウンロードするため既存の設定は削除する
        MIME_TYPES_HANDLERS = ["MIME_TYPES.rdf", "handlers.json"]
        for name in MIME_TYPES_HANDLERS:
            p = Path(self.marionette.profile_path).joinpath(name)
            if p.is_file():
                p.unlink()

        self.marionette.set_pref("browser.download.useDownloadDir", True)
        self.marionette.set_pref("browser.helperApps.neverAsk.saveToDisk",
                                 ",".join(MIME_TYPES))
        USER_DEFINED = 2
        self.marionette.set_pref("browser.download.folderList", USER_DEFINED)

    @property
    def download_dir(self):
        if self.__auto_download == False:
            raise Exception("auto download has not been activated")
        return self.__download_dir

    @download_dir.setter
    def download_dir(self, dir: str):
        p = Path(dir)
        if not p.is_dir():
            return

        full_path = str(p.resolve())
        if self.__auto_download == False:
            self.__activate_auto_download()
            self.__auto_download = True

        self.marionette.set_pref("browser.download.dir", full_path)
        self.__download_dir = full_path

    def set_download(self, dir: str):
        self.download_dir = dir

    def query_selector(self, selectors: str) -> HTMLElement:
        METHOD_CSS_SELECTOR = "css selector"
        return self.marionette.find_element(METHOD_CSS_SELECTOR, selectors)

    def query_selectors(self, selectors: str) -> List[HTMLElement]:
        METHOD_CSS_SELECTOR = "css selector"
        return self.marionette.find_elements(METHOD_CSS_SELECTOR, selectors)

    def wait(self, seconds: int):
        actions = Actions(self.marionette)
        actions.wait(seconds).perform()

    def quit(self):
        self.marionette.quit()

    def exec(self, script: str):

        # script 内での記述簡略化のため
        mrnt = self.marionette
        set_download = self.set_download
        wait = self.wait
        quit = self.quit
        query_selector = self.query_selector
        query_selectors = self.query_selectors

        exec(script)
Esempio n. 18
0
class Firefox(DesktopBrowser):
    """Firefox"""
    def __init__(self, path, options, job):
        DesktopBrowser.__init__(self, path, options, job)
        self.job = job
        self.task = None
        self.options = options
        self.path = path
        self.event_name = None
        self.moz_log = None
        self.marionette = None

    def prepare(self, job, task):
        """Prepare the profile/OS for the browser"""
        self.moz_log = os.path.join(task['dir'], task['prefix']) + '_moz.log'
        os.environ["MOZ_LOG_FILE"] = self.moz_log
        os.environ["MOZ_LOG"] = 'timestamp,sync,nsHttp:5,nsSocketTransport:5,nsStreamPump:5,'\
                                'nsHostResolver:5,pipnss:5'
        DesktopBrowser.prepare(self, job, task)
        profile_template = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), 'profiles', 'Firefox')
        if not task['cached'] and os.path.isdir(profile_template):
            try:
                if os.path.isdir(task['profile']):
                    shutil.rmtree(task['profile'])
                shutil.copytree(profile_template, task['profile'])
            except Exception:
                pass

    def launch(self, _job, task):
        """Launch the browser"""
        from marionette_driver.marionette import Marionette
        args = [
            '-profile', '"{0}"'.format(task['profile']), '-no-remote',
            '-width',
            str(task['width']), '-height',
            str(task['height']), '-marionette', 'about:blank'
        ]
        if self.path.find(' ') > -1:
            command_line = '"{0}"'.format(self.path)
        else:
            command_line = self.path
        command_line += ' ' + ' '.join(args)
        DesktopBrowser.launch_browser(self, command_line)
        self.marionette = Marionette('localhost', port=2828)
        self.marionette.start_session(timeout=60)
        self.marionette.navigate(START_PAGE)

    def stop(self, job, task):
        if self.marionette is not None:
            self.marionette.close()
        DesktopBrowser.stop(self, job, task)
        os.environ["MOZ_LOG_FILE"] = ''
        os.environ["MOZ_LOG"] = ''

    def run_task(self, task):
        """Run an individual test"""
        if self.marionette is not None:
            self.task = task
            logging.debug("Running test")
            end_time = monotonic.monotonic() + task['time_limit']
            task['current_step'] = 1
            recording = False
            while len(task['script']) and task['error'] is None and \
                    monotonic.monotonic() < end_time:
                self.prepare_task(task)
                command = task['script'].pop(0)
                if not recording and command['record']:
                    recording = True
                    self.on_start_recording(task)
                self.process_command(command)
                if command['record']:
                    self.wait_for_page_load()
                    if not task['combine_steps'] or not len(task['script']):
                        self.on_stop_recording(task)
                        recording = False
                        self.on_start_processing(task)
                        self.wait_for_processing(task)
                        if task['log_data']:
                            # Move on to the next step
                            task['current_step'] += 1
                            self.event_name = None
            # Always navigate to about:blank after finishing in case the tab is
            # remembered across sessions
            self.marionette.navigate('about:blank')
            self.task = None

    def wait_for_page_load(self):
        """Wait for the onload event from the extension"""
        pass

    def prepare_task(self, task):
        """Format the file prefixes for multi-step testing"""
        task['page_data'] = {}
        if task['current_step'] == 1:
            task['prefix'] = task['task_prefix']
            task['video_subdirectory'] = task['task_video_prefix']
        else:
            task['prefix'] = '{0}_{1:d}'.format(task['task_prefix'],
                                                task['current_step'])
            task['video_subdirectory'] = '{0}_{1:d}'.format(
                task['task_video_prefix'], task['current_step'])
        if task['video_subdirectory'] not in task['video_directories']:
            task['video_directories'].append(task['video_subdirectory'])
        if self.event_name is not None:
            task['step_name'] = self.event_name
        else:
            task['step_name'] = 'Step_{0:d}'.format(task['current_step'])

    def on_start_recording(self, task):
        """Notification that we are about to start an operation that needs to be recorded"""
        DesktopBrowser.on_start_recording(self, task)

    def on_stop_recording(self, task):
        """Notification that we are done with recording"""
        DesktopBrowser.on_stop_recording(self, task)

    def on_start_processing(self, task):
        """Start any processing of the captured data"""
        DesktopBrowser.on_start_processing(self, task)

    def wait_for_processing(self, task):
        """Wait for any background processing threads to finish"""
        DesktopBrowser.wait_for_processing(self, task)

    def process_command(self, command):
        """Process an individual script command"""
        logging.debug("Processing script command:")
        logging.debug(command)
        if command['command'] == 'navigate':
            self.marionette.navigate(command['target'])
        elif command['command'] == 'logdata':
            self.task['combine_steps'] = False
            if int(re.search(r'\d+', str(command['target'])).group()):
                logging.debug("Data logging enabled")
                self.task['log_data'] = True
            else:
                logging.debug("Data logging disabled")
                self.task['log_data'] = False
        elif command['command'] == 'combinesteps':
            self.task['log_data'] = True
            self.task['combine_steps'] = True
        elif command['command'] == 'seteventname':
            self.event_name = command['target']
        elif command['command'] == 'exec':
            self.marionette.execute_js_script(command['target'])
        elif command['command'] == 'sleep':
            delay = min(
                60,
                max(0, int(re.search(r'\d+', str(command['target'])).group())))
            if delay > 0:
                time.sleep(delay)
        elif command['command'] == 'setabm':
            self.task['stop_at_onload'] = \
                bool('target' in command and int(re.search(r'\d+',
                                                           str(command['target'])).group()) == 0)
        elif command['command'] == 'setactivitytimeout':
            if 'target' in command:
                self.task['activity_time'] = \
                    max(0, min(30, int(re.search(r'\d+', str(command['target'])).group())))
        elif command['command'] == 'setuseragent':
            self.task['user_agent_string'] = command['target']
        elif command['command'] == 'setcookie':
            if 'target' in command and 'value' in command:
                url = command['target'].strip()
                cookie = command['value']
                pos = cookie.find(';')
                if pos > 0:
                    cookie = cookie[:pos]
                pos = cookie.find('=')
                if pos > 0:
                    name = cookie[:pos].strip()
                    value = cookie[pos + 1:].strip()
                    if len(name) and len(value) and len(url):
                        self.marionette.add_cookie({
                            'url': url,
                            'name': name,
                            'value': value
                        })

    def navigate(self, url):
        """Navigate to the given URL"""
        if self.marionette is not None:
            self.marionette.navigate(url)
Esempio n. 19
0
#!/usr/bin/python2.7
#
# Script to work around Marionette bug 879816 (cannot click the modal 'ok' button
# following clicking something else).
#
from marionette_driver.marionette import Marionette
marionette = Marionette(host='localhost', port=2828)
marionette.start_session()
marionette.switch_to_frame()
marionette.execute_script(
    "document.getElementById('modal-dialog-prompt-ok').click();")
Esempio n. 20
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDevice, ADBProcessError, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = '10.0.2.2'
        PORT = 8888

        PATH_MAPPINGS = {
            '/js-input/webkit/PerformanceTests':
            'third_party/webkit/PerformanceTests',
        }

        dirs = self.query_abs_dirs()
        topsrcdir = os.path.join(dirs['abs_work_dir'], 'src')
        adb = self.query_exe('adb')

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(port=PORT,
                         docroot=os.path.join(topsrcdir, "build", "pgo"),
                         path_mappings=path_mappings)
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [
            os.path.join(profile_data_dir, profile, 'user.js')
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        outputdir = self.config.get('output_directory', '/sdcard')
        jarlog = posixpath.join(outputdir, 'en-US.log')
        profdata = posixpath.join(outputdir, 'default.profraw')

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        adbdevice = ADBDevice(adb=adb, device='emulator-5554')

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app='fennec',
                package_name=app,
                adb_path=adb,
                bin="target.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if 'Speedometer/index.html' in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                try:
                    localprof = '/builds/worker/workspace/default.profraw'
                    adbdevice.pull(profdata, localprof)
                    stats = os.stat(localprof)
                    if stats.st_size == 0:
                        # The file may not have been fully written yet, so retry until we
                        # get actual data.
                        time.sleep(2)
                    else:
                        break
                except ADBProcessError:
                    # The file may not exist at all yet, which would raise an
                    # ADBProcessError, so retry.
                    time.sleep(2)
            else:
                raise Exception("Unable to pull default.profraw")
            adbdevice.pull(jarlog, '/builds/worker/workspace/en-US.log')
        except ADBTimeoutError:
            self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError',
                       EXIT_STATUS_DICT[TBPL_RETRY])

        # We normally merge as part of a GENERATED_FILES step in the profile-use
        # build, but Android runs sometimes result in a truncated profile. We do
        # a merge here to make sure the data isn't corrupt so we can retry the
        # 'run' task if necessary.
        merge_cmd = [
            '/builds/worker/workspace/build/clang/bin/llvm-profdata',
            'merge',
            '/builds/worker/workspace/default.profraw',
            '-o',
            '/builds/worker/workspace/merged.profraw',
        ]
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                'INFRA-ERROR: Failed to merge profile data. Corrupt profile?',
                EXIT_STATUS_DICT[TBPL_RETRY])

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            'tar',
            '-acvf',
            '/builds/worker/artifacts/profdata.tar.xz',
            '-C',
            '/builds/worker/workspace',
            'merged.profraw',
            'en-US.log',
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
Esempio n. 21
0
class FirefoxMarionetteBase(object):
    """
    Wrap Marionette/Firefox into convenient interface.

    - https://marionette-client.readthedocs.io/
    - https://marionette-client.readthedocs.io/en/master/reference.html
    - https://marionette-client.readthedocs.io/en/master/interactive.html
    """
    def __init__(self):
        logger.info('Starting Marionette Gecko wrapper')

        # Configuration
        self.firefox_bin = self.find_firefox()
        self.firefox_host = 'localhost'
        self.firefox_port = 2828
        # TODO: Make configurable
        self.firefox_verbosity = 1
        #self.firefox_verbosity = 2

        # Timeout configuration
        self.startup_timeout = 20.0
        self.socket_timeout = 32.0
        self.page_timeout = 30.0
        self.script_timeout = 20.0
        self.shutdown_timeout = 10.0

        # Instance state defaults
        self.marionette = None
        self.firefox_run_headless = True
        self.firefox_do_shutdown = False
        self.firefox_already_started = False

    def enable_headless(self, run_headless=True):
        self.firefox_run_headless = run_headless

    def enable_shutdown(self, do_shutdown=True):
        self.firefox_do_shutdown = do_shutdown

    def boot_firefox(self, headless=True):

        # Indicate whether to run in headless mode
        self.enable_headless(headless)

        # Optionally shut down Marionette/Firefox after performing work
        # This will just be called if Python exits normally
        atexit.register(self.shutdown)

        # Check whether Firefox is already running
        logger.info(
            'Check for running instance of Marionette/Firefox at {}:{}'.format(
                self.firefox_host, self.firefox_port))

        if check_socket(self.firefox_host, self.firefox_port):
            logger.info('Will reuse running Marionette/Firefox')
            self.firefox_bin = None
            self.firefox_already_started = True
        else:
            logger.info('Will launch new Marionette/Firefox instance')

        # Connect to / start Marionette Gecko engine
        self.marionette = Marionette(host=self.firefox_host,
                                     port=self.firefox_port,
                                     bin=self.firefox_bin,
                                     socket_timeout=self.socket_timeout,
                                     startup_timeout=self.startup_timeout,
                                     headless=self.firefox_run_headless,
                                     verbose=self.firefox_verbosity)

        self.marionette.DEFAULT_SHUTDOWN_TIMEOUT = self.shutdown_timeout

        # Start a session with Marionette Gecko engine
        self.marionette.start_session()

        # Configure Marionette
        self.configure_marionette()

    def configure_marionette(self):

        # This specifies the time to wait for the page loading to complete.
        self.marionette.timeout.page_load = self.page_timeout

        # This specifies the time to wait for injected scripts to finish
        # before interrupting them.
        self.marionette.timeout.script = self.script_timeout

        # Configure a HTTP proxy server
        self.marionette.set_pref('network.proxy.type', 0, default_branch=True)

    @classmethod
    def find_firefox(cls):
        candidates = where.where('firefox')
        candidates += [
            '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
        ]
        firefox = find_program_candidate(candidates)
        logger.info('Found "firefox" program at {}'.format(firefox))
        return firefox

    def get_status(self):
        attributes = ['session', 'session_id']
        data = OrderedDict()
        for attribute in attributes:
            data[attribute] = getattr(self.marionette, attribute)
        return data

    def log_status(self):
        logger.info('Marionette report: {}'.format(
            json.dumps(self.get_status(), indent=4)))

    def has_active_session(self):
        is_initialized = self.marionette is not None and self.marionette.session_id is not None
        return is_initialized

    def ensure_session(self):
        #self.log_status()
        if not self.has_active_session():
            self.boot_firefox()
            logger.info(
                'No session with Marionette, started new session {}'.format(
                    self.marionette.session_id))

    def shutdown(self):
        if self.firefox_do_shutdown:

            logger.info('Aiming at shutdown')

            if self.firefox_already_started:
                logger.warning(
                    'Can not shutdown Firefox as it was already running before starting this program'
                )
                return False

            logger.info('Shutting down Marionette/Firefox')
            if self.marionette is not None:
                self.marionette.quit()
                return True

    def find_tag(self, tagname):
        try:
            element = self.marionette.find_element("tag name", tagname)
            return element
        except NoSuchElementException:
            pass

    def wait_for_element_tag(self, tagname):
        """
        Wait for element to appear.
        """
        waiter = Wait(self.marionette, timeout=20.0, interval=0.1)
        element = waiter.until(lambda m: self.find_tag(tagname))
        return element

    def render_image(self, element=None):
        """
        Return screenshot from element.
        """
        image = self.marionette.screenshot(element=element, format='binary')
        return image

    def set_window_size(self, width, height):
        self.marionette.set_window_rect(width=width, height=height)

    def get_window_rect(self):
        return self.marionette.window_rect
 def start_client():
     client = Marionette('localhost', port=2828)
     client.start_session()
     return client
Esempio n. 23
0
class MarionetteHelper:
    """ Helper for starting firefox and for remote browsing

    """

    def __init__(self, firefox_path, logger=None):
        self.logger = logger  # type: logging.Logger
        self.client = None  # type: Marionette
        self.ffpopen = None  # type: subprocess.Popen
        self.ffpath = firefox_path  # type: str
        if logger is None:
            self.logger = logging.getLogger("MarionetteHelper")
        self.logger.debug("Marionette helper init done")

    def _open_session(self, host='localhost', port=2828):
        """ Opens the session for marionette"""
        caps = {u'acceptInsecureCerts': True, }
        self.client = Marionette(host, port=port)
        self.client.start_session(capabilities=caps)

    def run_firefox(self):
        """ Start the firefox process"""
        self.logger.debug("Starting firefox process")
        self.ffpopen = subprocess.Popen([self.ffpath, "-marionette"])
        self.logger.debug("Opening marionette session")
        self._open_session()

    def quit_firefox(self):
        """ Close the firefox process and close marionette session"""
        #self.logger.debug("Closing firefox")
        #self.client._send_message("Marionette:Quit")
        self.client._request_in_app_shutdown("eForceQuit")
        self.client.delete_session(False)
        self.client.cleanup()
        self.client = None # reset client state
        #try:
        #    self.client.close()  # try to close the window anyway
        #except InvalidSessionIdException:
        #    pass
        #except socket.error:
        #    pass
        #finally:
        #    try:
        #        self.logger.debug("Closing marionette session")
        #        self.client.delete_session(False)  # close client session
        #    except InvalidSessionIdException:
        #        pass
        #    except socket.error:
        #        pass
        #    self.client = None  # reset client state
        #self.logger.debug("Waiting for firefox to close")
        #for _ in range(3):  # give the process 3 seconds to terminate
        #    time.sleep(1)
        #    self.ffpopen.poll()
        #    if self.ffpopen.returncode is not None:
        #        break
        #self.ffpopen.poll()
        #if self.ffpopen.returncode is None:
        #    self.logger.warning("Firefox not closed in time, killing it!")
        #    self.ffpopen.kill()  # process was not quit in time, kill it
        #self.ffpopen = None  # reset popen state
        #self.logger.debug("Firefox is closed")

    def ___get_client(self):
        """ Returns the internal marionette client object"""
        return self.client

    def navigate_to_url(self, url):
        """ Open an url in format of http[s]://example.com"""
        try:
            if "http" not in url:
                url = "http://" + url
            self.client.navigate(url)
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def back(self):
        """ Go a page backward"""
        try:
            self.client.go_back()
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def forward(self):
        """ Go a page forward"""
        try:
            self.client.go_forward()
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def follow_link(self, index=0):
        """ Click on a link"""
        try:
            links = self.client.find_elements(By.TAG_NAME, "a")
            links[index].click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except IndexError:
            self.logger.warning("Error: Out of bound")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_class_name(self, html_class_name):
        """ Click on first element via class name"""
        try:
            e = self.client.find_element(By.CLASS_NAME, html_class_name)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_css_selector(self, css_selector):
        """ Click on first element via css selector"""
        try:
            e = self.client.find_element(By.CSS_SELECTOR, css_selector)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_id(self, html_id):
        """ Click on first element via element id"""
        try:
            e = self.client.find_element(By.ID, html_id)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_name(self, html_name):
        """ Click on first element via element name"""
        try:
            e = self.client.find_element(By.NAME, html_name)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_tag_name(self, html_tag_name):
        """ Click on first element via tag name"""
        try:
            e = self.client.find_element(By.TAG_NAME, html_tag_name)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_xpath(self, xpath):
        """ Click on first element via xpath"""
        try:
            e = self.client.find_element(By.XPATH, xpath)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def click_element_by_link_text(self, html_link_text):
        """ Click on first element via link text"""
        try:
            e = self.client.find_element(By.LINK_TEXT, html_link_text)
            e.click()
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        except InsecureCertificateException:
            self.logger.warning("Warning: Insecure Certificate")
            return True
        except UnknownException as e:
            if "Reached error page" in str(e):
                self.logger.warning("Reached error page, ignoring...")
            else: # reraise, something very unexpected happened here
                t, v, tb = sys.exc_info()
                raise t, v, tb
            return False
        return True

    def send_keys_to_element_by_name(self, name, text):
        """ Sends text to an element via name"""
        try:
            e = self.client.find_element(By.NAME, name)
            e.send_keys(text)
        except ElementNotVisibleException:
            self.logger.warning("Error: Element not visible")
            return False
        except ElementNotSelectableException:
            self.logger.warning("Error: Element not selectable")
            return False
        except ElementNotAccessibleException:
            self.logger.warning("Error: Element not accessible")
            return False
        except ElementNotInteractableException:
            self.logger.warning("Error: Element not interactable")
            return False
        except NoSuchElementException:
            self.logger.warning("Error: Element does not exist")
            return False
        except TimeoutException:
            self.logger.warning("Error: Timeout")
            return False
        return True

    def select_window(self, index):
        """ Switch window via index"""
        try:
            self.client.switch_to_window(self.client.window_handles[index])
        except NoSuchWindowException:
            self.logger.warning("Error: Window does not exist")
            return False
        return True

    def close_window(self):
        """ Close the current window"""
        self.client.close()  # this won't close the last window, call quit_firefox instead

    def get_current_window_index(self):
        """ Get current windows index"""
        return self.client.window_handles.index(self.client.current_window_handle)

    def new_tab(self):
        """ Open a new empty tab"""
        with self.client.using_context("chrome"):
            self.client.find_element(By.ID, "menu_newNavigatorTab").click()

    def new_window(self):
        """ Open a new empty window"""
        with self.client.using_context("chrome"):
            self.client.execute_script("window.open();")

    def close_tab(self):
        """ Close the current tab"""
        self.close_window()  # basically the same as close windows
Esempio n. 24
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from marionette_driver.marionette import Marionette
import pdb

client = Marionette("localhost", port=2828)

client.start_session()

client.navigate("http://www.tianyancha.com/company/75507246")

element = client.find_element("class name", 'company_info')

print element.text

Esempio n. 25
0
class Puppet:
    MIME_TYPES = [
        "application/epub+zip",
        "application/gzip",
        "application/java-archive",
        "application/json",
        "application/ld+json",
        "application/msword",
        "application/octet-stream",
        "application/ogg",
        "application/pdf",
        "application/rtf",
        "application/vnd.amazon.ebook",
        "application/vnd.apple.installer+xml",
        "application/vnd.mozilla.xul+xml",
        "application/vnd.ms-excel",
        "application/vnd.ms-fontobject",
        "application/vnd.ms-powerpoint",
        "application/vnd.oasis.opendocument.presentation",
        "application/vnd.oasis.opendocument.spreadsheet",
        "application/vnd.oasis.opendocument.text",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "application/vnd.visio",
        "application/x-7z-compressed",
        "application/x-abiword",
        "application/x-bzip",
        "application/x-bzip2",
        "application/x-csh",
        "application/x-freearc",
        "application/xhtml+xml",
        "application/xml",
        "application/x-rar-compressed",
        "application/x-sh",
        "application/x-shockwave-flash",
        "application/x-tar",
        "application/zip",
        "appliction/php",
        "audio/aac",
        "audio/midi audio/x-midi",
        "audio/mpeg",
        "audio/ogg",
        "audio/wav",
        "audio/webm",
        "font/otf",
        "font/ttf",
        "font/woff",
        "font/woff2",
        "image/bmp",
        "image/gif",
        "image/jpeg",
        "image/png",
        "image/svg+xml",
        "image/tiff",
        "image/vnd.microsoft.icon",
        "image/webp",
        "text/calendar",
        "text/css",
        "text/csv",
        "text/html",
        "text/javascript",
        "text/javascript",
        "text/plain",
        "text/xml",
        "video/3gpp",
        "video/3gpp2",
        "video/mp2t",
        "video/mpeg",
        "video/ogg",
        "video/webm",
        "video/x-msvideo"
    ]
    METHOD_CSS_SELECTOR = "css selector"
    NO_LOG = "-"
    DELETE_TARGET_FILES = ["mimeTypes.rdf", "handlers.json"]
    USER_DEFINED = 2
    GECKO_LOG = Path(__file__).parent.resolve()

    def __init__(self, binary: str, profile: str):
        self.__has_session = False
        self.__auto_download = False
        self.__download_dir = ""

        if not Path(binary).is_file():
            print(f"Binary {binary} Not Found")
            return

        if not Path(profile).is_dir():
            print(f"Profile {profile} Not Found")
            return

        # geckodriver の log ファイル出力を抑止する
        self.marionette = Marionette(
            bin=binary, gecko_log=self.NO_LOG,  profile=profile)

        # start_session 前にファイルを消しておかないと
        # 後で自動ダウンロードできない
        self.__delete_download_profile()

        # start_session しないと quit もできない
        self.marionette.start_session()
        self.__has_session = True

    def __enter__(self):
        return self

    def __exit__(self, ex_type, ex_value, trace):
        if self.has_session:
            self.quit()

    @property
    def has_session(self):
        return self.__has_session

    @property
    def auto_download(self):
        return self.__auto_download

    def __delete_download_profile(self):
        # mimeTypes.rdf と handlers.json に
        # ファイル読み込み時の動作設定が保存されている(text/plain はファイルを保存、など)
        # 自動ダウンロードするため既存の設定は削除する
        for name in self.DELETE_TARGET_FILES:
            p = Path(self.marionette.profile_path).joinpath(name)
            if p.is_file():
                p.unlink()

    def __activate_auto_download(self):
        # 一度有効にすると同セッション内では無効にできない
        self.marionette.set_pref("browser.download.useDownloadDir", True)
        self.marionette.set_pref("browser.helperApps.neverAsk.saveToDisk",
                                 ",".join(self.MIME_TYPES))
        self.marionette.set_pref(
            "browser.download.folderList", self.USER_DEFINED)
        self.marionette.set_pref("browser.download.lastDir", None)
        self.__auto_download = True

    @property
    def download_dir(self):
        if self.__auto_download == False:
            raise Exception("auto download not activated")
        return self.__download_dir

    @download_dir.setter
    def download_dir(self, dir: str):
        p = Path(dir)
        if not p.is_dir():
            print(f"Download Dir {dir} Not Found")
            return

        full_path = str(p.resolve())
        if self.__auto_download == False:
            self.__activate_auto_download()
            # self.__auto_download = True

        self.marionette.set_pref("browser.download.dir", full_path)
        self.marionette.set_pref("browser.download.downloadDir", full_path)
        self.__download_dir = full_path

    def set_download(self, dir: str):
        self.download_dir = dir

    def query_selector(self, selectors: str) -> HTMLElement:
        return self.marionette.find_element(self.METHOD_CSS_SELECTOR, selectors)

    def query_selectors(self, selectors: str) -> List[HTMLElement]:
        return self.marionette.find_elements(self.METHOD_CSS_SELECTOR, selectors)

    def wait(self, seconds: int):
        actions = Actions(self.marionette)
        actions.wait(seconds).perform()

    def quit(self):
        profile = Path(self.marionette.profile_path)
        self.marionette.quit(clean=True)
        # self.__forced_rmdir(profile)
        # Path(self.GECKO_LOG).unlink()
        self.__has_session = False

    def exec(self, script: str) -> Optional[str]:
        # script 内での記述簡略化のため
        mrnt = self.marionette
        set_download = self.set_download
        wait = self.wait
        quit = self.quit
        query_selector = self.query_selector
        query_selectors = self.query_selectors

        try:
            exec(script)
            return None
        except Exception as err:
            return str(err.args[0])

    @classmethod
    def __forced_rmdir(self, p: Path):
        if p.is_dir():
            for f in p.iterdir():
                if f.is_file():
                    f.unlink()
                elif f.is_dir():
                    self.__forced_rmdir(f)
            p.rmdir()
Esempio n. 26
0
def marionette(request):
    m = Marionette(bin=request.config.option.bin)
    m.start_session()
    m.set_prefs({'signon.rememberSignons': False})
    request.addfinalizer(m.delete_session)
    return m
Esempio n. 27
0
class CommonTestCase(unittest.TestCase):

    __metaclass__ = MetaParameterized
    match_re = None
    failureException = AssertionError
    pydebugger = None

    def __init__(self, methodName, **kwargs):
        unittest.TestCase.__init__(self, methodName)
        self.loglines = []
        self.duration = 0
        self.expected = kwargs.pop('expected', 'pass')
        self.logger = get_default_logger()
        self.profile = FirefoxProfile()
        self.binary = kwargs.pop('binary', None)

    def _enter_pm(self):
        if self.pydebugger:
            self.pydebugger.post_mortem(sys.exc_info()[2])

    def _addSkip(self, result, reason):
        addSkip = getattr(result, 'addSkip', None)
        if addSkip is not None:
            addSkip(self, reason)
        else:
            warnings.warn(
                "TestResult has no addSkip method, skips not reported",
                RuntimeWarning, 2)
            result.addSuccess(self)

    def run(self, result=None):
        # Bug 967566 suggests refactoring run, which would hopefully
        # mean getting rid of this inner function, which only sits
        # here to reduce code duplication:
        def expected_failure(result, exc_info):
            addExpectedFailure = getattr(result, "addExpectedFailure", None)
            if addExpectedFailure is not None:
                addExpectedFailure(self, exc_info)
            else:
                warnings.warn(
                    "TestResult has no addExpectedFailure method, "
                    "reporting as passes", RuntimeWarning)
                result.addSuccess(self)

        self.start_time = time.time()
        orig_result = result
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            if startTestRun is not None:
                startTestRun()

        result.startTest(self)

        testMethod = getattr(self, self._testMethodName)
        if (getattr(self.__class__, "__unittest_skip__", False)
                or getattr(testMethod, "__unittest_skip__", False)):
            # If the class or method was skipped.
            try:
                skip_why = (
                    getattr(self.__class__, '__unittest_skip_why__', '')
                    or getattr(testMethod, '__unittest_skip_why__', ''))
                self._addSkip(result, skip_why)
            finally:
                result.stopTest(self)
            self.stop_time = time.time()
            return
        try:
            success = False
            try:
                if self.expected == "fail":
                    try:
                        self.setUp()
                    except Exception:
                        raise _ExpectedFailure(sys.exc_info())
                else:
                    self.setUp()
            except SkipTest as e:
                self._addSkip(result, str(e))
            except KeyboardInterrupt:
                raise
            except _ExpectedFailure as e:
                expected_failure(result, e.exc_info)
            except:
                self._enter_pm()
                result.addError(self, sys.exc_info())
            else:
                try:
                    if self.expected == 'fail':
                        try:
                            testMethod()
                        except:
                            raise _ExpectedFailure(sys.exc_info())
                        raise _UnexpectedSuccess
                    else:
                        testMethod()
                except self.failureException:
                    self._enter_pm()
                    result.addFailure(self, sys.exc_info())
                except KeyboardInterrupt:
                    raise
                except _ExpectedFailure as e:
                    expected_failure(result, e.exc_info)
                except _UnexpectedSuccess:
                    addUnexpectedSuccess = getattr(result,
                                                   'addUnexpectedSuccess',
                                                   None)
                    if addUnexpectedSuccess is not None:
                        addUnexpectedSuccess(self)
                    else:
                        warnings.warn(
                            "TestResult has no addUnexpectedSuccess method, reporting as failures",
                            RuntimeWarning)
                        result.addFailure(self, sys.exc_info())
                except SkipTest as e:
                    self._addSkip(result, str(e))
                except:
                    self._enter_pm()
                    result.addError(self, sys.exc_info())
                else:
                    success = True
                try:
                    if self.expected == "fail":
                        try:
                            self.tearDown()
                        except:
                            raise _ExpectedFailure(sys.exc_info())
                    else:
                        self.tearDown()
                except KeyboardInterrupt:
                    raise
                except _ExpectedFailure as e:
                    expected_failure(result, e.exc_info)
                except:
                    self._enter_pm()
                    result.addError(self, sys.exc_info())
                    success = False
            # Here we could handle doCleanups() instead of calling cleanTest directly
            self.cleanTest()

            if success:
                result.addSuccess(self)

        finally:
            result.stopTest(self)
            if orig_result is None:
                stopTestRun = getattr(result, 'stopTestRun', None)
                if stopTestRun is not None:
                    stopTestRun()

    @classmethod
    def match(cls, filename):
        """
        Determines if the specified filename should be handled by this
        test class; this is done by looking for a match for the filename
        using cls.match_re.
        """
        if not cls.match_re:
            return False
        m = cls.match_re.match(filename)
        return m is not None

    @classmethod
    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader,
                           testvars):
        """
        Adds all the tests in the specified file to the specified suite.
        """
        raise NotImplementedError

    @property
    def test_name(self):
        if hasattr(self, 'jsFile'):
            return os.path.basename(self.jsFile)
        else:
            return '{0}.py {1}.{2}'.format(self.__class__.__module__,
                                           self.__class__.__name__,
                                           self._testMethodName)

    def id(self):
        # TBPL starring requires that the "test name" field of a failure message
        # not differ over time. The test name to be used is passed to
        # mozlog via the test id, so this is overriden to maintain
        # consistency.
        return self.test_name

    def setUp(self):
        # Convert the marionette weakref to an object, just for the
        # duration of the test; this is deleted in tearDown() to prevent
        # a persistent circular reference which in turn would prevent
        # proper garbage collection.
        self.start_time = time.time()
        self.pingServer = PingServer()
        self.pingServer.start()
        self.marionette = Marionette(bin=self.binary, profile=self.profile)
        if self.marionette.session is None:
            self.marionette.start_session()
        self.marionette.reset_timeouts()

    def tearDown(self):
        self.marionette.cleanup()
        self.pingServer.stop()

    def cleanTest(self):
        self._deleteSession()

    def _deleteSession(self):
        if hasattr(self, 'start_time'):
            self.duration = time.time() - self.start_time
        if hasattr(self.marionette, 'session'):
            if self.marionette.session is not None:
                try:
                    self.loglines.extend(self.marionette.get_logs())
                except Exception, inst:
                    self.loglines = [['Error getting log: {}'.format(inst)]]
                try:
                    self.marionette.delete_session()
                except (socket.error, MarionetteException, IOError):
                    # Gecko has crashed?
                    self.marionette.session = None
                    try:
                        self.marionette.client.close()
                    except socket.error:
                        pass
        self.marionette = None
Esempio n. 28
0
class MarionetteHelper:
    def __init__(self, logger, success_symbol, failure_symbol):
        """
        Initialise the helper class.
        """
        self.client = None
        self.logger = logger
        self.success_symbol = success_symbol
        self.failure_symbol = failure_symbol

    def init_ff(self):
        """
        Initialises the connection to Firefox and starts a session.
        @return: -
        """
        if not check_socket(MARIONETTE_HOST, MARIONETTE_PORT):
            self.logger.error(
                u" > [ERROR] Please check if you started Firefox with the '-marionette' "
                "option or set 'marionette.enabled' to 'true' in 'about:config'. {}"
                .format(self.failure_symbol))
            sys.exit(1)
        self.client = Marionette(host=MARIONETTE_HOST, port=MARIONETTE_PORT)
        self.client.start_session()

    def get_existing_bookmarks(self):
        """
        Get the existing bookmarks from the Google Bookmarks API.
        We need to do this in Firefox to have the cookie set which authorities us with the API.
        @return: -
        """
        self.client.navigate(
            "https://www.google.com/bookmarks/?output=xml&num=10000")
        # Initialise XML object
        root = XML(self.client.page_source.encode("utf-8"))
        # Return set of bookmarks
        return set([bookmark[1].text for bookmark in root[0]])

    def save_button_contains_correct_text_save(self, *args):
        """
        Helper method for Marionette, here: check if fav button contains text "SAVE"
        @return: Whether or not the fav button contains the text "SAVE"
        """
        save_button = self.client.find_element(
            By.CLASS_NAME, "section-entity-action-save-button")
        return save_button.text == "SAVE"

    def save_button_contains_correct_text_saved(self, *args):
        """
        Helper method for Marionette, here: check if fav button contains text "SAVED"
        @return: Whether or not the fav button contains the text "SAVED"
        """
        save_button = self.client.find_element(
            By.CLASS_NAME, "section-entity-action-save-button")
        return save_button.text == "SAVED"

    def interactive_add_feature(self, coordinates):
        """
        Navigates to the Google Maps URL for the provided coordinates and waits for input.
        @return: -
        """
        url = "https://www.google.com/maps/search/?api=1&query={},{}"

        # This navigates Firefox to the passed URL
        self.client.navigate(url.format(coordinates[0], coordinates[1]))

        # Wait for input
        if sys.version_info[0] < 3:
            raw_input("Press Enter to continue...")
        else:
            input("Press Enter to continue...")

    def add_feature_2(self, url, list_add):
        """
        Tries to add a feature (bookmark / place) to your Google Maps fav list.
        @return: -
        """
        self.client.navigate(url)
        try:
            saved_button = Wait(self.client, timeout=1).until(
                expected.element_present(By.CSS_SELECTOR,
                                         "[data-value='Saved']"))
            self.logger.info(" > Feature was already saved")
            return utils.constants.ADD_FEATURE_ALREADY_ADDED
        except TimeoutException:
            pass
        try:
            save_button = Wait(self.client, timeout=5).until(
                expected.element_present(By.CSS_SELECTOR,
                                         "[data-value='Save']"))
            Wait(self.client,
                 timeout=5).until(expected.element_displayed(save_button))
        except TimeoutException:
            self.logger.error(" > Unable to find save button")
            return utils.constants.ADD_FEATURE_UNKNOWN_ERROR
        save_button.click()
        if list_add == utils.constants.LIST_STARRED_PLACES:
            data_index = 2
        elif list_add == utils.constants.LIST_WANT_TO_GO:
            data_index = 1
        else:
            data_index = -1
        css_selector = "#action-menu [data-index='{}']".format(data_index)
        sub_save_item = Wait(self.client, timeout=5).until(
            expected.element_present(By.CSS_SELECTOR, css_selector))
        Wait(self.client,
             timeout=5).until(expected.element_displayed(sub_save_item))
        sub_save_item.click()

    def add_feature(self, url):
        """
        Tries to add a feature (bookmark / place) to your Google Maps fav list.
        @return:
        - ADD_FEATURE_FAILURE if adding resulted in a known failure
        - ADD_FEATURE_SUCCESS if everything went fine
        - ADD_FEATURE_UNKNOWN_ERROR if we don't know what happened
        """

        # This navigates Firefox to the passed URL
        self.client.navigate(url)

        # We wait for the fav button to be present...
        save_button = Wait(self.client, timeout=10).until(
            expected.element_present(By.CLASS_NAME,
                                     "section-entity-action-save-button"))

        # ... and to be displayed
        displayed = Wait(self.client, timeout=10).until(
            expected.element_displayed(save_button))

        try:
            # Now we look for the correct text, it should say "SAVE"
            Wait(self.client,
                 timeout=6).until(self.save_button_contains_correct_text_save)
            try:
                # Click it to add the feature (bookmark / place) to the Google Maps fav list
                save_button.click()
            except NoSuchElementException:
                pass

            try:
                # Now the text should be "SAVED" and this indicates it was saved
                Wait(self.client, timeout=6).until(
                    self.save_button_contains_correct_text_saved)
            except TimeoutException:
                # We clicked but the fav button text didn't change, i.e. the click went wrong or timed out
                self.logger.error(" > [ERROR] Feature: '{}'".format(url))
                save_button = self.client.find_element(
                    By.CLASS_NAME, "section-entity-action-save-button")
                self.logger.error(
                    " > [ERROR] Save button didn't switch to 'SAVED', it contains '{}'"
                    .format(save_button.text))
                return ADD_FEATURE_FAILURE

            return ADD_FEATURE_SUCCESS

        except TimeoutException:
            # This is the case if the fave button didn't contain the text "SAVE".
            # This can happen if it contains "SAVED", but this shouldn't happen in the
            # first place because we don't try to add features if we know that they're
            # already added.
            # So most likely something truly went wrong here.
            self.logger.error(" > [ERROR] Feature: '{}'".format(url))
            save_button = self.client.find_element(
                By.CLASS_NAME, "section-entity-action-save-button")
            self.logger.error(
                " > [ERROR] Save button contained unknown text '{}'".format(
                    save_button.text))
            return ADD_FEATURE_UNKNOWN_ERROR
Esempio n. 29
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDeviceFactory, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = "10.0.2.2"
        PORT = 8888

        PATH_MAPPINGS = {
            "/js-input/webkit/PerformanceTests":
            "third_party/webkit/PerformanceTests",
        }

        dirs = self.query_abs_dirs()
        topsrcdir = dirs["abs_src_dir"]
        adb = self.query_exe("adb")

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(
            port=PORT,
            docroot=os.path.join(topsrcdir, "build", "pgo"),
            path_mappings=path_mappings,
        )
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, "testing", "profiles")
        with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh:
            base_profiles = json.load(fh)["profileserver"]

        prefpaths = [
            os.path.join(profile_data_dir, profile, "user.js")
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        outputdir = self.config.get("output_directory", "/sdcard/pgo_profile")
        jarlog = posixpath.join(outputdir, "en-US.log")
        profdata = posixpath.join(outputdir, "default_%p_random_%m.profraw")

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        if self.query_minidump_stackwalk():
            os.environ["MINIDUMP_STACKWALK"] = self.minidump_stackwalk_path
        os.environ["MINIDUMP_SAVE_PATH"] = self.query_abs_dirs(
        )["abs_blob_upload_dir"]
        if not self.symbols_path:
            self.symbols_path = os.environ.get("MOZ_FETCHES_DIR")

        # Force test_root to be on the sdcard for android pgo
        # builds which fail for Android 4.3 when profiles are located
        # in /data/local/tmp/test_root with
        # E AndroidRuntime: FATAL EXCEPTION: Gecko
        # E AndroidRuntime: java.lang.IllegalArgumentException: \
        #    Profile directory must be writable if specified: /data/local/tmp/test_root/profile
        # This occurs when .can-write-sentinel is written to
        # the profile in
        # mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java.
        # This is not a problem on later versions of Android. This
        # over-ride of test_root should be removed when Android 4.3 is no
        # longer supported.
        sdcard_test_root = "/sdcard/test_root"
        adbdevice = ADBDeviceFactory(adb=adb,
                                     device="emulator-5554",
                                     test_root=sdcard_test_root)
        if adbdevice.test_root != sdcard_test_root:
            # If the test_root was previously set and shared
            # the initializer will not have updated the shared
            # value. Force it to match the sdcard_test_root.
            adbdevice.test_root = sdcard_test_root
        adbdevice.mkdir(outputdir, parents=True)

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app="fennec",
                package_name=app,
                adb_path=adb,
                bin="geckoview-androidTest.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
                symbols_path=self.symbols_path,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if "Speedometer/index.html" in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                if not adbdevice.process_exist(app):
                    break
                time.sleep(2)
            else:
                raise Exception("Android App (%s) never quit" % app)

            # Pull all the profraw files and en-US.log
            adbdevice.pull(outputdir, "/builds/worker/workspace/")
        except ADBTimeoutError:
            self.fatal(
                "INFRA-ERROR: Failed with an ADBTimeoutError",
                EXIT_STATUS_DICT[TBPL_RETRY],
            )

        profraw_files = glob.glob("/builds/worker/workspace/*.profraw")
        if not profraw_files:
            self.fatal(
                "Could not find any profraw files in /builds/worker/workspace")
        merge_cmd = [
            os.path.join(os.environ["MOZ_FETCHES_DIR"],
                         "clang/bin/llvm-profdata"),
            "merge",
            "-o",
            "/builds/worker/workspace/merged.profdata",
        ] + profraw_files
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                "INFRA-ERROR: Failed to merge profile data. Corrupt profile?",
                EXIT_STATUS_DICT[TBPL_RETRY],
            )

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            "tar",
            "-acvf",
            "/builds/worker/artifacts/profdata.tar.xz",
            "-C",
            "/builds/worker/workspace",
            "merged.profdata",
            "en-US.log",
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
Esempio n. 30
0
def run_tests(firefox_path=None):
    basedir = os.path.dirname(__file__)

    if sys.platform == 'darwin' and os.path.isdir(firefox_path):
        firefox_path = os.path.join(firefox_path, 'Contents', 'MacOS',
                                    'firefox')

    driver = Marionette(app='fxdesktop',
                        bin=firefox_path,
                        gecko_log='-',
                        prefs={'xpinstall.signatures.required': False})
    driver.start_session()

    try:
        build1 = tempfile.NamedTemporaryFile(mode='wb',
                                             suffix='.xpi',
                                             delete=False)
        build2 = tempfile.NamedTemporaryFile(mode='wb',
                                             suffix='.xpi',
                                             delete=False)
        try:
            gulp_build(basedir, build1.name)
            jpm_build(basedir, os.path.join(basedir, 'testhelper'),
                      build2.name)

            addons = Addons(driver)
            addons.install(build1.name, temp=True)
            addons.install(build2.name, temp=True)
        finally:
            os.unlink(build1.name)
            os.unlink(build2.name)

        driver.expected = expected
        driver.keys = Keys

        class restore_url:
            def __enter__(self):
                self.url = driver.get_url()

            def __exit__(self, type, value, traceback):
                driver.navigate('about:blank')
                driver.navigate(self.url)

        driver.restore_url = restore_url

        def wait_until(method):
            Wait(driver, default_timeout).until(lambda d: method())

        driver.wait_until = wait_until

        def accept_alert():
            driver.switch_to_alert().accept()

        driver.accept_alert = accept_alert

        max_timestamp = {'value': 0}

        def get_urls():
            result = []
            prefix = '[testhelper] Loading: '
            new_timestamp = max_timestamp['value']
            with driver.using_context(driver.CONTEXT_CHROME):
                messages = driver.execute_script(
                    'return ' + 'Cc["@mozilla.org/consoleservice;1"]' +
                    '.getService(Ci.nsIConsoleService).getMessageArray()' +
                    '.map(m => m instanceof Ci.nsIScriptError ? ' +
                    '[m.timeStamp, m.errorMessage] : [null, null])')
            for timestamp, message in messages:
                if timestamp <= max_timestamp['value']:
                    continue
                if not message.startswith(prefix):
                    continue
                if timestamp > new_timestamp:
                    new_timestamp = timestamp
                result.append(message[len(prefix):])
            max_timestamp['value'] = new_timestamp
            return result

        driver.get_urls = get_urls

        def close_windows(keep):
            for h in [h for h in driver.chrome_window_handles if h != keep]:
                driver.switch_to_window(h)
                driver.close_chrome_window()
            driver.switch_to_window(keep)

        driver.close_windows = close_windows

        def close_background_tabs():
            current_tab = driver.current_window_handle
            for h in [h for h in driver.window_handles if h != current_tab]:
                driver.switch_to_window(h)
                driver.close()
            driver.switch_to_window(current_tab)

        driver.close_background_tabs = close_background_tabs

        def wait_for_load():
            code = 'return document.readyState == "complete";'
            driver.wait_until(lambda: driver.execute_script(code))

        driver.wait_for_load = wait_for_load

        def click(self):
            action = Actions(driver)
            action.click(self)
            action.perform()

        HTMLElement.click = click

        def middle_click(self):
            action = Actions(driver)
            action.middle_click(self)
            action.perform()

        HTMLElement.middle_click = middle_click

        def context_click(self):
            action = Actions(driver)
            action.context_click(self)
            action.perform()

        HTMLElement.context_click = context_click

        testdir = os.path.join(basedir, 'tests')
        for filename in os.listdir(testdir):
            if filename.startswith('.') or not filename.endswith('.py'):
                continue
            filepath = os.path.join(testdir, filename)
            globals = {}
            execfile(filepath, globals)
            globals['run'](driver)
    finally:
        driver.cleanup()
Esempio n. 31
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)
Esempio n. 32
0
def client():
    # On MacOS: /Applications/Firefox.app/Contents/MacOS/firefox -marionette
    client = Marionette(host='localhost', port=2828)
    client.start_session()
    return client
Esempio n. 33
0
def run_firefox(args):
    logger = logging.getLogger('sisyphus')
    logger.setLevel(logging.DEBUG)
    streamhandler = logging.StreamHandler(stream=sys.stdout)
    streamformatter = logging.Formatter(
        '%(asctime)s %(name)15s %(levelname)-8s %(message)s')
    streamhandler.setFormatter(streamformatter)
    logger.addHandler(streamhandler)

    # On Windows, make sure we point to the same temp directory as cygwin.
    if platform.system() == 'Windows':
        tempfile.tempdir = 'C:\\cygwin\\tmp'
    # Clean up tmpaddons
    tmpaddons = glob.glob(os.path.join(tempfile.gettempdir(), 'tmpaddon*'))
    for tmpaddon in tmpaddons:
        os.unlink(tmpaddon)

    # Work around Windows issues with shell metacharacters in url.
    if not args.url:
        if "URL" in os.environ:
            args.url = os.environ["URL"]
        else:
            logger.error("run_firefox: url is required")
            return

    # Load preferences of the form name=value from the
    # command line arguments.
    def eval_value(value):
        """Convert preference string value"""
        if value == 'true':
            value = True
        elif value == 'false':
            value = False
        else:
            try:
                value = eval(value)
            except NameError:
                # Leave string value alone.
                pass
        return value

    mozprofile_preferences = mozprofile.prefs.Preferences()

    preferences = {}
    set_preference_args = []
    for set_preference_arg in args.set_preferences:
        (name, value) = set_preference_arg.split('=', 1)
        set_preference_args.append((name, eval_value(value)))

    preferences.update(dict(set_preference_args))

    # Load preferences of from json files.
    for preference_json_arg in args.preference_jsons:
        preferences.update(
            dict(mozprofile_preferences.read_json(preference_json_arg)))

    # Load preferences from Firefox prefs.js/user.js files.
    for preference_file_arg in args.preference_files:
        preferences.update(
            dict(mozprofile_preferences.read_prefs(preference_file_arg)))

    logger.info("preferences: %s",
                json.dumps(preferences, indent=2, sort_keys=True))

    profile = mozprofile.profile.FirefoxProfile(profile=args.profile,
                                                preferences=preferences)
    client = Marionette(host='localhost',
                        port=2828,
                        bin=args.binary,
                        profile=profile,
                        gecko_log=args.gecko_log,
                        symbols_path=args.symbols_path)

    client.start_session()
    if args.restart:
        client.restart(clean=False, in_app=True)
    client.maximize_window()

    references = {'time_out_alarm_fired': False}

    if hasattr(signal, 'SIGALRM'):
        # Windows doesn't support SIGALRM. marionette
        # doesn't support cygwin paths...
        def timeout_handler(signum, frame):
            logger.warning("navigate: %s timed out" % args.url)
            references['time_out_alarm_fired'] = True
            client.quit()

        default_alarm_handler = signal.getsignal(signal.SIGALRM)

        signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(args.page_load_timeout + 2 * args.script_timeout)

    try:
        client.timeout.page_load = args.page_load_timeout
        client.timeout.script = args.script_timeout
        # Register the dialog closer for the browser. If the download
        # dialog appears, it will be closed and the browser window
        # will be closed. This forces marionette to return from
        # navigate and works around Bug 1366035. This version does
        # not dismiss normal Alerts which can be handled by Marionette's Alert.
        dialog_closer_script = """
var gDialogCloser;
var gDialogCloserObserver;
var gDialogCloserSubjects = [];

registerDialogCloser = function () {
  gDialogCloser = Components.classes['@mozilla.org/embedcomp/window-watcher;1'].getService(Components.interfaces.nsIWindowWatcher);
  gDialogCloserObserver = {observe: dialogCloser_observe};
  gDialogCloser.registerNotification(gDialogCloserObserver);
}

unregisterDialogCloser = function () {
  if (!gDialogCloserObserver || !gDialogCloser)
  {
    return;
  }

  gDialogCloser.unregisterNotification(gDialogCloserObserver);
  gDialogCloserObserver = null;
  gDialogCloser = null;
}

dialogCloser_observe = function (subject, topic, data) {
  if (subject instanceof ChromeWindow && topic == 'domwindowopened' )
  {
    gDialogCloserSubjects.push(subject);
    subject.setTimeout(closeDialog, 5000)
  }
}

closeDialog = function () {
  var subject;
  while ( (subject = gDialogCloserSubjects.pop()) != null)
  {
      if (subject.document instanceof XULDocument) {
          var uri = subject.document.documentURI;
          //if (uri.startsWith('chrome://') && uri.endsWith('ialog.xul')) {
          //    subject.close();
          //} else
          if (uri == 'chrome://mozapps/content/downloads/unknownContentType.xul') {
              dump('Sisyphus Runner: Closing Window due to download dialog\\n');
              subject.close();
              window.close();
          }
      }
  }
}

registerDialogCloser();
"""
        client.set_context(client.CONTEXT_CHROME)
        client.execute_script(dialog_closer_script,
                              new_sandbox=False,
                              script_timeout=client.timeout.script)
        client.set_context(client.CONTEXT_CONTENT)
        try:
            logger.info('New Page: %s' % args.url)
            client.navigate(args.url)
            client.maximize_window()
        except Exception, e:
            logger.warning('navigate: %s', e)

        # Do not call client.check_for_crash() as that will invoke
        # mozcrash which will delete the dump files. Handle the dumps
        # in the caller.
        client.set_context(client.CONTEXT_CONTENT)
        for content_script_url in args.content_scripts:
            content_script = get_remote_text(content_script_url)
            if content_script:
                try:
                    logger.info('<contentscript>\n%s\n</contentscript>',
                                content_script)
                    result = client.execute_script(
                        content_script,
                        script_args=[],
                        script_timeout=client.timeout.script)
                    logger.info('content script result: %s', result)
                except errors.ScriptTimeoutException, e:
                    logger.warning('content script: %s', e)
                except Exception, e:
                    logger.error('content script: %s', e)
Esempio n. 34
0
    def runApp(self,
               options,
               cmdargs=None,
               timeout=None,
               debuggerInfo=None,
               symbolsPath=None,
               valgrindPath=None,
               valgrindArgs=None,
               valgrindSuppFiles=None,
               **profileArgs):

        if cmdargs is None:
            cmdargs = []
        cmdargs = cmdargs[:]

        if self.use_marionette:
            cmdargs.append("-marionette")

        binary = options.app
        profile = self.createReftestProfile(options, **profileArgs)

        # browser environment
        env = self.buildBrowserEnv(options, profile.profile)

        self.log.info("Running with e10s: {}".format(options.e10s))
        self.log.info("Running with fission: {}".format(options.fission))

        def timeoutHandler():
            self.handleTimeout(timeout, proc, options.utilityPath,
                               debuggerInfo)

        interactive = False
        debug_args = None
        if debuggerInfo:
            interactive = debuggerInfo.interactive
            debug_args = [debuggerInfo.path] + debuggerInfo.args

        def record_last_test(message):
            """Records the last test seen by this harness for the benefit of crash logging."""
            def testid(test):
                if " " in test:
                    return test.split(" ")[0]
                return test

            if message["action"] == "test_start":
                self.lastTestSeen = testid(message["test"])
            elif message["action"] == "test_end":
                if self.lastTest and message["test"] == self.lastTest:
                    self.lastTestSeen = "Last test finished"
                else:
                    self.lastTestSeen = "{} (finished)".format(
                        testid(message["test"]))

        self.log.add_handler(record_last_test)

        kp_kwargs = {
            "kill_on_timeout": False,
            "cwd": SCRIPT_DIRECTORY,
            "onTimeout": [timeoutHandler],
            "processOutputLine": [self.outputHandler],
        }

        if mozinfo.isWin or mozinfo.isMac:
            # Prevents log interleaving on Windows at the expense of losing
            # true log order. See bug 798300 and bug 1324961 for more details.
            kp_kwargs["processStderrLine"] = [self.outputHandler]

        if interactive:
            # If an interactive debugger is attached,
            # don't use timeouts, and don't capture ctrl-c.
            timeout = None
            signal.signal(signal.SIGINT, lambda sigid, frame: None)

        runner_cls = mozrunner.runners.get(
            mozinfo.info.get("appname", "firefox"), mozrunner.Runner)
        runner = runner_cls(
            profile=profile,
            binary=binary,
            process_class=mozprocess.ProcessHandlerMixin,
            cmdargs=cmdargs,
            env=env,
            process_args=kp_kwargs,
        )
        runner.start(debug_args=debug_args,
                     interactive=interactive,
                     outputTimeout=timeout)
        proc = runner.process_handler
        self.outputHandler.proc_name = "GECKO({})".format(proc.pid)

        # Used to defer a possible IOError exception from Marionette
        marionette_exception = None

        if self.use_marionette:
            marionette_args = {
                "socket_timeout": options.marionette_socket_timeout,
                "startup_timeout": options.marionette_startup_timeout,
                "symbols_path": options.symbolsPath,
            }
            if options.marionette:
                host, port = options.marionette.split(":")
                marionette_args["host"] = host
                marionette_args["port"] = int(port)

            try:
                marionette = Marionette(**marionette_args)
                marionette.start_session()

                addons = Addons(marionette)
                if options.specialPowersExtensionPath:
                    addons.install(options.specialPowersExtensionPath,
                                   temp=True)

                addons.install(options.reftestExtensionPath, temp=True)

                marionette.delete_session()
            except IOError:
                # Any IOError as thrown by Marionette means that something is
                # wrong with the process, like a crash or the socket is no
                # longer open. We defer raising this specific error so that
                # post-test checks for leaks and crashes are performed and
                # reported first.
                marionette_exception = sys.exc_info()

        status = runner.wait()
        runner.process_handler = None
        self.outputHandler.proc_name = None

        if status:
            msg = (
                "TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s"
                % (self.lastTestSeen, status))
            # use process_output so message is logged verbatim
            self.log.process_output(None, msg)

        crashed = mozcrash.log_crashes(
            self.log,
            os.path.join(profile.profile, "minidumps"),
            options.symbolsPath,
            test=self.lastTestSeen,
        )
        if not status and crashed:
            status = 1

        runner.cleanup()
        self.cleanup(profile.profile)

        if marionette_exception is not None:
            exc, value, tb = marionette_exception
            raise reraise(exc, value, tb)

        self.log.info(
            "Process mode: {}".format("e10s" if options.e10s else "non-e10s"))
        return status
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]'))
Esempio n. 36
0
class Firefox(DesktopBrowser):
    """Firefox"""
    def __init__(self, path, options, job):
        DesktopBrowser.__init__(self, path, options, job)
        self.job = job
        self.task = None
        self.options = options
        self.path = path
        self.event_name = None
        self.moz_log = None
        self.marionette = None
        self.addons = None
        self.extension_id = None
        self.nav_error = None
        self.page_loaded = None
        self.recording = False
        self.connected = False
        self.start_offset = None
        self.browser_version = None
        self.log_pos = {}
        self.page = {}
        self.requests = {}
        self.last_activity = monotonic.monotonic()
        self.script_dir = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), 'js')
        self.start_page = 'http://127.0.0.1:8888/orange.html'

    def prepare(self, job, task):
        """Prepare the profile/OS for the browser"""
        self.moz_log = os.path.join(task['dir'], 'moz.log')
        self.log_pos = {}
        self.page = {}
        self.requests = {}
        os.environ["MOZ_LOG_FILE"] = self.moz_log
        os.environ["MOZ_LOG"] = 'timestamp,sync,nsHttp:5,nsSocketTransport:5'\
                                'nsHostResolver:5,pipnss:5'
        DesktopBrowser.prepare(self, job, task)
        profile_template = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), 'support', 'Firefox',
            'profile')
        if not task['cached'] and os.path.isdir(profile_template):
            try:
                if os.path.isdir(task['profile']):
                    shutil.rmtree(task['profile'])
                shutil.copytree(profile_template, task['profile'])
            except Exception:
                pass
        # delete any unsent crash reports
        crash_dir = None
        if platform.system() == 'Windows':
            if 'APPDATA' in os.environ:
                crash_dir = os.path.join(os.environ['APPDATA'], 'Mozilla',
                                         'Firefox', 'Crash Reports')
        else:
            crash_dir = os.path.join(os.path.expanduser('~'), '.mozilla',
                                     'firefox', 'Crash Reports')
        if crash_dir and os.path.isdir(crash_dir):
            logging.debug("Clearing crash reports in %s", crash_dir)
            try:
                shutil.rmtree(crash_dir)
            except Exception:
                pass
        # Prepare the config for the extension to query
        if self.job['message_server'] is not None:
            config = None
            names = [
                'block', 'block_domains', 'block_domains_except', 'headers',
                'cookies'
            ]
            for name in names:
                if name in task and task[name]:
                    if config is None:
                        config = {}
                    config[name] = task[name]
            self.job['message_server'].config = config

    def launch(self, job, task):
        """Launch the browser"""
        if self.job['message_server'] is not None:
            self.job['message_server'].flush_messages()
        self.connected = False
        from marionette_driver.marionette import Marionette
        from marionette_driver.addons import Addons
        args = [
            '-profile', '"{0}"'.format(task['profile']), '-no-remote',
            '-marionette', 'about:blank'
        ]
        if self.path.find(' ') > -1:
            command_line = '"{0}"'.format(self.path)
        else:
            command_line = self.path
        command_line += ' ' + ' '.join(args)
        DesktopBrowser.launch_browser(self, command_line)
        try:
            self.marionette = Marionette('localhost', port=2828)
            self.marionette.start_session(timeout=self.task['time_limit'])
            self.configure_prefs()
            logging.debug('Installing extension')
            self.addons = Addons(self.marionette)
            extension_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'support',
                'Firefox', 'extension')
            self.extension_id = self.addons.install(extension_path, temp=True)
            logging.debug('Resizing browser to %dx%d', task['width'],
                          task['height'])
            self.marionette.set_window_position(x=0, y=0)
            self.marionette.set_window_size(height=task['height'],
                                            width=task['width'])
            if 'browserVersion' in self.marionette.session_capabilities:
                self.browser_version = self.marionette.session_capabilities[
                    'browserVersion']
            self.marionette.navigate(self.start_page)
            time.sleep(0.5)
            self.wait_for_extension()
            if self.connected:
                # Override the UA String if necessary
                ua_string = self.execute_js('navigator.userAgent;')
                modified = False
                if 'uastring' in self.job:
                    ua_string = self.job['uastring']
                    modified = True
                if ua_string is not None and 'AppendUA' in task:
                    ua_string += ' ' + task['AppendUA']
                    modified = True
                if modified:
                    logging.debug(ua_string)
                    self.marionette.set_pref('general.useragent.override',
                                             ua_string)
                # Figure out the native viewport size
                size = self.execute_js(
                    "[window.innerWidth, window.innerHeight]")
                logging.debug(size)
                if size is not None and len(size) == 2:
                    task['actual_viewport'] = {
                        "width": size[0],
                        "height": size[1]
                    }
                    if 'adjust_viewport' in job and job['adjust_viewport']:
                        delta_x = max(task['width'] - size[0], 0)
                        delta_y = max(task['height'] - size[1], 0)
                        if delta_x or delta_y:
                            width = task['width'] + delta_x
                            height = task['height'] + delta_y
                            logging.debug('Resizing browser to %dx%d', width,
                                          height)
                            self.marionette.set_window_position(x=0, y=0)
                            self.marionette.set_window_size(height=height,
                                                            width=width)
                # Wait for the browser startup to finish
                DesktopBrowser.wait_for_idle(self)
        except Exception as err:
            task['error'] = 'Error starting Firefox: {0}'.format(err.__str__())

    def get_pref_value(self, value):
        """Convert a json pref value to Python"""
        str_match = re.match(r'^"(.*)"$', value)
        if value == 'true':
            value = True
        elif value == 'false':
            value = False
        elif re.match(r'^[\d]+$', value):
            value = int(value)
        elif str_match:
            value = str_match.group(1)
        else:
            value = None
        return value

    def configure_prefs(self):
        """Load the prefs file and configure them through marionette"""
        prefs = {}
        prefs_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                  'support', 'Firefox', 'profile', 'prefs.js')
        with open(prefs_file) as f_in:
            for line in f_in:
                matches = re.search(
                    r'user_pref\("([^"]+)",[\s]*([^\)]*)[\s]*\);', line)
                if matches:
                    key = matches.group(1).strip()
                    value = self.get_pref_value(matches.group(2).strip())
                    if value is not None:
                        prefs[key] = value
        if prefs:
            try:
                self.marionette.set_prefs(prefs, True)
            except Exception:
                pass

    def stop(self, job, task):
        """Kill the browser"""
        if self.extension_id is not None and self.addons is not None:
            try:
                self.addons.uninstall(self.extension_id)
            except Exception:
                pass
            self.extension_id = None
            self.addons = None
        if self.marionette is not None:
            try:
                self.marionette.close()
            except Exception:
                pass
            self.marionette = None
        DesktopBrowser.stop(self, job, task)
        # Make SURE the firefox processes are gone
        if platform.system() == "Linux":
            subprocess.call(['killall', '-9', 'firefox'])
            subprocess.call(['killall', '-9', 'firefox-trunk'])
        os.environ["MOZ_LOG_FILE"] = ''
        os.environ["MOZ_LOG"] = ''
        # delete the raw log files
        if self.moz_log is not None:
            files = sorted(glob.glob(self.moz_log + '*'))
            for path in files:
                try:
                    os.remove(path)
                except Exception:
                    pass

    def run_lighthouse_test(self, task):
        """Stub for lighthouse test"""
        pass

    def run_task(self, task):
        """Run an individual test"""
        if self.marionette is not None and self.connected:
            self.task = task
            logging.debug("Running test")
            end_time = monotonic.monotonic() + task['test_time_limit']
            task['current_step'] = 1
            recording = False
            while len(task['script']) and task['error'] is None and \
                    monotonic.monotonic() < end_time:
                self.prepare_task(task)
                command = task['script'].pop(0)
                if not recording and command['record']:
                    recording = True
                    self.on_start_recording(task)
                try:
                    self.process_command(command)
                except Exception:
                    logging.exception("Exception running task")
                if command['record']:
                    self.wait_for_page_load()
                    if not task['combine_steps'] or not len(task['script']):
                        self.on_stop_recording(task)
                        recording = False
                        self.on_start_processing(task)
                        self.wait_for_processing(task)
                        self.step_complete(task)
                        if task['log_data']:
                            # Move on to the next step
                            task['current_step'] += 1
                            self.event_name = None
                    task['navigated'] = True
            # Always navigate to about:blank after finishing in case the tab is
            # remembered across sessions
            try:
                self.marionette.navigate('about:blank')
            except Exception:
                logging.debug(
                    'Marionette exception navigating to about:blank after the test'
                )
            self.task = None

    def wait_for_extension(self):
        """Wait for the extension to send the started message"""
        if self.job['message_server'] is not None:
            end_time = monotonic.monotonic() + 30
            while monotonic.monotonic() < end_time:
                try:
                    self.job['message_server'].get_message(1)
                    logging.debug('Extension started')
                    self.connected = True
                    break
                except Exception:
                    pass

    def wait_for_page_load(self):
        """Wait for the onload event from the extension"""
        if self.job['message_server'] is not None and self.connected:
            start_time = monotonic.monotonic()
            end_time = start_time + self.task['time_limit']
            done = False
            while not done:
                try:
                    self.process_message(
                        self.job['message_server'].get_message(1))
                except Exception:
                    pass
                now = monotonic.monotonic()
                elapsed_test = now - start_time
                if self.nav_error is not None:
                    done = True
                    if self.page_loaded is None:
                        self.task['error'] = self.nav_error
                elif now >= end_time:
                    done = True
                    # only consider it an error if we didn't get a page load event
                    if self.page_loaded is None:
                        self.task['error'] = "Page Load Timeout"
                elif 'time' not in self.job or elapsed_test > self.job['time']:
                    elapsed_activity = now - self.last_activity
                    elapsed_page_load = now - self.page_loaded if self.page_loaded else 0
                    if elapsed_page_load >= 1 and elapsed_activity >= self.task[
                            'activity_time']:
                        done = True
                    elif self.task['error'] is not None:
                        done = True

    def execute_js(self, script):
        """Run javascipt"""
        ret = None
        if self.marionette is not None:
            try:
                ret = self.marionette.execute_script('return ' + script,
                                                     script_timeout=30)
            except Exception:
                pass
        return ret

    def run_js_file(self, file_name):
        """Execute one of our js scripts"""
        ret = None
        script = None
        script_file_path = os.path.join(self.script_dir, file_name)
        if os.path.isfile(script_file_path):
            with open(script_file_path, 'rb') as script_file:
                script = script_file.read()
        if script is not None:
            try:
                ret = self.marionette.execute_script('return ' + script,
                                                     script_timeout=30)
            except Exception:
                pass
            if ret is not None:
                logging.debug(ret)
        return ret

    def collect_browser_metrics(self, task):
        """Collect all of the in-page browser metrics that we need"""
        logging.debug("Collecting user timing metrics")
        user_timing = self.run_js_file('user_timing.js')
        if user_timing is not None:
            path = os.path.join(task['dir'],
                                task['prefix'] + '_timed_events.json.gz')
            with gzip.open(path, 'wb', 7) as outfile:
                outfile.write(json.dumps(user_timing))
        logging.debug("Collecting page-level metrics")
        page_data = self.run_js_file('page_data.js')
        if page_data is not None:
            task['page_data'].update(page_data)
        if 'customMetrics' in self.job:
            custom_metrics = {}
            for name in self.job['customMetrics']:
                logging.debug("Collecting custom metric %s", name)
                script = 'var wptCustomMetric = function() {' +\
                         self.job['customMetrics'][name] +\
                         '};try{return wptCustomMetric();}catch(e){};'
                try:
                    custom_metrics[name] = self.marionette.execute_script(
                        script, script_timeout=30)
                    if custom_metrics[name] is not None:
                        logging.debug(custom_metrics[name])
                except Exception:
                    pass
            path = os.path.join(task['dir'],
                                task['prefix'] + '_metrics.json.gz')
            with gzip.open(path, 'wb', 7) as outfile:
                outfile.write(json.dumps(custom_metrics))

    def process_message(self, message):
        """Process a message from the extension"""
        logging.debug(message)
        if self.recording:
            self.last_activity = monotonic.monotonic()
            try:
                # Make all of the timestamps relative to the test start to match the log events
                if 'timeStamp' in message['body']:
                    timestamp = message['body']['timeStamp']
                    seconds = int(timestamp / 1000)
                    milliseconds = timestamp - (seconds * 1000)
                    event_time = datetime.utcfromtimestamp(seconds)
                    event_time += timedelta(milliseconds=milliseconds)
                    elapsed = event_time - self.task['start_time']
                    message['body']['timeStamp'] = elapsed.total_seconds()
                cat, msg = message['path'].split('.', 1)
                if cat == 'webNavigation':
                    self.process_web_navigation(msg, message['body'])
                elif cat == 'webRequest':
                    self.process_web_request(msg, message['body'])
            except Exception:
                pass

    def process_web_navigation(self, message, evt):
        """Handle webNavigation.*"""
        if evt is not None:
            if message == 'onBeforeNavigate':
                if 'frameId' in evt and evt['frameId'] == 0:
                    self.page_loaded = None
                    logging.debug("Starting navigation")
                    if 'timeStamp' in evt and 'start' not in self.page:
                        self.page['start'] = evt['timeStamp']
            elif message == 'onCommitted':
                if 'timeStamp' in evt and 'frameId' in evt and evt['frameId'] == 0 \
                        and 'committed' not in self.page:
                    self.page['committed'] = evt['timeStamp']
                if 'injectScript' in self.job and self.marionette is not None:
                    logging.debug("Injecting script: \n%s",
                                  self.job['injectScript'])
                    try:
                        self.marionette.execute_script(
                            self.job['injectScript'], script_timeout=30)
                    except Exception:
                        pass
            elif message == 'onDOMContentLoaded':
                if 'timeStamp' in evt and 'frameId' in evt and evt[
                        'frameId'] == 0:
                    self.page['DOMContentLoaded'] = evt['timeStamp']
            elif message == 'onCompleted':
                if 'frameId' in evt and evt['frameId'] == 0:
                    self.page_loaded = monotonic.monotonic()
                    logging.debug("Page loaded")
                    if 'timeStamp' in evt:
                        self.page['loaded'] = evt['timeStamp']
            elif message == 'onErrorOccurred':
                if 'frameId' in evt and evt['frameId'] == 0:
                    self.page_loaded = monotonic.monotonic()
                    logging.debug("Page load failed")
                    if 'error' in evt:
                        self.nav_error = evt['error']
                    else:
                        self.nav_error = 'Navigation failed'

    def process_web_request(self, message, evt):
        """Handle webNavigation.*"""
        if evt is not None and 'requestId' in evt and 'timeStamp' in evt:
            if evt['requestId'] not in self.requests:
                self.requests[evt['requestId']] = {
                    'id': evt['requestId'],
                    'from_net': True
                }
            request = self.requests[evt['requestId']]
            if 'url' in evt and 'url' not in request:
                request['url'] = evt['url']
            if 'method' in evt and 'method' not in request:
                request['method'] = evt['method']
            if 'type' in evt and 'type' not in request:
                request['type'] = evt['type']
            if 'ip' in evt and 'ip' not in request:
                request['ip'] = evt['ip']
            if 'fromCache' in evt and evt['fromCache']:
                request['from_net'] = False
            if 'statusLine' in evt:
                request['status_line'] = evt['statusLine']
            if 'statusCode' in evt:
                request['status'] = evt['statusCode']
            if 'requestHeaders' in evt and 'request_headers' not in request:
                request['request_headers'] = list(evt['requestHeaders'])
            if 'responseHeaders' in evt and 'response_headers' not in request:
                request['response_headers'] = list(evt['responseHeaders'])

            if message == 'onBeforeRequest':
                request['created'] = evt['timeStamp']
            elif message == 'onSendHeaders':
                request['start'] = evt['timeStamp']
            elif message == 'onBeforeRedirect':
                if 'first_byte' not in request:
                    request['first_byte'] = evt['timeStamp']
                if 'end' not in request or evt['timeStamp'] > request['end']:
                    request['end'] = evt['timeStamp']
            elif message == 'onHeadersReceived':
                if 'first_byte' not in request:
                    request['first_byte'] = evt['timeStamp']
                if 'end' not in request or evt['timeStamp'] > request['end']:
                    request['end'] = evt['timeStamp']
            elif message == 'onResponseStarted':
                if 'first_byte' not in request:
                    request['first_byte'] = evt['timeStamp']
                if 'end' not in request or evt['timeStamp'] > request['end']:
                    request['end'] = evt['timeStamp']
            elif message == 'onCompleted':
                if 'first_byte' not in request:
                    request['first_byte'] = evt['timeStamp']
                if 'end' not in request or evt['timeStamp'] > request['end']:
                    request['end'] = evt['timeStamp']
            elif message == 'onErrorOccurred':
                if 'end' not in request or evt['timeStamp'] > request['end']:
                    request['end'] = evt['timeStamp']
                if 'error' in evt:
                    request['error'] = evt['error']
                if 'status' not in request:
                    request['status'] = 12999

    def prepare_task(self, task):
        """Format the file prefixes for multi-step testing"""
        if task['current_step'] == 1:
            task['prefix'] = task['task_prefix']
            task['video_subdirectory'] = task['task_video_prefix']
        else:
            task['prefix'] = '{0}_{1:d}'.format(task['task_prefix'],
                                                task['current_step'])
            task['video_subdirectory'] = '{0}_{1:d}'.format(
                task['task_video_prefix'], task['current_step'])
        if task['video_subdirectory'] not in task['video_directories']:
            task['video_directories'].append(task['video_subdirectory'])
        if self.event_name is not None:
            task['step_name'] = self.event_name
        else:
            task['step_name'] = 'Step_{0:d}'.format(task['current_step'])

    def on_start_recording(self, task):
        """Notification that we are about to start an operation that needs to be recorded"""
        # Clear the state
        self.page = {}
        self.requests = {}
        task['page_data'] = {'date': time.time()}
        task['page_result'] = None
        task['run_start_time'] = monotonic.monotonic()
        if self.browser_version is not None and 'browserVersion' not in task[
                'page_data']:
            task['page_data']['browserVersion'] = self.browser_version
            task['page_data']['browser_version'] = self.browser_version
        # Mark the start point in the various log files
        self.log_pos = {}
        if self.moz_log is not None:
            files = sorted(glob.glob(self.moz_log + '*'))
            for path in files:
                self.log_pos[path] = os.path.getsize(path)
        self.recording = True
        now = monotonic.monotonic()
        if not self.task['stop_at_onload']:
            self.last_activity = now
        if self.page_loaded is not None:
            self.page_loaded = now
        DesktopBrowser.on_start_recording(self, task)
        logging.debug('Starting measurement')
        task['start_time'] = datetime.utcnow()

    def on_stop_recording(self, task):
        """Notification that we are done with recording"""
        self.recording = False
        DesktopBrowser.on_stop_recording(self, task)
        if self.connected:
            if self.job['pngScreenShot']:
                screen_shot = os.path.join(task['dir'],
                                           task['prefix'] + '_screen.png')
                self.grab_screenshot(screen_shot, png=True)
            else:
                screen_shot = os.path.join(task['dir'],
                                           task['prefix'] + '_screen.jpg')
                self.grab_screenshot(screen_shot, png=False, resize=600)
        # Collect end of test data from the browser
        self.collect_browser_metrics(task)
        # Collect the interactive periods
        interactive = self.execute_js(
            'window.wrappedJSObject.wptagentGetInteractivePeriods();')
        if interactive is not None and len(interactive):
            interactive_file = os.path.join(
                task['dir'], task['prefix'] + '_interactive.json.gz')
            with gzip.open(interactive_file, 'wb', 7) as f_out:
                f_out.write(interactive)
        # Copy the log files
        if self.moz_log is not None:
            task['moz_log'] = os.path.join(task['dir'],
                                           task['prefix'] + '_moz.log')
            files = sorted(glob.glob(self.moz_log + '*'))
            for path in files:
                try:
                    base_name = os.path.basename(path)
                    dest = os.path.join(
                        task['dir'], task['prefix'] + '_' + base_name + '.gz')
                    start_pos = self.log_pos[
                        path] if path in self.log_pos else 0
                    end_pos = os.path.getsize(path)
                    if end_pos > start_pos:
                        length = end_pos - start_pos
                        logging.debug(
                            'Preparing moz log %s (%d bytes from %d)',
                            base_name, length, start_pos)
                        with open(path, 'rb') as f_in:
                            f_in.seek(start_pos)
                            with gzip.open(dest, 'wb', 7) as f_out:
                                while length > 0:
                                    read_bytes = min(length, 1024 * 1024)
                                    buff = f_in.read(read_bytes)
                                    read_bytes = len(buff)
                                    f_out.write(buff)
                                    length -= read_bytes
                except Exception:
                    pass

    def on_start_processing(self, task):
        """Start any processing of the captured data"""
        DesktopBrowser.on_start_processing(self, task)
        # Parse the moz log for the accurate request timings
        request_timings = []
        if 'moz_log' in task:
            from internal.support.firefox_log_parser import FirefoxLogParser
            parser = FirefoxLogParser()
            start_time = task['start_time'].strftime('%Y-%m-%d %H:%M:%S.%f')
            logging.debug('Parsing moz logs relative to %s start time',
                          start_time)
            request_timings = parser.process_logs(task['moz_log'], start_time)
            files = sorted(glob.glob(task['moz_log'] + '*'))
            for path in files:
                try:
                    os.remove(path)
                except Exception:
                    pass
        # Build the request and page data
        if len(request_timings) and task['current_step'] == 1:
            self.adjust_timings(request_timings)
        self.process_requests(request_timings, task)

    def adjust_timings(self, requests):
        """Adjust the request timings to start at zero for the earliest timestamp"""
        timestamps = [
            'dns_start', 'dns_end', 'connect_start', 'connect_end',
            'ssl_start', 'ssl_end', 'start', 'first_byte', 'end'
        ]
        earliest = None
        for request in requests:
            for entry in timestamps:
                if entry in request and request[entry] >= 0:
                    if earliest is None or request[entry] < earliest:
                        earliest = request[entry]
        logging.debug("Adjusting request timings by %0.3f seconds", earliest)
        if earliest is not None and earliest > 0:
            self.start_offset = earliest
            for request in requests:
                for entry in timestamps:
                    if entry in request and request[entry] >= 0:
                        request[entry] -= earliest

    def wait_for_processing(self, task):
        """Wait for any background processing threads to finish"""
        DesktopBrowser.wait_for_processing(self, task)

    def process_command(self, command):
        """Process an individual script command"""
        logging.debug("Processing script command:")
        logging.debug(command)
        if command['command'] == 'navigate':
            self.task['page_data']['URL'] = command['target']
            url = str(command['target']).replace('"', '\"')
            script = 'window.location="{0}";'.format(url)
            script = self.prepare_script_for_record(script)
            self.marionette.execute_script(script)
        elif command['command'] == 'logdata':
            self.task['combine_steps'] = False
            if int(re.search(r'\d+', str(command['target'])).group()):
                logging.debug("Data logging enabled")
                self.task['log_data'] = True
            else:
                logging.debug("Data logging disabled")
                self.task['log_data'] = False
        elif command['command'] == 'combinesteps':
            self.task['log_data'] = True
            self.task['combine_steps'] = True
        elif command['command'] == 'seteventname':
            self.event_name = command['target']
        elif command['command'] == 'exec':
            script = command['target']
            if command['record']:
                script = self.prepare_script_for_record(script)
            self.marionette.execute_script(script)
        elif command['command'] == 'sleep':
            delay = min(
                60,
                max(0, int(re.search(r'\d+', str(command['target'])).group())))
            if delay > 0:
                time.sleep(delay)
        elif command['command'] == 'setabm':
            self.task['stop_at_onload'] = \
                bool('target' in command and int(re.search(r'\d+',
                                                           str(command['target'])).group()) == 0)
        elif command['command'] == 'setactivitytimeout':
            if 'target' in command:
                milliseconds = int(
                    re.search(r'\d+', str(command['target'])).group())
                self.task['activity_time'] = max(
                    0, min(30,
                           float(milliseconds) / 1000.0))
        elif command['command'] == 'setuseragent':
            self.task['user_agent_string'] = command['target']
        elif command['command'] == 'firefoxpref':
            if 'target' in command and 'value' in command:
                self.set_pref(command['target'], command['value'])

    def navigate(self, url):
        """Navigate to the given URL"""
        if self.marionette is not None:
            try:
                self.marionette.navigate(url)
            except Exception as err:
                logging.debug("Error navigating Firefox: %s", str(err))

    def set_pref(self, key, value_str):
        """Set an individual pref value"""
        value = self.get_pref_value(value_str.strip())
        if value is not None:
            try:
                logging.debug('Setting Pref "%s" to %s', key, value_str)
                self.marionette.set_pref(key, value)
            except Exception:
                pass

    def grab_screenshot(self, path, png=True, resize=0):
        """Save the screen shot (png or jpeg)"""
        if self.marionette is not None:
            try:
                data = self.marionette.screenshot(format='binary', full=False)
                if data is not None:
                    resize_string = '' if not resize else '-resize {0:d}x{0:d} '.format(
                        resize)
                    if png:
                        with open(path, 'wb') as image_file:
                            image_file.write(data)
                        if len(resize_string):
                            cmd = '{0} -format png -define png:color-type=2 '\
                                '-depth 8 {1}"{2}"'.format(self.job['image_magick']['mogrify'],
                                                           resize_string, path)
                            logging.debug(cmd)
                            subprocess.call(cmd, shell=True)
                    else:
                        tmp_file = path + '.png'
                        with open(tmp_file, 'wb') as image_file:
                            image_file.write(data)
                        command = '{0} "{1}" {2}-quality {3:d} "{4}"'.format(
                            self.job['image_magick']['convert'], tmp_file,
                            resize_string, self.job['imageQuality'], path)
                        logging.debug(command)
                        subprocess.call(command, shell=True)
                        if os.path.isfile(tmp_file):
                            try:
                                os.remove(tmp_file)
                            except Exception:
                                pass
            except Exception as err:
                logging.debug('Exception grabbing screen shot: %s', str(err))

    def process_requests(self, request_timings, task):
        """Convert all of the request and page events into the format needed for WPT"""
        result = {}
        result['requests'] = self.merge_requests(request_timings)
        result['pageData'] = self.calculate_page_stats(result['requests'])
        devtools_file = os.path.join(
            task['dir'], task['prefix'] + '_devtools_requests.json.gz')
        with gzip.open(devtools_file, 'wb', 7) as f_out:
            json.dump(result, f_out)

    def get_empty_request(self, request_id, url):
        """Return and empty, initialized request"""
        parts = urlparse.urlsplit(url)
        request = {
            'type': 3,
            'id': request_id,
            'request_id': request_id,
            'ip_addr': '',
            'full_url': url,
            'is_secure': 1 if parts.scheme == 'https' else 0,
            'method': '',
            'host': parts.netloc,
            'url': parts.path,
            'responseCode': -1,
            'load_start': -1,
            'load_ms': -1,
            'ttfb_ms': -1,
            'dns_start': -1,
            'dns_end': -1,
            'dns_ms': -1,
            'connect_start': -1,
            'connect_end': -1,
            'connect_ms': -1,
            'ssl_start': -1,
            'ssl_end': -1,
            'ssl_ms': -1,
            'bytesIn': 0,
            'bytesOut': 0,
            'objectSize': 0,
            'initiator': '',
            'initiator_line': '',
            'initiator_column': '',
            'server_rtt': None,
            'headers': {
                'request': [],
                'response': []
            },
            'score_cache': -1,
            'score_cdn': -1,
            'score_gzip': -1,
            'score_cookies': -1,
            'score_keep-alive': -1,
            'score_minify': -1,
            'score_combine': -1,
            'score_compress': -1,
            'score_etags': -1,
            'gzip_total': None,
            'gzip_save': None,
            'minify_total': None,
            'minify_save': None,
            'image_total': None,
            'image_save': None,
            'cache_time': None,
            'cdn_provider': None,
            'server_count': None,
            'socket': -1
        }
        if len(parts.query):
            request['url'] += '?' + parts.query
        return request

    def get_header_value(self, headers, name):
        """Return the value for the given header"""
        value = ''
        name = name.lower()
        for header in headers:
            pos = header.find(':')
            if pos > 0:
                key = header[0:pos].lower()
                if key.startswith(name):
                    val = header[pos + 1:].strip()
                    if len(value):
                        value += '; '
                    value += val
        return value

    def merge_requests(self, request_timings):
        """Merge the requests from the extension and log files"""
        requests = []
        # Start with the requests reported from the extension
        for req_id in self.requests:
            try:
                req = self.requests[req_id]
                if req['from_net'] and 'start' in req and 'url' in req:
                    request = self.get_empty_request(req['id'], req['url'])
                    if 'ip' in req:
                        request['ip_addr'] = req['ip']
                    if 'method' in req:
                        request['method'] = req['method']
                    if 'status' in req:
                        request['responseCode'] = req['status']
                    if 'type' in req:
                        request['requestType'] = req['type']
                    if 'request_headers' in req:
                        for header in req['request_headers']:
                            if 'name' in header and 'value' in header:
                                header_text = '{0}: {1}'.format(
                                    header['name'], header['value'])
                                request['bytesOut'] += len(header_text) + 2
                                request['headers']['request'].append(
                                    header_text)
                    if 'status_line' in req:
                        request['bytesIn'] += len(req['status_line']) + 2
                        request['headers']['response'].append(
                            req['status_line'])
                    if 'response_headers' in req:
                        for header in req['response_headers']:
                            if 'name' in header and 'value' in header:
                                try:
                                    header_text = '{0}: {1}'.format(
                                        header['name'], header['value'])
                                    request['bytesIn'] += len(header_text) + 2
                                    request['headers']['response'].append(
                                        header_text)
                                except Exception:
                                    pass
                    if 'created' in req:
                        request['created'] = req['created']
                    request['load_start'] = int(round(req['start'] * 1000.0))
                    if 'first_byte' in req:
                        ttfb = int(
                            round((req['first_byte'] - req['start']) * 1000.0))
                        request['ttfb_ms'] = max(0, ttfb)
                    if 'end' in req:
                        load_time = int(
                            round((req['end'] - req['start']) * 1000.0))
                        request['load_ms'] = max(0, load_time)
                    size = self.get_header_value(
                        request['headers']['response'], 'Content-Length')
                    if len(size):
                        request['bytesIn'] += int(
                            re.search(r'\d+', str(size)).group())
                    requests.append(request)
            except Exception:
                pass
        # Overwrite them with the same requests from the logs
        for request in requests:
            for req in request_timings:
                try:
                    if 'claimed' not in req and 'url' in req and 'full_url' in request \
                            and 'start' in req and request['full_url'] == req['url']:
                        req['claimed'] = True
                        self.populate_request(request, req)
                except Exception:
                    pass
        # Add any events from the logs that weren't reported by the extension
        for req in request_timings:
            try:
                if 'claimed' not in req and 'url' in req and 'start' in req:
                    request = self.get_empty_request(req['id'], req['url'])
                    self.populate_request(request, req)
                    requests.append(request)
            except Exception:
                pass
        # parse values out of the headers
        for request in requests:
            try:
                value = self.get_header_value(request['headers']['response'],
                                              'Expires')
                if value:
                    request['expires'] = value
                value = self.get_header_value(request['headers']['response'],
                                              'Cache-Control')
                if value:
                    request['cacheControl'] = value
                value = self.get_header_value(request['headers']['response'],
                                              'Content-Type')
                if value:
                    request['contentType'] = value
                value = self.get_header_value(request['headers']['response'],
                                              'Content-Encoding')
                if value:
                    request['contentEncoding'] = value
                value = self.get_header_value(request['headers']['response'],
                                              'Content-Length')
                if value:
                    request['objectSize'] = value
            except Exception:
                pass
        requests.sort(key=lambda x: x['load_start'])
        return requests

    def populate_request(self, request, log_request):
        """Populate a request object from the log request values"""
        request['load_start'] = int(log_request['start'] * 1000)
        if 'status' in log_request:
            request['responseCode'] = log_request['status']
        if 'dns_start' in log_request and log_request['dns_start'] >= 0:
            request['dns_start'] = int(log_request['dns_start'] * 1000)
        if 'dns_end' in log_request and log_request['dns_end'] >= 0:
            request['dns_end'] = int(round(log_request['dns_end'] * 1000.0))
        if 'connect_start' in log_request and log_request['connect_start'] >= 0:
            request['connect_start'] = int(log_request['connect_start'] * 1000)
        if 'connect_end' in log_request and log_request['connect_end'] >= 0:
            request['connect_end'] = int(
                round(log_request['connect_end'] * 1000.0))
        if 'ssl_start' in log_request and log_request['ssl_start'] >= 0:
            request['ssl_start'] = int(log_request['ssl_start'] * 1000)
        if 'ssl_end' in log_request and log_request['ssl_end'] >= 0:
            request['ssl_end'] = int(round(log_request['ssl_end'] * 1000.0))
        if 'connection' in log_request:
            request['socket'] = log_request['connection']
        request['load_start'] = int(round(log_request['start'] * 1000.0))
        if 'first_byte' in log_request:
            request['ttfb_ms'] = int(round((log_request['first_byte'] - \
                                            log_request['start']) * 1000.0))
        if 'end' in log_request:
            request['load_ms'] = int(round((log_request['end'] - \
                                            log_request['start']) * 1000.0))
        if 'bytes_in' in log_request:
            request['bytesIn'] = log_request['bytes_in']
        if 'request_headers' in log_request:
            request['headers']['request'] = list(
                log_request['request_headers'])
        if 'response_headers' in log_request:
            request['headers']['response'] = list(
                log_request['response_headers'])

    def calculate_page_stats(self, requests):
        """Calculate the page-level stats"""
        page = {'loadTime': 0,
                'docTime': 0,
                'fullyLoaded': 0,
                'bytesOut': 0,
                'bytesOutDoc': 0,
                'bytesIn': 0,
                'bytesInDoc': 0,
                'requests': len(requests),
                'requestsDoc': 0,
                'responses_200': 0,
                'responses_404': 0,
                'responses_other': 0,
                'result': 0,
                'testStartOffset': 0,
                'cached': 1 if self.task['cached'] else 0,
                'optimization_checked': 0,
                'start_epoch': int((self.task['start_time'] - \
                                    datetime.utcfromtimestamp(0)).total_seconds())
               }
        if 'loaded' in self.page:
            page['loadTime'] = int(round(self.page['loaded'] * 1000.0))
            page['docTime'] = page['loadTime']
            page['loadEventStart'] = page['loadTime']
            page['loadEventEnd'] = page['loadTime']
        if 'DOMContentLoaded' in self.page:
            page['domContentLoadedEventStart'] = int(
                round(self.page['DOMContentLoaded'] * 1000.0))
            page['domContentLoadedEventEnd'] = page[
                'domContentLoadedEventStart']

        main_request = None
        index = 0
        for request in requests:
            if request['load_ms'] >= 0:
                end_time = request['load_start'] + request['load_ms']
                if end_time > page['fullyLoaded']:
                    page['fullyLoaded'] = end_time
                if end_time <= page['loadTime']:
                    page['requestsDoc'] += 1
                    page['bytesInDoc'] += request['bytesIn']
                    page['bytesOutDoc'] += request['bytesOut']
            page['bytesIn'] += request['bytesIn']
            page['bytesOut'] += request['bytesOut']
            if request['responseCode'] == 200:
                page['responses_200'] += 1
            elif request['responseCode'] == 404:
                page['responses_404'] += 1
                page['result'] = 99999
            elif request['responseCode'] > -1:
                page['responses_other'] += 1
            if main_request is None and \
                    (request['responseCode'] == 200 or request['responseCode'] == 304):
                main_request = request['id']
                request['is_base_page'] = True
                page['final_base_page_request'] = index
                page['final_base_page_request_id'] = main_request
                page['final_url'] = request['full_url']
                if 'URL' not in self.task['page_data']:
                    self.task['page_data']['URL'] = page['final_url']
                if request['ttfb_ms'] >= 0:
                    page['TTFB'] = request['load_start'] + request['ttfb_ms']
                if request['ssl_end'] >= request['ssl_start'] and \
                        request['ssl_start'] >= 0:
                    page['basePageSSLTime'] = int(round(request['ssl_end'] - \
                                                        request['ssl_start']))
        if page['responses_200'] == 0 and len(requests):
            if 'responseCode' in requests[0]:
                page['result'] = requests[0]['responseCode']
            else:
                page['result'] = 12999
        self.task['page_result'] = page['result']
        return page
Esempio n. 37
0
#!/usr/bin/python2.7
#
# Script to work around Marionette bug 879816 (cannot click the modal 'ok' button
# following clicking something else).
#
from marionette_driver.marionette import Marionette
marionette = Marionette(host='localhost', port=2828)
marionette.start_session()
marionette.switch_to_frame()
marionette.execute_script("document.getElementById('modal-dialog-prompt-ok').click();")
Esempio n. 38
0
from pprint import pprint
from marionette_driver import By
from marionette_driver.marionette import Marionette
from www.fileserver import FileServer

# Start services
FILE_SERVER = FileServer()

SERVICE_PORT = str(5000)
FILE_PORT = str(FILE_SERVER.port)
HOST = "http://localhost:"
ENDPOINT_PREFIX = HOST + SERVICE_PORT
TEST_HTML = HOST + FILE_PORT + "/"+ 'test_IAccessible.html'

CLIENT = Marionette(host='localhost', port=2828)
CLIENT.start_session()
CLIENT.navigate(TEST_HTML)

EVENT_PARAMS = urllib.urlencode({'interface': 'IAccessible', 'name': 'MSAA Checkbox', 'type' : 'EVENT_OBJECT_STATECHANGE'})
EVENT_ENDPOINT = ENDPOINT_PREFIX + "/event?%s"
CMD_PARAMS = urllib.urlencode({'interface': 'IAccessible', 'name': 'MSAA Checkbox', 'function': 'State'})
CMD_ENPOINT = ENDPOINT_PREFIX + "/cmd?%s"
ACCESSSIBLE_PARAMS = urllib.urlencode({'interface': 'IAccessible', 'name': 'MSAA Checkbox', 'depth': -1})
ACCESSIBLE_ENDPOINT = ENDPOINT_PREFIX + "/accessible?%s"

print "-----------------ACCESSIBLE------------------"
RESPONSE = json.load(urllib.urlopen(ACCESSIBLE_ENDPOINT % ACCESSSIBLE_PARAMS))
pprint(RESPONSE)
CHECKBOX = 0x2C
assert RESPONSE['IAccessible']['Role'] == CHECKBOX