Improve ChaCha testing where holes were found with mutation testing with Stryker
12 files changed, 154 insertions(+), 9 deletions(-) M src/RandN/Rngs/ChaCha.cs M src/RandN/Rngs/ChaChaVec128.Sse2.cs M src/RandN/Rngs/ChaChaVec128.cs M src/RandN/Rngs/ChaChaVec256.Avx2.cs M src/RandN/Rngs/ChaChaVec256.cs M src/Tests/Rngs/ChaCha256Tests.cs A => src/Tests/Rngs/ChaChaHelper.cs M src/Tests/Rngs/ChaChaInstrinsicsTests.cs M src/Tests/Rngs/ChaChaSoftwareTests.cs M src/Tests/Rngs/ChaChaTests.cs M src/Tests/Rngs/ChaChaVec128Tests.cs M src/Tests/Tests.csproj
M src/RandN/Rngs/ChaCha.cs +3 -3
@@ 72,8 72,8 @@ public sealed class ChaCha : ISeekableRn var key = seed.Key.Length != 0 ? seed.Key.Span : stackalloc UInt32[KeyLength]; #if X86_INTRINSICS - var core = ChaChaIntrinsics.Create(key, UInt64.MaxValue, seed.Stream, doubleRounds); - var blockBuffer = new BlockBuffer32<ChaChaIntrinsics, UInt64>(core); + var core = ChaChaIntrinsics.Create(key, UInt64.MaxValue, seed.Stream, doubleRounds); + var blockBuffer = new BlockBuffer32<ChaChaIntrinsics, UInt64>(core); #else var core = ChaChaSoftware.Create(key, UInt64.MaxValue, seed.Stream, doubleRounds); var blockBuffer = new BlockBuffer32<ChaChaSoftware, UInt64>(core); @@ 239,4 239,4 @@ public sealed class ChaCha : ISeekableRn /// </summary> public UInt32 Word { get; } } -} No newline at end of file +}
M src/RandN/Rngs/ChaChaVec128.Sse2.cs +1 -1
@@ 125,7 125,7 @@ internal sealed class ChaChaVec128 : ISe { fixed (UInt32* ptr = destination) { - Sse2.Store(ptr + 0, out0); + Sse2.Store(ptr, out0); Sse2.Store(ptr + 4, out1); Sse2.Store(ptr + 8, out2); Sse2.Store(ptr + 12, out3);
M src/RandN/Rngs/ChaChaVec128.cs +1 -1
@@ 125,7 125,7 @@ internal sealed class ChaChaVec128 : ISe fixed (UInt32* ptr = destination) { // Can we use StoreAligned here? Would that be beneficial? - out0.Store(ptr + 0); + out0.Store(ptr); out1.Store(ptr + 4); out2.Store(ptr + 8); out3.Store(ptr + 12);
M src/RandN/Rngs/ChaChaVec256.Avx2.cs +1 -1
@@ 128,7 128,7 @@ internal sealed class ChaChaVec256 : ISe { fixed (UInt32* ptr = destination) { - Sse2.Store(ptr + 0, out0.GetLower()); + Sse2.Store(ptr, out0.GetLower()); Sse2.Store(ptr + 4, out1.GetLower()); Sse2.Store(ptr + 8, out2.GetLower()); Sse2.Store(ptr + 12, out3.GetLower());
M src/RandN/Rngs/ChaChaVec256.cs +1 -1
@@ 127,7 127,7 @@ internal sealed class ChaChaVec256 : ISe { fixed (UInt32* ptr = destination) { - out0.GetLower().Store(ptr + 0); + out0.GetLower().Store(ptr); out1.GetLower().Store(ptr + 4); out2.GetLower().Store(ptr + 8); out3.GetLower().Store(ptr + 12);
M src/Tests/Rngs/ChaCha256Tests.cs +19 -0
@@ 1,5 1,6 @@ #if X86_INTRINSICS using System; +using RandN.Implementation; using Xunit; namespace RandN.Rngs @@ 78,6 79,24 @@ namespace RandN.Rngs for (Int32 i = 0; i < buffer.Length; i++) Assert.NotEqual(buffer[i], buffer2[i]); + Assert.Equal(1ul, rng.Stream); + } + + [Vec256HwAccelRequiredFact] + public void BlockLength() + { + var rng = ChaChaVec256.Create(new UInt32[] { 0, 0, 1, 0, 2, 0, 3, 0 }, UInt64.MaxValue, 0, 4); + Assert.Equal(ChaCha.BufferLength, rng.BlockLength); + } + + [Vec128HwAccelRequiredFact] + public void Generate1Kilobyte() + { + var rng = ChaChaVec256.Create(new UInt32[] { 0, 0, 0, 0, 0, 0, 0, 0 }, UInt64.MaxValue, 0, 10); + var blockBuffer = new BlockBuffer32<ChaChaVec256, UInt64>(rng); + + foreach (var expected in ChaChaHelper.ChaCha20Output1Kibibyte) + Assert.Equal(expected, blockBuffer.NextUInt32()); } } }
A => src/Tests/Rngs/ChaChaHelper.cs +49 -0
@@ 0,0 1,49 @@ +using System; +using System.Collections.Immutable; + +namespace RandN.Rngs; + +/// <summary> +/// Helpers for ChaCha tests. +/// </summary> +public static class ChaChaHelper +{ + /// <summary> + /// 1024 bytes of output from ChaCha20 with a seed of 0, stream of 0, beginning at position 0. + /// </summary> + public static ImmutableArray<UInt32> ChaCha20Output1Kibibyte { get; } = new UInt32[] + { + 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, + 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2, + 0xbee7079f, 0x7a385155, 0x7c97ba98, 0xd082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, + 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b, + 0xe6a0092d, 0xe16c2663, 0x8d17eae, 0x75a06819, 0x998e718e, 0xc662d37b, 0x3446c3b0, 0x5db3a0a9, + 0x68372701, 0xf5d7b1f, 0xfd3a1e28, 0x1ebc58e4, 0x13d3d273, 0xc094cfc9, 0x6271f35f, 0xf248a240, + 0x58a02013, 0x6b56b3d7, 0xaada20d5, 0xabfd23e, 0x20b1b8c5, 0x732785fb, 0x349763c3, 0xa4915cb4, + 0x83cbd42d, 0x2e0d84f8, 0x1358b1ed, 0x3fac6210, 0xfff82c1f, 0x5618cd6d, 0x6c1e6ae8, 0x7e166731, + 0x7488a6e5, 0xadc5472b, 0xdfd459fb, 0xb11dfd76, 0x3be01ee5, 0x2af8a91c, 0xdb3e17ca, 0x4793728b, + 0xf98be4e, 0xc9104d90, 0x472b4416, 0x84e9a083, 0xc9b60c86, 0x389cb357, 0xcf518fed, 0x4d8aa6fa, + 0xa32510e0, 0x4645509c, 0x614dcb9, 0x1528eba7, 0xd750511e, 0xa7ba04b2, 0x91f0d419, 0xdb171202, + 0xc8b5f15c, 0x1aa74f4c, 0xa1109687, 0x52ac95a6, 0x77565b7c, 0x218a6b4a, 0x8586e8aa, 0x4c098e86, + 0x9f49ef2, 0xca9f70a, 0x17887ec0, 0x638752aa, 0x333c7d79, 0x4bca672b, 0x2c6410c1, 0x47ec5121, + 0x8ccb84ee, 0x105fd842, 0x18cba8e2, 0x5f33b7c3, 0x9ac3e826, 0xc1bcb112, 0xb7777170, 0x2e733861, + 0x4db7aaed, 0xc00f41a1, 0x8c06ea55, 0xa26e999, 0xcf37e3cb, 0xe5003e5d, 0xfe0f23b3, 0x7990bdb, + 0xec7d087, 0x9841fe0b, 0xdd5867ea, 0x5ffb615a, 0x81f92dec, 0xe1ef1bf3, 0x171df853, 0xdb841716, + 0xd522881c, 0x7deed13c, 0x483632b5, 0x4f4bd28, 0xdca840b0, 0xd3f322c5, 0x4bec9ad9, 0xb8ed5780, + 0xa2310950, 0xc2f2dc4, 0x10470857, 0xda54570b, 0xb8bd5ffc, 0x1aefbb94, 0x7fa0e12d, 0xb9c4a08b, + 0x66103019, 0x6b05bced, 0x7a1e487b, 0x7b29460c, 0x9d9d58bb, 0xa675b6a5, 0x2e153e72, 0xcea4635e, + 0x839e4e03, 0x3a018ae5, 0x2f35e7f0, 0x148590b7, 0x4d1b3e3, 0x63b90b0d, 0x634b95b3, 0xbfd45f6b, + 0xbaad0a6d, 0x67d15f8, 0x1824cb2a, 0x75a476c1, 0xc3351b51, 0x568a21f6, 0xc65bea68, 0x82874bf5, + 0xf040b3f8, 0xbabec10a, 0x63cd625e, 0x80e77c2a, 0x856729c, 0xbfefa5ac, 0x37f2417c, 0xc0063f64, + 0x17077299, 0xf967e81d, 0x5ebf97d6, 0xbc1a01a6, 0xdb8c6cce, 0xd2941321, 0xfbd02dc0, 0x2c5adb60, + 0xc83dac17, 0xba97858, 0xdb0938ed, 0x54aa6eb9, 0xae8efc26, 0xc4652d0d, 0x89f472a, 0x2dbe4886, + 0x2ad801c8, 0xc0dd6f36, 0x634223ef, 0x7d41b6c0, 0x18a49d5f, 0x688db817, 0x9571e6e5, 0x30eec1c5, + 0xf221e895, 0xbb22425, 0x59eb1ce4, 0x1de41204, 0x3f8448c6, 0x7aecbfa9, 0xab61cf3d, 0x33574105, + 0x81fad316, 0x3936251, 0x564197fe, 0xdb65d02e, 0x5000bc4e, 0x648355ef, 0x4a1281ae, 0x13c0f528, + 0xbc2f2313, 0x8afd6d49, 0x7b656825, 0x14726d68, 0x1a2a38, 0xdd173090, 0x848769a9, 0xff5aba42, + 0x553f61f6, 0x3c23bb3c, 0xee9a6de4, 0x6c87a793, 0x29e8e9f5, 0xad8cb112, 0x2743b3f0, 0x7e42e0b2, + 0xceb766cf, 0x8d91c0b7, 0xf1df7bc4, 0xdf2a062a, 0x9301307, 0x5c5e7ace, 0x68017e91, 0xb7096130, + 0x3a6549cb, 0xf0ae2c6d, 0x3a78de05, 0x5fe9b9a, 0x34d11e38, 0x65ec948d, 0xb9c6f88, 0xc5529c61, + 0xb1003853, 0x7261836c, 0xdb8251b9, 0x42c0eec5, 0xf1229eb8, 0x735b081a, 0xcd11369a, 0x1860838d, + }.ToImmutableArray(); +}
M src/Tests/Rngs/ChaChaInstrinsicsTests.cs +12 -0
@@ 1,5 1,6 @@ #if X86_INTRINSICS using System; +using RandN.Implementation; using Xunit; namespace RandN.Rngs @@ 78,6 79,7 @@ namespace RandN.Rngs for (Int32 i = 0; i < buffer.Length; i++) Assert.NotEqual(buffer[i], buffer2[i]); + Assert.Equal(1ul, rng.Stream); } [Fact] @@ 86,6 88,16 @@ namespace RandN.Rngs var rng = ChaChaIntrinsics.Create(new UInt32[] { 0, 0, 1, 0, 2, 0, 3, 0 }, UInt64.MaxValue, 0, 4); Assert.Equal(ChaCha.BufferLength, rng.BlockLength); } + + [Fact] + public void Generate1Kilobyte() + { + var rng = ChaChaIntrinsics.Create(new UInt32[] { 0, 0, 0, 0, 0, 0, 0, 0 }, UInt64.MaxValue, 0, 10); + var blockBuffer = new BlockBuffer32<ChaChaIntrinsics, UInt64>(rng); + + foreach (var expected in ChaChaHelper.ChaCha20Output1Kibibyte) + Assert.Equal(expected, blockBuffer.NextUInt32()); + } } } #endif
M src/Tests/Rngs/ChaChaSoftwareTests.cs +13 -1
@@ 1,4 1,5 @@ using System; +using RandN.Implementation; using Xunit; namespace RandN.Rngs; @@ 77,6 78,7 @@ public sealed class ChaChaSoftwareTests for (Int32 i = 0; i < buffer.Length; i++) Assert.NotEqual(buffer[i], buffer2[i]); + Assert.Equal(1ul, rng.Stream); } [Fact] @@ 85,4 87,14 @@ public sealed class ChaChaSoftwareTests var rng = ChaChaSoftware.Create(new UInt32[] { 0, 0, 1, 0, 2, 0, 3, 0 }, UInt64.MaxValue, 0, 4); Assert.Equal(ChaCha.BufferLength, rng.BlockLength); } -} No newline at end of file + + [Fact] + public void Generate1Kilobyte() + { + var rng = ChaChaSoftware.Create(new UInt32[] { 0, 0, 0, 0, 0, 0, 0, 0 }, UInt64.MaxValue, 0, 10); + var blockBuffer = new BlockBuffer32<ChaChaSoftware, UInt64>(rng); + + foreach (var expected in ChaChaHelper.ChaCha20Output1Kibibyte) + Assert.Equal(expected, blockBuffer.NextUInt32()); + } +}
M src/Tests/Rngs/ChaChaTests.cs +34 -1
@@ 169,6 169,39 @@ public sealed class ChaChaTests Assert.Equal(expected, rng1.NextUInt32()); } + /// <summary> + /// 256 words generated from the empty seed (all zeros) + /// </summary> + [Fact] + public void Generate1Kilobyte() + { + var seed1 = new Seed(); + var rng1 = ChaCha.Create(seed1); + + foreach (var expected in ChaChaHelper.ChaCha20Output1Kibibyte) + Assert.Equal(expected, rng1.NextUInt32()); + } + + [Fact] + public void CounterWordWrapping() + { + var seed = new Seed(); + var rng1 = ChaCha.Create(seed); + var rng2 = ChaCha.Create(seed); + + rng1.Position = new ChaCha.Counter(1234, 0); + rng2.Position = new ChaCha.Counter(1233, 16); + + Assert.Equal(rng1.Position, rng2.Position); + for (var i = 0; i < 128; i++) + { + var expected = rng1.NextUInt32(); + var actual = rng2.NextUInt32(); + Assert.Equal(expected, actual); + } + Assert.Equal(rng1.Position, rng2.Position); + } + [Fact] public void BadArgs() { @@ 184,4 217,4 @@ public sealed class ChaChaTests Assert.Throws<ArgumentNullException>(() => ChaCha.GetChaCha12Factory().CreateSeed<StepRng>(null)); Assert.Throws<ArgumentNullException>(() => ChaCha.GetChaCha20Factory().CreateSeed<StepRng>(null)); } -} No newline at end of file +}
M src/Tests/Rngs/ChaChaVec128Tests.cs +19 -0
@@ 1,5 1,6 @@ #if X86_INTRINSICS using System; +using RandN.Implementation; using Xunit; namespace RandN.Rngs @@ 78,6 79,24 @@ namespace RandN.Rngs for (Int32 i = 0; i < buffer.Length; i++) Assert.NotEqual(buffer[i], buffer2[i]); + Assert.Equal(1ul, rng.Stream); + } + + [Vec128HwAccelRequiredFact] + public void BlockLength() + { + var rng = ChaChaVec128.Create(new UInt32[] { 0, 0, 1, 0, 2, 0, 3, 0 }, UInt64.MaxValue, 0, 4); + Assert.Equal(ChaCha.BufferLength, rng.BlockLength); + } + + [Vec128HwAccelRequiredFact] + public void Generate1Kilobyte() + { + var rng = ChaChaVec128.Create(new UInt32[] { 0, 0, 0, 0, 0, 0, 0, 0 }, UInt64.MaxValue, 0, 10); + var blockBuffer = new BlockBuffer32<ChaChaVec128, UInt64>(rng); + + foreach (var expected in ChaChaHelper.ChaCha20Output1Kibibyte) + Assert.Equal(expected, blockBuffer.NextUInt32()); } } }
M src/Tests/Tests.csproj +1 -0