diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/NativeAlarmDriverSetGuardTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/NativeAlarmDriverSetGuardTests.cs new file mode 100644 index 00000000..19ceab1a --- /dev/null +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/NativeAlarmDriverSetGuardTests.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors; +using ZB.MOM.WW.OtOpcUa.Core.Abstractions; + +namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns; + +/// +/// Gitea #642 guard: is a hand-maintained set; it must equal +/// the drivers whose concrete types implement . Discovery is by reflection over the +/// driver assemblies deployed beside this test; an assembly's driver type is the +/// constant whose FIELD NAME equals the assembly suffix (Driver.Galaxy -> Galaxy = "GalaxyMxGateway"). +/// +public sealed class NativeAlarmDriverSetGuardTests +{ + private static readonly string[] NonDriverSuffixes = [".Contracts", ".Addressing", ".Cli", ".Historian.Gateway"]; + + [Fact] + public void SupportsNativeAlarms_matches_the_drivers_implementing_IAlarmSource() + { + var binDir = Path.GetDirectoryName(typeof(NativeAlarmDriverSetGuardTests).Assembly.Location)!; + var constants = typeof(DriverTypeNames).GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(f => f.IsLiteral && f.FieldType == typeof(string)) + .ToDictionary(f => f.Name, f => (string)f.GetRawConstantValue()!, StringComparer.OrdinalIgnoreCase); + + const string prefix = "ZB.MOM.WW.OtOpcUa.Driver."; + var implementers = new HashSet(StringComparer.OrdinalIgnoreCase); + var scanned = 0; + foreach (var dll in Directory.GetFiles(binDir, prefix + "*.dll")) + { + var name = Path.GetFileNameWithoutExtension(dll); + if (NonDriverSuffixes.Any(s => name.EndsWith(s, StringComparison.Ordinal))) { continue; } + Type[] types; + try { types = Assembly.LoadFrom(dll).GetTypes(); } + catch (ReflectionTypeLoadException ex) { types = ex.Types.Where(t => t is not null).ToArray()!; } + scanned++; + if (!types.Any(t => t.IsClass && !t.IsAbstract && typeof(IAlarmSource).IsAssignableFrom(t))) { continue; } + var suffix = name[prefix.Length..]; + constants.TryGetValue(suffix, out var typeName).ShouldBeTrue($"no DriverTypeNames constant named '{suffix}' for {name}"); + implementers.Add(typeName!); + } + + scanned.ShouldBeGreaterThan(5, "driver assemblies were not deployed to the test output; the guard would be vacuous"); + implementers.Count.ShouldBeGreaterThan(0); + + foreach (var typeName in constants.Values) + { + NativeAlarmModel.SupportsNativeAlarms(typeName).ShouldBe(implementers.Contains(typeName), + $"NativeAlarmModel.AlarmDriverTypes disagrees with IAlarmSource for driver type '{typeName}'"); + } + } +} diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ZB.MOM.WW.OtOpcUa.AdminUI.Tests.csproj b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ZB.MOM.WW.OtOpcUa.AdminUI.Tests.csproj index 742ab993..69af045a 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ZB.MOM.WW.OtOpcUa.AdminUI.Tests.csproj +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ZB.MOM.WW.OtOpcUa.AdminUI.Tests.csproj @@ -40,6 +40,10 @@ + + + +