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 = ADBDevice(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_dir: raise Exception( "--coverage-output-dir is required when using --enable-coverage" ) if self.options.coverage: self.remote_coverage_output_file = posixpath.join( self.device.test_root, 'junit-coverage.ec') self.coverage_output_file = os.path.join( self.options.coverage_output_dir, 'junit-coverage.ec') self.server_init() self.cleanup() self.device.clear_logcat() self.build_profile() self.startServers(self.options, debuggerInfo=None) self.log.debug("Servers started")
def setup_adb_device(self): if self.device is None: self.device = ADBDevice(verbose=True) tune_performance(self.device, log=LOG) self.clear_app_data() self.set_debug_app_flag()
def setup_adb_device(self): if self.device is None: self.device = ADBDevice(verbose=True) if not self.config.get("disable_perf_tuning", False): tune_performance(self.device, log=LOG) self.clear_app_data() self.set_debug_app_flag()
def get_browser_meta(self): """Returns the browser name and version in a tuple (name, version). Uses mozversion as the primary method to get this meta data and for android this is the only method which exists to get this data. With android, we use the installerpath attribute to determine this and this only works with Firefox browsers. """ browser_name = None browser_version = None if self.config["app"] in self.firefox_android_apps: try: meta = mozversion.get_version(binary=self.installerpath) browser_name = meta.get("application_name") browser_version = meta.get("application_version") except Exception as e: LOG.warning( "Failed to get android browser meta data through mozversion: %s-%s" % (e.__class__.__name__, e)) if self.config["app"] == "chrome-m": # We absolutely need to determine the chrome # version here so that we can select the correct # chromedriver for browsertime from mozdevice import ADBDevice device = ADBDevice(verbose=True) binary = "com.android.chrome" pkg_info = device.shell_output("dumpsys package %s" % binary) version_matcher = re.compile(r".*versionName=([\d.]+)") for line in pkg_info.split("\n"): match = version_matcher.match(line) if match: browser_version = match.group(1) browser_name = self.config["app"] # First one found is the non-system # or latest version. break if not browser_version: raise Exception( "Could not determine version for Google Chrome for Android" ) if not browser_name: LOG.warning("Could not find a browser name") else: LOG.info("Browser name: %s" % browser_name) if not browser_version: LOG.warning("Could not find a browser version") else: LOG.info("Browser version: %s" % browser_version) return (browser_name, browser_version)
def get_device(self, adb_path, device_serial): # Create a mozdevice.ADBDevice 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 = ADBDevice(adb=adb_path, device=device_serial) devices[device_serial] = device return device
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 = ADBDevice(adb=adb_path, verbose=verbose_logging, device=device_serial) devices[device_serial] = device return device
def __init__(self, options, progs): cppunittests.CPPUnitTests.__init__(self) self.options = options self.device = ADBDevice(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 = ADBDevice(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 setup_adb_device(self): if self.device is None: self.device = ADBDevice(verbose=True) tune_performance(self.device, log=LOG) LOG.info("creating remote root folder for raptor: %s" % self.remote_test_root) self.device.rm(self.remote_test_root, force=True, recursive=True) self.device.mkdir(self.remote_test_root) self.device.chmod(self.remote_test_root, recursive=True, root=True) self.clear_app_data() self.set_debug_app_flag()
def setup_adb_device(self): if self.device is None: self.device = ADBDevice(verbose=True) if not self.config.get("disable_perf_tuning", False): tune_performance(self.device, log=LOG) if self.config['power_test']: disable_charging(self.device) LOG.info("creating remote root folder for raptor: %s" % self.remote_test_root) self.device.rm(self.remote_test_root, force=True, recursive=True) self.device.mkdir(self.remote_test_root) self.device.chmod(self.remote_test_root, recursive=True, root=True) self.clear_app_data() self.set_debug_app_flag()
def __call__(self, metadata): self.app_name = self.get_arg("android-app-name") self.android_activity = self.get_arg("android-activity") self.metadata = metadata try: self.device = ADBDevice(verbose=True, timeout=30) except (ADBError, AttributeError) as e: self.error("Could not connect to the phone. Is it connected?") raise DeviceError(str(e)) # install APKs for apk in self.get_arg("android-install-apk"): self.info("Installing %s" % apk) if apk in _PERMALINKS: apk = _PERMALINKS[apk] if apk.startswith("http"): with tempfile.TemporaryDirectory() as tmpdirname: target = Path(tmpdirname, "target.apk") self.info("Downloading %s" % apk) download_file(apk, target) self.info("Installing downloaded APK") self.device.install_app(str(target), replace=True) else: self.device.install_app(apk, replace=True) self.info("Done.") # checking that the app is installed if not self.device.is_app_installed(self.app_name): raise Exception("%s is not installed" % self.app_name) # set up default activity with the app name if none given if self.android_activity is None: # guess the activity, given the app if "fenix" in self.app_name: self.android_activity = "org.mozilla.fenix.IntentReceiverActivity" elif "geckoview_example" in self.app_name: self.android_activity = ( "org.mozilla.geckoview_example.GeckoViewActivity") self.set_arg("android_activity", self.android_activity) self.info("Android environment:") self.info("- Application name: %s" % self.app_name) self.info("- Activity: %s" % self.android_activity) self.info("- Intent: %s" % self.get_arg("android_intent")) return metadata
def run_tests_remote(tests, num_tests, prefix, options, slog): # Setup device with everything needed to run our tests. from mozdevice import ADBDevice, ADBError, ADBTimeoutError try: device = ADBDevice(device=options.device_serial, test_root=options.remote_test_root) init_remote_dir(device, options.remote_test_root) # Update the test root to point to our test directory. jit_tests_dir = posixpath.join(options.remote_test_root, 'jit-tests') options.remote_test_root = posixpath.join(jit_tests_dir, 'jit-tests') # Push js shell and libraries. init_remote_dir(device, jit_tests_dir) push_libs(options, device) push_progs(options, device, [prefix[0]]) device.chmod(options.remote_test_root, recursive=True, root=True) jtd_tests = posixpath.join(jit_tests_dir, 'tests') init_remote_dir(device, jtd_tests) device.push(JS_TESTS_DIR, jtd_tests, timeout=600) device.chmod(jtd_tests, recursive=True, root=True) device.push(os.path.dirname(TEST_DIR), options.remote_test_root, timeout=600) device.chmod(options.remote_test_root, recursive=True, root=True) prefix[0] = os.path.join(options.remote_test_root, 'js') except (ADBError, ADBTimeoutError): print("TEST-UNEXPECTED-FAIL | jit_test.py" + " : Device initialization failed") raise # Run all tests. pb = create_progressbar(num_tests, options) try: gen = get_remote_results(tests, device, prefix, options) ok = process_test_results(gen, num_tests, pb, options, slog) except (ADBError, ADBTimeoutError): print("TEST-UNEXPECTED-FAIL | jit_test.py" + " : Device error during test") raise return ok
def run_tests_remote(tests, num_tests, prefix, options, slog): # Setup device with everything needed to run our tests. from mozdevice import ADBDevice device = ADBDevice(device=options.device_serial, test_root=options.remote_test_root) init_remote_dir(device, options.remote_test_root) # Update the test root to point to our test directory. jit_tests_dir = posixpath.join(options.remote_test_root, 'jit-tests') options.remote_test_root = posixpath.join(jit_tests_dir, 'jit-tests') # Push js shell and libraries. init_remote_dir(device, jit_tests_dir) push_libs(options, device) push_progs(options, device, [prefix[0]]) device.chmod(options.remote_test_root, recursive=True, root=True) JitTest.CacheDir = posixpath.join(options.remote_test_root, '.js-cache') init_remote_dir(device, JitTest.CacheDir) jtd_tests = posixpath.join(jit_tests_dir, 'tests') init_remote_dir(device, jtd_tests) device.push(JS_TESTS_DIR, jtd_tests, timeout=600) device.chmod(jtd_tests, recursive=True, root=True) device.push(os.path.dirname(TEST_DIR), options.remote_test_root, timeout=600) device.chmod(options.remote_test_root, recursive=True, root=True) prefix[0] = os.path.join(options.remote_test_root, 'js') # Run all tests. pb = create_progressbar(num_tests, options) gen = get_remote_results(tests, device, prefix, options) ok = process_test_results(gen, num_tests, pb, options, slog) return ok
def init_device(options): # Initialize the device global DEVICE assert options.remote and options.js_shell if DEVICE is not None: return DEVICE from mozdevice import ADBDevice, ADBError, ADBTimeoutError try: if not options.local_lib: # if not specified, use the local directory containing # the js binary to find the necessary libraries. options.local_lib = posixpath.dirname(options.js_shell) DEVICE = ADBDevice(device=options.device_serial, test_root=options.remote_test_root) init_remote_dir(DEVICE, options.remote_test_root) bin_dir = posixpath.join(options.remote_test_root, 'bin') tests_dir = posixpath.join(options.remote_test_root, 'tests') # Push js shell and libraries. init_remote_dir(DEVICE, tests_dir) init_remote_dir(DEVICE, bin_dir) push_libs(options, DEVICE, bin_dir) push_progs(options, DEVICE, [options.js_shell], bin_dir) # update options.js_shell to point to the js binary on the device options.js_shell = os.path.join(bin_dir, 'js') return DEVICE except (ADBError, ADBTimeoutError): print("TEST-UNEXPECTED-FAIL | remote.py : Device initialization failed") raise
def run_tests(self): """ Generate the PGO profile data """ from mozhttpd import MozHttpd from mozprofile import Preferences from mozdevice import ADBDevice, ADBTimeoutError from six import string_types from marionette_driver.marionette import Marionette app = self.query_package_name() IP = '10.0.2.2' PORT = 8888 PATH_MAPPINGS = { '/js-input/webkit/PerformanceTests': 'third_party/webkit/PerformanceTests', } dirs = self.query_abs_dirs() topsrcdir = dirs['abs_src_dir'] adb = self.query_exe('adb') path_mappings = { k: os.path.join(topsrcdir, v) for k, v in PATH_MAPPINGS.items() } httpd = MozHttpd( port=PORT, docroot=os.path.join(topsrcdir, "build", "pgo"), path_mappings=path_mappings) httpd.start(block=False) profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles') with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh: base_profiles = json.load(fh)['profileserver'] prefpaths = [ os.path.join(profile_data_dir, profile, 'user.js') for profile in base_profiles ] prefs = {} for path in prefpaths: prefs.update(Preferences.read_prefs(path)) interpolation = {"server": "%s:%d" % httpd.httpd.server_address, "OOP": "false"} for k, v in prefs.items(): if isinstance(v, string_types): v = v.format(**interpolation) prefs[k] = Preferences.cast(v) outputdir = self.config.get('output_directory', '/sdcard/pgo_profile') jarlog = posixpath.join(outputdir, 'en-US.log') profdata = posixpath.join(outputdir, 'default_%p_random_%m.profraw') env = {} env["XPCOM_DEBUG_BREAK"] = "warn" env["MOZ_IN_AUTOMATION"] = "1" env["MOZ_JAR_LOG_FILE"] = jarlog env["LLVM_PROFILE_FILE"] = profdata if self.query_minidump_stackwalk(): os.environ['MINIDUMP_STACKWALK'] = self.minidump_stackwalk_path os.environ['MINIDUMP_SAVE_PATH'] = self.query_abs_dirs()['abs_blob_upload_dir'] if not self.symbols_path: self.symbols_path = os.environ.get("MOZ_FETCHES_DIR") # Force test_root to be on the sdcard for android pgo # builds which fail for Android 4.3 when profiles are located # in /data/local/tmp/tests with # E AndroidRuntime: FATAL EXCEPTION: Gecko # E AndroidRuntime: java.lang.IllegalArgumentException: \ # Profile directory must be writable if specified: /data/local/tmp/tests/profile # This occurs when .can-write-sentinel is written to # the profile in # mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java. # This is not a problem on later versions of Android. This # over-ride of test_root should be removed when Android 4.3 is no # longer supported. sdcard_test_root = '/sdcard/tests' adbdevice = ADBDevice(adb=adb, device='emulator-5554', test_root=sdcard_test_root) if adbdevice.test_root != sdcard_test_root: # If the test_root was previously set and shared # the initializer will not have updated the shared # value. Force it to match the sdcard_test_root. adbdevice.test_root = sdcard_test_root adbdevice.mkdir(outputdir) try: # Run Fennec a first time to initialize its profile driver = Marionette( app='fennec', package_name=app, adb_path=adb, bin="geckoview-androidTest.apk", prefs=prefs, connect_to_running_emulator=True, startup_timeout=1000, env=env, symbols_path=self.symbols_path, ) driver.start_session() # Now generate the profile and wait for it to complete for page in PAGES: driver.navigate("http://%s:%d/%s" % (IP, PORT, page)) timeout = 2 if 'Speedometer/index.html' in page: # The Speedometer test actually runs many tests internally in # javascript, so it needs extra time to run through them. The # emulator doesn't get very far through the whole suite, but # this extra time at least lets some of them process. timeout = 360 time.sleep(timeout) driver.set_context("chrome") driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] .createInstance(Components.interfaces.nsISupportsPRBool); Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); return cancelQuit.data; """) driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit) """) # There is a delay between execute_script() returning and the profile data # actually getting written out, so poll the device until we get a profile. for i in range(50): if not adbdevice.process_exist(app): break time.sleep(2) else: raise Exception("Android App (%s) never quit" % app) # Pull all the profraw files and en-US.log adbdevice.pull(outputdir, '/builds/worker/workspace/') except ADBTimeoutError: self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError', EXIT_STATUS_DICT[TBPL_RETRY]) profraw_files = glob.glob('/builds/worker/workspace/*.profraw') if not profraw_files: self.fatal('Could not find any profraw files in /builds/worker/workspace') merge_cmd = [ os.path.join(os.environ['MOZ_FETCHES_DIR'], 'clang/bin/llvm-profdata'), 'merge', '-o', '/builds/worker/workspace/merged.profdata', ] + profraw_files rc = subprocess.call(merge_cmd) if rc != 0: self.fatal('INFRA-ERROR: Failed to merge profile data. Corrupt profile?', EXIT_STATUS_DICT[TBPL_RETRY]) # tarfile doesn't support xz in this version of Python tar_cmd = [ 'tar', '-acvf', '/builds/worker/artifacts/profdata.tar.xz', '-C', '/builds/worker/workspace', 'merged.profdata', 'en-US.log', ] subprocess.check_call(tar_cmd) httpd.stop()
def create_browser_handler(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 = ADBDevice(verbose=True) self.device.clear_logcat() self.clear_app_data()
def main(): parser = argparse.ArgumentParser( usage='%(prog)s [options] <test command> (<test command option> ...)', description="Wrapper script for tests run on physical Android devices at Bitbar. Runs the provided command " "wrapped with required setup and teardown.") _args, extra_args = parser.parse_known_args() logging.basicConfig(format='%(asctime)-15s %(levelname)s %(message)s', level=logging.INFO, stream=sys.stdout) print('\nscript.py: starting') with open('/builds/worker/version') as versionfile: version = versionfile.read().strip() print('\nDockerfile version {}'.format(version)) taskcluster_debug = '*' task_cwd = os.getcwd() print('Current working directory: {}'.format(task_cwd)) with open('/builds/taskcluster/scriptvars.json') as scriptvars: scriptvarsenv = json.loads(scriptvars.read()) print('Bitbar test run: https://mozilla.testdroid.com/#testing/device-session/{}/{}/{}'.format( scriptvarsenv['TESTDROID_PROJECT_ID'], scriptvarsenv['TESTDROID_BUILD_ID'], scriptvarsenv['TESTDROID_RUN_ID'])) env = dict(os.environ) if 'PATH' in os.environ: path = os.environ['PATH'] else: path = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin' path += ':/builds/worker/android-sdk-linux/tools/bin:/builds/worker/android-sdk-linux/platform-tools' env['PATH'] = os.environ['PATH'] = path env['NEED_XVFB'] = 'false' env['DEVICE_NAME'] = scriptvarsenv['DEVICE_NAME'] env['ANDROID_DEVICE'] = scriptvarsenv['ANDROID_DEVICE'] env['DEVICE_SERIAL'] = scriptvarsenv['DEVICE_SERIAL'] env['HOST_IP'] = scriptvarsenv['HOST_IP'] env['DEVICE_IP'] = scriptvarsenv['DEVICE_IP'] env['DOCKER_IMAGE_VERSION'] = scriptvarsenv['DOCKER_IMAGE_VERSION'] if 'HOME' not in env: env['HOME'] = '/builds/worker' print('setting HOME to {}'.format(env['HOME'])) show_df() # If we are running normal tests we will be connected via usb and # there should be only one device connected. If we are running # power tests, the framework will have already called adb tcpip # 5555 on the device before it disconnected usb. There should be # no devices connected and we will need to perform an adb connect # to connect to the device. DEVICE_SERIAL will be set to either # the device's serial number or its ipaddress:5555 by the framework. try: adbhost = ADBHost(verbose=True) if env['DEVICE_SERIAL'].endswith(':5555'): # Power testing with adb over wifi. adbhost.command_output(["connect", env['DEVICE_SERIAL']]) devices = adbhost.devices() print(json.dumps(devices, indent=4)) if len(devices) != 1: fatal('Must have exactly one connected device. {} found.'.format(len(devices)), retry=True) except (ADBError, ADBTimeoutError) as e: fatal('{} Unable to obtain attached devices'.format(e), retry=True) try: for f in glob('/tmp/adb.*.log'): print('\n{}:\n'.format(f)) with open(f) as afile: print(afile.read()) except Exception as e: print('{} while reading adb logs'.format(e)) print('Connecting to Android device {}'.format(env['DEVICE_SERIAL'])) try: device = ADBDevice(device=env['DEVICE_SERIAL']) android_version = device.get_prop('ro.build.version.release') print('Android device version (ro.build.version.release): {}'.format(android_version)) # this can explode if an unknown device, explode now vs in an hour... device_type = get_device_type(device) # set device to UTC if device.is_rooted: device.shell_output('setprop persist.sys.timezone "UTC"', timeout=ADB_COMMAND_TIMEOUT) # show date for visual confirmation device_datetime = device.shell_output("date", timeout=ADB_COMMAND_TIMEOUT) print('Android device datetime: {}'.format(device_datetime)) # clean up the device. device.rm('/data/local/tests', recursive=True, force=True) device.rm('/data/local/tmp/*', recursive=True, force=True) device.rm('/data/local/tmp/xpcb', recursive=True, force=True) device.rm('/sdcard/tests', recursive=True, force=True) device.rm('/sdcard/raptor-profile', recursive=True, force=True) except (ADBError, ADBTimeoutError) as e: fatal("{} attempting to clean up device".format(e), retry=True) if taskcluster_debug: env['DEBUG'] = taskcluster_debug print('environment = {}'.format(json.dumps(env, indent=4))) # run the payload's command and ensure that: # - all output is printed # - no deadlock occurs between proc.poll() and sys.stdout.readline() # - more info # - https://bugzilla.mozilla.org/show_bug.cgi?id=1611936 # - https://stackoverflow.com/questions/58471094/python-subprocess-readline-hangs-cant-use-normal-options print("script.py: running command '%s'" % ' '.join(extra_args)) rc = None proc = subprocess.Popen(extra_args, # use standard os buffer size bufsize=-1, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) # Create the queue instance q = queue.Queue() # Kick off the monitoring thread thread = threading.Thread(target=_monitor_readline, args=(proc, q)) thread.daemon = True thread.start() start = datetime.now() while True: time.sleep(0.1) bail = True rc = proc.poll() if rc is None: bail = False # Re-set the thread timer start = datetime.now() out = "" while not q.empty(): out += q.get() if out: print(out.rstrip()) # In the case where the thread is still alive and reading, and # the process has exited and finished, give it up to X seconds # to finish reading if bail and thread.is_alive() and (datetime.now() - start).total_seconds() < 5: bail = False if bail: break print("script.py: command finished") # enable charging on device if it is disabled # see https://bugzilla.mozilla.org/show_bug.cgi?id=1565324 enable_charging(device, device_type) try: if env['DEVICE_SERIAL'].endswith(':5555'): device.command_output(["usb"]) adbhost.command_output(["disconnect", env['DEVICE_SERIAL']]) adbhost.kill_server() except (ADBError, ADBTimeoutError) as e: print('{} attempting adb kill-server'.format(e)) try: print('\nnetstat -aop\n%s\n\n' % subprocess.check_output( ['netstat', '-aop'], stderr=subprocess.STDOUT).decode()) except subprocess.CalledProcessError as e: print('{} attempting netstat'.format(e)) show_df() print('script.py: exiting with exitcode {}.'.format(rc)) return rc
def run_tests(self): """ Generate the PGO profile data """ from mozhttpd import MozHttpd from mozprofile import Preferences from mozdevice import ADBDevice, ADBTimeoutError from six import string_types from marionette_driver.marionette import Marionette app = self.query_package_name() IP = '10.0.2.2' PORT = 8888 PATH_MAPPINGS = { '/js-input/webkit/PerformanceTests': 'third_party/webkit/PerformanceTests', } dirs = self.query_abs_dirs() topsrcdir = os.path.join(dirs['abs_work_dir'], 'src') adb = self.query_exe('adb') path_mappings = { k: os.path.join(topsrcdir, v) for k, v in PATH_MAPPINGS.items() } httpd = MozHttpd(port=PORT, docroot=os.path.join(topsrcdir, "build", "pgo"), path_mappings=path_mappings) httpd.start(block=False) profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles') with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh: base_profiles = json.load(fh)['profileserver'] prefpaths = [ os.path.join(profile_data_dir, profile, 'user.js') for profile in base_profiles ] prefs = {} for path in prefpaths: prefs.update(Preferences.read_prefs(path)) interpolation = { "server": "%s:%d" % httpd.httpd.server_address, "OOP": "false" } for k, v in prefs.items(): if isinstance(v, string_types): v = v.format(**interpolation) prefs[k] = Preferences.cast(v) # Enforce 1proc. This isn't in one of the user.js files because those # are shared with desktop, which wants e10s. We can't interpolate # because the formatting code only works for strings, and this is a # bool pref. prefs["browser.tabs.remote.autostart"] = False outputdir = self.config.get('output_directory', '/sdcard/pgo_profile') jarlog = posixpath.join(outputdir, 'en-US.log') profdata = posixpath.join(outputdir, 'default_%p_random_%m.profraw') env = {} env["XPCOM_DEBUG_BREAK"] = "warn" env["MOZ_IN_AUTOMATION"] = "1" env["MOZ_JAR_LOG_FILE"] = jarlog env["LLVM_PROFILE_FILE"] = profdata adbdevice = ADBDevice(adb=adb, device='emulator-5554') adbdevice.mkdir(outputdir) try: # Run Fennec a first time to initialize its profile driver = Marionette( app='fennec', package_name=app, adb_path=adb, bin="target.apk", prefs=prefs, connect_to_running_emulator=True, startup_timeout=1000, env=env, ) driver.start_session() # Now generate the profile and wait for it to complete for page in PAGES: driver.navigate("http://%s:%d/%s" % (IP, PORT, page)) timeout = 2 if 'Speedometer/index.html' in page: # The Speedometer test actually runs many tests internally in # javascript, so it needs extra time to run through them. The # emulator doesn't get very far through the whole suite, but # this extra time at least lets some of them process. timeout = 360 time.sleep(timeout) driver.set_context("chrome") driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] .createInstance(Components.interfaces.nsISupportsPRBool); Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); return cancelQuit.data; """) driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit) """) # There is a delay between execute_script() returning and the profile data # actually getting written out, so poll the device until we get a profile. for i in range(50): if not adbdevice.process_exist(app): break time.sleep(2) else: raise Exception("Android App (%s) never quit" % app) # Pull all the profraw files and en-US.log adbdevice.pull(outputdir, '/builds/worker/workspace/') except ADBTimeoutError: self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError', EXIT_STATUS_DICT[TBPL_RETRY]) profraw_files = glob.glob('/builds/worker/workspace/*.profraw') if not profraw_files: self.fatal( 'Could not find any profraw files in /builds/worker/workspace') merge_cmd = [ os.path.join(os.environ['MOZ_FETCHES_DIR'], 'clang/bin/llvm-profdata'), 'merge', '-o', '/builds/worker/workspace/merged.profdata', ] + profraw_files rc = subprocess.call(merge_cmd) if rc != 0: self.fatal( 'INFRA-ERROR: Failed to merge profile data. Corrupt profile?', EXIT_STATUS_DICT[TBPL_RETRY]) # tarfile doesn't support xz in this version of Python tar_cmd = [ 'tar', '-acvf', '/builds/worker/artifacts/profdata.tar.xz', '-C', '/builds/worker/workspace', 'merged.profdata', 'en-US.log', ] subprocess.check_call(tar_cmd) httpd.stop()
def main(): parser = argparse.ArgumentParser( usage='%(prog)s [options] <test command> (<test command option> ...)', description= "Wrapper script for tests run on physical Android devices at Bitbar. Runs the provided command wrapped with required setup and teardown." ) _args, extra_args = parser.parse_known_args() logging.basicConfig(format='%(asctime)-15s %(levelname)s %(message)s', level=logging.INFO, stream=sys.stdout) print('\nBegin script.py') with open('/builds/worker/version') as versionfile: version = versionfile.read().strip() print('\nDockerfile version {}'.format(version)) taskcluster_debug = '*' task_cwd = os.getcwd() print('Current working directory: {}'.format(task_cwd)) with open('/builds/taskcluster/scriptvars.json') as scriptvars: scriptvarsenv = json.loads(scriptvars.read()) print( 'Bitbar test run: https://mozilla.testdroid.com/#testing/device-session/{}/{}/{}' .format(scriptvarsenv['TESTDROID_PROJECT_ID'], scriptvarsenv['TESTDROID_BUILD_ID'], scriptvarsenv['TESTDROID_RUN_ID'])) env = dict(os.environ) if 'PATH' in os.environ: path = os.environ['PATH'] else: path = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin' path += ':/builds/worker/android-sdk-linux/tools/bin:/builds/worker/android-sdk-linux/platform-tools' env['PATH'] = os.environ['PATH'] = path env['NEED_XVFB'] = 'false' env['DEVICE_NAME'] = scriptvarsenv['DEVICE_NAME'] env['ANDROID_DEVICE'] = scriptvarsenv['ANDROID_DEVICE'] env['DEVICE_SERIAL'] = scriptvarsenv['DEVICE_SERIAL'] env['HOST_IP'] = scriptvarsenv['HOST_IP'] env['DEVICE_IP'] = scriptvarsenv['DEVICE_IP'] env['DOCKER_IMAGE_VERSION'] = scriptvarsenv['DOCKER_IMAGE_VERSION'] if 'HOME' not in env: env['HOME'] = '/builds/worker' print('setting HOME to {}'.format(env['HOME'])) # If we are running normal tests we will be connected via usb and # there should be only one device connected. If we are running # power tests, the framework will have already called adb tcpip # 5555 on the device before it disconnected usb. There should be # no devices connected and we will need to perform an adb connect # to connect to the device. DEVICE_SERIAL will be set to either # the device's serial number or its ipaddress:5555 by the framework. try: adbhost = ADBHost() if env['DEVICE_SERIAL'].endswith(':5555'): # Power testing with adb over wifi. adbhost.command_output(["connect", env['DEVICE_SERIAL']]) devices = adbhost.devices() print(json.dumps(devices, indent=4)) if len(devices) != 1: fatal('Must have exactly one connected device. {} found.'.format( len(devices))) except ADBError as e: fatal('{} Unable to obtain attached devices'.format(e)) try: for f in glob('/tmp/adb.*.log'): print('\n{}:\n'.format(f)) with open(f) as afile: print(afile.read()) except Exception as e: print('{} while reading adb logs'.format(e)) print('Connecting to Android device {}'.format(env['DEVICE_SERIAL'])) try: device = ADBDevice(device=env['DEVICE_SERIAL']) android_version = device.get_prop('ro.build.version.release') print('Android device version (ro.build.version.release): {}'.format( android_version)) # this can explode if an unknown device, explode now vs in an hour... device_type = get_device_type(device) # clean up the device. device.rm('/data/local/tests', recursive=True, force=True, root=True) device.rm('/data/local/tmp/*', recursive=True, force=True, root=True) device.rm('/data/local/tmp/xpcb', recursive=True, force=True, root=True) device.rm('/sdcard/tests', recursive=True, force=True, root=True) device.rm('/sdcard/raptor-profile', recursive=True, force=True, root=True) except ADBError as e: fatal("{} attempting to clean up device".format(e)) if taskcluster_debug: env['DEBUG'] = taskcluster_debug print('environment = {}'.format(json.dumps(env, indent=4))) # run the payload's command print(' '.join(extra_args)) rc = None proc = subprocess.Popen(extra_args, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while rc == None: line = proc.stdout.readline() sys.stdout.write(line) rc = proc.poll() # enable charging on device if it is disabled # see https://bugzilla.mozilla.org/show_bug.cgi?id=1565324 enable_charging(device, device_type) try: if env['DEVICE_SERIAL'].endswith(':5555'): device.command_output(["usb"]) adbhost.command_output(["disconnect", env['DEVICE_SERIAL']]) adbhost.kill_server() except ADBError as e: print('{} attempting adb kill-server'.format(e)) try: print('\nnetstat -aop\n%s\n\n' % subprocess.check_output( ['netstat', '-aop'], stderr=subprocess.STDOUT)) except subprocess.CalledProcessError as e: print('{} attempting netstat'.format(e)) print('script.py exitcode {}'.format(rc)) return rc
def run_tests(self): """ Generate the PGO profile data """ from mozhttpd import MozHttpd from mozprofile import Preferences from mozdevice import ADBDevice, ADBProcessError, ADBTimeoutError from six import string_types from marionette_driver.marionette import Marionette app = self.query_package_name() IP = '10.0.2.2' PORT = 8888 PATH_MAPPINGS = { '/js-input/webkit/PerformanceTests': 'third_party/webkit/PerformanceTests', } dirs = self.query_abs_dirs() topsrcdir = os.path.join(dirs['abs_work_dir'], 'src') adb = self.query_exe('adb') path_mappings = { k: os.path.join(topsrcdir, v) for k, v in PATH_MAPPINGS.items() } httpd = MozHttpd(port=PORT, docroot=os.path.join(topsrcdir, "build", "pgo"), path_mappings=path_mappings) httpd.start(block=False) profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles') with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh: base_profiles = json.load(fh)['profileserver'] prefpaths = [ os.path.join(profile_data_dir, profile, 'user.js') for profile in base_profiles ] prefs = {} for path in prefpaths: prefs.update(Preferences.read_prefs(path)) interpolation = { "server": "%s:%d" % httpd.httpd.server_address, "OOP": "false" } for k, v in prefs.items(): if isinstance(v, string_types): v = v.format(**interpolation) prefs[k] = Preferences.cast(v) outputdir = self.config.get('output_directory', '/sdcard') jarlog = posixpath.join(outputdir, 'en-US.log') profdata = posixpath.join(outputdir, 'default.profraw') env = {} env["XPCOM_DEBUG_BREAK"] = "warn" env["MOZ_IN_AUTOMATION"] = "1" env["MOZ_JAR_LOG_FILE"] = jarlog env["LLVM_PROFILE_FILE"] = profdata adbdevice = ADBDevice(adb=adb, device='emulator-5554') try: # Run Fennec a first time to initialize its profile driver = Marionette( app='fennec', package_name=app, adb_path=adb, bin="target.apk", prefs=prefs, connect_to_running_emulator=True, startup_timeout=1000, env=env, ) driver.start_session() # Now generate the profile and wait for it to complete for page in PAGES: driver.navigate("http://%s:%d/%s" % (IP, PORT, page)) timeout = 2 if 'Speedometer/index.html' in page: # The Speedometer test actually runs many tests internally in # javascript, so it needs extra time to run through them. The # emulator doesn't get very far through the whole suite, but # this extra time at least lets some of them process. timeout = 360 time.sleep(timeout) driver.set_context("chrome") driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] .createInstance(Components.interfaces.nsISupportsPRBool); Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); return cancelQuit.data; """) driver.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit) """) # There is a delay between execute_script() returning and the profile data # actually getting written out, so poll the device until we get a profile. for i in range(50): try: localprof = '/builds/worker/workspace/default.profraw' adbdevice.pull(profdata, localprof) stats = os.stat(localprof) if stats.st_size == 0: # The file may not have been fully written yet, so retry until we # get actual data. time.sleep(2) else: break except ADBProcessError: # The file may not exist at all yet, which would raise an # ADBProcessError, so retry. time.sleep(2) else: raise Exception("Unable to pull default.profraw") adbdevice.pull(jarlog, '/builds/worker/workspace/en-US.log') except ADBTimeoutError: self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError', EXIT_STATUS_DICT[TBPL_RETRY]) # We normally merge as part of a GENERATED_FILES step in the profile-use # build, but Android runs sometimes result in a truncated profile. We do # a merge here to make sure the data isn't corrupt so we can retry the # 'run' task if necessary. merge_cmd = [ '/builds/worker/workspace/build/clang/bin/llvm-profdata', 'merge', '/builds/worker/workspace/default.profraw', '-o', '/builds/worker/workspace/merged.profraw', ] rc = subprocess.call(merge_cmd) if rc != 0: self.fatal( 'INFRA-ERROR: Failed to merge profile data. Corrupt profile?', EXIT_STATUS_DICT[TBPL_RETRY]) # tarfile doesn't support xz in this version of Python tar_cmd = [ 'tar', '-acvf', '/builds/worker/artifacts/profdata.tar.xz', '-C', '/builds/worker/workspace', 'merged.profraw', 'en-US.log', ] subprocess.check_call(tar_cmd) httpd.stop()
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)]))
def run(self, metadata): self.metadata = metadata replay_file = self.get_arg("file") # Check if we have a replay file if replay_file is None: raise ValueError("Proxy file not provided!!") if replay_file is not None and replay_file.startswith("http"): self.tmpdir = tempfile.TemporaryDirectory() target = pathlib.Path(self.tmpdir.name, "recording.zip") self.info("Downloading %s" % replay_file) download_file(replay_file, target) replay_file = target self.info("Setting up the proxy") command = [ self.mach_cmd.virtualenv_manager.python_path, "-m", "mozproxy.driver", "--local", "--topsrcdir=" + self.mach_cmd.topsrcdir, "--objdir=" + self.mach_cmd.topobjdir, "--profiledir=" + self.get_arg("profile-directory"), ] if metadata.flavor == "mobile-browser": command.extend(["--tool=%s" % "mitmproxy-android"]) command.extend(["--binary=android"]) else: command.extend(["--tool=%s" % "mitmproxy"]) # XXX See bug 1712337, we need a single point where we can get the binary used from command.extend( ["--binary=%s" % self.get_arg("browsertime-binary")]) if self.get_arg("mode") == "record": output = self.get_arg("output") if output is None: output = pathlib.Path(self.mach_cmd.topsrcdir, "artifacts") results_dir = get_output_dir(output) command.extend(["--mode", "record"]) command.append(str(pathlib.Path(results_dir, replay_file))) elif self.get_arg("mode") == "playback": command.extend(["--mode", "playback"]) command.append(str(replay_file)) else: raise ValueError( "Proxy mode not provided please provide proxy mode") print(" ".join(command)) self.output_handler = OutputHandler() self.proxy = ProcessHandler( command, processOutputLine=self.output_handler, onFinish=self.output_handler.finished, ) self.output_handler.proc = self.proxy self.proxy.run() # Wait until we've retrieved the proxy server's port number so we can # configure the browser properly. port = self.output_handler.wait_for_port() if port is None: raise ValueError( "Unable to retrieve the port number from mozproxy") self.info("Received port number %s from mozproxy" % port) prefs = { "network.proxy.type": 1, "network.proxy.http": "127.0.0.1", "network.proxy.http_port": port, "network.proxy.ssl": "127.0.0.1", "network.proxy.ssl_port": port, "network.proxy.no_proxies_on": "127.0.0.1", } browser_prefs = metadata.get_options("browser_prefs") browser_prefs.update(prefs) if metadata.flavor == "mobile-browser": self.info("Setting reverse port fw for android device") device = ADBDevice() device.create_socket_connection("reverse", "tcp:%s" % port, "tcp:%s" % port) return metadata
def __init__(self, options, log): xpcshell.XPCShellTests.__init__(self, log) self.options = options verbose = False if options['log_tbpl_level'] == 'debug' or options['log_mach_level'] == 'debug': verbose = True self.device = ADBDevice(adb=options['adbPath'] or 'adb', device=options['deviceSerial'], test_root=options['remoteTestRoot'], verbose=verbose) self.remoteTestRoot = posixpath.join(self.device.test_root, "xpc") # Add Android version (SDK level) to mozinfo so that manifest entries # can be conditional on android_version. mozinfo.info['android_version'] = self.device.version self.localBin = options['localBin'] self.pathMapping = [] # remoteBinDir contains xpcshell and its wrapper script, both of which must # be executable. Since +x permissions cannot usually be set on /mnt/sdcard, # and the test root may be on /mnt/sdcard, remoteBinDir is set to be on # /data/local, always. self.remoteBinDir = posixpath.join("/data", "local", "xpcb") # Terse directory names are used here ("c" for the components directory) # to minimize the length of the command line used to execute # xpcshell on the remote device. adb has a limit to the number # of characters used in a shell command, and the xpcshell command # line can be quite complex. self.remoteTmpDir = posixpath.join(self.remoteTestRoot, "tmp") self.remoteScriptsDir = self.remoteTestRoot self.remoteComponentsDir = posixpath.join(self.remoteTestRoot, "c") self.remoteModulesDir = posixpath.join(self.remoteTestRoot, "m") self.remoteMinidumpDir = posixpath.join(self.remoteTestRoot, "minidumps") self.profileDir = posixpath.join(self.remoteTestRoot, "p") self.remoteDebugger = options['debugger'] self.remoteDebuggerArgs = options['debuggerArgs'] self.testingModulesDir = options['testingModulesDir'] self.env = {} if options['objdir']: self.xpcDir = os.path.join(options['objdir'], "_tests/xpcshell") elif os.path.isdir(os.path.join(here, 'tests')): self.xpcDir = os.path.join(here, 'tests') else: print("Couldn't find local xpcshell test directory", file=sys.stderr) sys.exit(1) self.localAPKContents = ZipFile(options['localAPK']) if options['setup']: self.setupTestDir() self.setupUtilities() self.setupModules() self.initDir(self.remoteMinidumpDir) self.remoteAPK = None self.remoteAPK = posixpath.join(self.remoteBinDir, os.path.basename(options['localAPK'])) self.setAppRoot() # data that needs to be passed to the RemoteXPCShellTestThread self.mobileArgs = { 'device': self.device, 'remoteBinDir': self.remoteBinDir, 'remoteScriptsDir': self.remoteScriptsDir, 'remoteComponentsDir': self.remoteComponentsDir, 'remoteModulesDir': self.remoteModulesDir, 'options': self.options, 'remoteDebugger': self.remoteDebugger, 'pathMapping': self.pathMapping, 'profileDir': self.profileDir, 'remoteTmpDir': self.remoteTmpDir, 'remoteMinidumpDir': self.remoteMinidumpDir, } if self.remoteAPK: self.mobileArgs['remoteAPK'] = self.remoteAPK
def post_to_treeherder(self, tests): version = mozversion.get_version(binary=self.bin, sources=self.sources, dm_type='adb', device_serial=self.device_serial) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get('device_id') device_firmware_version_release = \ version.get('device_firmware_version_release') if not device: self.logger.error('Submitting to Treeherder is currently limited ' 'to devices.') return try: group = DEVICE_GROUP_MAP[device][device_firmware_version_release] job.add_group_name(group['name']) job.add_group_symbol(group['symbol']) job.add_job_name('Gaia Python Integration Test (%s)' % group['symbol']) job.add_job_symbol('Gip') except KeyError: self.logger.error('Unknown device id: %s or device firmware ' 'version: %s. Unable to determine Treeherder ' 'group. Supported devices: %s' % (device, device_firmware_version_release, [ '%s: %s' % (k, [fw for fw in v.keys()]) for k, v in DEVICE_GROUP_MAP.iteritems() ])) return # Determine revision hash from application revision revision = version['application_changeset'] project = version['application_repository'].split('/')[-1] lookup_url = urljoin( self.treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % (project, revision)) self.logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') # Determine test result if self.failed or self.unexpected_successes: job.add_result('testfailed') else: job.add_result('success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') # All B2G device builds are currently opt builds job.add_option_collection({'opt': True}) date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % version.get('gaia_changeset'), 'value': version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': version.get('gaia_date') and time.strftime( date_format, time.localtime(int(version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int(version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': version.get('device_firmware_version_release') }] ci_url = os.environ.get('BUILD_URL') if ci_url: job_details.append({ 'url': ci_url, 'value': ci_url, 'content_type': 'link', 'title': 'CI build:' }) # Attach logcat adb_device = ADBDevice(self.device_serial) with tempfile.NamedTemporaryFile(suffix='logcat.txt') as f: f.writelines(adb_device.get_logcat()) self.logger.debug('Logcat stored in: %s' % f.name) try: url = self.upload_to_s3(f.name) job_details.append({ 'url': url, 'value': 'logcat.txt', 'content_type': 'link', 'title': 'Log:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload logcat.txt', 'content_type': 'text', 'title': 'Error:' }) # Attach log files handlers = [ handler for handler in self.logger.handlers if isinstance(handler, StreamHandler) and os.path.exists(handler.stream.name) ] for handler in handlers: path = handler.stream.name filename = os.path.split(path)[-1] try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Log:' }) # Add log reference if type(handler.formatter) is TbplFormatter or \ type(handler.formatter) is LogLevelFilter and \ type(handler.formatter.inner) is TbplFormatter: job.add_log_reference(filename, url) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) # Attach reports for report in [self.html_output, self.xml_output]: if report is not None: filename = os.path.split(report)[-1] try: url = self.upload_to_s3(report) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Report:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=os.environ.get('TREEHERDER_KEY'), oauth_secret=os.environ.get('TREEHERDER_SECRET')) self.logger.debug('Sending results to Treeherder: %s' % job_collection.to_json()) response = request.post(job_collection) self.logger.debug('Response: %s' % response.read()) assert response.status == 200, 'Failed to send results!' self.logger.info( 'Results are available to view at: %s' % (urljoin(self.treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % (project, revision))))
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 = ADBDevice(adb=options.adb_path or 'adb', 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__ args = {'messageLogger': self.outputHandler} self.automation = RemoteAutomation(self.device, appName=options.app, remoteProfile=self.remoteProfile, remoteLog=options.remoteLogFile, processArgs=args) 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.device.clear_logcat() self.device.rm(self.remoteCache, force=True, recursive=True) procName = options.app.split('/')[-1] self.device.stop_application(procName) if self.device.process_exist(procName): self.log.error("unable to kill %s before starting tests!" % procName)
def prepare(self, profile, logfile): self._set_adb_logger(logfile) try: self.device = ADBDevice(verbose=self.verbose, logger_name="adb") except Exception: ERROR("Cannot initialize device") raise device = self.device self.profile = profile # checking that the app is installed if not device.is_app_installed(self.app_name): raise Exception("%s is not installed" % self.app_name) # debug flag LOG("Setting %s as the debug app on the phone" % self.app_name) device.shell("am set-debug-app --persistent %s" % self.app_name, stdout_callback=LOG) # creating the profile on the device LOG("Creating the profile on the device") remote_test_root = posixpath.join(device.test_root, "condprof") remote_profile = posixpath.join(remote_test_root, "profile") LOG("The profile on the phone will be at %s" % remote_profile) device.rm(remote_test_root, force=True, recursive=True) device.mkdir(remote_test_root) device.chmod(remote_test_root, recursive=True, root=True) device.rm(remote_profile, force=True, recursive=True) LOG("Pushing %s on the phone" % self.profile) device.push(profile, remote_profile) device.chmod(remote_profile, recursive=True, root=True) self.profile = profile self.remote_profile = remote_profile # creating the yml file yml_data = { "args": ["-marionette", "-profile", self.remote_profile], "prefs": DEFAULT_PREFS, "env": { "LOG_VERBOSE": 1, "R_LOG_LEVEL": 6, "MOZ_LOG": "" }, } yml_name = "%s-geckoview-config.yaml" % self.app_name yml_on_host = posixpath.join(tempfile.mkdtemp(), yml_name) write_yml_file(yml_on_host, yml_data) tmp_on_device = posixpath.join("/data", "local", "tmp") if not device.exists(tmp_on_device): raise IOError("%s does not exists on the device" % tmp_on_device) yml_on_device = posixpath.join(tmp_on_device, yml_name) try: device.rm(yml_on_device, force=True, recursive=True) device.push(yml_on_host, yml_on_device) device.chmod(yml_on_device, recursive=True, root=True) except Exception: LOG("could not create the yaml file on device. Permission issue?") raise # command line 'extra' args not used with geckoview apps; instead we use # an on-device config.yml file intent = "android.intent.action.VIEW" device.stop_application(self.app_name) device.launch_application(self.app_name, self.activity, intent, extras=None, url="about:blank") if not device.process_exist(self.app_name): raise Exception("Could not start %s" % self.app_name) LOG("Creating socket forwarding on port %d" % self.marionette_port) device.forward( local="tcp:%d" % self.marionette_port, remote="tcp:%d" % self.marionette_port, ) # we don't have a clean way for now to check that GV or Fenix # is ready to handle our tests. So here we just wait 30s LOG("Sleeping for 30s") time.sleep(30)
def __init__(self, options): MochitestDesktop.__init__(self, options.flavor, vars(options)) verbose = False if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug': verbose = True if hasattr(options, 'log'): delattr(options, 'log') self.certdbNew = True self.chromePushed = False self.mozLogName = "moz.log" self.device = ADBDevice(adb=options.adbPath or 'adb', device=options.deviceSerial, test_root=options.remoteTestRoot, verbose=verbose) if options.remoteTestRoot is None: options.remoteTestRoot = self.device.test_root options.dumpOutputDirectory = options.remoteTestRoot self.remoteLogFile = posixpath.join(options.remoteTestRoot, "logs", "mochitest.log") logParent = posixpath.dirname(self.remoteLogFile) self.device.rm(logParent, force=True, recursive=True) self.device.mkdir(logParent) self.remoteProfile = posixpath.join(options.remoteTestRoot, "profile/") self.device.rm(self.remoteProfile, force=True, recursive=True) self.counts = dict() self.message_logger = MessageLogger(logger=None) self.message_logger.logger = self.log process_args = {'messageLogger': self.message_logger, 'counts': self.counts} self.automation = RemoteAutomation(self.device, options.remoteappname, self.remoteProfile, self.remoteLogFile, processArgs=process_args) self.environment = self.automation.environment # 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.remoteModulesDir = posixpath.join(options.remoteTestRoot, "modules/") self.remoteCache = posixpath.join(options.remoteTestRoot, "cache/") self.device.rm(self.remoteCache, force=True, recursive=True) # move necko cache to a location that can be cleaned up options.extraPrefs += ["browser.cache.disk.parent_directory=%s" % self.remoteCache] self.remoteMozLog = posixpath.join(options.remoteTestRoot, "mozlog") self.device.rm(self.remoteMozLog, force=True, recursive=True) self.device.mkdir(self.remoteMozLog) self.remoteChromeTestDir = posixpath.join( options.remoteTestRoot, "chrome") self.device.rm(self.remoteChromeTestDir, force=True, recursive=True) self.device.mkdir(self.remoteChromeTestDir) procName = options.app.split('/')[-1] self.device.stop_application(procName) if self.device.process_exist(procName): self.log.warning("unable to kill %s before running tests!" % procName) # Add Android version (SDK level) to mozinfo so that manifest entries # can be conditional on android_version. self.log.info( "Android sdk version '%s'; will use this to filter manifests" % str(self.device.version)) mozinfo.info['android_version'] = str(self.device.version) mozinfo.info['isFennec'] = not ('geckoview' in options.app) mozinfo.info['is_emulator'] = self.device._device_serial.startswith('emulator-')
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, host=None, power_test=False, is_release_build=False, debug_mode=False): # Override the magic --host HOST_IP with the value of the environment variable. if host == 'HOST_IP': host = os.environ['HOST_IP'] 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.config['host'] = host self.config['power_test'] = power_test self.config['is_release_build'] = is_release_build 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 self.device = None # debug mode is currently only supported when running locally self.debug_mode = debug_mode if self.config['run_local'] else False # if running debug-mode reduce the pause after browser startup if self.debug_mode: self.post_startup_delay = 3000 self.log.info( "debug-mode enabled, reducing post-browser startup pause to %d ms" % self.post_startup_delay) # Create the profile; for geckoview/fennec we want a firefox profile type if self.config['app'] in ["geckoview", "fennec"]: 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) # add profile dir to our config self.config['local_profile_dir'] = self.profile.profile # 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'] in ["geckoview", "fennec"]: # create the android device handler; it gets initiated and sets up adb etc self.log.info("creating android device handler using mozdevice") self.device = ADBDevice(verbose=True) self.device.clear_logcat() if self.config['power_test']: init_geckoview_power_test(self) 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, symbols_path=self.config['symbols_path']) self.log.info("raptor config: %s" % str(self.config))