explicit_seed_seq_test.cc (7727B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/random/internal/explicit_seed_seq.h" 16 17 #include <iterator> 18 #include <random> 19 #include <utility> 20 21 #include "gmock/gmock.h" 22 #include "gtest/gtest.h" 23 #include "absl/random/seed_sequences.h" 24 25 namespace { 26 27 using ::absl::random_internal::ExplicitSeedSeq; 28 29 template <typename Sseq> 30 bool ConformsToInterface() { 31 // Check that the SeedSequence can be default-constructed. 32 { 33 Sseq default_constructed_seq; 34 } 35 // Check that the SeedSequence can be constructed with two iterators. 36 { 37 uint32_t init_array[] = {1, 3, 5, 7, 9}; 38 Sseq iterator_constructed_seq(init_array, &init_array[5]); 39 } 40 // Check that the SeedSequence can be std::initializer_list-constructed. 41 { 42 Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; 43 } 44 // Check that param() and size() return state provided to constructor. 45 { 46 uint32_t init_array[] = {1, 2, 3, 4, 5}; 47 Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]); 48 EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array)); 49 50 uint32_t state_array[ABSL_ARRAYSIZE(init_array)]; 51 seq.param(state_array); 52 53 for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) { 54 EXPECT_EQ(state_array[i], i + 1); 55 } 56 } 57 // Check for presence of generate() method. 58 { 59 Sseq seq; 60 uint32_t seeds[5]; 61 62 seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]); 63 } 64 return true; 65 } 66 } // namespace 67 68 TEST(SeedSequences, CheckInterfaces) { 69 // Control case 70 EXPECT_TRUE(ConformsToInterface<std::seed_seq>()); 71 72 // Abseil classes 73 EXPECT_TRUE(ConformsToInterface<ExplicitSeedSeq>()); 74 } 75 76 TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) { 77 const size_t kNumBlocks = 128; 78 79 uint32_t outputs[kNumBlocks]; 80 ExplicitSeedSeq seq; 81 seq.generate(outputs, &outputs[kNumBlocks]); 82 83 for (uint32_t& seed : outputs) { 84 EXPECT_EQ(seed, 0); 85 } 86 } 87 88 TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) { 89 const size_t kNumBlocks = 128; 90 91 uint32_t seed_material[kNumBlocks]; 92 std::random_device urandom{"/dev/urandom"}; 93 for (uint32_t& seed : seed_material) { 94 seed = urandom(); 95 } 96 ExplicitSeedSeq seq(seed_material, &seed_material[kNumBlocks]); 97 98 // Check that output is same as seed-material provided to constructor. 99 { 100 const size_t kNumGenerated = kNumBlocks / 2; 101 uint32_t outputs[kNumGenerated]; 102 seq.generate(outputs, &outputs[kNumGenerated]); 103 for (size_t i = 0; i < kNumGenerated; i++) { 104 EXPECT_EQ(outputs[i], seed_material[i]); 105 } 106 } 107 // Check that SeedSequence is stateless between invocations: Despite the last 108 // invocation of generate() only consuming half of the input-entropy, the same 109 // entropy will be recycled for the next invocation. 110 { 111 const size_t kNumGenerated = kNumBlocks; 112 uint32_t outputs[kNumGenerated]; 113 seq.generate(outputs, &outputs[kNumGenerated]); 114 for (size_t i = 0; i < kNumGenerated; i++) { 115 EXPECT_EQ(outputs[i], seed_material[i]); 116 } 117 } 118 // Check that when more seed-material is asked for than is provided, nonzero 119 // values are still written. 120 { 121 const size_t kNumGenerated = kNumBlocks * 2; 122 uint32_t outputs[kNumGenerated]; 123 seq.generate(outputs, &outputs[kNumGenerated]); 124 for (size_t i = 0; i < kNumGenerated; i++) { 125 EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]); 126 } 127 } 128 } 129 130 TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { 131 using testing::Each; 132 using testing::Eq; 133 using testing::Not; 134 using testing::Pointwise; 135 136 uint32_t entropy[4]; 137 std::random_device urandom("/dev/urandom"); 138 for (uint32_t& entry : entropy) { 139 entry = urandom(); 140 } 141 ExplicitSeedSeq seq_from_entropy(std::begin(entropy), std::end(entropy)); 142 // Copy constructor. 143 { 144 ExplicitSeedSeq seq_copy(seq_from_entropy); 145 EXPECT_EQ(seq_copy.size(), seq_from_entropy.size()); 146 147 std::vector<uint32_t> seeds_1(1000, 0); 148 std::vector<uint32_t> seeds_2(1000, 1); 149 150 seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); 151 seq_copy.generate(seeds_2.begin(), seeds_2.end()); 152 153 EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); 154 } 155 // Assignment operator. 156 { 157 for (uint32_t& entry : entropy) { 158 entry = urandom(); 159 } 160 ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy)); 161 162 std::vector<uint32_t> seeds_1(1000, 0); 163 std::vector<uint32_t> seeds_2(1000, 0); 164 165 seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); 166 another_seq.generate(seeds_2.begin(), seeds_2.end()); 167 168 // Assert precondition: Sequences generated by seed-sequences are not equal. 169 EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2))); 170 171 // Apply the assignment-operator. 172 // GCC 12 has a false-positive -Wstringop-overflow warning here. 173 #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) 174 #pragma GCC diagnostic push 175 #pragma GCC diagnostic ignored "-Wstringop-overflow" 176 #endif 177 another_seq = seq_from_entropy; 178 #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) 179 #pragma GCC diagnostic pop 180 #endif 181 182 // Re-generate seeds. 183 seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); 184 another_seq.generate(seeds_2.begin(), seeds_2.end()); 185 186 // Seeds generated by seed-sequences should now be equal. 187 EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); 188 } 189 // Move constructor. 190 { 191 // Get seeds from seed-sequence constructed from entropy. 192 std::vector<uint32_t> seeds_1(1000, 0); 193 seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); 194 195 // Apply move-constructor move the sequence to another instance. 196 absl::random_internal::ExplicitSeedSeq moved_seq( 197 std::move(seq_from_entropy)); 198 std::vector<uint32_t> seeds_2(1000, 1); 199 moved_seq.generate(seeds_2.begin(), seeds_2.end()); 200 // Verify that seeds produced by moved-instance are the same as original. 201 EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); 202 203 // Verify that the moved-from instance now behaves like a 204 // default-constructed instance. 205 EXPECT_EQ(seq_from_entropy.size(), 0); 206 seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); 207 EXPECT_THAT(seeds_1, Each(Eq(0))); 208 } 209 } 210 211 TEST(ExplicitSeedSeq, StdURBGGoldenTests) { 212 // Verify that for std::- URBG instances the results are stable across 213 // platforms (these should have deterministic output). 214 { 215 ExplicitSeedSeq seed_sequence{12, 34, 56}; 216 std::minstd_rand rng(seed_sequence); 217 218 std::minstd_rand::result_type values[4] = {rng(), rng(), rng(), rng()}; 219 EXPECT_THAT(values, 220 testing::ElementsAre(579252, 43785881, 464353103, 1501811174)); 221 } 222 223 { 224 ExplicitSeedSeq seed_sequence{12, 34, 56}; 225 std::mt19937 rng(seed_sequence); 226 227 std::mt19937::result_type values[4] = {rng(), rng(), rng(), rng()}; 228 EXPECT_THAT(values, testing::ElementsAre(138416803, 151130212, 33817739, 229 138416803)); 230 } 231 232 { 233 ExplicitSeedSeq seed_sequence{12, 34, 56}; 234 std::mt19937_64 rng(seed_sequence); 235 236 std::mt19937_64::result_type values[4] = {rng(), rng(), rng(), rng()}; 237 EXPECT_THAT(values, 238 testing::ElementsAre(19738651785169348, 1464811352364190456, 239 18054685302720800, 19738651785169348)); 240 } 241 }