about summary refs log tree commit diff
path: root/absl/random/internal/explicit_seed_seq_test.cc
blob: a55ad73948b93cf0c55899b2d5c1396ad74a5ee0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "absl/random/internal/explicit_seed_seq.h"

#include <iterator>
#include <random>
#include <utility>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/random/seed_sequences.h"

namespace {

template <typename Sseq>
bool ConformsToInterface() {
  // Check that the SeedSequence can be default-constructed.
  { Sseq default_constructed_seq; }
  // Check that the SeedSequence can be constructed with two iterators.
  {
    uint32_t init_array[] = {1, 3, 5, 7, 9};
    Sseq iterator_constructed_seq(init_array, &init_array[5]);
  }
  // Check that the SeedSequence can be std::initializer_list-constructed.
  { Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; }
  // Check that param() and size() return state provided to constructor.
  {
    uint32_t init_array[] = {1, 2, 3, 4, 5};
    Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]);
    EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));

    uint32_t state_array[ABSL_ARRAYSIZE(init_array)];
    seq.param(state_array);

    for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) {
      EXPECT_EQ(state_array[i], i + 1);
    }
  }
  // Check for presence of generate() method.
  {
    Sseq seq;
    uint32_t seeds[5];

    seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]);
  }
  return true;
}
}  // namespace

TEST(SeedSequences, CheckInterfaces) {
  // Control case
  EXPECT_TRUE(ConformsToInterface<std::seed_seq>());

  // Abseil classes
  EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>());
}

TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) {
  const size_t kNumBlocks = 128;

  uint32_t outputs[kNumBlocks];
  absl::random_internal::ExplicitSeedSeq seq;
  seq.generate(outputs, &outputs[kNumBlocks]);

  for (uint32_t& seed : outputs) {
    EXPECT_EQ(seed, 0);
  }
}

TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) {
  const size_t kNumBlocks = 128;

  uint32_t seed_material[kNumBlocks];
  std::random_device urandom{"/dev/urandom"};
  for (uint32_t& seed : seed_material) {
    seed = urandom();
  }
  absl::random_internal::ExplicitSeedSeq seq(seed_material,
                                             &seed_material[kNumBlocks]);

  // Check that output is same as seed-material provided to constructor.
  {
    const size_t kNumGenerated = kNumBlocks / 2;
    uint32_t outputs[kNumGenerated];
    seq.generate(outputs, &outputs[kNumGenerated]);
    for (size_t i = 0; i < kNumGenerated; i++) {
      EXPECT_EQ(outputs[i], seed_material[i]);
    }
  }
  // Check that SeedSequence is stateless between invocations: Despite the last
  // invocation of generate() only consuming half of the input-entropy, the same
  // entropy will be recycled for the next invocation.
  {
    const size_t kNumGenerated = kNumBlocks;
    uint32_t outputs[kNumGenerated];
    seq.generate(outputs, &outputs[kNumGenerated]);
    for (size_t i = 0; i < kNumGenerated; i++) {
      EXPECT_EQ(outputs[i], seed_material[i]);
    }
  }
  // Check that when more seed-material is asked for than is provided, nonzero
  // values are still written.
  {
    const size_t kNumGenerated = kNumBlocks * 2;
    uint32_t outputs[kNumGenerated];
    seq.generate(outputs, &outputs[kNumGenerated]);
    for (size_t i = 0; i < kNumGenerated; i++) {
      EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]);
    }
  }
}

TEST(ExplicitSeedSeq, CopyAndMoveConstructors) {
  using testing::Each;
  using testing::Eq;
  using testing::Not;
  using testing::Pointwise;

  uint32_t entropy[4];
  std::random_device urandom("/dev/urandom");
  for (uint32_t& entry : entropy) {
    entry = urandom();
  }
  absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy),
                                                          std::end(entropy));
  // Copy constructor.
  {
    absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy);
    EXPECT_EQ(seq_copy.size(), seq_from_entropy.size());

    std::vector<uint32_t> seeds_1;
    seeds_1.resize(1000, 0);
    std::vector<uint32_t> seeds_2;
    seeds_2.resize(1000, 1);

    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
    seq_copy.generate(seeds_2.begin(), seeds_2.end());

    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
  }
  // Assignment operator.
  {
    for (uint32_t& entry : entropy) {
      entry = urandom();
    }
    absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy),
                                                       std::end(entropy));

    std::vector<uint32_t> seeds_1;
    seeds_1.resize(1000, 0);
    std::vector<uint32_t> seeds_2;
    seeds_2.resize(1000, 0);

    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
    another_seq.generate(seeds_2.begin(), seeds_2.end());

    // Assert precondition: Sequences generated by seed-sequences are not equal.
    EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2)));

    // Apply the assignment-operator.
    another_seq = seq_from_entropy;

    // Re-generate seeds.
    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
    another_seq.generate(seeds_2.begin(), seeds_2.end());

    // Seeds generated by seed-sequences should now be equal.
    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
  }
  // Move constructor.
  {
    // Get seeds from seed-sequence constructed from entropy.
    std::vector<uint32_t> seeds_1;
    seeds_1.resize(1000, 0);
    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());

    // Apply move-constructor move the sequence to another instance.
    absl::random_internal::ExplicitSeedSeq moved_seq(
        std::move(seq_from_entropy));
    std::vector<uint32_t> seeds_2;
    seeds_2.resize(1000, 1);
    moved_seq.generate(seeds_2.begin(), seeds_2.end());
    // Verify that seeds produced by moved-instance are the same as original.
    EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));

    // Verify that the moved-from instance now behaves like a
    // default-constructed instance.
    EXPECT_EQ(seq_from_entropy.size(), 0);
    seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
    EXPECT_THAT(seeds_1, Each(Eq(0)));
  }
}