using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Wire; namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Wire; public sealed class CipWriterTests { [Fact] public void PatchU16_overwrites_an_already_written_word() { var w = new CipWriter().U16(0).U8(7); w.PatchU16(0, 0xABCD); w.ToArray().ShouldBe(new byte[] { 0xCD, 0xAB, 0x07 }); } [Theory] [InlineData(-1)] [InlineData(2)] // would straddle the end: only bytes 0..2 are written [InlineData(3)] [InlineData(100)] // inside spare buffer capacity (256), but never written public void PatchU16_outside_the_written_range_throws(int offset) { var w = new CipWriter().U16(0).U8(7); Should.Throw(() => w.PatchU16(offset, 1)); w.ToArray().ShouldBe(new byte[] { 0, 0, 7 }); // nothing was patched } } /// /// R07:L5: works in a buffer rented from and hands /// it back on . The returned array must be the caller's own (a later writer reusing /// the pooled buffer cannot change it), growth must keep every byte, and a writer used after ToArray must refuse /// rather than write into a buffer it no longer owns. /// public sealed class CipWriterPoolingTests { [Fact] public void Growth_past_the_initial_buffer_keeps_every_byte() { var w = new CipWriter(); for (var i = 0; i < 1000; i++) w.U8((byte)i); var bytes = w.ToArray(); bytes.Length.ShouldBe(1000); bytes.Select((b, i) => b == (byte)i).ShouldAllBe(ok => ok); } [Fact] public void A_returned_array_is_not_affected_by_a_later_writer_reusing_the_pooled_buffer() { var first = new CipWriter().U32(0xAABBCCDD).ToArray(); for (var i = 0; i < 50; i++) new CipWriter().U32(0x11111111).U32(0x22222222).ToArray(); first.ShouldBe(new byte[] { 0xDD, 0xCC, 0xBB, 0xAA }); } [Fact] public void ToArray_twice_returns_equal_independent_copies() { var w = new CipWriter().U16(0x0102); var a = w.ToArray(); var b = w.ToArray(); b.ShouldBe(a); b.ShouldNotBeSameAs(a); } [Fact] public void Writing_after_ToArray_is_refused() { var w = new CipWriter().U8(1); _ = w.ToArray(); Should.Throw(() => w.U8(2)); Should.Throw(() => w.PatchU16(0, 1)); w.Length.ShouldBe(1); } [Fact] public void Nested_writers_produce_independent_frames() { // The Execute PCCC / encapsulation builders build an inner writer while an outer one is live. var outer = new CipWriter().U8(0xAA); var inner = new CipWriter().U8(0x01).U8(0x02).ToArray(); var frame = outer.Bytes(inner).U8(0xBB).ToArray(); frame.ShouldBe(new byte[] { 0xAA, 0x01, 0x02, 0xBB }); } }