#!/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()