feat: bind controlled evidence to target command
This commit is contained in:
@@ -14,6 +14,7 @@ namespace PRecAnnot.Plant3D2026;
|
|||||||
internal sealed record FirstApplyAutomationConfiguration
|
internal sealed record FirstApplyAutomationConfiguration
|
||||||
{
|
{
|
||||||
public required string Phase { get; init; }
|
public required string Phase { get; init; }
|
||||||
|
public string? TargetCommand { get; init; }
|
||||||
public string? OrthoDrawingPath { get; init; }
|
public string? OrthoDrawingPath { get; init; }
|
||||||
public string? ModelDrawingPath { get; init; }
|
public string? ModelDrawingPath { get; init; }
|
||||||
public required string EvidencePath { get; init; }
|
public required string EvidencePath { get; init; }
|
||||||
@@ -85,6 +86,7 @@ internal sealed record FirstApplyAutomationEvidence
|
|||||||
{
|
{
|
||||||
public int SchemaVersion { get; init; } = 1;
|
public int SchemaVersion { get; init; } = 1;
|
||||||
public required string Phase { get; init; }
|
public required string Phase { get; init; }
|
||||||
|
public string? TargetCommand { get; init; }
|
||||||
public bool Completed { get; init; }
|
public bool Completed { get; init; }
|
||||||
public bool? Prepared { get; init; }
|
public bool? Prepared { get; init; }
|
||||||
public bool? FunctionalEvidenceCompleted { get; init; }
|
public bool? FunctionalEvidenceCompleted { get; init; }
|
||||||
|
|||||||
+17
-17
@@ -43,8 +43,8 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
if (terminal) return;
|
if (terminal) return;
|
||||||
config = ReadConfig();
|
config = ReadConfig();
|
||||||
RequireGate(config);
|
RequireGate(config);
|
||||||
discovery = DiscoverPxpOa(config);
|
discovery = DiscoverTargetCommand(config);
|
||||||
RequireDiscovery(discovery);
|
RequireDiscovery(discovery, config.TargetCommand);
|
||||||
var active = AcadApplication.DocumentManager.MdiActiveDocument ?? throw new InvalidOperationException("AUTOMATION_ACTIVE_DOCUMENT_MISSING");
|
var active = AcadApplication.DocumentManager.MdiActiveDocument ?? throw new InvalidOperationException("AUTOMATION_ACTIVE_DOCUMENT_MISSING");
|
||||||
if (!SamePath(active.Name, config.OrthoDrawingPath)) throw new InvalidOperationException("AUTOMATION_UNEXPECTED_ACTIVE_FIXTURE");
|
if (!SamePath(active.Name, config.OrthoDrawingPath)) throw new InvalidOperationException("AUTOMATION_UNEXPECTED_ACTIVE_FIXTURE");
|
||||||
fixture = active;
|
fixture = active;
|
||||||
@@ -128,9 +128,9 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!prepared) throw new InvalidOperationException("AUTOMATION_NOT_PREPARED");
|
if (!prepared) throw new InvalidOperationException("AUTOMATION_NOT_PREPARED");
|
||||||
if (runs.Count != 2) throw new InvalidOperationException("AUTOMATION_EXPECTS_EXACTLY_TWO_PXP_OA");
|
if (runs.Count != 2) throw new InvalidOperationException("AUTOMATION_EXPECTS_EXACTLY_TWO_TARGET_COMMAND_RUNS");
|
||||||
if (runs.Any(x => !x.WillStart || !x.Ended || x.Failed || x.Cancelled)) throw new InvalidOperationException("AUTOMATION_PXP_OA_NOT_ENDED");
|
if (runs.Any(x => !x.WillStart || !x.Ended || x.Failed || x.Cancelled)) throw new InvalidOperationException("AUTOMATION_TARGET_COMMAND_NOT_ENDED");
|
||||||
if (runs.Any(x => string.IsNullOrWhiteSpace(x.PlanId))) throw new InvalidOperationException("PXP_OA_PLAN_RESULT_NOT_CAPTURED");
|
if (runs.Any(x => string.IsNullOrWhiteSpace(x.PlanId))) throw new InvalidOperationException("AUTOMATION_TARGET_PLAN_RESULT_NOT_CAPTURED");
|
||||||
if (!string.Equals(runs[0].PlanId, runs[1].PlanId, StringComparison.Ordinal)) throw new InvalidOperationException("AUTOMATION_PLAN_IDS_DIFFER");
|
if (!string.Equals(runs[0].PlanId, runs[1].PlanId, StringComparison.Ordinal)) throw new InvalidOperationException("AUTOMATION_PLAN_IDS_DIFFER");
|
||||||
var apply = string.Equals(config!.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
var apply = string.Equals(config!.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
||||||
if ((!apply && runs.Any(x => x.Create != 1)) || (apply && (runs[0].Create != 1 || runs[1].Create != 0))) throw new InvalidOperationException("AUTOMATION_CREATE_COUNTS_INVALID");
|
if ((!apply && runs.Any(x => x.Create != 1)) || (apply && (runs[0].Create != 1 || runs[1].Create != 0))) throw new InvalidOperationException("AUTOMATION_CREATE_COUNTS_INVALID");
|
||||||
@@ -161,7 +161,7 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
correlations.Add(correlation);
|
correlations.Add(correlation);
|
||||||
if (correlation.RejectionReason is not null) return;
|
if (correlation.RejectionReason is not null) return;
|
||||||
runs.Add(new Run(runs.Count + 1, null, [], null, 0, 0, null, null, Dbmod(), null, Counts(database!), null, true, false, false, false, null, null));
|
runs.Add(new Run(runs.Count + 1, null, [], null, 0, 0, null, null, Dbmod(), null, Counts(database!), null, true, false, false, false, null, null));
|
||||||
transitions.Add("PxpOaWillStart:" + runs.Count);
|
transitions.Add("TargetCommandWillStart:" + runs.Count);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private static void OnEnded(object sender, CommandEventArgs args) => Finish(sender, args, false, false);
|
private static void OnEnded(object sender, CommandEventArgs args) => Finish(sender, args, false, false);
|
||||||
@@ -179,7 +179,7 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
var last = runs[^1];
|
var last = runs[^1];
|
||||||
if (last.Ended || last.Failed || last.Cancelled) return;
|
if (last.Ended || last.Failed || last.Cancelled) return;
|
||||||
runs[^1] = last with { Ended = !failed && !cancelled, Failed = failed, Cancelled = cancelled, DbmodAfter = Dbmod(), After = Counts(database!) };
|
runs[^1] = last with { Ended = !failed && !cancelled, Failed = failed, Cancelled = cancelled, DbmodAfter = Dbmod(), After = Counts(database!) };
|
||||||
transitions.Add(failed ? "PxpOaFailed" : cancelled ? "PxpOaCancelled" : "PxpOaEnded:" + last.Sequence);
|
transitions.Add(failed ? "TargetCommandFailed" : cancelled ? "TargetCommandCancelled" : "TargetCommandEnded:" + last.Sequence);
|
||||||
Write(false, null);
|
Write(false, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -235,18 +235,18 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
var first = runs.ElementAtOrDefault(0); var second = runs.ElementAtOrDefault(1);
|
var first = runs.ElementAtOrDefault(0); var second = runs.ElementAtOrDefault(1);
|
||||||
var apply = string.Equals(config.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
var apply = string.Equals(config.Phase, "Apply", StringComparison.OrdinalIgnoreCase);
|
||||||
var afterUndo = apply && terminal ? Counts(database!) : null;
|
var afterUndo = apply && terminal ? Counts(database!) : null;
|
||||||
var evidence = new FirstApplyAutomationEvidence { Phase = config.Phase, Completed = completed, Prepared = prepared, FunctionalEvidenceCompleted = completed, ErrorCode = error, DbmodBefore = dbmodBaseline, DbmodAfterCommand1 = first?.DbmodAfter, DbmodAfterCommand2 = second?.DbmodAfter, DbmodAfterUndo = apply && terminal ? Dbmod() : null, Baseline = baseline, AfterCommand1 = first?.After, AfterCommand2 = second?.After, AfterUndo = afterUndo, CompletedCommands = runs.Where(x => x.Ended).Select(x => "PXP-OA").Concat(apply && commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase)) ? ["UNDO"] : []).ToArray(), PlanIdRun1 = first?.PlanId, PlanIdRun2 = second?.PlanId, PlanIdsEqual = first is not null && second is not null ? string.Equals(first.PlanId, second.PlanId, StringComparison.Ordinal) : null, EligibleCandidateCount = first?.Candidates.Count, SelectedCandidateHash = first?.Candidates.OrderBy(x => x, StringComparer.Ordinal).FirstOrDefault(), ViewHash = first?.View, CreateRun1 = first?.Create, CreateRun2 = second?.Create, SkipRun2 = second?.Skip, ApplyOutcomeRun1 = first?.ApplyOutcome, ApplyOutcomeRun2 = second?.ApplyOutcome, PermitAccepted = first?.PermitAccepted, CommandsCompleted = runs.Count == 2 && runs.All(x => x.Ended) && (!apply || commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase))), InspectorsCompleted = baseline is not null && first?.After is not null && second?.After is not null && (!apply || afterUndo is not null), EvidenceFlushed = true, ExceptionText = applyExceptionText, CurrentState = terminal ? (completed ? "Completed" : "Failed") : "Prepared", StateTransitions = transitions, PxpOaDiscovery = discovery, Sentinels = sentinels, CommandEvents = commandEvents, CommandCorrelations = correlations };
|
var evidence = new FirstApplyAutomationEvidence { Phase = config.Phase, TargetCommand = config.TargetCommand, Completed = completed, Prepared = prepared, FunctionalEvidenceCompleted = completed, ErrorCode = error, DbmodBefore = dbmodBaseline, DbmodAfterCommand1 = first?.DbmodAfter, DbmodAfterCommand2 = second?.DbmodAfter, DbmodAfterUndo = apply && terminal ? Dbmod() : null, Baseline = baseline, AfterCommand1 = first?.After, AfterCommand2 = second?.After, AfterUndo = afterUndo, CompletedCommands = runs.Where(x => x.Ended).Select(x => config.TargetCommand).Concat(apply && commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase)) ? ["UNDO"] : []).ToArray(), PlanIdRun1 = first?.PlanId, PlanIdRun2 = second?.PlanId, PlanIdsEqual = first is not null && second is not null ? string.Equals(first.PlanId, second.PlanId, StringComparison.Ordinal) : null, EligibleCandidateCount = first?.Candidates.Count, SelectedCandidateHash = first?.Candidates.OrderBy(x => x, StringComparer.Ordinal).FirstOrDefault(), ViewHash = first?.View, CreateRun1 = first?.Create, CreateRun2 = second?.Create, SkipRun2 = second?.Skip, ApplyOutcomeRun1 = first?.ApplyOutcome, ApplyOutcomeRun2 = second?.ApplyOutcome, PermitAccepted = first?.PermitAccepted, CommandsCompleted = runs.Count == 2 && runs.All(x => x.Ended) && (!apply || commandEvents.Any(x => string.Equals(x, "Ended:UNDO", StringComparison.OrdinalIgnoreCase))), InspectorsCompleted = baseline is not null && first?.After is not null && second?.After is not null && (!apply || afterUndo is not null), EvidenceFlushed = true, ExceptionText = applyExceptionText, CurrentState = terminal ? (completed ? "Completed" : "Failed") : "Prepared", StateTransitions = transitions, PxpOaDiscovery = discovery, Sentinels = sentinels, CommandEvents = commandEvents, CommandCorrelations = correlations };
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(config.EvidencePath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(config.EvidencePath)!);
|
||||||
var temp = config.EvidencePath + ".tmp"; File.WriteAllText(temp, JsonSerializer.Serialize(evidence)); File.Move(temp, config.EvidencePath, true);
|
var temp = config.EvidencePath + ".tmp"; File.WriteAllText(temp, JsonSerializer.Serialize(evidence)); File.Move(temp, config.EvidencePath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PxpOaDiscoveryEvidence DiscoverPxpOa(Config value)
|
private static PxpOaDiscoveryEvidence DiscoverTargetCommand(Config value)
|
||||||
{
|
{
|
||||||
var entry = typeof(EntryPoint).Assembly;
|
var entry = typeof(EntryPoint).Assembly;
|
||||||
var loaded = AppDomain.CurrentDomain.GetAssemblies().Any(x => ReferenceEquals(x, entry));
|
var loaded = AppDomain.CurrentDomain.GetAssemblies().Any(x => ReferenceEquals(x, entry));
|
||||||
var location = entry.Location;
|
var location = entry.Location;
|
||||||
var hash = File.Exists(location) ? Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(location))) : null;
|
var hash = File.Exists(location) ? Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(location))) : null;
|
||||||
var declarations = AppDomain.CurrentDomain.GetAssemblies().SelectMany(Declarations).Where(x => string.Equals(x.Name, "PXP-OA", StringComparison.OrdinalIgnoreCase)).ToArray();
|
var declarations = AppDomain.CurrentDomain.GetAssemblies().SelectMany(Declarations).Where(x => string.Equals(x.Name, value.TargetCommand, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||||
var owner = declarations.SingleOrDefault(x => x.Assembly == entry);
|
var owner = declarations.SingleOrDefault(x => x.Assembly == entry);
|
||||||
var commandClass = entry.GetCustomAttributesData().Any(x => string.Equals(x.AttributeType.FullName, "Autodesk.AutoCAD.Runtime.CommandClassAttribute", StringComparison.Ordinal) && x.ConstructorArguments.Any(a => string.Equals(a.Value as Type, typeof(EntryPoint))));
|
var commandClass = entry.GetCustomAttributesData().Any(x => string.Equals(x.AttributeType.FullName, "Autodesk.AutoCAD.Runtime.CommandClassAttribute", StringComparison.Ordinal) && x.ConstructorArguments.Any(a => string.Equals(a.Value as Type, typeof(EntryPoint))));
|
||||||
return new(owner is not null, owner?.Name, owner?.Group, owner?.Flags, owner?.Type.FullName, owner?.Assembly.GetName().Name, location, hash, loaded, commandClass, hash is not null && string.Equals(hash, value.ExpectedEntryAssemblySha256, StringComparison.OrdinalIgnoreCase), declarations.Length);
|
return new(owner is not null, owner?.Name, owner?.Group, owner?.Flags, owner?.Type.FullName, owner?.Assembly.GetName().Name, location, hash, loaded, commandClass, hash is not null && string.Equals(hash, value.ExpectedEntryAssemblySha256, StringComparison.OrdinalIgnoreCase), declarations.Length);
|
||||||
@@ -264,13 +264,13 @@ internal static class ScriptDrivenFirstApplyAutomation
|
|||||||
yield return new(name, group, flags, type, assembly);
|
yield return new(name, group, flags, type, assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static void RequireDiscovery(PxpOaDiscoveryEvidence value)
|
private static void RequireDiscovery(PxpOaDiscoveryEvidence value, string targetCommand)
|
||||||
{
|
{
|
||||||
if (!value.AssemblyLoaded) throw new InvalidOperationException("PXP_OA_ASSEMBLY_NOT_LOADED");
|
if (!value.AssemblyLoaded) throw new InvalidOperationException("AUTOMATION_TARGET_ASSEMBLY_NOT_LOADED");
|
||||||
if (!value.BundleHashMatched) throw new InvalidOperationException("PXP_OA_BUNDLE_HASH_MISMATCH");
|
if (!value.BundleHashMatched) throw new InvalidOperationException("AUTOMATION_TARGET_BUNDLE_HASH_MISMATCH");
|
||||||
if (!value.CommandClassIncluded) throw new InvalidOperationException("PXP_OA_COMMANDCLASS_NOT_REGISTERED");
|
if (!value.CommandClassIncluded) throw new InvalidOperationException("AUTOMATION_TARGET_COMMANDCLASS_NOT_REGISTERED");
|
||||||
if (value.DeclarationCount > 1) throw new InvalidOperationException("PXP_OA_COMMAND_COLLISION");
|
if (value.DeclarationCount > 1) throw new InvalidOperationException("AUTOMATION_TARGET_COMMAND_COLLISION");
|
||||||
if (value.DeclarationCount == 0 || !value.Found) throw new InvalidOperationException("PXP_OA_COMMAND_NOT_FOUND");
|
if (value.DeclarationCount == 0 || !value.Found) throw new InvalidOperationException("AUTOMATION_TARGET_COMMAND_NOT_FOUND");
|
||||||
if (!string.Equals(value.GlobalName, "PXP-OA", StringComparison.Ordinal)) throw new InvalidOperationException("PXP_OA_GLOBAL_NAME_MISMATCH");
|
if (!string.Equals(value.GlobalName, targetCommand, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("AUTOMATION_TARGET_GLOBAL_NAME_MISMATCH");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ public sealed class PxpOaRegistrationTests
|
|||||||
[Fact] public void PxpOaDiscovery_RecordsCommandGroup() => Assert.Contains("GroupName", Observer);
|
[Fact] public void PxpOaDiscovery_RecordsCommandGroup() => Assert.Contains("GroupName", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RecordsFlags() => Assert.Contains("Flags", Observer);
|
[Fact] public void PxpOaDiscovery_RecordsFlags() => Assert.Contains("Flags", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RecordsDeclaringAssembly() => Assert.Contains("owner?.Assembly", Observer);
|
[Fact] public void PxpOaDiscovery_RecordsDeclaringAssembly() => Assert.Contains("owner?.Assembly", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RequiresLoadedAssembly() => Assert.Contains("PXP_OA_ASSEMBLY_NOT_LOADED", Observer);
|
[Fact] public void TargetDiscovery_RequiresLoadedAssembly() => Assert.Contains("AUTOMATION_TARGET_ASSEMBLY_NOT_LOADED", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RequiresCommandClass() => Assert.Contains("PXP_OA_COMMANDCLASS_NOT_REGISTERED", Observer);
|
[Fact] public void TargetDiscovery_RequiresCommandClass() => Assert.Contains("AUTOMATION_TARGET_COMMANDCLASS_NOT_REGISTERED", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RejectsCollision() => Assert.Contains("PXP_OA_COMMAND_COLLISION", Observer);
|
[Fact] public void TargetDiscovery_RejectsCollision() => Assert.Contains("AUTOMATION_TARGET_COMMAND_COLLISION", Observer);
|
||||||
[Fact] public void PxpOaDiscovery_RejectsBundleHashMismatch() => Assert.Contains("PXP_OA_BUNDLE_HASH_MISMATCH", Observer);
|
[Fact] public void TargetDiscovery_RejectsBundleHashMismatch() => Assert.Contains("AUTOMATION_TARGET_BUNDLE_HASH_MISMATCH", Observer);
|
||||||
[Fact] public void Prepare_StopsOnPxpOaDiscoveryFailure() => Assert.True(Observer.IndexOf("RequireDiscovery(discovery)", StringComparison.Ordinal) < Observer.IndexOf("PlantProject.LoadProject", StringComparison.Ordinal));
|
[Fact] public void Prepare_StopsOnPxpOaDiscoveryFailure() => Assert.True(Observer.IndexOf("RequireDiscovery(discovery)", StringComparison.Ordinal) < Observer.IndexOf("PlantProject.LoadProject", StringComparison.Ordinal));
|
||||||
[Fact] public void AutomationSentinel_DoesNotModifyDatabase() => Assert.DoesNotContain("StartTransaction", Entry[Entry.IndexOf("AutomationBeforePxp1Command", StringComparison.Ordinal)..Entry.IndexOf("private static void RunDiagnostic", StringComparison.Ordinal)]);
|
[Fact] public void AutomationSentinel_DoesNotModifyDatabase() => Assert.DoesNotContain("StartTransaction", Entry[Entry.IndexOf("AutomationBeforePxp1Command", StringComparison.Ordinal)..Entry.IndexOf("private static void RunDiagnostic", StringComparison.Ordinal)]);
|
||||||
[Fact] public void AutomationSentinel_IsTestOnly() => Assert.Contains("RequireGate", Observer);
|
[Fact] public void AutomationSentinel_IsTestOnly() => Assert.Contains("RequireGate", Observer);
|
||||||
@@ -32,7 +32,8 @@ public sealed class PxpOaRegistrationTests
|
|||||||
[Fact] public void Observer_RecordsEveryRejectionReason() => Assert.Contains("DOCUMENT_IDENTITY_MISMATCH", Observer);
|
[Fact] public void Observer_RecordsEveryRejectionReason() => Assert.Contains("DOCUMENT_IDENTITY_MISMATCH", Observer);
|
||||||
[Fact] public void Observer_DoesNotUseManagedDocumentReferenceAsAcceptance() => Assert.DoesNotContain("ReferenceEquals(sender, fixture)", Observer);
|
[Fact] public void Observer_DoesNotUseManagedDocumentReferenceAsAcceptance() => Assert.DoesNotContain("ReferenceEquals(sender, fixture)", Observer);
|
||||||
[Fact] public void Observer_RecordsExactlyTwoSequentialRuns() => Assert.Contains("runs.Count < 2", Observer);
|
[Fact] public void Observer_RecordsExactlyTwoSequentialRuns() => Assert.Contains("runs.Count < 2", Observer);
|
||||||
[Fact] public void Finalize_ReportsMissingPlanSeparatelyFromMissingRuns() => Assert.True(Observer.IndexOf("AUTOMATION_EXPECTS_EXACTLY_TWO_PXP_OA", StringComparison.Ordinal) < Observer.IndexOf("AUTOMATION_PLAN_IDS_DIFFER", StringComparison.Ordinal));
|
[Fact] public void Finalize_ReportsMissingPlanSeparatelyFromMissingRuns() => Assert.True(Observer.IndexOf("AUTOMATION_EXPECTS_EXACTLY_TWO_TARGET_COMMAND_RUNS", StringComparison.Ordinal) < Observer.IndexOf("AUTOMATION_PLAN_IDS_DIFFER", StringComparison.Ordinal));
|
||||||
|
[Fact] public void Observer_BindsDiscoveryAndEvidenceToConfiguredTarget() { Assert.Contains("DiscoverTargetCommand", Observer); Assert.Contains("value.TargetCommand", Observer); Assert.Contains("TargetCommand = config.TargetCommand", Observer); }
|
||||||
[Fact] public void ScriptDrivenPxpOa_BindsPlantContextToCommandDocument() => Assert.Contains("ReadCurrentContext(document)", Entry);
|
[Fact] public void ScriptDrivenPxpOa_BindsPlantContextToCommandDocument() => Assert.Contains("ReadCurrentContext(document)", Entry);
|
||||||
[Fact] public void ScriptDrivenPxpOa_UsesPreparedFixtureContextBeforeImplicitLookup() => Assert.Contains("TryGetPreparedContext(document, out var scriptContext)", Entry);
|
[Fact] public void ScriptDrivenPxpOa_UsesPreparedFixtureContextBeforeImplicitLookup() => Assert.Contains("TryGetPreparedContext(document, out var scriptContext)", Entry);
|
||||||
[Fact] public void ScriptDrivenPrepare_SelectsCanonicalTargetAndRetainsReadOnlyContext() { Assert.Contains("preparedContext = context", Observer); Assert.Contains("selectedTargetSnapshotId = candidates[0]", Observer); }
|
[Fact] public void ScriptDrivenPrepare_SelectsCanonicalTargetAndRetainsReadOnlyContext() { Assert.Contains("preparedContext = context", Observer); Assert.Contains("selectedTargetSnapshotId = candidates[0]", Observer); }
|
||||||
|
|||||||
Reference in New Issue
Block a user