using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Captures; public sealed class CaptureFileTests { private static readonly string[] Required = [ "logix-register-session.hex", "logix-forward-open.hex", "logix-read-dint.hex", "logix-read-dint-array16.hex", "logix-read-string.hex", "logix-read-bool.hex", "logix-write-dint.hex", "logix-write-string.hex", "logix-read-unknown-tag.hex", "logix-tags-listing.hex", "logix-forward-close.hex", "micro800-read-dint.hex", "slc500-read-n7.hex", "slc500-write-n7.hex", "slc500-read-f8.hex", "micrologix-read-n7.hex", "plc5-read-n7.hex", ]; public static TheoryData AllCaptures() { var data = new TheoryData(); foreach (var f in Directory.GetFiles(CaptureFile.Dir, "*.hex").Order()) data.Add(Path.GetFileName(f)); return data; } [Fact] public void Every_required_capture_is_present() => Required.Where(n => !File.Exists(Path.Combine(CaptureFile.Dir, n))).ShouldBeEmpty(); [Theory] [MemberData(nameof(AllCaptures))] public void Every_frame_length_field_matches_its_data_length(string name) { var frames = CaptureFile.Load(name); frames.ShouldNotBeEmpty(); foreach (var f in frames) { f.Bytes.Length.ShouldBeGreaterThanOrEqualTo(24, name); BitConverter.ToUInt16(f.Bytes, 2).ShouldBe((ushort)(f.Bytes.Length - 24), name); } } [Theory] [MemberData(nameof(AllCaptures))] public void Frames_alternate_request_then_reply(string name) { var frames = CaptureFile.Load(name); frames.Count.ShouldBe(frames.Count / 2 * 2, name); for (var i = 0; i < frames.Count; i++) frames[i].FromClient.ShouldBe(i % 2 == 0, $"{name} frame {i}"); } [Fact] public void CipPayload_strips_the_connected_sequence_count() { // Connected Read Tag Fragmented of TestDINT: service 0x52, path size 5 words, symbol 0x91 len 8. var request = CaptureFile.Load("logix-read-dint.hex")[0]; CaptureFile.CipPayload(request)[..4].ShouldBe(new byte[] { 0x52, 0x05, 0x91, 0x08 }); } [Fact] public void CipPayload_returns_the_unconnected_item_for_SendRRData() { // Large Forward Open request goes out in an unconnected data item (0xB2): service 0x5B. var request = CaptureFile.Load("logix-forward-open.hex")[0]; CaptureFile.CipPayload(request)[0].ShouldBe((byte)0x5B); } [Fact] public void CipPayload_is_empty_for_RegisterSession() => CaptureFile.CipPayload(CaptureFile.Load("logix-register-session.hex")[0]).ShouldBeEmpty(); }