ODM项目为了测试平台稳定性,会下载大量第三方app进行冷、热启动的测试,并生成测试报告。
根据测试结果可用于暴露平台性能上的一些缺陷。
实现:pythn+adb
以下分享源码实现过程
# !/usr/bin/python
# -*- coding: utf-8 -*-
# jianfeng.qian
import subprocess
import re
import glob
import os
import time
import xlsxwriter
from numpy import *
root = os.getcwd()
TEST_APP_CONFIG_DIR = 'test_app_config'
wait_time = 'WaitTime:'
launcher_state = 'LaunchState:'
status = 'Status:'
ITEM_TEST_TIMES = 2
APPS_START_COLD = 'COLD'
APPS_START_HOT = 'HOT'
devices_list_finally = []
skip_apps_install_list = []
EXCEL_RESULT_INFO = "EXCEL_RESULT_INFO_%s.xlsx"
EXCEL_RESULT_INFO_TITLE = ["APK NAME", "PACKAGE NAME", "ACTIVITY NAME", "LAUNCHER STATE", "WAIT TIME"]
EXCEL_RESULT_MEAN = "EXCEL_RESULT_MEAN_%s.xlsx"
EXCEL_RESULT_MEAN_TITLE = ["APK NAME", "PACKAGE NAME", "ACTIVITY NAME", "COLD WAIT TIME MEAN", "HOT WAIT TIME MEAN",
"COLD WAIT TIME MAX", "HOT WAIT TIME MAX"]
UNTESTED_LIST = "Untested_list_%s.txt"
special_dit = {"com.facebook.katana": "com.facebook.katana.activity.FbMainTabActivity",
"com.instagram.android": "com.instagram.android.activity.MainTabActivity",
"com.facebook.orca": "com.facebook.orca.auth.StartScreenActivity",
"com.google.android.youtube": "com.google.android.youtube.HomeActivity",
"com.snapchat.android": "com.snapchat.android.LandingPageActivity"}
"""
def resource_path(relative):
if hasattr(sys, "_MEIPASS"):
return os.path.join(sys._MEIPASS, relative)
return os.path.join(relative)
AAPT = resource_path(os.path.join("%s.exe" % "aapt"))
print(AAPT)
"""
# 测试数据采集
APK_NAME_LIST = []
PACKAGE_NAME_LIST = []
ACTIVITY_NAME_LIST = []
LAUNCHER_STATE_LIST = []
WAIT_TIME_LIST = []
EXCEL_LINE_LIST = [] # 导入excel 总表
# 测试平均数据采集
APK_NAME_LIST_MEAN = []
PACKAGE_NAME_LIST_MEAN = []
ACTIVITY_NAME_LIST_MEAN = []
COLD_WAIT_TIME_MEAN = []
HOT_WAIT_TIME_MEAN = []
COLD_WAIT_TIME_MAX = []
HOT_WAIT_TIME_MAX = []
EXCEL_LINE_LIST_MEAN = [] # 导入excel 总表
VERSION_SDK = ""
"""
class AppInfoConfig():
def __init__(self, apkPath):
self.apkPath = apkPath
def getApkBaseInfo(self):
p = subprocess.Popen(
"aapt dump badging %s" % (self.apkPath),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
result = []
# lineList = ""
for x in output.decode().splitlines():
if 'package: name=' in x:
match = re.compile("package: name='(\S+)'").match(x)
if not match:
raise Exception("can't get packageinfo")
packagename = match.group(1)
# lineList += "package=%s\n" % packagename
result.append(packagename)
elif 'launchable-activity: name=' in x:
match = re.compile("launchable-activity: name='(\S+)' ").match(x)
if not match:
raise Exception("can't get packageinfo")
launchable_activity = match.group(1)
# lineList += "activity=%s\n" % launchable_activity
result.append(launchable_activity)
# file_name = "%s\%s\%s.config" % (root, TEST_APP_CONFIG_DIR, packagename)
# with open(file_name, "w") as f1:
# f1.write(lineList)
p.communicate()
return result
"""
class AppInfoConfig():
def __init__(self, apkPath):
self.apkPath = apkPath
def find_str(self, find_name, sourceStr):
res = re.findall(find_name, sourceStr)
name = res[0]
return name
def check_package_name(self, result, packageName):
for k, v in special_dit.items():
if packageName == k:
result.append(v)
return True
return False
def getApkBaseInfo(self):
p = subprocess.Popen(
"aapt dump badging %s" % (self.apkPath),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
result = []
for x in output.decode().splitlines():
if 'package: name=' in x:
packagename = self.find_str("package: name='(.+?)'", x)
result.append(packagename)
if self.check_package_name(result, packagename):
break
"""
if packagename == "com.facebook.katana":
result.append("com.facebook.katana.activity.FbMainTabActivity")
break
elif packagename == "com.instagram.android":
result.append("com.instagram.android.activity.MainTabActivity")
break
elif packagename == "com.facebook.orca":
result.append("com.facebook.orca.auth.StartScreenActivity")
break
elif packagename == "com.google.android.youtube":
result.append("com.google.android.youtube.HomeActivity")
break
elif packagename == "com.snapchat.android":
result.append("com.snapchat.android.LandingPageActivity")
break
"""
elif 'launchable-activity: name=' in x:
launchable_activity = self.find_str("launchable-activity: name='(.+?)'", x)
result.append(launchable_activity)
p.communicate()
return result
def execCmd(cmd):
r = os.popen(cmd)
return r.read()
def match_info(a, b):
match = re.compile("%s (\S+)" % a).match(b)
return match.group(1)
def connectDevcie():
global VERSION_SDK
result = os.popen('adb devices').read().strip()
if len(result.splitlines()) == 1:
print("ADB Connect Error!")
return False
else:
VERSION_SDK = os.popen("adb shell getprop ro.build.version.sdk").read().strip()
print("ADB Connect successful. version SDK : %s" % VERSION_SDK)
"""
adb_root_status = execCmd('adb root & adb remount').splitlines()
for x in adb_root_status:
if 'remount succeeded' in x or 'adbd is already running as root' in x:
print("ADB root successful.")
return True
return True
"""
return True
def stat_app(startmode, package, activity):
item_test_result = []
start_cmd = ""
if startmode == APPS_START_COLD:
start_cmd = 'adb shell am start-activity -S -W'
elif startmode == APPS_START_HOT:
start_cmd = "adb shell am start-activity -W "
start = execCmd("%s %s/%s" % (start_cmd, package, activity)) # 冷启动
for x in start.splitlines():
if wait_time in x:
result = "%s %s " % (wait_time, match_info(wait_time, x))
item_test_result.append(result)
elif launcher_state in x:
result = "%s %s " % (launcher_state, match_info(launcher_state, x))
item_test_result.append(result)
elif status in x:
result = "%s %s " % (status, match_info(status, x))
item_test_result.append(result)
# os.system("adb shell am start-activity -W %s/%s" % (package, activity))
time.sleep(3)
if startmode == APPS_START_COLD:
execCmd("adb shell am force-stop %s " % package)
time.sleep(5)
# execCmd("adb shell \"echo 3 > /proc/sys/vm/drop_caches\"") # user版本下无法使用
execCmd("adb shell cmd package compile -m speed-profile -f %s" % package)
elif startmode == APPS_START_HOT:
execCmd("adb shell input keyevent 3")
# print(item_test_result)
if VERSION_SDK == "29":
time.sleep(10)
return item_test_result
def get_apks_list(file_dir):
file_list = []
files = glob.glob(file_dir + '/*')
index = 0
for file in files:
if os.path.splitext(file)[1] == '.apk':
index += 1
file_list.append("%s,%s" % (index, os.path.join(root, file)))
# file_list.sort()
return file_list
def write_excel(title, data, file):
workbook = xlsxwriter.Workbook(file)
worksheet = workbook.add_worksheet(u'sheet1')
worksheet.set_row(0, 30)
worksheet.set_column(0, 100, 70)
count = 0
for i in range(ord("A"), ord("Z") + 1):
if count == len(title):
break
worksheet.write("%s1" % chr(i), title[count], workbook.add_format({
'bold': True, 'border': 3, 'align': 'center', 'valign': 'vcenter', 'fg_color': '#019285',
'font_color': '#FFFFFF'
}))
count += 1
count_line = 0
for x, one in enumerate(data):
for y, i in enumerate(one):
worksheet.set_column(y + 1, x, len(i) + 20)
if title[x] == "ACTIVITY NAME" or title[x] == "PACKAGE NAME":
worksheet.set_column(y + 1, x, 100)
worksheet.write(y + 1, x, i, workbook.add_format({
'bold': False, 'border': 1, 'valign': 'vcenter', 'fg_color': '#ECECEC',
'font_color': '#000000'
}))
count_line += 1
workbook.close()
def test(apk_name, package, activity, test_mode):
wait_t = []
for times in range(ITEM_TEST_TIMES):
try:
result_info = stat_app(test_mode, package, activity)
print("Start app %s test (%s/%s) , info = %s" % (test_mode, times + 1, ITEM_TEST_TIMES, result_info))
APK_NAME_LIST.append(apk_name)
PACKAGE_NAME_LIST.append(package)
ACTIVITY_NAME_LIST.append(activity) # .split(".")[-1] 获取最后class name
LAUNCHER_STATE_LIST.append(result_info[1].split(":")[1])
WAIT_TIME_LIST.append(result_info[2].split(":")[1])
wait_t.append(int(result_info[2].split(":")[1]))
except Exception:
# print(Exception)
return None
return wait_t
def build_txt(path, list):
lineList = ""
for x in list:
lineList += x + "\n"
with open(path, "w") as f:
f.write(lineList)
def check_install(apk_file, apk_name, result):
print("%s Install %s" % (apk_name, result))
for x in result.splitlines():
if "failed to install" in x or "Install Failure" in x:
skip_apps_install_list.append(apk_file)
return True
return False
def do_test():
apks_list = get_apks_list(root)
for apk_file in apks_list:
test_index, apk_file = apk_file.split(",")
appinfo = AppInfoConfig(apk_file).getApkBaseInfo()
package = appinfo[0]
apk_name = os.path.basename(apk_file)
is_installed = False
try:
activity = appinfo[1]
except:
print("Apk parse error %s " % apk_file)
skip_apps_install_list.append(apk_file)
continue
print("Start test (%s/%s) apkName = %s , packageName = %s "", ActivityLauncher = %s " % (
test_index, len(apks_list), apk_name, package, activity))
# Step 1 check apk install
force_install_apps = os.popen("adb shell pm list packages %s" % package).read().strip()
if package in force_install_apps:
# print("Apk file %s installed" % apk_file)
# skip_apps_install_list.append(apk_file)
# continue
print("Apk file %s installed" % apk_file)
is_installed = True
# Step 2 install apk
if not is_installed:
installed = execCmd("adb install -r -d -g %s 2>&1" % apk_file) # 冷启动
if check_install(apk_file, apk_name, installed):
continue
time.sleep(10) # 延迟60秒
# Step 3 install apk
# 冷启动app测试
result_cold = test(apk_name, package, activity, APPS_START_COLD)
if result_cold == None:
skip_apps_install_list.append(apk_file)
continue
# 平均值计算,收集
APK_NAME_LIST_MEAN.append(apk_name)
PACKAGE_NAME_LIST_MEAN.append(package)
ACTIVITY_NAME_LIST_MEAN.append(activity)
COLD_WAIT_TIME_MEAN.append('%s' % round(mean(result_cold), 2))
COLD_WAIT_TIME_MAX.append('%s' % round(max(result_cold), 2))
# 测试热启动前,预启动应用
stat_app(APPS_START_HOT, package, activity) # 预启动
# 热启动app测试
result_hot = test(apk_name, package, activity, APPS_START_HOT)
if result_hot == None:
skip_apps_install_list.append(apk_file)
continue
HOT_WAIT_TIME_MEAN.append('%s' % round(mean(result_hot), 2))
HOT_WAIT_TIME_MAX.append('%s' % round(max(result_hot), 2))
# 卸载测试apk
if not is_installed:
uninstall_apps = os.popen("adb uninstall %s" % package).read().strip()
print(uninstall_apps)
# 统计测试源码数据
current_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
EXCEL_LINE_LIST.append(APK_NAME_LIST)
EXCEL_LINE_LIST.append(PACKAGE_NAME_LIST)
EXCEL_LINE_LIST.append(ACTIVITY_NAME_LIST)
EXCEL_LINE_LIST.append(LAUNCHER_STATE_LIST)
EXCEL_LINE_LIST.append(WAIT_TIME_LIST)
EXCEL_LINE_LIST_MEAN.append(APK_NAME_LIST_MEAN)
EXCEL_LINE_LIST_MEAN.append(PACKAGE_NAME_LIST_MEAN)
EXCEL_LINE_LIST_MEAN.append(ACTIVITY_NAME_LIST_MEAN)
EXCEL_LINE_LIST_MEAN.append(COLD_WAIT_TIME_MEAN)
EXCEL_LINE_LIST_MEAN.append(HOT_WAIT_TIME_MEAN)
EXCEL_LINE_LIST_MEAN.append(COLD_WAIT_TIME_MAX)
EXCEL_LINE_LIST_MEAN.append(HOT_WAIT_TIME_MAX)
# 生成测试源数据excel表格
write_excel(EXCEL_RESULT_INFO_TITLE, EXCEL_LINE_LIST, EXCEL_RESULT_INFO % current_time)
# 生成平均值excel表格
write_excel(EXCEL_RESULT_MEAN_TITLE, EXCEL_LINE_LIST_MEAN, EXCEL_RESULT_MEAN % current_time)
# 输出未测试的apk列表
print("app installed skip install list \n%s" % skip_apps_install_list)
build_txt(UNTESTED_LIST % current_time, skip_apps_install_list)
if __name__ == '__main__':
"""
apk_list = get_apks_list(root)
for x in apk_list:
print(x)
AppInfoConfig(x).getApkBaseInfo()
"""
ITEM_TEST_TIMES = int(input("请输入单个apk测试次数(默认冷热启动各50次)"))
if connectDevcie():
do_test()
|