using System; using System.IO; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Stability; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Tests { [Trait("Category", "Unit")] public sealed class PostMortemMmfTests : IDisposable { private readonly string _tempPath; public PostMortemMmfTests() { _tempPath = Path.Combine(Path.GetTempPath(), $"focas-mmf-{Guid.NewGuid():N}.bin"); } public void Dispose() { if (File.Exists(_tempPath)) File.Delete(_tempPath); } [Fact] public void Write_and_read_preserve_order_and_content() { using (var mmf = new PostMortemMmf(_tempPath, capacity: 10)) { mmf.Write(opKind: 1, "read R100"); mmf.Write(opKind: 2, "write MACRO:500 = 3.14"); mmf.Write(opKind: 3, "probe ok"); } // Reopen (simulating a reader after the writer crashed). using var reader = new PostMortemMmf(_tempPath, capacity: 10); var entries = reader.ReadAll(); entries.Length.ShouldBe(3); entries[0].OpKind.ShouldBe(1L); entries[0].Message.ShouldBe("read R100"); entries[1].OpKind.ShouldBe(2L); entries[2].Message.ShouldBe("probe ok"); } [Fact] public void Ring_buffer_wraps_at_capacity() { using var mmf = new PostMortemMmf(_tempPath, capacity: 3); for (var i = 0; i < 10; i++) mmf.Write(i, $"op-{i}"); var entries = mmf.ReadAll(); entries.Length.ShouldBe(3); // Oldest surviving entry is op-7 (entries 7,8,9 survive in FIFO order). entries[0].Message.ShouldBe("op-7"); entries[1].Message.ShouldBe("op-8"); entries[2].Message.ShouldBe("op-9"); } [Fact] public void Truncated_message_is_null_terminated_and_does_not_overflow() { using var mmf = new PostMortemMmf(_tempPath, capacity: 4); var big = new string('x', 500); // longer than the 240-byte message capacity mmf.Write(42, big); var entries = mmf.ReadAll(); entries.Length.ShouldBe(1); entries[0].Message.Length.ShouldBeLessThanOrEqualTo(240); entries[0].OpKind.ShouldBe(42L); } [Fact] public void Reopening_with_existing_data_preserves_entries() { using (var first = new PostMortemMmf(_tempPath, capacity: 5)) { first.Write(1, "first-run-1"); first.Write(2, "first-run-2"); } using var second = new PostMortemMmf(_tempPath, capacity: 5); var entries = second.ReadAll(); entries.Length.ShouldBe(2); entries[0].Message.ShouldBe("first-run-1"); } } }