autool/test_agent.py
2026-06-17 19:44:18 +08:00

158 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
test_agent.py - 场景测试脚本
通过指定 scene直接调用 GuiAgentBridge 在 Android 设备上执行完整场景测试。
脚本会复用 scene_configs.json、GuiAgentBridge 和当前 FSM 运行时逻辑,
因此适合验证 login/register 等场景化 prompt 是否按预期工作。
用法:直接修改下方 CONFIG 字典中的配置,然后运行 python3 test_agent.py
"""
import os
import sys
import logging
from pathlib import Path
# ==============================================================================
# 配置区 - 所有参数在这里修改,无需命令行参数
# ==============================================================================
CONFIG = {
"scene": "login", # 要测试的场景名,如 login/register/payment
"additional_info": "", # 可选:补充本次测试目标,会追加到 scene prompt 后
"device_serial": None, # ADB 设备序列号None = 自动选择第一个设备
"is_emulator": True, # 是否模拟器
"max_steps": None, # 可选:覆盖 scene 配置中的 total_stepsNone 表示使用配置值
"max_error_steps": 15, # 最大连续失败步数
"output_dir": "./test_agent_output", # 截图等输出目录
"app_name": None, # 可选:用于日志与 prompt 展示的 app 名称
}
# ==============================================================================
# 环境初始化
# ==============================================================================
ROOT = Path(__file__).resolve().parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(name)-12s] %(levelname)-7s %(message)s",
datefmt="%H:%M:%S",
)
logger = logging.getLogger("TestAgent")
# 导入项目模块 —— 触发平台自动注册
from DroidBot.core import PlatformFactory
from DroidBot import platforms # noqa: F401 - 触发 Android 平台注册
from DroidBot.guiagent_bridge import GuiAgentBridge
from DroidBot.guiagent_core.scene_config_loader import load_scene_config
def validate_scene(scene: str, platform: str = "android") -> dict:
"""校验 scene 是否存在,并返回对应配置。"""
scene_config = load_scene_config(platform)
scenes = scene_config.get("scenes", {})
if scene not in scenes:
available = ", ".join(sorted(scenes.keys()))
raise ValueError(f"未知场景 '{scene}',当前 {platform} 可用场景: {available}")
return scenes[scene]
def run_agent(config: dict):
"""初始化设备并执行指定 scene。"""
scene = config.get("scene")
if not scene:
raise ValueError("CONFIG['scene'] 必填")
additional_info = config.get("additional_info") or config.get("prompt") or ""
max_steps = config.get("max_steps")
max_error_steps = config.get("max_error_steps", 15)
output_dir = config.get("output_dir", "./test_agent_output")
os.makedirs(output_dir, exist_ok=True)
logger.info("=" * 60)
logger.info("校验场景配置...")
scene_spec = validate_scene(scene, platform="android")
logger.info(
"场景有效: scene=%s, initial_stage=%s, stages=%s",
scene,
scene_spec.get("initial_stage", "main"),
list(scene_spec.get("stages", {}).keys()),
)
logger.info("=" * 60)
logger.info("初始化 Android 设备...")
device = PlatformFactory.create_device(
"android",
device_serial=config.get("device_serial"),
is_emulator=config.get("is_emulator", True),
output_dir=output_dir,
)
try:
device.set_up()
device.connect()
logger.info("设备连接成功")
display_info = device.get_display_info()
width = display_info.get("width", 1080)
height = display_info.get("height", 1920)
logger.info(f"设备分辨率: {(width, height)}")
logger.info("初始化 GuiAgentBridge...")
bridge = GuiAgentBridge(
device=device,
app_name=config.get("app_name"),
)
logger.info("GuiAgentBridge 初始化成功")
current_state = device.get_current_state()
context = {"state": current_state}
if additional_info:
context["additional_info"] = additional_info
logger.info(f"附加信息: {additional_info}")
logger.info("=" * 60)
logger.info(
"开始执行场景测试: scene=%s, max_steps=%s, max_error_steps=%s",
scene,
max_steps if max_steps is not None else "scene_config",
max_error_steps,
)
success, message, reason_code = bridge.handle_with_guiagent(
category=scene,
context=context,
max_steps=max_steps,
max_error_steps=max_error_steps,
)
logger.info("=" * 60)
logger.info("场景执行完成")
logger.info("success=%s", success)
logger.info("message=%s", message)
logger.info("reason_code=%s", reason_code)
logger.info("=" * 60)
except KeyboardInterrupt:
logger.warning("用户手动中断")
except Exception as e:
logger.error(f"执行异常: {e}")
import traceback
traceback.print_exc()
finally:
logger.info("断开设备连接...")
try:
device.disconnect()
except Exception as e:
logger.warning(f"断开设备时发生异常: {e}")
logger.info("设备已断开")
if __name__ == "__main__":
run_agent(CONFIG)