class AndroidEmulator(object): """ Support running the Android emulator with an AVD from Mozilla test automation. Example usage: emulator = AndroidEmulator() if not emulator.is_running() and emulator.is_available(): if not emulator.check_avd(): warn("this may take a while...") emulator.update_avd() emulator.start() emulator.wait_for_start() emulator.wait() """ def __init__(self, avd_type='4.3', verbose=False, substs=None, device_serial=None): global verbose_logging self.emulator_log = None self.emulator_path = 'emulator' verbose_logging = verbose self.substs = substs self.avd_type = self._get_avd_type(avd_type) self.avd_info = AVD_DICT[self.avd_type] adb_path = _find_sdk_exe(substs, 'adb', False) if not adb_path: adb_path = 'adb' self.dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1, deviceSerial=device_serial) self.dm.default_timeout = 10 _log_debug("Emulator created with type %s" % self.avd_type) def __del__(self): if self.emulator_log: self.emulator_log.close() def is_running(self): """ Returns True if the Android emulator is running. """ for proc in psutil.process_iter(): name = proc.name() # On some platforms, "emulator" may start an emulator with # process name "emulator64-arm" or similar. if name and name.startswith('emulator'): return True return False def is_available(self): """ Returns True if an emulator executable is found. """ found = False emulator_path = _find_sdk_exe(self.substs, 'emulator', True) if emulator_path: self.emulator_path = emulator_path found = True return found def check_avd(self, force=False): """ Determine if the AVD is already installed locally. (This is usually used to determine if update_avd() is likely to require a download; it is a convenient way of determining whether a 'this may take a while' warning is warranted.) Returns True if the AVD is installed. """ avd = os.path.join(EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') if force and os.path.exists(avd): shutil.rmtree(avd) if os.path.exists(avd): _log_debug("AVD found at %s" % avd) return True return False def update_avd(self, force=False): """ If required, update the AVD via tooltool. If the AVD directory is not found, or "force" is requested, download the tooltool manifest associated with the AVD and then invoke tooltool.py on the manifest. tooltool.py will download the required archive (unless already present in the local tooltool cache) and install the AVD. """ avd = os.path.join(EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') if force and os.path.exists(avd): shutil.rmtree(avd) if not os.path.exists(avd): _download_file(TOOLTOOL_URL, 'tooltool.py', EMULATOR_HOME_DIR) url = '%s/%s' % (TRY_URL, self.avd_info.tooltool_manifest) _download_file(url, 'releng.manifest', EMULATOR_HOME_DIR) _tooltool_fetch() self._update_avd_paths() def start(self): """ Launch the emulator. """ def outputHandler(line): self.emulator_log.write("<%s>\n" % line) env = os.environ env['ANDROID_AVD_HOME'] = os.path.join(EMULATOR_HOME_DIR, "avd") command = [ self.emulator_path, "-avd", self.avd_info.name, "-port", "5554" ] if self.avd_info.extra_args: # -enable-kvm option is not valid on OSX if _get_host_platform( ) == 'macosx64' and '-enable-kvm' in self.avd_info.extra_args: self.avd_info.extra_args.remove('-enable-kvm') command += self.avd_info.extra_args log_path = os.path.join(EMULATOR_HOME_DIR, 'emulator.log') self.emulator_log = open(log_path, 'w') _log_debug("Starting the emulator with this command: %s" % ' '.join(command)) _log_debug("Emulator output will be written to '%s'" % log_path) self.proc = ProcessHandler(command, storeOutput=False, processOutputLine=outputHandler, env=env) self.proc.run() _log_debug("Emulator started with pid %d" % int(self.proc.proc.pid)) def wait_for_start(self): """ Verify that the emulator is running, the emulator device is visible to adb, and Android has booted. """ if not self.proc: _log_warning("Emulator not started!") return False if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Waiting for device status...") while (('emulator-5554', 'device') not in self.dm.devices()): time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Device status verified.") _log_debug("Checking that Android has booted...") complete = False while (not complete): output = '' try: output = self.dm.shellCheckOutput( ['getprop', 'sys.boot_completed'], timeout=5) except DMError: # adb not yet responding...keep trying pass if output.strip() == '1': complete = True else: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Android boot status verified.") if not self._verify_emulator(): return False if self.avd_info.uses_sut: if not self._verify_sut(): return False return True def wait(self): """ Wait for the emulator to close. If interrupted, close the emulator. """ try: self.proc.wait() except: if self.proc.poll() is None: self.cleanup() return self.proc.poll() def cleanup(self): """ Close the emulator. """ self.proc.kill(signal.SIGTERM) def get_avd_description(self): """ Return the human-friendly description of this AVD. """ return self.avd_info.description def _update_avd_paths(self): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") ini_file = os.path.join(avd_path, "test-1.ini") ini_file_new = os.path.join(avd_path, self.avd_info.name + ".ini") os.rename(ini_file, ini_file_new) avd_dir = os.path.join(avd_path, "test-1.avd") avd_dir_new = os.path.join(avd_path, self.avd_info.name + ".avd") os.rename(avd_dir, avd_dir_new) self._replace_ini_contents(ini_file_new) def _replace_ini_contents(self, path): with open(path, "r") as f: lines = f.readlines() with open(path, "w") as f: for line in lines: if line.startswith('path='): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") f.write('path=%s/%s.avd\n' % (avd_path, self.avd_info.name)) elif line.startswith('path.rel='): f.write('path.rel=avd/%s.avd\n' % self.avd_info.name) else: f.write(line) def _telnet_cmd(self, telnet, command): _log_debug(">>> " + command) telnet.write('%s\n' % command) result = telnet.read_until('OK', 10) _log_debug("<<< " + result) return result def _verify_emulator(self): telnet_ok = False tn = None while (not telnet_ok): try: tn = telnetlib.Telnet('localhost', self.avd_info.port, 10) if tn is not None: res = tn.read_until('OK', 10) self._telnet_cmd(tn, 'avd status') if self.avd_info.uses_sut: cmd = 'redir add tcp:%s:%s' % \ (str(self.avd_info.sut_port), str(self.avd_info.sut_port)) self._telnet_cmd(tn, cmd) cmd = 'redir add tcp:%s:%s' % \ (str(self.avd_info.sut_port2), str(self.avd_info.sut_port2)) self._telnet_cmd(tn, cmd) self._telnet_cmd(tn, 'redir list') self._telnet_cmd(tn, 'network status') tn.write('quit\n') tn.read_all() telnet_ok = True else: _log_warning("Unable to connect to port %d" % port) except: _log_warning("Trying again after unexpected exception") finally: if tn is not None: tn.close() if not telnet_ok: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False return telnet_ok def _verify_sut(self): sut_ok = False while (not sut_ok): try: tn = telnetlib.Telnet('localhost', self.avd_info.sut_port, 10) if tn is not None: _log_debug("Connected to port %d" % self.avd_info.sut_port) res = tn.read_until('$>', 10) if res.find('$>') == -1: _log_debug("Unexpected SUT response: %s" % res) else: _log_debug("SUT response: %s" % res) sut_ok = True tn.write('quit\n') tn.read_all() except: _log_debug("Caught exception while verifying sutagent") finally: if tn is not None: tn.close() if not sut_ok: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False return sut_ok def _get_avd_type(self, requested): if requested in AVD_DICT.keys(): return requested if self.substs: if not self.substs['TARGET_CPU'].startswith('arm'): return 'x86' if self.substs['MOZ_ANDROID_MIN_SDK_VERSION'] == '9': return '2.3' return '4.3'
class AndroidEmulator(object): """ Support running the Android emulator with an AVD from Mozilla test automation. Example usage: emulator = AndroidEmulator() if not emulator.is_running() and emulator.is_available(): if not emulator.check_avd(): warn("this may take a while...") emulator.update_avd() emulator.start() emulator.wait_for_start() emulator.wait() """ def __init__(self, avd_type='4.3', verbose=False, substs=None): global verbose_logging self.emulator_log = None self.emulator_path = 'emulator' verbose_logging = verbose self.substs = substs self.avd_type = self._get_avd_type(avd_type) self.avd_info = AVD_DICT[self.avd_type] adb_path = self._find_sdk_exe('adb', False) if not adb_path: adb_path = 'adb' self.dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1) self.dm.default_timeout = 10 _log_debug("Emulator created with type %s" % self.avd_type) def __del__(self): if self.emulator_log: self.emulator_log.close() def is_running(self): """ Returns True if the Android emulator is running. """ for proc in psutil.process_iter(): name = proc.name() # On some platforms, "emulator" may start an emulator with # process name "emulator64-arm" or similar. if name and name.startswith('emulator'): return True return False def is_available(self): """ Returns True if an emulator executable is found. """ found = False emulator_path = self._find_sdk_exe('emulator', True) if emulator_path: self.emulator_path = emulator_path found = True return found def check_avd(self, force=False): """ Determine if the AVD is already installed locally. (This is usually used to determine if update_avd() is likely to require a download; it is a convenient way of determining whether a 'this may take a while' warning is warranted.) Returns True if the AVD is installed. """ avd = os.path.join( EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') if force and os.path.exists(avd): shutil.rmtree(avd) if os.path.exists(avd): _log_debug("AVD found at %s" % avd) return True return False def update_avd(self, force=False): """ If required, update the AVD via tooltool. If the AVD directory is not found, or "force" is requested, download the tooltool manifest associated with the AVD and then invoke tooltool.py on the manifest. tooltool.py will download the required archive (unless already present in the local tooltool cache) and install the AVD. """ avd = os.path.join( EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') if force and os.path.exists(avd): shutil.rmtree(avd) if not os.path.exists(avd): _download_file(TOOLTOOL_URL, 'tooltool.py', EMULATOR_HOME_DIR) url = '%s/%s' % (TRY_URL, self.avd_info.tooltool_manifest) _download_file(url, 'releng.manifest', EMULATOR_HOME_DIR) _tooltool_fetch() self._update_avd_paths() def start(self): """ Launch the emulator. """ def outputHandler(line): self.emulator_log.write("<%s>\n" % line) env = os.environ env['ANDROID_AVD_HOME'] = os.path.join(EMULATOR_HOME_DIR, "avd") command = [self.emulator_path, "-avd", self.avd_info.name, "-port", "5554"] if self.avd_info.extra_args: command += self.avd_info.extra_args log_path = os.path.join(EMULATOR_HOME_DIR, 'emulator.log') self.emulator_log = open(log_path, 'w') _log_debug("Starting the emulator with this command: %s" % ' '.join(command)) _log_debug("Emulator output will be written to '%s'" % log_path) self.proc = ProcessHandler( command, storeOutput=False, processOutputLine=outputHandler, env=env) self.proc.run() _log_debug("Emulator started with pid %d" % int(self.proc.proc.pid)) def wait_for_start(self): """ Verify that the emulator is running, the emulator device is visible to adb, and Android has booted. """ if not self.proc: _log_warning("Emulator not started!") return False if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Waiting for device status...") while(('emulator-5554', 'device') not in self.dm.devices()): time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Device status verified.") _log_debug("Checking that Android has booted...") complete = False while(not complete): output = '' try: output = self.dm.shellCheckOutput( ['getprop', 'sys.boot_completed'], timeout=5) except DMError: # adb not yet responding...keep trying pass if output.strip() == '1': complete = True else: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False _log_debug("Android boot status verified.") if not self._verify_emulator(): return False if self.avd_info.uses_sut: if not self._verify_sut(): return False return True def wait(self): """ Wait for the emulator to close. If interrupted, close the emulator. """ try: self.proc.wait() except: if self.proc.poll() is None: self.cleanup() return self.proc.poll() def cleanup(self): """ Close the emulator. """ self.proc.kill(signal.SIGTERM) def get_avd_description(self): """ Return the human-friendly description of this AVD. """ return self.avd_info.description def _update_avd_paths(self): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") ini_file = os.path.join(avd_path, "test-1.ini") ini_file_new = os.path.join(avd_path, self.avd_info.name + ".ini") os.rename(ini_file, ini_file_new) avd_dir = os.path.join(avd_path, "test-1.avd") avd_dir_new = os.path.join(avd_path, self.avd_info.name + ".avd") os.rename(avd_dir, avd_dir_new) self._replace_ini_contents(ini_file_new) def _replace_ini_contents(self, path): with open(path, "r") as f: lines = f.readlines() with open(path, "w") as f: for line in lines: if line.startswith('path='): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") f.write('path=%s/%s.avd\n' % (avd_path, self.avd_info.name)) elif line.startswith('path.rel='): f.write('path.rel=avd/%s.avd\n' % self.avd_info.name) else: f.write(line) def _telnet_cmd(self, telnet, command): _log_debug(">>> " + command) telnet.write('%s\n' % command) result = telnet.read_until('OK', 10) _log_debug("<<< " + result) return result def _verify_emulator(self): telnet_ok = False tn = None while(not telnet_ok): try: tn = telnetlib.Telnet('localhost', self.avd_info.port, 10) if tn is not None: res = tn.read_until('OK', 10) self._telnet_cmd(tn, 'avd status') if self.avd_info.uses_sut: cmd = 'redir add tcp:%s:%s' % \ (str(self.avd_info.sut_port), str(self.avd_info.sut_port)) self._telnet_cmd(tn, cmd) cmd = 'redir add tcp:%s:%s' % \ (str(self.avd_info.sut_port2), str(self.avd_info.sut_port2)) self._telnet_cmd(tn, cmd) self._telnet_cmd(tn, 'redir list') self._telnet_cmd(tn, 'network status') tn.write('quit\n') tn.read_all() telnet_ok = True else: _log_warning("Unable to connect to port %d" % port) except: _log_warning("Trying again after unexpected exception") finally: if tn is not None: tn.close() if not telnet_ok: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False return telnet_ok def _verify_sut(self): sut_ok = False while(not sut_ok): try: tn = telnetlib.Telnet('localhost', self.avd_info.sut_port, 10) if tn is not None: _log_debug( "Connected to port %d" % self.avd_info.sut_port) res = tn.read_until('$>', 10) if res.find('$>') == -1: _log_debug("Unexpected SUT response: %s" % res) else: _log_debug("SUT response: %s" % res) sut_ok = True tn.write('quit\n') tn.read_all() except: _log_debug("Caught exception while verifying sutagent") finally: if tn is not None: tn.close() if not sut_ok: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False return sut_ok def _get_avd_type(self, requested): if requested in AVD_DICT.keys(): return requested if self.substs: if not self.substs['TARGET_CPU'].startswith('arm'): return 'x86' if self.substs['MOZ_ANDROID_MIN_SDK_VERSION'] == '9': return '2.3' return '4.3' def _find_sdk_exe(self, exe, tools): if tools: subdir = 'tools' var = 'ANDROID_TOOLS' else: subdir = 'platform-tools' var = 'ANDROID_PLATFORM_TOOLS' found = False # Can exe be found in the Android SDK? try: android_sdk_root = os.environ['ANDROID_SDK_ROOT'] exe_path = os.path.join( android_sdk_root, subdir, exe) if os.path.exists(exe_path): found = True else: _log_debug( "Unable to find executable at %s" % exe_path) except KeyError: _log_debug("ANDROID_SDK_ROOT not set") if not found and self.substs: # Can exe be found in ANDROID_TOOLS/ANDROID_PLATFORM_TOOLS? try: exe_path = os.path.join( self.substs[var], exe) if os.path.exists(exe_path): found = True else: _log_debug( "Unable to find executable at %s" % exe_path) except KeyError: _log_debug("%s not set" % var) if not found: # Can exe be found in the default bootstrap location? mozbuild_path = os.environ.get('MOZBUILD_STATE_PATH', os.path.expanduser(os.path.join('~', '.mozbuild'))) exe_path = os.path.join( mozbuild_path, 'android-sdk-linux', subdir, exe) if os.path.exists(exe_path): found = True else: _log_debug( "Unable to find executable at %s" % exe_path) if not found: # Is exe on PATH? exe_path = find_executable(exe) if exe_path: found = True else: _log_debug("Unable to find executable on PATH") if found: _log_debug("%s found at %s" % (exe, exe_path)) else: exe_path = None return exe_path
class AndroidEmulator(object): """ Support running the Android emulator with an AVD from Mozilla test automation. Example usage: emulator = AndroidEmulator() if not emulator.is_running() and emulator.is_available(): if not emulator.check_avd(): warn("this may take a while...") emulator.update_avd() emulator.start() emulator.wait_for_start() emulator.wait() """ def __init__(self, avd_type='4.3', verbose=False, substs=None, device_serial=None): global verbose_logging self.emulator_log = None self.emulator_path = 'emulator' verbose_logging = verbose self.substs = substs self.avd_type = self._get_avd_type(avd_type) self.avd_info = AVD_DICT[self.avd_type] self.gpu = True self.restarted = False adb_path = _find_sdk_exe(substs, 'adb', False) if not adb_path: adb_path = 'adb' self.dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1, deviceSerial=device_serial) self.dm.default_timeout = 10 _log_debug("Running on %s" % platform.platform()) _log_debug("Emulator created with type %s" % self.avd_type) def __del__(self): if self.emulator_log: self.emulator_log.close() def is_running(self): """ Returns True if the Android emulator is running. """ for proc in psutil.process_iter(): name = proc.name() # On some platforms, "emulator" may start an emulator with # process name "emulator64-arm" or similar. if name and name.startswith('emulator'): return True return False def is_available(self): """ Returns True if an emulator executable is found. """ found = False emulator_path = _find_sdk_exe(self.substs, 'emulator', True) if emulator_path: self.emulator_path = emulator_path found = True return found def check_avd(self, force=False): """ Determine if the AVD is already installed locally. (This is usually used to determine if update_avd() is likely to require a download; it is a convenient way of determining whether a 'this may take a while' warning is warranted.) Returns True if the AVD is installed. """ avd = os.path.join( EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') if force and os.path.exists(avd): shutil.rmtree(avd) if os.path.exists(avd): _log_debug("AVD found at %s" % avd) return True return False def update_avd(self, force=False): """ If required, update the AVD via tooltool. If the AVD directory is not found, or "force" is requested, download the tooltool manifest associated with the AVD and then invoke tooltool.py on the manifest. tooltool.py will download the required archive (unless already present in the local tooltool cache) and install the AVD. """ avd = os.path.join( EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.avd') ini_file = os.path.join( EMULATOR_HOME_DIR, 'avd', self.avd_info.name + '.ini') if force and os.path.exists(avd): shutil.rmtree(avd) if not os.path.exists(avd): if os.path.exists(ini_file): os.remove(ini_file) path = self.avd_info.tooltool_manifest _get_tooltool_manifest(self.substs, path, EMULATOR_HOME_DIR, 'releng.manifest') _tooltool_fetch() self._update_avd_paths() def start(self): """ Launch the emulator. """ if os.path.exists(EMULATOR_AUTH_FILE): os.remove(EMULATOR_AUTH_FILE) _log_debug("deleted %s" % EMULATOR_AUTH_FILE) # create an empty auth file to disable emulator authentication auth_file = open(EMULATOR_AUTH_FILE, 'w') auth_file.close() def outputHandler(line): self.emulator_log.write("<%s>\n" % line) if "Invalid value for -gpu" in line or "Invalid GPU mode" in line: self.gpu = False env = os.environ env['ANDROID_AVD_HOME'] = os.path.join(EMULATOR_HOME_DIR, "avd") command = [self.emulator_path, "-avd", self.avd_info.name, "-port", "5554"] if self.gpu: command += ['-gpu', 'swiftshader'] if self.avd_info.extra_args: # -enable-kvm option is not valid on OSX if _get_host_platform() == 'macosx64' and '-enable-kvm' in self.avd_info.extra_args: self.avd_info.extra_args.remove('-enable-kvm') command += self.avd_info.extra_args log_path = os.path.join(EMULATOR_HOME_DIR, 'emulator.log') self.emulator_log = open(log_path, 'w') _log_debug("Starting the emulator with this command: %s" % ' '.join(command)) _log_debug("Emulator output will be written to '%s'" % log_path) self.proc = ProcessHandler( command, storeOutput=False, processOutputLine=outputHandler, env=env) self.proc.run() _log_debug("Emulator started with pid %d" % int(self.proc.proc.pid)) def wait_for_start(self): """ Verify that the emulator is running, the emulator device is visible to adb, and Android has booted. """ if not self.proc: _log_warning("Emulator not started!") return False if self.check_completed(): return False _log_debug("Waiting for device status...") while(('emulator-5554', 'device') not in self.dm.devices()): time.sleep(10) if self.check_completed(): return False _log_debug("Device status verified.") _log_debug("Checking that Android has booted...") complete = False while(not complete): output = '' try: output = self.dm.shellCheckOutput( ['getprop', 'sys.boot_completed'], timeout=5) except DMError: # adb not yet responding...keep trying pass if output.strip() == '1': complete = True else: time.sleep(10) if self.check_completed(): return False _log_debug("Android boot status verified.") if not self._verify_emulator(): return False if self.avd_info.x86: _log_info("Running the x86 emulator; be sure to install an x86 APK!") else: _log_info("Running the arm emulator; be sure to install an arm APK!") return True def check_completed(self): if self.proc.proc.poll() is not None: if not self.gpu and not self.restarted: _log_warning("Emulator failed to start. Your emulator may be out of date.") _log_warning("Trying to restart the emulator without -gpu argument.") self.restarted = True self.start() return False _log_warning("Emulator has already completed!") log_path = os.path.join(EMULATOR_HOME_DIR, 'emulator.log') _log_warning("See log at %s and/or use --verbose for more information." % log_path) return True return False def wait(self): """ Wait for the emulator to close. If interrupted, close the emulator. """ try: self.proc.wait() except: if self.proc.poll() is None: self.cleanup() return self.proc.poll() def cleanup(self): """ Close the emulator. """ self.proc.kill(signal.SIGTERM) def get_avd_description(self): """ Return the human-friendly description of this AVD. """ return self.avd_info.description def _update_avd_paths(self): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") ini_file = os.path.join(avd_path, "test-1.ini") ini_file_new = os.path.join(avd_path, self.avd_info.name + ".ini") os.rename(ini_file, ini_file_new) avd_dir = os.path.join(avd_path, "test-1.avd") avd_dir_new = os.path.join(avd_path, self.avd_info.name + ".avd") os.rename(avd_dir, avd_dir_new) self._replace_ini_contents(ini_file_new) def _replace_ini_contents(self, path): with open(path, "r") as f: lines = f.readlines() with open(path, "w") as f: for line in lines: if line.startswith('path='): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") f.write('path=%s/%s.avd\n' % (avd_path, self.avd_info.name)) elif line.startswith('path.rel='): f.write('path.rel=avd/%s.avd\n' % self.avd_info.name) else: f.write(line) def _telnet_cmd(self, telnet, command): _log_debug(">>> " + command) telnet.write('%s\n' % command) result = telnet.read_until('OK', 10) _log_debug("<<< " + result) return result def _verify_emulator(self): telnet_ok = False tn = None while(not telnet_ok): try: tn = telnetlib.Telnet('localhost', 5554, 10) if tn is not None: tn.read_until('OK', 10) self._telnet_cmd(tn, 'avd status') self._telnet_cmd(tn, 'redir list') self._telnet_cmd(tn, 'network status') tn.write('quit\n') tn.read_all() telnet_ok = True else: _log_warning("Unable to connect to port 5554") except: _log_warning("Trying again after unexpected exception") finally: if tn is not None: tn.close() if not telnet_ok: time.sleep(10) if self.proc.proc.poll() is not None: _log_warning("Emulator has already completed!") return False return telnet_ok def _get_avd_type(self, requested): if requested in AVD_DICT.keys(): return requested if self.substs: if not self.substs['TARGET_CPU'].startswith('arm'): return 'x86' return '4.3'
class AndroidEmulator(object): """ Support running the Android emulator with an AVD from Mozilla test automation. Example usage: emulator = AndroidEmulator() if not emulator.is_running() and emulator.is_available(): if not emulator.check_avd(): warn("this may take a while...") emulator.update_avd() emulator.start() emulator.wait_for_start() emulator.wait() """ def __init__(self, avd_type="4.3", verbose=False, substs=None): self.emulator_log = None self.emulator_path = "emulator" self.verbose = verbose self.substs = substs self.avd_type = self._get_avd_type(avd_type) self.avd_info = AVD_DICT[self.avd_type] adb_path = self._find_sdk_exe("adb", False) if not adb_path: adb_path = "adb" self.dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1) self.dm.default_timeout = 10 self._log_debug("Emulator created with type %s" % self.avd_type) def __del__(self): if self.emulator_log: self.emulator_log.close() def is_running(self): """ Returns True if the Android emulator is running. """ for proc in psutil.process_iter(): name = proc.name() # On some platforms, "emulator" may start an emulator with # process name "emulator64-arm" or similar. if name and name.startswith("emulator"): return True return False def is_available(self): """ Returns True if an emulator executable is found. """ found = False emulator_path = self._find_sdk_exe("emulator", True) if emulator_path: self.emulator_path = emulator_path found = True return found def check_avd(self, force=False): """ Determine if the AVD is already installed locally. (This is usually used to determine if update_avd() is likely to require a download; it is a convenient way of determining whether a 'this may take a while' warning is warranted.) Returns True if the AVD is installed. """ avd = os.path.join(EMULATOR_HOME_DIR, "avd", self.avd_info.name + ".avd") if force and os.path.exists(avd): shutil.rmtree(avd) if os.path.exists(avd): self._log_debug("AVD found at %s" % avd) return True return False def update_avd(self, force=False): """ If required, update the AVD via tooltool. If the AVD directory is not found, or "force" is requested, download the tooltool manifest associated with the AVD and then invoke tooltool.py on the manifest. tooltool.py will download the required archive (unless already present in the local tooltool cache) and install the AVD. """ avd = os.path.join(EMULATOR_HOME_DIR, "avd", self.avd_info.name + ".avd") if force and os.path.exists(avd): shutil.rmtree(avd) if not os.path.exists(avd): self._fetch_tooltool() self._fetch_tooltool_manifest() self._tooltool_fetch() self._update_avd_paths() def start(self): """ Launch the emulator. """ def outputHandler(line): self.emulator_log.write("<%s>\n" % line) env = os.environ env["ANDROID_AVD_HOME"] = os.path.join(EMULATOR_HOME_DIR, "avd") command = [self.emulator_path, "-avd", self.avd_info.name, "-port", "5554"] if self.avd_info.extra_args: command += self.avd_info.extra_args log_path = os.path.join(EMULATOR_HOME_DIR, "emulator.log") self.emulator_log = open(log_path, "w") self._log_debug("Starting the emulator with this command: %s" % " ".join(command)) self._log_debug("Emulator output will be written to '%s'" % log_path) self.proc = ProcessHandler(command, storeOutput=False, processOutputLine=outputHandler, env=env) self.proc.run() self._log_debug("Emulator started with pid %d" % int(self.proc.proc.pid)) def wait_for_start(self): """ Verify that the emulator is running, the emulator device is visible to adb, and Android has booted. """ if not self.proc: self._log_warning("Emulator not started!") return False if self.proc.proc.poll() is not None: self._log_warning("Emulator has already completed!") return False self._log_debug("Waiting for device status...") while ("emulator-5554", "device") not in self.dm.devices(): time.sleep(10) if self.proc.proc.poll() is not None: self._log_warning("Emulator has already completed!") return False self._log_debug("Device status verified.") self._log_debug("Checking that Android has booted...") complete = False while not complete: output = "" try: output = self.dm.shellCheckOutput(["getprop", "sys.boot_completed"], timeout=5) except DMError: # adb not yet responding...keep trying pass if output.strip() == "1": complete = True else: time.sleep(10) if self.proc.proc.poll() is not None: self._log_warning("Emulator has already completed!") return False self._log_debug("Android boot status verified.") if not self._verify_emulator(): return False if self.avd_info.uses_sut: if not self._verify_sut(): return False return True def wait(self): """ Wait for the emulator to close. If interrupted, close the emulator. """ try: self.proc.wait() except: if self.proc.poll() is None: self.cleanup() return self.proc.poll() def cleanup(self): """ Close the emulator. """ self.proc.kill(signal.SIGTERM) def get_avd_description(self): """ Return the human-friendly description of this AVD. """ return self.avd_info.description def _log_debug(self, text): if self.verbose: print "DEBUG: %s" % text def _log_warning(self, text): print "WARNING: %s" % text def _fetch_tooltool(self): self._download_file(TOOLTOOL_URL, "tooltool.py", EMULATOR_HOME_DIR) def _fetch_tooltool_manifest(self): url = "https://hg.mozilla.org/%s/raw-file/%s/%s" % ("try", "default", self.avd_info.tooltool_manifest) self._download_file(url, "releng.manifest", EMULATOR_HOME_DIR) def _tooltool_fetch(self): def outputHandler(line): self._log_debug(line) command = ["python", "tooltool.py", "fetch", "-m", "releng.manifest"] proc = ProcessHandler(command, processOutputLine=outputHandler, storeOutput=False, cwd=EMULATOR_HOME_DIR) proc.run() try: proc.wait() except: if proc.poll() is None: proc.kill(signal.SIGTERM) def _update_avd_paths(self): avd_path = os.path.join(EMULATOR_HOME_DIR, "avd") ini_file = os.path.join(avd_path, "test-1.ini") ini_file_new = os.path.join(avd_path, self.avd_info.name + ".ini") os.rename(ini_file, ini_file_new) avd_dir = os.path.join(avd_path, "test-1.avd") avd_dir_new = os.path.join(avd_path, self.avd_info.name + ".avd") os.rename(avd_dir, avd_dir_new) self._replace_ini_contents(ini_file_new) def _download_file(self, url, filename, path): f = urllib2.urlopen(url) if not os.path.isdir(path): try: os.makedirs(path) except Exception, e: self._log_warning(str(e)) return False local_file = open(os.path.join(path, filename), "wb") local_file.write(f.read()) local_file.close() self._log_debug("Downloaded %s to %s/%s" % (url, path, filename)) return True
root=True) output = str(out.getvalue()).rstrip().splitlines() out.close() self.assertEquals(output, ['Mozilla', '/']) def test_port_forwarding(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 0)) port = s.getsockname()[1] s.close() # If successful then no exception is raised self.dm.forward("tcp:%s" % port, "tcp:2828") def test_port_forwarding_error(self): self.assertRaises(DMError, self.dm.forward, "", "") if __name__ == '__main__': dm = DeviceManagerADB() if not dm.devices(): print "There are no connected adb devices" sys.exit(1) if find_mount_permissions(dm, "/system") == "rw": print "We've found out that /system is mounted as 'rw'. This is because the command " \ "'adb remount' has been run before running this test case. Please reboot the device " \ "and try again." sys.exit(1) unittest.main()
def configure_devices(self): """ Ensure devices.ini is set up. """ keep_going = True device_ini = os.path.join(self.config['base-dir'], 'devices.ini') if os.path.exists(device_ini): response = raw_input( "Use existing device configuration at %s? (Y/n) " % device_ini).strip() if 'n' not in response.lower(): self.build_obj.log( logging.INFO, "autophone", {}, "Using device configuration at %s" % device_ini) return keep_going keep_going = False self.build_obj.log( logging.INFO, "autophone", {}, "You must configure at least one Android device " "before running autophone.") response = raw_input("Configure devices now? (Y/n) ").strip() if response.lower().startswith('y') or response == '': response = raw_input( "Connect your rooted Android test device(s) with usb and press Enter " ) adb_path = 'adb' try: if os.path.exists(self.build_obj.substs["ADB"]): adb_path = self.build_obj.substs["ADB"] except: if self.verbose: self.build_obj.log(logging.ERROR, "autophone", {}, str(sys.exc_info()[0])) # No build environment? try: adb_path = which.which('adb') except which.WhichError: adb_path = raw_input( "adb not found. Enter path to adb: ").strip() if self.verbose: print("Using adb at %s" % adb_path) dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1) device_index = 1 try: with open(os.path.join(self.config['base-dir'], 'devices.ini'), 'w') as f: for device in dm.devices(): serial = device[0] if self.verify_device(adb_path, serial): f.write("[device-%d]\nserialno=%s\n" % (device_index, serial)) device_index += 1 self.build_obj.log( logging.INFO, "autophone", {}, "Added '%s' to device configuration." % serial) keep_going = True else: self.build_obj.log( logging.WARNING, "autophone", {}, "Device '%s' is not rooted - skipping" % serial) except: self.build_obj.log( logging.ERROR, "autophone", {}, "Failed to get list of connected Android devices.") if self.verbose: self.build_obj.log(logging.ERROR, "autophone", {}, str(sys.exc_info()[0])) keep_going = False if device_index <= 1: self.build_obj.log( logging.ERROR, "autophone", {}, "No devices configured! (Can you see your rooted test device(s)" " in 'adb devices'?") keep_going = False if keep_going: self.config['devices-configured'] = True return keep_going
def test_remount(self): self.assertEquals(self.dm.remount(), 0) def test_list_devices(self): self.assertEquals(len(list(self.dm.devices())), 1) def test_shell(self): out = StringIO() self.dm.shell(["echo", "$COMPANY", ";", "pwd"], out, env={"COMPANY":"Mozilla"}, cwd="/", timeout=4, root=True) output = str(out.getvalue()).rstrip().splitlines() out.close() self.assertEquals(output, ['Mozilla', '/']) def test_reboot(self): # We are not running the reboot function because it takes too long #self.dm.reboot(wait=True) pass def test_port_forwarding(self): # I don't really know how to test this properly self.assertEquals(self.dm.forward("tcp:2828", "tcp:2828"), 0) if __name__ == '__main__': dm = DeviceManagerADB() if not dm.devices(): print "There are no connected adb devices" sys.exit(1) unittest.main()
def configure_devices(self): """ Ensure devices.ini is set up. """ keep_going = True device_ini = os.path.join(self.config['base-dir'], 'devices.ini') if os.path.exists(device_ini): response = raw_input( "Use existing device configuration at %s? (Y/n) " % device_ini).strip() if not 'n' in response.lower(): self.build_obj.log(logging.INFO, "autophone", {}, "Using device configuration at %s" % device_ini) return keep_going keep_going = False self.build_obj.log(logging.INFO, "autophone", {}, "You must configure at least one Android device before running autophone.") response = raw_input( "Configure devices now? (Y/n) ").strip() if response.lower().startswith('y') or response == '': response = raw_input( "Connect your rooted Android test device(s) with usb and press Enter ") adb_path = 'adb' try: if os.path.exists(self.build_obj.substs["ADB"]): adb_path = self.build_obj.substs["ADB"] except: if self.verbose: self.build_obj.log(logging.ERROR, "autophone", {}, str(sys.exc_info()[0])) # No build environment? try: adb_path = which.which('adb') except which.WhichError: adb_path = raw_input( "adb not found. Enter path to adb: ").strip() if self.verbose: print("Using adb at %s" % adb_path) dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1) device_index = 1 try: with open(os.path.join(self.config['base-dir'], 'devices.ini'), 'w') as f: for device in dm.devices(): serial = device[0] if self.verify_device(adb_path, serial): f.write("[device-%d]\nserialno=%s\n" % (device_index, serial)) device_index += 1 self.build_obj.log(logging.INFO, "autophone", {}, "Added '%s' to device configuration." % serial) keep_going = True else: self.build_obj.log(logging.WARNING, "autophone", {}, "Device '%s' is not rooted - skipping" % serial) except: self.build_obj.log(logging.ERROR, "autophone", {}, "Failed to get list of connected Android devices.") if self.verbose: self.build_obj.log(logging.ERROR, "autophone", {}, str(sys.exc_info()[0])) keep_going = False if device_index <= 1: self.build_obj.log(logging.ERROR, "autophone", {}, "No devices configured! (Can you see your rooted test device(s) in 'adb devices'?") keep_going = False if keep_going: self.config['devices-configured'] = True return keep_going