#!/usr/bin/env python3 """ 俄罗斯方块游戏深度测试脚本 使用Playwright无头浏览器进行专业测试 """ import asyncio from playwright.async_api import async_playwright import json import time from datetime import datetime from pathlib import Path import sys # 测试配置 BASE_URL = "http://129.211.173.87:8895/games/tetris/" OUTPUT_DIR = Path("/tmp/tetris_deep_test") OUTPUT_DIR.mkdir(exist_ok=True) # 重点测试版本 KEY_VERSIONS = [ # 优秀版本 {"file": "tetris_playcanvas_working.html", "name": "PlayCanvas工作版", "type": "playcanvas"}, {"file": "tetris_final_canvas2d.html", "name": "最终Canvas 2D版", "type": "canvas2d"}, {"file": "tetris_simple_fall_test.html", "name": "简单下落测试版", "type": "canvas2d"}, # 默认版本 {"file": "index.html", "name": "默认版", "type": "default"}, # 问题版本(需要修复) {"file": "tetris_simple_fixed.html", "name": "简单修复版", "type": "canvas2d"}, {"file": "tetris_working.html", "name": "工作版", "type": "canvas2d"}, ] async def test_version(page, version, test_id): """深度测试单个版本""" print(f"\n{'='*80}") print(f"🎮 深度测试: {version['name']} ({version['file']})") print(f"{'='*80}") results = { "version": version, "test_id": test_id, "start_time": datetime.now().isoformat(), "metrics": {}, "issues": [], "logs": [], "screenshots": [] } try: # 导航到页面 url = BASE_URL + version["file"] print(f"📡 访问: {url}") start_time = time.time() await page.goto(url, wait_until="networkidle", timeout=15000) load_time = time.time() - start_time results["metrics"]["load_time"] = load_time print(f"✅ 页面加载: {load_time:.2f}秒") # 等待页面完全初始化 await page.wait_for_timeout(2000) # 收集页面基本信息 title = await page.title() results["metrics"]["title"] = title print(f"📄 页面标题: {title}") # 检查Canvas元素 canvas_elements = await page.locator("canvas").all() canvas_count = len(canvas_elements) results["metrics"]["canvas_count"] = canvas_count print(f"🎨 Canvas数量: {canvas_count}") if canvas_count > 0: canvas = canvas_elements[0] box = await canvas.bounding_box() if box: canvas_size = f"{int(box['width'])}x{int(box['height'])}" results["metrics"]["canvas_size"] = canvas_size print(f"📐 Canvas尺寸: {canvas_size}") # 查找所有按钮 buttons = [] button_selectors = [ "#start-btn", "#start-button", "#restart-btn", "#pause-btn", "#left-btn", "#right-btn", "#down-btn", "#rotate-btn", "button:has-text('开始')", "button:has-text('Start')", "button:has-text('暂停')", "button:has-text('Pause')", "button:has-text('重来')", "button:has-text('Restart')", "button:has-text('旋转')", "button:has-text('Rotate')", "button:has-text('←')", "button:has-text('→')", "button:has-text('↓')", ] for selector in button_selectors: elements = await page.locator(selector).all() count = len(elements) if count > 0: buttons.append({"selector": selector, "count": count}) results["metrics"]["buttons_found"] = len(buttons) print(f"🔘 找到按钮: {len(buttons)}个") for btn in buttons: print(f" - {btn['selector']}: {btn['count']}个") # 监听控制台日志 console_logs = [] page.on("console", lambda msg: console_logs.append({ "time": datetime.now().isoformat(), "type": msg.type, "text": msg.text })) # 监听页面错误 page_errors = [] page.on("pageerror", lambda error: page_errors.append({ "time": datetime.now().isoformat(), "error": str(error) })) # 测试1: 点击开始按钮 print("\n🧪 测试1: 点击开始按钮") start_button = None for btn in buttons: if "start" in btn["selector"].lower() or "开始" in btn["selector"]: start_button = page.locator(btn["selector"]).first() break if start_button: try: await start_button.click(timeout=3000) results["metrics"]["start_button_clicked"] = True print("✅ 成功点击开始按钮") except Exception as e: results["issues"].append(f"开始按钮点击失败: {str(e)}") print(f"⚠️ 开始按钮点击失败: {e}") else: print("⚠️ 未找到开始按钮,尝试按空格键") await page.keyboard.press(" ") # 等待游戏开始 await page.wait_for_timeout(1000) # 测试2: 游戏运行状态检查 print("\n🧪 测试2: 游戏运行状态检查") await page.wait_for_timeout(3000) # 等待3秒让游戏运行 # 分析日志 spawn_logs = [log for log in console_logs if any(keyword in log["text"].lower() for keyword in ["生成", "spawn", "new piece", "方块", "piece"])] move_logs = [log for log in console_logs if any(keyword in log["text"].lower() for keyword in ["移动", "move", "下落", "drop", "位置", "x=", "y="])] error_logs = [log for log in console_logs if log["type"] == "error"] results["metrics"]["spawn_logs"] = len(spawn_logs) results["metrics"]["move_logs"] = len(move_logs) results["metrics"]["error_logs"] = len(error_logs) results["metrics"]["total_logs"] = len(console_logs) print(f"📊 日志分析:") print(f" 总日志: {len(console_logs)}条") print(f" 方块生成: {len(spawn_logs)}次") print(f" 方块移动: {len(move_logs)}次") print(f" 错误日志: {len(error_logs)}条") if spawn_logs: print(f" 最后生成: {spawn_logs[-1]['text'][:80]}...") if move_logs: recent_moves = move_logs[-3:] if len(move_logs) >= 3 else move_logs print(f" 最近移动:") for i, move in enumerate(recent_moves): print(f" {i+1}. {move['text'][:60]}...") # 测试3: 键盘控制测试 print("\n🧪 测试3: 键盘控制测试") try: # 测试左移 await page.keyboard.press("ArrowLeft") await page.wait_for_timeout(200) print("✅ 左箭头键测试") # 测试右移 await page.keyboard.press("ArrowRight") await page.wait_for_timeout(200) print("✅ 右箭头键测试") # 测试下移 await page.keyboard.press("ArrowDown") await page.wait_for_timeout(200) print("✅ 下箭头键测试") # 测试旋转 await page.keyboard.press(" ") await page.wait_for_timeout(200) print("✅ 空格键旋转测试") results["metrics"]["keyboard_tested"] = True except Exception as e: results["issues"].append(f"键盘控制测试失败: {str(e)}") print(f"⚠️ 键盘控制测试失败: {e}") # 测试4: 截图和性能检查 print("\n🧪 测试4: 截图和性能检查") # 游戏运行截图 screenshot_path = OUTPUT_DIR / f"{version['type']}_{version['name'].replace(' ', '_')}_{test_id}.png" await page.screenshot(path=str(screenshot_path), full_page=True) results["screenshots"].append(str(screenshot_path)) print(f"📸 截图保存: {screenshot_path}") # 检查游戏状态 game_running = len(move_logs) > 0 results["metrics"]["game_running"] = game_running results["metrics"]["game_working"] = len(spawn_logs) > 0 and len(move_logs) > 0 # 评估游戏状态 if len(spawn_logs) > 0 and len(move_logs) > 0: status = "🎉 完美工作" score = 100 elif len(spawn_logs) > 0 and len(move_logs) == 0: status = "⚠️ 方块生成但不下落" score = 60 results["issues"].append("方块生成但不下落") elif len(spawn_logs) == 0: status = "❌ 方块未生成" score = 30 results["issues"].append("方块未生成") else: status = "❓ 未知状态" score = 50 results["metrics"]["status"] = status results["metrics"]["score"] = score print(f"\n📈 测试结果: {status}") print(f"🏆 评分: {score}/100") if error_logs: print(f"\n⚠️ 发现错误:") for i, error in enumerate(error_logs[:5]): print(f" {i+1}. {error['text'][:100]}...") # 保存详细日志 log_file = OUTPUT_DIR / f"{version['type']}_{version['name'].replace(' ', '_')}_{test_id}_logs.json" with open(log_file, "w", encoding="utf-8") as f: json.dump({ "version": version, "console_logs": console_logs[-50:], # 最后50条日志 "page_errors": page_errors, "results": results }, f, ensure_ascii=False, indent=2) print(f"📄 详细日志保存: {log_file}") except Exception as e: results["issues"].append(f"测试异常: {str(e)}") print(f"💥 测试异常: {e}") results["end_time"] = datetime.now().isoformat() return results async def main(): """主测试函数""" print("🚀 俄罗斯方块游戏深度测试开始") print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"测试版本数: {len(KEY_VERSIONS)}") print(f"输出目录: {OUTPUT_DIR}") all_results = [] async with async_playwright() as p: # 启动浏览器 browser = await p.chromium.launch( headless=True, args=[ "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-accelerated-2d-canvas", "--disable-gpu" ] ) try: for i, version in enumerate(KEY_VERSIONS): print(f"\n{'#'*80}") print(f"测试进度: {i+1}/{len(KEY_VERSIONS)}") # 为每个版本创建新的上下文和页面 context = await browser.new_context( viewport={"width": 1280, "height": 720}, user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" ) page = await context.new_page() try: results = await test_version(page, version, i+1) all_results.append(results) finally: await page.close() await context.close() # 版本间间隔 await asyncio.sleep(1) finally: await browser.close() # 生成测试报告 generate_report(all_results) print(f"\n✅ 所有测试完成!") print(f"📁 测试结果保存在: {OUTPUT_DIR}") def generate_report(results): """生成测试报告""" report_file = OUTPUT_DIR / "tetris_deep_test_report.md" with open(report_file, "w", encoding="utf-8") as f: f.write("# 🎮 俄罗斯方块游戏深度测试报告\n\n") f.write(f"**测试时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"**测试版本**: {len(results)}个\n\n") f.write("## 📊 测试结果汇总\n\n") f.write("| 排名 | 版本名称 | 类型 | 评分 | 状态 | 方块生成 | 方块移动 | 错误数 |\n") f.write("|------|----------|------|------|------|----------|----------|--------|\n") # 按评分排序 sorted_results = sorted(results, key=lambda x: x["metrics"].get("score", 0), reverse=True) for i, result in enumerate(sorted_results): version = result["version"] metrics = result["metrics"] f.write(f"| {i+1} | {version['name']} | {version['type']} | ") f.write(f"{metrics.get('score', 0)}/100 | {metrics.get('status', '未知')} | ") f.write(f"{metrics.get('spawn_logs', 0)} | {metrics.get('move_logs', 0)} | ") f.write(f"{metrics.get('error_logs', 0)} |\n") f.write("\n## 🏆 最佳版本推荐\n\n") # 推荐最佳版本 excellent_versions = [r for r in sorted_results if r["metrics"].get("score", 0) >= 80] good_versions = [r for r in sorted_results if 60 <= r["metrics"].get("score", 0) < 80] problem_versions = [r for r in sorted_results if r["metrics"].get("score", 0) < 60] if excellent_versions: f.write("### 🎉 优秀版本 (评分≥80)\n\n") for result in excellent_versions[:3]: version = result["version"] metrics = result["metrics"] f.write(f"#### {version['name']}\n") f.write(f"- **文件**: `{version['file']}`\n") f.write(f"- **评分**: {metrics.get('score', 0)}/100\n") f.write(f"- **Canvas**: {metrics.get('canvas_size', '未知')}\n") f.write(f"- **游戏表现**: 方块生成{metrics.get('spawn_logs', 0)}次,移动{metrics.get('move_logs', 0)}次\n") f.write(f"- **访问地址**: {BASE_URL}{version['file']}\n") if result.get("issues"): f.write(f"- **问题**: {', '.join(result['issues'])}\n") f.write("\n") if good_versions: f.write("### ✅ 良好版本 (评分60-79)\n\n") for result in good_versions[:3]: version = result["version"] metrics = result["metrics"] f.write(f"- **{version['name']}**: {metrics.get('score', 0)}/100 - {metrics.get('status', '未知')}\n") if problem_versions: f.write("### ⚠️ 需要修复的版本 (评分<60)\n\n") for result in problem_versions: version = result["version"] metrics = result["metrics"] f.write(f"- **{version['name']}**: {metrics.get('score', 0)}/100 - {metrics.get('status', '未知')}\n") if result.get("issues"): f.write(f" - 问题: {', '.join(result['issues'])}\n") f.write("\n## 🔍 技术分析\n\n") # 技术分析 playcanvas_versions = [r for r in results if r["version"]["type"] == "playcanvas"] canvas2d_versions = [r for r in results if r["version"]["type"] == "canvas2d"] f.write("### 技术栈对比\n\n") f.write("| 技术栈 | 测试版本 | 平均评分 | 优点 | 缺点 |\n") f.write("|--------|----------|----------|------|------|\n") if playcanvas_versions: avg_score = sum(r["metrics"].get("score", 0) for r in playcanvas_versions) / len(playcanvas_versions) f.write(f"| PlayCanvas | {len(playcanvas_versions)} | {avg_score:.1f} | 性能好,移动次数多 | 依赖外部库,兼容性问题 |\n") if canvas2d_versions: avg_score = sum(r["metrics"].get("score", 0) for r in canvas2d_versions) / len(canvas2d_versions) f.write(f"| Canvas 2D | {len(canvas2d_versions)} | {avg_score:.1f} | 兼容性好,无依赖 | 性能一般 |\n") f.write("\n### 常见问题\n\n") f.write("1. **方块生成但不下落**: 游戏循环没有正确启动或时间计算错误\n") f.write("2. **键盘控制失效**: 事件监听器没有正确绑定\n") f.write("3. **Canvas渲染问题**: Canvas上下文获取失败或绘图API错误\n") f.write("4. **移动端兼容性**: 触摸事件处理不完整\n") f.write("\n## 🚀 建议\n\n") f.write("1. **立即使用**: PlayCanvas工作版 (性能最佳)\n") f.write("2. **稳定选择**: 最终Canvas 2D版 (兼容性最佳)\n") f.write("3. **需要修复**: 检查游戏循环逻辑,确保方块自动下落\n") f.write("4. **移动端优化**: 确保触摸事件正确处理\n") f.write(f"\n## 📁 测试文件\n\n") f.write(f"- **报告文件**: {report_file}\n") f.write(f"- **截图目录**: {OUTPUT_DIR}/\n") f.write(f"- **日志文件**: {OUTPUT_DIR}/*_logs.json\n") print(f"📄 测试报告生成: {report_file}") if __name__ == "__main__": asyncio.run(main())