Files
nanocode/happy-games/games/tetris/deep_test_playwright.py
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

417 lines
17 KiB
Python

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