UI自动化能力优化

This commit is contained in:
张家训
2026-07-08 18:04:48 +08:00
parent 9fdfb05b17
commit 39f2a7e17e
7 changed files with 2361 additions and 148 deletions
@@ -0,0 +1,286 @@
"""安装 Airtest 辅助工具(minicap/yosemite)到设备"""
import argparse
import json
import os
import subprocess
import sys
import time
def emit(event_type, level="INFO", message="", **extra):
payload = {"type": event_type, "level": level, "message": message}
payload.update(extra)
print(json.dumps(payload, ensure_ascii=False), flush=True)
def find_adb():
"""查找 adb 可执行文件"""
venv_dir = os.path.join(os.path.dirname(__file__), "..", ".venv")
candidates = [
os.path.join(venv_dir, "Lib", "site-packages", "airtest", "core", "android", "static", "adb", "windows", "adb.exe"),
os.path.join(venv_dir, "lib", "python3.11", "site-packages", "airtest", "core", "android", "static", "adb", "linux", "adb"),
os.path.join(venv_dir, "lib", "python3.10", "site-packages", "airtest", "core", "android", "static", "adb", "linux", "adb"),
]
for c in candidates:
if os.path.exists(c):
return c
return "adb"
def install_minicap(device_uri):
"""通过 Airtest 安装 minicap 并验证"""
try:
from airtest.core.api import auto_setup
import logging
logging.getLogger("airtest").setLevel(logging.INFO)
# 提取 serial
serial = device_uri.replace("Android:///", "").strip()
serial = serial.split("?")[0] if "?" in serial else serial
emit("log", "INFO", "正在连接设备并安装 minicap...")
# 不使用 cap_method=adbcap,让 Airtest 尝试安装 minicap
auto_setup(__file__, devices=[device_uri])
# 等待安装完成
time.sleep(3)
# 通过 ADB 检查 minicap 二进制文件是否存在于设备上
adb = find_adb()
check_cmd = [adb]
if serial:
check_cmd.extend(["-s", serial])
check_cmd.extend(["shell", "ls", "/data/local/tmp/minicap"])
emit("log", "INFO", "检查 minicap 是否已安装到设备...")
result = subprocess.run(check_cmd, capture_output=True, text=True, timeout=10)
combined = (result.stdout + result.stderr).strip()
if "No such file" in combined or "not found" in combined.lower():
emit("log", "WARN", "minicap 二进制文件未找到,安装失败")
return False
# minicap 文件存在,进一步验证是否能正常运行
# 尝试启动 minicap 并立即停止
test_cmd = [adb]
if serial:
test_cmd.extend(["-s", serial])
test_cmd.extend(["shell", "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-i"])
emit("log", "INFO", "验证 minicap 是否可正常运行...")
try:
test_result = subprocess.run(test_cmd, capture_output=True, text=True, timeout=5)
test_output = (test_result.stdout + test_result.stderr).strip()
if "pid=" in test_output or "web socket" in test_output.lower() or result.returncode == 0:
emit("log", "INFO", "minicap 安装成功且可正常运行")
return True
else:
emit("log", "WARN", f"minicap 运行测试输出: {test_output}")
# 即使测试输出不明确,文件存在也算安装成功
return True
except subprocess.TimeoutExpired:
# minicap 作为服务运行会超时,这是正常的
emit("log", "INFO", "minicap 安装成功(服务进程已启动)")
return True
except Exception as e:
emit("log", "ERROR", f"安装 minicap 失败: {e}")
return False
def install_yosemite(device_uri):
"""安装 Yosemite APK(用于 javacap 截图)"""
try:
from airtest.core.api import auto_setup, connect_device
import logging
logging.getLogger("airtest").setLevel(logging.INFO)
serial = device_uri.replace("Android:///", "").strip()
serial = serial.split("?")[0] if "?" in serial else serial
emit("log", "INFO", "正在连接设备...")
auto_setup(__file__, devices=[device_uri])
time.sleep(1)
# 通过 Airtest 的 Android 设备对象直接安装 Yosemite
from airtest.core.helper import G
device = G.DEVICE
emit("log", "INFO", "正在安装 Yosemite APK...")
# 使用设备对象的 yosemite 安装方法
try:
from airtest.core.android.yosemite import Yosemite
yosemite = Yosemite(device.adb)
yosemite.install_or_upgrade()
emit("log", "INFO", "Yosemite 安装成功")
return True
except TypeError:
# 旧版本 API 兼容
from airtest.core.android.yosemite import Yosemite
yosemite = Yosemite(device)
yosemite.install_or_upgrade()
emit("log", "INFO", "Yosemite 安装成功")
return True
except Exception as e:
emit("log", "WARN", f"Yosemite 安装失败: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="安装 Airtest 辅助工具")
parser.add_argument("--device", default="Android:///")
parser.add_argument("--tool", default="minicap", choices=["minicap", "yosemite", "all"])
parser.add_argument("--marker-dir", required=True, help="标记文件存放目录")
args = parser.parse_args()
os.makedirs(args.marker_dir, exist_ok=True)
# 从设备 URI 中提取 serial 用于标记文件名
serial = args.device.replace("Android:///", "").strip()
serial = serial.split("?")[0] if "?" in serial else serial
serial = serial if serial else "default"
safe_serial = serial.replace(":", "_").replace("/", "_")
results = {"minicap": False, "yosemite": False}
if args.tool in ("minicap", "all"):
results["minicap"] = install_minicap(args.device)
if args.tool in ("yosemite", "all"):
results["yosemite"] = install_yosemite(args.device)
# 写入标记文件
for tool_name, success in results.items():
marker_file = os.path.join(args.marker_dir, f".{tool_name}-{safe_serial}")
if success:
with open(marker_file, "w") as f:
f.write(json.dumps({"installed": True, "time": time.strftime("%Y-%m-%d %H:%M:%S")}))
emit("log", "INFO", f"{tool_name} 安装成功,已写入标记文件")
else:
# 如果失败,删除已有标记文件
if os.path.exists(marker_file):
os.remove(marker_file)
emit("log", "WARN", f"{tool_name} 安装失败,已清除标记")
# 输出最终结果
any_success = any(results.values())
emit("result", "INFO" if any_success else "ERROR",
json.dumps(results, ensure_ascii=False),
success=any_success)
return 0 if any_success else 1
if __name__ == "__main__":
sys.exit(main())
"""安装 Airtest 辅助工具(minicap/yosemite)到设备"""
import argparse
import json
import os
import sys
import time
def emit(event_type, level="INFO", message="", **extra):
payload = {"type": event_type, "level": level, "message": message}
payload.update(extra)
print(json.dumps(payload, ensure_ascii=False), flush=True)
def install_minicap(device_uri):
"""通过 Airtest 安装 minicap 并验证"""
try:
from airtest.core.api import auto_setup, snapshot
from airtest.core.android import Android as AndroidDevice
import logging
logging.getLogger("airtest").setLevel(logging.INFO)
emit("log", "INFO", "正在连接设备...")
# 不使用 cap_method=adbcap,让 Airtest 尝试安装 minicap
auto_setup(__file__, devices=[device_uri])
emit("log", "INFO", "设备已连接,正在初始化截图组件...")
# Airtest 在 auto_setup 时会自动安装 minicap,
# 尝试截图来验证是否安装成功
time.sleep(2)
test_path = os.path.join(os.path.dirname(__file__), "..", "test_minicap.png")
try:
snapshot(filename=test_path)
if os.path.exists(test_path) and os.path.getsize(test_path) > 100:
os.remove(test_path)
emit("log", "INFO", "minicap 安装成功,截图验证通过")
return True
else:
emit("log", "WARN", "截图文件过小,minicap 可能未正确安装")
if os.path.exists(test_path):
os.remove(test_path)
return False
except Exception as e:
emit("log", "WARN", f"minicap 截图测试失败: {e}")
return False
except Exception as e:
emit("log", "ERROR", f"安装 minicap 失败: {e}")
return False
def install_yosemite(device_uri):
"""安装 Yosemite APK(用于 javacap 截图)"""
try:
from airtest.core.api import auto_setup
from airtest.core.android.yosemite import Yosemite
import logging
logging.getLogger("airtest").setLevel(logging.INFO)
emit("log", "INFO", "正在安装 Yosemite APK...")
auto_setup(__file__, devices=[device_uri])
yosemite = Yosemite()
yosemite.install_or_upgrade()
emit("log", "INFO", "Yosemite 安装成功")
return True
except Exception as e:
emit("log", "WARN", f"Yosemite 安装失败: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="安装 Airtest 辅助工具")
parser.add_argument("--device", default="Android:///")
parser.add_argument("--tool", default="minicap", choices=["minicap", "yosemite", "all"])
parser.add_argument("--marker-dir", required=True, help="标记文件存放目录")
args = parser.parse_args()
os.makedirs(args.marker_dir, exist_ok=True)
# 从设备 URI 中提取 serial 用于标记文件名
serial = args.device.replace("Android:///", "").strip()
serial = serial.split("?")[0] if "?" in serial else serial
serial = serial if serial else "default"
safe_serial = serial.replace(":", "_").replace("/", "_")
results = {"minicap": False, "yosemite": False}
if args.tool in ("minicap", "all"):
results["minicap"] = install_minicap(args.device)
if args.tool in ("yosemite", "all"):
results["yosemite"] = install_yosemite(args.device)
# 写入标记文件
for tool_name, success in results.items():
marker_file = os.path.join(args.marker_dir, f".{tool_name}-{safe_serial}")
if success:
with open(marker_file, "w") as f:
f.write(json.dumps({"installed": True, "time": time.strftime("%Y-%m-%d %H:%M:%S")}))
emit("log", "INFO", f"{tool_name} 安装成功,已写入标记文件")
else:
# 如果失败,删除已有标记文件
if os.path.exists(marker_file):
os.remove(marker_file)
# 输出最终结果
any_success = any(results.values())
emit("result", "INFO" if any_success else "ERROR",
json.dumps(results, ensure_ascii=False),
success=any_success)
return 0 if any_success else 1
if __name__ == "__main__":
sys.exit(main())