| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- """P4 acceptance test suite.
- Tests all P4 milestones: M1 (AI frontend API), M2 (task dispatch),
- M3 (batch scheduler + monitor), M4 (reports + visualization data),
- M5 (deployment config).
- Run: python scripts/test_p4_acceptance.py
- """
- import json
- import os
- import sys
- import time
- from datetime import datetime
- # Add project root to path
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- PASS = 0
- FAIL = 0
- RESULTS = []
- def test(name, condition, detail=""):
- global PASS, FAIL
- if condition:
- PASS += 1
- RESULTS.append(("PASS", name, detail))
- print(f" [PASS] {name}")
- else:
- FAIL += 1
- RESULTS.append(("FAIL", name, detail))
- print(f" [FAIL] {name} - {detail}")
- def test_m1_ai_api():
- """P4-M1: AI API endpoints and frontend integration."""
- print("\n[P4-M1] AI Frontend Integration")
- try:
- from web.backend.app.routers import ai, ai_plan, analysis, adaptive, search
- test("AI router modules importable", True)
- except Exception as e:
- test("AI router modules importable", False, str(e))
- # Check frontend AI API file
- ai_api_path = os.path.join("web", "frontend", "src", "api", "ai.ts")
- test("Frontend AI API file exists", os.path.exists(ai_api_path))
- # Check AI views
- ai_views = ["PlanGenerator.vue", "L0Prescreen.vue", "AdaptiveOptimize.vue",
- "ResultAnalysis.vue", "FidelityCalibration.vue", "ExperienceEnhance.vue"]
- for view in ai_views:
- path = os.path.join("web", "frontend", "src", "views", "ai", view)
- test(f"AI view {view} exists", os.path.exists(path))
- # Check components
- comps = ["ConfidenceBadge.vue", "FeasibilityIndicator.vue"]
- for comp in comps:
- path = os.path.join("web", "frontend", "src", "components", "ai", comp)
- test(f"AI component {comp} exists", os.path.exists(path))
- def test_m2_task_dispatch():
- """P4-M2: Task management and dispatch."""
- print("\n[P4-M2] Task Dispatch and Callback")
- try:
- from web.backend.app.models.task import Task
- test("Task model importable", True)
- except Exception as e:
- test("Task model importable", False, str(e))
- try:
- from web.backend.app.services.task_manager import get_task_manager
- tm = get_task_manager()
- test("TaskManager service instantiable", tm is not None)
- except Exception as e:
- test("TaskManager service instantiable", False, str(e))
- # Check task router
- router_path = os.path.join("web", "backend", "app", "routers", "tasks.py")
- test("Tasks router exists", os.path.exists(router_path))
- # Check frontend task manager
- tm_path = os.path.join("web", "frontend", "src", "views", "TaskManager.vue")
- test("Frontend TaskManager exists", os.path.exists(tm_path))
- # Check local executor
- exec_path = os.path.join("scripts", "task_executor.py")
- test("Local task executor exists", os.path.exists(exec_path))
- def test_m3_batch_scheduler():
- """P4-M3: Batch scheduler and monitoring."""
- print("\n[P4-M3] Batch Scheduler and Monitoring")
- try:
- from web.backend.app.services.batch_scheduler import BatchScheduler, get_scheduler
- scheduler = get_scheduler()
- test("BatchScheduler instantiable", scheduler is not None)
- # Test add task
- task = scheduler.add_task("test-task-1", "Test Task", priority=5,
- parameters=[{"x": 1}, {"x": 2}])
- test("Add task to scheduler", task["task_id"] == "test-task-1")
- # Test statistics
- stats = scheduler.get_statistics()
- test("Scheduler statistics has queued_count", "queued_count" in stats)
- test("Scheduler statistics has overall_progress", "overall_progress" in stats)
- # Cleanup
- scheduler.cancel_task("test-task-1")
- except Exception as e:
- test("BatchScheduler functional", False, str(e))
- # Check monitor router
- monitor_path = os.path.join("web", "backend", "app", "routers", "monitor.py")
- test("Monitor router exists", os.path.exists(monitor_path))
- # Check frontend monitor
- monitor_vue = os.path.join("web", "frontend", "src", "views", "MonitorDashboard.vue")
- test("Frontend MonitorDashboard exists", os.path.exists(monitor_vue))
- # Check robust motorcad
- robust_path = os.path.join("scripts", "robust_motorcad.py")
- test("Robust MotorCAD core exists", os.path.exists(robust_path))
- def test_m4_visualization_reports():
- """P4-M4: Advanced visualization and reports."""
- print("\n[P4-M4] Visualization and Reports")
- try:
- from web.backend.app.services.report_generator import ReportGenerator, get_report_generator
- rg = get_report_generator()
- test("ReportGenerator instantiable", rg is not None)
- # Test JSON report generation (fallback mode)
- task_data = {"task_id": "test-report", "task_name": "Test",
- "status": "completed", "plan_data": {"x": 1},
- "result_metrics": {"efficiency": 90}}
- report_path = rg.generate_report(task_data)
- test("Report generated (JSON fallback)", os.path.exists(report_path))
- if os.path.exists(report_path):
- os.remove(report_path)
- except Exception as e:
- test("ReportGenerator functional", False, str(e))
- # Check reports router
- reports_path = os.path.join("web", "backend", "app", "routers", "reports.py")
- test("Reports router exists", os.path.exists(reports_path))
- # Check frontend visualization
- viz_path = os.path.join("web", "frontend", "src", "views", "AdvancedVisualization.vue")
- test("Frontend AdvancedVisualization exists", os.path.exists(viz_path))
- def test_m5_deployment():
- """P4-M5: Deployment configuration."""
- print("\n[P4-M5] Deployment and Packaging")
- files = {
- "Dockerfile": os.path.join("Dockerfile"),
- "docker-compose.yml": os.path.join("docker-compose.yml"),
- "nginx.conf": os.path.join("nginx.conf"),
- "deploy.ps1": os.path.join("deploy.ps1"),
- }
- for name, path in files.items():
- test(f"Deployment file {name} exists", os.path.exists(path))
- # Check deploy.ps1 is ASCII only
- deploy_path = files["deploy.ps1"]
- if os.path.exists(deploy_path):
- with open(deploy_path, "r", encoding="utf-8") as f:
- content = f.read()
- non_ascii = [c for c in content if ord(c) > 127]
- test("deploy.ps1 is ASCII-only", len(non_ascii) == 0,
- f"{len(non_ascii)} non-ASCII chars" if non_ascii else "")
- def test_router_registration():
- """Verify all routers are registered in main.py."""
- print("\n[Integration] Router Registration")
- main_path = os.path.join("web", "backend", "app", "main.py")
- if os.path.exists(main_path):
- with open(main_path, "r", encoding="utf-8") as f:
- content = f.read()
- expected_routers = ["tasks", "monitor", "reports"]
- for router in expected_routers:
- test(f"Router '{router}' imported in main.py", f"import {router}" in content or f", {router}" in content)
- test(f"Router '{router}' included in main.py", f"include_router({router}.router)" in content)
- else:
- test("main.py exists", False)
- def main():
- print("=" * 60)
- print("PCB AFM Simulation System - P4 Acceptance Test")
- print(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
- print("=" * 60)
- os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- test_m1_ai_api()
- test_m2_task_dispatch()
- test_m3_batch_scheduler()
- test_m4_visualization_reports()
- test_m5_deployment()
- test_router_registration()
- print("\n" + "=" * 60)
- print(f"RESULTS: {PASS} passed, {FAIL} failed, {PASS + FAIL} total")
- print("=" * 60)
- if FAIL > 0:
- print("\nFailed tests:")
- for status, name, detail in RESULTS:
- if status == "FAIL":
- print(f" - {name}: {detail}")
- sys.exit(1)
- else:
- print("\nAll tests passed!")
- sys.exit(0)
- if __name__ == "__main__":
- main()
|