class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' TIMEOUT_SEARCH = 'implicit' TIMEOUT_SCRIPT = 'script' TIMEOUT_PAGE = 'page load' def __init__(self, host='localhost', port=2828, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res='480x800', gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None, busybox=None): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self._test_name = None if bin: self.instance = GeckoInstance(host=self.host, port=self.port, bin=self.bin, profile=self.profile) self.instance.start() assert(self.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.setup(self, gecko_path=gecko_path) if busybox: self.emulator.install_busybox(busybox) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() @classmethod def getMarionetteOrExit(cls, *args, **kwargs): try: m = cls(*args, **kwargs) return m except InstallGeckoError: # Bug 812395 - the process of installing gecko into the emulator # and then restarting B2G tickles some bug in the emulator/b2g # that intermittently causes B2G to fail to restart. To work # around this in TBPL runs, we will fail gracefully from this # error so that the mozharness script can try the run again. # This string will get caught by mozharness and will cause it # to retry the tests. print "Error installing gecko!" # Exit without a normal exception to prevent mozharness from # flagging the error. sys.exit() def wait_for_port(self, timeout=3000): starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) data = sock.recv(16) sock.close() if '"from"' in data: time.sleep(5) return True except socket.error: pass time.sleep(1) return False def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = { 'type': command } if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"type": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = 'emulator' elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print ('PROCESS-CRASH | %s | abnormal termination with exit code %d' % (name, returncode)) return returncode is not None def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session @property def test_name(self): return self._test_name @test_name.setter def test_name(self, test_name): if self._send_message('setTestName', 'ok', value=test_name): self._test_name = test_name def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response @property def current_window_handle(self): self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): response = self._send_message('getTitle', 'value') return response @property def window_handles(self): response = self._send_message('getWindows', 'value') return response @property def page_source(self): response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None, focus=True): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id, focus=focus) else: response = self._send_message('switchToFrame', 'ok', value=frame, focus=focus) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def timeouts(self, timeout_type, ms): assert(timeout_type == self.TIMEOUT_SEARCH or timeout_type == self.TIMEOUT_SCRIPT or timeout_type == self.TIMEOUT_PAGE) response = self._send_message('timeouts', 'ok', timeoutType=timeout_type, ms=ms) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id } elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, async=True, new_sandbox=True, special_powers=False):
class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' def __init__(self, host='localhost', port=2828, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res='480x800', gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self.gecko_path = gecko_path if bin: self.instance = GeckoInstance(host=self.host, port=self.port, bin=self.bin, profile=self.profile) self.instance.start() assert(self.instance.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.wait_for_system_message(self) if self.gecko_path: self.emulator.install_gecko(self.gecko_path, self) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = { 'type': command } if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() if self.emulator: port = self.emulator.restart(self.local_port) if port is not None: self.port = self.client.port = port raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"type": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = 'emulator' elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print ('TEST-UNEXPECTED-FAIL - PROCESS CRASH - %s has terminated with exit code %d' % (name, returncode)) return returncode is not None def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response @property def current_window_handle(self): self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): response = self._send_message('getTitle', 'value') return response @property def window_handles(self): response = self._send_message('getWindows', 'value') return response @property def page_source(self): response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id) else: response = self._send_message('switchToFrame', 'ok', value=frame) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id } elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, timeout=True, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeJSScript', 'value', value=script, args=args, timeout=timeout, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def execute_script(self, script, script_args=None, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeScript', 'value', value=script, args=args, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def execute_async_script(self, script, script_args=None, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeAsyncScript', 'value', value=script, args=args, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def find_element(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElement', 'value', **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElements', 'value', **kwargs) assert(isinstance(response, list)) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements def log(self, msg, level=None): return self._send_message('log', 'ok', value=msg, level=level) def get_logs(self): return self._send_message('getLogs', 'value') def add_perf_data(self, suite, name, value): return self._send_message('addPerfData', 'ok', suite=suite, name=name, value=value) def get_perf_data(self): return self._send_message('getPerfData', 'value') def import_script(self, js_file): js = '' with open(js_file, 'r') as f: js = f.read() return self._send_message('importScript', 'ok', script=js) @property def application_cache(self): return ApplicationCache(self)
class Marionette(object): """ Represents a Marionette connection to a browser or device. """ CONTEXT_CHROME = 'chrome' # non-browser content: windows, dialogs, etc. CONTEXT_CONTENT = 'content' # browser content: iframes, divs, etc. TIMEOUT_SEARCH = 'implicit' TIMEOUT_SCRIPT = 'script' TIMEOUT_PAGE = 'page load' def __init__(self, host='localhost', port=2828, app=None, app_args=None, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res=None, gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None, busybox=None, symbols_path=None, timeout=None, device_serial=None): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self._test_name = None self.symbols_path = symbols_path self.timeout = timeout self.device_serial = device_serial if bin: port = int(self.port) if not Marionette.is_port_available(port, host=self.host): ex_msg = "%s:%d is unavailable." % (self.host, port) raise MarionetteException(message=ex_msg) if app: # select instance class for the given app try: instance_class = geckoinstance.apps[app] except KeyError: msg = 'Application "%s" unknown (should be one of %s)' raise NotImplementedError(msg % (app, geckoinstance.apps.keys())) else: instance_class = geckoinstance.GeckoInstance self.instance = instance_class(host=self.host, port=self.port, bin=self.bin, profile=self.profile, app_args=app_args) self.instance.start() assert(self.wait_for_port()), "Timed out waiting for port!" if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()), "Timed out waiting for port!" if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()), "Timed out waiting for port!" self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.setup(self, gecko_path=gecko_path, busybox=busybox) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() @staticmethod def is_port_available(port, host=''): port = int(port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind((host, port)) return True except socket.error: return False finally: s.close() @classmethod def getMarionetteOrExit(cls, *args, **kwargs): try: m = cls(*args, **kwargs) return m except InstallGeckoError: # Bug 812395 - the process of installing gecko into the emulator # and then restarting B2G tickles some bug in the emulator/b2g # that intermittently causes B2G to fail to restart. To work # around this in TBPL runs, we will fail gracefully from this # error so that the mozharness script can try the run again. # This string will get caught by mozharness and will cause it # to retry the tests. print "Error installing gecko!" # Exit without a normal exception to prevent mozharness from # flagging the error. sys.exit() def wait_for_port(self, timeout=30): starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) data = sock.recv(16) sock.close() if '"from"' in data: time.sleep(5) return True except socket.error: pass time.sleep(1) return False def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = { 'name': command } if self.session: message['sessionId'] = self.session if kwargs: message['parameters'] = kwargs try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"name": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: raise MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_NOT_INITIALIZED_ERROR: raise FrameSendNotInitializedError(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_FAILURE_ERROR: raise FrameSendFailureError(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None crashed = False if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = 'emulator' crashed = True if self.symbols_path and self.emulator.check_for_minidumps(self.symbols_path): crashed = True elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print ('PROCESS-CRASH | %s | abnormal termination with exit code %d' % (name, returncode)) return crashed def absolute_url(self, relative_url): ''' Returns an absolute url for files served from Marionette's www directory. :param relative_url: The url of a static file, relative to Marionette's www directory. ''' return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): ''' Creates a new Marionette session. You must call this method before performing any other action. ''' try: # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') except: exc, val, tb = sys.exc_info() self.check_for_crash() raise exc, val, tb self.b2g = 'b2g' in self.session return self.session @property def test_name(self): return self._test_name @test_name.setter def test_name(self, test_name): if self._send_message('setTestName', 'ok', value=test_name): self._test_name = test_name def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): ''' A JSON dictionary representing the capabilities of the current session. ''' response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): ''' Sets the maximum number of ms that an asynchronous script is allowed to run. If a script does not return in the specified amount of time, a ScriptTimeoutException is raised. :param timeout: The maximum number of milliseconds an asynchronous script can run without causing an ScriptTimeoutException to be raised ''' response = self._send_message('setScriptTimeout', 'ok', ms=timeout) return response def set_search_timeout(self, timeout): ''' Sets a timeout for the find methods. When searching for an element using either :class:`Marionette.find_element` or :class:`Marionette.find_elements`, the method will continue trying to locate the element for up to timeout ms. This can be useful if, for example, the element you're looking for might not exist immediately, because it belongs to a page which is currently being loaded. :param timeout: Timeout in milliseconds. ''' response = self._send_message('setSearchTimeout', 'ok', ms=timeout) return response @property def current_window_handle(self): ''' A reference to the current window. ''' self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): ''' Current title of the active window. ''' response = self._send_message('getTitle', 'value') return response @property def window_handles(self): ''' A list of references to all available browser windows if called in content context. If called while in the chrome context, it will list all available windows, not just browser windows (ie: not just 'navigator:browser';). ''' response = self._send_message('getWindows', 'value') return response @property def page_source(self): ''' A string representation of the DOM. ''' response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): ''' Closes the window that is in use by Marionette. :param window_id: id of the window you wish to closed ''' if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): ''' Sets the context that marionette commands are running in. :param context: Context, may be one of the class properties `CONTEXT_CHROME` or `CONTEXT_CONTENT`. Usage example: :: marionette.set_context(marionette.CONTEXT_CHROME) ''' assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): ''' Switch to the specified window; subsequent commands will be directed at the new window. :param window_id: The id or name of the window to switch to. ''' response = self._send_message('switchToWindow', 'ok', name=window_id) self.window = window_id return response def get_active_frame(self): ''' Returns an HTMLElement representing the frame Marionette is currently acting on. ''' response = self._send_message('getActiveFrame', 'value') if response: return HTMLElement(self, response) return None def switch_to_default_content(self): ''' Switch the current context to page's default content. ''' return self.switch_to_frame() def switch_to_frame(self, frame=None, focus=True): ''' Switch the current context to the specified frame. Subsequent commands will operate in the context of the specified frame, if applicable. :param frame: A reference to the frame to switch to: this can be an HTMLElement, an index, name or an id attribute. If you call switch_to_frame() without an argument, it will switch to the top-level frame. :param focus: A boolean value which determins whether to focus the frame that we just switched to. ''' if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id, focus=focus) else: response = self._send_message('switchToFrame', 'ok', id=frame, focus=focus) return response def get_url(self): ''' Returns the url of the active page in the browser. ''' response = self._send_message('getUrl', 'value') return response def get_window_type(self): ''' Gets the windowtype attribute of the window Marionette is currently acting on. This command only makes sense in a chrome context. You might use this method to distinguish a browser window from an editor window. ''' response = self._send_message('getWindowType', 'value') return response def navigate(self, url): ''' Causes the browser to navigate to the specified url. :param url: The url to navigate to. ''' response = self._send_message('goUrl', 'ok', url=url) return response def timeouts(self, timeout_type, ms): assert(timeout_type == self.TIMEOUT_SEARCH or timeout_type == self.TIMEOUT_SCRIPT or timeout_type == self.TIMEOUT_PAGE) response = self._send_message('timeouts', 'ok', type=timeout_type, ms=ms) return response def go_back(self): ''' Causes the browser to perform a back navigation. ''' response = self._send_message('goBack', 'ok') return response def go_forward(self): ''' Causes the browser to perform a forward navigation. ''' response = self._send_message('goForward', 'ok') return response def refresh(self): ''' Causes the browser to perform to refresh the current page. ''' response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id } elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, async=True, new_sandbox=True, special_powers=False, script_timeout=None, inactivity_timeout=None, filename=None):
class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' def __init__(self, host='localhost', port=2828, b2gbin=False, emulator=None, emulatorBinary=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None): self.host = host self.port = self.local_port = port self.b2gbin = b2gbin self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir if b2gbin: self.b2ginstance = B2GInstance(host=self.host, port=self.port, b2gbin=self.b2gbin) self.b2ginstance.start() assert(self.b2ginstance.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, emulatorBinary=emulatorBinary) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) def __del__(self): if self.emulator: self.emulator.close() if self.b2gbin: self.b2ginstance.close() for qemu in self.extra_emulators: qemu.emulator.close() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = { 'type': command } if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() if self.emulator: port = self.emulator.restart(self.local_port) if port is not None: self.port = self.client.port = port raise TimeoutException(message='socket.timeout', status=21, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"type": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == 7: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == 8: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == 10: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == 11: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == 17: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == 19: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == 21: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == 23: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == 28: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response def get_session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response def get_window(self): self.window = self._send_message('getWindow', 'value') return self.window def get_windows(self): response = self._send_message('getWindows', 'value') return response def close_window(self, window_id=None): if not window_id: window_id = self.get_window() response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id) else: response = self._send_message('switchToFrame', 'ok', value=frame) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id } elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, timeout=True, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeJSScript', 'value', value=script, args=args, timeout=timeout, newSandbox=new_sandbox) return self.unwrapValue(response) def execute_script(self, script, script_args=None, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeScript', 'value', value=script, args=args, newSandbox=new_sandbox) return self.unwrapValue(response) def execute_async_script(self, script, script_args=None, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeAsyncScript', 'value', value=script, args=args, newSandbox=new_sandbox) return self.unwrapValue(response) def find_element(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElement', 'value', **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElements', 'value', **kwargs) assert(isinstance(response, list)) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements def log(self, msg, level=None): return self._send_message('log', 'ok', value=msg, level=level) def get_logs(self): return self._send_message('getLogs', 'value')
class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' def __init__(self, host='localhost', port=2828, b2gbin=False, emulator=None, emulatorBinary=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None): self.host = host self.port = self.local_port = port self.b2gbin = b2gbin self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir if b2gbin: self.b2ginstance = B2GInstance(host=self.host, port=self.port, b2gbin=self.b2gbin) self.b2ginstance.start() assert (self.b2ginstance.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, emulatorBinary=emulatorBinary) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) def __del__(self): if self.emulator: self.emulator.close() if self.b2gbin: self.b2ginstance.close() for qemu in self.extra_emulators: qemu.emulator.close() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = {'type': command} if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() if self.emulator: port = self.emulator.restart(self.local_port) if port is not None: self.port = self.client.port = port raise TimeoutException(message='socket.timeout', status=21, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException( message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({ "type": "emulatorCmdResult", "id": response.get("id"), "result": result }) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == 7: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == 8: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == 10: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == 11: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == 17: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == 19: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == 21: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == 23: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == 28: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response def get_session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response def get_window(self): self.window = self._send_message('getWindow', 'value') return self.window def get_windows(self): response = self._send_message('getWindows', 'value') return response def close_window(self, window_id=None): if not window_id: window_id = self.get_window() response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert (context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id) else: response = self._send_message('switchToFrame', 'ok', value=frame) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id} elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, timeout=True, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeJSScript', 'value', value=script, args=args, timeout=timeout, newSandbox=new_sandbox) return self.unwrapValue(response) def execute_script(self, script, script_args=None, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeScript', 'value', value=script, args=args, newSandbox=new_sandbox) return self.unwrapValue(response) def execute_async_script(self, script, script_args=None, new_sandbox=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeAsyncScript', 'value', value=script, args=args, newSandbox=new_sandbox) return self.unwrapValue(response) def find_element(self, method, target, id=None): kwargs = {'value': target, 'using': method} if id: kwargs['element'] = id response = self._send_message('findElement', 'value', **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = {'value': target, 'using': method} if id: kwargs['element'] = id response = self._send_message('findElements', 'value', **kwargs) assert (isinstance(response, list)) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements def log(self, msg, level=None): return self._send_message('log', 'ok', value=msg, level=level) def get_logs(self): return self._send_message('getLogs', 'value')
class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' def __init__(self, host='localhost', port=2828, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res='480x800', gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None, busybox=None, load_early=False): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self._test_name = None if bin: port = int(self.port) if not Marionette.is_port_available(port, host=self.host): ex_msg = "%s:%d is unavailable." % (self.host, port) raise MarionetteException(message=ex_msg) self.instance = GeckoInstance(host=self.host, port=self.port, bin=self.bin, profile=self.profile) self.instance.start() assert (self.instance.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.setup(self, gecko_path=gecko_path, load_early=load_early) if busybox: self.emulator.install_busybox(busybox) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() @staticmethod def is_port_available(port, host=''): port = int(port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind((host, port)) return True except socket.error: return False finally: s.close() @classmethod def getMarionetteOrExit(cls, *args, **kwargs): try: m = cls(*args, **kwargs) return m except InstallGeckoError: # Bug 812395 - the process of installing gecko into the emulator # and then restarting B2G tickles some bug in the emulator/b2g # that intermittently causes B2G to fail to restart. To work # around this in TBPL runs, we will fail gracefully from this # error so that the mozharness script can try the run again. # This string will get caught by mozharness and will cause it # to retry the tests. print "Error installing gecko!" # Exit without a normal exception to prevent mozharness from # flagging the error. sys.exit() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = {'type': command} if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException( message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({ "type": "emulatorCmdResult", "id": response.get("id"), "result": result }) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = 'emulator' elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print( 'PROCESS-CRASH | %s | abnormal termination with exit code %d' % (name, returncode)) return returncode is not None def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session @property def test_name(self): return self._test_name @test_name.setter def test_name(self, test_name): if self._send_message('setTestName', 'ok', value=test_name): self._test_name = test_name def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response @property def current_window_handle(self): self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): response = self._send_message('getTitle', 'value') return response @property def window_handles(self): response = self._send_message('getWindows', 'value') return response @property def page_source(self): response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert (context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None, focus=True): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id, focus=focus) else: response = self._send_message('switchToFrame', 'ok', value=frame, focus=focus) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id} elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, async=True, new_sandbox=True, special_powers=False):
class Marionette(object): def __init__(self, host='localhost', port=2626): self.host = host self.port = port self.client = MarionetteClient(self.host, self.port) self.actor = 'marionette' self.session = None self.window = None def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): self.start_session() message = { 'to': self.actor, 'command': command } if self.session: message['session'] = self.session if kwargs: message.update(kwargs) response = self.client.send(message) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == 7: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == 8: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == 10: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == 11: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == 17: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == 19: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == 21: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == 23: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == 28: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') return self.session def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response def get_session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response def get_window(self): self.window = self._send_message('getWindow', 'value') return self.window def get_windows(self): response = self._send_message('getWindows', 'values') return response def close_window(self, window_id=None): if not window_id: window_id = self.get_window() response = self._send_message('closeWindow', 'ok', value=window_id) return response def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id) else: response = self._send_message('switchToFrame', 'ok', value=frame) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def execute_script(self, script, script_args=None): if script_args is None: script_args = [] response = self._send_message('executeScript', 'value', value=script, args=script_args) return response def execute_async_script(self, script, script_args=None): if script_args is None: script_args = [] response = self._send_message('executeAsyncScript', 'value', value=script, args=script_args) return response def find_element(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElement', 'value', **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = { 'value': target, 'using': method } if id: kwargs['element'] = id response = self._send_message('findElements', 'values', **kwargs) assert(isinstance(response, list)) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements
class Marionette(object): CONTEXT_CHROME = "chrome" CONTEXT_CONTENT = "content" TIMEOUT_SEARCH = "implicit" TIMEOUT_SCRIPT = "script" TIMEOUT_PAGE = "page load" def __init__( self, host="localhost", port=2828, app=None, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res=None, gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None, busybox=None, symbols_path=None, timeout=None, ): self.host = host self.port = self.local_port = port self.app = app self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self._test_name = None self.symbols_path = symbols_path self.timeout = timeout if bin: port = int(self.port) if not Marionette.is_port_available(port, host=self.host): ex_msg = "%s:%d is unavailable." % (self.host, port) raise MarionetteException(message=ex_msg) if app: # select instance class for the given app try: instance_class = geckoinstance.apps[app] except KeyError: msg = 'Application "%s" unknown (should be one of %s)' raise NotImplementedError(msg % (app, geckoinstance.apps.keys())) else: instance_class = geckoinstance.GeckoInstance self.instance = instance_class(host=self.host, port=self.port, bin=self.bin, profile=self.profile) self.instance.start() assert self.wait_for_port() if emulator: self.emulator = Emulator( homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res, ) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert self.emulator.wait_for_port() if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert self.emulator.wait_for_port() self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.setup(self, gecko_path=gecko_path, busybox=busybox) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() @staticmethod def is_port_available(port, host=""): port = int(port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind((host, port)) return True except socket.error: return False finally: s.close() @classmethod def getMarionetteOrExit(cls, *args, **kwargs): try: m = cls(*args, **kwargs) return m except InstallGeckoError: # Bug 812395 - the process of installing gecko into the emulator # and then restarting B2G tickles some bug in the emulator/b2g # that intermittently causes B2G to fail to restart. To work # around this in TBPL runs, we will fail gracefully from this # error so that the mozharness script can try the run again. # This string will get caught by mozharness and will cause it # to retry the tests. print "Error installing gecko!" # Exit without a normal exception to prevent mozharness from # flagging the error. sys.exit() def wait_for_port(self, timeout=3000): starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) data = sock.recv(16) sock.close() if '"from"' in data: time.sleep(5) return True except socket.error: pass time.sleep(1) return False def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ("newSession", "getStatus"): raise MarionetteException(message="Please start a session") message = {"type": command} if self.session: message["session"] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() raise TimeoutException(message="socket.timeout", status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == "ok" and response.get("ok") == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"type": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if "error" in response and isinstance(response["error"], dict): status = response["error"].get("status", 500) message = response["error"].get("message") stacktrace = response["error"].get("stacktrace") # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif ( status == ErrorCodes.INVALID_SELECTOR or status == ErrorCodes.INVALID_XPATH_SELECTOR or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER ): raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: raise MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_NOT_INITIALIZED_ERROR: raise FrameSendNotInitializedError(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_FAILURE_ERROR: raise FrameSendFailureError(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None crashed = False if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = "emulator" crashed = True if self.symbols_path and self.emulator.check_for_minidumps(self.symbols_path): crashed = True elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print ("PROCESS-CRASH | %s | abnormal termination with exit code %d" % (name, returncode)) return crashed def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message("getStatus", "value") def start_session(self, desired_capabilities=None): try: # We are ignoring desired_capabilities, at least for now. self.session = self._send_message("newSession", "value") except: exc, val, tb = sys.exc_info() self.check_for_crash() raise exc, val, tb self.b2g = "b2g" in self.session return self.session @property def test_name(self): return self._test_name @test_name.setter def test_name(self, test_name): if self._send_message("setTestName", "ok", value=test_name): self._test_name = test_name def delete_session(self): response = self._send_message("deleteSession", "ok") self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): response = self._send_message("getSessionCapabilities", "value") return response def set_script_timeout(self, timeout): response = self._send_message("setScriptTimeout", "ok", value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message("setSearchTimeout", "ok", value=timeout) return response @property def current_window_handle(self): self.window = self._send_message("getWindow", "value") return self.window @property def title(self): response = self._send_message("getTitle", "value") return response @property def window_handles(self): response = self._send_message("getWindows", "value") return response @property def page_source(self): response = self._send_message("getPageSource", "value") return response def close(self, window_id=None): if not window_id: window_id = self.current_window_handle response = self._send_message("closeWindow", "ok", value=window_id) return response def set_context(self, context): assert context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT return self._send_message("setContext", "ok", value=context) def switch_to_window(self, window_id): response = self._send_message("switchToWindow", "ok", value=window_id) self.window = window_id return response def get_active_frame(self): response = self._send_message("getActiveFrame", "value") if response: return HTMLElement(self, response) return None def switch_to_frame(self, frame=None, focus=True): if isinstance(frame, HTMLElement): response = self._send_message("switchToFrame", "ok", element=frame.id, focus=focus) else: response = self._send_message("switchToFrame", "ok", value=frame, focus=focus) return response def get_url(self): response = self._send_message("getUrl", "value") return response def get_window_type(self): response = self._send_message("getWindowType", "value") return response def navigate(self, url): response = self._send_message("goUrl", "ok", value=url) return response def timeouts(self, timeout_type, ms): assert ( timeout_type == self.TIMEOUT_SEARCH or timeout_type == self.TIMEOUT_SCRIPT or timeout_type == self.TIMEOUT_PAGE ) response = self._send_message("timeouts", "ok", timeoutType=timeout_type, ms=ms) return response def go_back(self): response = self._send_message("goBack", "ok") return response def go_forward(self): response = self._send_message("goForward", "ok") return response def refresh(self): response = self._send_message("refresh", "ok") return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {"ELEMENT": args.id} elif ( isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None ): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == "ELEMENT": unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script( self, script, script_args=None, async=True, new_sandbox=True, special_powers=False, script_timeout=None, filename=None, ):
class Marionette(object): CONTEXT_CHROME = 'chrome' CONTEXT_CONTENT = 'content' def __init__(self, host='localhost', port=2828, bin=None, profile=None, emulator=None, emulatorBinary=None, emulatorImg=None, emulator_res='480x800', connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir if bin: self.instance = GeckoInstance(host=self.host, port=self.port, bin=self.bin, profile=self.profile) self.instance.start() assert (self.instance.wait_for_port()) if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert (self.emulator.wait_for_port()) self.client = MarionetteClient(self.host, self.port) def __del__(self): if self.emulator: self.emulator.close() if self.bin: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = {'type': command} if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() if self.emulator: port = self.emulator.restart(self.local_port) if port is not None: self.port = self.client.port = port raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException( message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({ "type": "emulatorCmdResult", "id": response.get("id"), "result": result }) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response @property def current_window_handle(self): self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): response = self._send_message('getTitle', 'value') return response @property def window_handles(self): response = self._send_message('getWindows', 'value') return response @property def page_source(self): response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): assert (context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id) else: response = self._send_message('switchToFrame', 'ok', value=frame) return response def get_url(self): response = self._send_message('getUrl', 'value') return response def navigate(self, url): response = self._send_message('goUrl', 'ok', value=url) return response def go_back(self): response = self._send_message('goBack', 'ok') return response def go_forward(self): response = self._send_message('goForward', 'ok') return response def refresh(self): response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id} elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, timeout=True, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeJSScript', 'value', value=script, args=args, timeout=timeout, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def execute_script(self, script, script_args=None, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeScript', 'value', value=script, args=args, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def execute_async_script(self, script, script_args=None, new_sandbox=True, special_powers=False): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message('executeAsyncScript', 'value', value=script, args=args, newSandbox=new_sandbox, specialPowers=special_powers) return self.unwrapValue(response) def find_element(self, method, target, id=None): kwargs = {'value': target, 'using': method} if id: kwargs['element'] = id response = self._send_message('findElement', 'value', **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = {'value': target, 'using': method} if id: kwargs['element'] = id response = self._send_message('findElements', 'value', **kwargs) assert (isinstance(response, list)) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements def log(self, msg, level=None): return self._send_message('log', 'ok', value=msg, level=level) def get_logs(self): return self._send_message('getLogs', 'value') def add_perf_data(self, suite, name, value): return self._send_message('addPerfData', 'ok', suite=suite, name=name, value=value) def get_perf_data(self): return self._send_message('getPerfData', 'value') def import_script(self, file): f = open(file, "r") js = f.read() return self._send_message('importScript', 'ok', script=js)
class Marionette(object): """ Represents a Marionette connection to a browser or device. """ CONTEXT_CHROME = 'chrome' # non-browser content: windows, dialogs, etc. CONTEXT_CONTENT = 'content' # browser content: iframes, divs, etc. TIMEOUT_SEARCH = 'implicit' TIMEOUT_SCRIPT = 'script' TIMEOUT_PAGE = 'page load' def __init__(self, host='localhost', port=2828, app=None, app_args=None, bin=None, profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res=None, gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, logcat_dir=None, busybox=None, symbols_path=None, timeout=None): self.host = host self.port = self.local_port = port self.bin = bin self.instance = None self.profile = profile self.session = None self.window = None self.emulator = None self.extra_emulators = [] self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow self.logcat_dir = logcat_dir self._test_name = None self.symbols_path = symbols_path self.timeout = timeout if bin: port = int(self.port) if not Marionette.is_port_available(port, host=self.host): ex_msg = "%s:%d is unavailable." % (self.host, port) raise MarionetteException(message=ex_msg) if app: # select instance class for the given app try: instance_class = geckoinstance.apps[app] except KeyError: msg = 'Application "%s" unknown (should be one of %s)' raise NotImplementedError(msg % (app, geckoinstance.apps.keys())) else: instance_class = geckoinstance.GeckoInstance self.instance = instance_class(host=self.host, port=self.port, bin=self.bin, profile=self.profile, app_args=app_args) self.instance.start() assert(self.wait_for_port()), "Timed out waiting for port!" if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow, logcat_dir=self.logcat_dir, arch=emulator, sdcard=sdcard, emulatorBinary=emulatorBinary, userdata=emulatorImg, res=emulator_res) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()), "Timed out waiting for port!" if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert(self.emulator.wait_for_port()), "Timed out waiting for port!" self.client = MarionetteClient(self.host, self.port) if emulator: self.emulator.setup(self, gecko_path=gecko_path, busybox=busybox) def __del__(self): if self.emulator: self.emulator.close() if self.instance: self.instance.close() for qemu in self.extra_emulators: qemu.emulator.close() @staticmethod def is_port_available(port, host=''): port = int(port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind((host, port)) return True except socket.error: return False finally: s.close() @classmethod def getMarionetteOrExit(cls, *args, **kwargs): try: m = cls(*args, **kwargs) return m except InstallGeckoError: # Bug 812395 - the process of installing gecko into the emulator # and then restarting B2G tickles some bug in the emulator/b2g # that intermittently causes B2G to fail to restart. To work # around this in TBPL runs, we will fail gracefully from this # error so that the mozharness script can try the run again. # This string will get caught by mozharness and will cause it # to retry the tests. print "Error installing gecko!" # Exit without a normal exception to prevent mozharness from # flagging the error. sys.exit() def wait_for_port(self, timeout=3000): starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) data = sock.recv(16) sock.close() if '"from"' in data: time.sleep(5) return True except socket.error: pass time.sleep(1) return False def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ('newSession', 'getStatus'): raise MarionetteException(message="Please start a session") message = { 'type': command } if self.session: message['session'] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) if (response_key == 'ok' and response.get('ok') == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: raise MarionetteException(message="No emulator in this test to run " "command against.") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"type": "emulatorCmdResult", "id": response.get("id"), "result": result}) def _handle_error(self, response): if 'error' in response and isinstance(response['error'], dict): status = response['error'].get('status', 500) message = response['error'].get('message') stacktrace = response['error'].get('stacktrace') # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == ErrorCodes.NO_SUCH_ELEMENT: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_FRAME: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.STALE_ELEMENT_REFERENCE: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_NOT_VISIBLE: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_ELEMENT_STATE: raise InvalidElementStateException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNKNOWN_ERROR: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.ELEMENT_IS_NOT_SELECTABLE: raise ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.JAVASCRIPT_ERROR: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.XPATH_LOOKUP_ERROR: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.TIMEOUT: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_SUCH_WINDOW: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_COOKIE_DOMAIN: raise InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.UNABLE_TO_SET_COOKIE: raise UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.NO_ALERT_OPEN: raise NoAlertPresentException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.SCRIPT_TIMEOUT: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.INVALID_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR \ or status == ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER: raise InvalidSelectorException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS: raise MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_NOT_INITIALIZED_ERROR: raise FrameSendNotInitializedError(message=message, status=status, stacktrace=stacktrace) elif status == ErrorCodes.FRAME_SEND_FAILURE_ERROR: raise FrameSendFailureError(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def check_for_crash(self): returncode = None name = None crashed = False if self.emulator: if self.emulator.check_for_crash(): returncode = self.emulator.proc.returncode name = 'emulator' crashed = True if self.symbols_path and self.emulator.check_for_minidumps(self.symbols_path): crashed = True elif self.instance: # In the future, a check for crashed Firefox processes # should be here. pass if returncode is not None: print ('PROCESS-CRASH | %s | abnormal termination with exit code %d' % (name, returncode)) return crashed def absolute_url(self, relative_url): ''' Returns an absolute url for files served from Marionette's www directory. :param relative_url: The url of a static file, relative to Marionette's www directory. ''' return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): ''' Creates a new Marionette session. You must call this method before performing any other action. ''' try: # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') except: exc, val, tb = sys.exc_info() self.check_for_crash() raise exc, val, tb self.b2g = 'b2g' in self.session return self.session @property def test_name(self): return self._test_name @test_name.setter def test_name(self, test_name): if self._send_message('setTestName', 'ok', value=test_name): self._test_name = test_name def delete_session(self): response = self._send_message('deleteSession', 'ok') self.session = None self.window = None self.client.close() return response @property def session_capabilities(self): ''' A JSON dictionary representing the capabilities of the current session. ''' response = self._send_message('getSessionCapabilities', 'value') return response def set_script_timeout(self, timeout): ''' Sets the maximum number of ms that an asynchronous script is allowed to run. If a script does not return in the specified amount of time, a ScriptTimeoutException is raised. :param timeout: The maximum number of milliseconds an asynchronous script can run without causing an ScriptTimeoutException to be raised ''' response = self._send_message('setScriptTimeout', 'ok', value=timeout) return response def set_search_timeout(self, timeout): ''' Sets a timeout for the find methods. When searching for an element using either :class:`Marionette.find_element` or :class:`Marionette.find_elements`, the method will continue trying to locate the element for up to timeout ms. This can be useful if, for example, the element you're looking for might not exist immediately, because it belongs to a page which is currently being loaded. :param timeout: Timeout in milliseconds. ''' response = self._send_message('setSearchTimeout', 'ok', value=timeout) return response @property def current_window_handle(self): ''' A reference to the current window. ''' self.window = self._send_message('getWindow', 'value') return self.window @property def title(self): ''' Current title of the active window. ''' response = self._send_message('getTitle', 'value') return response @property def window_handles(self): ''' A list of references to all available browser windows if called in content context. If called while in the chrome context, it will list all available windows, not just browser windows (ie: not just 'navigator:browser';). ''' response = self._send_message('getWindows', 'value') return response @property def page_source(self): ''' A string representation of the DOM. ''' response = self._send_message('getPageSource', 'value') return response def close(self, window_id=None): ''' Closes the window that is in use by Marionette. :param window_id: id of the window you wish to closed ''' if not window_id: window_id = self.current_window_handle response = self._send_message('closeWindow', 'ok', value=window_id) return response def set_context(self, context): ''' Sets the context that marionette commands are running in. :param context: Context, may be one of the class properties `CONTEXT_CHROME` or `CONTEXT_CONTENT`. Usage example: :: marionette.set_context(marionette.CONTEXT_CHROME) ''' assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT) return self._send_message('setContext', 'ok', value=context) def switch_to_window(self, window_id): ''' Switch to the specified window; subsequent commands will be directed at the new window. :param window_id: The id or name of the window to switch to. ''' response = self._send_message('switchToWindow', 'ok', value=window_id) self.window = window_id return response def get_active_frame(self): ''' Returns an HTMLElement representing the frame Marionette is currently acting on. ''' response = self._send_message('getActiveFrame', 'value') if response: return HTMLElement(self, response) return None def switch_to_frame(self, frame=None, focus=True): ''' Switch the current context to the specified frame. Subsequent commands will operate in the context of the specified frame, if applicable. :param frame: A reference to the frame to switch to: this can be an HTMLElement, an index, name or an id attribute. If you call switch_to_frame() without an argument, it will switch to the top-level frame. :param focus: A boolean value which determins whether to focus the frame that we just switched to. ''' if isinstance(frame, HTMLElement): response = self._send_message('switchToFrame', 'ok', element=frame.id, focus=focus) else: response = self._send_message('switchToFrame', 'ok', value=frame, focus=focus) return response def get_url(self): ''' Returns the url of the active page in the browser. ''' response = self._send_message('getUrl', 'value') return response def get_window_type(self): ''' Gets the windowtype attribute of the window Marionette is currently acting on. This command only makes sense in a chrome context. You might use this method to distinguish a browser window from an editor window. ''' response = self._send_message('getWindowType', 'value') return response def navigate(self, url): ''' Causes the browser to navigate to the specified url. :param url: The url to navigate to. ''' response = self._send_message('goUrl', 'ok', value=url) return response def timeouts(self, timeout_type, ms): assert(timeout_type == self.TIMEOUT_SEARCH or timeout_type == self.TIMEOUT_SCRIPT or timeout_type == self.TIMEOUT_PAGE) response = self._send_message('timeouts', 'ok', timeoutType=timeout_type, ms=ms) return response def go_back(self): ''' Causes the browser to perform a back navigation. ''' response = self._send_message('goBack', 'ok') return response def go_forward(self): ''' Causes the browser to perform a forward navigation. ''' response = self._send_message('goForward', 'ok') return response def refresh(self): ''' Causes the browser to perform to refresh the current page. ''' response = self._send_message('refresh', 'ok') return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {'ELEMENT': args.id } elif (isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or isinstance(args, float) or args is None): wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == 'ELEMENT': unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, async=True, new_sandbox=True, special_powers=False, script_timeout=None, inactivity_timeout=None, filename=None):
class Marionette(object): CONTEXT_CHROME = "chrome" CONTEXT_CONTENT = "content" def __init__( self, host="localhost", port=2828, b2gbin=False, emulator=False, connectToRunningEmulator=False, homedir=None, baseurl=None, noWindow=False, ): self.host = host self.port = self.local_port = port self.b2gbin = b2gbin self.session = None self.window = None self.emulator = None self.homedir = homedir self.baseurl = baseurl self.noWindow = noWindow if b2gbin: self.b2ginstance = B2GInstance(host=self.host, port=self.port, b2gbin=self.b2gbin) self.b2ginstance.start() assert self.b2ginstance.wait_for_port() if emulator: self.emulator = Emulator(homedir=homedir, noWindow=self.noWindow) self.emulator.start() self.port = self.emulator.setup_port_forwarding(self.port) assert self.emulator.wait_for_port() if connectToRunningEmulator: self.emulator = Emulator(homedir=homedir) self.emulator.connect() self.port = self.emulator.setup_port_forwarding(self.port) assert self.emulator.wait_for_port() self.client = MarionetteClient(self.host, self.port) def __del__(self): if self.emulator: self.emulator.close() if self.b2gbin: self.b2ginstance.close() def _send_message(self, command, response_key, **kwargs): if not self.session and command not in ("newSession", "getStatus"): raise MarionetteException(message="Please start a session") message = {"type": command} if self.session: message["session"] = self.session if kwargs: message.update(kwargs) try: response = self.client.send(message) except socket.timeout: self.session = None self.window = None self.client.close() if self.emulator: port = self.emulator.restart(self.local_port) if port is not None: self.port = self.client.port = port raise TimeoutException(message="socket.timeout", status=21, stacktrace=None) if (response_key == "ok" and response.get("ok") == True) or response_key in response: return response[response_key] else: self._handle_error(response) def _handle_error(self, response): if "error" in response and isinstance(response["error"], dict): status = response["error"].get("status", 500) message = response["error"].get("message") stacktrace = response["error"].get("stacktrace") # status numbers come from # http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes if status == 7: raise NoSuchElementException(message=message, status=status, stacktrace=stacktrace) elif status == 8: raise NoSuchFrameException(message=message, status=status, stacktrace=stacktrace) elif status == 10: raise StaleElementException(message=message, status=status, stacktrace=stacktrace) elif status == 11: raise ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace) elif status == 17: raise JavascriptException(message=message, status=status, stacktrace=stacktrace) elif status == 19: raise XPathLookupException(message=message, status=status, stacktrace=stacktrace) elif status == 21: raise TimeoutException(message=message, status=status, stacktrace=stacktrace) elif status == 23: raise NoSuchWindowException(message=message, status=status, stacktrace=stacktrace) elif status == 28: raise ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace) else: raise MarionetteException(message=message, status=status, stacktrace=stacktrace) raise MarionetteException(message=response, status=500) def absolute_url(self, relative_url): return "%s%s" % (self.baseurl, relative_url) def status(self): return self._send_message("getStatus", "value") def start_session(self, desired_capabilities=None): # We are ignoring desired_capabilities, at least for now. self.session = self._send_message("newSession", "value") self.b2g = "b2g" in self.session return self.session def delete_session(self): response = self._send_message("deleteSession", "ok") self.session = None self.window = None self.client.close() return response def get_session_capabilities(self): response = self._send_message("getSessionCapabilities", "value") return response def set_script_timeout(self, timeout): response = self._send_message("setScriptTimeout", "ok", value=timeout) return response def set_search_timeout(self, timeout): response = self._send_message("setSearchTimeout", "ok", value=timeout) return response def get_window(self): self.window = self._send_message("getWindow", "value") return self.window def get_windows(self): response = self._send_message("getWindows", "value") return response def close_window(self, window_id=None): if not window_id: window_id = self.get_window() response = self._send_message("closeWindow", "ok", value=window_id) return response def set_context(self, context): assert context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT return self._send_message("setContext", "ok", value=context) def switch_to_window(self, window_id): response = self._send_message("switchToWindow", "ok", value=window_id) self.window = window_id return response def switch_to_frame(self, frame=None): if isinstance(frame, HTMLElement): response = self._send_message("switchToFrame", "ok", element=frame.id) else: response = self._send_message("switchToFrame", "ok", value=frame) return response def get_url(self): response = self._send_message("getUrl", "value") return response def navigate(self, url): response = self._send_message("goUrl", "ok", value=url) return response def go_back(self): response = self._send_message("goBack", "ok") return response def go_forward(self): response = self._send_message("goForward", "ok") return response def refresh(self): response = self._send_message("refresh", "ok") return response def wrapArguments(self, args): if isinstance(args, list): wrapped = [] for arg in args: wrapped.append(self.wrapArguments(arg)) elif isinstance(args, dict): wrapped = {} for arg in args: wrapped[arg] = self.wrapArguments(args[arg]) elif type(args) == HTMLElement: wrapped = {"ELEMENT": args.id} elif isinstance(args, bool) or isinstance(args, basestring) or isinstance(args, int) or args is None: wrapped = args return wrapped def unwrapValue(self, value): if isinstance(value, list): unwrapped = [] for item in value: unwrapped.append(self.unwrapValue(item)) elif isinstance(value, dict): unwrapped = {} for key in value: if key == "ELEMENT": unwrapped = HTMLElement(self, value[key]) else: unwrapped[key] = self.unwrapValue(value[key]) else: unwrapped = value return unwrapped def execute_js_script(self, script, script_args=None, timeout=True): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message("executeJSScript", "value", value=script, args=args, timeout=timeout) return self.unwrapValue(response) def execute_script(self, script, script_args=None): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message("executeScript", "value", value=script, args=args) return self.unwrapValue(response) def execute_async_script(self, script, script_args=None): if script_args is None: script_args = [] args = self.wrapArguments(script_args) response = self._send_message("executeAsyncScript", "value", value=script, args=args) return self.unwrapValue(response) def find_element(self, method, target, id=None): kwargs = {"value": target, "using": method} if id: kwargs["element"] = id response = self._send_message("findElement", "value", **kwargs) element = HTMLElement(self, response) return element def find_elements(self, method, target, id=None): kwargs = {"value": target, "using": method} if id: kwargs["element"] = id response = self._send_message("findElements", "value", **kwargs) assert isinstance(response, list) elements = [] for x in response: elements.append(HTMLElement(self, x)) return elements def log(self, msg, level=None): return self._send_message("log", "ok", value=msg, level=level) def get_logs(self): return self._send_message("getLogs", "value")