class FennecLauncher(Launcher): app_info = None adb = None package_name = None profile_class = FirefoxRegressionProfile remote_profile = None @classmethod def check_is_runnable(cls): try: devices = ADBHost().devices() except ADBError as adb_error: raise LauncherNotRunnable(str(adb_error)) if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg) ) self.adb.install_app(dest)
class FennecContext(RemoteContext): _remote_profiles_ini = None _remote_test_root = None def __init__(self, app=None, adb_path=None, avd_home=None, device_serial=None): self._adb = adb_path self.avd_home = avd_home self.remote_process = app self.device_serial = device_serial self.device = ADBAndroid(adb=self.adb, device=device_serial) def stop_application(self): self.device.stop_application(self.remote_process) @property def remote_test_root(self): if not self._remote_test_root: self._remote_test_root = self.device.test_root return self._remote_test_root @property def remote_profiles_ini(self): if not self._remote_profiles_ini: self._remote_profiles_ini = posixpath.join('/data', 'data', self.remote_process, 'files', 'mozilla', 'profiles.ini') return self._remote_profiles_ini
class FennecLauncher(Launcher): app_info = None adb = None package_name = None profile_class = FirefoxRegressionProfile remote_profile = None @classmethod def check_is_runnable(cls): try: devices = ADBHost().devices() except ADBError as adb_error: raise LauncherNotRunnable(str(adb_error)) if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg)) self.adb.install_app(dest)
def __init__(self, app, binary, run_local=False, obj_path=None, gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None, symbols_path=None): self.config = {} self.config['app'] = app self.config['binary'] = binary self.config['platform'] = mozinfo.os self.config['processor'] = mozinfo.processor self.config['run_local'] = run_local self.config['obj_path'] = obj_path self.config['gecko_profile'] = gecko_profile self.config['gecko_profile_interval'] = gecko_profile_interval self.config['gecko_profile_entries'] = gecko_profile_entries self.config['symbols_path'] = symbols_path self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv') self.log = get_default_logger(component='raptor-main') self.control_server = None self.playback = None self.benchmark = None self.gecko_profiler = None self.post_startup_delay = 30000 # raptor webext pause time after browser startup # Create the profile; for geckoview we want a firefox profile type if self.config['app'] == 'geckoview': self.profile = create_profile('firefox') else: self.profile = create_profile(self.config['app']) # Merge in base profiles with open(os.path.join(self.profile_data_dir, 'profiles.json'), 'r') as fh: base_profiles = json.load(fh)['raptor'] for name in base_profiles: path = os.path.join(self.profile_data_dir, name) self.log.info("Merging profile: {}".format(path)) self.profile.merge(path) # create results holder self.results_handler = RaptorResultsHandler() # when testing desktop browsers we use mozrunner to start the browser; when # testing on android (i.e. geckoview) we use mozdevice to control the device app if self.config['app'] == "geckoview": # create the android device handler; it gets initiated and sets up adb etc self.log.info("creating android device handler using mozdevice") self.device = ADBAndroid(verbose=True) self.device.clear_logcat() else: # create the desktop browser runner self.log.info("creating browser runner using mozrunner") self.output_handler = OutputHandler() process_args = { 'processOutputLine': [self.output_handler], } runner_cls = runners[app] self.runner = runner_cls( binary, profile=self.profile, process_args=process_args) self.log.info("raptor config: %s" % str(self.config))
class FennecLauncher(Launcher): app_info = None adb = None package_name = None remote_profile = None @classmethod def check_is_runnable(cls): # ADBHost().devices() seems to raise OSError when adb is not # installed and in PATH. TODO: maybe fix this in mozdevice. try: devices = ADBHost().devices() except OSError: raise LauncherNotRunnable("adb (Android Debug Bridge) is not" " installed or not in the PATH.") if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") def _install(self, dest): # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg) ) self.adb.install_app(dest)
def __init__(self, log, options): self.log = log verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True self.device = ADBAndroid(adb=options.adbPath or 'adb', device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) self.options = options self.log.debug("options=%s" % vars(options)) update_mozinfo() self.remote_profile = posixpath.join(self.device.test_root, 'junit-profile') if self.options.coverage and not self.options.coverage_output_path: raise Exception("--coverage-output-path is required when using --enable-coverage") self.remote_coverage_output_path = posixpath.join(self.device.test_root, 'junit-coverage.ec') self.server_init() self.cleanup() self.device.clear_logcat() self.build_profile() self.startServers( self.options, debuggerInfo=None, ignoreSSLTunnelExts=True) self.log.debug("Servers started")
def _install(self, dest): # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() self.adb.uninstall_app(self.package_name) self.adb.install_app(dest)
def __init__(self, log, options): self.log = log self.device = ADBAndroid(adb=options.adbPath or 'adb', device=options.deviceSerial, test_root=options.remoteTestRoot) self.options = options self.appname = self.options.app.split('/')[-1] self.logcat = None self.build_profile() self.log.debug("options=%s" % vars(options))
def __init__(self, app=None, adb_path=None, avd_home=None, device_serial=None): self._adb = adb_path self.avd_home = avd_home self.remote_process = app self.device_serial = device_serial self.device = ADBAndroid(adb=self.adb, device=device_serial)
def _install(self, dest): if self._get_device_status(): self.adb = ADBAndroid() if "y" != raw_input("WARNING: bisecting nightly fennec builds will" " clobber your existing nightly profile." " Continue? (y or n)"): raise Exception("Aborting!") self.adb.uninstall_app("org.mozilla.fennec") self.adb.install_app(dest) # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest)
def __init__(self, options, progs): cppunittests.CPPUnitTests.__init__(self) self.options = options self.device = ADBAndroid(adb=options.adb_path or 'adb', device=options.device_serial, test_root=options.remote_test_root) self.remote_test_root = posixpath.join(self.device.test_root, "cppunittests") self.remote_bin_dir = posixpath.join(self.remote_test_root, "b") self.remote_tmp_dir = posixpath.join(self.remote_test_root, "tmp") self.remote_home_dir = posixpath.join(self.remote_test_root, "h") if options.setup: self.setup_bin(progs)
def __init__(self, options, message_logger): """ Simple one-time initialization. """ MochitestDesktop.__init__(self, options.flavor, vars(options)) verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True self.device = ADBAndroid(adb=options.adbPath or 'adb', device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) # Check that Firefox is installed expected = options.app.split('/')[-1] if not self.device.is_app_installed(expected): raise Exception("%s is not installed on this device" % expected) options.logFile = "robocop.log" if options.remoteTestRoot is None: options.remoteTestRoot = self.device.test_root self.remoteProfile = posixpath.join(options.remoteTestRoot, "profile") self.remoteProfileCopy = posixpath.join(options.remoteTestRoot, "profile-copy") self.remoteModulesDir = posixpath.join(options.remoteTestRoot, "modules/") self.remoteConfigFile = posixpath.join(options.remoteTestRoot, "robotium.config") self.remoteLogFile = posixpath.join(options.remoteTestRoot, "logs", "robocop.log") self.options = options process_args = {'messageLogger': message_logger} self.auto = RemoteAutomation(self.device, options.remoteappname, self.remoteProfile, self.remoteLogFile, processArgs=process_args) self.environment = self.auto.environment self.remoteScreenshots = "/mnt/sdcard/Robotium-Screenshots" self.remoteMozLog = posixpath.join(options.remoteTestRoot, "mozlog") self.localLog = options.logFile self.localProfile = None self.certdbNew = True self.passed = 0 self.failed = 0 self.todo = 0
def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg))
class FennecLauncher(Launcher): app_info = None def _get_device_status(self): self.adbhost = ADBHost() if self.adbhost.devices(): return True if "y" == raw_input("WARNING: no device connected." " Connect a device and try again.\n" "Try again? (y or n): "): return self.get_device_status() raise Exception("Aborting!") def _install(self, dest): if self._get_device_status(): self.adb = ADBAndroid() if "y" != raw_input("WARNING: bisecting nightly fennec builds will" " clobber your existing nightly profile." " Continue? (y or n)"): raise Exception("Aborting!") self.adb.uninstall_app("org.mozilla.fennec") self.adb.install_app(dest) # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) def _start(self, **args): self.adb.launch_fennec("org.mozilla.fennec") def _stop(self): self.adb.stop_application("org.mozilla.fennec") def _get_app_info(self): return self.app_info
def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", self._get_package_name()) self.adb = ADBAndroid(require_root=False) try: self.adb.uninstall_app(self.package_name) except ADBError as msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg)) self.adb.install_app(dest)
class FennecLauncher(Launcher): app_info = None adb = None @classmethod def check_is_runnable(cls): # ADBHost().devices() seems to raise OSError when adb is not # installed and in PATH. TODO: maybe fix this in mozdevice. try: devices = ADBHost().devices() except OSError: raise LauncherNotRunnable("adb (Android Debug Bridge) is not" " installed or not in the PATH.") if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") if not raw_input("WARNING: bisecting fennec builds will clobber your" " existing profile. Type 'y' to continue:") == 'y': raise LauncherNotRunnable('Aborted.') def _install(self, dest): self.adb = ADBAndroid() self.adb.uninstall_app("org.mozilla.fennec") self.adb.install_app(dest) # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) def _start(self, **kwargs): self.adb.launch_fennec("org.mozilla.fennec") def _stop(self): self.adb.stop_application("org.mozilla.fennec") def get_app_info(self): return self.app_info
def run_raptor_test(self, **kwargs): build_obj = MozbuildObject.from_environment(cwd=HERE) firefox_android_browsers = ["fennec", "geckoview", "refbrow", "fenix"] if conditions.is_android( build_obj) or kwargs['app'] in firefox_android_browsers: from mozrunner.devices.android_device import verify_android_device from mozdevice import ADBAndroid, ADBHost if not verify_android_device( build_obj, install=True, app=kwargs['binary'], xre=True): # Equivalent to 'run_local' = True. return 1 debug_command = '--debug-command' if debug_command in sys.argv: sys.argv.remove(debug_command) raptor = self._spawn(RaptorRunner) try: if kwargs['app'] in firefox_android_browsers and kwargs[ 'power_test']: device = ADBAndroid(verbose=True) adbhost = ADBHost(verbose=True) device_serial = "%s:5555" % device.get_ip_address() device.command_output(["tcpip", "5555"]) raw_input( "Please disconnect your device from USB then press Enter/return..." ) adbhost.command_output(["connect", device_serial]) while len(adbhost.devices()) > 1: raw_input( "You must disconnect your device from USB before continuing." ) # must reset the environment DEVICE_SERIAL which was set during # verify_android_device to match our new tcpip value. os.environ["DEVICE_SERIAL"] = device_serial return raptor.run_test(sys.argv[2:], kwargs) except Exception as e: print(repr(e)) return 1 finally: try: if kwargs['app'] in firefox_android_browsers and kwargs[ 'power_test']: raw_input( "Connect device via USB and press Enter/return...") device = ADBAndroid(device=device_serial, verbose=True) device.command_output(["usb"]) adbhost.command_output(["disconnect", device_serial]) except Exception: adbhost.command_output(["kill-server"])
def run_raptor(self, **kwargs): build_obj = self is_android = Conditions.is_android(build_obj) or \ kwargs['app'] in FIREFOX_ANDROID_BROWSERS if is_android: from mozrunner.devices.android_device import ( verify_android_device, InstallIntent) from mozdevice import ADBAndroid, ADBHost install = InstallIntent.NO if kwargs.pop( 'noinstall', False) else InstallIntent.PROMPT if not verify_android_device( build_obj, install=install, app=kwargs['binary'], xre=True): # Equivalent to 'run_local' = True. return 1 debug_command = '--debug-command' if debug_command in sys.argv: sys.argv.remove(debug_command) raptor = self._spawn(RaptorRunner) try: if is_android and kwargs['power_test']: device = ADBAndroid(verbose=True) adbhost = ADBHost(verbose=True) device_serial = "{}:5555".format(device.get_ip_address()) device.command_output(["tcpip", "5555"]) six.input( "Please disconnect your device from USB then press Enter/return..." ) adbhost.command_output(["connect", device_serial]) while len(adbhost.devices()) > 1: six.input( "You must disconnect your device from USB before continuing." ) # must reset the environment DEVICE_SERIAL which was set during # verify_android_device to match our new tcpip value. os.environ["DEVICE_SERIAL"] = device_serial return raptor.run_test(sys.argv[2:], kwargs) except Exception as e: print(repr(e)) return 1 finally: try: if is_android and kwargs['power_test']: six.input( "Connect device via USB and press Enter/return...") device = ADBAndroid(device=device_serial, verbose=True) device.command_output(["usb"]) adbhost.command_output(["disconnect", device_serial]) except Exception: adbhost.command_output(["kill-server"])
def setup_app(self): # create the android device handler; it gets initiated and sets up adb etc self.log.info("creating android device handler using mozdevice") self.device = ADBAndroid(verbose=True, logger_name="recorder-adb - ") self.device_raptor_dir = "/sdcard/raptor" self.config['device_raptor_dir'] = self.device_raptor_dir if self.device.is_dir(self.device_raptor_dir): self.log.info("deleting existing device raptor dir: %s" % self.device_raptor_dir) self.device.rm(self.device_raptor_dir, recursive=True) self.log.info("creating raptor folder on sdcard: %s" % self.device_raptor_dir) self.device.mkdir(self.device_raptor_dir) self.device.chmod(self.device_raptor_dir, recursive=True)
def run_raptor(self, **kwargs): build_obj = self is_android = Conditions.is_android(build_obj) or \ kwargs['app'] in FIREFOX_ANDROID_BROWSERS if is_android: from mozrunner.devices.android_device import ( verify_android_device, InstallIntent) from mozdevice import ADBAndroid install = InstallIntent.NO if kwargs.pop( 'noinstall', False) else InstallIntent.PROMPT if not verify_android_device( build_obj, install=install, app=kwargs['binary'], xre=True): # Equivalent to 'run_local' = True. return 1 debug_command = '--debug-command' if debug_command in sys.argv: sys.argv.remove(debug_command) raptor = self._spawn(RaptorRunner) device = None try: if kwargs['power_test'] and is_android: device = ADBAndroid(verbose=True) disable_charging(device) return raptor.run_test(sys.argv[2:], kwargs) except Exception as e: print(repr(e)) return 1 finally: if kwargs['power_test'] and device: enable_charging(device)
def setup_device(self): self.device = ADBAndroid() if self.binary and self.proxy.mode is "record": if not self.skip_install: if self.device.is_app_installed(self.APP_NAME): print("Uninstalling app %s" % self.APP_NAME) self.device.uninstall_app(self.APP_NAME) print("Installing app %s" % self.APP_NAME) self.device.install_app(apk_path=self.binary) self.skip_install = True else: print("App already installed in a previous recording!!!!") else: print( "No binary provided or proxy in replay mode! Using existing app on the device." )
class Robocop(object): device = ADBAndroid() logger = logging.getLogger() def __init__(self, app, log): self.app = app self.log = log def run(self, identifier): # build browser environment # setup robotium config # setup remote profile # build browser args cmd = [ "am", "instrument", "-e quit_and_finish 1", "-e deviceroot {}".format(self.device.test_root), "-e class org.mozilla.gecko.tests.{}".format(identifier), "org.mozilla.roboexample.test/org.mozilla.gecko.FennecInstrumentationTestRunner", ] self.device.clear_logcat() self.logger.info("Running: {}".format(" ".join(cmd))) self.device.shell(" ".join(cmd)) # wait for process to end top = self.app while top == self.app: time.sleep(0.5) top = self.device.get_top_activity(timeout=60) log = "\n".join(self.device.get_logcat()) with open(self.log, "w") as f: f.writelines(log) assert "Failed: 0" in log
def __init__(self, inbound_branch=None, bits=mozinfo.bits, persist=None): self.inbound_branch = inbound_branch self.persist = persist if self.get_device_status(): self.adb = ADBAndroid() if "y" != raw_input("WARNING: bisecting nightly fennec builds will" " clobber your existing nightly profile." " Continue? (y or n)"): raise Exception("Aborting!")
def _get_device(substs, device_serial=None): global devices if device_serial in devices: device = devices[device_serial] else: adb_path = _find_sdk_exe(substs, 'adb', False) if not adb_path: adb_path = 'adb' device = ADBAndroid(adb=adb_path, verbose=verbose_logging, device=device_serial) devices[device_serial] = device return device
def get_device(self, adb_path, device_serial): # Create a mozdevice.ADBAndroid object for the specified device_serial # and cache it for future use. If the same device_serial is subsequently # requested, retrieve it from the cache to avoid costly re-initialization. global devices if device_serial in devices: device = devices[device_serial] else: device = ADBAndroid(adb=adb_path, device=device_serial) devices[device_serial] = device return device
def run_raptor(self, **kwargs): # Defers this import so that a transitive dependency doesn't # stop |mach bootstrap| from running from raptor.power import enable_charging, disable_charging build_obj = self is_android = Conditions.is_android(build_obj) or \ kwargs['app'] in ANDROID_BROWSERS if is_android: from mozrunner.devices.android_device import ( verify_android_device, InstallIntent) from mozdevice import ADBAndroid install = InstallIntent.NO if kwargs.pop( 'noinstall', False) else InstallIntent.YES verbose = False if kwargs.get('log_mach_verbose') or kwargs.get('log_tbpl_level') == 'debug' or \ kwargs.get('log_mach_level') == 'debug' or kwargs.get('log_raw_level') == 'debug': verbose = True if not verify_android_device( build_obj, install=install, app=kwargs['binary'], verbose=verbose, xre=True): # Equivalent to 'run_local' = True. return 1 debug_command = '--debug-command' if debug_command in sys.argv: sys.argv.remove(debug_command) raptor = self._spawn(RaptorRunner) device = None try: if kwargs['power_test'] and is_android: device = ADBAndroid(verbose=True) disable_charging(device) return raptor.run_test(sys.argv[2:], kwargs) except BinaryNotFoundException as e: self.log(logging.ERROR, 'raptor', {'error': str(e)}, 'ERROR: {error}') self.log(logging.INFO, 'raptor', {'help': e.help()}, '{help}') return 1 except Exception as e: print(repr(e)) return 1 finally: if kwargs['power_test'] and device: enable_charging(device)
class FennecNightly(Nightly): app_name = 'fennec' name = 'fennec' profile_class = FirefoxProfile build_regex = r'fennec-.*\.apk' build_info_regex = r'fennec-.*\.txt' binary = 'org.mozilla.fennec/.App' bits = None build_base_repo_name = "mobile" def get_device_status(self): self.adbhost = ADBHost() if self.adbhost.devices(): return True if "y" == raw_input("WARNING: no device connected." " Connect a device and try again.\n" "Try again? (y or n): "): return self.get_device_status() raise Exception("Aborting!") def __init__(self, inbound_branch=None, bits=mozinfo.bits, persist=None): self.inbound_branch = inbound_branch self.persist = persist if self.get_device_status(): self.adb = ADBAndroid() if "y" != raw_input("WARNING: bisecting nightly fennec builds will" " clobber your existing nightly profile." " Continue? (y or n)"): raise Exception("Aborting!") def get_inbound_branch(self, date): return "mozilla-central-android" def install(self): self.adb.uninstall_app("org.mozilla.fennec") self.adb.install_app(self.dest) return True def start(self, profile, addons, cmdargs): self.adb.launch_fennec("org.mozilla.fennec") return True def stop(self): self.adb.stop_application("org.mozilla.fennec") return True def get_app_info(self): return mozversion.get_version(binary=self.dest)
def _install(self, dest): # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg) )
def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", self._get_package_name()) self.adb = ADBAndroid(require_root=False) try: self.adb.uninstall_app(self.package_name) except ADBError, msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg) )
def __init__(self, log, options): self.log = log verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True self.device = ADBAndroid(adb=options.adbPath or 'adb', device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) self.options = options self.log.debug("options=%s" % vars(options)) update_mozinfo() self.remote_profile = posixpath.join(self.device.test_root, 'junit-profile') self.server_init() self.cleanup() self.device.clear_logcat() self.build_profile() self.startServers( self.options, debuggerInfo=None, ignoreSSLTunnelExts=True) self.log.debug("Servers started")
def verify_device(self, adb_path, device): """ Check that the specified device is available and rooted. """ try: device = ADBAndroid(adb=adb_path, device=device, timeout=10) if device._have_su or device._have_android_su or device._have_root_shell: return True except Exception: self.build_obj.log( logging.WARN, "autophone", {}, "Unable to verify root on device.") if self.verbose: self.build_obj.log(logging.ERROR, "autophone", {}, str(sys.exc_info()[0])) return False
def run_raptor(self, **kwargs): build_obj = self is_android = Conditions.is_android(build_obj) or \ kwargs['app'] in ANDROID_BROWSERS if is_android: from mozrunner.devices.android_device import ( verify_android_device, InstallIntent) from mozdevice import ADBAndroid install = InstallIntent.NO if kwargs.pop( 'noinstall', False) else InstallIntent.YES if not verify_android_device( build_obj, install=install, app=kwargs['binary'], xre=True): # Equivalent to 'run_local' = True. return 1 debug_command = '--debug-command' if debug_command in sys.argv: sys.argv.remove(debug_command) raptor = self._spawn(RaptorRunner) device = None try: if kwargs['power_test'] and is_android: device = ADBAndroid(verbose=True) disable_charging(device) return raptor.run_test(sys.argv[2:], kwargs) except BinaryNotFoundException as e: self.log(logging.ERROR, 'raptor', {'error': str(e)}, 'ERROR: {error}') self.log(logging.INFO, 'raptor', {'help': e.help()}, '{help}') return 1 except Exception as e: print(repr(e)) return 1 finally: if kwargs['power_test'] and device: enable_charging(device)
class RemoteReftest(RefTest): use_marionette = False resolver_cls = RemoteReftestResolver def __init__(self, options, scriptDir): RefTest.__init__(self, options.suite) self.run_by_manifest = False self.scriptDir = scriptDir self.localLogName = options.localLogName verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True print "set verbose!" self.device = ADBAndroid(adb=options.adb_path, device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) if options.remoteTestRoot is None: options.remoteTestRoot = posixpath.join(self.device.test_root, "reftest") options.remoteProfile = posixpath.join(options.remoteTestRoot, "profile") options.remoteLogFile = posixpath.join(options.remoteTestRoot, "reftest.log") options.logFile = options.remoteLogFile self.remoteProfile = options.remoteProfile self.remoteTestRoot = options.remoteTestRoot if not options.ignoreWindowSize: parts = self.device.get_info('screen')['screen'][0].split() width = int(parts[0].split(':')[1]) height = int(parts[1].split(':')[1]) if (width < 1366 or height < 1050): self.error("ERROR: Invalid screen resolution %sx%s, " "please adjust to 1366x1050 or higher" % (width, height)) self._populate_logger(options) self.outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath) # RemoteAutomation.py's 'messageLogger' is also used by mochitest. Mimic a mochitest # MessageLogger object to re-use this code path. self.outputHandler.write = self.outputHandler.__call__ self.automation = RemoteAutomation(self.device, options.app, self.remoteProfile, options.remoteLogFile, processArgs=None) self.automation._processArgs['messageLogger'] = self.outputHandler self.environment = self.automation.environment if self.automation.IS_DEBUG_BUILD: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 self.remoteCache = os.path.join(options.remoteTestRoot, "cache/") # Check that Firefox is installed expected = options.app.split('/')[-1] if not self.device.is_app_installed(expected): raise Exception("%s is not installed on this device" % expected) self.automation.deleteANRs() self.automation.deleteTombstones() self.device.clear_logcat() self.device.rm(self.remoteCache, force=True, recursive=True) procName = options.app.split('/')[-1] self.device.pkill(procName) if self.device.process_exist(procName): self.log.error("unable to kill %s before starting tests!" % procName) def findPath(self, paths, filename=None): for path in paths: p = path if filename: p = os.path.join(p, filename) if os.path.exists(self.getFullPath(p)): return path return None def startWebServer(self, options): """ Create the webserver on the host and start it up """ remoteXrePath = options.xrePath remoteUtilityPath = options.utilityPath localAutomation = Automation() localAutomation.IS_WIN32 = False localAutomation.IS_LINUX = False localAutomation.IS_MAC = False localAutomation.UNIXISH = False hostos = sys.platform if (hostos == 'mac' or hostos == 'darwin'): localAutomation.IS_MAC = True elif (hostos == 'linux' or hostos == 'linux2'): localAutomation.IS_LINUX = True localAutomation.UNIXISH = True elif (hostos == 'win32' or hostos == 'win64'): localAutomation.BIN_SUFFIX = ".exe" localAutomation.IS_WIN32 = True paths = [options.xrePath, localAutomation.DIST_BIN] options.xrePath = self.findPath(paths) if options.xrePath is None: print( "ERROR: unable to find xulrunner path for %s, " "please specify with --xre-path" % (os.name)) return 1 paths.append("bin") paths.append(os.path.join("..", "bin")) xpcshell = "xpcshell" if (os.name == "nt"): xpcshell += ".exe" if (options.utilityPath): paths.insert(0, options.utilityPath) options.utilityPath = self.findPath(paths, xpcshell) if options.utilityPath is None: print( "ERROR: unable to find utility path for %s, " "please specify with --utility-path" % (os.name)) return 1 options.serverProfilePath = tempfile.mkdtemp() self.server = ReftestServer(localAutomation, options, self.scriptDir) retVal = self.server.start() if retVal: return retVal retVal = self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT) if retVal: return retVal options.xrePath = remoteXrePath options.utilityPath = remoteUtilityPath return 0 def stopWebServer(self, options): self.server.stop() def killNamedProc(self, pname, orphans=True): """ Kill processes matching the given command name """ self.log.info("Checking for %s processes..." % pname) for proc in psutil.process_iter(): try: if proc.name() == pname: procd = proc.as_dict( attrs=['pid', 'ppid', 'name', 'username']) if proc.ppid() == 1 or not orphans: self.log.info("killing %s" % procd) try: os.kill(proc.pid, getattr(signal, "SIGKILL", signal.SIGTERM)) except Exception as e: self.log.info("Failed to kill process %d: %s" % (proc.pid, str(e))) else: self.log.info("NOT killing %s (not an orphan?)" % procd) except Exception: # may not be able to access process info for all processes continue def createReftestProfile(self, options, **kwargs): profile = RefTest.createReftestProfile(self, options, server=options.remoteWebServer, port=options.httpPort, **kwargs) profileDir = profile.profile prefs = {} prefs["app.update.url.android"] = "" prefs["browser.firstrun.show.localepicker"] = False prefs["reftest.remote"] = True prefs[ "datareporting.policy.dataSubmissionPolicyBypassAcceptance"] = True # move necko cache to a location that can be cleaned up prefs["browser.cache.disk.parent_directory"] = self.remoteCache prefs["layout.css.devPixelsPerPx"] = "1.0" # Because Fennec is a little wacky (see bug 1156817) we need to load the # reftest pages at 1.0 zoom, rather than zooming to fit the CSS viewport. prefs["apz.allow_zooming"] = False # Set the extra prefs. profile.set_preferences(prefs) try: self.device.push(profileDir, options.remoteProfile) self.device.chmod(options.remoteProfile, recursive=True) except Exception: print "Automation Error: Failed to copy profiledir to device" raise return profile def copyExtraFilesToProfile(self, options, profile): profileDir = profile.profile RefTest.copyExtraFilesToProfile(self, options, profile) if len(os.listdir(profileDir)) > 0: try: self.device.push(profileDir, options.remoteProfile) self.device.chmod(options.remoteProfile, recursive=True) except Exception: print "Automation Error: Failed to copy extra files to device" raise def printDeviceInfo(self, printLogcat=False): try: if printLogcat: logcat = self.device.get_logcat( filter_out_regexps=fennecLogcatFilters) print ''.join(logcat) print "Device info:" devinfo = self.device.get_info() for category in devinfo: if type(devinfo[category]) is list: print " %s:" % category for item in devinfo[category]: print " %s" % item else: print " %s: %s" % (category, devinfo[category]) print "Test root: %s" % self.device.test_root except Exception as e: print "WARNING: Error getting device information: %s" % str(e) def environment(self, **kwargs): return self.automation.environment(**kwargs) def buildBrowserEnv(self, options, profileDir): browserEnv = RefTest.buildBrowserEnv(self, options, profileDir) # remove desktop environment not used on device if "XPCOM_MEM_BLOAT_LOG" in browserEnv: del browserEnv["XPCOM_MEM_BLOAT_LOG"] return browserEnv def runApp(self, options, cmdargs=None, timeout=None, debuggerInfo=None, symbolsPath=None, valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, **profileArgs): if cmdargs is None: cmdargs = [] if self.use_marionette: cmdargs.append('-marionette') binary = options.app profile = self.createReftestProfile(options, **profileArgs) # browser environment env = self.buildBrowserEnv(options, profile.profile) self.log.info("Running with e10s: {}".format(options.e10s)) status, self.lastTestSeen = self.automation.runApp( None, env, binary, profile.profile, cmdargs, utilityPath=options.utilityPath, xrePath=options.xrePath, debuggerInfo=debuggerInfo, symbolsPath=symbolsPath, timeout=timeout) self.cleanup(profile.profile) return status def cleanup(self, profileDir): self.device.rm(self.remoteTestRoot, force=True, recursive=True) self.device.rm(self.remoteProfile, force=True, recursive=True) self.device.rm(self.remoteCache, force=True, recursive=True) RefTest.cleanup(self, profileDir)
class AndroidLauncher(Launcher): app_info = None adb = None package_name = None profile_class = FirefoxRegressionProfile remote_profile = None @abstractmethod def _get_package_name(self): raise NotImplementedError @abstractmethod def _launch(self): raise NotImplementedError @classmethod def check_is_runnable(cls): try: devices = ADBHost().devices() except ADBError as adb_error: raise LauncherNotRunnable(str(adb_error)) if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") def _install(self, dest): # get info now, as dest may be removed self.app_info = safe_get_version(binary=dest) self.package_name = self.app_info.get("package_name", self._get_package_name()) self.adb = ADBAndroid(require_root=False) try: self.adb.uninstall_app(self.package_name) except ADBError as msg: LOG.warning( "Failed to uninstall %s (%s)\nThis is normal if it is the" " first time the application is installed." % (self.package_name, msg)) self.adb.install_app(dest) def _start(self, profile=None, addons=(), cmdargs=(), preferences=None, adb_profile_dir=None): # for now we don't handle addons on the profile for fennec profile = self._create_profile(profile=profile, preferences=preferences) # send the profile on the device if not adb_profile_dir: adb_profile_dir = self.adb.test_root self.remote_profile = "/".join( [adb_profile_dir, os.path.basename(profile.profile)]) if self.adb.exists(self.remote_profile): self.adb.rm(self.remote_profile, recursive=True) LOG.debug("Pushing profile to device (%s -> %s)" % (profile.profile, self.remote_profile)) self.adb.push(profile.profile, self.remote_profile) self._launch() def _wait(self): while self.adb.process_exist(self.package_name): time.sleep(0.1) def _stop(self): self.adb.stop_application(self.package_name) if self.adb.exists(self.remote_profile): self.adb.rm(self.remote_profile, recursive=True) def get_app_info(self): return self.app_info
def __init__(self, options, scriptDir): RefTest.__init__(self, options.suite) self.run_by_manifest = False self.scriptDir = scriptDir self.localLogName = options.localLogName verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True print "set verbose!" self.device = ADBAndroid(adb=options.adb_path, device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) if options.remoteTestRoot is None: options.remoteTestRoot = posixpath.join(self.device.test_root, "reftest") options.remoteProfile = posixpath.join(options.remoteTestRoot, "profile") options.remoteLogFile = posixpath.join(options.remoteTestRoot, "reftest.log") options.logFile = options.remoteLogFile self.remoteProfile = options.remoteProfile self.remoteTestRoot = options.remoteTestRoot if not options.ignoreWindowSize: parts = self.device.get_info('screen')['screen'][0].split() width = int(parts[0].split(':')[1]) height = int(parts[1].split(':')[1]) if (width < 1366 or height < 1050): self.error("ERROR: Invalid screen resolution %sx%s, " "please adjust to 1366x1050 or higher" % (width, height)) self._populate_logger(options) self.outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath) # RemoteAutomation.py's 'messageLogger' is also used by mochitest. Mimic a mochitest # MessageLogger object to re-use this code path. self.outputHandler.write = self.outputHandler.__call__ self.automation = RemoteAutomation(self.device, options.app, self.remoteProfile, options.remoteLogFile, processArgs=None) self.automation._processArgs['messageLogger'] = self.outputHandler self.environment = self.automation.environment if self.automation.IS_DEBUG_BUILD: self.SERVER_STARTUP_TIMEOUT = 180 else: self.SERVER_STARTUP_TIMEOUT = 90 self.remoteCache = os.path.join(options.remoteTestRoot, "cache/") # Check that Firefox is installed expected = options.app.split('/')[-1] if not self.device.is_app_installed(expected): raise Exception("%s is not installed on this device" % expected) self.automation.deleteANRs() self.automation.deleteTombstones() self.device.clear_logcat() self.device.rm(self.remoteCache, force=True, recursive=True) procName = options.app.split('/')[-1] self.device.pkill(procName) if self.device.process_exist(procName): self.log.error("unable to kill %s before starting tests!" % procName)
class Raptor(object): """Container class for Raptor""" def __init__(self, app, binary, run_local=False, obj_path=None): self.config = {} self.config['app'] = app self.config['binary'] = binary self.config['platform'] = mozinfo.os self.config['processor'] = mozinfo.processor self.config['run_local'] = run_local self.config['obj_path'] = obj_path self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv') self.log = get_default_logger(component='raptor-main') self.control_server = None self.playback = None self.benchmark = None # Create the profile; for geckoview we want a firefox profile type if self.config['app'] == 'geckoview': self.profile = create_profile('firefox') else: self.profile = create_profile(self.config['app']) # Merge in base profiles with open(os.path.join(self.profile_data_dir, 'profiles.json'), 'r') as fh: base_profiles = json.load(fh)['raptor'] for name in base_profiles: path = os.path.join(self.profile_data_dir, name) self.log.info("Merging profile: {}".format(path)) self.profile.merge(path) # create results holder self.results_handler = RaptorResultsHandler() # when testing desktop browsers we use mozrunner to start the browser; when # testing on android (i.e. geckoview) we use mozdevice to control the device app if self.config['app'] == "geckoview": # create the android device handler; it gets initiated and sets up adb etc self.log.info("creating android device handler using mozdevice") self.device = ADBAndroid(verbose=True) self.device.clear_logcat() else: # create the desktop browser runner self.log.info("creating browser runner using mozrunner") self.output_handler = OutputHandler() process_args = { 'processOutputLine': [self.output_handler], } runner_cls = runners[app] self.runner = runner_cls(binary, profile=self.profile, process_args=process_args) self.log.info("raptor config: %s" % str(self.config)) @property def profile_data_dir(self): if 'MOZ_DEVELOPER_REPO_DIR' in os.environ: return os.path.join(os.environ['MOZ_DEVELOPER_REPO_DIR'], 'testing', 'profiles') if build: return os.path.join(build.topsrcdir, 'testing', 'profiles') return os.path.join(here, 'profile_data') def start_control_server(self): self.control_server = RaptorControlServer(self.results_handler) self.control_server.start() # for android we must make the control server available to the device if self.config['app'] == "geckoview": self.log.info( "making the raptor control server port available to device") _tcp_port = "tcp:%s" % self.control_server.port self.device.create_socket_connection('reverse', _tcp_port, _tcp_port) def get_playback_config(self, test): self.config['playback_tool'] = test.get('playback') self.log.info("test uses playback tool: %s " % self.config['playback_tool']) self.config['playback_binary_manifest'] = test.get( 'playback_binary_manifest', None) _key = 'playback_binary_zip_%s' % self.config['platform'] self.config['playback_binary_zip'] = test.get(_key, None) self.config['playback_pageset_manifest'] = test.get( 'playback_pageset_manifest', None) _key = 'playback_pageset_zip_%s' % self.config['platform'] self.config['playback_pageset_zip'] = test.get(_key, None) self.config['playback_recordings'] = test.get('playback_recordings', None) self.config['python3_win_manifest'] = test.get('python3_win_manifest', None) def run_test(self, test, timeout=None): self.log.info("starting raptor test: %s" % test['name']) self.log.info("test settings: %s" % str(test)) self.log.info("raptor config: %s" % str(self.config)) # benchmark-type tests require the benchmark test to be served out if test.get('type') == "benchmark": self.benchmark = Benchmark(self.config, test) benchmark_port = int(self.benchmark.port) else: benchmark_port = 0 gen_test_config(self.config['app'], test['name'], self.control_server.port, benchmark_port) # for android we must make the benchmarks server available to the device if self.config['app'] == "geckoview": self.log.info( "making the raptor benchmarks server port available to device") _tcp_port = "tcp:%s" % benchmark_port self.device.create_socket_connection('reverse', _tcp_port, _tcp_port) # must intall raptor addon each time because we dynamically update some content raptor_webext = os.path.join(webext_dir, 'raptor') self.log.info("installing webext %s" % raptor_webext) self.profile.addons.install(raptor_webext) # add test specific preferences if test.get("preferences", None) is not None: if self.config['app'] == "firefox": self.profile.set_preferences(json.loads(test['preferences'])) else: self.log.info("preferences were configured for the test, \ but we do not install them on non Firefox browsers." ) # on firefox we can get an addon id; chrome addon actually is just cmd line arg if self.config['app'] in ["firefox", "geckoview"]: webext_id = self.profile.addons.addon_details(raptor_webext)['id'] # some tests require tools to playback the test pages if test.get('playback', None) is not None: self.get_playback_config(test) # startup the playback tool self.playback = get_playback(self.config) # for geckoview we must copy the profile onto the device and set perms if self.config['app'] == "geckoview": if not self.device.is_app_installed(self.config['binary']): raise Exception('%s is not installed' % self.config['binary']) self.log.info("copying firefox profile onto the android device") self.device_profile = "/sdcard/raptor-profile" if self.device.is_dir(self.device_profile): self.device.rm(self.device_profile, recursive=True) self.device.mkdir(self.device_profile) self.device.push(self.profile.profile, self.device_profile) self.log.info("setting permisions to profile dir on the device") self.device.chmod(self.device_profile, recursive=True) # now start the geckoview app self.log.info("starting %s" % self.config['app']) extra_args = [ "-profile", self.device_profile, "--es", "env0", "LOG_VERBOSE=1", "--es", "env1", "R_LOG_LEVEL=6" ] try: # make sure the geckoview app is not running before # attempting to start. self.device.stop_application(self.config['binary']) self.device.launch_activity(self.config['binary'], "GeckoViewActivity", extra_args=extra_args, url='about:blank', fail_if_running=False) except Exception: self.log.error("Exception launching %s" % self.config['binary']) raise self.control_server.device = self.device self.control_server.app_name = self.config['binary'] else: # now start the desktop browser self.log.info("starting %s" % self.config['app']) self.runner.start() proc = self.runner.process_handler self.output_handler.proc = proc self.control_server.browser_proc = proc # set our cs flag to indicate we are running the browser/app self.control_server._finished = False # convert to seconds and account for page cycles timeout = int(timeout / 1000) * int(test['page_cycles']) try: elapsed_time = 0 while not self.control_server._finished: time.sleep(1) elapsed_time += 1 if elapsed_time > (timeout) - 5: # stop 5 seconds early self.log.info( "application timed out after {} seconds".format( timeout)) self.control_server.wait_for_quit() break finally: if self.config['app'] != "geckoview": try: self.runner.check_for_crashes() except NotImplementedError: # not implemented for Chrome pass # TODO: if on geckoview is there some cleanup here i.e. check for crashes? if self.playback is not None: self.playback.stop() # remove the raptor webext; as it must be reloaded with each subtest anyway # applies to firefox only; chrome the addon is actually just cmd line arg if self.config['app'] in ["firefox", "geckoview"]: self.log.info("removing webext %s" % raptor_webext) self.profile.addons.remove_addon(webext_id) if self.config['app'] != "geckoview": if self.runner.is_running(): self.runner.stop() # TODO the geckoview app should have been shutdown by this point by the # control server, but we can double-check here to make sure def process_results(self): # when running locally output results in build/raptor.json; when running # in production output to a local.json to be turned into tc job artifact if self.config.get('run_local', False): if 'MOZ_DEVELOPER_REPO_DIR' in os.environ: raptor_json_path = os.path.join( os.environ['MOZ_DEVELOPER_REPO_DIR'], 'testing', 'mozharness', 'build', 'raptor.json') else: raptor_json_path = os.path.join(here, 'raptor.json') else: raptor_json_path = os.path.join(os.getcwd(), 'local.json') self.config['raptor_json_path'] = raptor_json_path return self.results_handler.summarize_and_output(self.config) def clean_up(self): self.control_server.stop() if self.config['app'] != "geckoview": self.runner.stop() elif self.config['app'] == 'geckoview': self.log.info('removing reverse socket connections') self.device.remove_socket_connections('reverse') else: pass self.log.info("finished")
class FennecLauncher(Launcher): app_info = None adb = None package_name = None remote_profile = None @classmethod def check_is_runnable(cls): # ADBHost().devices() seems to raise OSError when adb is not # installed and in PATH. TODO: maybe fix this in mozdevice. try: devices = ADBHost().devices() except OSError: raise LauncherNotRunnable("adb (Android Debug Bridge) is not" " installed or not in the PATH.") if not devices: raise LauncherNotRunnable("No android device connected." " Connect a device and try again.") def _install(self, dest): # get info now, as dest may be removed self.app_info = mozversion.get_version(binary=dest) self.package_name = self.app_info.get("package_name", "org.mozilla.fennec") self.adb = ADBAndroid() self.adb.uninstall_app(self.package_name) self.adb.install_app(dest) def _start(self, profile=None, addons=(), cmdargs=(), preferences=None): # for now we don't handle addons on the profile for fennec profile = self._create_profile(profile=profile, preferences=preferences) # send the profile on the device self.remote_profile = "/".join([self.adb.test_root, os.path.basename(profile.profile)]) if self.adb.exists(self.remote_profile): self.adb.rm(self.remote_profile, recursive=True) self.adb.push(profile.profile, self.remote_profile) self.adb.launch_fennec(self.package_name, extra_args=["-profile", self.remote_profile]) def _stop(self): self.adb.stop_application(self.package_name) if self.adb.exists(self.remote_profile): self.adb.rm(self.remote_profile, recursive=True) def get_app_info(self): return self.app_info