修改游戏窗口获取逻辑以及完善获取模拟点击相关逻辑
This commit is contained in:
374
tests/test_game_window.py
Normal file
374
tests/test_game_window.py
Normal file
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
GameWindowManager 测试用例
|
||||
|
||||
测试环境要求:
|
||||
- Windows 操作系统
|
||||
- Python 3.9+
|
||||
- 安装依赖: pywin32, psutil
|
||||
|
||||
运行测试:
|
||||
python -m pytest tests/test_game_window.py -v
|
||||
python -m pytest tests/test_game_window.py::TestGameWindowManager::test_find_window_by_exe -v
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import time
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
|
||||
import psutil
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.core.game_window import GameWindowManager
|
||||
|
||||
|
||||
class TestGameWindowManager(unittest.TestCase):
|
||||
"""GameWindowManager 测试类"""
|
||||
|
||||
def setUp(self):
|
||||
"""每个测试方法前执行"""
|
||||
self.manager = GameWindowManager()
|
||||
|
||||
def tearDown(self):
|
||||
"""每个测试方法后执行"""
|
||||
self.manager = None
|
||||
|
||||
# ==================== 基础属性测试 ====================
|
||||
|
||||
def test_initial_state(self):
|
||||
"""测试初始状态"""
|
||||
self.assertIsNone(self.manager.hwnd)
|
||||
self.assertIsNone(self.manager.process_id)
|
||||
self.assertFalse(self.manager.is_window_captured)
|
||||
self.assertEqual(self.manager.client_size, (0, 0))
|
||||
self.assertEqual(self.manager.window_rect, (0, 0, 0, 0))
|
||||
|
||||
def test_game_constants(self):
|
||||
"""测试游戏常量配置"""
|
||||
self.assertEqual(self.manager.GAME_EXE_NAME, "ycgame.exe")
|
||||
self.assertEqual(self.manager.GAME_WINDOW_TITLE, "桃源深处有人家")
|
||||
|
||||
# ==================== 模拟窗口测试 ====================
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.win32process')
|
||||
@patch('src.core.game_window.psutil')
|
||||
def test_find_window_by_exe_success(self, mock_psutil, mock_win32process, mock_win32gui):
|
||||
"""测试通过exe名成功查找窗口"""
|
||||
# 模拟窗口句柄和进程ID
|
||||
mock_hwnd = 12345
|
||||
mock_pid = 67890
|
||||
|
||||
# 设置模拟返回值
|
||||
mock_win32gui.IsWindowVisible.return_value = True
|
||||
mock_win32gui.GetWindowText.return_value = "桃源深处有人家"
|
||||
mock_win32gui.GetWindowRect.return_value = (100, 100, 1100, 700)
|
||||
mock_win32process.GetWindowThreadProcessId.return_value = (None, mock_pid)
|
||||
|
||||
# 模拟进程对象
|
||||
mock_process = Mock()
|
||||
mock_process.exe.return_value = "C:\\Game\\ycgame.exe"
|
||||
mock_psutil.Process.return_value = mock_process
|
||||
|
||||
# 模拟EnumWindows回调
|
||||
def mock_enum_windows(callback, extra):
|
||||
callback(mock_hwnd, extra)
|
||||
return True
|
||||
mock_win32gui.EnumWindows = mock_enum_windows
|
||||
|
||||
# 执行测试
|
||||
result = self.manager.find_window_by_exe("ycgame.exe")
|
||||
|
||||
# 验证结果
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(self.manager.hwnd, mock_hwnd)
|
||||
self.assertEqual(self.manager.process_id, mock_pid)
|
||||
self.assertTrue(self.manager.is_window_captured)
|
||||
self.assertEqual(self.manager.client_size, (1000, 600))
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.win32process')
|
||||
@patch('src.core.game_window.psutil')
|
||||
def test_find_window_by_exe_not_found(self, mock_psutil, mock_win32process, mock_win32gui):
|
||||
"""测试通过exe名未找到窗口"""
|
||||
# 模拟没有匹配的窗口
|
||||
def mock_enum_windows(callback, extra):
|
||||
return True
|
||||
mock_win32gui.EnumWindows = mock_enum_windows
|
||||
|
||||
result = self.manager.find_window_by_exe("nonexistent.exe")
|
||||
|
||||
self.assertFalse(result)
|
||||
self.assertIsNone(self.manager.hwnd)
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.win32process')
|
||||
def test_find_window_by_title_success(self, mock_win32process, mock_win32gui):
|
||||
"""测试通过标题成功查找窗口"""
|
||||
mock_hwnd = 12345
|
||||
mock_pid = 67890
|
||||
|
||||
mock_win32gui.FindWindow.return_value = mock_hwnd
|
||||
mock_win32gui.GetWindowRect.return_value = (100, 100, 1100, 700)
|
||||
mock_win32process.GetWindowThreadProcessId.return_value = (None, mock_pid)
|
||||
|
||||
result = self.manager.find_window_by_title("桃源深处有人家")
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(self.manager.hwnd, mock_hwnd)
|
||||
self.assertEqual(self.manager.process_id, mock_pid)
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
def test_find_window_by_title_not_found(self, mock_win32gui):
|
||||
"""测试通过标题未找到窗口"""
|
||||
mock_win32gui.FindWindow.return_value = 0
|
||||
|
||||
# 模拟EnumWindows也没有找到
|
||||
def mock_enum_windows(callback, extra):
|
||||
return True
|
||||
mock_win32gui.EnumWindows = mock_enum_windows
|
||||
|
||||
result = self.manager.find_window_by_title("不存在的窗口")
|
||||
|
||||
self.assertFalse(result)
|
||||
self.assertIsNone(self.manager.hwnd)
|
||||
|
||||
# ==================== 综合查找测试 ====================
|
||||
|
||||
@patch.object(GameWindowManager, 'find_window_by_exe')
|
||||
@patch.object(GameWindowManager, 'find_window_by_title')
|
||||
def test_find_window_use_exe_success(self, mock_find_by_title, mock_find_by_exe):
|
||||
"""测试综合查找 - 通过exe成功"""
|
||||
mock_find_by_exe.return_value = True
|
||||
|
||||
result = self.manager.find_window(use_exe=True)
|
||||
|
||||
self.assertTrue(result)
|
||||
mock_find_by_exe.assert_called_once()
|
||||
mock_find_by_title.assert_not_called()
|
||||
|
||||
@patch.object(GameWindowManager, 'find_window_by_exe')
|
||||
@patch.object(GameWindowManager, 'find_window_by_title')
|
||||
def test_find_window_use_exe_fail_then_title(self, mock_find_by_title, mock_find_by_exe):
|
||||
"""测试综合查找 - exe失败后用标题"""
|
||||
mock_find_by_exe.return_value = False
|
||||
mock_find_by_title.return_value = True
|
||||
|
||||
result = self.manager.find_window(use_exe=True)
|
||||
|
||||
self.assertTrue(result)
|
||||
mock_find_by_exe.assert_called_once()
|
||||
mock_find_by_title.assert_called_once()
|
||||
|
||||
@patch.object(GameWindowManager, 'find_window_by_title')
|
||||
def test_find_window_use_title_only(self, mock_find_by_title):
|
||||
"""测试综合查找 - 只用标题"""
|
||||
mock_find_by_title.return_value = True
|
||||
|
||||
result = self.manager.find_window(use_exe=False)
|
||||
|
||||
self.assertTrue(result)
|
||||
mock_find_by_title.assert_called_once()
|
||||
|
||||
# ==================== 窗口操作测试 ====================
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
def test_bring_to_front(self, mock_win32gui):
|
||||
"""测试窗口置前"""
|
||||
self.manager._hwnd = 12345
|
||||
mock_win32gui.IsWindow.return_value = True
|
||||
|
||||
self.manager.bring_to_front()
|
||||
|
||||
mock_win32gui.SetForegroundWindow.assert_called_once_with(12345)
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
def test_bring_to_front_not_captured(self, mock_win32gui):
|
||||
"""测试窗口未捕获时不执行置前"""
|
||||
mock_win32gui.IsWindow.return_value = False
|
||||
|
||||
self.manager.bring_to_front()
|
||||
|
||||
mock_win32gui.SetForegroundWindow.assert_not_called()
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
def test_click(self, mock_win32gui):
|
||||
"""测试点击坐标转换"""
|
||||
self.manager._hwnd = 12345
|
||||
mock_win32gui.IsWindow.return_value = True
|
||||
mock_win32gui.GetWindowRect.return_value = (100, 100, 1100, 700)
|
||||
|
||||
# 执行点击
|
||||
self.manager.click(500, 300)
|
||||
|
||||
# 验证坐标转换 (窗口左上角100,100 + 相对坐标500,300 = 屏幕坐标600,400)
|
||||
# 注意:实际点击功能还未实现,这里只测试坐标转换逻辑
|
||||
|
||||
# ==================== 窗口信息测试 ====================
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.psutil')
|
||||
def test_get_window_info_captured(self, mock_psutil, mock_win32gui):
|
||||
"""测试获取窗口信息 - 已捕获"""
|
||||
mock_hwnd = 12345
|
||||
mock_pid = 67890
|
||||
|
||||
self.manager._hwnd = mock_hwnd
|
||||
self.manager._process_id = mock_pid
|
||||
mock_win32gui.IsWindow.return_value = True
|
||||
mock_win32gui.GetWindowText.return_value = "桃源深处有人家"
|
||||
mock_win32gui.GetWindowRect.return_value = (100, 100, 1100, 700)
|
||||
|
||||
mock_process = Mock()
|
||||
mock_process.exe.return_value = "C:\\Game\\ycgame.exe"
|
||||
mock_psutil.Process.return_value = mock_process
|
||||
|
||||
info = self.manager.get_window_info()
|
||||
|
||||
self.assertTrue(info["captured"])
|
||||
self.assertEqual(info["hwnd"], mock_hwnd)
|
||||
self.assertEqual(info["title"], "桃源深处有人家")
|
||||
self.assertEqual(info["process_id"], mock_pid)
|
||||
self.assertEqual(info["exe_path"], "C:\\Game\\ycgame.exe")
|
||||
self.assertEqual(info["rect"], (100, 100, 1100, 700))
|
||||
self.assertEqual(info["size"], (1000, 600))
|
||||
|
||||
def test_get_window_info_not_captured(self):
|
||||
"""测试获取窗口信息 - 未捕获"""
|
||||
info = self.manager.get_window_info()
|
||||
|
||||
self.assertFalse(info["captured"])
|
||||
self.assertIsNone(info["hwnd"])
|
||||
self.assertIsNone(info["title"])
|
||||
self.assertIsNone(info["process_id"])
|
||||
self.assertIsNone(info["exe_path"])
|
||||
self.assertIsNone(info["rect"])
|
||||
self.assertIsNone(info["size"])
|
||||
|
||||
# ==================== 错误处理测试 ====================
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.win32process')
|
||||
@patch('src.core.game_window.psutil')
|
||||
def test_find_window_by_exe_access_denied(self, mock_psutil, mock_win32process, mock_win32gui):
|
||||
"""测试访问被拒绝时的处理"""
|
||||
mock_win32gui.IsWindowVisible.return_value = True
|
||||
mock_win32process.GetWindowThreadProcessId.return_value = (None, 12345)
|
||||
mock_psutil.Process.side_effect = psutil.AccessDenied("Access denied")
|
||||
|
||||
def mock_enum_windows(callback, extra):
|
||||
callback(12345, extra)
|
||||
return True
|
||||
mock_win32gui.EnumWindows = mock_enum_windows
|
||||
|
||||
result = self.manager.find_window_by_exe("ycgame.exe")
|
||||
|
||||
# 应该正常处理异常,返回False
|
||||
self.assertFalse(result)
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
def test_find_window_by_exe_exception(self, mock_win32gui):
|
||||
"""测试异常情况处理"""
|
||||
mock_win32gui.EnumWindows.side_effect = Exception("Test exception")
|
||||
|
||||
result = self.manager.find_window_by_exe("ycgame.exe")
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
# ==================== 集成测试(需要实际游戏运行)====================
|
||||
|
||||
@unittest.skip("需要实际游戏运行")
|
||||
def test_integration_find_real_game_window_by_exe(self):
|
||||
"""集成测试:查找真实游戏窗口(通过exe)"""
|
||||
result = self.manager.find_window_by_exe("ycgame.exe")
|
||||
|
||||
if result:
|
||||
print(f"\n找到游戏窗口:")
|
||||
print(f" HWND: {self.manager.hwnd}")
|
||||
print(f" PID: {self.manager.process_id}")
|
||||
print(f" 大小: {self.manager.client_size}")
|
||||
info = self.manager.get_window_info()
|
||||
print(f" 标题: {info['title']}")
|
||||
print(f" 路径: {info['exe_path']}")
|
||||
else:
|
||||
print("\n未找到游戏窗口,请确保游戏已运行")
|
||||
|
||||
# 不强制断言,因为游戏可能未运行
|
||||
|
||||
@unittest.skip("需要实际游戏运行")
|
||||
def test_integration_find_real_game_window_by_title(self):
|
||||
"""集成测试:查找真实游戏窗口(通过标题)"""
|
||||
result = self.manager.find_window_by_title("桃源深处有人家")
|
||||
|
||||
if result:
|
||||
print(f"\n找到游戏窗口:")
|
||||
print(f" HWND: {self.manager.hwnd}")
|
||||
print(f" 大小: {self.manager.client_size}")
|
||||
else:
|
||||
print("\n未找到游戏窗口,请确保游戏已运行")
|
||||
|
||||
@unittest.skip("需要实际游戏运行")
|
||||
def test_integration_window_operations(self):
|
||||
"""集成测试:窗口操作"""
|
||||
# 先捕获窗口
|
||||
if not self.manager.capture_window():
|
||||
self.skipTest("游戏未运行,跳过测试")
|
||||
|
||||
# 测试获取信息
|
||||
info = self.manager.get_window_info()
|
||||
self.assertTrue(info["captured"])
|
||||
|
||||
# 测试置前(不验证结果,只是确保不报错)
|
||||
try:
|
||||
self.manager.bring_to_front()
|
||||
except Exception as e:
|
||||
self.fail(f"bring_to_front 抛出异常: {e}")
|
||||
|
||||
# 等待一下观察效果
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
class TestGameWindowManagerPerformance(unittest.TestCase):
|
||||
"""性能测试"""
|
||||
|
||||
@patch('src.core.game_window.win32gui')
|
||||
@patch('src.core.game_window.win32process')
|
||||
@patch('src.core.game_window.psutil')
|
||||
def test_find_window_performance(self, mock_psutil, mock_win32process, mock_win32gui):
|
||||
"""测试查找窗口性能"""
|
||||
# 设置模拟数据
|
||||
mock_win32gui.IsWindowVisible.return_value = True
|
||||
mock_win32gui.GetWindowText.return_value = "桃源深处有人家"
|
||||
mock_win32gui.GetWindowRect.return_value = (100, 100, 1100, 700)
|
||||
mock_win32process.GetWindowThreadProcessId.return_value = (None, 12345)
|
||||
|
||||
mock_process = Mock()
|
||||
mock_process.exe.return_value = "C:\\Game\\ycgame.exe"
|
||||
mock_psutil.Process.return_value = mock_process
|
||||
|
||||
def mock_enum_windows(callback, extra):
|
||||
for i in range(100): # 模拟100个窗口
|
||||
callback(10000 + i, extra)
|
||||
return True
|
||||
mock_win32gui.EnumWindows = mock_enum_windows
|
||||
|
||||
manager = GameWindowManager()
|
||||
|
||||
# 测量查找时间
|
||||
import time
|
||||
start = time.time()
|
||||
result = manager.find_window_by_exe("ycgame.exe")
|
||||
elapsed = time.time() - start
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertLess(elapsed, 1.0, "查找窗口应该在一秒内完成")
|
||||
print(f"\n查找100个窗口耗时: {elapsed:.3f}秒")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 运行测试
|
||||
unittest.main(verbosity=2)
|
||||
356
tests/test_input_simulator.py
Normal file
356
tests/test_input_simulator.py
Normal file
@@ -0,0 +1,356 @@
|
||||
"""
|
||||
InputSimulator 测试用例
|
||||
|
||||
测试环境要求:
|
||||
- Windows 操作系统
|
||||
- Python 3.9+
|
||||
- 安装依赖: pywin32
|
||||
|
||||
运行测试:
|
||||
python -m pytest tests/test_input_simulator.py -v
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import time
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch, MagicMock, call
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.core.input_simulator import InputSimulator, Point, SwipePath
|
||||
from src.core.game_window import GameWindowManager
|
||||
|
||||
|
||||
class TestInputSimulator(unittest.TestCase):
|
||||
"""InputSimulator 测试类"""
|
||||
|
||||
def setUp(self):
|
||||
"""每个测试方法前执行"""
|
||||
self.mock_window = Mock(spec=GameWindowManager)
|
||||
self.mock_window.is_window_captured = True
|
||||
self.mock_window.window_rect = (100, 100, 1100, 700) # left, top, right, bottom
|
||||
|
||||
self.simulator = InputSimulator(self.mock_window)
|
||||
|
||||
def tearDown(self):
|
||||
"""每个测试方法后执行"""
|
||||
self.simulator = None
|
||||
|
||||
# ==================== 基础功能测试 ====================
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_click_short(self, mock_win32api):
|
||||
"""测试短按点击"""
|
||||
self.simulator.click(100, 200)
|
||||
|
||||
# 验证鼠标移动
|
||||
mock_win32api.SetCursorPos.assert_called()
|
||||
|
||||
# 验证按下和释放
|
||||
mock_win32api.mouse_event.assert_any_call(
|
||||
unittest.mock.ANY, 0, 0, 0, 0 # MOUSEEVENTF_LEFTDOWN
|
||||
)
|
||||
mock_win32api.mouse_event.assert_any_call(
|
||||
unittest.mock.ANY, 0, 0, 0, 0 # MOUSEEVENTF_LEFTUP
|
||||
)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_click_long(self, mock_win32api):
|
||||
"""测试长按"""
|
||||
start_time = time.time()
|
||||
self.simulator.click(100, 200, duration=0.5)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
# 验证长按时间(允许误差)
|
||||
self.assertGreaterEqual(elapsed, 0.5)
|
||||
self.assertLess(elapsed, 0.7)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_click_different_buttons(self, mock_win32api):
|
||||
"""测试不同鼠标按钮"""
|
||||
# 左键
|
||||
self.simulator.click(100, 200, button="left")
|
||||
# 右键
|
||||
self.simulator.click(100, 200, button="right")
|
||||
# 中键
|
||||
self.simulator.click(100, 200, button="middle")
|
||||
|
||||
# 验证调用了3次按下和3次释放
|
||||
self.assertEqual(mock_win32api.mouse_event.call_count, 6)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_click_window_not_captured(self, mock_win32api):
|
||||
"""测试窗口未捕获时抛出异常"""
|
||||
self.mock_window.is_window_captured = False
|
||||
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
self.simulator.click(100, 200)
|
||||
|
||||
self.assertIn("窗口未捕获", str(context.exception))
|
||||
|
||||
# ==================== 滑动测试 ====================
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_swipe_basic(self, mock_win32api):
|
||||
"""测试基础滑动"""
|
||||
self.simulator.swipe(100, 200, 300, 400, duration=0.1)
|
||||
|
||||
# 验证鼠标移动被多次调用(滑动过程)
|
||||
self.assertGreater(mock_win32api.SetCursorPos.call_count, 5)
|
||||
|
||||
# 验证按下和释放
|
||||
mock_win32api.mouse_event.assert_any_call(
|
||||
unittest.mock.ANY, 0, 0, 0, 0 # MOUSEEVENTF_LEFTDOWN
|
||||
)
|
||||
mock_win32api.mouse_event.assert_any_call(
|
||||
unittest.mock.ANY, 0, 0, 0, 0 # MOUSEEVENTF_LEFTUP
|
||||
)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_swipe_duration(self, mock_win32api):
|
||||
"""测试滑动持续时间"""
|
||||
start_time = time.time()
|
||||
self.simulator.swipe(100, 200, 300, 400, duration=0.3)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
# 验证滑动时间(允许误差)
|
||||
self.assertGreaterEqual(elapsed, 0.3)
|
||||
self.assertLess(elapsed, 0.5)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_swipe_coordinate_conversion(self, mock_win32api):
|
||||
"""测试滑动坐标转换"""
|
||||
self.simulator.swipe(0, 0, 100, 100)
|
||||
|
||||
# 获取所有SetCursorPos调用
|
||||
calls = mock_win32api.SetCursorPos.call_args_list
|
||||
|
||||
# 第一个调用应该是起点(窗口左上角100,100 + 相对坐标0,0 = 屏幕坐标100,100)
|
||||
first_call = calls[0]
|
||||
self.assertEqual(first_call[0][0], (100, 100))
|
||||
|
||||
# 最后一个调用应该是终点(窗口左上角100,100 + 相对坐标100,100 = 屏幕坐标200,200)
|
||||
last_call = calls[-1]
|
||||
self.assertEqual(last_call[0][0], (200, 200))
|
||||
|
||||
# ==================== 键盘测试 ====================
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_key_press_letter(self, mock_win32api):
|
||||
"""测试字母按键"""
|
||||
self.simulator.key_press('a')
|
||||
|
||||
# 验证按下和释放
|
||||
self.assertEqual(mock_win32api.keybd_event.call_count, 2)
|
||||
|
||||
# 验证按下(第二个参数为0表示按下)
|
||||
mock_win32api.keybd_event.assert_any_call(ord('A'), 0, 0, 0)
|
||||
|
||||
# 验证释放(第三个参数包含KEYEVENTF_KEYUP)
|
||||
mock_win32api.keybd_event.assert_any_call(
|
||||
ord('A'), 0, unittest.mock.ANY, 0
|
||||
)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_key_press_special(self, mock_win32api):
|
||||
"""测试特殊按键"""
|
||||
special_keys = ['enter', 'esc', 'space', 'tab', 'f1', 'ctrl']
|
||||
|
||||
for key in special_keys:
|
||||
self.simulator.key_press(key)
|
||||
|
||||
# 每个按键应该有按下和释放两个调用
|
||||
self.assertEqual(mock_win32api.keybd_event.call_count, len(special_keys) * 2)
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_key_press_unknown(self, mock_win32api):
|
||||
"""测试未知按键"""
|
||||
self.simulator.key_press('unknown_key')
|
||||
|
||||
# 不应该调用keybd_event
|
||||
mock_win32api.keybd_event.assert_not_called()
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_key_down_up(self, mock_win32api):
|
||||
"""测试按住和释放按键"""
|
||||
self.simulator.key_down('shift')
|
||||
self.simulator.key_up('shift')
|
||||
|
||||
# 验证只调用了两次(一次按下,一次释放)
|
||||
self.assertEqual(mock_win32api.keybd_event.call_count, 2)
|
||||
|
||||
# 第一次是按下(第3个参数为0)
|
||||
first_call = mock_win32api.keybd_event.call_args_list[0]
|
||||
self.assertEqual(first_call[0][2], 0) # dwFlags是第3个位置参数
|
||||
|
||||
# 第二次是释放(第3个参数不为0)
|
||||
second_call = mock_win32api.keybd_event.call_args_list[1]
|
||||
self.assertNotEqual(second_call[0][2], 0)
|
||||
|
||||
# ==================== 便捷方法测试 ====================
|
||||
|
||||
@patch.object(InputSimulator, 'click')
|
||||
def test_double_click(self, mock_click):
|
||||
"""测试双击"""
|
||||
self.simulator.double_click(100, 200)
|
||||
|
||||
# 验证click被调用了两次
|
||||
self.assertEqual(mock_click.call_count, 2)
|
||||
mock_click.assert_any_call(100, 200, duration=0, button='left')
|
||||
|
||||
@patch.object(InputSimulator, 'click')
|
||||
def test_long_press(self, mock_click):
|
||||
"""测试长按便捷方法"""
|
||||
self.simulator.long_press(100, 200, duration=2.0)
|
||||
|
||||
# 验证调用了click并传递了duration
|
||||
mock_click.assert_called_once_with(100, 200, duration=2.0, button='left')
|
||||
|
||||
@patch.object(InputSimulator, 'swipe')
|
||||
def test_drag(self, mock_swipe):
|
||||
"""测试拖拽便捷方法"""
|
||||
self.simulator.drag(100, 200, 300, 400, duration=1.0, button='left')
|
||||
|
||||
# 验证调用了swipe
|
||||
mock_swipe.assert_called_once_with(100, 200, 300, 400, 1.0, 'left')
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_scroll(self, mock_win32api):
|
||||
"""测试滚动"""
|
||||
self.simulator.scroll(500, 300, delta=-3)
|
||||
|
||||
# 验证鼠标移动
|
||||
mock_win32api.SetCursorPos.assert_called_once()
|
||||
|
||||
# 验证滚动事件(delta * 120 = -360)
|
||||
mock_win32api.mouse_event.assert_called_once_with(
|
||||
unittest.mock.ANY, 0, 0, -360, 0
|
||||
)
|
||||
|
||||
# ==================== 坐标转换测试 ====================
|
||||
|
||||
def test_coordinate_conversion(self):
|
||||
"""测试坐标转换"""
|
||||
# 窗口位置 (100, 100),点击窗口内 (50, 50)
|
||||
screen_coords = self.simulator._to_screen_coords(50, 50)
|
||||
|
||||
# 屏幕坐标应该是 (150, 150)
|
||||
self.assertEqual(screen_coords, (150, 150))
|
||||
|
||||
def test_coordinate_conversion_different_window(self):
|
||||
"""测试不同窗口位置的坐标转换"""
|
||||
self.mock_window.window_rect = (200, 100, 1200, 700)
|
||||
|
||||
screen_coords = self.simulator._to_screen_coords(100, 200)
|
||||
|
||||
# 屏幕坐标应该是 (300, 300)
|
||||
self.assertEqual(screen_coords, (300, 300))
|
||||
|
||||
# ==================== 性能测试 ====================
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_click_performance(self, mock_win32api):
|
||||
"""测试点击性能"""
|
||||
start = time.time()
|
||||
|
||||
# 执行10次点击(减少次数避免超时)
|
||||
for i in range(10):
|
||||
self.simulator.click(i, i)
|
||||
|
||||
elapsed = time.time() - start
|
||||
|
||||
# 10次点击应该在3秒内完成
|
||||
self.assertLess(elapsed, 3.0)
|
||||
print(f"\n10次点击耗时: {elapsed:.3f}秒")
|
||||
|
||||
@patch('src.core.input_simulator.win32api')
|
||||
def test_swipe_smoothness(self, mock_win32api):
|
||||
"""测试滑动平滑度(步数)"""
|
||||
self.simulator.swipe(0, 0, 1000, 1000, duration=1.0)
|
||||
|
||||
# 获取SetCursorPos调用次数(应该至少有60次,每秒60步)
|
||||
call_count = mock_win32api.SetCursorPos.call_count
|
||||
|
||||
# 验证有足够的步数保证平滑
|
||||
self.assertGreaterEqual(call_count, 60)
|
||||
print(f"\n滑动步数: {call_count}")
|
||||
|
||||
|
||||
class TestPoint(unittest.TestCase):
|
||||
"""Point 数据类测试"""
|
||||
|
||||
def test_point_creation(self):
|
||||
"""测试创建Point"""
|
||||
p = Point(10, 20)
|
||||
self.assertEqual(p.x, 10)
|
||||
self.assertEqual(p.y, 20)
|
||||
|
||||
def test_point_addition(self):
|
||||
"""测试Point相加"""
|
||||
p1 = Point(10, 20)
|
||||
p2 = Point(5, 5)
|
||||
result = p1 + p2
|
||||
|
||||
self.assertEqual(result.x, 15)
|
||||
self.assertEqual(result.y, 25)
|
||||
|
||||
def test_point_iteration(self):
|
||||
"""测试Point迭代"""
|
||||
p = Point(10, 20)
|
||||
coords = list(p)
|
||||
|
||||
self.assertEqual(coords, [10, 20])
|
||||
|
||||
|
||||
class TestIntegration(unittest.TestCase):
|
||||
"""集成测试(需要实际游戏运行)"""
|
||||
|
||||
@unittest.skip("需要实际游戏运行")
|
||||
def test_real_click(self):
|
||||
"""测试真实点击"""
|
||||
from src.core.game_window import GameWindowManager
|
||||
|
||||
window = GameWindowManager()
|
||||
if not window.capture_window():
|
||||
self.skipTest("游戏未运行")
|
||||
|
||||
simulator = InputSimulator(window)
|
||||
|
||||
# 在游戏窗口中心点击
|
||||
size = window.client_size
|
||||
center_x = size[0] // 2
|
||||
center_y = size[1] // 2
|
||||
|
||||
print(f"\n将在游戏窗口中心 ({center_x}, {center_y}) 点击")
|
||||
simulator.click(center_x, center_y)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
@unittest.skip("需要实际游戏运行")
|
||||
def test_real_swipe(self):
|
||||
"""测试真实滑动"""
|
||||
from src.core.game_window import GameWindowManager
|
||||
|
||||
window = GameWindowManager()
|
||||
if not window.capture_window():
|
||||
self.skipTest("游戏未运行")
|
||||
|
||||
simulator = InputSimulator(window)
|
||||
|
||||
size = window.client_size
|
||||
start_x = size[0] // 4
|
||||
start_y = size[1] // 2
|
||||
end_x = size[0] * 3 // 4
|
||||
end_y = size[1] // 2
|
||||
|
||||
print(f"\n将从 ({start_x}, {start_y}) 滑动到 ({end_x}, {end_y})")
|
||||
simulator.swipe(start_x, start_y, end_x, end_y, duration=1.0)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user