appetizer自动遍历客户端工具
appetizerio命令行工具
appetizerio源码分析

Appetizer 客户端


appetizer 官方地址

什么是 Appetizer

Appetizer 通过 DEX 插桩的方法,全自动地向 APP 内多处插入代码,在程序运行的过程中,监控异常和闪退、搜集主线程卡顿与耗时操作、HTTP/HTTPS 请求和响应、CPU 和 Java 堆内存消耗等
采集代码经过调优,对 APP 运行性能影响小于 1%。 收集的运行数据存储在设备的本地,完成测试后上传到 Appetizer 服务端进行分析,产生详细的问题报告、各项指标等
各项数据可以以多种格式导出,JSON, CSV, HTML,支持不同定制化数据分析以及集成服务

note: 当前最新版本 (1.4.10) 仅支持 json 格式的导出

客户端简单使用

  1. 上传 apk 插桩并安装插桩包
  2. 跑个 maxim 自动遍历试试,相关配置可参考app自动遍历测试maxim
  3. 手机端上传运行数据到 appetizer 服务端即可在客户端上看到可视化的详细的问题报告和各项指标

客户端有个最大的 bug 就是分析次数有限制,当然你也可以注册不同的账号还获得更多的分析次数
而且不支持输出报告的定制化,工作中开发人员不愿意安装客户端并查看分析测试报告等问题

中文社区

中文社区

用户文档

用户文档
命令行工具及DevOps使用

AppetizerIO 命令行工具


除了客户端以外,appetizer 还开源了 appetizerIO 命令行工具,针对不同的项目,可以对其进行微小的修改和适配

项目地址

AppetizerIO 命令行工具 insights

命令行工具基本使用

  1. 登录账户
# 通过username和password登录
python insights.py login username password

# 通过apikey登录(在客户端上获取自己的apikey)
python insights.py apikey <apikey>
  1. 上传并下载待测插桩 app

命令行插桩的 App 主要面向自动化遍历技术,为避免自动化误点,默认会关闭浮动框,需要打开可在命令行插桩时增加 --enable-inapp-menu选项

# <apk>待测app路径:如d:/test.apk,<processed_apk>插桩后的app名:如test_apk_appetizer.apk
python insights.py process <apk> <processed_apk>

process(args)源码摘要(从 url 下载并转存,值得借鉴,这里贴一下)

def process(args):
    ......
    print('3. download processed APK')
    if (downloadURL.startswith('http')):
        r = requests.get(downloadURL)
    else:
        r = requests.get(CONFIG['file_server'] + downloadURL)
    if r.status_code != 200:
        print('download failed')
        return 1
    print('download completed')
    with open(args.processed_apk, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024000):
            f.write(chunk)
  1. 安装处理后的插桩 app
    这里可以通过提供的命令行安装并授权也可以用基本的 adb install 命令安装
# serialno1和serialno2为手机序列号,该命令支持多设备同时安装插桩app
python insights.py install my_processed.apk -s serialno1 -s serialno2
  • 查看源码只是对 adb 做了一次封装,卸载手机上的同包名的 app 并安装插桩 app,然后给予 app 读写手机存储的权限(小米无法自动化授权,建议在安装完成后授权读写 SDCARD),install(args)函数源码如下
def install(args):
    ......
    print('1. install processed APK')
    for d in serialnos:
        adb(['uninstall', pkg], d)
        adb(['install', args.apk], d) # Note: Xiaomi will pop up a dialog
    print('APK installed')

    print('2. grant permissions for logging')
    for d in serialnos:
        adb(['shell', 'pm', 'grant', pkg, 'android.permission.WRITE_EXTERNAL_STORAGE'], d, True)
        adb(['shell', 'pm', 'grant', pkg, 'android.permission.READ_EXTERNAL_STORAGE'], d, True)
  1. 上传 log 获取分析报告
    自动化遍历测试后可通过 app 手动上传到 appetizer 服务器(公有云),然后在客户端上获取到分析报告的下载地址或者用命令行上传并在打印日志中获取到分析报告下载地址

--clear是可选参数,用于从设备下载 log 后将设备上 log 清空(存储在手机上的位置为/sdcard/Android/data/<待测app包名>/files

python insights.py analyze my_processed.apk -s serialno1 -s serialno2 --clear

analyze(args) 部分源码:

def analyze(args):
....
    print('2. upload log file %s, with pkg: %s' % (log_zip, pkg))
    print('uploading......')
    with open(log_zip, 'rb') as f:
        suffix = r_json['uploadUrl'] if 'uploadUrl' in r_json else ''
        ret = requests.post(CONFIG['upload_server'] + suffix, files={'file': f}, data={'key': key, 'token': token}).json()
    if ret is None or 'success' not in ret or not ret['success']:
        print('upload error')
        return 1

    print('3. server analyzing')
    r_json = None
    while True:
        r = requests.get(CONFIG['check_analysis'], headers={'Authorization': authorization}, params={'key': key})
        r_json = r.json()
        if r_json['success'] != True:
            print(r_json)
            return 1
        if r_json['state'] == 'return_upload_auth' or r_json['state'] == 'upload_finish' or r_json['state'] == 'server_download':
            print('waiting...... server is downloading log')
        elif r_json['state'] == 'analyzing':
            print('waiting...... server is analyzing')
        elif r_json['state'] == 'analyze_success' or r_json['state'] == 'report_exporting':
            print('waiting...... server is exporting the report')
        elif r_json['state'] == 'report_export_success' or r_json['state'] == 'server_upload':
            print('waiting...... server is uploading the generated report')
        elif r_json['state'] == 'server_upload_success':
            print('server has generated and uploaded the report')
            if 'downloadURL' in r_json:
                print('download report data at:')
                downloadURL = r_json['downloadURL']
                print('3. download processed APK')
                if (downloadURL.startswith('http')):
                    print(downloadURL)
                else:
                    print(CONFIG['file_server'] + downloadURL)
            break
        else:
            print(r_json)
            print('server fails to analyze the logs')
            return 1
        time.sleep(ANXIETY)
......

扩展阅读


参考