203 lines
6.4 KiB
Python
203 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Windows 平台自动化测试启动脚本
|
||
|
||
此脚本用于启动 Windows 桌面应用程序的自动化测试。
|
||
与 Android 版本不同,Windows 版本不需要从 CSV 文件读取参数,
|
||
而是直接通过 Config 类配置或命令行参数指定目标窗口或可执行文件。
|
||
|
||
用法示例:
|
||
#修改 Config 类中的默认值后直接运行
|
||
python start_windows.py
|
||
|
||
"""
|
||
import argparse
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
from dataclasses import dataclass
|
||
from typing import Optional
|
||
|
||
# 找到项目根目录
|
||
ROOT = Path(__file__).resolve().parent
|
||
|
||
# 确保根目录在Python路径中
|
||
if str(ROOT) not in sys.path:
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
|
||
@dataclass
|
||
class Config:
|
||
"""
|
||
Windows 平台测试配置
|
||
|
||
修改此类中的属性来设置默认值,然后直接运行 python start_windows.py 即可。
|
||
"""
|
||
|
||
# ===== Windows 必要参数 =====
|
||
# DAVE THE DIVER Counter-Strike 2
|
||
WINDOW_TITLE: str = "old - 记事本"
|
||
|
||
# 可执行文件路径(可选)。如果窗口未找到,将使用此路径启动应用
|
||
# 例如: "C:\\Windows\\System32\\notepad.exe"
|
||
EXE_PATH: Optional[str] = None
|
||
|
||
# Steam 游戏 ID(可选)。如果指定,将使用 steam://rungameid/{ID} 启动游戏
|
||
# 例如: CS2 = 730, DAVE THE DIVER = 1868140
|
||
STEAM_GAME_ID: Optional[str] = None
|
||
|
||
# ===== 输出配置 =====
|
||
# 输出目录基础路径,实际目录会自动添加时间戳
|
||
OUTPUT_BASE_DIR: str = "./win_output"
|
||
|
||
# ===== 测试策略配置 =====
|
||
# 测试策略: "none", "memory_guided", "manual"
|
||
POLICY: str = "memory_guided"
|
||
|
||
# 测试超时时间(秒)
|
||
TIMEOUT: int = 3600
|
||
|
||
# 要生成的事件总数
|
||
EVENT_COUNT: int = 1000
|
||
|
||
# 每两个事件之间的间隔(秒)
|
||
EVENT_INTERVAL: int = 1
|
||
|
||
# ===== 功能开关 =====
|
||
# 是否启用 GuiAgent 进行智能UI交互
|
||
ENABLE_GUIAGENT: bool = False
|
||
|
||
# 是否以调试模式运行
|
||
DEBUG_MODE: bool = False
|
||
|
||
# 是否为输入事件添加随机性
|
||
RANDOM_INPUT: bool = False
|
||
|
||
|
||
def generate_output_dir(window_title: str, base_dir: str = "./output") -> str:
|
||
"""
|
||
生成带时间戳的输出目录
|
||
|
||
格式: ./output/WindowTitle_YYYYMMDD_HHMMSS
|
||
"""
|
||
# 清理窗口标题,移除特殊字符
|
||
sanitized_title = "".join(c if c.isalnum() or c in ('_', '-') else '_' for c in window_title)
|
||
sanitized_title = sanitized_title[:30] # 限制长度
|
||
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
folder_name = f"{sanitized_title}_{timestamp}"
|
||
|
||
return os.path.join(base_dir, folder_name)
|
||
|
||
|
||
def run_droidbot_direct(config: Config, output_dir: str) -> None:
|
||
"""
|
||
直接调用 DroidBot 进行测试(不通过子进程)
|
||
"""
|
||
|
||
from DroidBot.droidbot import DroidBot
|
||
|
||
print(f"{'='*60}")
|
||
print(f"[INFO] 启动 Windows 平台自动化测试")
|
||
print(f"[INFO] 目标窗口: {config.WINDOW_TITLE}")
|
||
if config.EXE_PATH:
|
||
print(f"[INFO] 可执行文件: {config.EXE_PATH}")
|
||
if config.STEAM_GAME_ID:
|
||
print(f"[INFO] Steam 游戏 ID: {config.STEAM_GAME_ID}")
|
||
print(f"[INFO] 输出目录: {output_dir}")
|
||
print(f"[INFO] 测试策略: {config.POLICY}")
|
||
print(f"[INFO] 超时时间: {config.TIMEOUT}秒")
|
||
print(f"{'='*60}\n")
|
||
|
||
# 创建 DroidBot 实例
|
||
droidbot = DroidBot(
|
||
package_name=config.STEAM_GAME_ID, # Windows 平台: package_name -> steam_game_id
|
||
app_name=config.WINDOW_TITLE, # Windows 平台: app_name -> window_title
|
||
device_serial=config.EXE_PATH, # Windows 平台: device_serial -> exe_path
|
||
is_emulator=False,
|
||
output_dir=output_dir,
|
||
policy_name=config.POLICY,
|
||
random_input=config.RANDOM_INPUT,
|
||
event_interval=config.EVENT_INTERVAL,
|
||
timeout=config.TIMEOUT,
|
||
event_count=config.EVENT_COUNT,
|
||
cv_mode=True, # Windows 平台默认使用 CV 模式
|
||
debug_mode=config.DEBUG_MODE,
|
||
keep_app=True, # Windows 应用通常不需要卸载
|
||
keep_env=True,
|
||
profiling_method=None,
|
||
grant_perm=False,
|
||
enable_accessibility_hard=False,
|
||
humanoid=None,
|
||
ignore_ad=False,
|
||
replay_output=None,
|
||
enable_guiagent=config.ENABLE_GUIAGENT,
|
||
platform="windows"
|
||
)
|
||
|
||
droidbot.start()
|
||
|
||
|
||
def main():
|
||
"""
|
||
主函数
|
||
"""
|
||
# 创建配置实例
|
||
config = Config()
|
||
|
||
# 验证参数
|
||
if not config.WINDOW_TITLE:
|
||
print("[ERROR] 必须在 Config 类中设置 WINDOW_TITLE")
|
||
sys.exit(1)
|
||
|
||
if config.EXE_PATH and not os.path.exists(config.EXE_PATH):
|
||
print(f"[WARN] 指定的可执行文件不存在: {config.EXE_PATH}")
|
||
print("[WARN] 将尝试直接通过窗口标题查找应用")
|
||
|
||
# 确定输出目录
|
||
output_dir = generate_output_dir(config.WINDOW_TITLE, config.OUTPUT_BASE_DIR)
|
||
|
||
# 规范化路径
|
||
output_dir = output_dir.replace('\\', '/')
|
||
|
||
# 创建输出目录
|
||
try:
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
print(f"[INFO] 输出目录: {output_dir}")
|
||
except OSError as e:
|
||
print(f"[ERROR] 创建输出目录失败: {e}")
|
||
sys.exit(1)
|
||
|
||
# 保存命令日志
|
||
cmd_log_path = os.path.join(output_dir, "config_dump.txt")
|
||
with open(cmd_log_path, "w", encoding="utf-8") as f:
|
||
f.write(f"Window Title: {config.WINDOW_TITLE}\n")
|
||
f.write(f"Exe Path: {config.EXE_PATH or 'N/A'}\n")
|
||
f.write(f"Output Dir: {output_dir}\n")
|
||
f.write(f"Policy: {config.POLICY}\n")
|
||
f.write(f"Timeout: {config.TIMEOUT}\n")
|
||
f.write(f"Event Count: {config.EVENT_COUNT}\n")
|
||
f.write(f"Event Interval: {config.EVENT_INTERVAL}\n")
|
||
f.write(f"Enable GuiAgent: {config.ENABLE_GUIAGENT}\n")
|
||
f.write(f"Debug Mode: {config.DEBUG_MODE}\n")
|
||
f.write(f"Random Input: {config.RANDOM_INPUT}\n")
|
||
|
||
try:
|
||
# 直接调用 DroidBot
|
||
run_droidbot_direct(config, output_dir)
|
||
print(f"\n[SUCCESS] 测试任务完成")
|
||
except KeyboardInterrupt:
|
||
print(f"\n[WARN] 用户手动中断测试")
|
||
sys.exit(0)
|
||
except Exception as e:
|
||
print(f"\n[ERROR] 执行任务时发生异常: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|