Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerProfile.cs
T
Joseph Doherty 326b22a7ba fix(abcip-tests): expression-body KnownProfiles.All to fix static-init NRE
KnownProfiles.All used a { get; } = [...] initialiser declared textually
before the static readonly profile fields it references, so C# static-init
order captured an all-null list. ForFamily() then NRE'd on p.Family, faulting
AbServerFixture..ctor and blocking the whole AbCip.IntegrationTests suite.

Expression-body it so it evaluates on access, after the fields are set.
Live-verified against the controllogix fixture: suite goes 6F/1P/3skip ->
10 pass / 0 fail / 3 skip (skips = emulator-mode tests).

Integration-sweep follow-up #1 (archreview/plans/INTEGRATION-SWEEP-STATUS.md).
2026-07-15 03:28:04 -04:00

68 lines
3.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests;
/// <summary>
/// Per-family marker for the <c>ab_server</c> Docker compose profile a given test
/// targets. The compose file (<c>Docker/docker-compose.yml</c>) is the canonical
/// source of truth for which tags a family seeds + which <c>--plc</c> mode the
/// simulator boots in; this record just ties a family enum to operator-facing
/// notes so fixture + test code can filter / branch by family.
/// </summary>
/// <param name="Family">OtOpcUa driver family this profile targets.</param>
/// <param name="ComposeProfile">The <c>docker compose --profile</c> name that brings
/// this family's ab_server up. Matches the service key in the compose file.</param>
/// <param name="Notes">Operator-facing description of coverage + any quirks.</param>
public sealed record AbServerProfile(
AbCipPlcFamily Family,
string ComposeProfile,
string Notes)
{
/// <summary>Default ab_server port — matches the compose-file port-map + the
/// CIP / EtherNet/IP standard.</summary>
public const int DefaultPort = 44818;
}
/// <summary>Canonical profiles covering every AB CIP family shipped in PRs 912.</summary>
public static class KnownProfiles
{
/// <summary>Gets all known server profiles.</summary>
/// <remarks>Expression-bodied (evaluated on access) so it observes the
/// <c>static readonly</c> profile fields below after they are initialised;
/// a <c>{ get; } =</c> initialiser here runs in textual order — before those
/// fields — and would capture an all-null list.</remarks>
public static IReadOnlyList<AbServerProfile> All =>
[ControlLogix, CompactLogix, Micro800, GuardLogix];
/// <summary>Gets the ControlLogix profile.</summary>
public static readonly AbServerProfile ControlLogix = new(
Family: AbCipPlcFamily.ControlLogix,
ComposeProfile: "controllogix",
Notes: "Widest-coverage profile — PR 9 baseline. UDTs unit-tested via golden Template Object buffers; ab_server lacks full UDT emulation.");
/// <summary>Gets the CompactLogix profile.</summary>
public static readonly AbServerProfile CompactLogix = new(
Family: AbCipPlcFamily.CompactLogix,
ComposeProfile: "compactlogix",
Notes: "ab_server doesn't enforce the narrower ConnectionSize; driver-side profile caps it per PR 10.");
/// <summary>Gets the Micro800 profile.</summary>
public static readonly AbServerProfile Micro800 = new(
Family: AbCipPlcFamily.Micro800,
ComposeProfile: "micro800",
Notes: "--plc=Micro800 mode (unconnected-only, empty path). Driver-side enforcement verified in the unit suite.");
/// <summary>Gets the GuardLogix profile.</summary>
public static readonly AbServerProfile GuardLogix = new(
Family: AbCipPlcFamily.GuardLogix,
ComposeProfile: "guardlogix",
Notes: "ab_server has no safety subsystem — _S-suffixed seed tag triggers driver-side ViewOnly classification only.");
/// <summary>Gets the profile for a specific family.</summary>
/// <param name="family">The AB CIP PLC family.</param>
/// <returns>The corresponding server profile.</returns>
public static AbServerProfile ForFamily(AbCipPlcFamily family) =>
All.FirstOrDefault(p => p.Family == family)
?? throw new ArgumentOutOfRangeException(nameof(family), family, "No integration profile for this family.");
}