using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
///
/// The FIXED: address form added by Gitea #540, and the guarantee that adding it did not
/// disturb the three forms that were already there.
///
[Trait("Category", "Unit")]
public sealed class FocasAddressFixedFormTests
{
[Theory]
[InlineData("FIXED:Identity/SeriesNumber", "Identity/SeriesNumber")]
[InlineData("FIXED:Axes/X/AbsolutePosition", "Axes/X/AbsolutePosition")]
[InlineData("FIXED:Timers/PowerOnSeconds", "Timers/PowerOnSeconds")]
[InlineData("FIXED:Spindle/S1/Load", "Spindle/S1/Load")]
[InlineData("fixed:Program/Name", "Program/Name")] // prefix is case-insensitive
[InlineData(" FIXED:Program/Name ", "Program/Name")] // outer whitespace tolerated
public void Parses_a_fixed_tree_path(string input, string expectedPath)
{
var parsed = FocasAddress.TryParse(input);
parsed.ShouldNotBeNull();
parsed.Kind.ShouldBe(FocasAreaKind.Fixed);
parsed.FixedPath.ShouldBe(expectedPath);
parsed.Canonical.ShouldBe("FIXED:" + expectedPath);
}
[Theory]
[InlineData("FIXED:")] // no path
[InlineData("FIXED: ")]
[InlineData("FIXED:/Identity/Series")] // leading separator
[InlineData("FIXED:Identity/Series/")] // trailing separator
[InlineData("FIXED:Identity//Series")] // empty segment
[InlineData("FIXED:Identity/ /Series")] // blank segment
[InlineData("FIXED:MACRO:500")] // a colon would make the grammar ambiguous
public void Rejects_a_path_that_is_not_addressable(string input)
=> FocasAddress.TryParse(input).ShouldBeNull();
///
/// The exact strings the defect produced. Each names a real fixed-tree field but carries the
/// device host, which is the tag's Device row's job — so they must still be rejected rather
/// than quietly accepted with the host treated as a path segment.
///
[Theory]
[InlineData("10.201.31.5:8193/Identity/SeriesNumber")]
[InlineData("focas://10.201.31.5:8193/Axes/X/AbsolutePosition")]
[InlineData("axis:5")] // the manual picker's old default
public void Still_rejects_what_the_defect_authored(string input)
=> FocasAddress.TryParse(input).ShouldBeNull();
///
/// F is a valid PMC area letter, so the FIXED: prefix has to be tested before
/// the PMC branch. Both forms must survive that ordering.
///
[Fact]
public void The_fixed_prefix_does_not_shadow_the_pmc_F_area()
{
FocasAddress.TryParse("F20.3")!.Kind.ShouldBe(FocasAreaKind.Pmc);
FocasAddress.TryParse("F20.3")!.PmcLetter.ShouldBe("F");
FocasAddress.TryParse("FIXED:Program/Name")!.Kind.ShouldBe(FocasAreaKind.Fixed);
}
[Theory]
[InlineData("R100")]
[InlineData("X0.0")]
[InlineData("PARAM:1020")]
[InlineData("PARAM:1815/0")]
[InlineData("MACRO:500")]
public void The_pre_existing_forms_still_round_trip(string input)
{
var parsed = FocasAddress.TryParse(input);
parsed.ShouldNotBeNull();
parsed.Kind.ShouldNotBe(FocasAreaKind.Fixed);
parsed.Canonical.ShouldBe(input);
}
/// The one place the FIXED: spelling is built, so emitter and parser cannot drift.
[Fact]
public void ForFixedPath_produces_what_TryParse_accepts()
{
var built = FocasAddress.ForFixedPath("Identity/SeriesNumber");
built.ShouldBe("FIXED:Identity/SeriesNumber");
FocasAddress.TryParse(built)!.FixedPath.ShouldBe("Identity/SeriesNumber");
}
}