177 lines
5.6 KiB
C#
177 lines
5.6 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class FocasPmcCoalescerTests
|
|
{
|
|
[Fact]
|
|
public void Empty_input_yields_no_groups()
|
|
{
|
|
var groups = FocasPmcCoalescer.Plan(Array.Empty<PmcAddressRequest>());
|
|
groups.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void Contiguous_same_letter_same_path_coalesces_into_one_group()
|
|
{
|
|
// 100 contiguous R-letter byte reads at byte 0..99
|
|
var requests = new List<PmcAddressRequest>();
|
|
for (var i = 0; i < 100; i++)
|
|
requests.Add(new PmcAddressRequest("R", PathId: 1, ByteNumber: i, ByteWidth: 1, OriginalIndex: i));
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(1);
|
|
var g = groups[0];
|
|
g.Letter.ShouldBe("R");
|
|
g.PathId.ShouldBe(1);
|
|
g.StartByte.ShouldBe(0);
|
|
g.ByteCount.ShouldBe(100);
|
|
g.Members.Count.ShouldBe(100);
|
|
g.Members[42].Offset.ShouldBe(42);
|
|
g.Members[42].OriginalIndex.ShouldBe(42);
|
|
}
|
|
|
|
[Fact]
|
|
public void Range_cap_splits_oversized_runs_into_multiple_groups()
|
|
{
|
|
// 300 contiguous bytes — must split (cap = 256)
|
|
var requests = new List<PmcAddressRequest>();
|
|
for (var i = 0; i < 300; i++)
|
|
requests.Add(new PmcAddressRequest("R", 1, i, 1, i));
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(2);
|
|
groups[0].ByteCount.ShouldBe(FocasPmcCoalescer.MaxRangeBytes);
|
|
groups[0].StartByte.ShouldBe(0);
|
|
groups[1].StartByte.ShouldBe(FocasPmcCoalescer.MaxRangeBytes);
|
|
groups[1].ByteCount.ShouldBe(300 - FocasPmcCoalescer.MaxRangeBytes);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gap_within_bridge_threshold_is_bridged()
|
|
{
|
|
// Two runs: R0..R9 then R20..R29 — gap = 10 bytes, within bridge cap of 16.
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 0, 1, 0),
|
|
new("R", 1, 9, 1, 1),
|
|
new("R", 1, 20, 1, 2),
|
|
new("R", 1, 29, 1, 3),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(1);
|
|
groups[0].StartByte.ShouldBe(0);
|
|
groups[0].ByteCount.ShouldBe(30);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gap_larger_than_bridge_threshold_splits()
|
|
{
|
|
// Two runs: R0 then R100 — gap of 99 bytes >> 16, must split.
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 0, 1, 0),
|
|
new("R", 1, 100, 1, 1),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(2);
|
|
groups[0].StartByte.ShouldBe(0);
|
|
groups[1].StartByte.ShouldBe(100);
|
|
}
|
|
|
|
[Fact]
|
|
public void Different_letters_split_into_separate_groups()
|
|
{
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 0, 1, 0),
|
|
new("R", 1, 1, 1, 1),
|
|
new("D", 1, 0, 1, 2),
|
|
new("D", 1, 1, 1, 3),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(2);
|
|
groups.ShouldContain(g => g.Letter == "R" && g.ByteCount == 2);
|
|
groups.ShouldContain(g => g.Letter == "D" && g.ByteCount == 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Different_paths_split_into_separate_groups()
|
|
{
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 0, 1, 0),
|
|
new("R", 1, 1, 1, 1),
|
|
new("R", 2, 0, 1, 2),
|
|
new("R", 2, 1, 1, 3),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(2);
|
|
groups.ShouldContain(g => g.Letter == "R" && g.PathId == 1);
|
|
groups.ShouldContain(g => g.Letter == "R" && g.PathId == 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Wider_data_types_extend_range_correctly()
|
|
{
|
|
// R0 is Int32 (4 bytes covers R0..R3), R4 is Byte → contiguous, one group of 5 bytes.
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 0, ByteWidth: 4, 0),
|
|
new("R", 1, 4, ByteWidth: 1, 1),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(1);
|
|
groups[0].ByteCount.ShouldBe(5);
|
|
groups[0].Members[0].ByteWidth.ShouldBe(4);
|
|
groups[0].Members[0].Offset.ShouldBe(0);
|
|
groups[0].Members[1].ByteWidth.ShouldBe(1);
|
|
groups[0].Members[1].Offset.ShouldBe(4);
|
|
}
|
|
|
|
[Fact]
|
|
public void Overlapping_requests_do_not_grow_range_beyond_their_union()
|
|
{
|
|
// R10 Int32 (R10..R13) + R12 Byte — overlap; range should still be 4 bytes from 10.
|
|
var requests = new List<PmcAddressRequest>
|
|
{
|
|
new("R", 1, 10, 4, 0),
|
|
new("R", 1, 12, 1, 1),
|
|
};
|
|
|
|
var groups = FocasPmcCoalescer.Plan(requests);
|
|
|
|
groups.Count.ShouldBe(1);
|
|
groups[0].StartByte.ShouldBe(10);
|
|
groups[0].ByteCount.ShouldBe(4);
|
|
groups[0].Members[1].Offset.ShouldBe(2); // member at byte 12, offset within range = 2
|
|
}
|
|
|
|
[Fact]
|
|
public void ByteWidth_helper_matches_data_type_sizes()
|
|
{
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Bit).ShouldBe(1);
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Byte).ShouldBe(1);
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Int16).ShouldBe(2);
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Int32).ShouldBe(4);
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Float32).ShouldBe(4);
|
|
FocasPmcCoalescer.ByteWidth(FocasDataType.Float64).ShouldBe(8);
|
|
}
|
|
}
|