r"""Smoke test for QET pending-device scene persistence. Run with FreeCADCmd.exe, not system Python: D:\fc\run-FreeCAD-1.1.1\bin\FreeCADCmd.exe tests\manual\freecad_pending_device_scene_smoke.py """ import json import os import shutil import sys import tempfile from pathlib import Path import FreeCAD as App REPO_ROOT = Path(__file__).resolve().parents[2] MODULE_DIR = REPO_ROOT / "src" / "Mod" / "FreeCADExchange" if str(MODULE_DIR) not in sys.path: sys.path.insert(0, str(MODULE_DIR)) import DeviceImport # noqa: E402 def _close_doc(doc): if doc is None: return try: App.closeDocument(doc.Name) except Exception: pass def _make_source_model(path): doc = App.newDocument("SmokeSourceModel") try: body = doc.addObject("Part::Box", "Body") body.Length = 10 body.Width = 8 body.Height = 6 doc.recompute() doc.saveAs(str(path)) finally: _close_doc(doc) def _assert_close(actual, expected, label): if abs(float(actual) - float(expected)) > 1e-6: raise AssertionError("{0}: expected {1}, got {2}".format(label, expected, actual)) def main(): temp_dir = Path(tempfile.mkdtemp(prefix="qet_pending_device_smoke_")) try: source_model = temp_dir / "device.FCStd" scene_file = temp_dir / "QETScene.FCStd" _make_source_model(source_model) scene = App.newDocument("QETSceneSmoke") root = DeviceImport._ensure_root_group(scene, None, "project-smoke") device_group, created = DeviceImport._ensure_device_group( scene, root, "element-smoke", "instance-smoke", str(source_model), "N600", 0, ) if not created: raise AssertionError("smoke device group should be newly created") DeviceImport._set_device_assembly_state( device_group, DeviceImport.ASSEMBLY_STATE_PENDING, ) mount_target = scene.addObject("App::Part", "MountingPlate") mount_target.Label = "安装板" mount_target.Placement = App.Placement(App.Vector(100, 200, 300), App.Rotation()) DeviceImport._ensure_string_property( mount_target, "QetCarrierKind", "QET Mount", "Smoke mount target kind", "mounting_plate", ) DeviceImport.insert_pending_device( scene, device_group, mount_target=mount_target, mount_placement=App.Placement(App.Vector(10, 20, 30), App.Rotation()), mount_normal=App.Vector(0, 0, 1), mount_offset_mm=5.0, ) device_group_name = device_group.Name scene.recompute() scene.saveAs(str(scene_file)) _close_doc(scene) reopened = App.openDocument(str(scene_file)) try: reopened_device = reopened.getObject(device_group_name) if reopened_device is None: raise AssertionError("reopened scene does not contain device group") if getattr(reopened_device, "QetAssemblyState", "") != DeviceImport.ASSEMBLY_STATE_PLACED: raise AssertionError("reopened device is not marked Placed") _assert_close(reopened_device.Placement.Base.x, 10.0, "placement x") _assert_close(reopened_device.Placement.Base.y, 20.0, "placement y") _assert_close(reopened_device.Placement.Base.z, 35.0, "placement z") if getattr(reopened_device, "QetMountHostName", "") != "MountingPlate": raise AssertionError("mount host name was not persisted") if getattr(reopened_device, "QetMountHostKind", "") != "mounting_plate": raise AssertionError("mount host kind was not persisted") if getattr(reopened_device, "QetMountOffsetMm", "") != "5.000000": raise AssertionError("mount offset was not persisted") normal_payload = json.loads(getattr(reopened_device, "QetMountHostNormalJson", "{}") or "{}") _assert_close(normal_payload.get("z", 0.0), 1.0, "normal z") finally: _close_doc(reopened) result_path = os.environ.get("QET_PENDING_DEVICE_SMOKE_RESULT", "").strip() if result_path: Path(result_path).write_text( json.dumps( { "ok": True, "scene": str(scene_file), "device": device_group_name, "placement": {"x": 10.0, "y": 20.0, "z": 35.0}, "assembly_state": DeviceImport.ASSEMBLY_STATE_PLACED, "mount_host": "MountingPlate", }, ensure_ascii=False, indent=2, ), encoding="utf-8", ) print("SMOKE_OK scene={0}".format(scene_file)) return 0 finally: if os.environ.get("QET_KEEP_SMOKE_OUTPUT", "").strip() != "1": shutil.rmtree(str(temp_dir), ignore_errors=True) if __name__ == "__main__": raise SystemExit(main())