158 lines
5.6 KiB
Python
158 lines
5.6 KiB
Python
#!/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_steps;None 表示使用配置值
|
||
"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)
|