docs: complete XML doc comments via fixdocs (2757 to 131 findings)

Add missing <returns>/<param>/<summary>/<typeparam> tags and clean up
misused inheritdoc across 481 files so the documented API surface is
complete. Documentation-only (zero code lines changed). The 131 remaining
findings are inheritdoc-style warnings deliberately left to preserve
hand-written implementation rationale (plan-decision notes, race-condition
explanations).
This commit is contained in:
Joseph Doherty
2026-06-03 12:34:34 -04:00
parent c6d9b20d9f
commit bd6c0b4d3d
481 changed files with 2550 additions and 1668 deletions
@@ -68,6 +68,7 @@ public sealed class AbLegacyBitIndexRangeTests
AbLegacyAddress.TryParse("N7:0/-1").ShouldBeNull();
/// <summary>Verifies that bit in word RMW against L file uses 32-bit parent and high bit.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Bit_in_word_RMW_against_L_file_uses_32bit_parent_and_high_bit()
{
@@ -93,6 +94,7 @@ public sealed class AbLegacyBitIndexRangeTests
}
/// <summary>Verifies that bit in word RMW high bit 15 does not corrupt via sign extension.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Bit_in_word_RMW_high_bit_15_does_not_corrupt_via_sign_extension()
{
@@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
public sealed class AbLegacyBitRmwTests
{
/// <summary>Verifies that setting a bit reads the parent word, ORs the bit, and writes back.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Bit_set_reads_parent_word_ORs_bit_writes_back()
{
@@ -33,6 +34,7 @@ public sealed class AbLegacyBitRmwTests
}
/// <summary>Verifies that clearing a bit preserves other bits in the word.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Bit_clear_preserves_other_bits_in_N_file_word()
{
@@ -54,6 +56,7 @@ public sealed class AbLegacyBitRmwTests
}
/// <summary>Verifies that concurrent bit writes to the same word compose correctly.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Concurrent_bit_writes_to_same_word_compose_correctly()
{
@@ -79,6 +82,7 @@ public sealed class AbLegacyBitRmwTests
}
/// <summary>Verifies that repeated bit writes reuse the parent word runtime.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Repeat_bit_writes_reuse_parent_runtime()
{
@@ -12,6 +12,7 @@ public sealed class AbLegacyCapabilityTests
// ---- ITagDiscovery ----
/// <summary>Verifies that DiscoverAsync emits pre-declared tags under the device folder.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task DiscoverAsync_emits_pre_declared_tags_under_device_folder()
{
@@ -40,6 +41,7 @@ public sealed class AbLegacyCapabilityTests
// ---- ISubscribable ----
/// <summary>Verifies that Subscribe initial poll raises OnDataChange.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Subscribe_initial_poll_raises_OnDataChange()
{
@@ -66,6 +68,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that Unsubscribe halts polling.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Unsubscribe_halts_polling()
{
@@ -96,6 +99,7 @@ public sealed class AbLegacyCapabilityTests
// ---- IHostConnectivityProbe ----
/// <summary>Verifies that GetHostStatuses returns one status per device.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task GetHostStatuses_returns_one_per_device()
{
@@ -114,6 +118,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that Probe transitions to Running on successful read.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Probe_transitions_to_Running_on_successful_read()
{
@@ -138,6 +143,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that Probe transitions to Stopped on read failure.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Probe_transitions_to_Stopped_on_read_failure()
{
@@ -162,6 +168,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that Probe is disabled when ProbeAddress is null.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Probe_disabled_when_ProbeAddress_is_null()
{
@@ -180,6 +187,7 @@ public sealed class AbLegacyCapabilityTests
// ---- IPerCallHostResolver ----
/// <summary>Verifies that ResolveHost returns declared device for known tag.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task ResolveHost_returns_declared_device_for_known_tag()
{
@@ -204,6 +212,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that ResolveHost falls back to first device for unknown tags.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task ResolveHost_falls_back_to_first_device_for_unknown()
{
@@ -218,6 +227,7 @@ public sealed class AbLegacyCapabilityTests
}
/// <summary>Verifies that ResolveHost falls back to DriverInstanceId when no devices exist.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task ResolveHost_falls_back_to_DriverInstanceId_when_no_devices()
{
@@ -243,31 +253,22 @@ public sealed class AbLegacyCapabilityTests
/// <summary>Gets list of variables created during discovery.</summary>
public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new();
/// <summary>Records folder creation.</summary>
/// <param name="browseName">The browse name of the folder.</param>
/// <param name="displayName">The display name of the folder.</param>
/// <inheritdoc />
public IAddressSpaceBuilder Folder(string browseName, string displayName)
{ Folders.Add((browseName, displayName)); return this; }
/// <summary>Records variable creation.</summary>
/// <param name="browseName">The browse name of the variable.</param>
/// <param name="displayName">The display name of the variable.</param>
/// <param name="info">The driver attribute information.</param>
/// <inheritdoc />
public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info)
{ Variables.Add((browseName, info)); return new Handle(info.FullName); }
/// <summary>Records property addition (stub implementation).</summary>
/// <param name="_">The property name (unused).</param>
/// <param name="__">The data type (unused).</param>
/// <param name="___">The property value (unused).</param>
/// <inheritdoc />
public void AddProperty(string _, DriverDataType __, object? ___) { }
private sealed class Handle(string fullRef) : IVariableHandle
{
/// <summary>Gets the full reference of the variable.</summary>
/// <inheritdoc />
public string FullReference => fullRef;
/// <summary>Marks the variable as an alarm condition.</summary>
/// <param name="info">The alarm condition information.</param>
/// <inheritdoc />
public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink();
}
/// <summary>Null sink for alarm condition transitions.</summary>
@@ -18,6 +18,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
// ---- Driver.AbLegacy-011 ----
/// <summary>Verifies that Dispose performs teardown without blocking on async operations.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Dispose_runs_teardown_without_blocking_on_async_wait()
{
@@ -47,6 +48,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
}
/// <summary>Verifies that Dispose can be called multiple times without throwing.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Dispose_is_idempotent()
{
@@ -61,6 +63,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
}
/// <summary>Verifies that Dispose does not deadlock under a single-threaded synchronization context.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Dispose_under_single_threaded_sync_context_does_not_deadlock()
{
@@ -19,6 +19,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that InitializeAsync with devices assigns family profiles.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task InitializeAsync_with_devices_assigns_family_profiles()
{
@@ -41,6 +42,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that InitializeAsync with malformed host address faults.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task InitializeAsync_with_malformed_host_address_faults()
{
@@ -55,6 +57,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that ShutdownAsync clears devices.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ShutdownAsync_clears_devices()
{
@@ -115,6 +118,7 @@ public sealed class AbLegacyDriverTests
// ---- Driver.AbLegacy-012: profile fields consumed ----
/// <summary>Verifies that EffectiveCipPath falls back to profile default when host path is empty.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task EffectiveCipPath_falls_back_to_profile_default_when_host_path_is_empty()
{
@@ -135,6 +139,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that EffectiveCipPath preserves explicit host path.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task EffectiveCipPath_preserves_explicit_host_path()
{
@@ -154,6 +159,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that long tag on MicroLogix device is rejected at initialization.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Long_tag_on_MicroLogix_device_rejected_at_init()
{
@@ -170,6 +176,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that long tag on SLC 500 device is accepted.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Long_tag_on_Slc500_device_accepted()
{
@@ -186,6 +193,7 @@ public sealed class AbLegacyDriverTests
}
/// <summary>Verifies that string tag on PLC-5 device is rejected at initialization.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task String_tag_on_Plc5_device_rejected_at_init()
{
@@ -19,13 +19,24 @@ public sealed class AbLegacyLoggerInjectionTests
{
public readonly List<(LogLevel Level, string Message)> Entries = new();
/// <inheritdoc />
/// <summary>Begins a logical operation scope (returns a no-op scope).</summary>
/// <typeparam name="TState">The type of the state to associate with the scope.</typeparam>
/// <param name="state">The state identifier for the scope.</param>
/// <returns>A no-op disposable scope.</returns>
public IDisposable BeginScope<TState>(TState state) where TState : notnull => NullScope.Instance;
/// <inheritdoc />
/// <summary>Checks whether logging is enabled for the given level (always true).</summary>
/// <param name="logLevel">The log level to check.</param>
/// <returns><see langword="true"/> always.</returns>
public bool IsEnabled(LogLevel logLevel) => true;
/// <inheritdoc />
/// <summary>Records a log entry into the captured entries list.</summary>
/// <typeparam name="TState">The type of the log state object.</typeparam>
/// <param name="logLevel">The severity level of the log entry.</param>
/// <param name="eventId">The event identifier for the log entry.</param>
/// <param name="state">The state object associated with the log entry.</param>
/// <param name="exception">An optional exception to log.</param>
/// <param name="formatter">A function that formats the state and exception into a message string.</param>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func<TState, Exception?, string> formatter)
=> Entries.Add((logLevel, formatter(state, exception)));
@@ -33,7 +44,7 @@ public sealed class AbLegacyLoggerInjectionTests
{
public static readonly NullScope Instance = new();
/// <inheritdoc />
/// <summary>Disposes the no-op scope (no-op).</summary>
public void Dispose() { }
}
}
@@ -50,6 +61,7 @@ public sealed class AbLegacyLoggerInjectionTests
}
/// <summary>Verifies that driver initialization failure emits an error log.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task InitializeAsync_failure_emits_error_log()
{
@@ -68,6 +80,7 @@ public sealed class AbLegacyLoggerInjectionTests
}
/// <summary>Verifies that the first non-zero libplctag status per device is logged.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task First_nonzero_libplctag_status_per_device_is_logged()
{
@@ -23,6 +23,7 @@ public sealed class AbLegacyReadWriteTests
// ---- Read ----
/// <summary>Verifies that an unknown reference maps to BadNodeIdUnknown.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Unknown_reference_maps_to_BadNodeIdUnknown()
{
@@ -34,6 +35,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that a successful N-file read returns a Good status.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Successful_N_file_read_returns_Good_value()
{
@@ -51,6 +53,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that repeated reads reuse the runtime.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Repeat_read_reuses_runtime()
{
@@ -67,6 +70,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that non-zero libplctag status values map via AbLegacyStatusMapper.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task NonZero_libplctag_status_maps_via_AbLegacyStatusMapper()
{
@@ -82,6 +86,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that read exceptions surface as BadCommunicationError.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Read_exception_surfaces_BadCommunicationError()
{
@@ -96,6 +101,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that batched reads preserve order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Batched_reads_preserve_order()
{
@@ -120,6 +126,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that read tag creation parameters are composed from device and profile.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Read_TagCreateParams_composed_from_device_and_profile()
{
@@ -140,6 +147,7 @@ public sealed class AbLegacyReadWriteTests
// ---- Write ----
/// <summary>Verifies that a non-writable tag rejects with BadNotWritable.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Non_writable_tag_rejects_with_BadNotWritable()
{
@@ -153,6 +161,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that a successful N-file write encodes and flushes the data.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Successful_N_file_write_encodes_and_flushes()
{
@@ -169,6 +178,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that bit-within-word write now succeeds via RMW.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Bit_within_word_write_now_succeeds_via_RMW()
{
@@ -190,6 +200,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that write exceptions surface as BadCommunicationError.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Write_exception_surfaces_BadCommunicationError()
{
@@ -204,6 +215,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that batch write preserves order across different outcomes.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Batch_write_preserves_order_across_outcomes()
{
@@ -233,6 +245,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that cancellation propagates through the driver.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Cancellation_propagates()
{
@@ -250,6 +263,7 @@ public sealed class AbLegacyReadWriteTests
}
/// <summary>Verifies that ShutdownAsync disposes all runtimes.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ShutdownAsync_disposes_runtimes()
{
@@ -30,9 +30,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
/// <param name="p">The tag creation parameters.</param>
public OverlapDetectingFake(AbLegacyTagCreateParams p) : base(p) { }
/// <summary>Reads the tag asynchronously while tracking concurrent operations.</summary>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the read operation.</returns>
/// <inheritdoc />
public override async Task ReadAsync(CancellationToken ct)
{
EnterOp();
@@ -45,9 +43,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
finally { LeaveOp(); }
}
/// <summary>Writes to the tag asynchronously while tracking concurrent operations.</summary>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the write operation.</returns>
/// <inheritdoc />
public override async Task WriteAsync(CancellationToken ct)
{
EnterOp();
@@ -69,6 +65,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
}
/// <summary>Verifies that concurrent reads of the same tag are serialised against the shared runtime.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Concurrent_reads_of_same_tag_are_serialised_against_the_shared_runtime()
{
@@ -102,6 +99,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
}
/// <summary>Verifies that concurrent read and write operations on the same tag do not overlap.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Concurrent_read_and_write_of_same_tag_do_not_overlap()
{
@@ -41,9 +41,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
/// <param name="p">The tag creation parameters.</param>
public FakeAbLegacyTag(AbLegacyTagCreateParams p) => CreationParams = p;
/// <summary>Initializes the tag asynchronously.</summary>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <inheritdoc />
public virtual Task InitializeAsync(CancellationToken ct)
{
InitializeCount++;
@@ -51,9 +49,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
return Task.CompletedTask;
}
/// <summary>Reads the tag value asynchronously.</summary>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <inheritdoc />
public virtual Task ReadAsync(CancellationToken ct)
{
ReadCount++;
@@ -61,9 +57,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
return Task.CompletedTask;
}
/// <summary>Writes the tag value asynchronously.</summary>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <inheritdoc />
public virtual Task WriteAsync(CancellationToken ct)
{
WriteCount++;
@@ -71,20 +65,13 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
return Task.CompletedTask;
}
/// <summary>Gets the current tag status.</summary>
/// <returns>The status code.</returns>
/// <inheritdoc />
public virtual int GetStatus() => Status;
/// <summary>Decodes the tag value based on the specified data type and bit index.</summary>
/// <param name="type">The AbLegacy data type.</param>
/// <param name="bitIndex">The bit index if applicable.</param>
/// <returns>The decoded value.</returns>
/// <inheritdoc />
public virtual object? DecodeValue(AbLegacyDataType type, int? bitIndex) => Value;
/// <summary>Encodes the tag value based on the specified data type and bit index.</summary>
/// <param name="type">The AbLegacy data type.</param>
/// <param name="bitIndex">The bit index if applicable.</param>
/// <param name="value">The value to encode.</param>
/// <inheritdoc />
public virtual void EncodeValue(AbLegacyDataType type, int? bitIndex, object? value) => Value = value;
/// <summary>Disposes the tag.</summary>
@@ -100,9 +87,7 @@ internal sealed class FakeAbLegacyTagFactory : IAbLegacyTagFactory
/// <summary>Gets or sets an optional customization function for tag creation.</summary>
public Func<AbLegacyTagCreateParams, FakeAbLegacyTag>? Customise { get; set; }
/// <summary>Creates a new AbLegacy tag with the specified parameters.</summary>
/// <param name="p">The tag creation parameters.</param>
/// <returns>The created tag.</returns>
/// <inheritdoc />
public IAbLegacyTagRuntime Create(AbLegacyTagCreateParams p)
{
var fake = Customise?.Invoke(p) ?? new FakeAbLegacyTag(p);