Skip to content

Commit 9adb7c9

Browse files
zbucclaude
andcommitted
Add multi-subject E2E test suite with 9 test cases
- Add 9 test subjects (27 images total): bottle, bottle2, car, cube, dog, dudeguy, piss, star, vase - Update test_e2e_validation.py to automatically discover and test all subjects - Export all test results to GitHub Pages for regression tracking - Update .gitignore to include test_images (reference images for E2E tests) Each test case generates IoU metrics for front/side/top views, with results exported to docs/e2e-results/ for visualization on GitHub Pages. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 71f512a commit 9adb7c9

30 files changed

Lines changed: 95 additions & 63 deletions

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ __pycache__/
1919
*.blend2
2020

2121
# Generated test artifacts
22-
test_images/
23-
blender_blocking/test_images/
2422
blender_blocking/test_output/
2523

2624
# OS

blender_blocking/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ env/
1111
ENV/
1212

1313
# Test outputs
14-
test_images/
14+
test_output/
1515

1616
# IDE
1717
.vscode/

blender_blocking/test_e2e_validation.py

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -359,78 +359,112 @@ def test_with_sample_images(
359359
print(result.stderr)
360360
return False
361361

362-
# Use vase test images
363-
reference_paths = {
364-
"front": str(test_images_dir / "vase_front.png"),
365-
"side": str(test_images_dir / "vase_side.png"),
366-
"top": str(test_images_dir / "vase_top.png"),
367-
}
368-
369-
# Run validation with many slices to capture profile details
370-
validator = E2EValidator(
371-
iou_threshold=0.7,
372-
render_config=render_config,
373-
workflow_config=workflow_config,
374-
config_label=config_label,
375-
progress=progress,
376-
)
377-
passed, results = validator.validate_reconstruction(
378-
reference_paths, num_slices=num_slices
379-
)
380-
381-
# Print detailed results
382-
validator.print_detailed_results()
362+
# Discover all test subjects (files ending in _front.png, _side.png, _top.png)
363+
test_subjects = set()
364+
for img_path in test_images_dir.glob("*_front.png"):
365+
subject = img_path.stem.replace("_front", "")
366+
# Check if all three views exist
367+
if (
368+
(test_images_dir / f"{subject}_side.png").exists()
369+
and (test_images_dir / f"{subject}_top.png").exists()
370+
):
371+
test_subjects.add(subject)
372+
373+
test_subjects = sorted(test_subjects)
374+
375+
if not test_subjects:
376+
print("ERROR: No complete test image sets found")
377+
return False
378+
379+
print(f"\nFound {len(test_subjects)} test subjects: {', '.join(test_subjects)}")
380+
print("=" * 60)
383381

384382
# Export results for GitHub Pages
385383
export_dir = base_dir.parent / "docs" / "e2e-results"
386-
try:
387-
exporter = E2EResultsExporter(export_dir)
388-
recon_mode = (
389-
validator.workflow_config.reconstruction.reconstruction_mode
390-
if validator.workflow_config
391-
else "legacy"
392-
)
393-
test_name = f"vase_{recon_mode}_{config_label}"
394-
395-
# Get metadata
396-
cfg = validator.workflow_config
397-
metadata = {
398-
"num_slices": num_slices,
399-
"config_label": config_label,
400-
"reconstruction_mode": recon_mode,
401-
"blender_version": bpy.app.version_string if BLENDER_AVAILABLE else "N/A",
402-
"profile_samples": cfg.profile_sampling.num_samples if cfg else None,
403-
"radial_segments": cfg.mesh_from_profile.radial_segments if cfg else None,
404-
"render_resolution": list(cfg.render_silhouette.resolution) if cfg else None,
384+
exporter = E2EResultsExporter(export_dir)
385+
386+
all_passed = True
387+
test_case_names = []
388+
389+
for subject_idx, subject in enumerate(test_subjects, 1):
390+
print(f"\n[Test {subject_idx}/{len(test_subjects)}] Testing subject: {subject}")
391+
print("-" * 60)
392+
393+
reference_paths = {
394+
"front": str(test_images_dir / f"{subject}_front.png"),
395+
"side": str(test_images_dir / f"{subject}_side.png"),
396+
"top": str(test_images_dir / f"{subject}_top.png"),
405397
}
406398

407-
# Export this test case
408-
exporter.export_test_case(
409-
test_name=test_name,
410-
views=["front", "side", "top"],
411-
reference_paths=reference_paths,
412-
rendered_paths=validator.rendered_paths,
413-
results=results,
414-
metadata=metadata,
399+
# Run validation
400+
validator = E2EValidator(
401+
iou_threshold=0.7,
402+
render_config=render_config,
403+
workflow_config=workflow_config,
404+
config_label=config_label,
405+
progress=progress,
406+
)
407+
passed, results = validator.validate_reconstruction(
408+
reference_paths, num_slices=num_slices
415409
)
416410

417-
# Update index with all test cases
418-
import json
411+
all_passed = all_passed and passed
419412

420-
existing_cases = set()
421-
index_file = export_dir / "index.json"
422-
if index_file.exists():
423-
with open(index_file) as f:
424-
index_data = json.load(f)
425-
existing_cases = set(index_data.get("test_cases", []))
413+
# Print detailed results
414+
validator.print_detailed_results()
426415

427-
existing_cases.add(test_name)
428-
exporter.export_index(sorted(existing_cases))
416+
# Export results for GitHub Pages
417+
try:
418+
recon_mode = (
419+
validator.workflow_config.reconstruction.reconstruction_mode
420+
if validator.workflow_config
421+
else "legacy"
422+
)
423+
test_name = f"{subject}_{recon_mode}_{config_label}"
424+
test_case_names.append(test_name)
425+
426+
# Get metadata
427+
cfg = validator.workflow_config
428+
metadata = {
429+
"subject": subject,
430+
"num_slices": num_slices,
431+
"config_label": config_label,
432+
"reconstruction_mode": recon_mode,
433+
"blender_version": bpy.app.version_string if BLENDER_AVAILABLE else "N/A",
434+
"profile_samples": cfg.profile_sampling.num_samples if cfg else None,
435+
"radial_segments": cfg.mesh_from_profile.radial_segments if cfg else None,
436+
"render_resolution": list(cfg.render_silhouette.resolution) if cfg else None,
437+
}
438+
439+
# Export this test case
440+
exporter.export_test_case(
441+
test_name=test_name,
442+
views=["front", "side", "top"],
443+
reference_paths=reference_paths,
444+
rendered_paths=validator.rendered_paths,
445+
results=results,
446+
metadata=metadata,
447+
)
448+
449+
except Exception as e:
450+
print(f"Warning: Failed to export results for {subject}: {e}")
429451

452+
# Update index with all test cases
453+
try:
454+
exporter.export_index(sorted(test_case_names))
455+
print(f"\n✓ Exported {len(test_case_names)} test cases to GitHub Pages")
430456
except Exception as e:
431-
print(f"Warning: Failed to export results for GitHub Pages: {e}")
457+
print(f"Warning: Failed to export index for GitHub Pages: {e}")
432458

433-
return passed
459+
# Print overall summary
460+
print("\n" + "=" * 60)
461+
print("OVERALL TEST SUMMARY")
462+
print("=" * 60)
463+
print(f"Total subjects tested: {len(test_subjects)}")
464+
print(f"Result: {'✓ ALL PASSED' if all_passed else '✗ SOME FAILED'}")
465+
print("=" * 60)
466+
467+
return all_passed
434468

435469

436470
def test_with_custom_images(
4.1 KB
Loading
4.55 KB
Loading
6.66 KB
Loading
1.91 KB
Loading
1.91 KB
Loading
1.8 KB
Loading
3.02 KB
Loading

0 commit comments

Comments
 (0)