#!/usr/bin/env python3 """真实集成烟测:本机启动 Gradio,上传工程/JSON,渲染PNG并验证下载。 运行:BLENDER_BIN=/path/to/blender python scripts/smoke_test.py 默认明确使用 CPU;不声称测试了 ZeroGPU。需要先安装 requirements。 """ from __future__ import annotations import json import os from pathlib import Path import sys import tempfile import zipfile ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) os.environ['RENDER_BACKEND'] = 'cpu' os.environ.setdefault('GRADIO_ANALYTICS_ENABLED', 'False') os.environ['NO_PROXY'] = ','.join(filter(None, [os.environ.get('NO_PROXY', ''), '127.0.0.1', 'localhost'])) def main(): from gradio_client import Client, handle_file from PIL import Image with tempfile.TemporaryDirectory(prefix='blender-space-smoke-') as tmp: temp = Path(tmp) os.environ['RENDER_DATA_DIR'] = str(temp / 'jobs-data') import app import service from blender_runtime import ensure_blender binary = ensure_blender() demo_file = ROOT / 'examples/demo.blend' if not demo_file.is_file(): raise RuntimeError('缺少 examples/demo.blend,请先运行 scripts/create_demo.py。') project = temp / 'project.zip' with zipfile.ZipFile(project, 'w') as archive: archive.write(demo_file, 'scene/demo.blend') config = temp / 'render.json' config.write_text(json.dumps({'blend_file':'scene/demo.blend','mode':'image', 'width':640,'height':360,'samples':16,'batch_size':1}), encoding='utf-8') demo = app.build_app().queue() _, url, _ = demo.launch(server_name='127.0.0.1', server_port=7867, prevent_thread_lock=True, quiet=True, mcp_server=True, allowed_paths=[str(service.PUBLIC_ROOT)], theme=app.gr.themes.Soft(), css=app.CSS) try: client = Client(url, verbose=False, download_files=str(temp / 'downloads'), httpx_kwargs={'trust_env':False}) environment = client.predict(api_name='/environment_info') probe, _, _ = client.predict(api_name='/cpu_self_test') assert probe['ok'] and probe['backend'] == 'CPU' and not probe['gpu_verified'] job_id, prepared = client.predict([handle_file(str(project))], '{}', handle_file(str(config)), api_name='/prepare_job') checked = client.predict(job_id, api_name='/inspect_job') assert checked['status'] == 'ready', checked status = client.predict(job_id, api_name='/render_chunk') assert status['status'] == 'complete' and not status['error'], status summary, files, image, video = client.predict(job_id, api_name='/finalize_job') assert summary['export_complete'] and image and not video, summary with Image.open(image) as rendered: assert rendered.size == (640,360) rendered.verify() archive_file = next(Path(f) for f in files if str(f).endswith('.zip')) with zipfile.ZipFile(archive_file) as result_zip: assert 'frames/frame_000001.png' in result_zip.namelist() assert result_zip.testzip() is None artifacts = ROOT / 'test-artifacts' artifacts.mkdir(exist_ok=True) import shutil shutil.copy2(image, artifacts / 'cpu-render-preview.png') report = {'ok':True,'backend':'CPU','blender':probe['blender_version'], 'api_upload_zip':True,'api_upload_config':True,'api_render_png':True, 'image_dimensions':[640,360],'api_download_zip_verified':True, 'zero_gpu_tested':False,'environment':environment} (artifacts / 'api-smoke-report.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8') print(json.dumps(report,ensure_ascii=False,indent=2)) finally: demo.close() if __name__ == '__main__': main()