docs(src): add missing XML docs and strip tracking-ID comments
Sweep of 203 source files resolving CommentChecker findings: add <summary>/<param>/<returns>/<inheritdoc> where missing, and remove resolved task/issue tracking markers (Tests-NNN, Worker-NNN, Server-NNN, Task N) from code comments. Comment/doc-only — no logic changes. Server+Tests build clean under TreatWarningsAsErrors.
This commit is contained in:
@@ -371,28 +371,20 @@ public sealed class AlarmCommandExecutorTests
|
||||
/// <summary>Gets the last alarm filter prefix.</summary>
|
||||
public string? LastFilterPrefix { get; private set; }
|
||||
|
||||
/// <summary>Records a subscription.</summary>
|
||||
/// <param name="command">The subscribe-alarms command.</param>
|
||||
/// <param name="sessionId">The session identifier.</param>
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(SubscribeAlarmsCommand command, string sessionId)
|
||||
{
|
||||
LastSubscription = command.SubscriptionExpression;
|
||||
LastSessionId = sessionId;
|
||||
}
|
||||
|
||||
/// <summary>Records an unsubscribe request.</summary>
|
||||
/// <inheritdoc />
|
||||
public void Unsubscribe()
|
||||
{
|
||||
UnsubscribeCalled = true;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge request.</summary>
|
||||
/// <param name="alarmGuid">The alarm identifier.</param>
|
||||
/// <param name="comment">The acknowledge comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int Acknowledge(
|
||||
Guid alarmGuid, string comment, string operatorUser,
|
||||
string operatorNode, string operatorDomain, string operatorFullName)
|
||||
@@ -406,15 +398,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge by name request.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The group name.</param>
|
||||
/// <param name="comment">The acknowledge comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string comment, string operatorUser, string operatorNode,
|
||||
@@ -428,8 +412,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
/// <summary>Gets the last acknowledge by name tuple.</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Queries the active alarms with the given filter prefix.</summary>
|
||||
/// <param name="alarmFilterPrefix">The alarm filter prefix for the query.</param>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<ActiveAlarmSnapshot> QueryActive(string? alarmFilterPrefix)
|
||||
{
|
||||
LastFilterPrefix = alarmFilterPrefix;
|
||||
@@ -439,7 +422,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
/// <summary>Gets the number of poll calls.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Increments the poll count.</summary>
|
||||
/// <inheritdoc />
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-024: pins both the disposal contract and the
|
||||
/// Pins both the disposal contract and the
|
||||
/// origin of the propagated exception. The fake throws
|
||||
/// <c>InvalidOperationException("simulated wnwrap subscribe failure")</c>
|
||||
/// from <c>Subscribe</c>; the handler must propagate that exact
|
||||
@@ -199,7 +199,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-024 regression: every method that touches the underlying
|
||||
/// Every method that touches the underlying
|
||||
/// <see cref="IMxAccessAlarmConsumer"/> must invoke the configured
|
||||
/// STA-affinity guard. A guard that throws (simulating an off-STA
|
||||
/// call) must propagate from every command-path entry point.
|
||||
@@ -238,7 +238,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-024 regression: a guard that throws must propagate from
|
||||
/// A guard that throws must propagate from
|
||||
/// every command-path entry point — proving the guard is not
|
||||
/// swallowed by an inner try/catch.
|
||||
/// </summary>
|
||||
@@ -274,7 +274,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-9: ForcedMode=Subtag builds a subtag consumer (via the
|
||||
/// ForcedMode=Subtag builds a subtag consumer (via the
|
||||
/// injected standby factory) and advises it — the primary
|
||||
/// (alarmmgr) consumer is NOT created.
|
||||
/// </summary>
|
||||
@@ -312,7 +312,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-9: ForcedMode=Unspecified + a non-empty watch list builds a
|
||||
/// ForcedMode=Unspecified + a non-empty watch list builds a
|
||||
/// failover composite (primary + subtag standby). Forcing the primary
|
||||
/// to fail on subscribe with a threshold of 1 drives the composite to
|
||||
/// switch to the subtag provider, which must enqueue an
|
||||
@@ -359,7 +359,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-9: a non-failover subscribe (alarmmgr-only) never enqueues a
|
||||
/// A non-failover subscribe (alarmmgr-only) never enqueues a
|
||||
/// provider-mode-changed event, and a subsequent Unsubscribe detaches
|
||||
/// the handler so no event leaks.
|
||||
/// </summary>
|
||||
@@ -378,7 +378,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-9: the mapper builds a well-formed OnAlarmProviderModeChanged
|
||||
/// The mapper builds a well-formed OnAlarmProviderModeChanged
|
||||
/// MxEvent — correct family and populated body fields.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
@@ -436,8 +436,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
/// <summary>Gets a value indicating whether the consumer has been disposed.</summary>
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
/// <summary>Subscribes to alarms with the given subscription string.</summary>
|
||||
/// <param name="subscription">The subscription reference.</param>
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(string subscription)
|
||||
{
|
||||
LastSubscription = subscription;
|
||||
@@ -447,13 +446,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by GUID.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByGuid(
|
||||
Guid alarmGuid, string ackComment, string ackOperatorName,
|
||||
string ackOperatorNode, string ackOperatorDomain, string ackOperatorFullName)
|
||||
@@ -463,15 +456,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by name.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string ackComment, string ackOperatorName, string ackOperatorNode,
|
||||
@@ -485,13 +470,13 @@ public sealed class AlarmCommandHandlerTests
|
||||
/// <summary>Gets the last acknowledge-by-name parameters.</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Returns a snapshot of active alarms.</summary>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms() => SnapshotResult;
|
||||
|
||||
/// <summary>Gets the number of times polled.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Polls once for alarm updates.</summary>
|
||||
/// <inheritdoc />
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
|
||||
@@ -394,20 +394,13 @@ public sealed class AlarmDispatcherTests
|
||||
AlarmTransitionEmitted?.Invoke(this, transition);
|
||||
}
|
||||
|
||||
/// <summary>Records the subscription reference.</summary>
|
||||
/// <param name="subscription">The subscription reference.</param>
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(string subscription)
|
||||
{
|
||||
LastSubscription = subscription;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge-by-GUID call with operator identity.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByGuid(
|
||||
Guid alarmGuid,
|
||||
string ackComment,
|
||||
@@ -425,15 +418,7 @@ public sealed class AlarmDispatcherTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge-by-name call with alarm name, provider, and group.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string ackComment, string ackOperatorName, string ackOperatorNode,
|
||||
@@ -447,7 +432,7 @@ public sealed class AlarmDispatcherTests
|
||||
/// <summary>Gets the last acknowledge-by-name tuple (alarm name, provider, group).</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Returns the current snapshot result collection.</summary>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms()
|
||||
{
|
||||
return SnapshotResult;
|
||||
@@ -456,7 +441,7 @@ public sealed class AlarmDispatcherTests
|
||||
/// <summary>Gets the count of poll operations.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Increments the poll count.</summary>
|
||||
/// <inheritdoc />
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
/// </summary>
|
||||
private sealed class FlakyPrimary : IMxAccessAlarmConsumer
|
||||
{
|
||||
/// <summary>Raised when the fake forwards a simulated alarm transition via <see cref="Raise"/>.</summary>
|
||||
public event EventHandler<MxAlarmTransitionEvent>? AlarmTransitionEmitted;
|
||||
|
||||
public bool ThrowOnPoll = true;
|
||||
@@ -32,7 +33,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
/// When set, <see cref="PollOnce"/> throws
|
||||
/// <see cref="OutOfMemoryException"/> instead of a
|
||||
/// <see cref="System.Runtime.InteropServices.COMException"/>, to
|
||||
/// exercise the OOM-safe exception filter (Worker.Tests-032).
|
||||
/// exercise the OOM-safe exception filter.
|
||||
/// </summary>
|
||||
public bool ThrowOutOfMemoryOnPoll;
|
||||
|
||||
@@ -45,6 +46,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
/// </summary>
|
||||
public int SubscribeCount;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(string s)
|
||||
{
|
||||
SubscribeCount++;
|
||||
@@ -54,6 +56,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PollOnce()
|
||||
{
|
||||
Polls++;
|
||||
@@ -68,14 +71,20 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByGuid(Guid g, string c, string a, string b, string d, string e) => 11;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(string n, string p, string gr, string c, string a, string b, string d, string e) => 11;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms() => Array.Empty<MxAlarmSnapshotRecord>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() { }
|
||||
|
||||
/// <summary>Raises <see cref="AlarmTransitionEmitted"/> with the given event, simulating a COM-forwarded transition.</summary>
|
||||
/// <param name="e">The transition event to forward.</param>
|
||||
public void Raise(MxAlarmTransitionEvent e) => AlarmTransitionEmitted?.Invoke(this, e);
|
||||
}
|
||||
|
||||
@@ -85,27 +94,33 @@ public sealed class FailoverAlarmConsumerTests
|
||||
/// </summary>
|
||||
private sealed class StubStandby : IMxAccessAlarmConsumer
|
||||
{
|
||||
/// <summary>Raised when the fake forwards a simulated alarm transition via <see cref="Raise"/>.</summary>
|
||||
public event EventHandler<MxAlarmTransitionEvent>? AlarmTransitionEmitted;
|
||||
|
||||
public bool Subscribed;
|
||||
|
||||
/// <summary>
|
||||
/// When set, <see cref="SnapshotActiveAlarms"/> throws — modeling a
|
||||
/// priming-snapshot failure during failover (Worker-026).
|
||||
/// priming-snapshot failure during failover.
|
||||
/// </summary>
|
||||
public bool ThrowOnSnapshot;
|
||||
|
||||
/// <summary>Number of <see cref="SnapshotActiveAlarms"/> calls.</summary>
|
||||
public int SnapshotCalls;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(string s) => Subscribed = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PollOnce() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByGuid(Guid g, string c, string a, string b, string d, string e) => 22;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(string n, string p, string gr, string c, string a, string b, string d, string e) => 22;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms()
|
||||
{
|
||||
SnapshotCalls++;
|
||||
@@ -117,8 +132,11 @@ public sealed class FailoverAlarmConsumerTests
|
||||
return Array.Empty<MxAlarmSnapshotRecord>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() { }
|
||||
|
||||
/// <summary>Raises <see cref="AlarmTransitionEmitted"/> with the given event, simulating a COM-forwarded transition.</summary>
|
||||
/// <param name="e">The transition event to forward.</param>
|
||||
public void Raise(MxAlarmTransitionEvent e) => AlarmTransitionEmitted?.Invoke(this, e);
|
||||
}
|
||||
|
||||
@@ -128,6 +146,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
PreviousState = MxAlarmStateKind.Unspecified,
|
||||
};
|
||||
|
||||
/// <summary>Proves that the consumer switches to the subtag standby after the primary fails the configured threshold of consecutive times.</summary>
|
||||
[Fact]
|
||||
public void Primary_FailsThresholdTimes_SwitchesToSubtag()
|
||||
{
|
||||
@@ -154,6 +173,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
Assert.Equal(unchecked((int)0x80004005), changes[0].HResult);
|
||||
}
|
||||
|
||||
/// <summary>Proves that once failed over, transitions raised by the standby are forwarded through the consumer's <c>AlarmTransitionEmitted</c> event.</summary>
|
||||
[Fact]
|
||||
public void AfterSwitch_StandbyTransitionsAreForwarded()
|
||||
{
|
||||
@@ -174,6 +194,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
Assert.Same(transition, forwarded);
|
||||
}
|
||||
|
||||
/// <summary>Proves that once the primary heals, the consumer fails back to it only after the configured number of consecutive clean probes.</summary>
|
||||
[Fact]
|
||||
public void WhileDegraded_PrimaryHeals_FailsBackAfterStableProbes()
|
||||
{
|
||||
@@ -212,6 +233,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
Assert.Equal(subscribeCountAfterFailover, primary.SubscribeCount);
|
||||
}
|
||||
|
||||
/// <summary>Proves that before any failover, transitions raised by the primary are forwarded but transitions from the inactive standby are suppressed.</summary>
|
||||
[Fact]
|
||||
public void BeforeFailover_PrimaryTransitionsAreForwarded()
|
||||
{
|
||||
@@ -252,7 +274,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
FailoverSettings settings = new FailoverSettings(threshold: 1, probeIntervalSeconds: 0, stableProbes: 3);
|
||||
using FailoverAlarmConsumer sut = new FailoverAlarmConsumer(primary, standby, settings);
|
||||
|
||||
sut.Subscribe(@"\\HOST\Galaxy!Area"); // Subscribe attempt #1 (throws) → Subtag
|
||||
sut.Subscribe(@"\\HOST\Galaxy!Area"); // Subscribe attempt (throws) → Subtag
|
||||
|
||||
// Capture how many Subscribe calls the initial setup caused (exactly 1:
|
||||
// the attempt that threw and triggered failover).
|
||||
@@ -272,6 +294,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
Assert.Equal(subscribeCountAfterSetup, primary.SubscribeCount);
|
||||
}
|
||||
|
||||
/// <summary>Proves that acknowledgment calls delegate to whichever child (primary or standby) is currently active.</summary>
|
||||
[Fact]
|
||||
public void Acknowledge_DelegatesToActiveChild()
|
||||
{
|
||||
@@ -325,7 +348,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-026 regression: when the standby's priming
|
||||
/// When the standby's priming
|
||||
/// <c>SnapshotActiveAlarms</c> throws during failover, the switch must
|
||||
/// still (a) fire <c>ProviderModeChanged</c> so the gateway learns the
|
||||
/// feed went degraded, (b) leave <see cref="FailoverAlarmConsumer.Mode"/>
|
||||
@@ -361,7 +384,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-026 regression: when a <c>ProviderModeChanged</c> subscriber's
|
||||
/// When a <c>ProviderModeChanged</c> subscriber's
|
||||
/// handler throws (modeling the AlarmCommandHandler's event-queue enqueue
|
||||
/// overflowing at capacity), the switch must still take effect and the
|
||||
/// exception must not escape the switch path into the poll loop.
|
||||
@@ -389,7 +412,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-031 regression: with a non-zero
|
||||
/// With a non-zero
|
||||
/// <see cref="FailoverSettings.ProbeIntervalSeconds"/>, two back-to-back
|
||||
/// <c>ProbeOnce</c> calls must throttle — the second falls inside the
|
||||
/// interval and must NOT re-poll the primary. Two consecutive calls
|
||||
@@ -422,7 +445,7 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-032 regression: <c>RunPrimary</c>'s
|
||||
/// <c>RunPrimary</c>'s
|
||||
/// <c>when (ex is not OutOfMemoryException)</c> filter must let an
|
||||
/// <see cref="OutOfMemoryException"/> propagate rather than swallowing it
|
||||
/// and counting it toward the failover threshold. No mode change must
|
||||
@@ -447,11 +470,17 @@ public sealed class FailoverAlarmConsumerTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-032 regression: <see cref="FailoverSettings"/> clamps
|
||||
/// sub-1 <c>threshold</c> and <c>stableProbes</c> (and sub-0
|
||||
/// Verifies that <see cref="FailoverSettings"/> clamps out-of-range
|
||||
/// <c>threshold</c> and <c>stableProbes</c> values (and negative
|
||||
/// <c>probeIntervalSeconds</c>) to their safe minimums so a misconfigured
|
||||
/// bind cannot change failover semantics.
|
||||
/// </summary>
|
||||
/// <param name="threshold">The raw, possibly out-of-range threshold to construct with.</param>
|
||||
/// <param name="probeInterval">The raw, possibly negative probe interval (seconds) to construct with.</param>
|
||||
/// <param name="stableProbes">The raw, possibly out-of-range stable-probe count to construct with.</param>
|
||||
/// <param name="expectedThreshold">The expected clamped <see cref="FailoverSettings.Threshold"/>.</param>
|
||||
/// <param name="expectedProbeInterval">The expected clamped <see cref="FailoverSettings.ProbeIntervalSeconds"/>.</param>
|
||||
/// <param name="expectedStableProbes">The expected clamped <see cref="FailoverSettings.StableProbes"/>.</param>
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, 1, 0, 1)]
|
||||
[InlineData(-5, -5, -5, 1, 0, 1)]
|
||||
|
||||
@@ -199,26 +199,39 @@ public sealed class LmxSubtagAlarmSourceTests
|
||||
private readonly Dictionary<string, int> handlesByAddress = new(StringComparer.Ordinal);
|
||||
private int nextItemHandle = 100;
|
||||
|
||||
/// <summary>Item definitions passed to <see cref="AddItem"/> or <see cref="AddItem2"/>, in call order.</summary>
|
||||
public List<string> AddedItems { get; } = new();
|
||||
|
||||
/// <summary>Number of times <see cref="Advise"/> has been called.</summary>
|
||||
public int AdviseCount { get; private set; }
|
||||
|
||||
/// <summary>Server handles passed to <see cref="Advise"/>, in call order.</summary>
|
||||
public List<int> AdvisedServerHandles { get; } = new();
|
||||
|
||||
/// <summary>Recorded <see cref="Write"/> calls, in call order.</summary>
|
||||
public List<WriteRecord> Writes { get; } = new();
|
||||
|
||||
/// <summary>Item handles passed to <see cref="UnAdvise"/>, in call order.</summary>
|
||||
public List<int> UnAdvisedItemHandles { get; } = new();
|
||||
|
||||
/// <summary>Item handles passed to <see cref="RemoveItem"/>, in call order.</summary>
|
||||
public List<int> RemovedItemHandles { get; } = new();
|
||||
|
||||
/// <summary>Number of times <see cref="Unregister"/> has been called.</summary>
|
||||
public int UnregisterCount { get; private set; }
|
||||
|
||||
/// <summary>Returns the item handle most recently assigned to the given item address.</summary>
|
||||
/// <param name="itemAddress">The item definition/address to look up.</param>
|
||||
/// <returns>The item handle assigned by <see cref="AddItem"/>.</returns>
|
||||
public int LastItemHandleFor(string itemAddress) => handlesByAddress[itemAddress];
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Register(string clientName) => FakeServerHandle;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Unregister(int serverHandle) => UnregisterCount++;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AddItem(int serverHandle, string itemDefinition)
|
||||
{
|
||||
AddedItems.Add(itemDefinition);
|
||||
@@ -227,55 +240,76 @@ public sealed class LmxSubtagAlarmSourceTests
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AddItem2(int serverHandle, string itemDefinition, string itemContext)
|
||||
=> AddItem(serverHandle, itemDefinition);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveItem(int serverHandle, int itemHandle) => RemovedItemHandles.Add(itemHandle);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Advise(int serverHandle, int itemHandle)
|
||||
{
|
||||
AdviseCount++;
|
||||
AdvisedServerHandles.Add(serverHandle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UnAdvise(int serverHandle, int itemHandle) => UnAdvisedItemHandles.Add(itemHandle);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AdviseSupervisory(int serverHandle, int itemHandle)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Write(int serverHandle, int itemHandle, object? value, int userId)
|
||||
=> Writes.Add(new WriteRecord(serverHandle, itemHandle, value, userId));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured2(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Suspend(int serverHandle, int itemHandle) => new object();
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Activate(int serverHandle, int itemHandle) => new object();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword) => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid) => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
=> AddItem(serverHandle, itemDefinition);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Immutable record of a single <see cref="Write"/> call captured by <see cref="Writes"/>.</summary>
|
||||
internal sealed class WriteRecord
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="WriteRecord"/> class from a recorded write.</summary>
|
||||
/// <param name="serverHandle">The server handle the write targeted.</param>
|
||||
/// <param name="itemHandle">The item handle the write targeted.</param>
|
||||
/// <param name="value">The written value.</param>
|
||||
/// <param name="userId">The writing user's identifier.</param>
|
||||
public WriteRecord(int serverHandle, int itemHandle, object? value, int userId)
|
||||
{
|
||||
ServerHandle = serverHandle;
|
||||
@@ -284,12 +318,16 @@ public sealed class LmxSubtagAlarmSourceTests
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
/// <summary>The server handle the write targeted.</summary>
|
||||
public int ServerHandle { get; }
|
||||
|
||||
/// <summary>The item handle the write targeted.</summary>
|
||||
public int ItemHandle { get; }
|
||||
|
||||
/// <summary>The written value.</summary>
|
||||
public object? Value { get; }
|
||||
|
||||
/// <summary>The writing user's identifier.</summary>
|
||||
public int UserId { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZB.MOM.WW.MxGateway.Worker.MxAccess;
|
||||
namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess;
|
||||
|
||||
/// <summary>
|
||||
/// Worker-007 regression tests for <see cref="MxAccessComServer"/>. The
|
||||
/// Regression tests for <see cref="MxAccessComServer"/>. The
|
||||
/// adapter no longer falls back to late-bound <c>Type.InvokeMember</c>
|
||||
/// reflection: a COM object must implement either the typed
|
||||
/// <c>ILMXProxyServer</c> COM interface family (production) or
|
||||
@@ -124,8 +124,7 @@ public sealed class MxAccessComServerTests
|
||||
/// <summary>Gets the recorded method calls as strings.</summary>
|
||||
public IReadOnlyList<string> Calls => calls.ToArray();
|
||||
|
||||
/// <summary>Records a Register call and returns the configured handle.</summary>
|
||||
/// <param name="clientName">The client name to record.</param>
|
||||
/// <inheritdoc />
|
||||
public int Register(string clientName)
|
||||
{
|
||||
calls.Add($"Register:{clientName}");
|
||||
@@ -138,159 +137,111 @@ public sealed class MxAccessComServerTests
|
||||
return registerHandle;
|
||||
}
|
||||
|
||||
/// <summary>Records an Unregister call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <inheritdoc />
|
||||
public void Unregister(int serverHandle)
|
||||
{
|
||||
calls.Add($"Unregister:{serverHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an AddItem call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
/// <inheritdoc />
|
||||
public int AddItem(int serverHandle, string itemDefinition)
|
||||
{
|
||||
calls.Add($"AddItem:{serverHandle}:{itemDefinition}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an AddItem2 call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
/// <param name="itemContext">The item context string to record.</param>
|
||||
/// <inheritdoc />
|
||||
public int AddItem2(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
calls.Add($"AddItem2:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records a RemoveItem call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public void RemoveItem(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"RemoveItem:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an Advise call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public void Advise(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Advise:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an UnAdvise call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public void UnAdvise(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"UnAdvise:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an AdviseSupervisory call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public void AdviseSupervisory(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Write call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <inheritdoc />
|
||||
public void Write(int serverHandle, int itemHandle, object? value, int userId)
|
||||
{
|
||||
calls.Add($"Write:{serverHandle}:{itemHandle}:{value}:{userId}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Write2 call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestamp">The timestamp value.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <inheritdoc />
|
||||
public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId)
|
||||
{
|
||||
calls.Add($"Write2:{serverHandle}:{itemHandle}:{value}:{timestamp}:{userId}");
|
||||
}
|
||||
|
||||
/// <summary>Records a WriteSecured call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="currentUserId">The current user identifier.</param>
|
||||
/// <param name="verifierUserId">The verifier user identifier.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value)
|
||||
{
|
||||
calls.Add($"WriteSecured:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}");
|
||||
}
|
||||
|
||||
/// <summary>Records a WriteSecured2 call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="currentUserId">The current user identifier.</param>
|
||||
/// <param name="verifierUserId">The verifier user identifier.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestamp">The timestamp value.</param>
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured2(
|
||||
int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp)
|
||||
{
|
||||
calls.Add($"WriteSecured2:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}:{timestamp}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Suspend call and returns a canned status.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public object Suspend(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Suspend:{serverHandle}:{itemHandle}");
|
||||
return new object();
|
||||
}
|
||||
|
||||
/// <summary>Records an Activate call and returns a canned status.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <inheritdoc />
|
||||
public object Activate(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Activate:{serverHandle}:{itemHandle}");
|
||||
return new object();
|
||||
}
|
||||
|
||||
/// <summary>Records an AuthenticateUser call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="verifyUser">The user name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">The credential; recorded only as a fixed marker, never echoed.</param>
|
||||
/// <inheritdoc />
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword)
|
||||
{
|
||||
calls.Add($"AuthenticateUser:{serverHandle}:{verifyUser}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an ArchestrAUserToId call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="userIdGuid">The ArchestrA user GUID to resolve.</param>
|
||||
/// <inheritdoc />
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid)
|
||||
{
|
||||
calls.Add($"ArchestrAUserToId:{serverHandle}:{userIdGuid}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an AddBufferedItem call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
/// <param name="itemContext">The item context string to record.</param>
|
||||
/// <inheritdoc />
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
calls.Add($"AddBufferedItem:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records a SetBufferedUpdateInterval call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="updateIntervalMilliseconds">The buffered update interval in milliseconds.</param>
|
||||
/// <inheritdoc />
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
calls.Add($"SetBufferedUpdateInterval:{serverHandle}:{updateIntervalMilliseconds}");
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess;
|
||||
public sealed class MxAccessCommandExecutorTests
|
||||
{
|
||||
/// <summary>Verifies that Register command calls MXAccess on the STA thread and preserves the server handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Register_CallsMxAccessOnStaAndPreservesServerHandle()
|
||||
{
|
||||
@@ -40,6 +41,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Unregister command calls MXAccess on the STA thread and removes the tracked server handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Unregister_CallsMxAccessOnStaAndRemovesTrackedServerHandle()
|
||||
{
|
||||
@@ -59,6 +61,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Unregister preserves the HResult when MXAccess throws and does not rewrite the failure.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_UnregisterWhenMxAccessThrows_PreservesHResultAndDoesNotRewriteFailure()
|
||||
{
|
||||
@@ -86,6 +89,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem command calls MXAccess on the STA thread and tracks the item handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddItem_CallsMxAccessOnStaAndTracksItemHandle()
|
||||
{
|
||||
@@ -123,6 +127,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem2 command passes the context exactly and tracks the item handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddItem2_PassesContextExactlyAndTracksItemHandle()
|
||||
{
|
||||
@@ -160,6 +165,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that RemoveItem command calls MXAccess on the STA thread and removes the tracked item handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_RemoveItem_CallsMxAccessOnStaAndRemovesTrackedItemHandle()
|
||||
{
|
||||
@@ -188,6 +194,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that RemoveItem removes tracked advice after MXAccess succeeds on an advised handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_RemoveItemWithAdvisedHandle_RemovesTrackedAdviceAfterMxAccessSucceeds()
|
||||
{
|
||||
@@ -213,6 +220,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that RemoveItem preserves the HResult and keeps the tracked item handle when using a cross-server handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_RemoveItemWithCrossServerHandle_PreservesHResultAndKeepsTrackedItemHandle()
|
||||
{
|
||||
@@ -247,6 +255,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem2 preserves the HResult when MXAccess throws and does not track the item handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddItem2WhenMxAccessThrows_PreservesHResultAndDoesNotTrackItemHandle()
|
||||
{
|
||||
@@ -276,6 +285,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Advise command calls MXAccess on the STA thread and tracks plain advice.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Advise_CallsMxAccessOnStaAndTracksPlainAdvice()
|
||||
{
|
||||
@@ -309,6 +319,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AdviseSupervisory calls a distinct MXAccess method and tracks supervisory advice.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AdviseSupervisory_CallsDistinctMxAccessMethodAndTracksSupervisoryAdvice()
|
||||
{
|
||||
@@ -341,6 +352,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that UnAdvise command calls MXAccess on the STA thread and removes the tracked advice.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_UnAdvise_CallsMxAccessOnStaAndRemovesTrackedAdvice()
|
||||
{
|
||||
@@ -369,6 +381,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Advise preserves the HResult when MXAccess throws and does not track the advice.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AdviseWhenMxAccessThrows_PreservesHResultAndDoesNotTrackAdvice()
|
||||
{
|
||||
@@ -399,6 +412,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that UnAdvise preserves the HResult when MXAccess throws and keeps the tracked advice.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_UnAdviseWhenMxAccessThrows_PreservesHResultAndKeepsTrackedAdvice()
|
||||
{
|
||||
@@ -433,6 +447,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that SubscribeBulk runs sequential MXAccess calls and returns per-item results.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_SubscribeBulk_RunsSequentialMxAccessCallsAndReturnsPerItemResults()
|
||||
{
|
||||
@@ -478,6 +493,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// one BulkWriteResult per entry in input order, including a per-entry COM
|
||||
/// failure surfaced as <c>WasSuccessful=false</c> with the underlying HRESULT.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteBulk_RunsSequentialWritesAndReturnsPerEntryResults()
|
||||
{
|
||||
@@ -530,6 +546,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Write2Bulk forwards value AND timestamp to each per-entry Write2.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Write2Bulk_ForwardsValueAndTimestampPerEntry()
|
||||
{
|
||||
@@ -556,6 +573,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that WriteSecuredBulk forwards both user ids per entry.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteSecuredBulk_ForwardsUserIdsPerEntry()
|
||||
{
|
||||
@@ -580,6 +598,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that WriteSecured2Bulk forwards user ids, value, and timestamp per entry.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteSecured2Bulk_ForwardsUserIdsValueAndTimestampPerEntry()
|
||||
{
|
||||
@@ -612,6 +631,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// The fake COM object never fires events so the wait always times out — but
|
||||
/// the lifecycle calls must still happen, in order, on the STA.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_ReadBulk_WhenTagNotCached_TakesSnapshotLifecycleAndTimesOut()
|
||||
{
|
||||
@@ -650,6 +670,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ReadBulk with no payload returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_ReadBulkWithoutPayload_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -671,6 +692,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that UnsubscribeBulk removes items after UnAdvise failure.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_UnsubscribeBulk_RemovesItemAfterUnAdviseFailure()
|
||||
{
|
||||
@@ -700,6 +722,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ShutdownGracefullyAsync cleans up handles in advice, item, server order.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ShutdownGracefullyAsync_CleansHandlesInAdviceItemServerOrder()
|
||||
{
|
||||
@@ -725,6 +748,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ShutdownGracefullyAsync records cleanup failures and continues.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ShutdownGracefullyAsync_RecordsCleanupFailuresAndContinues()
|
||||
{
|
||||
@@ -753,6 +777,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Register without payload returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_RegisterWithoutPayload_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -774,6 +799,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem without payload returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddItemWithoutPayload_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -795,6 +821,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Advise without payload returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AdviseWithoutPayload_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -816,6 +843,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Write dispatches the converted value to MXAccess on the STA thread.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Write_CallsMxAccessOnStaWithConvertedValue()
|
||||
{
|
||||
@@ -838,6 +866,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Write2 forwards the converted value and timestamp to MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Write2_ForwardsValueAndTimestamp()
|
||||
{
|
||||
@@ -860,6 +889,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that WriteSecured forwards the operator and verifier user ids to MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteSecured_ForwardsUserIds()
|
||||
{
|
||||
@@ -881,6 +911,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that WriteSecured2 forwards user ids, value, and timestamp to MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteSecured2_ForwardsUserIdsValueAndTimestamp()
|
||||
{
|
||||
@@ -904,6 +935,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Write without a payload returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteWithoutPayload_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -926,6 +958,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Write without a value returns an invalid request error.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WriteWithoutValue_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -953,6 +986,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies Suspend calls MXAccess on the STA and maps the native status to MxStatusProxy.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Suspend_CallsMxAccessOnStaAndMapsStatus()
|
||||
{
|
||||
@@ -977,6 +1011,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies Activate calls MXAccess on the STA and maps the native status to MxStatusProxy.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Activate_CallsMxAccessOnStaAndMapsStatus()
|
||||
{
|
||||
@@ -1000,6 +1035,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies AuthenticateUser passes credentials to MXAccess on the STA and returns the user id.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AuthenticateUser_CallsMxAccessOnStaAndReturnsUserId()
|
||||
{
|
||||
@@ -1026,6 +1062,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// command reply or any recorded diagnostic — the password is only ever
|
||||
/// handed straight to the MXAccess wrapper.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AuthenticateUser_DoesNotLeakPassword()
|
||||
{
|
||||
@@ -1051,6 +1088,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies ArchestrAUserToId calls MXAccess on the STA and returns the resolved user id.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_ArchestrAUserToId_CallsMxAccessOnStaAndReturnsUserId()
|
||||
{
|
||||
@@ -1072,6 +1110,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies AddBufferedItem calls MXAccess on the STA and tracks the buffered item handle.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddBufferedItem_CallsMxAccessOnStaAndTracksItemHandle()
|
||||
{
|
||||
@@ -1104,6 +1143,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies SetBufferedUpdateInterval calls MXAccess on the STA and returns a base OK reply.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_SetBufferedUpdateInterval_CallsMxAccessOnStaAndReturnsOk()
|
||||
{
|
||||
@@ -1131,6 +1171,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <c>throw</c> would propagate an unhandled exception through <c>WorkerPipeSession</c>
|
||||
/// and no other test would catch it.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_WithUnknownCommandKind_ReturnsInvalidRequestWithUnsupportedDiagnostic()
|
||||
{
|
||||
@@ -1860,9 +1901,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <summary>Gets the list of operations performed on this fake object.</summary>
|
||||
public IReadOnlyList<string> OperationNames => operationNames.ToArray();
|
||||
|
||||
/// <summary>Registers a client and returns a server handle.</summary>
|
||||
/// <param name="clientName">Name of the client to register.</param>
|
||||
/// <returns>The server handle for the registered client.</returns>
|
||||
/// <inheritdoc />
|
||||
public int Register(string clientName)
|
||||
{
|
||||
operationNames.Add($"Register:{clientName}");
|
||||
@@ -1872,8 +1911,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return registerHandle;
|
||||
}
|
||||
|
||||
/// <summary>Unregisters a server and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle to unregister.</param>
|
||||
/// <inheritdoc />
|
||||
public void Unregister(int serverHandle)
|
||||
{
|
||||
operationNames.Add($"Unregister:{serverHandle}");
|
||||
@@ -1886,10 +1924,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Adds an item to the server and returns its item handle.</summary>
|
||||
/// <param name="serverHandle">Server handle to add the item to.</param>
|
||||
/// <param name="itemDefinition">Item definition string.</param>
|
||||
/// <returns>The item handle for the added item.</returns>
|
||||
/// <inheritdoc />
|
||||
public int AddItem(
|
||||
int serverHandle,
|
||||
string itemDefinition)
|
||||
@@ -1907,11 +1942,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return addItemHandle;
|
||||
}
|
||||
|
||||
/// <summary>Adds an item to the server with context and returns its item handle.</summary>
|
||||
/// <param name="serverHandle">Server handle to add the item to.</param>
|
||||
/// <param name="itemDefinition">Item definition string.</param>
|
||||
/// <param name="itemContext">Item context string.</param>
|
||||
/// <returns>The item handle for the added item.</returns>
|
||||
/// <inheritdoc />
|
||||
public int AddItem2(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
@@ -1931,9 +1962,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return addItem2Handle;
|
||||
}
|
||||
|
||||
/// <summary>Removes an item from the server and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle from which to remove the item.</param>
|
||||
/// <param name="itemHandle">Item handle to remove.</param>
|
||||
/// <inheritdoc />
|
||||
public void RemoveItem(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
@@ -1949,9 +1978,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advises on item changes and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the advisory subscription.</param>
|
||||
/// <param name="itemHandle">Item handle to advise on.</param>
|
||||
/// <inheritdoc />
|
||||
public void Advise(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
@@ -1967,9 +1994,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes an item advice subscription and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle from which to remove the subscription.</param>
|
||||
/// <param name="itemHandle">Item handle to remove advice from.</param>
|
||||
/// <inheritdoc />
|
||||
public void UnAdvise(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
@@ -1985,9 +2010,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advises supervisory on item changes and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the supervisory subscription.</param>
|
||||
/// <param name="itemHandle">Item handle to advise supervisory on.</param>
|
||||
/// <inheritdoc />
|
||||
public void AdviseSupervisory(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
@@ -2027,11 +2050,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <summary>Gets the thread ID on which the most recent write was called.</summary>
|
||||
public int? WriteThreadId { get; private set; }
|
||||
|
||||
/// <summary>Writes a value to an item and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the write.</param>
|
||||
/// <param name="itemHandle">Item handle to write to.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="userId">MXAccess user id for the write.</param>
|
||||
/// <inheritdoc />
|
||||
public void Write(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -2047,12 +2066,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
ThrowIfWriteFailureConfigured(itemHandle);
|
||||
}
|
||||
|
||||
/// <summary>Writes a timestamped value to an item and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the write.</param>
|
||||
/// <param name="itemHandle">Item handle to write to.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="timestamp">Source timestamp for the write.</param>
|
||||
/// <param name="userId">MXAccess user id for the write.</param>
|
||||
/// <inheritdoc />
|
||||
public void Write2(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -2070,12 +2084,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
ThrowIfWriteFailureConfigured(itemHandle);
|
||||
}
|
||||
|
||||
/// <summary>Performs a secured write to an item and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the write.</param>
|
||||
/// <param name="itemHandle">Item handle to write to.</param>
|
||||
/// <param name="currentUserId">Operator user id.</param>
|
||||
/// <param name="verifierUserId">Verifier user id.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -2093,13 +2102,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
ThrowIfWriteFailureConfigured(itemHandle);
|
||||
}
|
||||
|
||||
/// <summary>Performs a secured timestamped write to an item and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the write.</param>
|
||||
/// <param name="itemHandle">Item handle to write to.</param>
|
||||
/// <param name="currentUserId">Operator user id.</param>
|
||||
/// <param name="verifierUserId">Verifier user id.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="timestamp">Source timestamp for the write.</param>
|
||||
/// <inheritdoc />
|
||||
public void WriteSecured2(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -2181,10 +2184,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <summary>Gets the interval passed to SetBufferedUpdateInterval, if called.</summary>
|
||||
public int? SetBufferedUpdateIntervalValue { get; private set; }
|
||||
|
||||
/// <summary>Suspends an item and returns a canned status whose fields drive MxStatusProxy conversion.</summary>
|
||||
/// <param name="serverHandle">Server handle for the suspend.</param>
|
||||
/// <param name="itemHandle">Item handle to suspend.</param>
|
||||
/// <returns>A status stand-in with all-OK fields.</returns>
|
||||
/// <inheritdoc />
|
||||
public object Suspend(int serverHandle, int itemHandle)
|
||||
{
|
||||
operationNames.Add($"Suspend:{serverHandle}:{itemHandle}");
|
||||
@@ -2194,10 +2194,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return new FakeMxStatus { success = 1, category = 0, detectedBy = 0, detail = 0 };
|
||||
}
|
||||
|
||||
/// <summary>Activates an item and returns a canned status whose fields drive MxStatusProxy conversion.</summary>
|
||||
/// <param name="serverHandle">Server handle for the activate.</param>
|
||||
/// <param name="itemHandle">Item handle to activate.</param>
|
||||
/// <returns>A status stand-in with all-OK fields.</returns>
|
||||
/// <inheritdoc />
|
||||
public object Activate(int serverHandle, int itemHandle)
|
||||
{
|
||||
operationNames.Add($"Activate:{serverHandle}:{itemHandle}");
|
||||
@@ -2207,11 +2204,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return new FakeMxStatus { success = 1, category = 0, detectedBy = 0, detail = 0 };
|
||||
}
|
||||
|
||||
/// <summary>Authenticates a user and returns a canned user id.</summary>
|
||||
/// <param name="serverHandle">Server handle for the authentication.</param>
|
||||
/// <param name="verifyUser">User name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">Credential; recorded only to assert it is never logged.</param>
|
||||
/// <returns>The canned MXAccess user id (1).</returns>
|
||||
/// <inheritdoc />
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword)
|
||||
{
|
||||
// Deliberately does NOT include the password in the operation log.
|
||||
@@ -2223,10 +2216,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>Resolves an ArchestrA user GUID and returns a canned user id.</summary>
|
||||
/// <param name="serverHandle">Server handle for the resolution.</param>
|
||||
/// <param name="userIdGuid">ArchestrA user GUID to resolve.</param>
|
||||
/// <returns>The canned MXAccess user id (7).</returns>
|
||||
/// <inheritdoc />
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid)
|
||||
{
|
||||
operationNames.Add($"ArchestrAUserToId:{serverHandle}:{userIdGuid}");
|
||||
@@ -2235,11 +2225,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return 7;
|
||||
}
|
||||
|
||||
/// <summary>Adds a buffered item and returns a canned item handle.</summary>
|
||||
/// <param name="serverHandle">Server handle to add the item to.</param>
|
||||
/// <param name="itemDefinition">Item definition string.</param>
|
||||
/// <param name="itemContext">Item context string.</param>
|
||||
/// <returns>The canned buffered item handle (1).</returns>
|
||||
/// <inheritdoc />
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
operationNames.Add($"AddBufferedItem:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
@@ -2249,9 +2235,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>Sets the buffered update interval and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the interval change.</param>
|
||||
/// <param name="updateIntervalMilliseconds">Buffered update interval in milliseconds.</param>
|
||||
/// <inheritdoc />
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
operationNames.Add($"SetBufferedUpdateInterval:{serverHandle}:{updateIntervalMilliseconds}");
|
||||
@@ -2274,8 +2258,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <summary>Gets the fake COM object.</summary>
|
||||
public FakeMxAccessComObject FakeComObject { get; }
|
||||
|
||||
/// <summary>Creates and returns the fake MXAccess COM object.</summary>
|
||||
/// <returns>The fake COM object.</returns>
|
||||
/// <inheritdoc />
|
||||
public object Create()
|
||||
{
|
||||
return FakeComObject;
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed class MxAccessLiveComCreationTests
|
||||
private const string DefaultLiveAddItem2Context = "TestChildObject";
|
||||
|
||||
/// <summary>Verifies that StartAsync creates the installed MXAccess COM object on the STA thread when opted in.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[LiveMxAccessFact]
|
||||
public async Task StartAsync_WhenOptedIn_CreatesInstalledMxAccessComObjectOnSta()
|
||||
{
|
||||
@@ -24,6 +25,7 @@ public sealed class MxAccessLiveComCreationTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Register and Unregister round-trip server handles with installed MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[LiveMxAccessFact]
|
||||
public async Task RegisterAndUnregister_WhenOptedIn_RoundTripsInstalledMxAccessServerHandle()
|
||||
{
|
||||
@@ -61,6 +63,7 @@ public sealed class MxAccessLiveComCreationTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem and RemoveItem round-trip item handles with installed MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[LiveMxAccessFact]
|
||||
public async Task AddItemAndRemoveItem_WhenOptedIn_RoundTripsInstalledMxAccessItemHandle()
|
||||
{
|
||||
@@ -129,6 +132,7 @@ public sealed class MxAccessLiveComCreationTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that AddItem2 and RemoveItem preserve item context with installed MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[LiveMxAccessFact]
|
||||
public async Task AddItem2AndRemoveItem_WhenOptedIn_PreservesContextForInstalledMxAccess()
|
||||
{
|
||||
@@ -198,6 +202,7 @@ public sealed class MxAccessLiveComCreationTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Advise and UnAdvise round-trip subscriptions with installed MXAccess.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[LiveMxAccessFact]
|
||||
public async Task AdviseAndUnAdvise_WhenOptedIn_RoundTripsInstalledMxAccessSubscription()
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// <summary>
|
||||
/// Verifies that StartAsync creates the MXAccess COM object and attaches the event sink on the STA thread.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task StartAsync_CreatesComObjectAndAttachesEventSinkOnStaThread()
|
||||
{
|
||||
@@ -42,6 +43,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// <summary>
|
||||
/// Verifies that StartAsync maps creation exceptions with HResult when the factory fails.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task StartAsync_WhenFactoryFails_MapsCreationExceptionWithHResult()
|
||||
{
|
||||
@@ -63,6 +65,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// <summary>
|
||||
/// Verifies that Dispose detaches the event sink on the STA thread.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task Dispose_DetachesEventSinkOnStaThread()
|
||||
{
|
||||
@@ -116,9 +119,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// </summary>
|
||||
public ApartmentState? CreateApartmentState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the COM object or throws the configured exception.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public object Create()
|
||||
{
|
||||
CreateThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
@@ -158,11 +159,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// </summary>
|
||||
public string? SessionId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the MXAccess COM object and records thread context.
|
||||
/// </summary>
|
||||
/// <param name="mxAccessComObject">MXAccess COM object to attach.</param>
|
||||
/// <param name="sessionId">Identifier of the session.</param>
|
||||
/// <inheritdoc />
|
||||
public void Attach(
|
||||
object mxAccessComObject,
|
||||
string sessionId)
|
||||
@@ -172,9 +169,7 @@ public sealed class MxAccessStaSessionTests
|
||||
SessionId = sessionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the MXAccess COM object and records thread context.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Detach()
|
||||
{
|
||||
DetachThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
@@ -188,6 +183,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// This proves the fix in WorkerPipeSession (and the new internal constructor) correctly
|
||||
/// wires the factory rather than leaving alarmCommandHandler null.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task StartAsync_WithAlarmCommandHandlerFactory_SubscribeAlarmsCommandReachesHandler()
|
||||
{
|
||||
@@ -231,6 +227,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// test fails if the diagnostic regresses to a misleading message that still
|
||||
/// happens to contain the word "alarm".
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task StartAsync_WithoutAlarmCommandHandlerFactory_SubscribeAlarmsReturnsInvalidRequest()
|
||||
{
|
||||
@@ -267,6 +264,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// loop calls PollOnce on the handler via the STA within a reasonable timeout.
|
||||
/// This proves polling is driven by the STA rather than the consumer's internal timer.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task StartAsync_WithAlarmCommandHandlerFactory_PollOnceCalledViaSta()
|
||||
{
|
||||
@@ -304,6 +302,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// immediately after Dispose and stays frozen — deterministic, with no
|
||||
/// elapsed-time "no further polls" window that a slow agent could race.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task Dispose_StopsAlarmPollLoop()
|
||||
{
|
||||
@@ -346,12 +345,13 @@ public sealed class MxAccessStaSessionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-005 regression: when the alarm poll loop's PollOnce throws a
|
||||
/// When the alarm poll loop's PollOnce throws a
|
||||
/// real failure (e.g. a COMException from GetXmlCurrentAlarms2), the
|
||||
/// failure must be recorded as a fault on the event queue so a broken
|
||||
/// alarm subscription becomes observable on the IPC fault path instead
|
||||
/// of silently faulting the never-awaited poll task.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RunAlarmPollLoop_WhenPollOnceThrows_RecordsFaultOnEventQueue()
|
||||
{
|
||||
@@ -389,7 +389,7 @@ public sealed class MxAccessStaSessionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-016 regression: the alarm poll loop's catch for the graceful
|
||||
/// The alarm poll loop's catch for the graceful
|
||||
/// STA-runtime-shutdown signal must NOT also swallow a vanilla
|
||||
/// <see cref="InvalidOperationException"/> raised from inside the marshalled
|
||||
/// poll lambda — for example the STA-affinity assertion thrown by
|
||||
@@ -399,6 +399,7 @@ public sealed class MxAccessStaSessionTests
|
||||
/// <see cref="InvalidOperationException"/> from <c>PollOnce</c> must reach
|
||||
/// the fault-recording arm and become observable on the event queue.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RunAlarmPollLoop_WhenPollOnceThrowsInvalidOperation_RecordsFaultOnEventQueue()
|
||||
{
|
||||
@@ -438,7 +439,7 @@ public sealed class MxAccessStaSessionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-008 regression: the STA-affinity guard throws when an
|
||||
/// The STA-affinity guard throws when an
|
||||
/// IMxAccessAlarmConsumer call is attempted off the thread that created
|
||||
/// the consumer, mirroring the MxAccessSession.CreationThreadId invariant.
|
||||
/// </summary>
|
||||
@@ -455,7 +456,7 @@ public sealed class MxAccessStaSessionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-008: the STA-affinity guard is a no-op on the owning thread and
|
||||
/// The STA-affinity guard is a no-op on the owning thread and
|
||||
/// when no alarm consumer is configured (expected thread id null).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
@@ -495,52 +496,35 @@ public sealed class MxAccessStaSessionTests
|
||||
get { lock (gate) return lastPollThreadId; }
|
||||
}
|
||||
|
||||
/// <summary>Subscribes to alarm events.</summary>
|
||||
/// <param name="command">The subscribe-alarms command.</param>
|
||||
/// <param name="sessionId">The session identifier.</param>
|
||||
/// <inheritdoc />
|
||||
public void Subscribe(SubscribeAlarmsCommand command, string sessionId)
|
||||
{
|
||||
IsSubscribed = true;
|
||||
LastSubscription = command.SubscriptionExpression;
|
||||
}
|
||||
|
||||
/// <summary>Unsubscribes from alarm events.</summary>
|
||||
/// <inheritdoc />
|
||||
public void Unsubscribe()
|
||||
{
|
||||
IsSubscribed = false;
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by guid.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="comment">The acknowledgment comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int Acknowledge(Guid alarmGuid, string comment, string operatorUser,
|
||||
string operatorNode, string operatorDomain, string operatorFullName)
|
||||
=> 0;
|
||||
|
||||
/// <summary>Acknowledges an alarm by name.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="comment">The acknowledgment comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
/// <inheritdoc />
|
||||
public int AcknowledgeByName(string alarmName, string providerName, string groupName,
|
||||
string comment, string operatorUser, string operatorNode,
|
||||
string operatorDomain, string operatorFullName)
|
||||
=> 0;
|
||||
|
||||
/// <summary>Queries active alarms.</summary>
|
||||
/// <param name="alarmFilterPrefix">Optional alarm name filter prefix.</param>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<ActiveAlarmSnapshot> QueryActive(string? alarmFilterPrefix)
|
||||
=> Array.Empty<ActiveAlarmSnapshot>();
|
||||
|
||||
/// <summary>Polls for alarm events once.</summary>
|
||||
/// <inheritdoc />
|
||||
public void PollOnce()
|
||||
{
|
||||
lock (gate)
|
||||
|
||||
@@ -115,6 +115,7 @@ public sealed class MxAccessValueCacheTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryWaitForUpdate returns true when the cache is updated after the baseline.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task TryWaitForUpdate_ReturnsTrue_WhenSetFiresAfterBaselineVersion()
|
||||
{
|
||||
|
||||
@@ -294,16 +294,13 @@ public sealed class SubtagAlarmConsumerTests
|
||||
/// <summary>Gets the most recent (address, value) pair passed to <see cref="Write"/>.</summary>
|
||||
public (string Address, object? Value)? LastWrite { get; private set; }
|
||||
|
||||
/// <summary>Records the advised subtag addresses.</summary>
|
||||
/// <param name="itemAddresses">The subtag references to advise.</param>
|
||||
/// <inheritdoc />
|
||||
public void Advise(IReadOnlyCollection<string> itemAddresses)
|
||||
{
|
||||
Advised.AddRange(itemAddresses);
|
||||
}
|
||||
|
||||
/// <summary>Records the most recent write.</summary>
|
||||
/// <param name="itemAddress">The subtag reference to write.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <inheritdoc />
|
||||
public void Write(string itemAddress, object? value)
|
||||
{
|
||||
LastWrite = (itemAddress, value);
|
||||
|
||||
@@ -23,6 +23,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
AckCommentSubtag = "Tank01.Level.HiHi.ackmsg",
|
||||
};
|
||||
|
||||
/// <summary>Verifies that an active transition from false to true emits an unacked raise.</summary>
|
||||
[Fact]
|
||||
public void ActiveFalseToTrue_EmitsRaise()
|
||||
{
|
||||
@@ -37,6 +38,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal("Area", e.Record.Group);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a subtag target built from a real Galaxy area round-trips to native alarmmgr provider/group/tag-name fields.</summary>
|
||||
[Fact]
|
||||
public void ActiveFalseToTrue_AlarmMgrShape_EmitsNativeProviderGroupTagName()
|
||||
{
|
||||
@@ -59,6 +61,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal("TestMachine_001.TestAlarm001", e.Record.TagName);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that when the alarm reference has no provider "!" separator, the whole reference is used as the tag name.</summary>
|
||||
[Fact]
|
||||
public void ActiveFalseToTrue_NoProviderBang_UsesWholeReferenceAsTagName()
|
||||
{
|
||||
@@ -77,6 +80,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(string.Empty, e.Record.Group);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an out-of-order un-ack arriving before the active-false clear still results in an AckRtn transition.</summary>
|
||||
[Fact]
|
||||
public void OutOfOrderAckThenClear_StillEmitsAckRtn()
|
||||
{
|
||||
@@ -91,6 +95,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(MxAlarmStateKind.AckRtn, e.Record.State);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that constructing the state machine with two targets sharing an active subtag address throws.</summary>
|
||||
[Fact]
|
||||
public void DuplicateActiveSubtag_Throws()
|
||||
{
|
||||
@@ -110,7 +115,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-028 regression: two watch-list entries sharing an
|
||||
/// Two watch-list entries sharing an
|
||||
/// <see cref="AlarmSubtagTarget.AlarmFullReference"/> (but using distinct
|
||||
/// subtag addresses) must throw at construction, symmetric with the
|
||||
/// duplicate-address guard, rather than silently overwriting the earlier
|
||||
@@ -134,6 +139,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Throws<ArgumentException>(() => new SubtagAlarmStateMachine(new[] { first, second }));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acking an active, unacked alarm emits an AckAlm transition from UnackAlm.</summary>
|
||||
[Fact]
|
||||
public void AckedTrueWhileActive_EmitsAck()
|
||||
{
|
||||
@@ -146,6 +152,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(MxAlarmStateKind.UnackAlm, e.PreviousState);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clearing an active, unacked alarm emits an UnackRtn transition.</summary>
|
||||
[Fact]
|
||||
public void ActiveTrueToFalse_WhileUnacked_EmitsUnackRtn()
|
||||
{
|
||||
@@ -157,6 +164,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(MxAlarmStateKind.UnackRtn, e.Record.State);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clearing an active, acked alarm emits an AckRtn transition.</summary>
|
||||
[Fact]
|
||||
public void ActiveTrueToFalse_WhileAcked_EmitsAckRtn()
|
||||
{
|
||||
@@ -169,6 +177,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(MxAlarmStateKind.AckRtn, e.Record.State);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>SnapshotActive</c> reflects the current active and acked state.</summary>
|
||||
[Fact]
|
||||
public void Snapshot_ReflectsActiveAndAckedState()
|
||||
{
|
||||
@@ -180,6 +189,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
Assert.Equal(MxAlarmStateKind.AckAlm, snap.State);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a value change on an address not bound to any target produces no events.</summary>
|
||||
[Fact]
|
||||
public void UnknownAddress_NoEvents()
|
||||
{
|
||||
@@ -189,7 +199,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-033 regression: an ack arriving while the alarm is NOT
|
||||
/// An ack arriving while the alarm is NOT
|
||||
/// active must emit nothing and must NOT latch
|
||||
/// <c>AckedDuringEpisode</c> — otherwise a stale ack from a prior episode
|
||||
/// would mis-latch the next raise into a spurious ACK_RTN on clear. The
|
||||
@@ -214,7 +224,7 @@ public sealed class SubtagAlarmStateMachineTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-033 regression: a priority-subtag value change must flow
|
||||
/// A priority-subtag value change must flow
|
||||
/// through <c>CoerceInt</c> into the emitted record's
|
||||
/// <see cref="MxAlarmSnapshotRecord.Priority"/>. A non-numeric value must
|
||||
/// leave the prior priority unchanged (the CoerceInt fallback path).
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class SyntheticAlarmGuidTests
|
||||
Assert.NotEqual(Guid.Empty, SyntheticAlarmGuid.ForReference(string.Empty));
|
||||
|
||||
/// <summary>
|
||||
/// Worker-027 regression: <see cref="SyntheticAlarmGuid.ForReference"/>
|
||||
/// Regression test: <see cref="SyntheticAlarmGuid.ForReference"/>
|
||||
/// must derive its GUID without routing through
|
||||
/// <see cref="System.Security.Cryptography"/>, because on net48
|
||||
/// <c>MD5.Create()</c> throws under the Windows FIPS-compliance policy.
|
||||
|
||||
@@ -125,7 +125,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-001 regression: the consumer must own no internal
|
||||
/// The consumer must own no internal
|
||||
/// <see cref="Timer"/>. A thread-pool timer calling the
|
||||
/// apartment-threaded wnwrap COM object off its owning STA can
|
||||
/// deadlock on cross-apartment marshaling, so the timer field and
|
||||
@@ -144,7 +144,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-001 regression: no public constructor may accept a
|
||||
/// No public constructor may accept a
|
||||
/// poll-interval parameter. A non-zero poll interval was the only
|
||||
/// way to arm the off-STA timer; removing the parameter makes the
|
||||
/// footgun structurally unreachable.
|
||||
@@ -163,7 +163,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-022: pins the "new alarm sighting" branch of
|
||||
/// Pins the "new alarm sighting" branch of
|
||||
/// <see cref="WnWrapAlarmConsumer.ComputeTransitions"/>. A GUID
|
||||
/// that appears in <c>next</c> but not in <c>previous</c> must
|
||||
/// produce exactly one transition with
|
||||
@@ -191,7 +191,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-022: pins the "state unchanged" branch. A GUID
|
||||
/// Pins the "state unchanged" branch. A GUID
|
||||
/// present in both snapshots with identical
|
||||
/// <see cref="MxAlarmSnapshotRecord.State"/> must produce no
|
||||
/// transition — a regression that emits a transition every poll
|
||||
@@ -218,7 +218,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-022: pins the "state changed" branch. A GUID
|
||||
/// Pins the "state changed" branch. A GUID
|
||||
/// present in both snapshots with a different state must produce
|
||||
/// one transition carrying the prior state so the proto layer
|
||||
/// can distinguish e.g. <c>UnackAlm</c>→<c>AckAlm</c>
|
||||
@@ -247,7 +247,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-022: pins the "alarm cleared from the active set"
|
||||
/// Pins the "alarm cleared from the active set"
|
||||
/// branch. AVEVA drops cleared alarms from
|
||||
/// <c>GetXmlCurrentAlarms2</c>'s active set rather than emitting a
|
||||
/// transition record. A GUID present in
|
||||
@@ -273,7 +273,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-022: pins the multi-alarm fan-out. Multiple
|
||||
/// Pins the multi-alarm fan-out. Multiple
|
||||
/// simultaneous transitions (new + changed + unchanged + dropped)
|
||||
/// in one snapshot must produce exactly the changed and new
|
||||
/// entries — not the unchanged and not the dropped.
|
||||
|
||||
Reference in New Issue
Block a user