class TestConsoleLogCapture(): def setup(self): try: self.client = Marionette(host='localhost', port=2828) self.client.start_session() self.client.set_pref('general.warnOnAboutConfig', False) except: sys.exit("Could not find Firefox browser running") def test_push_notification_received(self): self.client.navigate( "https://people.mozilla.org/~ewong2/push-notification-test/") unregister_button = self.client.find_element(By.ID, "unreg_btn") if unregister_button.is_displayed() == True: unregister_button.click() Wait(self.client, timeout=5, interval=1).until( expected.element_not_displayed(By.ID, "unreg_btn")) Wait(self.client).until(expected.element_displayed(By.ID, "reg_btn")) self.client.find_element(By.ID, "reg_btn").click() Wait(self.client).until( expected.element_displayed(By.ID, "subscribe_btn")) self.client.find_element(By.ID, "subscribe_btn").click() Wait(self.client).until(expected.element_displayed(By.ID, "doXhr_btn")) self.client.find_element(By.ID, "doXhr_btn").click() result = self.web_console_filter_for_string("Received a push message") assert result == 1 def web_console_filter_for_string(self, console_string=None): self.client.set_context(self.client.CONTEXT_CHROME) handles = self.client.window_handles chrome_handles = self.client.chrome_window_handles browser_handle = self.client.current_chrome_window_handle notifications = 0 for handle in chrome_handles: if handle != browser_handle: console_handle = handle self.client.switch_to_window(console_handle) time.sleep(1) results = self.client.find_elements(By.CLASS_NAME, "console-string") for result in results: if console_string in result.text: notifications = notifications + 1 self.client.find_element(By.CLASS_NAME, "webconsole-clear-console-button").click() return notifications def tear_down(self): self.client.close()
class TestConsoleLogCapture(): def setup(self): try: self.client = Marionette(host='localhost', port=2828) self.client.start_session() self.client.set_pref('general.warnOnAboutConfig', False) except: sys.exit("Could not find Firefox browser running") def test_push_notification_received(self): self.client.navigate("https://people.mozilla.org/~ewong2/push-notification-test/") unregister_button = self.client.find_element(By.ID, "unreg_btn") if unregister_button.is_displayed() == True: unregister_button.click() Wait(self.client, timeout=5, interval=1).until(expected.element_not_displayed(By.ID, "unreg_btn")) Wait(self.client).until(expected.element_displayed(By.ID, "reg_btn")) self.client.find_element(By.ID, "reg_btn").click() Wait(self.client).until(expected.element_displayed(By.ID, "subscribe_btn")) self.client.find_element(By.ID, "subscribe_btn").click() Wait(self.client).until(expected.element_displayed(By.ID, "doXhr_btn")) self.client.find_element(By.ID, "doXhr_btn").click() result = self.web_console_filter_for_string("Received a push message") assert result == 1 def web_console_filter_for_string(self, console_string=None): self.client.set_context(self.client.CONTEXT_CHROME) handles = self.client.window_handles chrome_handles = self.client.chrome_window_handles browser_handle = self.client.current_chrome_window_handle notifications = 0 for handle in chrome_handles: if handle != browser_handle: console_handle = handle self.client.switch_to_window(console_handle) time.sleep(1) results = self.client.find_elements(By.CLASS_NAME, "console-string") for result in results: if console_string in result.text: notifications = notifications + 1 self.client.find_element(By.CLASS_NAME, "webconsole-clear-console-button").click() return notifications def tear_down(self): self.client.close()
class Runner(object): _INPUT_NONE = '' _INPUT_EXIT_COMMAND = 'exit' _INPUT_MULTIPLE_LINE = ' \\' _INPUT_COMMAND_PREFIX = ':' _INPUT_SYSTEM_APP_KEYWORD = 'system' _is_async = False _sync_prompt = '>>> ' _async_prompt = 'a>> ' _prompt = _sync_prompt def __init__(self, **kwargs): # Added parser parser = OptionParser() parser.add_option('-a', '--address', action='store', type='string', dest='address', default='localhost', help='The host address of b2g instance. Default=localhost') parser.add_option('-p', '--port', action='store', type='int', dest='port', default=2828, help='The port of b2g instance.') parser.add_option('-l', '--list', action='store_true', dest='enable_list', default='False', help='List all apps of b2g instance. Default=False') parser.add_option('-c', '--connect', action='store', type='string', dest='connect', default=None, help='Connect to the App iframe.' \ 'Use # ID or App URL to connect.') (options, args) = parser.parse_args() self.connect = options.connect # start marionette session self.m = Marionette(options.address, options.port) self.m.start_session() # list all iframes if options.enable_list: self.list_all_iframes() # list active iframes elif self.connect == None: self.list_active_iframes() # connect to App if self.connect == None: exit(0) else: # connect App print 'Start...' if self.open_app(self.connect): self.start_js() else: exit(-1) def switch_sync_async(self): self._is_async = not self._is_async self._prompt = self._async_prompt if self._is_async else self._sync_prompt print 'Swith to', ('Async' if self._is_async else 'Sync'), 'JS execution' def start_js(self): print 'Enter \'%s\' or Crtl+D to exit the shell.' % self._INPUT_EXIT_COMMAND print 'And enter \':h\' for more commands.' try: while True: input = raw_input(self._prompt) # if input is EXIT command, exit this program if input.lower() == self._INPUT_EXIT_COMMAND: self.goodbye() break # if input is NONE, then do nothing and keep going... elif input == self._INPUT_NONE: continue # if input start with ":", it should be the command. elif input.startswith(self._INPUT_COMMAND_PREFIX): CmdDispatcher(self, input) # if the postfix of input is MULTIPLE_LINE, then record this line and wait the next line until no input with MULTIPLE_LINE. elif input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input[:len(input) - 1] + '; ' while True: next_input = raw_input('... ') if next_input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input_multiple + next_input[:len(next_input) - 1] + '; ' pass else: input_multiple = input_multiple + next_input + '; ' print self.execute_script(input_multiple) break # if input is NOT above inputs, then run marionette.execute_script(INPUT)/marionette.execute_async_script(INPUT) # and print return value. else: print self.execute_script(input) except EOFError: self.goodbye() exit() except Exception as e: print e if self.open_app(-1): self.start_js() exit() def execute_script(self, script): try: if self._is_async: return self.m.execute_async_script(script) else: return self.m.execute_script(script) except Exception as ex: print str(ex.message) def goodbye(self): print 'End. Bye!!' def open_app(self, input): try: # connect App by ID app_id = int(input) if app_id < 0: print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() else: iframes = self.get_all_iframes_id_name_pair() print 'Connect to', iframes[str(app_id)] self.set_current_frame(iframes[str(app_id)]) self.m.switch_to_frame(app_id) return True except(ValueError): # connect to System app if (input.lower() == self._INPUT_SYSTEM_APP_KEYWORD) or (input.lower() in self._get_system_URL()): print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() return True # connect App by substring iframes = self.get_all_iframes_id_name_pair() suitable_iframes = {} for k, v in iframes.items(): if input in v: suitable_iframes[k] = v # connect to App if there is one fit if len(suitable_iframes) == 1: target = suitable_iframes.keys()[0] print 'Connect to', suitable_iframes.values()[0] self.set_current_frame(suitable_iframes.values()[0]) self.m.switch_to_frame(int(target)) return True # exit if there are more than one app fit the query elif len(suitable_iframes) > 1: print 'There are more than one Apps fit the query string [', input, '].' print '{0:2s} {1:s}'.format('#', 'App URL') for k, v in sorted(suitable_iframes.items()): print '{0:2s} {1:s}'.format(k, v) return False # exit if there is no app fit the query else: print 'There is no App fit the query string [', input, '].' return False def set_current_frame(self, frame_name): self._current_frame = frame_name def get_current_frame(self): return self._current_frame def switch_to_system_frame(self): self.m.switch_to_frame() def _get_system_URL(self): self.switch_to_system_frame() system_URL = self.m.execute_script('return document.URL') return system_URL def _get_all_iframes(self): self.switch_to_system_frame() iframes = self.m.execute_script('return document.getElementsByTagName("iframe")') return iframes def get_all_iframes_id_name_pair(self): iframes = self._get_all_iframes() result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] result[str(idx)] = iframe.get_attribute('src') return result def list_all_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') print '{0:2s} {1:7s} {2:s}'.format('-1', '', self._get_system_URL()) for idx in range(0, iframes['length']): iframe = iframes[str(idx)] print '{0:2s} {1:7s} {2:s}'.format(str(idx), ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) def list_active_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] if iframe.is_displayed(): result[str(idx)] = iframe for idx, iframe in result.items(): print '{0:2s} {1:7s} {2:s}'.format(idx, ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) # TODO: seems like should using Javascript to get the idx of each iframe!? def get_all_iframes_by_marionette(self): iframes = self.m.find_elements('css selector', 'iframe') return iframes def list_all_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() for iframe in iframes: print iframe.get_attribute('src') def list_active_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() result = [] for iframe in iframes: if iframe.is_displayed(): result.append(iframe) for iframe in result: print iframe.get_attribute('src')
assert(m.close_window('window2')) assert(m.set_script_timeout(1000)) assert(m.set_search_timeout(500)) assert(m.get_url() == server.TEST_URL) assert(m.navigate(server.TEST_URL)) assert(m.go_back()) assert(m.go_forward()) assert(m.refresh()) assert(m.execute_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_js_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_js_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(m.execute_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(m.execute_async_script(server.TEST_EXECUTE_SCRIPT)) assert(m.execute_async_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert(str(m.find_element(HTMLElement.CLASS, 'heading')) == server.TEST_FIND_ELEMENT) assert([str(x) for x in m.find_elements(HTMLElement.TAG, 'p')] == server.TEST_FIND_ELEMENTS) assert(str(m.find_element(HTMLElement.CLASS, 'heading').find_element(HTMLElement.TAG, 'h1')) == server.TEST_FIND_ELEMENT) assert([str(x) for x in m.find_element(HTMLElement.ID, 'div1').find_elements(HTMLElement.SELECTOR, '.main')] == \ server.TEST_FIND_ELEMENTS) assert(m.find_element(HTMLElement.ID, 'id1').click()) assert(m.find_element(HTMLElement.ID, 'id2').text() == server.TEST_GET_TEXT) assert(m.find_element(HTMLElement.ID, 'id3').send_keys('Mozilla Firefox')) assert(m.find_element(HTMLElement.ID, 'id3').value() == server.TEST_GET_VALUE) assert(m.find_element(HTMLElement.ID, 'id3').clear()) assert(m.find_element(HTMLElement.ID, 'id3').selected()) assert(m.find_element(HTMLElement.ID, 'id1').equals(m.find_element(HTMLElement.TAG, 'p'))) assert(m.find_element(HTMLElement.ID, 'id3').enabled()) assert(m.find_element(HTMLElement.ID, 'id3').displayed()) assert(m.find_element(HTMLElement.ID, 'id3').get_attribute('value') == server.TEST_GET_VALUE) assert(m.delete_session())
assert (m.navigate(server.TEST_URL)) assert (m.go_back()) assert (m.go_forward()) assert (m.refresh()) assert (m.execute_script(server.TEST_EXECUTE_SCRIPT)) assert (m.execute_js_script(server.TEST_EXECUTE_SCRIPT)) assert (m.execute_js_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert (m.execute_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert (m.execute_async_script(server.TEST_EXECUTE_SCRIPT)) assert (m.execute_async_script(server.TEST_EXECUTE_SCRIPT, server.TEST_EXECUTE_SCRIPT_ARGS)) assert (str(m.find_element(HTMLElement.CLASS, 'heading')) == server.TEST_FIND_ELEMENT) assert ([str(x) for x in m.find_elements(HTMLElement.TAG, 'p') ] == server.TEST_FIND_ELEMENTS) assert (str( m.find_element(HTMLElement.CLASS, 'heading').find_element( HTMLElement.TAG, 'h1')) == server.TEST_FIND_ELEMENT) assert([str(x) for x in m.find_element(HTMLElement.ID, 'div1').find_elements(HTMLElement.SELECTOR, '.main')] == \ server.TEST_FIND_ELEMENTS) assert (m.find_element(HTMLElement.ID, 'id1').click()) assert (m.find_element(HTMLElement.ID, 'id2').text() == server.TEST_GET_TEXT) assert (m.find_element(HTMLElement.ID, 'id3').send_keys('Mozilla Firefox')) assert (m.find_element(HTMLElement.ID, 'id3').value() == server.TEST_GET_VALUE) assert (m.find_element(HTMLElement.ID, 'id3').clear()) assert (m.find_element(HTMLElement.ID, 'id3').is_selected()) assert (m.find_element(HTMLElement.ID,
class current_frame(): filename_screenshot = "" filename_htmldump = "" def main(self, LOGDIR): # # The first variable is the log directory. # ucount = 0 self.marionette = Marionette(host='localhost', port=2828) self.marionette.start_session() self.marionette.set_search_timeout(1000) # # Now loop through all the iframes, gathering details about each one. # print "" print "Iframe for 'top level' () ..." self.filename_screenshot = LOGDIR + "top_level" + ".png" self.filename_htmldump = LOGDIR + "top_level" + ".html" self.marionette.switch_to_frame() self.record_frame() frames = self.marionette.find_elements("tag name", "iframe") for fnum in range (0, len(frames)): # # App name is usually in the "src" attribute, so it's worth a shot.. # frame_src = frames[fnum].get_attribute("src") if frame_src != "": startpos = frame_src.index('/') + 2 stoppos = frame_src.index('.') appname = frame_src[startpos:stoppos] filename = appname else: ucount = ucount + 1 appname = "(unknown)" filename = "unknown_" + str(ucount) # # Because we call this script sometimes when we hit a Marionette issue, # these filenames may already exist (and we'd overwrite them!), so # add 'DEBUG_' to the start of the filename. # filename = "DEBUG_" + filename filename_details = LOGDIR + filename + "_iframe_details.txt" self.filename_screenshot = LOGDIR + filename + ".png" self.filename_htmldump = LOGDIR + filename + ".html" # # This iframe gives me problems sometimes, so I'm ignoring it for now. # if appname == "costcontrol": continue print "" print "Iframe for app \"" + appname + "\" ..." # # Record the iframe details (pretty verbose, but 'execute_script' # wasn't letting me use 'for' loops in js for some reason). # print " |_ iframe details saved to : " + filename_details f = open(filename_details, 'w') f.write("Attributes for this iframe ...\n") num_attribs = self.marionette.execute_script("return document.getElementsByTagName('iframe')[" + str(fnum) + "].attributes.length;") for i in range(0,num_attribs): attrib_name = self.marionette.execute_script("return document.getElementsByTagName('iframe')[" + str(fnum) + "].attributes[" + str(i) + "].nodeName;") attrib_value = self.marionette.execute_script("return document.getElementsByTagName('iframe')[" + str(fnum) + "].attributes[" + str(i) + "].nodeValue;") f.write(" |_ " + attrib_name.ljust(20) + ": \"" + attrib_value + "\"\n") f.close() # # Switch to this frame. # self.marionette.switch_to_frame(fnum) self.record_frame() self.marionette.switch_to_frame() def record_frame(self): # # Take the screenshot and save it to the file. # print " |_ screenshot saved to : " + self.filename_screenshot screenshot = self.marionette.screenshot()[22:] with open(self.filename_screenshot, 'w') as f: f.write(base64.decodestring(screenshot)) f.close() # # Take the html dump and save it to the file. # print " |_ html dump saved to : " + self.filename_htmldump f = open(self.filename_htmldump, 'w') f.write(self.marionette.page_source.encode('ascii', 'ignore') ) f.close()
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script("GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error("couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element("id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception("Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script("return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False
class TestRun(object): def __init__(self, adb="adb", serial=None): self.test_results = {} self.test_results_file = None self.m = None self.gaia_apps = None self.screenshot_path = None self.logcat_path = None self.app_name = None self.attempt = None self.num_apps = None self.device = None self.serial = serial self.port = None self.run_log = logging.getLogger('marketplace-test') if self.serial: self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial) else: self.dm = DeviceManagerADB(adbPath=adb) def reset_marionette(self): try: self.m.delete_session() except Exception: pass self.m = None self.get_marionette() def get_marionette(self): if not self.m: self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) else: tries = 5 while tries > 0: try: self.m.get_url() break except MarionetteException as e: if "Please start a session" in str(e): time.sleep(5) self.m = Marionette(port=self.port) self.m.start_session() self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.gaia_apps = GaiaApps(self.m) tries -= 1 else: raise e else: self.run_log.error("Can't connect to marionette, rebooting") self.restart_device() return self.m def write_to_file(self, data): with open("%s.tmp" % self.test_results_file, "w") as f: f.write(data) shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file) def add_values(self, key, value): if self.serial: self.test_results["%s_%s" % (key, self.serial)] = value else: self.test_results["%s" % key] = value def add_result(self, passed=False, status=None, uninstalled_failure=False): values = {} if status: if not passed: values["status"] = "FAILED: %s" % status else: values["status"] = "PASS" if self.screenshot_path: values["screenshot"] = self.screenshot_path if self.logcat_path: values["logcat"] = self.logcat_path if uninstalled_failure: values["uninstalled_failure"] = uninstalled_failure entry = "%s_%s" % (self.app_name, self.attempt) self.test_results[entry] = values def launch_with_manifest(self, manifest): self.m.switch_to_frame() result = self.m.execute_async_script( "GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000) if result == False: raise Exception("launch timed out") app = GaiaApp(frame=result.get('frame'), src=result.get('src'), name=result.get('name'), origin=result.get('origin')) if app.frame_id is None: raise Exception("failed to launch; there is no app frame") self.m.switch_to_frame(app.frame_id) return app def uninstall_with_manifest(self, manifest): self.m.switch_to_frame() script = """ GaiaApps.locateWithManifestURL('%s', null, function uninstall(app) { navigator.mozApps.mgmt.uninstall(app); marionetteScriptFinished(true); }); """ result = self.m.execute_async_script(script % manifest, script_timeout=60000) if result != True: self.add_result(status="Failed to uninstall app with url '%s'" % manifest) return app def forward_port(self): # get unused port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 0)) addr, port = s.getsockname() s.close() dm_tries = 0 self.run_log.info("using port %s" % port) while dm_tries < 20: if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0: break dm_tries += 1 time.sleep(3) else: return False self.port = port return True def restart_device(self, restart_tries=0): self.run_log.info("rebooting") # TODO restarting b2g doesn't seem to work... reboot then while restart_tries < 3: restart_tries += 1 self.dm.reboot(wait=True) self.run_log.info("forwarding") if not self.forward_port(): self.run_log.error("couldn't forward port in time, rebooting") continue self.m = Marionette(port=self.port) if not self.m.wait_for_port(180): self.run_log.error( "couldn't contact marionette in time, rebooting") continue time.sleep(1) self.m.start_session() try: Wait(self.m, timeout=240).until(lambda m: m.find_element( "id", "lockscreen-container").is_displayed()) # It retuns a little early time.sleep(2) self.device = GaiaDevice(self.m) self.device.add_device_manager(self.dm) self.device.unlock() self.gaia_apps = GaiaApps(self.m) except (MarionetteException, IOError, socket.error) as e: self.run_log.error("got exception: %s, going to retry" % e) try: self.m.delete_session() except: # at least attempt to clear the session if possible pass continue break else: raise Exception( "Couldn't restart the device in time, even after 3 tries") def readystate_wait(self, app): try: Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script( "return window.document.readyState;") == "complete") except ScriptTimeoutException as e: return False return True def record_icons(self): self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") self.num_apps = len(icons) def check_if_app_installed(self, timeout=180): # TODO: Find a better way to do this than checking homescreen # I hope there is one... self.device.touch_home_button() icons = self.m.find_elements("class name", "icon") start = time.time() end = start + 180 found_icon = None claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon while time.time() < end: if not found_icon: icons = self.m.find_elements("class name", "icon") # We can't do set comparison b/c references change if len(icons) > self.num_apps: for icon in icons: if "loading" in icon.get_attribute("innerHTML"): found_icon = icon break else: claims_its_loaded += 1 if claims_its_loaded == 3: return True else: if "loading" not in found_icon.get_attribute("innerHTML"): return True time.sleep(2) return False
class Runner(object): _INPUT_NONE = '' _INPUT_EXIT_COMMAND = 'exit' _INPUT_MULTIPLE_LINE = ' \\' _INPUT_COMMAND_PREFIX = ':' _INPUT_SYSTEM_APP_KEYWORD = 'system' _KEYBOARD_FRAME_NAME = 'app://keyboard.gaiamobile.org/index.html' _is_async = False _sync_prompt = '>>> ' _async_prompt = 'a>> ' _prompt = _sync_prompt def __init__(self, **kwargs): # Added parser parser = OptionParser() parser.add_option( '-a', '--address', action='store', type='string', dest='address', default='localhost', help='The host address of b2g instance. Default=localhost') parser.add_option('-p', '--port', action='store', type='int', dest='port', default=2828, help='The port of b2g instance.') parser.add_option('-l', '--list', action='store_true', dest='enable_list', default='False', help='List all apps of b2g instance. Default=False') parser.add_option('-c', '--connect', action='store', type='string', dest='connect', default=None, help='Connect to the App iframe.' \ 'Use # ID or App URL to connect.') (options, args) = parser.parse_args() self.connect = options.connect # start marionette session self.m = Marionette(options.address, options.port) self.m.start_session() # list all iframes if options.enable_list: self.list_all_iframes() # list active iframes elif self.connect == None: self.list_active_iframes() # connect to App if self.connect == None: exit(0) else: # connect App print 'Start...' if self.open_app(self.connect): self.start_js() else: exit(-1) def switch_sync_async(self): self._is_async = not self._is_async self._prompt = self._async_prompt if self._is_async else self._sync_prompt print 'Swith to', ('Async' if self._is_async else 'Sync'), 'JS execution' def start_js(self): print 'Enter \'%s\' or Crtl+D to exit the shell.' % self._INPUT_EXIT_COMMAND print 'And enter \':h\' for more commands.' try: while True: input = raw_input(self._prompt) # if input is EXIT command, exit this program if input.lower() == self._INPUT_EXIT_COMMAND: self.goodbye() break # if input is NONE, then do nothing and keep going... elif input == self._INPUT_NONE: continue # if input start with ":", it should be the command. elif input.startswith(self._INPUT_COMMAND_PREFIX): CmdDispatcher(self, input) # if the postfix of input is MULTIPLE_LINE, then record this line and wait the next line until no input with MULTIPLE_LINE. elif input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input[:len(input) - 1] + '; ' while True: next_input = raw_input('... ') if next_input.endswith(self._INPUT_MULTIPLE_LINE): input_multiple = input_multiple + next_input[:len( next_input) - 1] + '; ' pass else: input_multiple = input_multiple + next_input + '; ' print self.execute_script(input_multiple) break # if input is NOT above inputs, then run marionette.execute_script(INPUT)/marionette.execute_async_script(INPUT) # and print return value. else: print self.execute_script(input) except EOFError: self.goodbye() exit() except Exception as e: print e if self.open_app(-1): self.start_js() exit() def execute_script(self, script): try: if self._is_async: return self.m.execute_async_script(script) else: return self.m.execute_script(script) except Exception as ex: print str(ex.message) def goodbye(self): print 'End. Bye!!' def open_app(self, input): try: # connect App by ID app_id = int(input) if app_id < 0: print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() else: iframes = self.get_all_iframes_id_name_pair() print 'Connect to', iframes[str(app_id)] self.set_current_frame(iframes[str(app_id)]) if iframes[str(app_id)] == self._KEYBOARD_FRAME_NAME: self.m.switch_to_frame(app_id, False) else: self.m.switch_to_frame(app_id) return True except (ValueError): # connect to System app if (input.lower() == self._INPUT_SYSTEM_APP_KEYWORD) or ( input.lower() in self._get_system_URL()): print 'Connect to', self._get_system_URL() self.set_current_frame(self._get_system_URL()) self.m.switch_to_frame() return True # connect App by substring iframes = self.get_all_iframes_id_name_pair() suitable_iframes = {} for k, v in iframes.items(): if input in v: suitable_iframes[k] = v # connect to App if there is one fit if len(suitable_iframes) == 1: target = suitable_iframes.keys()[0] print 'Connect to', suitable_iframes.values()[0] self.set_current_frame(suitable_iframes.values()[0]) if suitable_iframes.values()[0] == self._KEYBOARD_FRAME_NAME: self.m.switch_to_frame(int(target), False) else: self.m.switch_to_frame(int(target)) return True # exit if there are more than one app fit the query elif len(suitable_iframes) > 1: print 'There are more than one Apps fit the query string [', input, '].' print '{0:2s} {1:s}'.format('#', 'App URL') for k, v in sorted(suitable_iframes.items()): print '{0:2s} {1:s}'.format(k, v) return False # exit if there is no app fit the query else: print 'There is no App fit the query string [', input, '].' return False def set_current_frame(self, frame_name): self._current_frame = frame_name def get_current_frame(self): return self._current_frame def switch_to_system_frame(self): self.m.switch_to_frame() def _get_system_URL(self): self.switch_to_system_frame() system_URL = self.m.execute_script('return document.URL') return system_URL def _get_all_iframes(self): self.switch_to_system_frame() iframes = self.m.execute_script( 'return document.getElementsByTagName("iframe")') return iframes def get_all_iframes_id_name_pair(self): iframes = self._get_all_iframes() result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] result[str(idx)] = iframe.get_attribute('src') return result def list_all_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') print '{0:2s} {1:7s} {2:s}'.format('-1', '', self._get_system_URL()) for idx in range(0, iframes['length']): iframe = iframes[str(idx)] print '{0:2s} {1:7s} {2:s}'.format( str(idx), ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) def list_active_iframes(self): iframes = self._get_all_iframes() print '{0:2s} {1:7s} {2:s}'.format('#', 'Status', 'App URL') result = {} for idx in range(0, iframes['length']): iframe = iframes[str(idx)] if iframe.is_displayed(): result[str(idx)] = iframe for idx, iframe in result.items(): print '{0:2s} {1:7s} {2:s}'.format( idx, ('active' if iframe.is_displayed() else ''), iframe.get_attribute('src')) # TODO: seems like should using Javascript to get the idx of each iframe!? def get_all_iframes_by_marionette(self): iframes = self.m.find_elements('css selector', 'iframe') return iframes def list_all_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() for iframe in iframes: print iframe.get_attribute('src') def list_active_iframes_by_marionette(self): iframes = self.get_all_iframes_by_marionette() result = [] for iframe in iframes: if iframe.is_displayed(): result.append(iframe) for iframe in result: print iframe.get_attribute('src')