using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; /// /// Gitea #548 — the sibling-name disambiguator that makes a CNC reporting two axes both /// called AA publishable as AA_1 / AA_2. /// [Trait("Category", "Unit")] public sealed class FixedTreeNamesTests { [Fact] public void Unique_names_pass_through_unchanged() { FixedTreeNames.Disambiguate(["X", "Y", "Z"]).ShouldBe(["X", "Y", "Z"]); } [Fact] public void Every_member_of_a_collision_set_is_suffixed_in_slot_order() { // The whole set, not just the second occurrence: bare "AA" must cease to exist when // ambiguous, or it would silently mean "one of the two". FixedTreeNames.Disambiguate(["AA", "Y", "AA"]).ShouldBe(["AA_1", "Y", "AA_2"]); } [Fact] public void An_unrelated_name_between_duplicates_does_not_shift_their_ordinals() { // The ordinal counts occurrences of the SAME name, not positions in the list — adding an // unrelated axis never renames an existing duplicate pair. FixedTreeNames.Disambiguate(["B", "AA", "Y", "AA"]).ShouldBe(["B", "AA_1", "Y", "AA_2"]); } [Fact] public void Duplicate_grouping_is_case_insensitive() { // The snapshot cache and the read path both compare OrdinalIgnoreCase, so "Aa" and "AA" // would collide there — they must be treated as one collision set here too. FixedTreeNames.Disambiguate(["Aa", "AA"]).ShouldBe(["Aa_1", "AA_2"]); } [Fact] public void A_generated_segment_never_collides_with_a_genuine_name() { // Pathological but must not silently alias: a real sibling already named "S_1" claims // that segment, so the duplicate set escalates past it. FixedTreeNames.Disambiguate(["S_1", "S", "S"]).ShouldBe(["S_1", "S_2", "S_3"]); } [Fact] public void Three_way_collisions_number_sequentially() { FixedTreeNames.Disambiguate(["AA", "AA", "AA"]).ShouldBe(["AA_1", "AA_2", "AA_3"]); } [Fact] public void The_output_is_always_unique() { var segments = FixedTreeNames.Disambiguate(["A", "A_1", "A", "A_2", "A"]); segments.Distinct(StringComparer.OrdinalIgnoreCase).Count().ShouldBe(segments.Count); } [Fact] public void An_empty_list_yields_an_empty_list() { FixedTreeNames.Disambiguate([]).ShouldBeEmpty(); } }