1 | /* |
2 | * Copyright 2012-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/Random.h> |
18 | |
19 | #include <algorithm> |
20 | #include <random> |
21 | #include <thread> |
22 | #include <unordered_set> |
23 | #include <vector> |
24 | |
25 | #include <glog/logging.h> |
26 | |
27 | #include <folly/portability/GTest.h> |
28 | |
29 | using namespace folly; |
30 | |
31 | TEST(Random, StateSize) { |
32 | using namespace folly::detail; |
33 | |
34 | // uint_fast32_t is uint64_t on x86_64, w00t |
35 | EXPECT_EQ( |
36 | sizeof(uint_fast32_t) / 4 + 3, StateSizeT<std::minstd_rand0>::value); |
37 | EXPECT_EQ(624, StateSizeT<std::mt19937>::value); |
38 | #if FOLLY_HAVE_EXTRANDOM_SFMT19937 |
39 | EXPECT_EQ(624, StateSizeT<__gnu_cxx::sfmt19937>::value); |
40 | #endif |
41 | EXPECT_EQ(24, StateSizeT<std::ranlux24_base>::value); |
42 | } |
43 | |
44 | TEST(Random, Simple) { |
45 | uint32_t prev = 0, seed = 0; |
46 | for (int i = 0; i < 1024; ++i) { |
47 | EXPECT_NE(seed = randomNumberSeed(), prev); |
48 | prev = seed; |
49 | } |
50 | } |
51 | |
52 | TEST(Random, FixedSeed) { |
53 | // clang-format off |
54 | struct ConstantRNG { |
55 | typedef uint32_t result_type; |
56 | result_type operator()() { |
57 | return 4; // chosen by fair dice roll. |
58 | // guaranteed to be random. |
59 | } |
60 | static constexpr result_type min() { |
61 | return std::numeric_limits<result_type>::min(); |
62 | } |
63 | static constexpr result_type max() { |
64 | return std::numeric_limits<result_type>::max(); |
65 | } |
66 | }; |
67 | // clang-format on |
68 | |
69 | ConstantRNG gen; |
70 | |
71 | // Pick a constant random number... |
72 | auto value = Random::rand32(10, gen); |
73 | |
74 | // Loop to make sure it really is constant. |
75 | for (int i = 0; i < 1024; ++i) { |
76 | auto result = Random::rand32(10, gen); |
77 | EXPECT_EQ(value, result); |
78 | } |
79 | } |
80 | |
81 | TEST(Random, MultiThreaded) { |
82 | const int n = 100; |
83 | std::vector<uint32_t> seeds(n); |
84 | std::vector<std::thread> threads; |
85 | for (int i = 0; i < n; ++i) { |
86 | threads.push_back( |
87 | std::thread([i, &seeds] { seeds[i] = randomNumberSeed(); })); |
88 | } |
89 | for (auto& t : threads) { |
90 | t.join(); |
91 | } |
92 | std::sort(seeds.begin(), seeds.end()); |
93 | for (int i = 0; i < n - 1; ++i) { |
94 | EXPECT_LT(seeds[i], seeds[i + 1]); |
95 | } |
96 | } |
97 | |
98 | TEST(Random, sanity) { |
99 | // edge cases |
100 | EXPECT_EQ(folly::Random::rand32(0), 0); |
101 | EXPECT_EQ(folly::Random::rand32(12, 12), 0); |
102 | EXPECT_EQ(folly::Random::rand64(0), 0); |
103 | EXPECT_EQ(folly::Random::rand64(12, 12), 0); |
104 | |
105 | // 32-bit repeatability, uniqueness |
106 | constexpr int kTestSize = 1000; |
107 | { |
108 | std::vector<uint32_t> vals; |
109 | folly::Random::DefaultGenerator rng; |
110 | rng.seed(0xdeadbeef); |
111 | for (int i = 0; i < kTestSize; ++i) { |
112 | vals.push_back(folly::Random::rand32(rng)); |
113 | } |
114 | rng.seed(0xdeadbeef); |
115 | for (int i = 0; i < kTestSize; ++i) { |
116 | EXPECT_EQ(vals[i], folly::Random::rand32(rng)); |
117 | } |
118 | EXPECT_EQ( |
119 | vals.size(), |
120 | std::unordered_set<uint32_t>(vals.begin(), vals.end()).size()); |
121 | } |
122 | |
123 | // 64-bit repeatability, uniqueness |
124 | { |
125 | std::vector<uint64_t> vals; |
126 | folly::Random::DefaultGenerator rng; |
127 | rng.seed(0xdeadbeef); |
128 | for (int i = 0; i < kTestSize; ++i) { |
129 | vals.push_back(folly::Random::rand64(rng)); |
130 | } |
131 | rng.seed(0xdeadbeef); |
132 | for (int i = 0; i < kTestSize; ++i) { |
133 | EXPECT_EQ(vals[i], folly::Random::rand64(rng)); |
134 | } |
135 | EXPECT_EQ( |
136 | vals.size(), |
137 | std::unordered_set<uint64_t>(vals.begin(), vals.end()).size()); |
138 | } |
139 | } |
140 | |
141 | #ifndef _WIN32 |
142 | TEST(Random, SecureFork) { |
143 | // Random buffer size is 128, must be less than that. |
144 | int retries = 100; |
145 | while (true) { |
146 | unsigned char buffer = 0; |
147 | // Init random buffer |
148 | folly::Random::secureRandom(&buffer, 1); |
149 | |
150 | auto pid = fork(); |
151 | EXPECT_NE(pid, -1); |
152 | if (pid) { |
153 | // parent |
154 | int status = 0; |
155 | folly::Random::secureRandom(&buffer, 1); |
156 | auto pid2 = wait(&status); |
157 | EXPECT_EQ(pid, pid2); |
158 | // Since this *is* random data, we could randomly end up with |
159 | // the same byte. Try again a few times if so before assuming |
160 | // real failure. |
161 | if (WEXITSTATUS(status) == buffer && retries-- > 0) { |
162 | continue; |
163 | } |
164 | EXPECT_NE(WEXITSTATUS(status), buffer); |
165 | return; |
166 | } else { |
167 | // child |
168 | folly::Random::secureRandom(&buffer, 1); |
169 | exit(buffer); // Do not print gtest results |
170 | } |
171 | } |
172 | } |
173 | #endif |
174 | |