thread.start() # run some trivial unit tests which just verify the protocol m = Marionette(host='localhost', port=2626) assert(m.status()['os']['arch'] == 'x86') assert(m.start_session()) assert(m.get_session_capabilities()['javascriptEnabled'] == True) assert(m.get_window() == server.TEST_CURRENT_WINDOW) assert(m.window == server.TEST_CURRENT_WINDOW) assert(m.get_windows() == server.TEST_WINDOW_LIST) assert(m.switch_to_window('window2')) assert(m.window == 'window2') 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)
def dual_driving(): try: print('Will connect to mobile..') mm = Marionette(host='localhost', port=2829) mm.start_session() if disable_ua_overrides_by_default: set_mozilla_pref(mm, 'general.useragent.site_specific_overrides', False) set_mozilla_pref(mm, 'general.useragent.updates.enabled', False) print('Will connect to desktop...') md = Marionette(host='localhost', port=2828) md.start_session() md.set_search_timeout(1000) # especially required for webcompat.com JS-driven loading ignored_bugs = [] buglist = [] device_map = get_device_map() for line in open(ignore_file, 'r'): if line[0] == '#': continue ignored_bugs.append(line.strip()) if start_url: print 'Starting from bug search %s' % start_url md.navigate(start_url) buglist = extract_buglist(md) else: buglist = extract_buglist_from_file(filename) i = 1 #print(len(buglist)) for item in buglist: if len(item) <= 1: print 'Warning: we expect data format ID Summary URL, something is missing' continue if i<start_at: i+=1 continue buglink = '' if '://' not in item[0]: # assuming this is Bugzilla data from a tab-separated file - in other words a plain bug number # TODO: will we ever process lists of webcompat.com "plain numbers"?? buglink = 'https://bugzilla.mozilla.org/show_bug.cgi?id=%s' % item[0] else: # we've got a bug tracker URL (right?) buglink = item[0] # users who have bugzilla's "load next bug in search" don't need an extra navigate() call if buglink not in md.get_url(): print('Item %s, Loading bug %s'%(i,item[0])) md.navigate(item[0]) if len(item) == 2: # URL is not in the data - let's load the bug first and try to get it from there url = get_url_from_bugpage(md) else: url = item[2] if not url: i+=1 continue if url.strip() == '': i+=1 continue if '://' not in url: url = 'http://%s' % url url = url.strip().rstrip('\r\n') location = urlparse.urlparse(url) hostname = location.hostname.rstrip('\r\n') print str(i) + ' : ' + url reset_spoof(mm) try: mm.navigate(url) try_to_launch_url_in_android(device_map, url) print 'READY to analyze %s, \n%s' % (item[0], item[1]) except: print('could not load %s, try again by pressing u\n\n' % url) options_menu(mm, url, md, device_map) mm.delete_session() md.delete_session() except Exception as err: print err try: mm.delete_session() except: pass try: md.delete_session() except: pass
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 TestCase(tornado.testing.AsyncTestCase): def __init__(self, *args, **kwargs): #self.config = kwargs.pop("config") self.handler = kwargs.pop('handler') self.io_loop = kwargs.pop('io_loop') self.cert_test_app = None super(TestCase, self).__init__(*args, **kwargs) def setUp(self): super(TestCase, self).setUp() # import environment # from environment import InProcessTestEnvironment # self.environment = environment.get(InProcessTestEnvironment) # self.server = self.environment.server self.marionette = None self.create_marionette() self.io_loop.run_sync(self.use_cert_app) def tearDown(self): super(TestCase, self).tearDown() self.io_loop.run_sync(self.close_cert_app) def create_marionette(self): if not self.marionette or not self.marionette.session: self.marionette = Marionette() self.marionette.start_session() @tornado.gen.coroutine def use_cert_app(self): # app management is done in the system app self.marionette.switch_to_frame() self.marionette.import_script("tests/app_management.js") script = "GaiaApps.launchWithName('CertTest App');" try: self.cert_test_app = self.marionette.execute_async_script(script, script_timeout=5000) self.marionette.switch_to_frame(self.cert_test_app["frame"]) self.assertTrue('certtest' in self.marionette.get_url()) except MarionetteException as e: ok = yield self.instruct("Could not launch CertTest app automatically." \ "Please launch by hand then hit OK to continue.") self.assertTrue(ok, "Could not launch CertTest app") except Exception as e: message = "Unexpected exception: %s" % e yield self.instruct(message) self.fail(message) @tornado.gen.coroutine def close_cert_app(self): self.marionette.import_script("tests/app_management.js") # app management is done in the system app self.marionette.switch_to_frame() script = "GaiaApps.kill('%s');" % self.cert_test_app["origin"] try: self.marionette.execute_async_script(script, script_timeout=5000) self.assertTrue('certtest' not in self.marionette.get_url()) except MarionetteException as e: ok = yield self.instruct("Could not close CertTest app automatically." \ "Please close by hand then hit OK to continue.") self.assertTrue(ok, "Could not close CertTest app") except Exception as e: message = "Unexpected exception: %s" % e yield self.instruct(message) self.fail(message) def get_new_ioloop(self): return self.io_loop def prompt(self, message): """Prompt the user for a reply. Returns a future which must be yielded. This will trigger an overlay in the host browser window which can be used to tell the user to perform an action or to input some manual data for us to work on. Sample usage:: answer = yield prompt("What's the meaning of life?") assert answer == 42 This function is a simple wrapper for ``tornado.gen.Task``, and is equivalent to the usage of that. :param message: The question to ask or message to give the user. :returns: A generator which must be yielded. Once yielded, the return value will be the value of the prompt, or False if the user hit 'Cancel' """ return tornado.gen.Task(self.handler.get_user_input, message) def instruct(self, message): """Presents the user with an instruction. Returns a future which must be yielded. This will trigger an overlay in the host browser window which can be used to tell the user to perform an action or to input some manual data for us to work on. Sample usage:: answer = yield prompt("What's the meaning of life?") assert answer == 42 This function is a simple wrapper for ``tornado.gen.Task``, and is equivalent to the usage of that. :param message: The instruction you want to give the user :returns: A generator which must be yielded. Once yielded, the reutrn value will be either True if they succeeded or False if they did not. """ return tornado.gen.Task(self.handler.instruct_user, message)