def set_up_device(opt): if not opt.wifi_ssid or not opt.wifi_key or not opt.wifi_pass: raise ValueError('Missing --wifi options') mc = Marionette('localhost', opt.adb_port) for i in range(2): try: mc.start_session() break except socket.error: sh('adb forward tcp:%s tcp:%s' % (opt.adb_port, opt.adb_port)) if opt.shell: from pdb import set_trace set_trace() return # watch out! This is how gaiatest does it. mc.__class__ = type('Marionette', (Marionette, MarionetteTouchMixin), {}) device = GaiaDevice(mc) device.restart_b2g() apps = GaiaApps(mc) data_layer = GaiaData(mc) lockscreen = LockScreen(mc) mc.setup_touch() lockscreen.unlock() apps.kill_all() data_layer.enable_wifi() if opt.wifi_key == 'WPA-PSK': pass_key = 'psk' elif opt.wifi_key == 'WEP': pass_key = 'wep' else: assert 0, 'unknown key management' data = {'ssid': opt.wifi_ssid, 'keyManagement': opt.wifi_key, pass_key: opt.wifi_pass} data_layer.connect_to_wifi(data) mc.switch_to_frame() all_apps = set(a['manifest']['name'] for a in get_installed(apps)) if 'Marketplace Dev' not in all_apps: mc.execute_script( 'navigator.mozApps.install' '("https://marketplace-dev.allizom.org/manifest.webapp");') wait_for_element_displayed(mc, 'id', 'app-install-install-button') yes = mc.find_element('id', 'app-install-install-button') mc.tap(yes) wait_for_element_displayed(mc, 'id', 'system-banner') print 'Pushing payment prefs' sh('adb shell stop b2g') sh('adb push "%s" /data/local/user.js' % ( os.path.join(os.path.dirname(__file__), 'payment-prefs.js'))) sh('adb shell start b2g') print 'When your device reboots, Marketplace Dev will be installed'
def _StartProcess(self): if not self.isEmulatorInitialized: print("Starting Emulator ...") self.emulatorProcess = subprocess.Popen( [self.emulatorStartScript], cwd=os.path.dirname(self.emulatorStartScript), shell=True) # adb shell setprop net.dns1 10.0.2.3 self._isBootFinished() self.monitoringProcessId = self.adb.getPID( self.monitoredProcessName) print("Forwarding TCP port %d ..." % self.forwardedPortADB) self.adb.command([ "forward", "tcp:%d" % self.forwardedPortADB, "tcp:%d" % self.forwardedPortADB ]) self.isEmulatorInitialized = True time.sleep(20) if self.crashSuccess: print("Restarting %s ..." % self.monitoredProcessName) self.adb.killProcess(self.monitoredProcessName, True) time.sleep(40) self.monitoringProcessId = self.adb.getPID( self.monitoredProcessName) self.crashSuccess = False self.debugLogData = str() self.adb.checkCmd(["logcat", "-c"]) print("Starting Marionette session ...") marionette = Marionette('localhost', self.forwardedPortADB) print(marionette.status()) marionette.start_session() marionette.set_script_timeout(self.scriptTimeout) marionette.switch_to_frame() lock = gaia.LockScreen(marionette) lock.unlock() apps = gaia.GaiaApps(marionette) print(apps.runningApps()) print("Launching Browser application") apps.launch(self.appName, switch_to_frame=True) print("Navigating to %s ..." % self.publisherURL) marionette.execute_script( "return window.wrappedJSObject.Browser.navigate('%s')" % self.publisherURL) self.isMonitorInitialized = True
def launch_app(app_name, adb_path="adb", script_timeout=5000, marionette=None, device_serial=None): """ Launches the given app NOTE: if a marionette session is passed, this function switches to the top-most frame. """ dm = mozdevice.DeviceManagerADB(adbPath=adb_path, deviceSerial=device_serial) installed_app_name = app_name.lower() installed_app_name = installed_app_name.replace(" ", "-") dm.forward("tcp:2828", "tcp:2828") if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() launch_app = """ var launchWithName = function(name) { let apps = window.wrappedJSObject.applications || window.wrappedJSObject.Applications; let installedApps = apps.installedApps; for (let manifestURL in installedApps) { let app = installedApps[manifestURL]; let origin = null; let entryPoints = app.manifest.entry_points; if (entryPoints) { for (let ep in entryPoints) { let currentEntryPoint = entryPoints[ep]; let appName = currentEntryPoint.name; if (name == appName.toLowerCase()) { app.launch(); return true; } } } else { let appName = app.manifest.name; if (name == appName.toLowerCase()) { app.launch(); return true; } } } return false; }; return launchWithName("%s"); """ m.set_script_timeout(script_timeout) m.execute_script(launch_app % app_name.lower()) if not marionette: m.delete_session()
def uninstall_app(app_name, adb_path="adb", script_timeout=5000, marionette=None): """ Uninstalls the given app. NOTE: if a marionette session is passed, this function switches to the top-most frame. """ dm = mozdevice.DeviceManagerADB(adbPath=adb_path) installed_app_name = app_name.lower() installed_app_name = installed_app_name.replace(" ", "-") if dm.forward("tcp:2828", "tcp:2828") != 0: raise Exception("Can't use localhost:2828 for port forwarding." \ "Is something else using port 2828?") if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() uninstall_app = """ var uninstallWithName = function(name) { let apps = window.wrappedJSObject.applications || window.wrappedJSObject.Applications; let installedApps = apps.installedApps; for (let manifestURL in installedApps) { let app = installedApps[manifestURL]; let origin = null; let entryPoints = app.manifest.entry_points; if (entryPoints) { for (let ep in entryPoints) { let currentEntryPoint = entryPoints[ep]; let appName = currentEntryPoint.name; if (name == appName.toLowerCase()) { window.wrappedJSObject.navigator.mozApps.mgmt.uninstall(app); return true; } } } else { let appName = app.manifest.name; if (name == appName.toLowerCase()) { window.wrappedJSObject.navigator.mozApps.mgmt.uninstall(app); return true; } } } return false; }; return uninstallWithName("%s"); """ m.set_script_timeout(script_timeout) m.execute_script(uninstall_app % app_name.lower()) if not marionette: m.delete_session()
def launch_app(app_name, adb_path="adb", script_timeout=5000, marionette=None, device_serial=None): """ Launches the given app NOTE: if a marionette session is passed, this function switches to the top-most frame. """ dm = mozdevice.DeviceManagerADB(adbPath=adb_path,deviceSerial=device_serial) installed_app_name = app_name.lower() installed_app_name = installed_app_name.replace(" ", "-") dm.forward("tcp:2828", "tcp:2828") if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() launch_app = """ var launchWithName = function(name) { let apps = window.wrappedJSObject.applications || window.wrappedJSObject.Applications; let installedApps = apps.installedApps; for (let manifestURL in installedApps) { let app = installedApps[manifestURL]; let origin = null; let entryPoints = app.manifest.entry_points; if (entryPoints) { for (let ep in entryPoints) { let currentEntryPoint = entryPoints[ep]; let appName = currentEntryPoint.name; if (name == appName.toLowerCase()) { app.launch(); return true; } } } else { let appName = app.manifest.name; if (name == appName.toLowerCase()) { app.launch(); return true; } } } return false; }; return launchWithName("%s"); """ m.set_script_timeout(script_timeout) m.execute_script(launch_app % app_name.lower()) if not marionette: m.delete_session()
def _StartProcess(self): if not self.isDeviceInitialized: print("Starting ...") self.monitoringProcessId = self.adb.getPID( self.monitoredProcessName) print("Forwarding TCP port %d ..." % self.forwardedPortADB) self.adb.command([ "forward", "tcp:%d" % self.forwardedPortADB, "tcp:%d" % self.forwardedPortADB ]) self.isDeviceInitialized = True print("Sleeping ...") time.sleep(20) if self.crashSuccess: print("Restarting %s" % self.monitoredProcessName) self.adb.killProcess(self.monitoredProcessName, True) time.sleep(40) self.monitoringProcessId = self.adb.getPID( self.monitoredProcessName) self.crashSuccess = False self.debugLogData = str() self.adb.checkCmd(["logcat", "-c"]) print("Starting Marionette session") marionette = Marionette('localhost', self.forwardedPortADB) print(marionette.status()) marionette.start_session() marionette.set_script_timeout(self.scriptTimeout) marionette.switch_to_frame() lock = gaia.LockScreen(marionette) assert (lock.unlock()) apps = gaia.GaiaApps(marionette) print(apps.runningApps()) print("Launching Browser application") apps.launch(self.appName, switch_to_frame=True) print("Navigating to %s ..." % self.publisherURL) marionette.execute_script( "return window.wrappedJSObject.Browser.navigate('%s')" % self.publisherURL) self.isMonitorInitialized = True
def _StartProcess(self): if not self.isEmulatorInitialized: print("Starting Emulator ...") self.emulatorProcess = subprocess.Popen( [self.emulatorStartScript], cwd=os.path.dirname(self.emulatorStartScript), shell=True) # adb shell setprop net.dns1 10.0.2.3 self._isBootFinished() self.monitoringProcessId = self.adb.getPID(self.monitoredProcessName) print("Forwarding TCP port %d ..." % self.forwardedPortADB) self.adb.command(["forward", "tcp:%d" % self.forwardedPortADB, "tcp:%d" % self.forwardedPortADB]) self.isEmulatorInitialized = True time.sleep(20) if self.crashSuccess: print("Restarting %s ..." % self.monitoredProcessName) self.adb.killProcess(self.monitoredProcessName, True) time.sleep(40) self.monitoringProcessId = self.adb.getPID(self.monitoredProcessName) self.crashSuccess = False self.debugLogData = str() self.adb.checkCmd(["logcat", "-c"]) print("Starting Marionette session ...") marionette = Marionette('localhost', self.forwardedPortADB) print(marionette.status()) marionette.start_session() marionette.set_script_timeout(self.scriptTimeout) marionette.switch_to_frame() lock = gaia.LockScreen(marionette) lock.unlock() apps = gaia.GaiaApps(marionette) print(apps.runningApps()) print("Launching Browser application") apps.launch(self.appName, switch_to_frame=True) print("Navigating to %s ..." % self.publisherURL) marionette.execute_script("return window.wrappedJSObject.Browser.navigate('%s')" % self.publisherURL) self.isMonitorInitialized = True
def _StartProcess(self): if not self.isDeviceInitialized: print("Starting ...") self.monitoringProcessId = self.adb.getPID(self.monitoredProcessName) print("Forwarding TCP port %d ..." % self.forwardedPortADB) self.adb.command(["forward", "tcp:%d" % self.forwardedPortADB, "tcp:%d" % self.forwardedPortADB]) self.isDeviceInitialized = True print("Sleeping ...") time.sleep(20) if self.crashSuccess: print("Restarting %s" % self.monitoredProcessName) self.adb.killProcess(self.monitoredProcessName, True) time.sleep(40) self.monitoringProcessId = self.adb.getPID(self.monitoredProcessName) self.crashSuccess = False self.debugLogData = str() self.adb.checkCmd(["logcat", "-c"]) print("Starting Marionette session") marionette = Marionette('localhost', self.forwardedPortADB) print(marionette.status()) marionette.start_session() marionette.set_script_timeout(self.scriptTimeout) marionette.switch_to_frame() lock = gaia.LockScreen(marionette) assert(lock.unlock()) apps = gaia.GaiaApps(marionette) print(apps.runningApps()) print("Launching Browser application") apps.launch(self.appName, switch_to_frame=True) print("Navigating to %s ..." % self.publisherURL) marionette.execute_script("return window.wrappedJSObject.Browser.navigate('%s')" % self.publisherURL) self.isMonitorInitialized = True
def kill(origin): marionette = Marionette(host='localhost', port=2828) marionette.start_session() marionette.set_context(marionette.CONTEXT_CONTENT) marionette.execute_script("window.wrappedJSObject.WindowManager.kill('%s')" % origin)
class B2gExecutor(Executor): def __init__(self, app_name, app_id, device=False): self.device = device if self.device: call(['adb', 'forward', 'tcp:2828', 'tcp:2828']) self._app_name = app_name self._app_id = app_id self._marionette = Marionette() self._marionette.start_session() # https://github.com/mozilla-b2g/gaia/blob/b568b7ae8adb6ee3651bd75acbaaedff86a08912/tests/python/gaia-ui-tests/gaiatest/gaia_test.py js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) self._marionette.import_script(js) js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_data_layer.js")) self._marionette.set_context(self._marionette.CONTEXT_CHROME) self._marionette.import_script(js) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) ''' # C:\Users\Jun-Wei\Desktop\b2g\battery\manifest.webapp #app = GaiaApps(self._marionette).launch(self._app_name) #app = GaiaApps(self._marionette).launch('Battery', manifest_url='C:/Users/Jun-Wei/Desktop/b2g/battery/manifest.webapp', entry_point='/index.html') app = GaiaApps(self._marionette).launch('Battery') print app.frame print app.src print app.origin print app.name #print g_app.manifest_url #self._app_frame = g_app.frame self._app_frame_id = app.frame self._app_src = app.src self._app_origin = app.origin #self.app_manifest_url = g_app.manifest_url #self.gaia_apps = GaiaApps(self.__marionette) #print self.gaia_apps.displayed_app.name #print self.gaia_apps.installed_apps #print self.gaia_apps.running_apps() #js = os.path.abspath(os.path.join(__file__, os.path.pardir, 'atoms', "gaia_apps.js")) #self.__marionette.import_script(js) ''' def fire_event(self, clickable): logger.info('fire_event: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) try: # id staring with DomAnalyzer.serial_prefix is given by our monkey and should be ignored when locating if clickable.get_id() and not clickable.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', clickable.get_id()).tap() elif clickable.get_xpath(): self._marionette.find_element('xpath', clickable.get_xpath()).tap() else: logger.error('No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in fire_event(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in fire_event(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() def fill_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', input_field.get_id()).send_keys(input_field.get_value()) elif input_field.get_xpath(): self._marionette.find_element('xpath', input_field.get_xpath()).send_keys(input_field.get_value()) else: logger.error('No id nor xpath for an input field in the form id: %s (xpath: %s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in fill_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in fill_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def empty_form(self, clickable): for f in clickable.get_forms(): for input_field in f.get_inputs(): try: if input_field.get_id() and not input_field.get_id().startswith(DomAnalyzer.serial_prefix): self._marionette.find_element('id', input_field.get_id()).clear() elif input_field.get_xpath(): self._marionette.find_element('xpath', input_field.get_xpath()).clear() else: logger.error('No id nor xpath for an input field in the form %s (%s)', f.get_id(), f.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in empty_form(): id: %s (xpath: %s)', f.get_id(), f.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in empty_form(): id: %s (xpath: %s)', str(e), f.get_id(), f.get_xpath()) sys.exit() def get_source(self): return self._marionette.page_source.encode(sys.stdout.encoding, 'ignore') def get_screenshot(self, clickable=None): element = None if clickable: try: if clickable.get_id() and not clickable.get_id().startswith(DomAnalyzer.serial_prefix): element = self._marionette.find_element('id', clickable.get_id()) elif clickable.get_xpath(): element = self._marionette.find_element('xpath', clickable.get_xpath()) else: logger.error('No id nor xpath for the clickable: id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) sys.exit() except (ElementNotVisibleException, InvalidElementStateException, NoSuchElementException): logger.info('Element is not interactable in get_screenshot(): id: %s (xpath: %s)', clickable.get_id(), clickable.get_xpath()) except Exception as e: logger.error('Unknown Exception: %s in get_screenshot(): id: %s (xpath: %s)', str(e), clickable.get_id(), clickable.get_xpath()) sys.exit() if not element: # set context to CHROME to capture whole screen # system frame e.g. FileNotFound cannot be captured without CONTEXT_CHROME (Don't know why) self._marionette.set_context(self._marionette.CONTEXT_CHROME) screenshot = self._marionette.screenshot(element) self._marionette.set_context(self._marionette.CONTEXT_CONTENT) return screenshot def switch_to_frame(self, by, frame_str): """ :param by: options: "id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector", "anon attribute" """ # self.switch_to_top_frame() frame = self._marionette.find_element(by, frame_str) self._marionette.switch_to_frame(frame) def switch_to_top_frame(self): self._marionette.switch_to_frame() # switch to the top-level frame def restart_app(self): # remember to disable screen timeout and screen lock before testing # todo: open b2g simulator, install app, # unlock_screen # self._marionette.execute_script('window.wrappedJSObject.lockScreen.unlock();') self.kill_all_apps() # kill_all_apps() will also kill the 'homescreen app' on real device # so trigger a home event to restore homescreen if self.device: self._dispatch_home_button_event() self.clear_data() self.touch_home_button() # launch the app if self.device: icon = self._marionette.find_element('xpath', "//li[contains(@aria-label, '" + self._app_name + "')]") else: icon = self._marionette.find_element('xpath', "//div[contains(@class, 'icon')]//span[contains(text(),'" + self._app_name + "')]") icon.tap() time.sleep(5) # wait for app screen self._marionette.switch_to_frame() # this wait seems not working, need to find another useful one Wait(self._marionette).until(lambda m: m.find_element('css selector', "iframe[data-url*='" + self._app_id + "']").is_displayed()) app_frame = self._marionette.find_element('css selector', "iframe[data-url*='" + self._app_id + "']") self._marionette.switch_to_frame(app_frame) def touch_home_button(self): # ref: https://github.com/mozilla-b2g/gaia/blob/master/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L751 apps = GaiaApps(self._marionette) if apps.displayed_app.name.lower() != 'homescreen': # touching home button will return to homescreen self._dispatch_home_button_event() Wait(self._marionette).until( lambda m: apps.displayed_app.name.lower() == 'homescreen') apps.switch_to_displayed_app() else: apps.switch_to_displayed_app() mode = self._marionette.find_element(By.TAG_NAME, 'body').get_attribute('class') self._dispatch_home_button_event() apps.switch_to_displayed_app() if 'edit-mode' in mode: # touching home button will exit edit mode Wait(self._marionette).until(lambda m: m.find_element( By.TAG_NAME, 'body').get_attribute('class') != mode) else: # touching home button inside homescreen will scroll it to the top Wait(self._marionette).until(lambda m: m.execute_script( "return window.wrappedJSObject.scrollY") == 0) def _dispatch_home_button_event(self): self._marionette.switch_to_frame() self._marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('home'));") time.sleep(0.5) def clear_data(self): # for now, clear contact data # https://github.com/mozilla-b2g/gaia/blob/v2.2/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L208 self._marionette.set_context(self._marionette.CONTEXT_CHROME) result = self._marionette.execute_async_script('return GaiaDataLayer.removeAllContacts();') assert result, 'Unable to remove all contacts' self._marionette.set_context(self._marionette.CONTEXT_CONTENT) time.sleep(0.5) def kill_all_apps(self): self._marionette.switch_to_frame() self._marionette.execute_async_script(""" // Kills all running apps, except the homescreen. function killAll() { let manager = window.wrappedJSObject.appWindowManager; let apps = manager.getApps(); for (let id in apps) { let origin = apps[id].origin; if (origin.indexOf('verticalhome') == -1) { manager.kill(origin); } } }; killAll(); // return true so execute_async_script knows the script is complete marionetteScriptFinished(true); """) time.sleep(0.5)
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) 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())
class B2GMixin(object): profileDir = None userJS = "/data/local/user.js" marionette = None def __init__(self, host=None, marionetteHost=None, marionettePort=2828, **kwargs): # (allowing marionneteHost to be specified seems a bit # counter-intuitive since we normally get it below from the ip # address, however we currently need it to be able to connect # via adb port forwarding and localhost) if marionetteHost: self.marionetteHost = marionetteHost elif host: self.marionetteHost = host self.marionettePort = marionettePort def cleanup(self): """ If a user profile was setup on the device, restore it to the original. """ if self.profileDir: self.restoreProfile() def waitForPort(self, timeout): """Waits for the marionette server to respond, until the timeout specified. :param timeout: Timeout parameter in seconds. """ print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionettePort, self.marionetteHost) sock.connect((self.marionetteHost, self.marionettePort)) data = sock.recv(16) sock.close() if '"from"' in data: return True except socket.error: pass except Exception as e: raise DMError("Could not connect to marionette: %s" % e) time.sleep(1) raise DMError("Could not communicate with Marionette port") def setupMarionette(self, scriptTimeout=60000): """ Starts a marionette session. If no host was given at init, the ip of the device will be retrieved and networking will be established. """ if not self.marionetteHost: self.setupDHCP() self.marionetteHost = self.getIP() if not self.marionette: self.marionette = Marionette(self.marionetteHost, self.marionettePort) if not self.marionette.session: self.waitForPort(30) self.marionette.start_session() self.marionette.set_script_timeout(scriptTimeout) def restartB2G(self): """ Restarts the b2g process on the device. """ #restart b2g so we start with a clean slate if self.marionette and self.marionette.session: self.marionette.delete_session() self.shellCheckOutput(['stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. tries = 10 while "b2g" in self.shellCheckOutput(['ps', 'b2g']) and tries > 0: tries -= 1 time.sleep(1) if tries == 0: raise DMError("Could not kill b2g process") self.shellCheckOutput(['start', 'b2g']) def setupProfile(self, prefs=None): """Sets up the user profile on the device. :param prefs: String of user_prefs to add to the profile. Defaults to a standard b2g testing profile. """ # currently we have no custom prefs to set (when bug 800138 is fixed, # we will probably want to enable marionette on an external ip by # default) if not prefs: prefs = "" #remove previous user.js if there is one if not self.profileDir: self.profileDir = tempfile.mkdtemp() our_userJS = os.path.join(self.profileDir, "user.js") mozfile.remove(our_userJS) #copy profile try: self.getFile(self.userJS, our_userJS) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) with open(our_userJS, 'a') as user_file: user_file.write("%s" % prefs) self.pushFile(our_userJS, self.userJS) self.restartB2G() self.setupMarionette() def setupDHCP(self, interfaces=['eth0', 'wlan0']): """Sets up networking. :param interfaces: Network connection types to try. Defaults to eth0 and wlan0. """ all_interfaces = [line.split()[0] for line in \ self.shellCheckOutput(['netcfg']).splitlines()[1:]] interfaces_to_try = filter(lambda i: i in interfaces, all_interfaces) tries = 5 print "Setting up DHCP..." while tries > 0: print "attempts left: %d" % tries try: for interface in interfaces_to_try: self.shellCheckOutput(['netcfg', interface, 'dhcp'], timeout=10) if self.getIP(interfaces=[interface]): return except DMError: pass time.sleep(1) tries -= 1 raise DMError("Could not set up network connection") def restoreProfile(self): """ Restores the original user profile on the device. """ if not self.profileDir: raise DMError("There is no profile to restore") #if we successfully copied the profile, make a backup of the file our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) mozfile.remove(self.profileDir) self.profileDir = None def getAppInfo(self): """ Returns the appinfo, with an additional "date" key. :rtype: dictionary """ if not self.marionette or not self.marionette.session: self.setupMarionette() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
class GCli(object): def __init__(self): self.commands = { 'connectwifi': { 'function': self.connect_to_wifi, 'args': [ {'name': 'ssid', 'help': 'SSID of the network to connect to'}, {'name': '--security', 'choices': ['WPA-PSK', 'WEP'], 'help': 'Security model of the network'}, {'name': '--password', 'help': 'Password to access the network'}], 'help': 'Connect to a WiFi network'}, 'disablewifi': { 'function': self.disable_wifi, 'help': 'Disable WiFi'}, 'enablewifi': { 'function': self.enable_wifi, 'help': 'Enable WiFi'}, 'forgetallwifi': { 'function': self.forget_all_wifi_networks, 'help': 'Forget all WiFi networks'}, 'getknownwifi': { 'function': self.known_wifi_networks, 'help': 'Show known WiFi networks'}, 'getsetting': { 'function': self.get_setting, 'args': [{ 'name': 'name', 'help': 'Name of the setting to retrieve'}], 'help': 'Show the current value of a setting'}, 'holdhome': { 'function': self.hold_home, 'help': 'Simulate holding the home button'}, 'holdsleep': { 'function': self.hold_sleep, 'help': 'Simulate holding the sleep button'}, 'home': { 'function': self.home, 'help': 'Simulate pressing the home button'}, 'killapps': { 'function': self.kill_all_apps, 'help': 'Kill all running apps'}, 'launchapp': { 'function': self.launch_app, 'args': [ {'name': 'name', 'nargs': argparse.REMAINDER, 'help': 'Name of app to launch'}], 'help': 'Launch an application'}, 'lock': { 'function': self.lock, 'help': 'Lock screen'}, 'screenshot': { 'function': self.screenshot, 'help': 'Take a screenshot'}, 'sendsms': { 'function': self.send_sms, 'args': [ {'name': 'number', 'help': 'Phone number of recipient'}, {'name': 'message', 'help': 'Message content'}], 'help': 'Send an SMS'}, 'setsetting': { 'function': self.set_setting, 'args': [ {'name': 'name', 'help': 'Name of setting to change'}, {'name': 'value', 'help': 'New value for setting'}], 'help': 'Change the value of a setting'}, 'sleep': { 'function': self.sleep, 'help': 'Enter sleep mode'}, 'unlock': { 'function': self.unlock, 'help': 'Unlock screen'}, 'volume': { 'function': self.volume, 'args': [ {'name': 'direction', 'choices': ['down', 'up'], 'help': 'Direction to change the volume'}], 'help': 'Change the volume'}, 'wake': { 'function': self.wake, 'help': 'Wake from sleep mode'}} self.parser = argparse.ArgumentParser() self.add_options(self.parser) self.add_commands(self.parser) def run(self, args=sys.argv[1:]): args = self.parser.parse_args() host, port = args.address.split(':') self.marionette = Marionette(host=host, port=int(port)) self.marionette.start_session() self.apps = gaiatest.GaiaApps(self.marionette) self.data_layer = gaiatest.GaiaData(self.marionette) self.lock_screen = gaiatest.LockScreen(self.marionette) ret = args.func(args) if ret is None: ret = 0 self.marionette.delete_session() sys.exit(ret) def add_options(self, parser): parser.add_argument( '--address', default='localhost:2828', help='Address (host:port) of running Gecko instance to connect to ' '(default: %(default)s)') def add_commands(self, parser): subparsers = parser.add_subparsers( title='Commands', metavar='<command>') for (name, props) in sorted(self.commands.iteritems()): subparser = subparsers.add_parser(name, help=props['help']) if props.get('args'): for arg in props['args']: kwargs = {k: v for k, v in arg.items() if k is not 'name'} subparser.add_argument(arg['name'], **kwargs) subparser.set_defaults(func=props['function']) def connect_to_wifi(self, args): network = { 'ssid': args.ssid, 'keyManagement': args.security or 'NONE'} if args.security == 'WEP': network['wep'] = args.password elif args.security == 'WPA-PSK': network['psk'] = args.password self.data_layer.connect_to_wifi(network) def disable_wifi(self, args): self.data_layer.disable_wifi() def enable_wifi(self, args): self.data_layer.enable_wifi() def forget_all_wifi_networks(self, args): self.data_layer.forget_all_networks() def get_setting(self, args): print '%s: %s' % ( args.name, self.data_layer.get_setting(args.name)) def home(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('home'));") def hold_home(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('holdhome'));") def hold_sleep(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('holdsleep'));") def kill_all_apps(self, args): self.apps.kill_all() def known_wifi_networks(self, args): networks = [n for n in self.data_layer.known_networks if 'ssid' in n] if len(networks) > 0: for i, network in enumerate(networks): print '%s: %s' % (i + 1, network['ssid']) else: print 'No known networks.' def launch_app(self, args): for name in args.name: self.apps.launch(name) def lock(self, args): self.lock_screen.lock() def screenshot(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('home+sleep'));") def send_sms(self, args): self.data_layer.send_sms(args.number, args.message) def set_setting(self, args): self.data_layer.set_setting(args.name, args.value) def sleep(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('sleep'));") def unlock(self, args): self.lock_screen.unlock() def volume(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('volume%s'));" % args.direction) def wake(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('wake'));")
class B2GMochitest(MochitestUtilsMixin): marionette = None def __init__(self, marionette_args, out_of_process=True, profile_data_dir=None, locations=os.path.join(here, 'server-locations.txt')): super(B2GMochitest, self).__init__() self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' if profile_data_dir: self.preferences = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref') ] self.webapps = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp') ] # mozinfo is populated by the parent class if mozinfo.info['debug']: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 def setup_common_options(self, options): test_url = self.buildTestPath(options) # For B2G emulators buildURLOptions has been called # without calling buildTestPath first and that # causes manifestFile not to be set if not "manifestFile=tests.json" in self.urlOpts: self.urlOpts.append("manifestFile=%s" % options.manifestFile) if len(self.urlOpts) > 0: test_url += "?" + "&".join(self.urlOpts) self.test_script_args.append(test_url) def buildTestPath(self, options): if options.manifestFile != 'tests.json': super(B2GMochitest, self).buildTestPath(options, disabled=False) return self.buildTestURL(options) def build_profile(self, options): # preferences prefs = {} for path in self.preferences: prefs.update(Preferences.read_prefs(path)) for v in options.extraPrefs: thispref = v.split("=", 1) if len(thispref) < 2: print "Error: syntax error in --setpref=" + v sys.exit(1) prefs[thispref[0]] = thispref[1] # interpolate the preferences interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort), "OOP": "true" if self.out_of_process else "false" } prefs = json.loads(json.dumps(prefs) % interpolation) for pref in prefs: prefs[pref] = Preferences.cast(prefs[pref]) kwargs = { 'addons': self.getExtensionsToInstall(options), 'apps': self.webapps, 'locations': self.locations_file, 'preferences': prefs, 'proxy': { "remote": options.webServer } } if options.profile: self.profile = Profile.clone(options.profile, **kwargs) else: self.profile = Profile(**kwargs) options.profilePath = self.profile.profile # TODO bug 839108 - mozprofile should probably handle this manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest def run_tests(self, options): """ Prepare, configure, run tests and cleanup """ manifest = self.build_profile(options) self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log") if options.debugger or not options.autorun: timeout = None else: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 timeout = options.timeout + 30.0 log.info("runtestsb2g.py | Running tests: start.") status = 0 try: self.marionette_args['profile'] = self.profile self.marionette = Marionette(**self.marionette_args) self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists(posixpath.dirname( self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) self.startServers(options, None) self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) # Disable offline status management (bug 777145), otherwise the network # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script( script.read(), script_args=self.test_script_args) else: self.marionette.execute_script( self.test_script, script_args=self.test_script_args) status = self.runner.wait() if status is None: # the runner has timed out status = 124 except KeyboardInterrupt: log.info("runtests.py | Received keyboard interrupt.\n") status = -1 except: traceback.print_exc() log.error( "Automation Error: Received unexpected exception while running application\n" ) if hasattr(self, 'runner'): self.runner.check_for_crashes() status = 1 self.stopServers() log.info("runtestsb2g.py | Running tests: end.") if manifest is not None: self.cleanup(manifest, options) return status
# The Initial Developer of the Original Code is # Mozilla Foundation. # Portions created by the Initial Developer are Copyright (C) 2011 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Jonathan Griffin <*****@*****.**> # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** from marionette import Marionette, HTMLElement if __name__ == '__main__': # launch Fennec with Marionette before starting this test! m = Marionette(host='localhost', port=2828) assert (m.start_session()) assert (10 == m.execute_script('return 10;'))
class B2GMixin(object): profileDir = None userJS = "/data/local/user.js" marionette = None def __init__(self, host=None, marionette_port=2828, **kwargs): self.marionetteHost = host self.marionettePort = marionette_port def cleanup(self): if self.profileDir: self.restoreProfile() def waitForPort(self, timeout): """ Wait for the marionette server to respond. Timeout parameter is in seconds """ print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta( seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionettePort, self.marionetteHost) sock.connect((self.marionetteHost, self.marionettePort)) data = sock.recv(16) sock.close() if '"from"' in data: return True except socket.error: pass except Exception as e: raise DMError("Could not connect to marionette: %s" % e) time.sleep(1) raise DMError("Could not communicate with Marionette port") def setupMarionette(self): """ Start a marionette session. If no host is given, then this will get the ip of the device, and set up networking if needed. """ if not self.marionetteHost: self.setupDHCP() self.marionetteHost = self.getIP() if not self.marionette: self.marionette = Marionette(self.marionetteHost, self.marionettePort) if not self.marionette.session: self.waitForPort(30) self.marionette.start_session() def restartB2G(self): """ Restarts the b2g process on the device """ #restart b2g so we start with a clean slate if self.marionette and self.marionette.session: self.marionette.delete_session() self.shellCheckOutput(['stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. tries = 10 while "b2g" in self.shellCheckOutput(['ps', 'b2g']) and tries > 0: tries -= 1 time.sleep(1) if tries == 0: raise DMError("Could not kill b2g process") self.shellCheckOutput(['start', 'b2g']) def setupProfile(self, prefs=None): """ Sets up the user profile on the device, The 'prefs' is a string of user_prefs to add to the profile. If it is not set, it will default to a standard b2g testing profile. """ if not prefs: prefs = """ user_pref("power.screen.timeout", 999999); user_pref("devtools.debugger.force-local", false); """ #remove previous user.js if there is one if not self.profileDir: self.profileDir = tempfile.mkdtemp() our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): os.remove(our_userJS) #copy profile try: output = self.getFile(self.userJS, our_userJS) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_userJS): self.shellCheckOutput( ['dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) with open(our_userJS, 'a') as user_file: user_file.write("%s" % prefs) self.pushFile(our_userJS, self.userJS) self.restartB2G() self.setupMarionette() def setupDHCP(self, conn_type='eth0'): """ Sets up networking. If conn_type is not set, it will assume eth0. """ tries = 5 while tries > 0: print "attempts left: %d" % tries try: self.shellCheckOutput(['netcfg', conn_type, 'dhcp'], timeout=10) if self.getIP(): return except DMError: pass tries = tries - 1 raise DMError("Could not set up network connection") def restoreProfile(self): """ Restores the original profile """ if not self.profileDir: raise DMError("There is no profile to restore") #if we successfully copied the profile, make a backup of the file our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): self.shellCheckOutput( ['dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) shutil.rmtree(self.profileDir) self.profileDir = None def getAppInfo(self): """ Returns the appinfo, with an additional "date" key. """ if not self.marionette or not self.marionette.session: self.setupMarionette() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
class GCli(object): def __init__(self): self.commands = { "connectwifi": { "function": self.connect_to_wifi, "args": [ {"name": "ssid", "help": "SSID of the network to connect to"}, {"name": "--security", "choices": ["WPA-PSK", "WEP"], "help": "Security model of the network"}, {"name": "--password", "help": "Password to access the network"}, ], "help": "Connect to a WiFi network", }, "disablewifi": {"function": self.disable_wifi, "help": "Disable WiFi"}, "enablewifi": {"function": self.enable_wifi, "help": "Enable WiFi"}, "forgetallwifi": {"function": self.forget_all_wifi_networks, "help": "Forget all WiFi networks"}, "getknownwifi": {"function": self.known_wifi_networks, "help": "Show known WiFi networks"}, "getsetting": { "function": self.get_setting, "args": [{"name": "name", "help": "Name of the setting to retrieve"}], "help": "Show the current value of a setting", }, "holdhome": {"function": self.hold_home, "help": "Simulate holding the home button"}, "holdsleep": {"function": self.hold_sleep, "help": "Simulate holding the sleep button"}, "home": {"function": self.home, "help": "Simulate pressing the home button"}, "killapps": {"function": self.kill_all_apps, "help": "Kill all running apps"}, "launchapp": { "function": self.launch_app, "args": [{"name": "name", "nargs": argparse.REMAINDER, "help": "Name of app to launch"}], "help": "Launch an application", }, "lock": {"function": self.lock, "help": "Lock screen"}, "screenshot": {"function": self.screenshot, "help": "Take a screenshot"}, "sendsms": { "function": self.send_sms, "args": [ {"name": "number", "help": "Phone number of recipient"}, {"name": "message", "help": "Message content"}, ], "help": "Send an SMS", }, "setsetting": { "function": self.set_setting, "args": [ {"name": "name", "help": "Name of setting to change"}, {"name": "value", "help": "New value for setting"}, ], "help": "Change the value of a setting", }, "sleep": {"function": self.sleep, "help": "Enter sleep mode"}, "unlock": {"function": self.unlock, "help": "Unlock screen"}, "volume": { "function": self.volume, "args": [{"name": "direction", "choices": ["down", "up"], "help": "Direction to change the volume"}], "help": "Change the volume", }, "wake": {"function": self.wake, "help": "Wake from sleep mode"}, } self.parser = argparse.ArgumentParser() self.add_options(self.parser) self.add_commands(self.parser) def run(self, args=sys.argv[1:]): args = self.parser.parse_args() host, port = args.address.split(":") self.marionette = Marionette(host=host, port=int(port)) self.marionette.start_session() self.apps = gaiatest.GaiaApps(self.marionette) self.data_layer = gaiatest.GaiaData(self.marionette) self.lock_screen = gaiatest.LockScreen(self.marionette) ret = args.func(args) if ret is None: ret = 0 self.marionette.delete_session() sys.exit(ret) def add_options(self, parser): parser.add_argument( "--address", default="localhost:2828", help="Address (host:port) of running Gecko instance to connect to " "(default: %(default)s)", ) def add_commands(self, parser): subparsers = parser.add_subparsers(title="Commands", metavar="<command>") for (name, props) in sorted(self.commands.iteritems()): subparser = subparsers.add_parser(name, help=props["help"]) if props.get("args"): for arg in props["args"]: kwargs = {k: v for k, v in arg.items() if k is not "name"} subparser.add_argument(arg["name"], **kwargs) subparser.set_defaults(func=props["function"]) def connect_to_wifi(self, args): network = {"ssid": args.ssid, "keyManagement": args.security or "NONE"} if args.security == "WEP": network["wep"] = args.password elif args.security == "WPA-PSK": network["psk"] = args.password self.data_layer.connect_to_wifi(network) def disable_wifi(self, args): self.data_layer.disable_wifi() def enable_wifi(self, args): self.data_layer.enable_wifi() def forget_all_wifi_networks(self, args): self.data_layer.forget_all_networks() def get_setting(self, args): print "%s: %s" % (args.name, self.data_layer.get_setting(args.name)) def home(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('home'));") def hold_home(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('holdhome'));") def hold_sleep(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('holdsleep'));") def kill_all_apps(self, args): self.apps.kill_all() def known_wifi_networks(self, args): networks = [n for n in self.data_layer.known_networks if "ssid" in n] if len(networks) > 0: for i, network in enumerate(networks): print "%s: %s" % (i + 1, network["ssid"]) else: print "No known networks." def launch_app(self, args): for name in args.name: self.apps.launch(name) def lock(self, args): self.lock_screen.lock() def screenshot(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('home+sleep'));") def send_sms(self, args): self.data_layer.send_sms(args.number, args.message) def set_setting(self, args): self.data_layer.set_setting(args.name, args.value) def sleep(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('sleep'));") def unlock(self, args): self.lock_screen.unlock() def volume(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('volume%s'));" % args.direction) def wake(self, args): self.marionette.execute_script("window.wrappedJSObject.dispatchEvent(new Event('wake'));")
def ftu(locale, device_flags, hashes): db = get_db(locale) app = 'ftu' app_categ = 'communications' app_type = 'apps' gaia_hash = hashes[0] hg_hash = hashes[1] screenshot_path = app_type + '/' + app_categ + '/' + app create_screenshot_dir(locale, screenshot_path) client = Marionette('localhost', 2828) client.start_session() change_screen_timeout(client) ftu_iframe = client.find_element('css selector',"iframe[src='app://communications.gaiamobile.org/ftu/index.html']") if client.switch_to_frame(ftu_iframe): time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#languages file_name = 'languages' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#activation-screen') # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 1st attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # invalid pin -> error about pin characters # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-pin_screen-invalid_pin' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (attempt 1 of 3) pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 2nd attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen-wrong_pin-1st_time' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (attempt 2 of 3) pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 3rd attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen-wrong_pin-2nd_time' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (atempt 3 of 3) - continues to puk screen pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # tap the header in order to hide keyboard and get full screenshot of the puk screen header = client.find_element('css selector', '#unlock-sim-header') header.tap() # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - 1st attempt) file_name = 'data_3g-unlock_sim_screen-puk_screen' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # invalid puk -> error about puk characters # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - puk error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_puk' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong puk - invalid new pin -> error about pin characters puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(wrong_sim_puk) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_new_pin' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert new pin without confirmed it -> error about pin confirmation (wrong puk is preserved at its input) newpin_input = client.find_element('css selector', '#newpin-input') newpin_input.send_keys(sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_new_pin_confirmation' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert confirmation of the new pin (wrong puk and new pin are preserved at their inputs) newpin_input = client.find_element('css selector', '#confirm-newpin-input') newpin_input.send_keys(sim_pin) # wrong puk (attempt 1 of 9) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - puk error) file_name = 'data_3g-unlock_sim_screen-puk_screen-wrong_puk-1st_time' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong puk (attempt 2 of 9) (new pin and its confirmation are preserved at their inputs) puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(wrong_sim_puk) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - 2nd attempt) file_name = 'data_3g-unlock_sim_screen-puk_screen-wrong_puk-2nd_time' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert right puk (attempt 2 of 9) (new pin and its confirmation are preserved at their inputs) puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(sim_puk) # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g file_name = 'data_3g' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = { "sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#activation-screen') # switch to app://communications.gaiamobile.org/ftu/index.html#wifi next = client.find_element('css selector', '#forward') next.tap() time.sleep(long_time) # enable overlay "scanningNetworks" spinner screen (loading-overlay) client.execute_script("window.wrappedJSObject.utils.overlay.show('scanningNetworks', 'spinner');") time.sleep(middle_time) # overlay "scanningNetworks" spinner screen file_name = 'wifi-loading_overlay-scanning_networks' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#loading-overlay') # disable overlay "scanningNetworks" spinner screen client.execute_script("window.wrappedJSObject.utils.overlay.hide('scanningNetworks', 'spinner');") time.sleep(middle_time) # app://communications.gaiamobile.org/ftu/index.html#wifi file_name = 'wifi' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#wifi') # switch to app://communications.gaiamobile.org/ftu/index.html#date_and_time next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # Set time in order to be the same at every screenshot set_time(client) # app://communications.gaiamobile.org/ftu/index.html#date_and_time file_name = 'date_and_time' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#date_and_time') # switch to app://communications.gaiamobile.org/ftu/index.html#geolocation next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#geolocation file_name = 'geolocation' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#geolocation') # switch to app://communications.gaiamobile.org/ftu/index.html#import_contacts next = client.find_element('css selector', '#forward') next.tap() time.sleep(middle_time) # app://communications.gaiamobile.org/ftu/index.html#import_contacts file_name = 'import_contacts' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#import_contacts') # switch to app://communications.gaiamobile.org/ftu/index.html#welcome_browser next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#welcome_browser file_name = 'welcome_browser' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#browser_privacy next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#browser_privacy file_name = 'browser_privacy' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step1 (first tutorial screen) next = client.find_element('css selector', '#forward') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step1 (first tutorial screen) file_name = 'step1-first_tutorial_screen' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step1 next = client.find_element('css selector', '#lets-go-button') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step1 file_name = 'step1' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step2 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step2 file_name = 'step2' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step3 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step3 file_name = 'step3' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step4 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step4 file_name = 'step4' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step4 (last tutorial screen) next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step4 (last tutorial screen) file_name = 'step4-last_tutorial_screen' screenshot_entry = { "id" : file_name, "script" : app, "gaia" : gaia_hash, "hg" : hg_hash, "changed" : False, "path" : screenshot_path} test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # Close ftu frame next = client.find_element('css selector', '#tutorialFinished') next.tap() time.sleep(short_time) set_db(locale, db) else: #TO DO raise proper error print "switch to ftu frame failed"
waitFor( function() { window.wrappedJSObject.LockScreen.unlock(); waitFor( function() { finish(window.wrappedJSObject.LockScreen.locked); }, function() { return !window.wrappedJSObject.LockScreen.locked; } ); }, function() { return !!window.wrappedJSObject.LockScreen; } ); """ from marionette import Marionette marionette = Marionette('localhost', 2828) marionette.start_session() marionette.import_script('gaia_apps.js') marionette.set_script_timeout(60000) marionette.execute_script(unlock) result = marionette.execute_async_script("GaiaApps.launchWithName('%s')" % "MozCampAsia-QualityApps") marionette.switch_to_frame(result.get("frame")) pdb.set_trace()
#!/usr/bin/python2.7 # # Script to work around Marionette bug 879816 (cannot click the modal 'ok' button # following clicking something else). # from marionette import Marionette marionette = Marionette(host='localhost', port=2828) marionette.start_session() marionette.switch_to_frame() marionette.execute_script("document.getElementById('modal-dialog-prompt-ok').click();")
class B2GRunner(object): remote_profile_dir = None def __init__(self, dm, url, tmpdir, marionette_host=None, marionette_port=None): self.dm = dm self.url = url self.tmpdir = tmpdir self.userJS = "/data/local/user.js" self.marionette_host = marionette_host or 'localhost' self.marionette_port = marionette_port or 2828 self.marionette = None def wait_for_port(self, timeout): 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(('localhost', self.marionette_port)) data = sock.recv(16) sock.close() if '"from"' in data: return True except: import traceback print traceback.format_exc() time.sleep(1) return False def restart_b2g(self): #restart b2g so we start with a clean slate self.dm.checkCmd(['shell', 'stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. time.sleep(10) self.dm.checkCmd(['shell', 'start', 'b2g']) #wait for marionette port to come up if not self.wait_for_port(30000): raise Exception( "Could not communicate with Marionette port after restarting B2G" ) self.marionette = Marionette(self.marionette_host, self.marionette_port) def setup_profile(self): #remove previous user.js if there is one our_user_js = os.path.join(self.tmpdir, "user.js") if os.path.exists(our_user_js): os.remove(our_user_js) #copy profile try: self.dm.checkCmd(["pull", self.userJS, our_user_js]) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_user_js): self.dm.checkCmd([ 'shell', 'dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS ]) user_js = open(our_user_js, 'a') user_js.write(""" user_pref("power.screen.timeout", 999999); """) user_js.close() self.dm.checkCmd(['push', our_user_js, self.userJS]) self.restart_b2g() def start(self): #forward the marionette port self.dm.checkCmd([ 'forward', 'tcp:%s' % self.marionette_port, 'tcp:%s' % self.marionette_port ]) print "Setting up profile" self.setup_profile() #enable ethernet connection print "Running netcfg, it may take some time." self.dm.checkCmd(['shell', 'netcfg', 'eth0', 'dhcp']) #launch app session = self.marionette.start_session() if 'b2g' not in session: raise Exception("bad session value %s returned by start_session" % session) # start the tests by navigating to the mochitest url self.marionette.execute_script("window.location.href='%s';" % self.url) def stop(self): self.marionette.delete_session()
def run(self, script, address='localhost:2828', symbols=None, treeherder='https://treeherder.mozilla.org/', reset=False, **kwargs): try: host, port = address.split(':') except ValueError: raise ValueError('--address must be in the format host:port') # Check that Orangutan is installed self.adb_device = ADBDevice(self.device_serial) orng_path = posixpath.join('data', 'local', 'orng') if not self.adb_device.exists(orng_path): raise Exception('Orangutan not found! Please install it according ' 'to the documentation.') self.runner = B2GDeviceRunner( serial=self.device_serial, process_args={'stream': None}, symbols_path=symbols, logdir=self.temp_dir) if reset: self.runner.start() else: self.runner.device.connect() port = self.runner.device.setup_port_forwarding(remote_port=port) assert self.runner.device.wait_for_port(port), \ 'Timed out waiting for port!' marionette = Marionette(host=host, port=port) marionette.start_session() try: marionette.set_context(marionette.CONTEXT_CHROME) self.is_debug = marionette.execute_script( 'return Components.classes["@mozilla.org/xpcom/debug;1"].' 'getService(Components.interfaces.nsIDebug2).isDebugBuild;') marionette.set_context(marionette.CONTEXT_CONTENT) if reset: gaia_device = GaiaDevice(marionette) gaia_device.wait_for_b2g_ready(timeout=120) gaia_device.unlock() gaia_apps = GaiaApps(marionette) gaia_apps.kill_all() # TODO: Disable bluetooth, emergency calls, carrier, etc # Run Orangutan script remote_script = posixpath.join(self.adb_device.test_root, 'orng.script') self.adb_device.push(script, remote_script) self.start_time = time.time() # TODO: Kill remote process on keyboard interrupt self.adb_device.shell('%s %s %s' % (orng_path, self.device_properties['input'], remote_script)) self.end_time = time.time() self.adb_device.rm(remote_script) except (MarionetteException, IOError): if self.runner.crashed: # Crash has been detected pass else: raise self.runner.check_for_crashes(test_name='b2gmonkey') # Report results to Treeherder required_envs = ['TREEHERDER_KEY', 'TREEHERDER_SECRET'] if all([os.environ.get(v) for v in required_envs]): self.post_to_treeherder(script, treeherder) else: self._logger.info( 'Results will not be posted to Treeherder. Please set the ' 'following environment variables to enable Treeherder ' 'reports: %s' % ', '.join([ v for v in required_envs if not os.environ.get(v)]))
# Mozilla Foundation. # Portions created by the Initial Developer are Copyright (C) 2011 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Jonathan Griffin <*****@*****.**> # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** from marionette import Marionette, HTMLElement if __name__ == '__main__': # launch Fennec with Marionette before starting this test! m = Marionette(host='localhost', port=2828) assert(m.start_session()) assert(10 == m.execute_script('return 10;'))
def uninstall_app(app_name, adb_path="adb", script_timeout=5000, marionette=None, device_serial=None): """ Uninstalls the given app. NOTE: if a marionette session is passed, this function switches to the top-most frame. """ def check_uninstall(marionette): uninstall = marionette.execute_script("return window.wrappedJSObject.uninstall;") return uninstall != 'none' def check_click_uninstall(marionette): button = marionette.find_element('css selector', 'gaia-confirm .confirm') try: button.click() not_displayed = not button.is_displayed() except StaleElementException: not_displayed = True return not_displayed dm = mozdevice.DeviceManagerADB(adbPath=adb_path, deviceSerial=device_serial) installed_app_name = app_name.lower() installed_app_name = installed_app_name.replace(" ", "-") dm.forward("tcp:2828", "tcp:2828") print 'requesting uninstall of app', app_name if not marionette: m = Marionette() m.start_session() else: m = marionette m.switch_to_frame() m.set_context("chrome") m.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); window.wrappedJSObject.uninstall = 'none'; Services.obs.addObserver(function observer(subject, topic) { Services.obs.removeObserver(observer, topic); window.wrappedJSObject.uninstall = 'ask'; }, "webapps-ask-uninstall", false); Services.obs.addObserver(function observer(subject, topic) { Services.obs.removeObserver(observer, topic); window.wrappedJSObject.uninstall = 'uninstall'; }, "webapps-uninstall", false); """) m.set_context("content") uninstall_app = """ var uninstallWithName = function(name) { let apps = window.wrappedJSObject.applications || window.wrappedJSObject.Applications; let installedApps = apps.installedApps; for (let manifestURL in installedApps) { let app = installedApps[manifestURL]; let origin = null; let entryPoints = app.manifest.entry_points; if (entryPoints) { for (let ep in entryPoints) { let currentEntryPoint = entryPoints[ep]; let appName = currentEntryPoint.name; if (name == appName.toLowerCase()) { window.wrappedJSObject.navigator.mozApps.mgmt.uninstall(app); return true; } } } else { let appName = app.manifest.name; if (name == appName.toLowerCase()) { window.wrappedJSObject.navigator.mozApps.mgmt.uninstall(app); return true; } } } return false; }; return uninstallWithName("%s"); """ m.set_script_timeout(script_timeout) result = m.execute_script(uninstall_app % app_name.lower()) if result: m.set_context("chrome") Wait(m, 10).until(check_uninstall) uninstall = m.execute_script("return window.wrappedJSObject.uninstall;") m.set_context("content") if uninstall == 'ask': m.switch_to_frame() Wait(m, 20).until(element_displayed(m.find_element('css selector', 'gaia-confirm .confirm'))) Wait(m, 20).until(check_click_uninstall) if not marionette: m.delete_session()
class B2GDesktopReftest(RefTest): marionette = None def __init__(self, marionette_args): RefTest.__init__(self) self.last_test = os.path.basename(__file__) self.marionette_args = marionette_args self.profile = None self.runner = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.timeout = None def run_marionette_script(self): self.marionette = Marionette(**self.marionette_args) assert(self.marionette.wait_for_port()) self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) if os.path.isfile(self.test_script): f = open(self.test_script, 'r') self.test_script = f.read() f.close() self.marionette.execute_script(self.test_script) def run_tests(self, test_path, options): reftestlist = self.getManifestPath(test_path) if not reftestlist.startswith('file://'): reftestlist = 'file://%s' % reftestlist self.profile = self.create_profile(options, reftestlist, profile_to_clone=options.profile) env = self.buildBrowserEnv(options, self.profile.profile) kp_kwargs = { 'processOutputLine': [self._on_output], 'onTimeout': [self._on_timeout], 'kill_on_timeout': False } if not options.debugger: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 self.timeout = options.timeout + 30.0 log.info("%s | Running tests: start.", os.path.basename(__file__)) cmd, args = self.build_command_line(options.app, ignore_window_size=options.ignoreWindowSize, browser_arg=options.browser_arg) self.runner = FirefoxRunner(profile=self.profile, binary=cmd, cmdargs=args, env=env, process_class=ProcessHandler, process_args=kp_kwargs, symbols_path=options.symbolsPath) status = 0 try: self.runner.start(outputTimeout=self.timeout) log.info("%s | Application pid: %d", os.path.basename(__file__), self.runner.process_handler.pid) # kick starts the reftest harness self.run_marionette_script() status = self.runner.wait() finally: self.runner.check_for_crashes(test_name=self.last_test) self.runner.cleanup() if status > 0: log.testFail("%s | application terminated with exit code %s", self.last_test, status) elif status < 0: log.info("%s | application killed with signal %s", self.last_test, -status) log.info("%s | Running tests: end.", os.path.basename(__file__)) return status def create_profile(self, options, reftestlist, profile_to_clone=None): profile = RefTest.createReftestProfile(self, options, reftestlist, profile_to_clone=profile_to_clone) prefs = {} # Turn off the locale picker screen prefs["browser.firstrun.show.localepicker"] = False prefs["b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html" prefs["b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp" prefs["dom.ipc.tabs.disabled"] = False prefs["dom.mozBrowserFramesEnabled"] = True prefs["font.size.inflation.emPerLine"] = 0 prefs["font.size.inflation.minTwips"] = 0 prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org" prefs["reftest.browser.iframe.enabled"] = False prefs["reftest.remote"] = False prefs["reftest.uri"] = "%s" % reftestlist # Set a future policy version to avoid the telemetry prompt. prefs["toolkit.telemetry.prompted"] = 999 prefs["toolkit.telemetry.notifiedOptOut"] = 999 # Set the extra prefs. profile.set_preferences(prefs) return profile def build_command_line(self, app, ignore_window_size=False, browser_arg=None): cmd = os.path.abspath(app) args = ['-marionette'] if browser_arg: args += [browser_arg] if not ignore_window_size: args.extend(['--screen', '800x1000']) return cmd, args def _on_output(self, line): print(line) # TODO use structured logging if "TEST-START" in line and "|" in line: self.last_test = line.split("|")[1].strip() def _on_timeout(self): msg = "%s | application timed out after %s seconds with no output" log.testFail(msg % (self.last_test, self.timeout)) # kill process to get a stack self.runner.stop(sig=signal.SIGABRT)
def ftu(locale, device_flags, hashes): db = get_db(locale) app = 'ftu' app_categ = 'communications' app_type = 'apps' gaia_hash = hashes[0] hg_hash = hashes[1] screenshot_path = app_type + '/' + app_categ + '/' + app create_screenshot_dir(locale, screenshot_path) client = Marionette('localhost', 2828) client.start_session() change_screen_timeout(client) ftu_iframe = client.find_element( 'css selector', "iframe[src='app://communications.gaiamobile.org/ftu/index.html']") if client.switch_to_frame(ftu_iframe): time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#languages file_name = 'languages' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#activation-screen') # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 1st attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # invalid pin -> error about pin characters # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-pin_screen-invalid_pin' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (attempt 1 of 3) pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 2nd attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen-wrong_pin-1st_time' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (attempt 2 of 3) pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (unlock-sim-screen screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim pin screen - unlock-sim-screen - 3rd attempt) file_name = 'data_3g-unlock_sim_screen-pin_screen-wrong_pin-2nd_time' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong pin (atempt 3 of 3) - continues to puk screen pin_input = client.find_element('css selector', '#pin-input') pin_input.send_keys(wrong_sim_pin) # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # tap the header in order to hide keyboard and get full screenshot of the puk screen header = client.find_element('css selector', '#unlock-sim-header') header.tap() # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - 1st attempt) file_name = 'data_3g-unlock_sim_screen-puk_screen' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # invalid puk -> error about puk characters # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - puk error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_puk' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong puk - invalid new pin -> error about pin characters puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(wrong_sim_puk) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_new_pin' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert new pin without confirmed it -> error about pin confirmation (wrong puk is preserved at its input) newpin_input = client.find_element('css selector', '#newpin-input') newpin_input.send_keys(sim_pin) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - pin error) file_name = 'data_3g-unlock_sim_screen-puk_screen-invalid_new_pin_confirmation' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert confirmation of the new pin (wrong puk and new pin are preserved at their inputs) newpin_input = client.find_element('css selector', '#confirm-newpin-input') newpin_input.send_keys(sim_pin) # wrong puk (attempt 1 of 9) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - puk error) file_name = 'data_3g-unlock_sim_screen-puk_screen-wrong_puk-1st_time' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert wrong puk (attempt 2 of 9) (new pin and its confirmation are preserved at their inputs) puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(wrong_sim_puk) # stay at app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen) next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g (sim puk screen - unlock-sim-screen - 2nd attempt) file_name = 'data_3g-unlock_sim_screen-puk_screen-wrong_puk-2nd_time' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#unlock-sim-screen') # insert right puk (attempt 2 of 9) (new pin and its confirmation are preserved at their inputs) puk_input = client.find_element('css selector', '#puk-input') puk_input.send_keys(sim_puk) # switch to app://communications.gaiamobile.org/ftu/index.html#data_3g next = client.find_element('css selector', '#unlock-sim-button') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#data_3g file_name = 'data_3g' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {"sim": True, "no-sim": False} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#activation-screen') # switch to app://communications.gaiamobile.org/ftu/index.html#wifi next = client.find_element('css selector', '#forward') next.tap() time.sleep(long_time) # enable overlay "scanningNetworks" spinner screen (loading-overlay) client.execute_script( "window.wrappedJSObject.utils.overlay.show('scanningNetworks', 'spinner');" ) time.sleep(middle_time) # overlay "scanningNetworks" spinner screen file_name = 'wifi-loading_overlay-scanning_networks' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#loading-overlay') # disable overlay "scanningNetworks" spinner screen client.execute_script( "window.wrappedJSObject.utils.overlay.hide('scanningNetworks', 'spinner');" ) time.sleep(middle_time) # app://communications.gaiamobile.org/ftu/index.html#wifi file_name = 'wifi' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#wifi') # switch to app://communications.gaiamobile.org/ftu/index.html#date_and_time next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # Set time in order to be the same at every screenshot set_time(client) # app://communications.gaiamobile.org/ftu/index.html#date_and_time file_name = 'date_and_time' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#date_and_time') # switch to app://communications.gaiamobile.org/ftu/index.html#geolocation next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#geolocation file_name = 'geolocation' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#geolocation') # switch to app://communications.gaiamobile.org/ftu/index.html#import_contacts next = client.find_element('css selector', '#forward') next.tap() time.sleep(middle_time) # app://communications.gaiamobile.org/ftu/index.html#import_contacts file_name = 'import_contacts' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry, '#import_contacts') # switch to app://communications.gaiamobile.org/ftu/index.html#welcome_browser next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#welcome_browser file_name = 'welcome_browser' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#browser_privacy next = client.find_element('css selector', '#forward') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#browser_privacy file_name = 'browser_privacy' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step1 (first tutorial screen) next = client.find_element('css selector', '#forward') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step1 (first tutorial screen) file_name = 'step1-first_tutorial_screen' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step1 next = client.find_element('css selector', '#lets-go-button') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step1 file_name = 'step1' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step2 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step2 file_name = 'step2' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step3 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step3 file_name = 'step3' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step4 next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(short_time) # app://communications.gaiamobile.org/ftu/index.html#step4 file_name = 'step4' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # switch to app://communications.gaiamobile.org/ftu/index.html#step4 (last tutorial screen) next = client.find_element('css selector', '#forwardTutorial') next.tap() time.sleep(long_time) # app://communications.gaiamobile.org/ftu/index.html#step4 (last tutorial screen) file_name = 'step4-last_tutorial_screen' screenshot_entry = { "id": file_name, "script": app, "gaia": gaia_hash, "hg": hg_hash, "changed": False, "path": screenshot_path } test_flags = {} if check_flags(test_flags, device_flags): screenshot_entry.update(test_flags) if take_screenshot(locale, screenshot_path, client, file_name): screenshot_entry['changed'] = True log_displayed_l10n_strings(client, db, app, app_categ, screenshot_entry) # Close ftu frame next = client.find_element('css selector', '#tutorialFinished') next.tap() time.sleep(short_time) set_db(locale, db) else: #TO DO raise proper error print "switch to ftu frame failed"
class B2GManager(object): def __init__(self, dm, tmpdir=None, userJS=None, marionette_host=None, marionette_port=None): self.dm = dm self.tmpdir = tmpdir self.userJS = userJS or "/data/local/user.js" self.marionette_host = marionette_host or 'localhost' self.marionette_port = marionette_port or 2828 self.marionette = None #timeout in seconds def wait_for_port(self, timeout): print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionette_port, self.marionette_host) sock.connect((self.marionette_host, self.marionette_port)) data = sock.recv(16) sock.close() if '"from"' in data: print "got it" return True except: import traceback print traceback.format_exc() time.sleep(1) return False def get_marionette(self): self.marionette = Marionette(self.marionette_host, self.marionette_port) def restart_b2g(self): #restart b2g so we start with a clean slate self.dm.checkCmd(['shell', 'stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. time.sleep(10) self.dm.checkCmd(['shell', 'start', 'b2g']) #wait for marionette port to come up print "connect to marionette" if not self.wait_for_port(30): raise Exception("Could not communicate with Marionette port after restarting B2G") self.get_marionette() def set_tmpdir(self, tmpdir): self.tmpdir = tmpdir def setup_profile(self, prefs): if not self.tmpdir: raise Exception("You must set the tmpdir") #remove previous user.js if there is one our_user_js = os.path.join(self.tmpdir, "user.js") if os.path.exists(our_user_js): os.remove(our_user_js) #copy profile try: self.dm.checkCmd(["pull", self.userJS, our_user_js]) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_user_js): self.dm.checkCmd(['shell', 'dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) print "opening userjs" user_js = open(our_user_js, 'a') print "Writing: %s" % prefs user_js.write("%s" % prefs) print "closing" user_js.close() self.dm.checkCmd(['push', our_user_js, self.userJS]) self.restart_b2g() def forward_port(self): self.dm.checkCmd(['forward', 'tcp:%s' % self.marionette_port, 'tcp:%s' % self.marionette_port]) def setup_ethernet(self): #TODO: need to add timeout tries = 3 while tries > 0: print "on try: %d" % tries output = StringIO.StringIO() self.dm.shell(['ifconfig', 'eth0'], output) print "we get back %s" % output.getvalue() if "ip" in output.getvalue(): return output.close() try: self.dm.checkCmd(['shell', 'netcfg', 'eth0', 'dhcp'], timeout=10) except DMError: pass tries = tries - 1 raise DMError("Could not set up ethernet connection") def restore_profile(self): if not self.tmpdir: raise Exception("You must set the tmpdir") #if we successfully copied the profile, make a backup of the file our_user_js = os.path.join(self.tmpdir, "user.js") if os.path.exists(our_user_js): self.dm.checkCmd(['shell', 'dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) def get_appinfo(self): if not self.marionette: self.forward_port() self.wait_for_port(30) self.get_marionette() self.marionette.start_session() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
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')
class MatsRunner(object): def __init__(self, config_file = 'config.ini', url = 'about:blank'): self.config_file = config_file self.url = url def start(self): ''' This method starts MATS. ''' print 'Using ' + MatsController.__name__ + ' as controller.' print 'Loading config from "' + self.config_file + '"...', self.config = get_config(self.config_file) #get_config makes sure that the config makes sense. More details in get_config.py self.marionette_port = self.config['Marionette']['port'] print 'OK' print 'Starting Firefox/Nightly from "' + self.config['Firefox']['binary'] + '" with Marionette on port ' + str(self.marionette_port) + '.' self.FirefoxThread = FirefoxThread(self.config['Firefox']['binary'], self.marionette_port) self.FirefoxThread.start() print 'Creating controller' pid = self.FirefoxThread.getPID() # this function blocks until PID is available from FirefoxThread self.controller = MatsController(pid) print 'Starting controller' self.controller.start() self.controller.wait_for_ready() print 'Waiting for Marionette port to open (' + str(self.marionette_port) + ')' portReady = self.FirefoxThread.waitForMarionettePortOpenReady(self.config['Marionette']['port_timeout']) if portReady: print 'Marionette port open' else: print 'Error: timeout, shutting down MATS' self.controller.stop() self.FirefoxThread.stop() return #TODO: remove line below once https://bugzilla.mozilla.org/show_bug.cgi?id=753273 is fixed #sleep(10) try: print 'Starting Marionette' self.marionette = Marionette('localhost', self.marionette_port) #TODO: move starting session and navigation to separate methods print 'Starting session' sleep(5) #TODO temporary workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=757078 self.marionette_session = self.marionette.start_session() print 'Navigating to ' + self.url print self.marionette.navigate(self.url) except Exception as e: print 'Error starting Marionette' fall(e) self.controller.stop() self.FirefoxThread.stop() print 'MATS up and running. Waiting until Firefox/Nightly stops.' def wait_for_stop(self): self.FirefoxThread.join() print 'Stopping controller' self.controller.stop() self.controller.join() print 'MATS runner finishes.' def stop(self): self.FirefoxThread.stop() self.FirefoxThread.join() print 'Stopping controller' self.controller.stop() self.controller.join() print 'MATS runner finishes.' def instantiate_a11y(self): ''' runs via marionette script taken from http://dxr.lanedo.com/search.cgi?tree=mozilla-central&string=nsIAccessibleApplication to force A11y engine instantiation. sets context to content after doing it's stuff ''' script = \ ''' const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval; const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication; var gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"]. getService(nsIAccessibleRetrieval); app = gAccRetrieval.getApplicationAccessible(). QueryInterface(nsIAccessibleApplication); return (app) ? true : false; ''' self.marionette.set_context("chrome") notNull = self.marionette.execute_script(script) self.marionette.set_context("content") return notNull def is_a11y_instantiated(self): ''' runs via marionette script that checks if A11y engine is instantiated. and returns true if so. sets context to content after doing it's stuff ''' script = \ ''' var enabled; enabled = Components.manager.QueryInterface(Ci.nsIServiceManager) .isServiceInstantiatedByContractID( "@mozilla.org/accessibilityService;1", Ci.nsISupports); return enabled; ''' self.marionette.set_context('chrome') result = self.marionette.execute_script(script) self.marionette.set_context('content') return result def wait_for_event(self, event_string, callable, timeout = 60): ''' this method is the easiest interface to wait for an event. First, it registers listener. Second, it calls callable (TODO: add arguments) Third, it waits for the event or timeouts it returns True if the event was captured, and False if timeout occured TODO: abstract it to cross-platform ''' arrived = threading.Event() def callback(event): print 'got event! ' + str(event) arrived.set() self.controller.register_event_listener(event_string, callback) self.controller.unpause_event_loop() callable() result = arrived.wait(timeout) self.controller.pause_event_loop() self.controller.deregister_event_listener(event_string, callback) return result
class GCli(object): def __init__(self): self.commands = { 'connectwifi': { 'function': self.connect_to_wifi, 'args': [ {'name': 'ssid', 'help': 'SSID of the network to connect to'}, {'name': '--security', 'choices': ['WPA-PSK', 'WEP'], 'help': 'Security model of the network'}, {'name': '--password', 'help': 'Password to access the network'}], 'help': 'Connect to a WiFi network'}, 'disablewifi': { 'function': self.disable_wifi, 'help': 'Disable WiFi'}, 'enablewifi': { 'function': self.enable_wifi, 'help': 'Enable WiFi'}, 'forgetallwifi': { 'function': self.forget_all_wifi_networks, 'help': 'Forget all WiFi networks'}, 'getknownwifi': { 'function': self.known_wifi_networks, 'help': 'Show known WiFi networks'}, 'getsetting': { 'function': self.get_setting, 'args': [{ 'name': 'name', 'help': 'Name of the setting to retrieve'}], 'help': 'Show the current value of a setting'}, 'holdhome': { 'function': self.hold_home, 'help': 'Simulate holding the home button'}, 'holdsleep': { 'function': self.hold_sleep, 'help': 'Simulate holding the sleep button'}, 'home': { 'function': self.home, 'help': 'Simulate pressing the home button'}, 'killapps': { 'function': self.kill_all_apps, 'help': 'Kill all running apps'}, 'launchapp': { 'function': self.launch_app, 'args': [ {'name': 'name', 'nargs': argparse.REMAINDER, 'help': 'Name of app to launch'}], 'help': 'Launch an application'}, 'listallapps': { 'function': self.list_all_apps, 'help': 'List all apps'}, 'listrunningapps': { 'function': self.list_running_apps, 'help': 'List the running apps'}, 'lock': { 'function': self.lock, 'help': 'Lock screen'}, 'screenshot': { 'function': self.screenshot, 'help': 'Take a screenshot'}, 'sendsms': { 'function': self.send_sms, 'args': [ {'name': 'number', 'help': 'Phone number of recipient'}, {'name': 'message', 'help': 'Message content'}], 'help': 'Send an SMS'}, 'setsetting': { 'function': self.set_setting, 'args': [ {'name': 'name', 'help': 'Name of setting to change'}, {'name': 'value', 'help': 'New value for setting'}], 'help': 'Change the value of a setting'}, 'sleep': { 'function': self.sleep, 'help': 'Enter sleep mode'}, 'unlock': { 'function': self.unlock, 'help': 'Unlock screen'}, 'volume': { 'function': self.volume, 'args': [ {'name': 'direction', 'choices': ['down', 'up'], 'help': 'Direction to change the volume'}], 'help': 'Change the volume'}, 'wake': { 'function': self.wake, 'help': 'Wake from sleep mode'}} self.parser = argparse.ArgumentParser() self.add_options(self.parser) self.add_commands(self.parser) def run(self, args=sys.argv[1:]): args = self.parser.parse_args() host, port = args.address.split(':') self.marionette = Marionette(host=host, port=int(port)) self.marionette.start_session() self.apps = gaiatest.GaiaApps(self.marionette) self.data_layer = gaiatest.GaiaData(self.marionette) self.lock_screen = gaiatest.LockScreen(self.marionette) ret = args.func(args) if ret is None: ret = 0 self.marionette.delete_session() sys.exit(ret) def add_options(self, parser): parser.add_argument( '--address', default='localhost:2828', help='Address (host:port) of running Gecko instance to connect to ' '(default: %(default)s)') def add_commands(self, parser): subparsers = parser.add_subparsers( title='Commands', metavar='<command>') for (name, props) in sorted(self.commands.iteritems()): subparser = subparsers.add_parser(name, help=props['help']) if props.get('args'): for arg in props['args']: kwargs = {k: v for k, v in arg.items() if k is not 'name'} subparser.add_argument(arg['name'], **kwargs) subparser.set_defaults(func=props['function']) def connect_to_wifi(self, args): network = { 'ssid': args.ssid, 'keyManagement': args.security or 'NONE'} if args.security == 'WEP': network['wep'] = args.password elif args.security == 'WPA-PSK': network['psk'] = args.password self.data_layer.connect_to_wifi(network) def disable_wifi(self, args): self.data_layer.disable_wifi() def enable_wifi(self, args): self.data_layer.enable_wifi() def forget_all_wifi_networks(self, args): self.data_layer.forget_all_networks() def get_setting(self, args): print '%s: %s' % ( args.name, self.data_layer.get_setting(args.name)) def home(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('home'));") def hold_home(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('holdhome'));") def hold_sleep(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('holdsleep'));") def kill_all_apps(self, args): self.apps.kill_all() def known_wifi_networks(self, args): networks = [n for n in self.data_layer.known_networks if 'ssid' in n] if len(networks) > 0: for i, network in enumerate(networks): print '%s: %s' % (i + 1, network['ssid']) else: print 'No known networks.' def launch_app(self, args): for name in args.name: self.apps.launch(name) def list_all_apps(self, args): for i, app in enumerate(sorted(self.apps.installed_apps, key=lambda a: a.name.lower())): print '%d: %s' % (i + 1, app.name) def list_running_apps(self, args): for i, app in enumerate(sorted(self.apps.running_apps, key=lambda a: a.name.lower())): print '%d: %s' % (i + 1, app.name) def lock(self, args): self.lock_screen.lock() def screenshot(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('home+sleep'));") def send_sms(self, args): self.data_layer.send_sms(args.number, args.message) def set_setting(self, args): self.data_layer.set_setting(args.name, args.value) def sleep(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('sleep'));") def unlock(self, args): self.lock_screen.unlock() def volume(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('volume%s'));" % args.direction) def wake(self, args): self.marionette.execute_script( "window.wrappedJSObject.dispatchEvent(new Event('wake'));")
class B2GMixin(object): profileDir = None userJS = "/data/local/user.js" marionette = None def __init__(self, host=None, marionetteHost=None, marionettePort=2828, **kwargs): # (allowing marionneteHost to be specified seems a bit # counter-intuitive since we normally get it below from the ip # address, however we currently need it to be able to connect # via adb port forwarding and localhost) if marionetteHost: self.marionetteHost = marionetteHost elif host: self.marionetteHost = host self.marionettePort = marionettePort def cleanup(self): if self.profileDir: self.restoreProfile() def waitForPort(self, timeout): """ Wait for the marionette server to respond. Timeout parameter is in seconds """ print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionettePort, self.marionetteHost) sock.connect((self.marionetteHost, self.marionettePort)) data = sock.recv(16) sock.close() if '"from"' in data: return True except socket.error: pass except Exception as e: raise DMError("Could not connect to marionette: %s" % e) time.sleep(1) raise DMError("Could not communicate with Marionette port") def setupMarionette(self): """ Start a marionette session. If no host is given, then this will get the ip of the device, and set up networking if needed. """ if not self.marionetteHost: self.setupDHCP() self.marionetteHost = self.getIP() if not self.marionette: self.marionette = Marionette(self.marionetteHost, self.marionettePort) if not self.marionette.session: self.waitForPort(30) self.marionette.start_session() def restartB2G(self): """ Restarts the b2g process on the device """ #restart b2g so we start with a clean slate if self.marionette and self.marionette.session: self.marionette.delete_session() self.shellCheckOutput(['stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. tries = 10 while "b2g" in self.shellCheckOutput(['ps', 'b2g']) and tries > 0: tries -= 1 time.sleep(1) if tries == 0: raise DMError("Could not kill b2g process") self.shellCheckOutput(['start', 'b2g']) def setupProfile(self, prefs=None): """ Sets up the user profile on the device, The 'prefs' is a string of user_prefs to add to the profile. If it is not set, it will default to a standard b2g testing profile. """ if not prefs: prefs = """ user_pref("power.screen.timeout", 999999); user_pref("devtools.debugger.force-local", false); """ #remove previous user.js if there is one if not self.profileDir: self.profileDir = tempfile.mkdtemp() our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): os.remove(our_userJS) #copy profile try: output = self.getFile(self.userJS, our_userJS) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) with open(our_userJS, 'a') as user_file: user_file.write("%s" % prefs) self.pushFile(our_userJS, self.userJS) self.restartB2G() self.setupMarionette() def setupDHCP(self, conn_type='eth0'): """ Sets up networking. If conn_type is not set, it will assume eth0. """ tries = 5 while tries > 0: print "attempts left: %d" % tries try: self.shellCheckOutput(['netcfg', conn_type, 'dhcp'], timeout=10) if self.getIP(): return except DMError: pass tries = tries - 1 raise DMError("Could not set up network connection") def restoreProfile(self): """ Restores the original profile """ if not self.profileDir: raise DMError("There is no profile to restore") #if we successfully copied the profile, make a backup of the file our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): self.shellCheckOutput(['dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) shutil.rmtree(self.profileDir) self.profileDir = None def getAppInfo(self): """ Returns the appinfo, with an additional "date" key. """ if not self.marionette or not self.marionette.session: self.setupMarionette() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
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')
class B2GMochitest(MochitestUtilsMixin): marionette = None def __init__(self, marionette_args, out_of_process=True, profile_data_dir=None, locations=os.path.join(here, 'server-locations.txt')): super(B2GMochitest, self).__init__() self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' # structured logging self.message_logger = MessageLogger(logger=log) if profile_data_dir: self.preferences = [os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref')] self.webapps = [os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp')] # mozinfo is populated by the parent class if mozinfo.info['debug']: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 def setup_common_options(self, options): test_url = self.buildTestPath(options) # For B2G emulators buildURLOptions has been called # without calling buildTestPath first and that # causes manifestFile not to be set if not "manifestFile=tests.json" in self.urlOpts: self.urlOpts.append("manifestFile=%s" % options.manifestFile) if len(self.urlOpts) > 0: test_url += "?" + "&".join(self.urlOpts) self.test_script_args.append(test_url) def buildTestPath(self, options, testsToFilter=None): if options.manifestFile != 'tests.json': super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False) return self.buildTestURL(options) def build_profile(self, options): # preferences prefs = {} for path in self.preferences: prefs.update(Preferences.read_prefs(path)) for v in options.extraPrefs: thispref = v.split("=", 1) if len(thispref) < 2: print "Error: syntax error in --setpref=" + v sys.exit(1) prefs[thispref[0]] = thispref[1] # interpolate the preferences interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort), "OOP": "true" if self.out_of_process else "false" } prefs = json.loads(json.dumps(prefs) % interpolation) for pref in prefs: prefs[pref] = Preferences.cast(prefs[pref]) kwargs = { 'addons': self.getExtensionsToInstall(options), 'apps': self.webapps, 'locations': self.locations_file, 'preferences': prefs, 'proxy': {"remote": options.webServer} } if options.profile: self.profile = Profile.clone(options.profile, **kwargs) else: self.profile = Profile(**kwargs) options.profilePath = self.profile.profile # TODO bug 839108 - mozprofile should probably handle this manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest def run_tests(self, options): """ Prepare, configure, run tests and cleanup """ manifest = self.build_profile(options) self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log") # configuring the message logger's buffering self.message_logger.buffering = options.quiet if options.debugger or not options.autorun: timeout = None else: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 timeout = options.timeout + 30.0 log.info("runtestsb2g.py | Running tests: start.") status = 0 try: def on_output(line): messages = self.message_logger.write(line) for message in messages: if message['action'] == 'test_start': self.runner.last_test = message['test'] # The logging will be handled by on_output, so we set the stream to None process_args = {'processOutputLine': on_output, 'stream': None} self.marionette_args['process_args'] = process_args self.marionette_args['profile'] = self.profile self.marionette = Marionette(**self.marionette_args) self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists(posixpath.dirname(self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) self.startServers(options, None) self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) # Disable offline status management (bug 777145), otherwise the network # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script(script.read(), script_args=self.test_script_args) else: self.marionette.execute_script(self.test_script, script_args=self.test_script_args) status = self.runner.wait() if status is None: # the runner has timed out status = 124 except KeyboardInterrupt: log.info("runtests.py | Received keyboard interrupt.\n"); status = -1 except: traceback.print_exc() log.error("Automation Error: Received unexpected exception while running application\n") if hasattr(self, 'runner'): self.runner.check_for_crashes() status = 1 self.stopServers() log.info("runtestsb2g.py | Running tests: end.") if manifest is not None: self.cleanup(manifest, options) return status def getGMPPluginPath(self, options): # TODO: bug 1043403 return None
def run(self, script, address='localhost:2828', symbols=None, treeherder='https://treeherder.mozilla.org/', reset=False, **kwargs): try: host, port = address.split(':') except ValueError: raise ValueError('--address must be in the format host:port') # Check that Orangutan is installed self.adb_device = ADBDevice(self.device_serial) orng_path = posixpath.join('data', 'local', 'orng') if not self.adb_device.exists(orng_path): raise Exception('Orangutan not found! Please install it according ' 'to the documentation.') self.runner = B2GDeviceRunner(serial=self.device_serial, process_args={'stream': None}, symbols_path=symbols, logdir=self.temp_dir) if reset: self.runner.start() else: self.runner.device.connect() port = self.runner.device.setup_port_forwarding(remote_port=port) assert self.runner.device.wait_for_port(port), \ 'Timed out waiting for port!' marionette = Marionette(host=host, port=port) marionette.start_session() try: marionette.set_context(marionette.CONTEXT_CHROME) self.is_debug = marionette.execute_script( 'return Components.classes["@mozilla.org/xpcom/debug;1"].' 'getService(Components.interfaces.nsIDebug2).isDebugBuild;') marionette.set_context(marionette.CONTEXT_CONTENT) if reset: gaia_device = GaiaDevice(marionette) gaia_device.wait_for_b2g_ready(timeout=120) gaia_device.unlock() gaia_apps = GaiaApps(marionette) gaia_apps.kill_all() # TODO: Disable bluetooth, emergency calls, carrier, etc # Run Orangutan script remote_script = posixpath.join(self.adb_device.test_root, 'orng.script') self.adb_device.push(script, remote_script) self.start_time = time.time() # TODO: Kill remote process on keyboard interrupt self.adb_device.shell( '%s %s %s' % (orng_path, self.device_properties['input'], remote_script)) self.end_time = time.time() self.adb_device.rm(remote_script) except (MarionetteException, IOError): if self.runner.crashed: # Crash has been detected pass else: raise self.runner.check_for_crashes(test_name='b2gmonkey') # Report results to Treeherder required_envs = ['TREEHERDER_KEY', 'TREEHERDER_SECRET'] if all([os.environ.get(v) for v in required_envs]): self.post_to_treeherder(script, treeherder) else: self._logger.info( 'Results will not be posted to Treeherder. Please set the ' 'following environment variables to enable Treeherder ' 'reports: %s' % ', '.join([v for v in required_envs if not os.environ.get(v)]))
class B2GDesktopReftest(RefTest): marionette = None def __init__(self, marionette_args): RefTest.__init__(self) self.last_test = os.path.basename(__file__) self.marionette_args = marionette_args self.profile = None self.runner = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.timeout = None def run_marionette_script(self): self.marionette = Marionette(**self.marionette_args) assert (self.marionette.wait_for_port()) self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) if os.path.isfile(self.test_script): f = open(self.test_script, 'r') self.test_script = f.read() f.close() self.marionette.execute_script(self.test_script) def run_tests(self, test_path, options): reftestlist = self.getManifestPath(test_path) if not reftestlist.startswith('file://'): reftestlist = 'file://%s' % reftestlist self.profile = self.create_profile(options, reftestlist, profile_to_clone=options.profile) env = self.buildBrowserEnv(options, self.profile.profile) kp_kwargs = { 'processOutputLine': [self._on_output], 'onTimeout': [self._on_timeout], 'kill_on_timeout': False } if not options.debugger: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 self.timeout = options.timeout + 30.0 log.info("%s | Running tests: start.", os.path.basename(__file__)) cmd, args = self.build_command_line( options.app, ignore_window_size=options.ignoreWindowSize, browser_arg=options.browser_arg) self.runner = FirefoxRunner(profile=self.profile, binary=cmd, cmdargs=args, env=env, process_class=ProcessHandler, process_args=kp_kwargs, symbols_path=options.symbolsPath) status = 0 try: self.runner.start(outputTimeout=self.timeout) log.info("%s | Application pid: %d", os.path.basename(__file__), self.runner.process_handler.pid) # kick starts the reftest harness self.run_marionette_script() status = self.runner.wait() finally: self.runner.check_for_crashes(test_name=self.last_test) self.runner.cleanup() if status > 0: log.testFail("%s | application terminated with exit code %s", self.last_test, status) elif status < 0: log.info("%s | application killed with signal %s", self.last_test, -status) log.info("%s | Running tests: end.", os.path.basename(__file__)) return status def create_profile(self, options, reftestlist, profile_to_clone=None): profile = RefTest.createReftestProfile( self, options, reftestlist, profile_to_clone=profile_to_clone) prefs = {} # Turn off the locale picker screen prefs["browser.firstrun.show.localepicker"] = False prefs[ "b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html" prefs[ "b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp" prefs["browser.tabs.remote"] = False prefs["dom.ipc.tabs.disabled"] = False prefs["dom.mozBrowserFramesEnabled"] = True prefs["font.size.inflation.emPerLine"] = 0 prefs["font.size.inflation.minTwips"] = 0 prefs[ "network.dns.localDomains"] = "app://test-container.gaiamobile.org" prefs["reftest.browser.iframe.enabled"] = False prefs["reftest.remote"] = False prefs["reftest.uri"] = "%s" % reftestlist # Set a future policy version to avoid the telemetry prompt. prefs["toolkit.telemetry.prompted"] = 999 prefs["toolkit.telemetry.notifiedOptOut"] = 999 # Set the extra prefs. profile.set_preferences(prefs) return profile def build_command_line(self, app, ignore_window_size=False, browser_arg=None): cmd = os.path.abspath(app) args = ['-marionette'] if browser_arg: args += [browser_arg] if not ignore_window_size: args.extend(['--screen', '800x1000']) return cmd, args def _on_output(self, line): print(line) # TODO use structured logging if "TEST-START" in line and "|" in line: self.last_test = line.split("|")[1].strip() def _on_timeout(self): msg = "%s | application timed out after %s seconds with no output" log.testFail(msg % (self.last_test, self.timeout)) # kill process to get a stack self.runner.stop(sig=signal.SIGABRT)
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 B2GMochitest(MochitestUtilsMixin): marionette = None def __init__(self, marionette_args, logger_options, out_of_process=True, profile_data_dir=None, locations=os.path.join(here, 'server-locations.txt')): super(B2GMochitest, self).__init__(logger_options) self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' self.remote_chrome_test_dir = None if profile_data_dir: self.preferences = [ os.path.join( profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref')] self.webapps = [ os.path.join( profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp')] # mozinfo is populated by the parent class if mozinfo.info['debug']: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 def setup_common_options(self, options): test_url = self.buildTestPath(options) # For B2G emulators buildURLOptions has been called # without calling buildTestPath first and that # causes manifestFile not to be set if not "manifestFile=tests.json" in self.urlOpts: self.urlOpts.append("manifestFile=%s" % options.manifestFile) if len(self.urlOpts) > 0: test_url += "?" + "&".join(self.urlOpts) self.test_script_args.append(test_url) def buildTestPath(self, options, testsToFilter=None): if options.manifestFile != 'tests.json': super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False) return self.buildTestURL(options) def build_profile(self, options): # preferences prefs = {} for path in self.preferences: prefs.update(Preferences.read_prefs(path)) for v in options.extraPrefs: thispref = v.split("=", 1) if len(thispref) < 2: print "Error: syntax error in --setpref=" + v sys.exit(1) prefs[thispref[0]] = thispref[1] # interpolate the preferences interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort), "OOP": "true" if self.out_of_process else "false"} prefs = json.loads(json.dumps(prefs) % interpolation) for pref in prefs: prefs[pref] = Preferences.cast(prefs[pref]) kwargs = { 'addons': self.getExtensionsToInstall(options), 'apps': self.webapps, 'locations': self.locations_file, 'preferences': prefs, 'proxy': {"remote": options.webServer} } if options.profile: self.profile = Profile.clone(options.profile, **kwargs) else: self.profile = Profile(**kwargs) options.profilePath = self.profile.profile # TODO bug 839108 - mozprofile should probably handle this manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest def run_tests(self, options): """ Prepare, configure, run tests and cleanup """ self.setTestRoot(options) manifest = self.build_profile(options) self.logPreamble(self.getActiveTests(options)) # configuring the message logger's buffering self.message_logger.buffering = options.quiet if options.debugger or not options.autorun: timeout = None else: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 timeout = options.timeout + 30.0 self.log.info("runtestsb2g.py | Running tests: start.") status = 0 try: def on_output(line): messages = self.message_logger.write(line) for message in messages: if message['action'] == 'test_start': self.runner.last_test = message['test'] # The logging will be handled by on_output, so we set the stream to # None process_args = {'processOutputLine': on_output, 'stream': None} self.marionette_args['process_args'] = process_args self.marionette_args['profile'] = self.profile self.marionette = Marionette(**self.marionette_args) self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists( posixpath.dirname( self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) if options.chrome: # Update chrome manifest file in profile with correct path. self.writeChromeManifest(options) self.leak_report_file = posixpath.join( self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log') # We don't want to copy the host env onto the device, so pass in an # empty env. self.browserEnv = self.buildBrowserEnv(options, env={}) # B2G emulator debug tests still make external connections, so don't # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug # 1039019). if mozinfo.info[ 'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv: del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] self.runner.env.update(self.browserEnv) # Despite our efforts to clean up servers started by this script, in practice # we still see infrequent cases where a process is orphaned and interferes # with future tests, typically because the old server is keeping the port in use. # Try to avoid those failures by checking for and killing orphan servers before # trying to start new ones. self.killNamedOrphans('ssltunnel') self.killNamedOrphans('xpcshell') self.startServers(options, None) # In desktop mochitests buildTestPath is called before buildURLOptions. This # means options.manifestFile has already been converted to the proper json # style manifest. Not so with B2G, that conversion along with updating the URL # option will happen later. So backup and restore options.manifestFile to # prevent us from trying to pass in an instance of TestManifest via url param. manifestFile = options.manifestFile options.manifestFile = None self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) options.manifestFile = manifestFile self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.test_script_args.append(options.chrome) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) # Disable offline status management (bug 777145), otherwise the network # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for # emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) self.marionette.execute_script(""" let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; Services.prefs.setBoolPref(SECURITY_PREF, true); if (!testUtils.hasOwnProperty("specialPowersObserver")) { let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] .getService(Components.interfaces.mozIJSSubScriptLoader); loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js", testUtils); testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver(); testUtils.specialPowersObserver.init(); testUtils.specialPowersObserver._loadFrameScript(); } """) if options.chrome: self.app_ctx.dm.removeDir(self.remote_chrome_test_dir) self.app_ctx.dm.mkDir(self.remote_chrome_test_dir) local = super(B2GMochitest, self).getChromeTestDir(options) local = os.path.join(local, "chrome") remote = self.remote_chrome_test_dir self.log.info( "pushing %s to %s on device..." % (local, remote)) self.app_ctx.dm.pushDir(local, remote) if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script( script.read(), script_args=self.test_script_args) else: self.marionette.execute_script( self.test_script, script_args=self.test_script_args) status = self.runner.wait() if status is None: # the runner has timed out status = 124 local_leak_file = tempfile.NamedTemporaryFile() self.app_ctx.dm.getFile( self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) mozleak.process_leak_log( local_leak_file.name, leak_thresholds=options.leakThresholds, ignore_missing_leaks=options.ignoreMissingLeaks, log=self.log, stack_fixer=get_stack_fixer_function(options.utilityPath, options.symbolsPath), ) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n") status = -1 except: traceback.print_exc() self.log.error( "Automation Error: Received unexpected exception while running application\n") if hasattr(self, 'runner'): self.runner.check_for_crashes() status = 1 self.stopServers() self.log.info("runtestsb2g.py | Running tests: end.") if manifest is not None: self.cleanup(manifest, options) return status def getGMPPluginPath(self, options): if options.gmp_path: return options.gmp_path return '/system/b2g/gmp-clearkey/0.1' def getChromeTestDir(self, options): # The chrome test directory returned here is the remote location # of chrome test files. A reference to this directory is requested # when building the profile locally, before self.app_ctx is defined. # To get around this, return a dummy directory until self.app_ctx # is defined; the correct directory will be returned later, over- # writing the dummy. if hasattr(self, 'app_ctx'): self.remote_chrome_test_dir = posixpath.join( self.app_ctx.remote_test_root, 'chrome') return self.remote_chrome_test_dir return 'dummy-chrome-test-dir'
class B2GMixin(object): profileDir = None userJS = "/data/local/user.js" marionette = None def __init__(self, host=None, marionetteHost=None, marionettePort=2828, **kwargs): # (allowing marionneteHost to be specified seems a bit # counter-intuitive since we normally get it below from the ip # address, however we currently need it to be able to connect # via adb port forwarding and localhost) if marionetteHost: self.marionetteHost = marionetteHost elif host: self.marionetteHost = host self.marionettePort = marionettePort def cleanup(self): """ If a user profile was setup on the device, restore it to the original. """ if self.profileDir: self.restoreProfile() def waitForPort(self, timeout): """Waits for the marionette server to respond, until the timeout specified. :param timeout: Timeout parameter in seconds. """ print "waiting for port" starttime = datetime.datetime.now() while datetime.datetime.now() - starttime < datetime.timedelta( seconds=timeout): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "trying %s %s" % (self.marionettePort, self.marionetteHost) sock.connect((self.marionetteHost, self.marionettePort)) data = sock.recv(16) sock.close() if '"from"' in data: return True except socket.error: pass except Exception as e: raise DMError("Could not connect to marionette: %s" % e) time.sleep(1) raise DMError("Could not communicate with Marionette port") def setupMarionette(self, scriptTimeout=60000): """ Starts a marionette session. If no host was given at init, the ip of the device will be retrieved and networking will be established. """ if not self.marionetteHost: self.setupDHCP() self.marionetteHost = self.getIP() if not self.marionette: self.marionette = Marionette(self.marionetteHost, self.marionettePort) if not self.marionette.session: self.waitForPort(30) self.marionette.start_session() self.marionette.set_script_timeout(scriptTimeout) def restartB2G(self): """ Restarts the b2g process on the device. """ #restart b2g so we start with a clean slate if self.marionette and self.marionette.session: self.marionette.delete_session() self.shellCheckOutput(['stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. tries = 10 while "b2g" in self.shellCheckOutput(['ps', 'b2g']) and tries > 0: tries -= 1 time.sleep(1) if tries == 0: raise DMError("Could not kill b2g process") self.shellCheckOutput(['start', 'b2g']) def setupProfile(self, prefs=None): """Sets up the user profile on the device. :param prefs: String of user_prefs to add to the profile. Defaults to a standard b2g testing profile. """ # currently we have no custom prefs to set (when bug 800138 is fixed, # we will probably want to enable marionette on an external ip by # default) if not prefs: prefs = "" #remove previous user.js if there is one if not self.profileDir: self.profileDir = tempfile.mkdtemp() our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): os.remove(our_userJS) #copy profile try: self.getFile(self.userJS, our_userJS) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_userJS): self.shellCheckOutput( ['dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) with open(our_userJS, 'a') as user_file: user_file.write("%s" % prefs) self.pushFile(our_userJS, self.userJS) self.restartB2G() self.setupMarionette() def setupDHCP(self, interfaces=['eth0', 'wlan0']): """Sets up networking. :param interfaces: Network connection types to try. Defaults to eth0 and wlan0. """ all_interfaces = [line.split()[0] for line in \ self.shellCheckOutput(['netcfg']).splitlines()[1:]] interfaces_to_try = filter(lambda i: i in interfaces, all_interfaces) tries = 5 print "Setting up DHCP..." while tries > 0: print "attempts left: %d" % tries try: for interface in interfaces_to_try: self.shellCheckOutput(['netcfg', interface, 'dhcp'], timeout=10) if self.getIP(interfaces=[interface]): return except DMError: pass time.sleep(1) tries -= 1 raise DMError("Could not set up network connection") def restoreProfile(self): """ Restores the original user profile on the device. """ if not self.profileDir: raise DMError("There is no profile to restore") #if we successfully copied the profile, make a backup of the file our_userJS = os.path.join(self.profileDir, "user.js") if os.path.exists(our_userJS): self.shellCheckOutput( ['dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) shutil.rmtree(self.profileDir) self.profileDir = None def getAppInfo(self): """ Returns the appinfo, with an additional "date" key. :rtype: dictionary """ if not self.marionette or not self.marionette.session: self.setupMarionette() self.marionette.set_context("chrome") appinfo = self.marionette.execute_script(""" var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] .getService(Components.interfaces.nsIXULAppInfo); return appInfo; """) (year, month, day) = (appinfo["appBuildID"][0:4], appinfo["appBuildID"][4:6], appinfo["appBuildID"][6:8]) appinfo['date'] = "%s-%s-%s" % (year, month, day) return appinfo
class B2GMochitest(MochitestUtilsMixin): marionette = None def __init__(self, marionette_args, logger_options, out_of_process=True, profile_data_dir=None, locations=os.path.join(here, 'server-locations.txt')): super(B2GMochitest, self).__init__(logger_options) self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' self.remote_chrome_test_dir = None if profile_data_dir: self.preferences = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref') ] self.webapps = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp') ] # mozinfo is populated by the parent class if mozinfo.info['debug']: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 def setup_common_options(self, options): test_url = self.buildTestPath(options) # For B2G emulators buildURLOptions has been called # without calling buildTestPath first and that # causes manifestFile not to be set if not "manifestFile=tests.json" in self.urlOpts: self.urlOpts.append("manifestFile=%s" % options.manifestFile) if len(self.urlOpts) > 0: test_url += "?" + "&".join(self.urlOpts) self.test_script_args.append(test_url) def buildTestPath(self, options, testsToFilter=None): if options.manifestFile != 'tests.json': super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False) return self.buildTestURL(options) def build_profile(self, options): # preferences prefs = {} for path in self.preferences: prefs.update(Preferences.read_prefs(path)) for v in options.extraPrefs: thispref = v.split("=", 1) if len(thispref) < 2: print "Error: syntax error in --setpref=" + v sys.exit(1) prefs[thispref[0]] = thispref[1] # interpolate the preferences interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort), "OOP": "true" if self.out_of_process else "false" } prefs = json.loads(json.dumps(prefs) % interpolation) for pref in prefs: prefs[pref] = Preferences.cast(prefs[pref]) kwargs = { 'addons': self.getExtensionsToInstall(options), 'apps': self.webapps, 'locations': self.locations_file, 'preferences': prefs, 'proxy': { "remote": options.webServer } } if options.profile: self.profile = Profile.clone(options.profile, **kwargs) else: self.profile = Profile(**kwargs) options.profilePath = self.profile.profile # TODO bug 839108 - mozprofile should probably handle this manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest def run_tests(self, options): """ Prepare, configure, run tests and cleanup """ manifest = self.build_profile(options) self.logPreamble(self.getActiveTests(options)) # configuring the message logger's buffering self.message_logger.buffering = options.quiet if options.debugger or not options.autorun: timeout = None else: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 timeout = options.timeout + 30.0 self.log.info("runtestsb2g.py | Running tests: start.") status = 0 try: def on_output(line): messages = self.message_logger.write(line) for message in messages: if message['action'] == 'test_start': self.runner.last_test = message['test'] # The logging will be handled by on_output, so we set the stream to None process_args = {'processOutputLine': on_output, 'stream': None} self.marionette_args['process_args'] = process_args self.marionette_args['profile'] = self.profile self.marionette = Marionette(**self.marionette_args) self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists(posixpath.dirname( self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) if options.chrome: # Update chrome manifest file in profile with correct path. self.writeChromeManifest(options) self.leak_report_file = posixpath.join( self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log') # We don't want to copy the host env onto the device, so pass in an # empty env. self.browserEnv = self.buildBrowserEnv(options, env={}) # B2G emulator debug tests still make external connections, so don't # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug 1039019). if mozinfo.info[ 'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv: del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] self.runner.env.update(self.browserEnv) self.startServers(options, None) self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.test_script_args.append(options.chrome) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) # Disable offline status management (bug 777145), otherwise the network # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) if options.chrome: self.app_ctx.dm.removeDir(self.remote_chrome_test_dir) self.app_ctx.dm.mkDir(self.remote_chrome_test_dir) local = super(B2GMochitest, self).getChromeTestDir(options) local = os.path.join(local, "chrome") remote = self.remote_chrome_test_dir self.log.info("pushing %s to %s on device..." % (local, remote)) self.app_ctx.dm.pushDir(local, remote) if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script( script.read(), script_args=self.test_script_args) else: self.marionette.execute_script( self.test_script, script_args=self.test_script_args) status = self.runner.wait() if status is None: # the runner has timed out status = 124 local_leak_file = tempfile.NamedTemporaryFile() self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) processLeakLog(local_leak_file.name, options) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n") status = -1 except: traceback.print_exc() self.log.error( "Automation Error: Received unexpected exception while running application\n" ) if hasattr(self, 'runner'): self.runner.check_for_crashes() status = 1 self.stopServers() self.log.info("runtestsb2g.py | Running tests: end.") if manifest is not None: self.cleanup(manifest, options) return status def getGMPPluginPath(self, options): # TODO: bug 1043403 return None def getChromeTestDir(self, options): # The chrome test directory returned here is the remote location # of chrome test files. A reference to this directory is requested # when building the profile locally, before self.app_ctx is defined. # To get around this, return a dummy directory until self.app_ctx # is defined; the correct directory will be returned later, over- # writing the dummy. if hasattr(self, 'app_ctx'): self.remote_chrome_test_dir = posixpath.join( self.app_ctx.remote_test_root, 'chrome') return self.remote_chrome_test_dir return 'dummy-chrome-test-dir'
class B2GRunner(object): remote_profile_dir = None def __init__(self, dm, url, tmpdir, marionette_host=None, marionette_port=None): self.dm = dm self.url = url self.tmpdir = tmpdir self.userJS = "/data/local/user.js" self.marionette_host = marionette_host or 'localhost' self.marionette_port = marionette_port or 2828 self.marionette = None def wait_for_port(self, timeout): 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(('localhost', self.marionette_port)) data = sock.recv(16) sock.close() if '"from"' in data: return True except: import traceback print traceback.format_exc() time.sleep(1) return False def restart_b2g(self): #restart b2g so we start with a clean slate self.dm.checkCmd(['shell', 'stop', 'b2g']) # Wait for a bit to make sure B2G has completely shut down. time.sleep(10) self.dm.checkCmd(['shell', 'start', 'b2g']) #wait for marionette port to come up if not self.wait_for_port(30000): raise Exception("Could not communicate with Marionette port after restarting B2G") self.marionette = Marionette(self.marionette_host, self.marionette_port) def setup_profile(self): #remove previous user.js if there is one our_user_js = os.path.join(self.tmpdir, "user.js") if os.path.exists(our_user_js): os.remove(our_user_js) #copy profile try: self.dm.checkCmd(["pull", self.userJS, our_user_js]) except subprocess.CalledProcessError: pass #if we successfully copied the profile, make a backup of the file if os.path.exists(our_user_js): self.dm.checkCmd(['shell', 'dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) user_js = open(our_user_js, 'a') user_js.write(""" user_pref("power.screen.timeout", 999999); """) user_js.close() self.dm.checkCmd(['push', our_user_js, self.userJS]) self.restart_b2g() def start(self): #forward the marionette port self.dm.checkCmd(['forward', 'tcp:%s' % self.marionette_port, 'tcp:%s' % self.marionette_port]) print "Setting up profile" self.setup_profile() #enable ethernet connection print "Running netcfg, it may take some time." self.dm.checkCmd(['shell', 'netcfg', 'eth0', 'dhcp']) #launch app session = self.marionette.start_session() if 'b2g' not in session: raise Exception("bad session value %s returned by start_session" % session) # start the tests by navigating to the mochitest url self.marionette.execute_script("window.location.href='%s';" % self.url) def stop(self): self.marionette.delete_session()
class B2GMochitest(MochitestUtilsMixin): marionette = None def __init__(self, marionette_args, logger_options, out_of_process=True, profile_data_dir=None, locations=os.path.join(here, 'server-locations.txt')): super(B2GMochitest, self).__init__(logger_options) self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' self.remote_chrome_test_dir = None if profile_data_dir: self.preferences = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref') ] self.webapps = [ os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp') ] # mozinfo is populated by the parent class if mozinfo.info['debug']: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 def setup_common_options(self, options): test_url = self.buildTestPath(options) # For B2G emulators buildURLOptions has been called # without calling buildTestPath first and that # causes manifestFile not to be set if not "manifestFile=tests.json" in self.urlOpts: self.urlOpts.append("manifestFile=%s" % options.manifestFile) if len(self.urlOpts) > 0: test_url += "?" + "&".join(self.urlOpts) self.test_script_args.append(test_url) def buildTestPath(self, options, testsToFilter=None): if options.manifestFile != 'tests.json': super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False) return self.buildTestURL(options) def build_profile(self, options): # preferences prefs = {} for path in self.preferences: prefs.update(Preferences.read_prefs(path)) for v in options.extraPrefs: thispref = v.split("=", 1) if len(thispref) < 2: print "Error: syntax error in --setpref=" + v sys.exit(1) prefs[thispref[0]] = thispref[1] # interpolate the preferences interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort), "OOP": "true" if self.out_of_process else "false" } prefs = json.loads(json.dumps(prefs) % interpolation) for pref in prefs: prefs[pref] = Preferences.cast(prefs[pref]) kwargs = { 'addons': self.getExtensionsToInstall(options), 'apps': self.webapps, 'locations': self.locations_file, 'preferences': prefs, 'proxy': { "remote": options.webServer } } if options.profile: self.profile = Profile.clone(options.profile, **kwargs) else: self.profile = Profile(**kwargs) options.profilePath = self.profile.profile # TODO bug 839108 - mozprofile should probably handle this manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest def run_tests(self, options): """ Prepare, configure, run tests and cleanup """ self.setTestRoot(options) manifest = self.build_profile(options) self.logPreamble(self.getActiveTests(options)) # configuring the message logger's buffering self.message_logger.buffering = options.quiet if options.debugger or not options.autorun: timeout = None else: if not options.timeout: if mozinfo.info['debug']: options.timeout = 420 else: options.timeout = 300 timeout = options.timeout + 30.0 self.log.info("runtestsb2g.py | Running tests: start.") status = 0 try: def on_output(line): messages = self.message_logger.write(line) for message in messages: if message['action'] == 'test_start': self.runner.last_test = message['test'] # The logging will be handled by on_output, so we set the stream to # None process_args = {'processOutputLine': on_output, 'stream': None} self.marionette_args['process_args'] = process_args self.marionette_args['profile'] = self.profile self.marionette = Marionette(**self.marionette_args) self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists(posixpath.dirname( self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) if options.chrome: # Update chrome manifest file in profile with correct path. self.writeChromeManifest(options) self.leak_report_file = posixpath.join( self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log') # We don't want to copy the host env onto the device, so pass in an # empty env. self.browserEnv = self.buildBrowserEnv(options, env={}) # B2G emulator debug tests still make external connections, so don't # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug # 1039019). if mozinfo.info[ 'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv: del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] self.runner.env.update(self.browserEnv) # Despite our efforts to clean up servers started by this script, in practice # we still see infrequent cases where a process is orphaned and interferes # with future tests, typically because the old server is keeping the port in use. # Try to avoid those failures by checking for and killing orphan servers before # trying to start new ones. self.killNamedOrphans('ssltunnel') self.killNamedOrphans('xpcshell') self.startServers(options, None) # In desktop mochitests buildTestPath is called before buildURLOptions. This # means options.manifestFile has already been converted to the proper json # style manifest. Not so with B2G, that conversion along with updating the URL # option will happen later. So backup and restore options.manifestFile to # prevent us from trying to pass in an instance of TestManifest via url param. manifestFile = options.manifestFile options.manifestFile = None self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) options.manifestFile = manifestFile self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) self.test_script_args.append(options.chrome) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) # Disable offline status management (bug 777145), otherwise the network # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for # emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) self.marionette.execute_script(""" let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; Services.prefs.setBoolPref(SECURITY_PREF, true); if (!testUtils.hasOwnProperty("specialPowersObserver")) { let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] .getService(Components.interfaces.mozIJSSubScriptLoader); loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js", testUtils); testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver(); testUtils.specialPowersObserver.init(); testUtils.specialPowersObserver._loadFrameScript(); } """) if options.chrome: self.app_ctx.dm.removeDir(self.remote_chrome_test_dir) self.app_ctx.dm.mkDir(self.remote_chrome_test_dir) local = super(B2GMochitest, self).getChromeTestDir(options) local = os.path.join(local, "chrome") remote = self.remote_chrome_test_dir self.log.info("pushing %s to %s on device..." % (local, remote)) self.app_ctx.dm.pushDir(local, remote) if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script( script.read(), script_args=self.test_script_args) else: self.marionette.execute_script( self.test_script, script_args=self.test_script_args) status = self.runner.wait() if status is None: # the runner has timed out status = 124 local_leak_file = tempfile.NamedTemporaryFile() self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) mozleak.process_leak_log( local_leak_file.name, leak_thresholds=options.leakThresholds, ignore_missing_leaks=options.ignoreMissingLeaks, log=self.log, ) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n") status = -1 except: traceback.print_exc() self.log.error( "Automation Error: Received unexpected exception while running application\n" ) if hasattr(self, 'runner'): self.runner.check_for_crashes() status = 1 self.stopServers() self.log.info("runtestsb2g.py | Running tests: end.") if manifest is not None: self.cleanup(manifest, options) return status def getGMPPluginPath(self, options): if options.gmp_path: return options.gmp_path return '/system/b2g/gmp-clearkey/0.1' def getChromeTestDir(self, options): # The chrome test directory returned here is the remote location # of chrome test files. A reference to this directory is requested # when building the profile locally, before self.app_ctx is defined. # To get around this, return a dummy directory until self.app_ctx # is defined; the correct directory will be returned later, over- # writing the dummy. if hasattr(self, 'app_ctx'): self.remote_chrome_test_dir = posixpath.join( self.app_ctx.remote_test_root, 'chrome') return self.remote_chrome_test_dir return 'dummy-chrome-test-dir'
def kill(origin): marionette = Marionette(host='localhost', port=2828) marionette.start_session() marionette.set_context(marionette.CONTEXT_CONTENT) marionette.execute_script( "window.wrappedJSObject.WindowManager.kill('%s')" % origin)