20398c43b686 — Michael Johnson 2 years ago
Improve ChaCha testing where holes were found with mutation testing with Stryker
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
@@ 33,6 33,7 @@ 
 
   <ItemGroup Condition="'$(TargetFramework)'=='net48'">
     <PackageReference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel" Version="14.0.0" />
+    <PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
   </ItemGroup>
 
   <ItemGroup>