342 lines
11 KiB
Python
342 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
API测试客户端
|
||
用于测试Isometric Room Generator渲染服务
|
||
"""
|
||
|
||
import requests
|
||
import time
|
||
import json
|
||
import sys
|
||
|
||
|
||
class IRGClient:
|
||
"""Isometric Room Generator API客户端"""
|
||
|
||
def __init__(self, base_url: str = "http://localhost:8003"):
|
||
self.base_url = base_url.rstrip('/')
|
||
|
||
def health_check(self):
|
||
"""健康检查"""
|
||
try:
|
||
response = requests.get(f"{self.base_url}/health", timeout=10)
|
||
return response.json()
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def create_render_task(self, request_data: dict):
|
||
"""创建渲染任务"""
|
||
try:
|
||
response = requests.post(
|
||
f"{self.base_url}/render",
|
||
json=request_data,
|
||
timeout=30
|
||
)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def get_task_status(self, task_id: str):
|
||
"""获取任务状态"""
|
||
try:
|
||
response = requests.get(
|
||
f"{self.base_url}/render/{task_id}", timeout=10)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def download_result(self, task_id: str, output_file: str):
|
||
"""下载渲染结果"""
|
||
try:
|
||
response = requests.get(
|
||
f"{self.base_url}/render/{task_id}/download",
|
||
timeout=60
|
||
)
|
||
response.raise_for_status()
|
||
|
||
with open(output_file, 'wb') as f:
|
||
f.write(response.content)
|
||
return {"success": True, "file": output_file}
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def wait_for_completion(self, task_id: str, max_wait: int = 300):
|
||
"""等待任务完成"""
|
||
start_time = time.time()
|
||
|
||
while time.time() - start_time < max_wait:
|
||
status_result = self.get_task_status(task_id)
|
||
|
||
if "error" in status_result:
|
||
return status_result
|
||
|
||
status = status_result.get("status")
|
||
print(f"任务状态: {status}")
|
||
|
||
if status == "completed":
|
||
return status_result
|
||
elif status == "failed":
|
||
return status_result
|
||
|
||
time.sleep(2)
|
||
|
||
return {"error": "任务超时"}
|
||
|
||
|
||
def test_basic_render():
|
||
"""测试基本渲染功能"""
|
||
print("=" * 60)
|
||
print("测试基本渲染功能")
|
||
print("=" * 60)
|
||
|
||
client = IRGClient()
|
||
|
||
# 1. 健康检查
|
||
print("1. 健康检查...")
|
||
health = client.health_check()
|
||
print(f"健康状态: {json.dumps(health, indent=2, ensure_ascii=False)}")
|
||
|
||
if not health.get("blender_available"):
|
||
print("❌ Blender不可用,请检查安装")
|
||
return
|
||
|
||
# 2. 创建渲染任务
|
||
print("\n2. 创建渲染任务...")
|
||
request_data = {
|
||
"room": {
|
||
"length": 7.0,
|
||
"width": 4.0,
|
||
"height": 3.0,
|
||
"prop_type": 0 # 0=无, 1=落地窗窗, 2=拱门, 3=门"
|
||
},
|
||
"camera": {
|
||
"height": 1.3,
|
||
"view_type": 2, #正视图:1, 侧视图:2
|
||
"rotation_angle": 45.0
|
||
},
|
||
"render": {
|
||
"resolution_x": 1080,
|
||
"resolution_y": 2400,
|
||
"engine": "eevee" # 固定为EEVEE
|
||
}
|
||
}
|
||
|
||
create_result = client.create_render_task(request_data)
|
||
print(f"创建结果: {json.dumps(create_result, indent=2, ensure_ascii=False)}")
|
||
|
||
if "error" in create_result:
|
||
print("❌ 任务创建失败")
|
||
return
|
||
|
||
task_id = create_result.get("task_id")
|
||
if not task_id:
|
||
print("❌ 未获取到任务ID")
|
||
return
|
||
|
||
# 3. 等待任务完成
|
||
print(f"\n3. 等待任务完成 (ID: {task_id})...")
|
||
completion_result = client.wait_for_completion(task_id)
|
||
|
||
if "error" in completion_result:
|
||
print(f"❌ 任务失败: {completion_result['error']}")
|
||
return
|
||
|
||
if completion_result.get("status") == "completed":
|
||
print("✅ 任务完成!")
|
||
|
||
# 4. 下载结果
|
||
print("\n4. 下载渲染结果...")
|
||
output_file = f"test_render_{task_id}.png"
|
||
download_result = client.download_result(task_id, output_file)
|
||
|
||
if "error" in download_result:
|
||
print(f"❌ 下载失败: {download_result['error']}")
|
||
else:
|
||
print(f"✅ 渲染结果已保存到: {output_file}")
|
||
else:
|
||
print(f"❌ 任务失败: {completion_result.get('error')}")
|
||
|
||
|
||
def test_different_views():
|
||
"""测试不同视图"""
|
||
print("=" * 60)
|
||
print("测试不同视图")
|
||
print("=" * 60)
|
||
|
||
client = IRGClient()
|
||
|
||
# 测试正视图和侧视图
|
||
test_cases = [
|
||
{
|
||
"name": "正视图",
|
||
"data": {
|
||
"room": {"length": 4.0, "width": 4.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 1, "rotation_angle": 0.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "侧视图-45度",
|
||
"data": {
|
||
"room": {"length": 4.0, "width": 4.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "侧视图-30度",
|
||
"data": {
|
||
"room": {"length": 4.0, "width": 4.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 30.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
}
|
||
]
|
||
|
||
for i, test_case in enumerate(test_cases, 1):
|
||
print(f"\n{i}. 测试{test_case['name']}...")
|
||
|
||
create_result = client.create_render_task(test_case['data'])
|
||
if "error" in create_result:
|
||
print(f"❌ 创建失败: {create_result['error']}")
|
||
continue
|
||
|
||
task_id = create_result.get("task_id")
|
||
print(f"任务ID: {task_id}")
|
||
|
||
# 快速检查(不等待完成)
|
||
time.sleep(1)
|
||
status = client.get_task_status(task_id)
|
||
print(f"当前状态: {status.get('status', 'unknown')}")
|
||
|
||
|
||
def test_prop_types():
|
||
"""测试不同道具类型"""
|
||
print("=" * 60)
|
||
print("测试不同道具类型")
|
||
print("=" * 60)
|
||
|
||
client = IRGClient()
|
||
|
||
# 测试不同道具类型
|
||
test_cases = [
|
||
{
|
||
"name": "无道具",
|
||
"data": {
|
||
"room": {"length": 5.0, "width": 4.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "落地窗",
|
||
"data": {
|
||
"room": {"length": 5.0, "width": 4.0, "height": 3.0, "prop_type": 1},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "拱门",
|
||
"data": {
|
||
"room": {"length": 5.0, "width": 4.0, "height": 3.0, "prop_type": 2},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "门",
|
||
"data": {
|
||
"room": {"length": 5.0, "width": 4.0, "height": 3.0, "prop_type": 3},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
}
|
||
]
|
||
|
||
for i, test_case in enumerate(test_cases, 1):
|
||
print(f"\n{i}. 测试{test_case['name']}...")
|
||
|
||
create_result = client.create_render_task(test_case['data'])
|
||
if "error" in create_result:
|
||
print(f"❌ 创建失败: {create_result['error']}")
|
||
continue
|
||
|
||
task_id = create_result.get("task_id")
|
||
print(f"任务ID: {task_id}")
|
||
|
||
# 快速检查(不等待完成)
|
||
time.sleep(1)
|
||
status = client.get_task_status(task_id)
|
||
print(f"当前状态: {status.get('status', 'unknown')}")
|
||
|
||
|
||
def test_room_dimensions():
|
||
"""测试房间尺寸调整逻辑"""
|
||
print("=" * 60)
|
||
print("测试房间尺寸调整逻辑")
|
||
print("=" * 60)
|
||
|
||
client = IRGClient()
|
||
|
||
# 测试房间尺寸调整(width > length 的情况)
|
||
test_cases = [
|
||
{
|
||
"name": "正常尺寸 (length >= width)",
|
||
"data": {
|
||
"room": {"length": 6.0, "width": 4.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
},
|
||
{
|
||
"name": "需要调整 (width > length)",
|
||
"data": {
|
||
"room": {"length": 4.0, "width": 6.0, "height": 3.0, "prop_type": 0},
|
||
"camera": {"height": 1.3, "view_type": 2, "rotation_angle": 45.0},
|
||
"render": {"resolution_x": 800, "resolution_y": 600, "engine": "eevee"}
|
||
}
|
||
}
|
||
]
|
||
|
||
for i, test_case in enumerate(test_cases, 1):
|
||
print(f"\n{i}. 测试{test_case['name']}...")
|
||
|
||
create_result = client.create_render_task(test_case['data'])
|
||
if "error" in create_result:
|
||
print(f"❌ 创建失败: {create_result['error']}")
|
||
continue
|
||
|
||
task_id = create_result.get("task_id")
|
||
print(f"任务ID: {task_id}")
|
||
|
||
# 快速检查(不等待完成)
|
||
time.sleep(1)
|
||
status = client.get_task_status(task_id)
|
||
print(f"当前状态: {status.get('status', 'unknown')}")
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
if len(sys.argv) > 1:
|
||
if sys.argv[1] == "views":
|
||
test_different_views()
|
||
elif sys.argv[1] == "props":
|
||
test_prop_types()
|
||
elif sys.argv[1] == "dimensions":
|
||
test_room_dimensions()
|
||
else:
|
||
print("用法: python test_client.py [views|props|dimensions]")
|
||
print(" views - 测试不同视图")
|
||
print(" props - 测试不同道具类型")
|
||
print(" dimensions - 测试房间尺寸调整")
|
||
else:
|
||
test_basic_render()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|