Esempio n. 1
0
    def create_marionette():
        """Returns current Marionette session, or creates one if
        one does not exist.

        """

        m = TestCase.stored.marionette
        if m is None:
            m = Marionette()
            m.wait_for_port()
            m.start_session()
            TestCase.stored.marionette = m

        return TestCase.stored.marionette
Esempio n. 2
0
    def create_marionette():
        """Returns current Marionette session, or creates one if
        one does not exist.

        """

        m = TestCase.stored.marionette
        if m is None:
            m = Marionette()
            m.wait_for_port()
            m.start_session()
            TestCase.stored.marionette = m

        return TestCase.stored.marionette
class MtbfJobRunner(BaseActionRunner):
    serial = None
    marionette = None
    flash_params = {
        'branch': 'mozilla-b2g34_v2_1-flame-kk-eng',
        'build': '',
        'build_id': ''
    }
    flashed = False

    def __init__(self, **kwargs):
        self.logger = logger
        BaseActionRunner.__init__(self)

    def setup(self):
        if not self.serial or not self.port:
            logger.error("Fail to get device")
            raise DMError
        self.config_raptor()

        self.marionette and self.marionette.session and self.marionette.cleanup(
        )
        self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial,
                                             port=self.port)
        self.marionette = Marionette(device_serial=self.serial, port=self.port)
        self.marionette.wait_for_port()
        self.marionette.start_session()
        self.device = GaiaDevice(marionette=self.marionette, manager=self.dm)
        self.apps = GaiaApps(self.marionette)
        self.data_layer = GaiaData(self.marionette)
        if self.flashed:
            self.device.wait_for_b2g_ready()

    def adb_test(self):
        if not hasattr(self,
                       'serial') or os.system("ANDROID_SERIAL=" + self.serial +
                                              " adb shell ls") != 0:
            logger.error("Device not found or can't be controlled")
            return False
        return True

    @action(enabled=False)
    def add_7mobile_action(self):
        # workaround for waiting for boot
        self.marionette.wait_for_port()
        self.marionette.start_session()
        self.data_layer = GaiaData(self.marionette)
        self.data_layer.set_setting('ril.data.apnSettings',
                                    [[{
                                        "carrier": "(7-Mobile) (MMS)",
                                        "apn": "opentalk",
                                        "mmsc": "http://mms",
                                        "mmsproxy": "210.241.199.199",
                                        "mmsport": "9201",
                                        "types": ["mms"]
                                    }, {
                                        "carrier": "(7-Mobile) (Internet)",
                                        "apn": "opentalk",
                                        "types": ["default", "supl"]
                                    }]])
        return True

    @action(enabled=False)
    def change_memory(self):
        # This function only work in flame
        # TODO: use native adb/fastboot command to change memory?
        # Make sure it's in fastboot mode, TODO: leverage all fastboot command in one task function
        memory = 512  # default set 512
        if 'MEM' in os.environ:
            memory = os.environ['MEM']
        elif self.settings['change_memory'][
                'enabled'] and 'memory' in self.settings['change_memory']:
            memory = self.settings['change_memory']['memory']
        if self.adb_test():
            os.system("adb reboot bootloader")
            memory = 512
            mem_str = str(memory)
            os.system("fastboot oem mem " + mem_str)
            # Preventing from async timing of fastboot
            os.system("fastboot reboot")
            self.device_obj.create_adb_forward(self.port)
            return True
        logger.error("Can't find device")
        self.marionette.wait_for_port()
        self.device_obj.create_adb_forward(self.port)
        return False

    @action(enabled=False)
    def config_raptor(self):
        settings = self.settings
        if 'config_raptor' in settings and settings['config_raptor']['config']:
            with open(os.path.expandvars(
                    settings['config_raptor']['config'])) as conf:
                self.raptor = json.load(conf)
                self.raptor['path'] = settings['config_raptor']['config']
                self.raptor['monitorJobFolder'] = settings['config_raptor'][
                    'monitorJobFolder']

    @action(enabled=True)
    def collect_memory_report(self):
        zip_utils.collect_about_memory(
            "mtbf_driver")  # TODO: give a correct path for about memory folder

    def get_free_device(self):
        do = device_pool.get_device(self.serial)
        if do:
            # Record device serial and store dp instance
            self.serial = do.serial
            self.device_obj = do
            if do.create_adb_forward():
                self.port = do.adb_forwarded_port
                logger.info("Device found, ANDROID_SERIAL= " + self.serial)
                return do
            logger.error("Port forwarding failed")
            raise DMError
        logger.warning(
            "No available device.  Please retry after device released")
        # TODO: more handling for no available device

    def validate_flash_params(self):
        ## Using system environment variable as temporary solution TODO: use other way for input params
        ## Check if package(files)/folder exists and return, else raise exception
        if not 'FLASH_BASEDIR' in os.environ:
            raise AttributeError("No FLASH_BASEDIR set")
        basedir = os.environ['FLASH_BASEDIR']
        if not 'FLASH_BUILDID' in os.environ:
            ## TODO: if latest/ exists, use latest as default
            logging.info("No build id set. search in base dir")
            buildid = ""
            flash_dir = basedir
        else:
            buildid = os.environ['FLASH_BUILDID']
            # re-format build id based on pvt folder structure
            if '-' in buildid:
                buildid = buildid.replace("-", "")
            year = buildid[:4]
            month = buildid[4:6]
            datetime = '-'.join(
                [year, month] +
                [buildid[i + 6:i + 8] for i in range(0, len(buildid[6:]), 2)])
            flash_dir = os.path.join(basedir, year, month, datetime)
        if not os.path.isdir(flash_dir):
            raise AttributeError("Flash  directory " + flash_dir +
                                 " not exist")
        flash_files = glob.glob(os.path.join(flash_dir, '*'))
        flash_src = {}
        for flash_file in flash_files:
            logger.debug("Flash source found: [" + flash_file + "]")
            if os.path.isdir(flash_file):
                continue
            elif re.match("^b2g-[0-9]*.*\.tar\.gz$", flash_file):
                flash_src['gecko'] = flash_file
            elif "gaia.zip" == flash_file:
                flash_src['gaia'] = flash_file
            elif "symbol" in flash_file:
                flash_src['symbol'] = flash_file
            elif "zip" in flash_file and not ("gaia.zip" in flash_file):
                flash_src['image'] = flash_file
        return flash_src

    @action(enabled=True)
    def full_flash(self):
        flash_src = self.validate_flash_params()
        if self.flashed:
            logger.warning("Flash performed; skip flashing")
            return True
        if not flash_src:
            logger.warning("Invalid build folder/build_id, skip flashing")
            return False
        if not 'image' in flash_src:
            logger.warning("No available image for flash, skip flashing")
            return False
        try:
            self.temp_dir = tempfile.mkdtemp()
            logger.info('Create temporary folder:' + self.temp_dir)
            Decompressor().unzip(flash_src['image'], self.temp_dir)
            # set the permissions to rwxrwxr-x (509 in python's os.chmod)
            os.chmod(
                self.temp_dir + '/b2g-distro/flash.sh',
                stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP
                | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
            os.chmod(
                self.temp_dir + '/b2g-distro/load-config.sh',
                stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP
                | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
            os.system('cd ' + self.temp_dir + '/b2g-distro; ./flash.sh -f')
            # support NO_FTU environment for skipping FTU (e.g. monkey test)
            if 'NO_FTU' in os.environ and os.environ['NO_FTU'] == 'true':
                logger.log('The [NO_FTU] is [true].')
                os.system(
                    "ANDROID_SERIAL=" + self.serial +
                    'adb wait-for-device && adb shell stop b2g; (RET=$(adb root); if ! case ${RET} in *"cannot"*) true;; *) false;; esac; then adb remount && sleep 5; else exit 1; fi; ./disable_ftu.py) || (echo "No root permission, cannot setup NO_FTU."); adb reboot;'
                )
        finally:
            try:
                shutil.rmtree(self.temp_dir)  # delete directory
            except OSError:
                logger.error('Can not remove temporary folder:' +
                             self.temp_dir,
                             level=Logger._LEVEL_WARNING)
        self.flashed = True

    @action(enabled=False)
    def shallow_flash(self):
        flash_src = self.validate_flash_params()
        if self.flashed:
            logger.warning("Flash performed; skip flashing")
            return True
        if not flash_src:
            logger.warning("Invalid build folder/build_id, skip flashing")
            return False
        if not 'gaia' in flash_src or not 'gecko' in flash_src:
            logger.warning("No gaia or gecko archive, skip flashing")
            return False
        cmd = 'flash_tool/shallow_flash.sh -y --gecko="' + flash_src[
            'gecko'] + '" --gaia="' + flash_src['gaia'] + '"'
        if _platform == 'darwin':
            cmd = cmd.replace('=', ' ')
        ret = os.system(cmd)
        if ret != 0:
            logger.info("Shallow flash ended abnormally")
            return False
        self.flashed = True
        os.system("ANDROID_SERIAL=" + self.serial + " adb wait-for-device")

    @action(enabled=True)
    def enable_certified_apps_debug(self):
        if self.serial:
            os.system(
                "ANDROID_SERIAL=" + self.serial +
                " flash_tool/enable_certified_apps_for_devtools.sh && adb wait-for-device"
            )
            logger.debug("Successfully enabling certified apps for debugging")
            return True
        return False

    def release(self):
        device_pool.release()

    def start_monitoring(self):
        job = {
            'name': 'mtbf',
            'type': 'moz_minions.kevin.MtbfToRaptorMinion',
            'serial': self.serial,
            'job_info': {
                'pid': os.getpid(),
                'program': sys.argv[0],
            }
        }
        if hasattr(self, 'raptor'):
            raptor = self.raptor
            job['job_info'].update(self.raptor)
            if "monitorJobFolder" in self.raptor:
                dirpath = os.path.expandvars(self.raptor['monitorJobFolder'])
            else:
                dirpath = "/tmp/mtbf"
            if not os.path.isdir(dirpath):
                os.makedirs(dirpath)
            timestamp = time.strftime('%Y-%m-%d-%H-%M-%S+0000', time.gmtime())
            filename = job['name'] + "_" + timestamp + ".json"
            self.monitor_conf = os.path.join(dirpath, filename)

            job['job_info']['conf'] = self.monitor_conf

            with open(self.monitor_conf, 'w') as fh:
                fh.write(json.dumps(job, indent=2, sort_keys=True))

    def stop_monitoring(self):
        if hasattr(self, 'raptor'):
            os.remove(self.monitor_conf)
            self.monitor_conf = None

    def check_version(self):
        # FIXME: fix check version to use package import
        cmd = "cd flash_tool/ && NO_COLOR=TRUE ./check_versions.py | sed -e 's| \{2,\}||g' -e 's|\[0m||g'"
        if self.serial:
            cmd = "ANDROID_SERIAL=" + self.serial + " " + cmd
        os.system(cmd)

    @action(enabled=False)
    def patch_marionette(self):
        os.system(
            "M_PATH=/mnt/mtbf_shared/paul/ /mnt/mtbf_shared/paul/marionette_update.sh"
        )
        import time
        time.sleep(10)
        self.device_obj.create_adb_forward(self.port)

    def mtbf_options(self):
        ## load mtbf parameters
        if not 'MTBF_TIME' in os.environ:
            logger.warning("MTBF_TIME is not set")
        if not 'MTBF_CONF' in os.environ:
            logger.warning("MTBF_CONF is not set")

        parser = self.parser.parser
        parser.add_argument("--testvars", help="Test variables for b2g")
        self.parse_options()
        # FIXME: make rootdir of testvars could be customized
        mtbf_testvars_dir = "/mnt/mtbf_shared/testvars"
        if not hasattr(self.options, 'testvars') or not self.options.testvars:
            testvars = os.path.join(mtbf_testvars_dir,
                                    "testvars_" + self.serial + ".json")
            logger.info("testvar is [" + testvars + "]")
            if os.path.exists(testvars):
                self.options.testvars = parser.testvars = testvars
                logger.info("testvar [" + testvars + "] found")
            else:
                raise AttributeError("testvars[" + testvars +
                                     "] doesn't exist")

    def remove_settings_opt(self):
        for e in sys.argv[1:]:
            if '--settings' in e:
                idx = sys.argv.index(e)
                sys.argv.remove(e)
                if len(sys.argv) > idx and not '--' in sys.argv[idx]:
                    del sys.argv[idx]
                break

    @action(enabled=False)
    def mtbf_daily(self):
        parser = GaiaTestArguments()

        opts = []
        for k, v in self.kwargs.iteritems():
            opts.append("--" + k)
            opts.append(v)

        options, tests = parser.parse_args(sys.argv[1:] + opts)
        structured.commandline.add_logging_group(parser)
        logger = structured.commandline.setup_logging(options.logger_name,
                                                      options,
                                                      {"tbpl": sys.stdout})
        options.logger = logger
        options.testvars = [self.options.testvars]
        runner = GaiaTestRunner(**vars(options))
        runner.run_tests(["tests"])

    @action(enabled=True)
    def run_mtbf(self):
        mtbf.main(testvars=self.options.testvars, **self.kwargs)

    def execute(self):
        self.marionette.cleanup()
        self.marionette = Marionette(device_serial=self.serial, port=self.port)
        self.marionette.wait_for_port()
        # run test runner here
        self.remove_settings_opt()
        self.kwargs = {}
        if self.port:
            self.kwargs['address'] = "localhost:" + str(self.port)
        logger.info("Using address[localhost:" + str(self.port) + "]")
        self.start_monitoring()
        self.mtbf_daily()
        self.run_mtbf()
        self.stop_monitoring()

    def pre_flash(self):
        pass

    def flash(self):
        self.shallow_flash()
        self.full_flash()
        # workaround for waiting for boot

    def post_flash(self):
        self.setup()
        self.check_version()
        self.change_memory()
        self.add_7mobile_action()
        self.enable_certified_apps_debug()
        self.patch_marionette()

    def output_crash_report_no_to_log(self, serial):
        if serial in CrashScan.get_current_all_dev_serials():
            crash_result = CrashScan.get_crash_no_by_serial(serial)
            if crash_result['crashNo'] > 0:
                logger.error("CrashReportFound: device " + serial + " has " +
                             str(crash_result['crashNo']) + " crashes.")
            else:
                logger.info(
                    "CrashReportNotFound: No crash report found in device " +
                    serial)
        else:
            logger.error("CrashReportAdbError: Can't find device in ADB list")

    def collect_report(self, serial):
        self.output_crash_report_no_to_log(serial)

    def run(self):
        try:
            if self.get_free_device():
                self.mtbf_options()
                self.pre_flash()
                self.flash()
                self.device_obj.create_adb_forward()
                self.port = self.device_obj.adb_forwarded_port
                self.post_flash()
                self.execute()
                self.collect_report(self.serial)
        finally:
            self.release()
Esempio n. 4
0
class B2GMochitest(MochitestUtilsMixin):
    marionette = None

    def __init__(self,
                 marionette_args,
                 logger_options,
                 out_of_process=True,
                 profile_data_dir=None,
                 locations=os.path.join(here, 'server-locations.txt')):
        super(B2GMochitest, self).__init__(logger_options)
        self.marionette_args = marionette_args
        self.out_of_process = out_of_process
        self.locations_file = locations
        self.preferences = []
        self.webapps = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.test_script_args = [self.out_of_process]
        self.product = 'b2g'
        self.remote_chrome_test_dir = None

        if profile_data_dir:
            self.preferences = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('pref')
            ]
            self.webapps = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('webapp')
            ]

        # mozinfo is populated by the parent class
        if mozinfo.info['debug']:
            self.SERVER_STARTUP_TIMEOUT = 180
        else:
            self.SERVER_STARTUP_TIMEOUT = 90

    def setup_common_options(self, options):
        test_url = self.buildTestPath(options)
        # For B2G emulators buildURLOptions has been called
        # without calling buildTestPath first and that
        # causes manifestFile not to be set
        if not "manifestFile=tests.json" in self.urlOpts:
            self.urlOpts.append("manifestFile=%s" % options.manifestFile)

        if len(self.urlOpts) > 0:
            test_url += "?" + "&".join(self.urlOpts)
        self.test_script_args.append(test_url)

    def buildTestPath(self, options, testsToFilter=None):
        if options.manifestFile != 'tests.json':
            super(B2GMochitest, self).buildTestPath(options,
                                                    testsToFilter,
                                                    disabled=False)
        return self.buildTestURL(options)

    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = {
            "server": "%s:%s" % (options.webServer, options.httpPort),
            "OOP": "true" if self.out_of_process else "false"
        }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {
                "remote": options.webServer
            }
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest

    def run_tests(self, options):
        """ Prepare, configure, run tests and cleanup """

        self.setTestRoot(options)

        manifest = self.build_profile(options)
        self.logPreamble(self.getActiveTests(options))

        # configuring the message logger's buffering
        self.message_logger.buffering = options.quiet

        if options.debugger or not options.autorun:
            timeout = None
        else:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            timeout = options.timeout + 30.0

        self.log.info("runtestsb2g.py | Running tests: start.")
        status = 0
        try:

            def on_output(line):
                messages = self.message_logger.write(line)
                for message in messages:
                    if message['action'] == 'test_start':
                        self.runner.last_test = message['test']

            # The logging will be handled by on_output, so we set the stream to
            # None
            process_args = {'processOutputLine': on_output, 'stream': None}
            self.marionette_args['process_args'] = process_args
            self.marionette_args['profile'] = self.profile

            self.marionette = Marionette(**self.marionette_args)
            self.runner = self.marionette.runner
            self.app_ctx = self.runner.app_ctx

            self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                             'log', 'mochitest.log')
            if not self.app_ctx.dm.dirExists(posixpath.dirname(
                    self.remote_log)):
                self.app_ctx.dm.mkDirs(self.remote_log)

            if options.chrome:
                # Update chrome manifest file in profile with correct path.
                self.writeChromeManifest(options)

            self.leak_report_file = posixpath.join(
                self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log')

            # We don't want to copy the host env onto the device, so pass in an
            # empty env.
            self.browserEnv = self.buildBrowserEnv(options, env={})

            # B2G emulator debug tests still make external connections, so don't
            # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug
            # 1039019).
            if mozinfo.info[
                    'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
                del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
            self.runner.env.update(self.browserEnv)

            # Despite our efforts to clean up servers started by this script, in practice
            # we still see infrequent cases where a process is orphaned and interferes
            # with future tests, typically because the old server is keeping the port in use.
            # Try to avoid those failures by checking for and killing orphan servers before
            # trying to start new ones.
            self.killNamedOrphans('ssltunnel')
            self.killNamedOrphans('xpcshell')

            self.startServers(options, None)

            # In desktop mochitests buildTestPath is called before buildURLOptions. This
            # means options.manifestFile has already been converted to the proper json
            # style manifest. Not so with B2G, that conversion along with updating the URL
            # option will happen later. So backup and restore options.manifestFile to
            # prevent us from trying to pass in an instance of TestManifest via url param.
            manifestFile = options.manifestFile
            options.manifestFile = None
            self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
            options.manifestFile = manifestFile

            self.test_script_args.append(not options.emulator)
            self.test_script_args.append(options.wifi)
            self.test_script_args.append(options.chrome)

            self.runner.start(outputTimeout=timeout)

            self.marionette.wait_for_port()
            self.marionette.start_session()
            self.marionette.set_context(self.marionette.CONTEXT_CHROME)

            # Disable offline status management (bug 777145), otherwise the network
            # will be 'offline' when the mochitests start.  Presumably, the network
            # won't be offline on a real device, so we only do this for
            # emulators.
            self.marionette.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.io.manageOfflineStatus = false;
                Services.io.offline = false;
                """)

            self.marionette.execute_script("""
                let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
                Services.prefs.setBoolPref(SECURITY_PREF, true);

                if (!testUtils.hasOwnProperty("specialPowersObserver")) {
                  let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
                    .getService(Components.interfaces.mozIJSSubScriptLoader);
                  loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
                    testUtils);
                  testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
                  testUtils.specialPowersObserver.init();
                  testUtils.specialPowersObserver._loadFrameScript();
                }
                """)

            if options.chrome:
                self.app_ctx.dm.removeDir(self.remote_chrome_test_dir)
                self.app_ctx.dm.mkDir(self.remote_chrome_test_dir)
                local = super(B2GMochitest, self).getChromeTestDir(options)
                local = os.path.join(local, "chrome")
                remote = self.remote_chrome_test_dir
                self.log.info("pushing %s to %s on device..." %
                              (local, remote))
                self.app_ctx.dm.pushDir(local, remote)

            if os.path.isfile(self.test_script):
                with open(self.test_script, 'r') as script:
                    self.marionette.execute_script(
                        script.read(), script_args=self.test_script_args)
            else:
                self.marionette.execute_script(
                    self.test_script, script_args=self.test_script_args)
            status = self.runner.wait()

            if status is None:
                # the runner has timed out
                status = 124

            local_leak_file = tempfile.NamedTemporaryFile()
            self.app_ctx.dm.getFile(self.leak_report_file,
                                    local_leak_file.name)
            self.app_ctx.dm.removeFile(self.leak_report_file)

            mozleak.process_leak_log(
                local_leak_file.name,
                leak_thresholds=options.leakThresholds,
                ignore_missing_leaks=options.ignoreMissingLeaks,
                log=self.log,
            )
        except KeyboardInterrupt:
            self.log.info("runtests.py | Received keyboard interrupt.\n")
            status = -1
        except:
            traceback.print_exc()
            self.log.error(
                "Automation Error: Received unexpected exception while running application\n"
            )
            if hasattr(self, 'runner'):
                self.runner.check_for_crashes()
            status = 1

        self.stopServers()

        self.log.info("runtestsb2g.py | Running tests: end.")

        if manifest is not None:
            self.cleanup(manifest, options)
        return status

    def getGMPPluginPath(self, options):
        if options.gmp_path:
            return options.gmp_path
        return '/system/b2g/gmp-clearkey/0.1'

    def getChromeTestDir(self, options):
        # The chrome test directory returned here is the remote location
        # of chrome test files. A reference to this directory is requested
        # when building the profile locally, before self.app_ctx is defined.
        # To get around this, return a dummy directory until self.app_ctx
        # is defined; the correct directory will be returned later, over-
        # writing the dummy.
        if hasattr(self, 'app_ctx'):
            self.remote_chrome_test_dir = posixpath.join(
                self.app_ctx.remote_test_root, 'chrome')
            return self.remote_chrome_test_dir
        return 'dummy-chrome-test-dir'
Esempio n. 5
0
class B2GMochitest(MochitestUtilsMixin):
    marionette = None

    def __init__(self,
                 marionette_args,
                 logger_options,
                 out_of_process=True,
                 profile_data_dir=None,
                 locations=os.path.join(here, 'server-locations.txt')):
        super(B2GMochitest, self).__init__(logger_options)
        self.marionette_args = marionette_args
        self.out_of_process = out_of_process
        self.locations_file = locations
        self.preferences = []
        self.webapps = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.test_script_args = [self.out_of_process]
        self.product = 'b2g'
        self.remote_chrome_test_dir = None

        if profile_data_dir:
            self.preferences = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('pref')
            ]
            self.webapps = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('webapp')
            ]

        # mozinfo is populated by the parent class
        if mozinfo.info['debug']:
            self.SERVER_STARTUP_TIMEOUT = 180
        else:
            self.SERVER_STARTUP_TIMEOUT = 90

    def setup_common_options(self, options):
        test_url = self.buildTestPath(options)
        # For B2G emulators buildURLOptions has been called
        # without calling buildTestPath first and that
        # causes manifestFile not to be set
        if not "manifestFile=tests.json" in self.urlOpts:
            self.urlOpts.append("manifestFile=%s" % options.manifestFile)

        if len(self.urlOpts) > 0:
            test_url += "?" + "&".join(self.urlOpts)
        self.test_script_args.append(test_url)

    def buildTestPath(self, options, testsToFilter=None):
        if options.manifestFile != 'tests.json':
            super(B2GMochitest, self).buildTestPath(options,
                                                    testsToFilter,
                                                    disabled=False)
        return self.buildTestURL(options)

    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = {
            "server": "%s:%s" % (options.webServer, options.httpPort),
            "OOP": "true" if self.out_of_process else "false"
        }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {
                "remote": options.webServer
            }
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest

    def run_tests(self, options):
        """ Prepare, configure, run tests and cleanup """

        manifest = self.build_profile(options)
        self.logPreamble(self.getActiveTests(options))

        # configuring the message logger's buffering
        self.message_logger.buffering = options.quiet

        if options.debugger or not options.autorun:
            timeout = None
        else:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            timeout = options.timeout + 30.0

        self.log.info("runtestsb2g.py | Running tests: start.")
        status = 0
        try:

            def on_output(line):
                messages = self.message_logger.write(line)
                for message in messages:
                    if message['action'] == 'test_start':
                        self.runner.last_test = message['test']

            # The logging will be handled by on_output, so we set the stream to None
            process_args = {'processOutputLine': on_output, 'stream': None}
            self.marionette_args['process_args'] = process_args
            self.marionette_args['profile'] = self.profile

            self.marionette = Marionette(**self.marionette_args)
            self.runner = self.marionette.runner
            self.app_ctx = self.runner.app_ctx

            self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                             'log', 'mochitest.log')
            if not self.app_ctx.dm.dirExists(posixpath.dirname(
                    self.remote_log)):
                self.app_ctx.dm.mkDirs(self.remote_log)

            if options.chrome:
                # Update chrome manifest file in profile with correct path.
                self.writeChromeManifest(options)

            self.leak_report_file = posixpath.join(
                self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log')

            # We don't want to copy the host env onto the device, so pass in an
            # empty env.
            self.browserEnv = self.buildBrowserEnv(options, env={})

            # B2G emulator debug tests still make external connections, so don't
            # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug 1039019).
            if mozinfo.info[
                    'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
                del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
            self.runner.env.update(self.browserEnv)

            self.startServers(options, None)
            self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
            self.test_script_args.append(not options.emulator)
            self.test_script_args.append(options.wifi)
            self.test_script_args.append(options.chrome)

            self.runner.start(outputTimeout=timeout)

            self.marionette.wait_for_port()
            self.marionette.start_session()
            self.marionette.set_context(self.marionette.CONTEXT_CHROME)

            # Disable offline status management (bug 777145), otherwise the network
            # will be 'offline' when the mochitests start.  Presumably, the network
            # won't be offline on a real device, so we only do this for emulators.
            self.marionette.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.io.manageOfflineStatus = false;
                Services.io.offline = false;
                """)

            if options.chrome:
                self.app_ctx.dm.removeDir(self.remote_chrome_test_dir)
                self.app_ctx.dm.mkDir(self.remote_chrome_test_dir)
                local = super(B2GMochitest, self).getChromeTestDir(options)
                local = os.path.join(local, "chrome")
                remote = self.remote_chrome_test_dir
                self.log.info("pushing %s to %s on device..." %
                              (local, remote))
                self.app_ctx.dm.pushDir(local, remote)

            if os.path.isfile(self.test_script):
                with open(self.test_script, 'r') as script:
                    self.marionette.execute_script(
                        script.read(), script_args=self.test_script_args)
            else:
                self.marionette.execute_script(
                    self.test_script, script_args=self.test_script_args)
            status = self.runner.wait()

            if status is None:
                # the runner has timed out
                status = 124

            local_leak_file = tempfile.NamedTemporaryFile()
            self.app_ctx.dm.getFile(self.leak_report_file,
                                    local_leak_file.name)
            self.app_ctx.dm.removeFile(self.leak_report_file)

            processLeakLog(local_leak_file.name, options)
        except KeyboardInterrupt:
            self.log.info("runtests.py | Received keyboard interrupt.\n")
            status = -1
        except:
            traceback.print_exc()
            self.log.error(
                "Automation Error: Received unexpected exception while running application\n"
            )
            if hasattr(self, 'runner'):
                self.runner.check_for_crashes()
            status = 1

        self.stopServers()

        self.log.info("runtestsb2g.py | Running tests: end.")

        if manifest is not None:
            self.cleanup(manifest, options)
        return status

    def getGMPPluginPath(self, options):
        # TODO: bug 1043403
        return None

    def getChromeTestDir(self, options):
        # The chrome test directory returned here is the remote location
        # of chrome test files. A reference to this directory is requested
        # when building the profile locally, before self.app_ctx is defined.
        # To get around this, return a dummy directory until self.app_ctx
        # is defined; the correct directory will be returned later, over-
        # writing the dummy.
        if hasattr(self, 'app_ctx'):
            self.remote_chrome_test_dir = posixpath.join(
                self.app_ctx.remote_test_root, 'chrome')
            return self.remote_chrome_test_dir
        return 'dummy-chrome-test-dir'
Esempio n. 6
0
class B2GMochitest(MochitestUtilsMixin):
    marionette = None

    def __init__(self, marionette_args,
                 logger_options,
                 out_of_process=True,
                 profile_data_dir=None,
                 locations=os.path.join(here, 'server-locations.txt')):
        super(B2GMochitest, self).__init__(logger_options)
        self.marionette_args = marionette_args
        self.out_of_process = out_of_process
        self.locations_file = locations
        self.preferences = []
        self.webapps = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.test_script_args = [self.out_of_process]
        self.product = 'b2g'
        self.remote_chrome_test_dir = None

        if profile_data_dir:
            self.preferences = [
                os.path.join(
                    profile_data_dir,
                    f) for f in os.listdir(profile_data_dir) if f.startswith('pref')]
            self.webapps = [
                os.path.join(
                    profile_data_dir,
                    f) for f in os.listdir(profile_data_dir) if f.startswith('webapp')]

        # mozinfo is populated by the parent class
        if mozinfo.info['debug']:
            self.SERVER_STARTUP_TIMEOUT = 180
        else:
            self.SERVER_STARTUP_TIMEOUT = 90

    def setup_common_options(self, options):
        test_url = self.buildTestPath(options)
        # For B2G emulators buildURLOptions has been called
        # without calling buildTestPath first and that
        # causes manifestFile not to be set
        if not "manifestFile=tests.json" in self.urlOpts:
            self.urlOpts.append("manifestFile=%s" % options.manifestFile)

        if len(self.urlOpts) > 0:
            test_url += "?" + "&".join(self.urlOpts)
        self.test_script_args.append(test_url)

    def buildTestPath(self, options, testsToFilter=None):
        if options.manifestFile != 'tests.json':
            super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False)
        return self.buildTestURL(options)

    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = {
            "server": "%s:%s" %
            (options.webServer,
             options.httpPort),
            "OOP": "true" if self.out_of_process else "false"}
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {"remote": options.webServer}
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest

    def run_tests(self, options):
        """ Prepare, configure, run tests and cleanup """

        self.setTestRoot(options)

        manifest = self.build_profile(options)
        self.logPreamble(self.getActiveTests(options))

        # configuring the message logger's buffering
        self.message_logger.buffering = options.quiet

        if options.debugger or not options.autorun:
            timeout = None
        else:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            timeout = options.timeout + 30.0

        self.log.info("runtestsb2g.py | Running tests: start.")
        status = 0
        try:
            def on_output(line):
                messages = self.message_logger.write(line)
                for message in messages:
                    if message['action'] == 'test_start':
                        self.runner.last_test = message['test']

            # The logging will be handled by on_output, so we set the stream to
            # None
            process_args = {'processOutputLine': on_output,
                            'stream': None}
            self.marionette_args['process_args'] = process_args
            self.marionette_args['profile'] = self.profile

            self.marionette = Marionette(**self.marionette_args)
            self.runner = self.marionette.runner
            self.app_ctx = self.runner.app_ctx

            self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                             'log', 'mochitest.log')
            if not self.app_ctx.dm.dirExists(
                posixpath.dirname(
                    self.remote_log)):
                self.app_ctx.dm.mkDirs(self.remote_log)

            if options.chrome:
                # Update chrome manifest file in profile with correct path.
                self.writeChromeManifest(options)

            self.leak_report_file = posixpath.join(
                self.app_ctx.remote_test_root,
                'log',
                'runtests_leaks.log')

            # We don't want to copy the host env onto the device, so pass in an
            # empty env.
            self.browserEnv = self.buildBrowserEnv(options, env={})

            # B2G emulator debug tests still make external connections, so don't
            # pass MOZ_DISABLE_NONLOCAL_CONNECTIONS to them for now (bug
            # 1039019).
            if mozinfo.info[
                    'debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
                del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
            self.runner.env.update(self.browserEnv)

            # Despite our efforts to clean up servers started by this script, in practice
            # we still see infrequent cases where a process is orphaned and interferes
            # with future tests, typically because the old server is keeping the port in use.
            # Try to avoid those failures by checking for and killing orphan servers before
            # trying to start new ones.
            self.killNamedOrphans('ssltunnel')
            self.killNamedOrphans('xpcshell')

            self.startServers(options, None)

            # In desktop mochitests buildTestPath is called before buildURLOptions. This
            # means options.manifestFile has already been converted to the proper json
            # style manifest. Not so with B2G, that conversion along with updating the URL
            # option will happen later. So backup and restore options.manifestFile to
            # prevent us from trying to pass in an instance of TestManifest via url param.
            manifestFile = options.manifestFile
            options.manifestFile = None
            self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
            options.manifestFile = manifestFile

            self.test_script_args.append(not options.emulator)
            self.test_script_args.append(options.wifi)
            self.test_script_args.append(options.chrome)

            self.runner.start(outputTimeout=timeout)

            self.marionette.wait_for_port()
            self.marionette.start_session()
            self.marionette.set_context(self.marionette.CONTEXT_CHROME)

            # Disable offline status management (bug 777145), otherwise the network
            # will be 'offline' when the mochitests start.  Presumably, the network
            # won't be offline on a real device, so we only do this for
            # emulators.
            self.marionette.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.io.manageOfflineStatus = false;
                Services.io.offline = false;
                """)

            self.marionette.execute_script("""
                let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
                Services.prefs.setBoolPref(SECURITY_PREF, true);

                if (!testUtils.hasOwnProperty("specialPowersObserver")) {
                  let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
                    .getService(Components.interfaces.mozIJSSubScriptLoader);
                  loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
                    testUtils);
                  testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
                  testUtils.specialPowersObserver.init();
                  testUtils.specialPowersObserver._loadFrameScript();
                }
                """)

            if options.chrome:
                self.app_ctx.dm.removeDir(self.remote_chrome_test_dir)
                self.app_ctx.dm.mkDir(self.remote_chrome_test_dir)
                local = super(B2GMochitest, self).getChromeTestDir(options)
                local = os.path.join(local, "chrome")
                remote = self.remote_chrome_test_dir
                self.log.info(
                    "pushing %s to %s on device..." %
                    (local, remote))
                self.app_ctx.dm.pushDir(local, remote)

            if os.path.isfile(self.test_script):
                with open(self.test_script, 'r') as script:
                    self.marionette.execute_script(
                        script.read(),
                        script_args=self.test_script_args)
            else:
                self.marionette.execute_script(
                    self.test_script,
                    script_args=self.test_script_args)
            status = self.runner.wait()

            if status is None:
                # the runner has timed out
                status = 124

            local_leak_file = tempfile.NamedTemporaryFile()
            self.app_ctx.dm.getFile(
                self.leak_report_file,
                local_leak_file.name)
            self.app_ctx.dm.removeFile(self.leak_report_file)

            mozleak.process_leak_log(
                local_leak_file.name,
                leak_thresholds=options.leakThresholds,
                ignore_missing_leaks=options.ignoreMissingLeaks,
                log=self.log,
                stack_fixer=get_stack_fixer_function(options.utilityPath,
                                                     options.symbolsPath),
            )
        except KeyboardInterrupt:
            self.log.info("runtests.py | Received keyboard interrupt.\n")
            status = -1
        except:
            traceback.print_exc()
            self.log.error(
                "Automation Error: Received unexpected exception while running application\n")
            if hasattr(self, 'runner'):
                self.runner.check_for_crashes()
            status = 1

        self.stopServers()

        self.log.info("runtestsb2g.py | Running tests: end.")

        if manifest is not None:
            self.cleanup(manifest, options)
        return status

    def getGMPPluginPath(self, options):
        if options.gmp_path:
            return options.gmp_path
        return '/system/b2g/gmp-clearkey/0.1'

    def getChromeTestDir(self, options):
        # The chrome test directory returned here is the remote location
        # of chrome test files. A reference to this directory is requested
        # when building the profile locally, before self.app_ctx is defined.
        # To get around this, return a dummy directory until self.app_ctx
        # is defined; the correct directory will be returned later, over-
        # writing the dummy.
        if hasattr(self, 'app_ctx'):
            self.remote_chrome_test_dir = posixpath.join(
                self.app_ctx.remote_test_root,
                'chrome')
            return self.remote_chrome_test_dir
        return 'dummy-chrome-test-dir'
Esempio n. 7
0
class B2GDesktopReftest(RefTest):
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert (self.marionette.wait_for_port())
        self.marionette.start_session()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, test_path, options):
        reftestlist = self.getManifestPath(test_path)
        if not reftestlist.startswith('file://'):
            reftestlist = 'file://%s' % reftestlist

        self.profile = self.create_profile(options,
                                           reftestlist,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = {
            'processOutputLine': [self._on_output],
            'onTimeout': [self._on_timeout],
            'kill_on_timeout': False
        }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(
            options.app,
            ignore_window_size=options.ignoreWindowSize,
            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d", os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s", self.last_test,
                     -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, reftestlist, profile_to_clone=None):
        profile = RefTest.createReftestProfile(
            self, options, reftestlist, profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        prefs[
            "b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html"
        prefs[
            "b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp"
        prefs["browser.tabs.remote"] = False
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs[
            "network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        prefs["reftest.uri"] = "%s" % reftestlist
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self,
                           app,
                           ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])
        return cmd, args

    def _on_output(self, line):
        print(line)
        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)
Esempio n. 8
0
class B2GMochitest(MochitestUtilsMixin):
    marionette = None

    def __init__(self, marionette_args,
                       out_of_process=True,
                       profile_data_dir=None,
                       locations=os.path.join(here, 'server-locations.txt')):
        super(B2GMochitest, self).__init__()
        self.marionette_args = marionette_args
        self.out_of_process = out_of_process
        self.locations_file = locations
        self.preferences = []
        self.webapps = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.test_script_args = [self.out_of_process]
        self.product = 'b2g'

        # structured logging
        self.message_logger = MessageLogger(logger=log)

        if profile_data_dir:
            self.preferences = [os.path.join(profile_data_dir, f)
                                 for f in os.listdir(profile_data_dir) if f.startswith('pref')]
            self.webapps = [os.path.join(profile_data_dir, f)
                             for f in os.listdir(profile_data_dir) if f.startswith('webapp')]

        # mozinfo is populated by the parent class
        if mozinfo.info['debug']:
            self.SERVER_STARTUP_TIMEOUT = 180
        else:
            self.SERVER_STARTUP_TIMEOUT = 90

    def setup_common_options(self, options):
        test_url = self.buildTestPath(options)
        # For B2G emulators buildURLOptions has been called
        # without calling buildTestPath first and that
        # causes manifestFile not to be set
        if not "manifestFile=tests.json" in self.urlOpts:
            self.urlOpts.append("manifestFile=%s" % options.manifestFile)

        if len(self.urlOpts) > 0:
            test_url += "?" + "&".join(self.urlOpts)
        self.test_script_args.append(test_url)

    def buildTestPath(self, options, testsToFilter=None):
        if options.manifestFile != 'tests.json':
            super(B2GMochitest, self).buildTestPath(options, testsToFilter, disabled=False)
        return self.buildTestURL(options)

    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort),
                          "OOP": "true" if self.out_of_process else "false" }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {"remote": options.webServer}
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest

    def run_tests(self, options):
        """ Prepare, configure, run tests and cleanup """

        manifest = self.build_profile(options)
        self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")

        # configuring the message logger's buffering
        self.message_logger.buffering = options.quiet

        if options.debugger or not options.autorun:
            timeout = None
        else:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            timeout = options.timeout + 30.0

        log.info("runtestsb2g.py | Running tests: start.")
        status = 0
        try:
            def on_output(line):
                messages = self.message_logger.write(line)
                for message in messages:
                    if message['action'] == 'test_start':
                        self.runner.last_test = message['test']

            # The logging will be handled by on_output, so we set the stream to None
            process_args = {'processOutputLine': on_output,
                            'stream': None}
            self.marionette_args['process_args'] = process_args
            self.marionette_args['profile'] = self.profile

            self.marionette = Marionette(**self.marionette_args)
            self.runner = self.marionette.runner
            self.app_ctx = self.runner.app_ctx

            self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                             'log', 'mochitest.log')
            if not self.app_ctx.dm.dirExists(posixpath.dirname(self.remote_log)):
                self.app_ctx.dm.mkDirs(self.remote_log)

            self.startServers(options, None)
            self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
            self.test_script_args.append(not options.emulator)
            self.test_script_args.append(options.wifi)


            self.runner.start(outputTimeout=timeout)

            self.marionette.wait_for_port()
            self.marionette.start_session()
            self.marionette.set_context(self.marionette.CONTEXT_CHROME)

            # Disable offline status management (bug 777145), otherwise the network
            # will be 'offline' when the mochitests start.  Presumably, the network
            # won't be offline on a real device, so we only do this for emulators.
            self.marionette.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.io.manageOfflineStatus = false;
                Services.io.offline = false;
                """)

            if os.path.isfile(self.test_script):
                with open(self.test_script, 'r') as script:
                    self.marionette.execute_script(script.read(),
                                                   script_args=self.test_script_args)
            else:
                self.marionette.execute_script(self.test_script,
                                               script_args=self.test_script_args)
            status = self.runner.wait()

            if status is None:
                # the runner has timed out
                status = 124
        except KeyboardInterrupt:
            log.info("runtests.py | Received keyboard interrupt.\n");
            status = -1
        except:
            traceback.print_exc()
            log.error("Automation Error: Received unexpected exception while running application\n")
            if hasattr(self, 'runner'):
                self.runner.check_for_crashes()
            status = 1

        self.stopServers()

        log.info("runtestsb2g.py | Running tests: end.")

        if manifest is not None:
            self.cleanup(manifest, options)
        return status

    def getGMPPluginPath(self, options):
        # TODO: bug 1043403
        return None
Esempio n. 9
0
class MtbfJobRunner(BaseActionRunner):
    serial = None
    marionette = None
    flash_params = {
        'branch': 'mozilla-b2g34_v2_1-flame-kk-eng',
        'build': '',
        'build_id': ''
    }
    flashed = False

    def __init__(self, **kwargs):
        self.logger = logger
        BaseActionRunner.__init__(self)

    def setup(self):
        if not self.serial or not self.port:
            logger.error("Fail to get device")
            raise DMError
        self.marionette and self.marionette.session and self.marionette.cleanup()
        self.dm = mozdevice.DeviceManagerADB(deviceSerial=self.serial, port=self.port)
        self.marionette = Marionette(device_serial=self.serial, port=self.port)
        self.marionette.start_session()
        self.device = GaiaDevice(marionette=self.marionette, manager=self.dm)
        self.apps = GaiaApps(self.marionette)
        self.data_layer = GaiaData(self.marionette)
        self.device.wait_for_b2g_ready()

    def adb_test(self):
        if not hasattr(self, 'serial') or os.system("ANDROID_SERIAL=" + self.serial + " adb shell ls") != 0:
            logger.error("Device not found or can't be controlled")
            return False
        return True

    @action(enabled=False)
    def add_7mobile_action(self):
        # workaround for waiting for boot
        self.marionette.start_session()
        self.data_layer = GaiaData(self.marionette)
        self.data_layer.set_setting('ril.data.apnSettings',
                                    [[
                                        {"carrier": "(7-Mobile) (MMS)",
                                            "apn": "opentalk",
                                            "mmsc": "http://mms",
                                            "mmsproxy": "210.241.199.199",
                                            "mmsport": "9201",
                                            "types": ["mms"]},
                                        {"carrier": "(7-Mobile) (Internet)",
                                            "apn": "opentalk",
                                            "types": ["default", "supl"]}
                                    ]])
        return True

    @action(enabled=False)
    def change_memory(self):
        # This function only work in flame
        # TODO: use native adb/fastboot command to change memory?
        # Make sure it's in fastboot mode, TODO: leverage all fastboot command in one task function
        memory = 512 # default set 512
        if 'MEM' in os.environ:
            memory = os.environ['MEM']
        elif self.settings['change_memory']['enabled'] and 'memory' in self.settings['change_memory']:
            memory = self.settings['change_memory']['memory']
        if self.adb_test():
            os.system("adb reboot bootloader")
            memory = 512
            mem_str = str(memory)
            os.system("fastboot oem mem " + mem_str)
            # Preventing from async timing of fastboot
            os.system("fastboot reboot")
            self.device_obj.create_adb_forward(self.port)
            return True
        logger.error("Can't find device")
        self.marionette.wait_for_port()
        self.device_obj.create_adb_forward(self.port)
        return False

    @action(enabled=True)
    def collect_memory_report(self):
        zip_utils.collect_about_memory("mtbf_driver")  # TODO: give a correct path for about memory folder

    def get_free_device(self):
        do = device_pool.get_device(self.serial)
        if do:
            # Record device serial and store dp instance
            self.serial = do.serial
            self.device_obj = do
            if do.create_adb_forward():
                self.port = do.adb_forwarded_port
                logger.info("Device found, ANDROID_SERIAL= " + self.serial)
                return do
            logger.error("Port forwarding failed")
            raise DMError
        logger.warning("No available device.  Please retry after device released")
        # TODO: more handling for no available device

    def validate_flash_params(self):
        ## Using system environment variable as temporary solution TODO: use other way for input params
        ## Check if package(files)/folder exists and return, else raise exception
        if not 'FLASH_BASEDIR' in os.environ:
            raise AttributeError("No FLASH_BASEDIR set")
        basedir = os.environ['FLASH_BASEDIR']
        if not 'FLASH_BUILDID' in os.environ:
        ## TODO: if latest/ exists, use latest as default
            logging.info("No build id set. search in base dir")
            buildid = ""
            flash_dir = basedir
        else:
            buildid = os.environ['FLASH_BUILDID']
            # re-format build id based on pvt folder structure
            if '-' in buildid:
                buildid = buildid.replace("-", "")
            year = buildid[:4]
            month = buildid[4:6]
            datetime = '-'.join([year, month] + [buildid[i + 6:i + 8] for i in range(0, len(buildid[6:]), 2)])
            flash_dir = os.path.join(basedir, year, month, datetime)
        if not os.path.isdir(flash_dir):
            raise AttributeError("Flash  directory " + flash_dir + " not exist")
        flash_files = glob.glob(os.path.join(flash_dir, '*'))
        flash_src = {}
        for flash_file in flash_files:
            logger.debug("Flash source found: [" + flash_file + "]")
            if os.path.isdir(flash_file):
                continue
            elif re.match("^b2g-[0-9]*.*\.tar\.gz$", flash_file):
                flash_src['gecko'] = flash_file
            elif "gaia.zip" == flash_file:
                flash_src['gaia'] = flash_file
            elif "symbol" in flash_file:
                flash_src['symbol'] = flash_file
            elif "zip" in flash_file and not ("gaia.zip" in flash_file):
                flash_src['image'] = flash_file
        return flash_src

    @action(enabled=True)
    def full_flash(self):
        flash_src = self.validate_flash_params()
        if self.flashed:
            logger.warning("Flash performed; skip flashing")
            return True
        if not flash_src:
            logger.warning("Invalid build folder/build_id, skip flashing")
            return False
        if not 'image' in flash_src:
            logger.warning("No available image for flash, skip flashing")
            return False
        try:
            self.temp_dir = tempfile.mkdtemp()
            logger.info('Create temporary folder:' + self.temp_dir)
            Decompressor().unzip(flash_src['image'], self.temp_dir)
            # set the permissions to rwxrwxr-x (509 in python's os.chmod)
            os.chmod(self.temp_dir + '/b2g-distro/flash.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
            os.chmod(self.temp_dir + '/b2g-distro/load-config.sh', stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
            os.system('cd ' + self.temp_dir + '/b2g-distro; ./flash.sh -f')
            # support NO_FTU environment for skipping FTU (e.g. monkey test)
            if 'NO_FTU' in os.environ and os.environ['NO_FTU'] == 'true':
                logger.log('The [NO_FTU] is [true].')
                os.system("ANDROID_SERIAL=" + self.serial + 'adb wait-for-device && adb shell stop b2g; (RET=$(adb root); if ! case ${RET} in *"cannot"*) true;; *) false;; esac; then adb remount && sleep 5; else exit 1; fi; ./disable_ftu.py) || (echo "No root permission, cannot setup NO_FTU."); adb reboot;')
        finally:
            try:
                shutil.rmtree(self.temp_dir)  # delete directory
            except OSError:
                logger.error('Can not remove temporary folder:' + self.temp_dir, level=Logger._LEVEL_WARNING)
        self.flashed = True

    @action(enabled=False)
    def shallow_flash(self):
        flash_src = self.validate_flash_params()
        if self.flashed:
            logger.warning("Flash performed; skip flashing")
            return True
        if not flash_src:
            logger.warning("Invalid build folder/build_id, skip flashing")
            return False
        if not 'gaia' in flash_src or not 'gecko' in flash_src:
            logger.warning("No gaia or gecko archive, skip flashing")
            return False
        cmd = 'flash_tool/shallow_flash.sh -y --gecko="' + flash_src['gecko'] + '" --gaia="' + flash_src['gaia'] + '"'
        if _platform == 'darwin':
            cmd = cmd.replace('=', ' ')
        ret = os.system(cmd)
        if ret != 0:
            logger.info("Shallow flash ended abnormally")
            return False
        self.flashed = True
        os.system("ANDROID_SERIAL=" + self.serial + " adb wait-for-device")

    @action(enabled=True)
    def enable_certified_apps_debug(self):
        if self.serial:
            os.system("ANDROID_SERIAL=" + self.serial + " flash_tool/enable_certified_apps_for_devtools.sh && adb wait-for-device")
            logger.debug("Successfully enabling certified apps for debugging")
            return True
        return False

    def release(self):
        device_pool.release()

    def check_version(self):
        # FIXME: fix check version to use package import
        cmd = "cd flash_tool/ && NO_COLOR=TRUE ./check_versions.py | sed -e 's| \{2,\}||g' -e 's|\[0m||g'"
        if self.serial:
            cmd = "ANDROID_SERIAL=" + self.serial + " " + cmd
        os.system(cmd)

    @action(enabled=False)
    def patch_marionette(self):
        os.system("M_PATH=/mnt/mtbf_shared/paul/ /mnt/mtbf_shared/paul/marionette_update.sh")
        import time
        time.sleep(10)
        self.device_obj.create_adb_forward(self.port)

    def mtbf_options(self):
        ## load mtbf parameters
        if not 'MTBF_TIME' in os.environ:
            logger.warning("MTBF_TIME is not set")
        if not 'MTBF_CONF' in os.environ:
            logger.warning("MTBF_CONF is not set")

        parser = self.parser.parser
        parser.add_argument("--testvars", help="Test variables for b2g")
        self.parse_options()
        # FIXME: make rootdir of testvars could be customized
        mtbf_testvars_dir = "/mnt/mtbf_shared/testvars"
        if not hasattr(self.options, 'testvars') or not self.options.testvars:
            testvars = os.path.join(mtbf_testvars_dir, "testvars_" + self.serial + ".json")
            logger.info("testvar is [" + testvars + "]")
            if os.path.exists(testvars):
                self.options.testvars = parser.testvars = testvars
                logger.info("testvar [" + testvars + "] found")
            else:
                raise AttributeError("testvars[" + testvars + "] doesn't exist")

        ## #TODO: finish parsing arguments for flashing
        ## parser.add_argument("--flashdir", help="directory for pulling build")
        ## parser.add_argument("--buildid", help="build id for pulling build")
        ## self.parse_options()
        ## if hasattr(self.parser.option, 'flashdir'):
        ##     os.environ['FLASH_BASEDIR'] = self.parser.option.flashdir
        ## if hasattr(self.parser.option, 'buildid'):
        ##     os.environ['FLASH_BUILDID'] = self.parser.option.buildid

    def remove_settings_opt(self):
        for e in sys.argv[1:]:
            if '--settings' in e:
                idx = sys.argv.index(e)
                sys.argv.remove(e)
                if len(sys.argv) > idx and not '--' in sys.argv[idx]:
                    del sys.argv[idx]
                break

    @action(enabled=False)
    def mtbf_daily(self):
        parser = GaiaTestOptions()

        opts = []
        for k, v in self.kwargs.iteritems():
            opts.append("--" + k)
            opts.append(v)

        options, tests = parser.parse_args(sys.argv[1:] + opts)
        structured.commandline.add_logging_group(parser)
        logger = structured.commandline.setup_logging(
            options.logger_name, options, {"tbpl": sys.stdout})
        options.logger = logger
        options.testvars = [self.options.testvars]
        runner = GaiaTestRunner(**vars(options))
        runner.run_tests(["tests"])

    @action(enabled=True)
    def run_mtbf(self):
        mtbf.main(testvars=self.options.testvars, **self.kwargs)

    def execute(self):
        self.marionette.cleanup()
        self.marionette = Marionette(device_serial=self.serial, port=self.port)
        self.marionette.wait_for_port()
        # run test runner here
        self.remove_settings_opt()
        self.kwargs = {}
        if self.port:
            self.kwargs['address'] = "localhost:" + str(self.port)
        logger.info("Using address[localhost:" + str(self.port) + "]")
        self.mtbf_daily()
        self.run_mtbf()

    def pre_flash(self):
        pass

    def flash(self):
        self.shallow_flash()
        self.full_flash()
        # workaround for waiting for boot

    def post_flash(self):
        self.setup()
        self.check_version()
        self.change_memory()
        self.add_7mobile_action()
        self.enable_certified_apps_debug()
        self.patch_marionette()

    def output_crash_report_no_to_log(self, serial):
        if serial in CrashScan.get_current_all_dev_serials():
            crash_result = CrashScan.get_crash_no_by_serial(serial)
            if crash_result['crashNo'] > 0:
                logger.error("CrashReportFound: device " + serial + " has " + str(crash_result['crashNo']) + " crashes.")
            else:
                logger.info("CrashReportNotFound: No crash report found in device " + serial)
        else:
            logger.error("CrashReportAdbError: Can't find device in ADB list")

    def collect_report(self, serial):
        self.output_crash_report_no_to_log(serial)

    def run(self):
        try:
            if self.get_free_device():
                self.mtbf_options()
                self.pre_flash()
                self.flash()
                self.device_obj.create_adb_forward()
                self.port = self.device_obj.adb_forwarded_port
                self.post_flash()
                self.execute()
                self.collect_report(self.serial)
        finally:
            self.release()
Esempio n. 10
0
class B2GDesktopReftest(RefTest):
    marionette = None

    def __init__(self, marionette_args):
        RefTest.__init__(self)
        self.last_test = os.path.basename(__file__)
        self.marionette_args = marionette_args
        self.profile = None
        self.runner = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.timeout = None

    def run_marionette_script(self):
        self.marionette = Marionette(**self.marionette_args)
        assert(self.marionette.wait_for_port())
        self.marionette.start_session()
        self.marionette.set_context(self.marionette.CONTEXT_CHROME)

        if os.path.isfile(self.test_script):
            f = open(self.test_script, 'r')
            self.test_script = f.read()
            f.close()
        self.marionette.execute_script(self.test_script)

    def run_tests(self, test_path, options):
        reftestlist = self.getManifestPath(test_path)
        if not reftestlist.startswith('file://'):
            reftestlist = 'file://%s' % reftestlist

        self.profile = self.create_profile(options, reftestlist,
                                           profile_to_clone=options.profile)
        env = self.buildBrowserEnv(options, self.profile.profile)
        kp_kwargs = { 'processOutputLine': [self._on_output],
                      'onTimeout': [self._on_timeout],
                      'kill_on_timeout': False }

        if not options.debugger:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            self.timeout = options.timeout + 30.0

        log.info("%s | Running tests: start.", os.path.basename(__file__))
        cmd, args = self.build_command_line(options.app,
                            ignore_window_size=options.ignoreWindowSize,
                            browser_arg=options.browser_arg)
        self.runner = FirefoxRunner(profile=self.profile,
                                    binary=cmd,
                                    cmdargs=args,
                                    env=env,
                                    process_class=ProcessHandler,
                                    process_args=kp_kwargs,
                                    symbols_path=options.symbolsPath)

        status = 0
        try:
            self.runner.start(outputTimeout=self.timeout)
            log.info("%s | Application pid: %d",
                     os.path.basename(__file__),
                     self.runner.process_handler.pid)

            # kick starts the reftest harness
            self.run_marionette_script()
            status = self.runner.wait()
        finally:
            self.runner.check_for_crashes(test_name=self.last_test)
            self.runner.cleanup()

        if status > 0:
            log.testFail("%s | application terminated with exit code %s",
                         self.last_test, status)
        elif status < 0:
            log.info("%s | application killed with signal %s",
                         self.last_test, -status)

        log.info("%s | Running tests: end.", os.path.basename(__file__))
        return status

    def create_profile(self, options, reftestlist, profile_to_clone=None):
        profile = RefTest.createReftestProfile(self, options, reftestlist,
                                               profile_to_clone=profile_to_clone)

        prefs = {}
        # Turn off the locale picker screen
        prefs["browser.firstrun.show.localepicker"] = False
        prefs["b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html"
        prefs["b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp"
        prefs["dom.ipc.tabs.disabled"] = False
        prefs["dom.mozBrowserFramesEnabled"] = True
        prefs["font.size.inflation.emPerLine"] = 0
        prefs["font.size.inflation.minTwips"] = 0
        prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org"
        prefs["reftest.browser.iframe.enabled"] = False
        prefs["reftest.remote"] = False
        prefs["reftest.uri"] = "%s" % reftestlist
        # Set a future policy version to avoid the telemetry prompt.
        prefs["toolkit.telemetry.prompted"] = 999
        prefs["toolkit.telemetry.notifiedOptOut"] = 999

        # Set the extra prefs.
        profile.set_preferences(prefs)
        return profile

    def build_command_line(self, app, ignore_window_size=False,
                           browser_arg=None):
        cmd = os.path.abspath(app)
        args = ['-marionette']

        if browser_arg:
            args += [browser_arg]

        if not ignore_window_size:
            args.extend(['--screen', '800x1000'])
        return cmd, args

    def _on_output(self, line):
        print(line)
        # TODO use structured logging
        if "TEST-START" in line and "|" in line:
            self.last_test = line.split("|")[1].strip()

    def _on_timeout(self):
        msg = "%s | application timed out after %s seconds with no output"
        log.testFail(msg % (self.last_test, self.timeout))

        # kill process to get a stack
        self.runner.stop(sig=signal.SIGABRT)
Esempio n. 11
0
class B2GMochitest(MochitestUtilsMixin):
    marionette = None

    def __init__(self,
                 marionette_args,
                 out_of_process=True,
                 profile_data_dir=None,
                 locations=os.path.join(here, 'server-locations.txt')):
        super(B2GMochitest, self).__init__()
        self.marionette_args = marionette_args
        self.out_of_process = out_of_process
        self.locations_file = locations
        self.preferences = []
        self.webapps = None
        self.test_script = os.path.join(here, 'b2g_start_script.js')
        self.test_script_args = [self.out_of_process]
        self.product = 'b2g'

        if profile_data_dir:
            self.preferences = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('pref')
            ]
            self.webapps = [
                os.path.join(profile_data_dir, f)
                for f in os.listdir(profile_data_dir) if f.startswith('webapp')
            ]

        # mozinfo is populated by the parent class
        if mozinfo.info['debug']:
            self.SERVER_STARTUP_TIMEOUT = 180
        else:
            self.SERVER_STARTUP_TIMEOUT = 90

    def setup_common_options(self, options):
        test_url = self.buildTestPath(options)
        # For B2G emulators buildURLOptions has been called
        # without calling buildTestPath first and that
        # causes manifestFile not to be set
        if not "manifestFile=tests.json" in self.urlOpts:
            self.urlOpts.append("manifestFile=%s" % options.manifestFile)

        if len(self.urlOpts) > 0:
            test_url += "?" + "&".join(self.urlOpts)
        self.test_script_args.append(test_url)

    def buildTestPath(self, options):
        if options.manifestFile != 'tests.json':
            super(B2GMochitest, self).buildTestPath(options, disabled=False)
        return self.buildTestURL(options)

    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = {
            "server": "%s:%s" % (options.webServer, options.httpPort),
            "OOP": "true" if self.out_of_process else "false"
        }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {
                "remote": options.webServer
            }
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest

    def run_tests(self, options):
        """ Prepare, configure, run tests and cleanup """

        manifest = self.build_profile(options)
        self.leak_report_file = os.path.join(options.profilePath,
                                             "runtests_leaks.log")

        if options.debugger or not options.autorun:
            timeout = None
        else:
            if not options.timeout:
                if mozinfo.info['debug']:
                    options.timeout = 420
                else:
                    options.timeout = 300
            timeout = options.timeout + 30.0

        log.info("runtestsb2g.py | Running tests: start.")
        status = 0
        try:
            self.marionette_args['profile'] = self.profile
            self.marionette = Marionette(**self.marionette_args)
            self.runner = self.marionette.runner
            self.app_ctx = self.runner.app_ctx

            self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                             'log', 'mochitest.log')
            if not self.app_ctx.dm.dirExists(posixpath.dirname(
                    self.remote_log)):
                self.app_ctx.dm.mkDirs(self.remote_log)

            self.startServers(options, None)
            self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
            self.test_script_args.append(not options.emulator)
            self.test_script_args.append(options.wifi)

            self.runner.start(outputTimeout=timeout)

            self.marionette.wait_for_port()
            self.marionette.start_session()
            self.marionette.set_context(self.marionette.CONTEXT_CHROME)

            # Disable offline status management (bug 777145), otherwise the network
            # will be 'offline' when the mochitests start.  Presumably, the network
            # won't be offline on a real device, so we only do this for emulators.
            self.marionette.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.io.manageOfflineStatus = false;
                Services.io.offline = false;
                """)

            if os.path.isfile(self.test_script):
                with open(self.test_script, 'r') as script:
                    self.marionette.execute_script(
                        script.read(), script_args=self.test_script_args)
            else:
                self.marionette.execute_script(
                    self.test_script, script_args=self.test_script_args)
            status = self.runner.wait()
            if status is None:
                # the runner has timed out
                status = 124
        except KeyboardInterrupt:
            log.info("runtests.py | Received keyboard interrupt.\n")
            status = -1
        except:
            traceback.print_exc()
            log.error(
                "Automation Error: Received unexpected exception while running application\n"
            )
            if hasattr(self, 'runner'):
                self.runner.check_for_crashes()
            status = 1

        self.stopServers()

        log.info("runtestsb2g.py | Running tests: end.")

        if manifest is not None:
            self.cleanup(manifest, options)
        return status
Esempio n. 12
0
class TestRun(object):
    def __init__(self, adb="adb", serial=None):
        self.test_results = {}
        self.test_results_file = None
        self.m = None
        self.gaia_apps = None
        self.screenshot_path = None
        self.logcat_path = None
        self.app_name = None
        self.attempt = None
        self.num_apps = None
        self.device = None
        self.serial = serial
        self.port = None
        self.run_log = logging.getLogger('marketplace-test')
        if self.serial:
            self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial)
        else:
            self.dm = DeviceManagerADB(adbPath=adb)

    def reset_marionette(self):
        try:
            self.m.delete_session()
        except Exception:
            pass
        self.m = None
        self.get_marionette()

    def get_marionette(self):
        if not self.m:
            self.m = Marionette(port=self.port)
            self.m.start_session()
            self.device = GaiaDevice(self.m)
            self.device.add_device_manager(self.dm)
            self.gaia_apps = GaiaApps(self.m)
        else:
            tries = 5
            while tries > 0:
                try:
                    self.m.get_url()
                    break
                except MarionetteException as e:
                    if "Please start a session" in str(e):
                        time.sleep(5)
                        self.m = Marionette(port=self.port)
                        self.m.start_session()
                        self.device = GaiaDevice(self.m)
                        self.device.add_device_manager(self.dm)
                        self.gaia_apps = GaiaApps(self.m)
                        tries -= 1
                    else:
                        raise e
            else:
                self.run_log.error("Can't connect to marionette, rebooting")
                self.restart_device()
        return self.m

    def write_to_file(self, data):
        with open("%s.tmp" % self.test_results_file, "w") as f:
            f.write(data)
        shutil.copyfile("%s.tmp" % self.test_results_file, self.test_results_file)

    def add_values(self, key, value):
        if self.serial:
            self.test_results["%s_%s" % (key, self.serial)] = value
        else:
            self.test_results["%s" % key] = value

    def add_result(self, passed=False, status=None, uninstalled_failure=False):
        values = {}
        if status:
            if not passed:
                values["status"] = "FAILED: %s" % status
            else:
                values["status"] = "PASS"
        if self.screenshot_path:
            values["screenshot"] = self.screenshot_path
        if self.logcat_path:
            values["logcat"] = self.logcat_path
        if uninstalled_failure:
            values["uninstalled_failure"] = uninstalled_failure
        entry = "%s_%s" % (self.app_name, self.attempt)
        self.test_results[entry] = values

    def launch_with_manifest(self, manifest):
        self.m.switch_to_frame() 
        result = self.m.execute_async_script("GaiaApps.launchWithManifestURL('%s')" % manifest, script_timeout=30000)
        if result == False:
            raise Exception("launch timed out")
        app = GaiaApp(frame=result.get('frame'),
                      src=result.get('src'),
                      name=result.get('name'),
                      origin=result.get('origin'))
        if app.frame_id is None:
            raise Exception("failed to launch; there is no app frame")
        self.m.switch_to_frame(app.frame_id)
        return app


    def uninstall_with_manifest(self, manifest):
        self.m.switch_to_frame() 
        script = """
        GaiaApps.locateWithManifestURL('%s',
                                       null,
                                       function uninstall(app) {
                                       navigator.mozApps.mgmt.uninstall(app);
                                       marionetteScriptFinished(true);
                                       });
        """
        result = self.m.execute_async_script(script % manifest, script_timeout=60000)
        if result != True:
            self.add_result(status="Failed to uninstall app with url '%s'" % manifest)
        return app

    def forward_port(self):
        # get unused port
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('localhost', 0))
        addr, port = s.getsockname()
        s.close()

        dm_tries = 0
        self.run_log.info("using port %s" % port)
        while dm_tries < 20:
            if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0:
                break
            dm_tries += 1
            time.sleep(3)
        else:
            return False
        self.port = port
        return True

    def restart_device(self, restart_tries=0):
        self.run_log.info("rebooting")
        # TODO restarting b2g doesn't seem to work... reboot then
        while restart_tries < 3:
            restart_tries += 1
            self.dm.reboot(wait=True)
            self.run_log.info("forwarding")
            if not self.forward_port():
                self.run_log.error("couldn't forward port in time, rebooting")
                continue
            self.m = Marionette(port=self.port)
            if not self.m.wait_for_port(180):
                self.run_log.error("couldn't contact marionette in time, rebooting")
                continue
            time.sleep(1)
            self.m.start_session()
            try:
                Wait(self.m, timeout=240).until(lambda m: m.find_element("id", "lockscreen-container").is_displayed())
                # It retuns a little early
                time.sleep(2)
                self.device = GaiaDevice(self.m)
                self.device.add_device_manager(self.dm)
                self.device.unlock()
                self.gaia_apps = GaiaApps(self.m)
            except (MarionetteException, IOError, socket.error) as e:
                self.run_log.error("got exception: %s, going to retry" % e)
                try:
                    self.m.delete_session()
                except:
                    # at least attempt to clear the session if possible
                    pass
                continue
            break
        else:
            raise Exception("Couldn't restart the device in time, even after 3 tries")

    def readystate_wait(self, app):
        try:
            Wait(self.get_marionette(), timeout=30).until(lambda m: m.execute_script("return window.document.readyState;") == "complete")
        except ScriptTimeoutException as e:
            return False
        return True  

    def record_icons(self):
        self.device.touch_home_button()
        icons = self.m.find_elements("class name", "icon")
        self.num_apps = len(icons)

    def check_if_app_installed(self, timeout=180):
        # TODO: Find a better way to do this than checking homescreen
        # I hope there is one...
        self.device.touch_home_button()
        icons = self.m.find_elements("class name", "icon")
        start = time.time()
        end = start + 180
        found_icon = None
        claims_its_loaded = 0 # this is used in case 'loading' isn't applied immediately to the icon
        while time.time() < end:
            if not found_icon:
                icons = self.m.find_elements("class name", "icon")
                # We can't do set comparison b/c references change
                if len(icons) > self.num_apps:
                    for icon in icons:
                        if "loading" in icon.get_attribute("innerHTML"):
                            found_icon = icon
                            break 
                    else:
                        claims_its_loaded += 1
                        if claims_its_loaded == 3:
                            return True
            else:
                if "loading" not in found_icon.get_attribute("innerHTML"):
                    return True
            time.sleep(2)
        return False
Esempio n. 13
0
class TestRun(object):
    def __init__(self, adb="adb", serial=None):
        self.test_results = {}
        self.test_results_file = None
        self.m = None
        self.gaia_apps = None
        self.screenshot_path = None
        self.logcat_path = None
        self.app_name = None
        self.attempt = None
        self.num_apps = None
        self.device = None
        self.serial = serial
        self.port = None
        self.run_log = logging.getLogger('marketplace-test')
        if self.serial:
            self.dm = DeviceManagerADB(adbPath=adb, deviceSerial=serial)
        else:
            self.dm = DeviceManagerADB(adbPath=adb)

    def reset_marionette(self):
        try:
            self.m.delete_session()
        except Exception:
            pass
        self.m = None
        self.get_marionette()

    def get_marionette(self):
        if not self.m:
            self.m = Marionette(port=self.port)
            self.m.start_session()
            self.device = GaiaDevice(self.m)
            self.device.add_device_manager(self.dm)
            self.gaia_apps = GaiaApps(self.m)
        else:
            tries = 5
            while tries > 0:
                try:
                    self.m.get_url()
                    break
                except MarionetteException as e:
                    if "Please start a session" in str(e):
                        time.sleep(5)
                        self.m = Marionette(port=self.port)
                        self.m.start_session()
                        self.device = GaiaDevice(self.m)
                        self.device.add_device_manager(self.dm)
                        self.gaia_apps = GaiaApps(self.m)
                        tries -= 1
                    else:
                        raise e
            else:
                self.run_log.error("Can't connect to marionette, rebooting")
                self.restart_device()
        return self.m

    def write_to_file(self, data):
        with open("%s.tmp" % self.test_results_file, "w") as f:
            f.write(data)
        shutil.copyfile("%s.tmp" % self.test_results_file,
                        self.test_results_file)

    def add_values(self, key, value):
        if self.serial:
            self.test_results["%s_%s" % (key, self.serial)] = value
        else:
            self.test_results["%s" % key] = value

    def add_result(self, passed=False, status=None, uninstalled_failure=False):
        values = {}
        if status:
            if not passed:
                values["status"] = "FAILED: %s" % status
            else:
                values["status"] = "PASS"
        if self.screenshot_path:
            values["screenshot"] = self.screenshot_path
        if self.logcat_path:
            values["logcat"] = self.logcat_path
        if uninstalled_failure:
            values["uninstalled_failure"] = uninstalled_failure
        entry = "%s_%s" % (self.app_name, self.attempt)
        self.test_results[entry] = values

    def launch_with_manifest(self, manifest):
        self.m.switch_to_frame()
        result = self.m.execute_async_script(
            "GaiaApps.launchWithManifestURL('%s')" % manifest,
            script_timeout=30000)
        if result == False:
            raise Exception("launch timed out")
        app = GaiaApp(frame=result.get('frame'),
                      src=result.get('src'),
                      name=result.get('name'),
                      origin=result.get('origin'))
        if app.frame_id is None:
            raise Exception("failed to launch; there is no app frame")
        self.m.switch_to_frame(app.frame_id)
        return app

    def uninstall_with_manifest(self, manifest):
        self.m.switch_to_frame()
        script = """
        GaiaApps.locateWithManifestURL('%s',
                                       null,
                                       function uninstall(app) {
                                       navigator.mozApps.mgmt.uninstall(app);
                                       marionetteScriptFinished(true);
                                       });
        """
        result = self.m.execute_async_script(script % manifest,
                                             script_timeout=60000)
        if result != True:
            self.add_result(status="Failed to uninstall app with url '%s'" %
                            manifest)
        return app

    def forward_port(self):
        # get unused port
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('localhost', 0))
        addr, port = s.getsockname()
        s.close()

        dm_tries = 0
        self.run_log.info("using port %s" % port)
        while dm_tries < 20:
            if self.dm.forward("tcp:%d" % port, "tcp:2828") == 0:
                break
            dm_tries += 1
            time.sleep(3)
        else:
            return False
        self.port = port
        return True

    def restart_device(self, restart_tries=0):
        self.run_log.info("rebooting")
        # TODO restarting b2g doesn't seem to work... reboot then
        while restart_tries < 3:
            restart_tries += 1
            self.dm.reboot(wait=True)
            self.run_log.info("forwarding")
            if not self.forward_port():
                self.run_log.error("couldn't forward port in time, rebooting")
                continue
            self.m = Marionette(port=self.port)
            if not self.m.wait_for_port(180):
                self.run_log.error(
                    "couldn't contact marionette in time, rebooting")
                continue
            time.sleep(1)
            self.m.start_session()
            try:
                Wait(self.m, timeout=240).until(lambda m: m.find_element(
                    "id", "lockscreen-container").is_displayed())
                # It retuns a little early
                time.sleep(2)
                self.device = GaiaDevice(self.m)
                self.device.add_device_manager(self.dm)
                self.device.unlock()
                self.gaia_apps = GaiaApps(self.m)
            except (MarionetteException, IOError, socket.error) as e:
                self.run_log.error("got exception: %s, going to retry" % e)
                try:
                    self.m.delete_session()
                except:
                    # at least attempt to clear the session if possible
                    pass
                continue
            break
        else:
            raise Exception(
                "Couldn't restart the device in time, even after 3 tries")

    def readystate_wait(self, app):
        try:
            Wait(self.get_marionette(),
                 timeout=30).until(lambda m: m.execute_script(
                     "return window.document.readyState;") == "complete")
        except ScriptTimeoutException as e:
            return False
        return True

    def record_icons(self):
        self.device.touch_home_button()
        icons = self.m.find_elements("class name", "icon")
        self.num_apps = len(icons)

    def check_if_app_installed(self, timeout=180):
        # TODO: Find a better way to do this than checking homescreen
        # I hope there is one...
        self.device.touch_home_button()
        icons = self.m.find_elements("class name", "icon")
        start = time.time()
        end = start + 180
        found_icon = None
        claims_its_loaded = 0  # this is used in case 'loading' isn't applied immediately to the icon
        while time.time() < end:
            if not found_icon:
                icons = self.m.find_elements("class name", "icon")
                # We can't do set comparison b/c references change
                if len(icons) > self.num_apps:
                    for icon in icons:
                        if "loading" in icon.get_attribute("innerHTML"):
                            found_icon = icon
                            break
                    else:
                        claims_its_loaded += 1
                        if claims_its_loaded == 3:
                            return True
            else:
                if "loading" not in found_icon.get_attribute("innerHTML"):
                    return True
            time.sleep(2)
        return False