Example #1
0
    def publish_devices_info_event(online_devices, app_name):
        devices = []
        for device_id, device_info in online_devices.items():
            message_info = {
                'id': device_id,
                'info': {
                    'name': device_info.device_name,
                    'model': device_info.model,
                    'os': device_info.os_version,
                    'sn': device_info.sn
                },
            }
            devices.append(message_info)
            try:
                app_info = device_info.get_app_info(app_name)
                if app_info.get('AppName'):
                    message_info['app'] = {
                        'appName': app_info['AppName'],
                        'version': app_info['VersionNumber'],
                        'build': app_info['BuildNumber'],
                        'packageName': app_info['BundleID']
                    }
            except Exception:
                _log.error(f'Read app info error!\n {traceback.format_exc()}')

        lyrebird.publish('ios.device', devices, state=True)
    def publish_devices_package_info(online_devices, package_name):
        devices_info_list = []
        for device_id, device_info in online_devices.items():
            device_detail = online_devices[device_id]
            if device_detail.device_info is None:
                continue
            item = {
                'id': device_id,
                'info': {
                    'product': device_detail.product,
                    'model': device_detail.model,
                    'os': device_detail.get_release_version(),
                    'ip': device_detail.get_device_ip(),
                    'resolution': device_detail.get_device_resolution()
                }
            }
            app = device_info.package_info(package_name)
            if app.version_name:
                item['app'] = {
                    'packageName': package_name,
                    'startActivity': app.launch_activity,
                    'version': app.version_name
                }
            devices_info_list.append(item)

        lyrebird.publish('android.device', devices_info_list, state=True)
    def check_base(self, obj):
        try:
            # 检查base schema
            check_schema(obj)
            # 检查url是否有重复项存在
            redundant_items = check_url_redundant(obj)
            if redundant_items:
                redundant_items_str = '\n'.join(redundant_items)
                logger.error(
                    f'API-Coverage import API file error: Duplicated API\n'
                    f'{len(redundant_items)} duplicated API:\n'
                    f'{redundant_items_str}\n')
                resp = context.make_fail_response('导入API有重复项' +
                                                  str(redundant_items))
                lyrebird.publish('api_coverage', 'error', name='import_base')
                return resp
            # 获取base内容,解析出base的business等字段
            filename = obj.get('business') + obj.get(
                'version_name') + '.' + str(obj.get('version_code'))
            app_context.filename = filename
            app_context.business = obj.get('business')
            app_context.version_name = obj.get('version_name')
            app_context.version_code = obj.get('version_code')
            return
        except Exception as e:
            resp = context.make_fail_response(f'导入文件有误: {e}\n请重新import base')

        return resp
Example #4
0
def execute_command():
    if request.method == 'POST':
        _command = request.json.get('command')
        if not _command:
            return make_fail_response('Empty command!')

        _device_id = request.json.get('device_id', '')
        device = device_service.devices.get(_device_id)
        if not device:
            return make_fail_response('Device not found!')

        res = device.adb_command_executor(_command)
        output = res.stdout.decode()
        err_str = res.stderr.decode()

        publish_channel = 'android.command'
        publish_message = {
            'command': res.args,
            'returncode': res.returncode,
            'result': err_str if err_str else output
        }
        publish(publish_channel, publish_message)

        if err_str:
            return make_fail_response(err_str)
        else:
            return make_ok_response(data=output)
Example #5
0
def device_controller(device_id, action):
    controller_actions = {'install': _install_package}

    if request.method == 'PUT':
        device = device_service.devices.get(device_id)
        if not device:
            return make_fail_response(f'Device {device_id} not found!')
        if not controller_actions.get(action):
            return make_fail_response(f'Unknown device action: {action}')

        action_func = controller_actions.get(action)
        res = action_func(device, request)

        publish_channel = 'android.' + action
        publish_message = {
            'command':
            res.args,
            'returncode':
            res.returncode,
            'result':
            res.stderr.decode()
            if res.stderr.decode() else res.stdout.decode()
        }
        publish(publish_channel, publish_message)

        if res.returncode != 0:
            return make_fail_response(res.stderr.decode())
        else:
            return make_ok_response()
Example #6
0
def test_state_getter(event_server):
    import lyrebird
    lyrebird.application.server['event'] = event_server
    test_state = lyrebird.state.get('Test')
    assert test_state == None
    lyrebird.publish('Test', 'TestMessage', state=True)
    test_state = lyrebird.state.get('Test')
    assert test_state == 'TestMessage'
def devices():
    check_android_home()
    res = subprocess.run(f'{adb} devices -l',
                         shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    output = res.stdout.decode()
    err_str = res.stderr.decode()
    online_devices = {}

    # ADB command error
    if res.returncode != 0:
        print('Get devices list error', err_str)
        return online_devices

    lines = [line for line in output.split('\n') if line]
    if len(lines) > 1:
        for line in lines[1:]:
            device = Device.from_adb_line(line)
            online_devices[device.device_id] = device

    devices_list = [on_device for on_device in list(online_devices.keys())]
    last_devices_str = lyrebird.state.get(
        'android.device') if lyrebird.state.get('android.device') else []
    last_devices_list = [
        last_device.get('id') for last_device in last_devices_str
    ]

    if devices_list != last_devices_list:
        devices_info_list = []
        for device_id in online_devices:
            device_detail = online_devices[device_id]
            if device_detail.device_info == None:
                continue
            item = {
                'id': device_id,
                'info': {
                    'product': device_detail.product,
                    'model': device_detail.model,
                    'os': device_detail.get_release_version(),
                    'ip': device_detail.get_device_ip(),
                    'resolution': device_detail.get_device_resolution()
                }
            }
            package_name = config.load().package_name
            app = device.package_info(package_name)
            if app.version_name:
                item['app'] = {
                    'packageName': package_name,
                    'startActivity': app.launch_activity,
                    'version': app.version_name
                }
            devices_info_list.append(item)

        lyrebird.publish('android.device', devices_info_list, state=True)

    return online_devices
Example #8
0
 def set_filter_conf(self):
     filter_data = request.form.get('filter_data')
     try:
         resp = FilterHandler().save_filer_conf(json.loads(filter_data))
         lyrebird.publish('api_coverage', 'operation', name='set_filter')
     except Exception as e:
         lyrebird.publish('api_coverage', 'error', name='set_filter')
         return context.make_fail_response("传入的非json文件" + str(repr(e)))
     return resp
Example #9
0
def take_screenshot(platform, device_id):
    """
    channel: {platform}.cmd
    """
    channel = platform + '.cmd'
    cmd = {}
    cmd['cmd'] = 'screenshot'
    cmd['device_id'] = [device_id]
    lyrebird.publish(channel.lower(), cmd)
    return application.make_ok_response(message=f'Take screenshot success!')
Example #10
0
 def clear_result(self):
     ResultHandler().clear_cache_result()
     # 获取基准文件
     base_dict = BaseDataHandler().get_base_source()
     # 初始化正常会进行数据的处理:覆盖率初始化 & API LIST初始化
     if not isinstance(base_dict, Response):
         mergeAlgorithm.first_result_handler(base_dict)
         mergeAlgorithm.coverage_arithmetic(base_dict)
     lyrebird.publish('api_coverage', 'operation', name='clear_result')
     return context.make_ok_response()
Example #11
0
def devices():
    check_android_home()
    res = subprocess.run(f'{adb} devices -l', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = res.stdout.decode()
    err_str = res.stderr.decode()
    # ADB command error
    if len(output) <= 0 < len(err_str):
        print('Get devices list error', err_str)
        return []
    
    lines = [line for line in output.split('\n') if line]
    # online_devices contains information for plugin own
    online_devices = {}
    # devices_info contains information for bugit
    devices_info = []
    
    # no device connected
    if len(lines) > 1:
        for line in lines[1:]:
            device = Device.from_adb_line(line)
            online_devices[device.device_id] = device

    for device_id in online_devices:
        device_detail = online_devices[device_id]
        item = {}
        item['id'] = device_id
        item['info'] = {
            'product': device_detail.product,
            'model': device_detail.model
        }
        if device_detail.device_info == None:
            continue
        for line in device_detail.device_info:
            if 'ro.build.version.release' in line:
                item['info']['os'] = line[line.rfind('[') + 1:line.rfind(']')].strip()
                break
        devices_info.append(item)

    last_devices_info = lyrebird.state.get('android.device')
    if last_devices_info:
        last_devices_list = [last_device.get('id') for last_device in last_devices_info]
    else:
        last_devices_list = []

    if devices_info:
        devices_list = [on_device.get('id') for on_device in devices_info]
    else:
        devices_list = []

    if devices_list != last_devices_list:
        lyrebird.publish('android.device', devices_info, state=True)
    
    return online_devices
Example #12
0
 def get_screen_shot(self, msg):
     screen_shots = []
     for item in device_service.devices:
         device = device_service.devices[item]
         screen_shot_path = device.take_screen_shot()
         screen_shots.append({
             'id': item,
             'screenshot': {
                 'name': os.path.basename(screen_shot_path),
                 'path': screen_shot_path
             }
         })
     lyrebird.publish('ios.screenshot', screen_shots, state=True)
Example #13
0
 def get_screen_shot(self):
     screen_shots = []
     for item in device_service.devices:
         device = device_service.devices[item]
         self.take_screen_shot(item)
         screen_shots.append({
             'id': item,
             'screenshot': {
                 'name': device.model.replace(' ', '_'),
                 'path': self.get_screenshot_image(item)
             }
         })
     lyrebird.publish('ios.screenshot', screen_shots, state=True)
Example #14
0
 def get_screenshots(self, message):
     screenshot_list = []
     for device_id in device_service.devices:
         device_detail = device_service.devices[device_id]
         screenshot_path = device_detail.take_screen_shot()
         item = {}
         item['id'] = device_id
         item['screenshot'] = {
             'name': os.path.basename(screenshot_path),
             'path': screenshot_path
         }
         screenshot_list.append(item)
     lyrebird.publish('android.screenshot', screenshot_list)
 def save_filer_conf(self, conf_obj):
     self.init_filter_conf()
     try:
         check_filter_schema(conf_obj)
         f = codecs.open(FILTER_CONF, 'w', 'utf-8')
         f.write(json.dumps(conf_obj))
         f.close()
         # 配置保存后当时生效
         app_context.filter_dic = conf_obj
         return context.make_ok_response()
     except Exception as e:
         msg = '过滤请求的配置文件格式有误:' + e.__getattribute__('message')
         lyrebird.publish('api_coverage', 'error', name='set_filter')
         return context.make_fail_response(msg)
    def adb_command_executor(self, command):
        command = command.strip()
        lyrebird.publish('android.command', command)

        isAdbCommand = command.startswith('adb ')
        if isAdbCommand:
            command_adb, command_options = command.split(' ', 1)
            command = f'{command_adb} -s {self.device_id} {command_options}'

        p = subprocess.run(command,
                           shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)
        return p
Example #17
0
 def coverage_handler(self):
     """
     总体覆盖率
     """
     # 获取handle前的历史覆盖率为做对比用
     history_coverage = app_context.coverage['total']
     test_len = len(
         list(filter(lambda x: x.get('status') == 1,
                     app_context.merge_list)))
     if app_context.coverage['len'] == 0:
         coverage = 0
     else:
         coverage = round(test_len / app_context.coverage['len'] * 100, 2)
     # 为了传给Overbridge的socket信息format数据格式
     app_context.coverage['total'] = coverage
     # 覆盖率有变化才emit & publish 覆盖率的变化消息给API-Coverage前端,overbridge前端,和消息总线
     if not history_coverage == coverage:
         handler_time = time.time()
         # 限制频繁emit io msg,在两次之间大于指定时间间隔才会emit
         if handler_time - app_context.covtime > app_context.SOCKET_PUSH_INTERVAL:
             lyrebird.emit('coverage message',
                           app_context.coverage.get('total'),
                           namespace='/api_coverage')
             app_context.covtime = handler_time
         by_priority = [
             p.get('value') for p in app_context.coverage['priorities']
         ]
         lyrebird.publish(
             'coverage',
             dict(name='coverage',
                  value=app_context.coverage.get('total'),
                  by_priority=by_priority))
     app_context.coverage['test_len'] = test_len
     # 各优先级对应覆盖率
     for item_dic in app_context.coverage.get('priorities'):
         item_length = item_dic.get('len')
         test_item_length = len(
             list(
                 filter(
                     lambda x: x.get('priority') == item_dic.get('label')
                     and x.get('status') == 1, app_context.merge_list)))
         if item_length == 0:
             coverage = 0
         else:
             coverage = round(test_item_length / item_length * 100, 2)
         item_dic['value'] = coverage
         item_dic['test_len'] = test_item_length
Example #18
0
    def crash_checker(self, line):
        crash_log_path = os.path.join(crash_dir,
                                      'android_crash_%s.log' % self.device_id)

        if str(line).find('FATAL EXCEPTION') > 0:
            self.start_catch_log = True
            self._log_crash_cache.append(str(line))
            lyrebird.publish('crash',
                             'android',
                             path=crash_log_path,
                             id=self.device_id)
        elif str(line).find('AndroidRuntime') > 0 and self.start_catch_log:
            self._log_crash_cache.append(str(line))
        else:
            self.start_catch_log = False
            with codecs.open(crash_log_path, 'w') as f:
                f.write('\n'.join(self._log_crash_cache))
Example #19
0
    def anr_checker(self, line):
        if ('ANR' not in line) or ('ActivityManager' not in line):
            return

        anr_package = line.strip().split()[-2]
        re_str = "^([a-z_]{1}[a-z0-9_]*(\.[a-z_]{1}[a-z0-9_]*)*)$"
        # Check pkg name
        if re.match(re_str, anr_package) is None:
            return

        anr_file_name = os.path.join(
            anr_dir, f'android_anr_{self.device_id}_{anr_package}.log')
        p = subprocess.run(
            f'{adb} -s {self.device_id} pull "/data/anr/traces.txt" {anr_file_name}',
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
        if p.returncode != 0:
            logger.error('Catch ANR log error!\n' + p.stderr.decode())
            return

        # check whether pid of the anr_package exists or not
        with codecs.open(anr_file_name, 'r', 'utf-8') as f:
            anr_pid_line = f.readline()
            # expected anr_pid_line: ----- pid 21335 at 2019-06-24 16:21:15 -----
            while 'pid' not in anr_pid_line:
                anr_pid_line = f.readline()
        _anr_pid = anr_pid_line.strip().split()[2]
        anr_package = self.get_package_from_pid(_anr_pid)

        target_package_name = config.load().package_name
        if anr_package == target_package_name:
            with codecs.open(anr_file_name, 'r', 'utf-8') as f:
                log_anr_cache = f.readlines()
            anr_info = {
                'device_id': self.device_id,
                'log': log_anr_cache,
                'log_file_path': anr_file_name
            }
            lyrebird.publish('android.crash', anr_info)

            title = f'Application {anr_package} not responding on Android device {self.device_id}!\n'
            desc = title + 'ANR log:\n\n' + ''.join(log_anr_cache)

            lyrebird.event.issue(title, desc)
Example #20
0
def pubilsh_error_msg(msg):
    """
    将错误信息通过消息总线发送出去,订阅tracking频道的其他插件会监听到
    :param msg: 错误信息详情
    
    """

    app_context.error_list.append(msg)
    lyrebird.publish('tracking.error', msg)
    lyrebird.publish('tracking.error', msg, state=True)
    lyrebird.publish('tracking.error_list', app_context.error_list)
    lyrebird.publish('tracking.error_list', app_context.error_list, state=True)
Example #21
0
def get_screenshots(message):
    if message.get('cmd') != 'screenshot':
        return
    screenshot_list = []
    device_list = message.get('device_id')
    for device_id in device_list:
        device_detail = device_service.devices.get(device_id)
        if not device_detail:
            continue
        screenshot_detail = device_detail.take_screen_shot()
        screenshot_list.append({
            'id': device_id,
            'screenshot': {
                'name':
                os.path.basename(screenshot_detail.get('screen_shot_file')),
                'path': screenshot_detail.get('screen_shot_file')
            }
        })
    publish('android.screenshot', screenshot_list)
Example #22
0
def get_screen_shot(message):
    if message.get('cmd') != 'screenshot':
        return
    screen_shots = []
    device_list = message.get('device_id')
    for device_id in device_list:
        device = device_service.devices.get(device_id)
        if not device:
            continue
        screen_shot_info = device.take_screen_shot()
        screen_shots.append({
            'id': device_id,
            'screenshot': {
                'name':
                os.path.basename(screen_shot_info.get('screen_shot_file')),
                'path': screen_shot_info.get('screen_shot_file')
            }
        })
    lyrebird.publish('ios.screenshot', screen_shots, state=True)
 def import_result_handler(self):
     json_file = request.files.get('json-import')
     mimetype = json_file.content_type
     # 读取文件流,注意文件流只能read一次
     bytes_obj = json_file.read()
     try:
         result_obj = json.loads(bytes_obj)
         # 获取import文件的sha1
         import_sha1 = result_obj.get('base_sha1')
         if app_context.base_sha1 == import_sha1:
             # merge import result and cache result
             # check_result_schema(result_obj)
             app_context.coverage = json.loads(bytes_obj).get('coverage')
             mergeAlgorithm.merge_resume(result_obj.get('test_data'))
             # 放入缓存
             # app_context.merge_list = json.loads(bytes_obj).get('test_data')
             # app_context.coverage = json.loads(bytes_obj).get('coverage')
             resp = context.make_ok_response()
             lyrebird.publish('api_coverage', 'operation', name='import_result')
         else:
             resp = context.make_fail_response('导入的测试结果和之前选择base不匹配')
             lyrebird.publish('api_coverage', 'error', name='import_result')
     except Exception as e:
         resp = context.make_fail_response('导入文件内容格式有误:' + str(e))
         lyrebird.publish('api_coverage', 'error', name='import_result')
     return resp
Example #24
0
def application_controller(device_id, package_name, action):
    controller_actions = {
        'uninstall': _uninstall_package,
        'clear': _clear_package,
        'stop': _stop_package,
        'start': _start_package
    }

    if request.method == 'PUT':
        device = device_service.devices.get(device_id)
        if not device:
            return make_fail_response(f'Device {device_id} not found!')
        package = device.package_info(package_name)
        if not package:
            return make_fail_response(f'Application {package_name} not found!')
        if not controller_actions.get(action):
            return make_fail_response(f'Unknown application action: {action}')

        action_func = controller_actions.get(action)
        res = action_func(device, package, request)

        publish_channel = 'android.' + action
        publish_message = {
            'command':
            res.args,
            'returncode':
            res.returncode,
            'result':
            res.stderr.decode()
            if res.stderr.decode() else res.stdout.decode()
        }
        publish(publish_channel, publish_message)

        if res.returncode != 0:
            return make_fail_response(res.stderr.decode())
        # When adb uninstall <package> fail, the returncode is 0, while the output string contains `Failure`
        elif 'Failure' in res.stdout.decode():
            return make_fail_response(res.stdout.decode())
        else:
            return make_ok_response()
Example #25
0
    def crash_checker(self, line):

        if line.find('FATAL EXCEPTION') > 0:
            self.start_catch_log = True
            _crashed_pid = [_ for _ in line.strip().split()][2]
            self._crashed_package = self.get_package_from_pid(_crashed_pid)
            self._log_crash_cache.append(line)

        elif line.find('AndroidRuntime') > 0 and self.start_catch_log:
            self._log_crash_cache.append(line)

        elif self.start_catch_log:
            _crash_file = os.path.abspath(
                os.path.join(
                    crash_dir,
                    f'android_crash_{self.device_id}_{self._crashed_package}.log'
                ))

            with codecs.open(_crash_file, 'w', 'utf-8') as f:
                f.write(''.join(self._log_crash_cache))

            target_package_name = config.load().package_name
            if self._crashed_package == target_package_name:
                crash_info = {
                    'device_id': self.device_id,
                    'log': self._log_crash_cache,
                    'log_file_path': _crash_file
                }
                lyrebird.publish('android.crash', crash_info)

                title = f'Android device {self.device_id} crashed!\n'
                desc = title + 'Crash log:\n\n' + ''.join(
                    self._log_crash_cache)
                lyrebird.event.issue(title, desc)

            self.start_catch_log = False
            self._log_crash_cache = []

        else:
            return
    def import_base_handler(self):
        json_file = request.files.get('json-import')
        mimetype = json_file.content_type
        # 判断是不是json格式的文件
        if mimetype == 'application/json':
            # 读取文件流,注意文件流只能read一次
            bytes_obj = json_file.read()
            try:
                check_result = BaseDataHandler().check_base(json.loads(bytes_obj))
                if check_result:
                    return check_result

                self.write_wb(DEFAULT_BASE, bytes_obj)
                # 读取json文件
                json_obj = json.loads(codecs.open(DEFAULT_BASE, 'r', 'utf-8').read())
                # 获取文件的sha1
                app_context.base_sha1 = self.get_sha1(bytes_obj)
                # 初次处理,切换后的result
                mergeAlgorithm.first_result_handler(json_obj)
                mergeAlgorithm.coverage_arithmetic(json_obj)
                resp = context.make_ok_response()
                lyrebird.publish('api_coverage', 'operation', name='import_base')
            except Exception as e:
                resp = context.make_fail_response('导入文件内容格式有误:' + str(e))
                lyrebird.publish('api_coverage', 'error', name='import_base')
        else:
            resp = context.make_fail_response("Error.The selected non - JSON file.")
            lyrebird.publish('api_coverage', 'error', name='import_base')
        return resp
Example #27
0
 def publish_devices_info_event(self, online_devices):
     devices = []
     for item in online_devices:
         info = online_devices[item]
         app = online_devices[item].get_app_info(
             self.get_default_app_name())
         devices.append({
             'id': info.device_id,
             'info': {
                 'name': info.device_name,
                 'model': info.model,
                 'os': info.os_version,
                 'sn': info.sn
             },
             'app': {
                 'name': app['AppName'],
                 'version': app['VersionNumber'],
                 'build': app['BuildNumber'],
                 'bundleID': app['BundleID']
             }
         })
     lyrebird.publish('ios.device', devices, state=True)
    def check_base(self, obj):
        try:
            # 检查base schema
            check_schema(obj)
            # 检查url是否有重复项存在
            redundant_items = check_url_redundant(obj)
            if redundant_items:
                resp = context.make_fail_response('导入API有重复项' + str(redundant_items))
                lyrebird.publish('api_coverage', 'error', name='import_base')
                return resp
            # 获取base内容,解析出base的business等字段
            filename = obj.get('business') + obj.get('version_name') + '.' + str(obj.get('version_code'))
            app_context.filename = filename
            app_context.business = obj.get('business')
            app_context.version_name = obj.get('version_name')
            app_context.version_code = obj.get('version_code')
            return
        except Exception as e:
            resp = context.make_fail_response(
                '导入文件有误:' + '\n' + e.__getattribute__('message') + '\n' + '!请重新import base')

        return resp
Example #29
0
 def publish_devices_info_event(self, online_devices, app_name):
     devices = []
     for item in online_devices:
         device_info = online_devices[item]
         app_info = online_devices[item].get_app_info(app_name)
         message_info = {
             'id': device_info.device_id,
             'info': {
                 'name': device_info.device_name,
                 'model': device_info.model,
                 'os': device_info.os_version,
                 'sn': device_info.sn
             },
         }
         if app_info.get('AppName'):
             message_info['app'] = {
                 'name': app_info['AppName'],
                 'version': app_info['VersionNumber'],
                 'build': app_info['BuildNumber'],
                 'bundleID': app_info['BundleID']
             }
         devices.append(message_info)
     lyrebird.publish('ios.device', devices, state=True)
Example #30
0
 def save_result(self):
     # 传入文件名
     filename = request.form.get('result_name')
     ResultHandler().save_result(filename)
     lyrebird.publish('api_coverage', 'operation', name='save_result')
     return context.make_ok_response()