Exemplo n.º 1
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
Exemplo n.º 2
0
class B2GPopulate(object):

    PERSISTENT_STORAGE_PATH = '/data/local/storage/persistent'

    handler = mozlog.StreamHandler()
    handler.setFormatter(mozlog.MozFormatter(include_timestamp=True))
    logger = mozlog.getLogger('B2GPopulate', handler)

    def __init__(self, marionette, log_level='INFO'):
        self.marionette = marionette
        self.data_layer = GaiaData(self.marionette)
        self.device = GaiaDevice(self.marionette)
        dm = mozdevice.DeviceManagerADB()
        self.device.add_device_manager(dm)

        self.logger.setLevel(getattr(mozlog, log_level.upper()))

        if self.device.is_android_build:
            self.idb_dir = 'idb'
            for candidate in self.device.manager.listFiles(
                    '/'.join([self.PERSISTENT_STORAGE_PATH, 'chrome'])):
                if re.match('\d.*idb', candidate):
                    self.idb_dir = candidate
                    break

    def populate(self, call_count=None, contact_count=None, message_count=None,
                 music_count=None, picture_count=None, video_count=None,
                 event_count=None):

        restart = any([call_count, contact_count, message_count])

        if restart:
            self.logger.debug('Stopping B2G')
            self.device.stop_b2g()

        if call_count is not None:
            self.populate_calls(call_count, restart=False)

        if contact_count is not None:
            self.populate_contacts(contact_count, restart=False)

        if event_count is not None:
            self.populate_events(event_count, restart=False)

        if message_count is not None:
            self.populate_messages(message_count, restart=False)

        if restart:
            self.start_b2g()

        if music_count > 0:
            self.populate_music(music_count)

        if picture_count > 0:
            self.populate_pictures(picture_count)

        if video_count > 0:
            self.populate_videos(video_count)

    def populate_calls(self, count, restart=True):
        # only allow preset db values for calls
        db_counts = DB_PRESET_COUNTS['call']
        if not count in db_counts:
            raise InvalidCountError('call')
        self.logger.info('Populating %d calls' % count)
        db_counts.sort(reverse=True)
        for marker in db_counts:
            if count >= marker:
                key = 'communications.gaiamobile.org'
                local_id = json.loads(self.device.manager.pullFile(
                    '/data/local/webapps/webapps.json'))[key]['localId']
                db_zip_name = pkg_resources.resource_filename(
                    __name__, os.path.sep.join(['resources', 'dialerDb.zip']))
                db_name = 'dialerDb-%d.sqlite' % marker
                self.logger.debug('Extracting %s from %s' % (
                    db_name, db_zip_name))
                db = ZipFile(db_zip_name).extract(db_name)
                if restart:
                    self.device.stop_b2g()
                destination = '/'.join([self.PERSISTENT_STORAGE_PATH,
                                        '%s+f+app+++%s' % (local_id, key),
                                        self.idb_dir,
                                        '2584670174dsitanleecreR.sqlite'])
                self.logger.debug('Pushing %s to %s' % (db, destination))
                self.device.push_file(db, destination=destination)
                self.logger.debug('Removing %s' % db)
                os.remove(db)
                if restart:
                    self.start_b2g()
                break

    def populate_contacts(self, count, restart=True, include_pictures=True):
        # only allow preset db values for contacts
        db_counts = DB_PRESET_COUNTS['contact']
        if not count in db_counts:
            raise InvalidCountError('contact')
        self.device.manager.removeDir('/'.join([
            self.PERSISTENT_STORAGE_PATH, 'chrome',
            self.idb_dir, '*csotncta*']))
        self.logger.info('Populating %d contacts' % count)
        db_counts.sort(reverse=True)
        for marker in db_counts:
            if count >= marker:
                db_zip_name = pkg_resources.resource_filename(
                    __name__, os.path.sep.join(['resources',
                                                'contactsDb.zip']))
                db_name = 'contactsDb-%d.sqlite' % marker
                self.logger.debug('Extracting %s from %s' % (
                    db_name, db_zip_name))
                db = ZipFile(db_zip_name).extract(db_name)
                if restart:
                    self.device.stop_b2g()
                destination = '/'.join([
                    self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir,
                    '3406066227csotncta.sqlite'])
                self.logger.debug('Pushing %s to %s' % (db, destination))
                self.device.push_file(db, destination=destination)
                self.logger.debug('Removing %s' % db)
                os.remove(db)
                if marker > 0 and include_pictures:
                    self.logger.debug('Adding contact pictures')
                    pictures_zip_name = pkg_resources.resource_filename(
                        __name__, os.path.sep.join(
                            ['resources', 'contactsPictures.zip']))
                    temp = tempfile.mkdtemp()
                    self.logger.debug('Extracting %s to %s' % (
                        pictures_zip_name, temp))
                    ZipFile(pictures_zip_name).extractall(temp)
                    destination = '/'.join([
                        self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir,
                        '3406066227csotncta'])
                    self.logger.debug('Pushing %s to %s' % (temp, destination))
                    self.device.manager.pushDir(temp, destination)
                    self.logger.debug('Removing %s' % temp)
                    shutil.rmtree(temp)
                if restart:
                    self.start_b2g()
                break

    def populate_events(self, count, restart=True):
        # only allow preset db values for events
        db_counts = DB_PRESET_COUNTS['event']
        if not count in db_counts:
            raise InvalidCountError('event')
        self.logger.info('Populating %d events' % count)
        db_counts.sort(reverse=True)
        for marker in db_counts:
            if count >= marker:
                key = 'calendar.gaiamobile.org'
                local_id = json.loads(self.device.manager.pullFile(
                    '/data/local/webapps/webapps.json'))[key]['localId']
                db_zip_name = pkg_resources.resource_filename(
                    __name__, os.path.sep.join(['resources',
                                                'calendarDb.zip']))
                db_name = 'calendarDb-%d.sqlite' % marker
                self.logger.debug('Extracting %s from %s' % (
                    db_name, db_zip_name))
                db = ZipFile(db_zip_name).extract(db_name)
                if restart:
                    self.device.stop_b2g()
                destination = '/'.join([self.PERSISTENT_STORAGE_PATH,
                                        '%s+f+app+++%s' % (local_id, key),
                                        self.idb_dir,
                                        '125582036br2agd-nceal.sqlite'])
                self.logger.debug('Pushing %s to %s' % (db, destination))
                self.device.push_file(db, destination=destination)
                self.logger.debug('Removing %s' % db)
                os.remove(db)
                if restart:
                    self.start_b2g()
                break

    def populate_messages(self, count, restart=True):
        # only allow preset db values for messages
        db_counts = DB_PRESET_COUNTS['message']
        if not count in db_counts:
            raise InvalidCountError('message')
        self.logger.info('Populating %d messages' % count)
        db_counts.sort(reverse=True)
        for marker in db_counts:
            if count >= marker:
                db_zip_name = pkg_resources.resource_filename(
                    __name__, os.path.sep.join(['resources', 'smsDb.zip']))
                db_name = 'smsDb-%d.sqlite' % marker
                self.logger.debug('Extracting %s from %s' % (
                    db_name, db_zip_name))
                db = ZipFile(db_zip_name).extract(db_name)
                if restart:
                    self.device.stop_b2g()
                destination = '/'.join([
                    self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir,
                    '226660312ssm.sqlite'])
                self.logger.debug('Pushing %s to %s' % (db, destination))
                self.device.push_file(db, destination=destination)
                os.remove(db)
                if marker > 0:
                    self.logger.debug('Adding message attachments')
                    all_attachments_zip_name = pkg_resources.resource_filename(
                        __name__, os.path.sep.join(
                            ['resources', 'smsAttachments.zip']))
                    attachments_zip_name = 'smsAttachments-%d.zip' % marker
                    self.logger.debug('Extracting %s from %s' % (
                        attachments_zip_name, all_attachments_zip_name))
                    attachments_zip = ZipFile(
                        all_attachments_zip_name).extract(attachments_zip_name)
                    temp = tempfile.mkdtemp()
                    self.logger.debug('Extracting %s to %s' % (
                        attachments_zip, temp))
                    ZipFile(attachments_zip).extractall(temp)
                    destination = '/'.join([
                        self.PERSISTENT_STORAGE_PATH, 'chrome', self.idb_dir,
                        '226660312ssm'])
                    self.logger.debug('Pushing %s to %s' % (temp, destination))
                    self.device.manager.pushDir(temp, destination)
                    self.logger.debug('Removing %s' % temp)
                    shutil.rmtree(temp)
                    self.logger.debug('Removing %s' % attachments_zip)
                    os.remove(attachments_zip)
                if restart:
                    self.start_b2g()
                break

    def populate_music(self, count, source='MUS_0001.mp3',
                       destination='sdcard', tracks_per_album=10):
        self.remove_media('music')

        import math
        from mutagen.easyid3 import EasyID3
        music_file = pkg_resources.resource_filename(
            __name__, os.path.sep.join(['resources', source]))
        local_filename = music_file.rpartition(os.path.sep)[-1]

        # copy the mp3 file into a temp location
        with tempfile.NamedTemporaryFile() as local_copy:
            self.logger.debug('Creating copy of %s at %s' % (
                music_file, local_copy.name))
            local_copy.write(open(music_file).read())
            music_file = local_copy.name

            mp3 = EasyID3(music_file)
            album_count = math.ceil(float(count) / tracks_per_album)
            self.logger.info('Populating %d music files (%d album%s)' % (
                count, album_count, 's' if album_count > 1 else ''))

            for i in range(1, count + 1):
                album = math.ceil(float(i) / float(tracks_per_album))
                track = i - ((album - 1) * tracks_per_album)
                mp3['title'] = 'Track %d' % track
                mp3['artist'] = 'Artist %d' % album
                mp3['album'] = 'Album %d' % album
                mp3['tracknumber'] = str(track)
                mp3.save()
                remote_filename = '_%s.'.join(
                    iter(local_filename.split('.'))) % i
                remote_destination = os.path.join(destination, remote_filename)
                self.logger.debug('Pushing %s to %s' % (
                    music_file, remote_destination))
                self.device.push_file(music_file, 1, remote_destination)

    def populate_pictures(self, count, source='IMG_0001.jpg',
                          destination='sdcard/DCIM/100MZLLA'):
        self.populate_files('picture', source, count, destination)

    def populate_videos(self, count, source='VID_0001.3gp',
                        destination='sdcard/DCIM/100MZLLA'):
        self.populate_files('video', source, count, destination)

    def populate_files(self, file_type, source, count, destination=''):
        self.remove_media(file_type)

        self.logger.info('Populating %d %s files' % (count, file_type))
        source_file = pkg_resources.resource_filename(
            __name__, os.path.sep.join(['resources', source]))
        self.logger.debug('Pushing %d copies of %s to %s' % (
            count, source_file, destination))
        self.device.push_file(source_file, count, destination)

    def remove_media(self, file_type):
        if self.device.is_android_build:
            files = getattr(self.data_layer, '%s_files' % file_type) or []
            if len(files) > 0:
                self.logger.info('Removing %d %s files' % (
                    len(files), file_type))
                for filename in files:
                    self.logger.debug('Removing %s' % filename)
                    self.device.manager.removeFile(filename)
                # TODO Wait for files to be deleted
                time.sleep(5)
                files = getattr(self.data_layer, '%s_files' % file_type) or []
            if not len(files) == 0:
                raise IncorrectCountError(
                    '%s files' % file_type, 0, len(files))

    def start_b2g(self):
        self.logger.debug('Starting B2G')
        self.device.start_b2g()
        self.data_layer = GaiaData(self.marionette)
Exemplo n.º 3
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