test: cover deterministic Ortho planning
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"executionMode": "DryRun",
|
||||||
|
"runtimeExecuted": false,
|
||||||
|
"plantProjectDetected": null,
|
||||||
|
"orthoDetection": "NotExecuted",
|
||||||
|
"viewportCount": null,
|
||||||
|
"dataLinksAvailable": null,
|
||||||
|
"objectsRead": null,
|
||||||
|
"plan": {
|
||||||
|
"create": null,
|
||||||
|
"update": null,
|
||||||
|
"skip": null,
|
||||||
|
"conflict": null,
|
||||||
|
"unsupported": null
|
||||||
|
},
|
||||||
|
"planDeterministic": true,
|
||||||
|
"dbmodBefore": null,
|
||||||
|
"dbmodAfter": null,
|
||||||
|
"dwgModified": null,
|
||||||
|
"dcfModified": null,
|
||||||
|
"projectXmlModified": null,
|
||||||
|
"unhandledExceptions": null,
|
||||||
|
"blocker": "No controlled test Ortho DWG/project path was supplied or discovered; runtime DryRun was not claimed."
|
||||||
|
}
|
||||||
@@ -10,7 +10,9 @@ The first real Plant 3D 2026 build is complete against the workstation assemblie
|
|||||||
| Ribbon | Implemented with `Autodesk.Windows`; idempotent tab/panel guard |
|
| Ribbon | Implemented with `Autodesk.Windows`; idempotent tab/panel guard |
|
||||||
| Adapter | Compiled against real 2026 assemblies |
|
| Adapter | Compiled against real 2026 assemblies |
|
||||||
| Bundle | Local smoke package built and checked for Autodesk DLL exclusion |
|
| Bundle | Local smoke package built and checked for Autodesk DLL exclusion |
|
||||||
| Runtime | Not executed in AutoCAD/Plant process |
|
| Runtime smoke | Manual NETLOAD confirmed on Plant 3D 2026; automatic AppLoader and Ortho DryRun remain unverified |
|
||||||
|
| Ortho read path | Compiled against `OrthoDocUtils`, `OrthoProject`, `DwgSheetDefinitions`, `DwgViewDefs`, and `DataLinksManager`; runtime fixture pending |
|
||||||
|
| Dry-run plan | Deterministic Core builder with DTO boundary and Apply guard; no annotation writes |
|
||||||
| Production deploy | NOT PERFORMED |
|
| Production deploy | NOT PERFORMED |
|
||||||
|
|
||||||
Fixed API assumptions: foundation-only lifecycle became `IExtensionApplication`; command descriptors became actual `CommandMethod` registrations; Plant project detection uses `PlantApplication.CurrentProject`; Ribbon uses `ComponentManager.Ribbon`, `RibbonTab`, `RibbonPanelSource`, and `RibbonButton`. Remaining blockers are runtime loading, Ortho API/data-link mapping, safe DWG transaction implementation, and golden fixture validation.
|
Fixed API assumptions: foundation-only lifecycle became `IExtensionApplication`; command descriptors became actual `CommandMethod` registrations; Ortho detection uses `OrthoDocUtils.IsValidOrthoDrawing` plus the Ortho project part; viewport metadata uses `DwgSheetDefinitions`/`DwgViewDefs`; object properties use read-only `DataLinksManager.SelectAcPpRowIds` and `GetAllProperties`; Core receives only immutable DTOs. Remaining blockers are a controlled Ortho fixture, geometry/object-to-viewport mapping, legacy source provenance, and future Apply implementation.
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# PXP-AAV read-path analysis
|
||||||
|
|
||||||
|
Status: traced to the existing command registration and the shared dry-run plan path; annotation writes are not implemented in this slice.
|
||||||
|
|
||||||
|
| RuleId | LegacyCommand | LegacyProject | LegacyFile | ObservedBehavior | TargetUseCase | TargetClass | Test | Status |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| PXP-AAV-001 | PXP-AAV | legacy audit | `docs/legacy-rule-provenance.csv` | legacy provenance is recorded, but the requested legacy checkout is absent on this workstation | read objects visible to an Ortho view and build a dry-run plan | adapter DTO -> `AnnotationPlan` | `AnnotationPlan_InputOrderDoesNotChangeResult` | partial |
|
||||||
|
|
||||||
|
The implementation deliberately does not claim behavioral equivalence. The next required input is a read-only copy of the legacy source or a golden test Ortho drawing with expected diagnostics.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# PXP-OA read-path analysis
|
||||||
|
|
||||||
|
Status: command is a real `CommandMethod` and uses the same safe context/read-plan pipeline. No Ortho annotation is created, updated, or deleted.
|
||||||
|
|
||||||
|
| RuleId | LegacyCommand | LegacyProject | LegacyFile | ObservedBehavior | TargetUseCase | TargetClass | Test | Status |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| PXP-OA-001 | PXP-OA | legacy audit | `docs/legacy-rule-provenance.csv` | legacy provenance is available, but source checkout was not found locally | Ortho context and DataLinks read diagnostics | adapter DTO -> `AnnotationPlan` | `ApplyMode_IsBlocked` | partial |
|
||||||
|
|
||||||
|
Exact object filters, annotation text, placement rules, and existing-annotation detection remain unconfirmed until the legacy source or a controlled golden fixture is supplied.
|
||||||
@@ -12,3 +12,8 @@ Copy-Item -Recurse -Force .\Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle
|
|||||||
In a controlled Plant 3D 2026 session, verify that the entry assembly loads, the `Промхимпроект` tab and `Аннотации` panel occur once, and the `Аннотировать` button invokes `PHP_REC_ANNOT`. Open a Plant project and active DWG, run all three commands, verify the project guard and diagnostic-only behavior, and confirm no unhandled exception. Repeat initialization and confirm no Ribbon duplicate. DWG mutation, Ortho viewport/data-link behavior, undo, repeat-run idempotence, and golden-DWG comparison remain runtime blockers for the implementation wave.
|
In a controlled Plant 3D 2026 session, verify that the entry assembly loads, the `Промхимпроект` tab and `Аннотации` panel occur once, and the `Аннотировать` button invokes `PHP_REC_ANNOT`. Open a Plant project and active DWG, run all three commands, verify the project guard and diagnostic-only behavior, and confirm no unhandled exception. Repeat initialization and confirm no Ribbon duplicate. DWG mutation, Ortho viewport/data-link behavior, undo, repeat-run idempotence, and golden-DWG comparison remain runtime blockers for the implementation wave.
|
||||||
|
|
||||||
Deploy status: NOT PERFORMED.
|
Deploy status: NOT PERFORMED.
|
||||||
|
# Current slice: Ortho read / dry-run
|
||||||
|
|
||||||
|
`P_REC_ANNOT_RUNTIME_MODE=Smoke` reads context only. `DryRun` reads Ortho view metadata and DataLinks properties, builds an immutable deterministic `AnnotationPlan`, and never applies it. `Apply` is blocked with `APPLY_MODE_NOT_IMPLEMENTED`; unknown values resolve to `DryRun`.
|
||||||
|
|
||||||
|
The read adapter uses `OrthoDocUtils.IsValidOrthoDrawing`, `OrthoProject.DwgSheetDefinitions`, `IDwgViewDef`, and read-only `DataLinksManager` methods. The adapter does not expose AutoCAD or Plant objects to Core and contains no write calls. A runtime DryRun result is not published until a controlled test Ortho DWG is available; see `docs/evidence/ortho-dry-run-summary.json`.
|
||||||
|
|||||||
@@ -86,4 +86,61 @@ public sealed class FoundationTests
|
|||||||
var references = typeof(AnnotationRequest).Assembly.GetReferencedAssemblies();
|
var references = typeof(AnnotationRequest).Assembly.GetReferencedAssemblies();
|
||||||
Assert.DoesNotContain(references, x => x.Name?.StartsWith("Autodesk", StringComparison.OrdinalIgnoreCase) == true || x.Name?.StartsWith("Ac", StringComparison.OrdinalIgnoreCase) == true);
|
Assert.DoesNotContain(references, x => x.Name?.StartsWith("Autodesk", StringComparison.OrdinalIgnoreCase) == true || x.Name?.StartsWith("Ac", StringComparison.OrdinalIgnoreCase) == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact] public void OrthoDetection_NoPlantProject_ReturnsNotOrtho() => Assert.Equal(OrthoDetectionStatus.NotOrtho, OrthoDetectionClassifier.Classify(false, false, false).Status);
|
||||||
|
[Fact] public void OrthoDetection_NormalDwg_ReturnsNotOrtho() => Assert.Equal(OrthoDetectionStatus.NotOrtho, OrthoDetectionClassifier.Classify(true, true, false).Status);
|
||||||
|
[Fact] public void OrthoDetection_ConfirmedOrtho_ReturnsConfirmed() => Assert.Equal(OrthoDetectionStatus.Confirmed, OrthoDetectionClassifier.Classify(true, true, true).Status);
|
||||||
|
[Fact] public void OrthoDetection_AmbiguousMetadata_ReturnsAmbiguous() => Assert.Equal(OrthoDetectionStatus.Ambiguous, OrthoDetectionClassifier.Classify(true, true, false, true).Status);
|
||||||
|
|
||||||
|
[Fact] public void AnnotationPlan_InputOrderDoesNotChangeResult()
|
||||||
|
{
|
||||||
|
var viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" };
|
||||||
|
var objects = new[] { Snapshot("b", "Pipe", "L-2"), Snapshot("a", "Valve", "V-1") };
|
||||||
|
var builder = new AnnotationPlanBuilder();
|
||||||
|
var first = builder.Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = objects });
|
||||||
|
var second = builder.Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = objects.Reverse().ToArray() });
|
||||||
|
Assert.Equal(first.PlanId, second.PlanId);
|
||||||
|
Assert.Equal(first.Summary, second.Summary);
|
||||||
|
Assert.Equal(first.Operations.Select(x => x.SourceSnapshotId), second.Operations.Select(x => x.SourceSnapshotId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void AnnotationPlan_MissingLineNumber_IsSkipped()
|
||||||
|
{
|
||||||
|
var plan = Build(Snapshot("a", "Pipe", null, null));
|
||||||
|
Assert.Equal(AnnotationOperationKind.Skip, Assert.Single(plan.Operations).Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void AnnotationPlan_UnsupportedObject_IsMarkedUnsupported()
|
||||||
|
{
|
||||||
|
var plan = Build(Snapshot("a", "", "L-1"));
|
||||||
|
Assert.Equal(AnnotationOperationKind.Unsupported, Assert.Single(plan.Operations).Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void AnnotationPlan_ExistingAnnotation_BecomesUpdate()
|
||||||
|
{
|
||||||
|
var viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" };
|
||||||
|
var item = Snapshot("a", "Pipe", "L-1");
|
||||||
|
var plan = new AnnotationPlanBuilder().Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = new[] { item }, Existing = new[] { new ExistingAnnotation("a", "old", new(0, 0)) } });
|
||||||
|
Assert.Equal(AnnotationOperationKind.Update, Assert.Single(plan.Operations).Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void AnnotationPlan_DuplicateCandidate_BecomesConflict()
|
||||||
|
{
|
||||||
|
var plan = Build(Snapshot("a", "Pipe", "L-1"), Snapshot("a", "Pipe", "L-1"));
|
||||||
|
Assert.Equal(AnnotationOperationKind.Conflict, plan.Operations.Last().Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void ApplyMode_IsBlocked() => Assert.Throws<InvalidOperationException>(() => new ExecutionModeWriteGuard(PRecAnnotExecutionMode.Apply).EnsureWriteAllowed("test"));
|
||||||
|
|
||||||
|
[Fact] public void DryRunPath_HasNoForWriteOperations()
|
||||||
|
{
|
||||||
|
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "PlantReadPath.cs"));
|
||||||
|
Assert.DoesNotContain("ForWrite", source);
|
||||||
|
Assert.DoesNotContain("AppendEntity", source);
|
||||||
|
Assert.DoesNotContain("AcceptChanges", source);
|
||||||
|
Assert.DoesNotContain("SetProperties", source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnnotationPlan Build(params PlantObjectSnapshot[] objects) => new AnnotationPlanBuilder().Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" }, Objects = objects });
|
||||||
|
private static PlantObjectSnapshot Snapshot(string id, string type, string? line, string? tag = "TAG") => new() { SnapshotId = id, ObjectClass = type, LineNumber = line, Tag = tag, Geometry = new GeometrySnapshot { PaperPosition = new Point2Dto(1, 2) } };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user