using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers;
///
/// Gitea #540. The previous edition of this file asserted
/// Build("axis", 5) == "axis:5" for four "parameter groups" that are not FOCAS
/// addressing spaces — it pinned the exact string the driver could not parse, which is why
/// a fully-green suite coexisted with a picker whose default value was unusable. Every case
/// here therefore asserts the address ROUND-TRIPS through the driver's parser, not merely
/// that it has the expected shape.
///
public sealed class FocasAddressBuilderTests
{
[Theory]
[InlineData(FocasManualAddressKind.Pmc, "R", 100, null, "R100")]
[InlineData(FocasManualAddressKind.Pmc, "X", 0, 0, "X0.0")]
[InlineData(FocasManualAddressKind.Pmc, "F", 20, 3, "F20.3")]
[InlineData(FocasManualAddressKind.Pmc, "r", 100, null, "R100")] // letter is normalised
[InlineData(FocasManualAddressKind.Parameter, null, 1020, null, "PARAM:1020")]
[InlineData(FocasManualAddressKind.Parameter, null, 1815, 0, "PARAM:1815/0")]
[InlineData(FocasManualAddressKind.Macro, null, 500, null, "MACRO:500")]
[InlineData(FocasManualAddressKind.Macro, null, 500, 3, "MACRO:500")] // macros have no bit form
public void Build_produces_an_address_the_driver_can_parse(
FocasManualAddressKind kind, string? letter, int number, int? bit, string expected)
{
var built = FocasAddressBuilder.Build(kind, letter, number, bit);
built.ShouldBe(expected);
FocasAddress.TryParse(built).ShouldNotBeNull();
}
[Theory]
[InlineData(FocasManualAddressKind.Pmc, "Z", 10, null)] // not a PMC area letter
[InlineData(FocasManualAddressKind.Pmc, "", 10, null)] // no area chosen
[InlineData(FocasManualAddressKind.Pmc, "R", 10, 8)] // PMC bits are 0-7
[InlineData(FocasManualAddressKind.Pmc, "R", -1, null)] // negative byte
[InlineData(FocasManualAddressKind.Parameter, null, 1815, 32)] // parameter bits are 0-31
[InlineData(FocasManualAddressKind.Parameter, null, -5, null)]
public void Build_returns_empty_rather_than_an_unusable_address(
FocasManualAddressKind kind, string? letter, int number, int? bit)
=> FocasAddressBuilder.Build(kind, letter, number, bit).ShouldBeEmpty();
///
/// The contract that matters, stated over the whole offered surface rather than a sample:
/// nothing the picker can compose is unreadable. Every PMC area the picker lists, at a
/// whole-word address and at each legal bit.
///
[Fact]
public void Every_offered_pmc_area_composes_a_parseable_address()
{
foreach (var letter in FocasAddressBuilder.PmcLetters)
{
foreach (int? bit in new int?[] { null, 0, 7 })
{
var built = FocasAddressBuilder.Build(FocasManualAddressKind.Pmc, letter, 12, bit);
built.ShouldNotBeEmpty($"PMC area '{letter}' bit '{bit}' composed nothing");
FocasAddress.TryParse(built).ShouldNotBeNull($"'{built}' does not parse");
}
}
}
// ---- Gitea #647: ALARM: selectors ----
[Theory]
[InlineData("*", null, null, "ALARM:*")]
[InlineData("*", 401, null, "ALARM:*")] // numbers apply to a type scope only
[InlineData("OTHER", null, null, "ALARM:OTHER")]
[InlineData("Servo", null, null, "ALARM:Servo")]
[InlineData("Servo", 401, null, "ALARM:Servo/401")]
[InlineData("Servo", 401, 453, "ALARM:Servo/401-453")]
[InlineData("Servo", 401, 401, "ALARM:Servo/401")]
[InlineData("T9", 750, 758, "ALARM:T9/750-758")]
public void BuildAlarm_produces_a_selector_the_driver_can_parse(string scope, int? lo, int? hi, string expected)
{
var built = FocasAddressBuilder.BuildAlarm(scope, lo, hi);
built.ShouldBe(expected);
FocasAddress.TryParse(built)!.Kind.ShouldBe(FocasAreaKind.Alarm);
}
[Theory]
[InlineData("Servo", 453, 401)] // reversed range
[InlineData("Bogus", null, null)] // not a type
[InlineData("", null, null)]
public void BuildAlarm_returns_empty_rather_than_an_unusable_selector(string scope, int? lo, int? hi)
=> FocasAddressBuilder.BuildAlarm(scope, lo, hi).ShouldBeEmpty();
[Fact]
public void Every_offered_alarm_scope_composes_a_parseable_selector()
{
foreach (var scope in FocasAddressBuilder.AlarmScopes)
FocasAddress.TryParse(FocasAddressBuilder.BuildAlarm(scope, 1, 2)).ShouldNotBeNull(scope);
}
}