test(vtags): thread-safe CapturingHistoryWriter + drop redundant wait (H5c review follow-up)
This commit is contained in:
+10
-7
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using Akka.Actor;
|
using Akka.Actor;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -191,7 +192,8 @@ public sealed class VirtualTagHostActorTests : RuntimeActorTestBase
|
|||||||
|
|
||||||
// Wait for the history record to land (delivered on the same actor turn as the publish).
|
// Wait for the history record to land (delivered on the same actor turn as the publish).
|
||||||
AwaitAssert(() => writer.Calls.Count.ShouldBe(1));
|
AwaitAssert(() => writer.Calls.Count.ShouldBe(1));
|
||||||
var (path, value) = writer.Calls[0];
|
writer.Calls.TryPeek(out var captured).ShouldBeTrue();
|
||||||
|
var (path, value) = captured;
|
||||||
path.ShouldBe("eq-1/speed-rpm");
|
path.ShouldBe("eq-1/speed-rpm");
|
||||||
value.Value.ShouldBe(42.0);
|
value.Value.ShouldBe(42.0);
|
||||||
value.StatusCode.ShouldBe(0u); // OPC UA Good
|
value.StatusCode.ShouldBe(0u); // OPC UA Good
|
||||||
@@ -213,11 +215,10 @@ public sealed class VirtualTagHostActorTests : RuntimeActorTestBase
|
|||||||
|
|
||||||
host.Tell(new VirtualTagActor.EvaluationResult("vt-1", 42.0, DateTime.UtcNow, CorrelationId.NewId()));
|
host.Tell(new VirtualTagActor.EvaluationResult("vt-1", 42.0, DateTime.UtcNow, CorrelationId.NewId()));
|
||||||
|
|
||||||
// The value is still published…
|
// The value is still published — and the publish completing means OnResult's turn is done,
|
||||||
|
// so the historian (which would have been called on the same turn) was never touched.
|
||||||
publish.ExpectMsg<OpcUaPublishActor.AttributeValueUpdate>();
|
publish.ExpectMsg<OpcUaPublishActor.AttributeValueUpdate>();
|
||||||
// …but the historian was never touched.
|
writer.Calls.IsEmpty.ShouldBeTrue();
|
||||||
publish.ExpectNoMsg(TimeSpan.FromMilliseconds(200));
|
|
||||||
writer.Calls.ShouldBeEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A plan with an explicit Expression + DependencyRefs (the H1b in-place-change case).</summary>
|
/// <summary>A plan with an explicit Expression + DependencyRefs (the H1b in-place-change case).</summary>
|
||||||
@@ -348,12 +349,14 @@ public sealed class VirtualTagHostActorTests : RuntimeActorTestBase
|
|||||||
/// assert the host historizes (or does not) and with what path + snapshot.</summary>
|
/// assert the host historizes (or does not) and with what path + snapshot.</summary>
|
||||||
private sealed class CapturingHistoryWriter : IHistoryWriter
|
private sealed class CapturingHistoryWriter : IHistoryWriter
|
||||||
{
|
{
|
||||||
public readonly List<(string Path, DataValueSnapshot Value)> Calls = new();
|
// ConcurrentQueue: Record runs on the actor thread, the test asserts on the test thread —
|
||||||
|
// a plain List would be a cross-thread data race under load.
|
||||||
|
public readonly ConcurrentQueue<(string Path, DataValueSnapshot Value)> Calls = new();
|
||||||
|
|
||||||
/// <summary>Captures the path + snapshot of a Record call.</summary>
|
/// <summary>Captures the path + snapshot of a Record call.</summary>
|
||||||
/// <param name="path">The virtual tag path.</param>
|
/// <param name="path">The virtual tag path.</param>
|
||||||
/// <param name="value">The data value snapshot.</param>
|
/// <param name="value">The data value snapshot.</param>
|
||||||
public void Record(string path, DataValueSnapshot value) => Calls.Add((path, value));
|
public void Record(string path, DataValueSnapshot value) => Calls.Enqueue((path, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Deterministic no-op evaluator: keeps spawned children inert so tests drive the host's
|
/// <summary>Deterministic no-op evaluator: keeps spawned children inert so tests drive the host's
|
||||||
|
|||||||
Reference in New Issue
Block a user