From e5f10f2667634c83ea2bd95e0298cc5ae8cf1b9b Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Sat, 13 Jun 2026 00:21:55 -0400 Subject: [PATCH] test(engine): pin audio FourCC constants to ASCII packing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds regression tests that would have caught the PCMs16 FourCC typo (0x73334d50 'PM3s' vs correct 0x734D4350 'PCMs'). The prior tests referenced the symbol on both sides of the assertion, so a wrong literal stayed self-consistent and green. These new tests: 1. Assert each public FourCC constant equals the little-endian ASCII packing of its documented characters (the same convention the NDI SDK and the video FourCCs use). 2. Feed a raw PCM16 buffer tagged with the *literal* 0x734D4350 and assert the peak is decoded — proving a real Teams legacy sender's FourCC routes to the int16 decoder rather than the unknown branch. --- .../Pipeline/AudioPeakComputerTests.cs | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/AudioPeakComputerTests.cs b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/AudioPeakComputerTests.cs index 382af71..52a43f0 100644 --- a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/AudioPeakComputerTests.cs +++ b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/AudioPeakComputerTests.cs @@ -22,7 +22,7 @@ public class AudioPeakComputerTests [Fact] public void UnknownFourCC_ReturnsZero_RatherThanThrow() { - // Receiver loop must never crash on an unrecognized format — better to + // Receiver loop must never crash on an unrecognized format — better to // show silence on the meter than to take down the pipeline. var floats = new[] { 0.5f, -0.5f }; var bytes = AsBytes(floats); @@ -77,7 +77,7 @@ public class AudioPeakComputerTests public void Fltp_TotalSamplesSmallerThanBuffer_OnlyConsumesReportedRange() { // The reported range covers only the first 3 floats. The 4th - // (largest) is past `totalSamples` and must be ignored — otherwise we'd + // (largest) is past `totalSamples` and must be ignored — otherwise we'd // be reading beyond what the source said it wrote. var floats = new[] { 0.1f, -0.2f, 0.3f, 0.99f }; var bytes = AsBytes(floats); @@ -128,10 +128,59 @@ public class AudioPeakComputerTests var samples = new[] { (short)0, (short)16384, (short)-16383 }; var bytes = AsBytes(samples); var peak = AudioPeakComputer.ComputePeak(bytes, AudioPeakComputer.FourCC_PCMs16, samples.Length); - // 16384 / 32767 ≈ 0.500015; tolerate small precision drift. + // 16384 / 32767 ≈ 0.500015; tolerate small precision drift. Assert.InRange(peak, 0.49, 0.51); } + // ============================================================ + // FourCC constant integrity — regression guard + // ============================================================ + // + // The NDI SDK packs FourCCs little-endian: the first ASCII character + // lands in the least-significant byte. These tests pin each public + // constant to the little-endian packing of its documented characters, + // so a transposed/typo'd literal (the PCMs16 0x73334d50 → 0x734D4350 + // fix) fails loudly instead of silently routing real audio to the + // "unknown format → 0.0" branch. + + [Fact] + public void FourCC_FLTP_MatchesAsciiPacking() + { + AudioPeakComputer.FourCC_FLTP.Should().Be(Pack('F', 'L', 'T', 'p')); + } + + [Fact] + public void FourCC_FLT_MatchesAsciiPacking() + { + AudioPeakComputer.FourCC_FLT.Should().Be(Pack('F', 'L', 'T', ' ')); + } + + [Fact] + public void FourCC_PCMs16_MatchesAsciiPacking() + { + // Regression: was 0x73334d50 ('P','M','3','s'), should be 'P','C','M','s'. + AudioPeakComputer.FourCC_PCMs16.Should().Be(Pack('P', 'C', 'M', 's')); + AudioPeakComputer.FourCC_PCMs16.Should().Be(0x734D4350u); + } + + [Fact] + public void Pcms16_DecodedViaLiteralFourCC_NotJustSymbol() + { + // Belt-and-braces: feed a PCM16 buffer tagged with the *literal* FourCC + // a real legacy Teams/NDI sender emits, not the symbol. If the constant + // ever drifts from the wire value again, this peak collapses to 0.0 + // because ComputePeak falls through to the unknown branch. + const uint wirePcms16 = 0x734D4350; // little-endian 'P','C','M','s' + var samples = new[] { (short)0, (short)16384, (short)-200 }; + var bytes = AsBytes(samples); + var peak = AudioPeakComputer.ComputePeak(bytes, wirePcms16, samples.Length); + peak.Should().BeInRange(0.49, 0.51); + } + + /// Little-endian ASCII FourCC packing: first char in the low byte. + private static uint Pack(char a, char b, char c, char d) => + (uint)a | ((uint)b << 8) | ((uint)c << 16) | ((uint)d << 24); + private static byte[] AsBytes(T[] arr) where T : struct { var span = MemoryMarshal.Cast(arr.AsSpan());