The Battle for Wesnoth  1.19.6+dev
test_rng.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-test"
17 
18 #include <boost/test/unit_test.hpp>
19 #include "random_synced.hpp"
20 #include "random_deterministic.hpp"
21 #include "config.hpp"
22 #include <sstream>
23 #include <iomanip>
24 
26 
27 /* this test adapted from validation routine at
28  http://www.boost.org/doc/libs/1_38_0/libs/random/random_test.cpp
29 */
30 BOOST_AUTO_TEST_CASE( validate_mt19937 )
31 {
32  std::mt19937 rng;
33  for (int i = 0; i < 9999 ; i++) {
34  // silence C4834 warning in MSVC
35  static_cast<void>(rng());
36  }
37  unsigned long val = rng();
38  BOOST_CHECK_EQUAL( val , 4123659995U );
39 }
40 
41 /* this test checks the soundness of mt_rng string manipulations */
42 BOOST_AUTO_TEST_CASE( test_mt_rng_seed_manip )
43 {
44  uint32_t seed = 42;
45  std::stringstream stream;
46  stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
47 
48  std::string seed_str = stream.str();
49 
51  rng.seed_random(seed_str);
52 
53  BOOST_CHECK (rng.get_random_seed() == seed);
54  BOOST_CHECK (rng.get_random_seed_str() == seed_str);
55 
56  std::string seed_str2 = rng.get_random_seed_str();
57  rng.seed_random(seed_str2);
58 
59  BOOST_CHECK (rng.get_random_seed() == seed);
60  BOOST_CHECK (rng.get_random_seed_str() == seed_str);
61 
62 
63  uint32_t seed3 = 1123581321; //try the same with a different number
64  std::stringstream stream2;
65  stream2 << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed3;
66  std::string seed_str3 = stream2.str();
67 
68  rng.seed_random(seed_str3);
69  BOOST_CHECK (rng.get_random_seed() == seed3);
70  BOOST_CHECK (rng.get_random_seed_str() == seed_str3);
71 
72  std::string seed_str4 = rng.get_random_seed_str();
73  rng.seed_random(seed_str4);
74 
75  BOOST_CHECK (rng.get_random_seed() == seed3);
76  BOOST_CHECK (rng.get_random_seed_str() == seed_str3);
77 
78 
79  //now check that the results that shouldn't match don't
80  BOOST_CHECK (seed != seed3);
81  BOOST_CHECK (seed_str != seed_str3);
82 
83 }
84 
85 BOOST_AUTO_TEST_CASE( test_mt_rng_config_seed_manip )
86 {
87  uint32_t seed = 42;
88  std::stringstream stream;
89  stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
90  std::string seed_str = stream.str();
91 
92  config cfg;
93  cfg["random_seed"] = seed_str;
94  cfg["random_calls"] = 0;
95 
96  randomness::mt_rng rng(cfg);
97 
98  BOOST_CHECK (rng.get_random_seed() == seed);
99  BOOST_CHECK (rng.get_random_seed_str() == seed_str);
100 
101  std::string seed_str2 = rng.get_random_seed_str();
102  rng.seed_random(seed_str2);
103 
104  BOOST_CHECK (rng.get_random_seed() == seed);
105  BOOST_CHECK (rng.get_random_seed_str() == seed_str);
106 
107 
108  uint32_t seed3 = 1123581321; //try the same with a different number
109  std::stringstream stream2;
110  stream2 << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed3;
111  std::string seed_str3 = stream2.str();
112 
113  config cfg2;
114  cfg2["random_seed"] = seed_str3;
115  cfg2["random_calls"] = 0;
116 
117  randomness::mt_rng rng2(cfg2);
118 
119  BOOST_CHECK (rng2.get_random_seed() == seed3);
120  BOOST_CHECK (rng2.get_random_seed_str() == seed_str3);
121 
122  std::string seed_str4 = rng2.get_random_seed_str();
123  rng2.seed_random(seed_str4);
124 
125  BOOST_CHECK (rng2.get_random_seed() == seed3);
126  BOOST_CHECK (rng2.get_random_seed_str() == seed_str3);
127 
128 
129  //now check that the results that shouldn't match don't
130  BOOST_CHECK (seed != seed3);
131  BOOST_CHECK (seed_str != seed_str3);
132 }
133 
134 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility )
135 {
136  config cfg;
137  cfg["random_seed"] = "5eedf00d";
138  cfg["random_calls"] = 0;
139 
140  randomness::mt_rng rng1(cfg);
141  randomness::mt_rng rng2(cfg);
142 
143  BOOST_CHECK(rng1 == rng2);
144  for (int i = 0; i < 10 ; i++) {
145  BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
146  }
147 }
148 
149 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility2 )
150 {
151  config cfg;
152  cfg["random_seed"] = "18da5eed";
153  cfg["random_calls"] = 9999;
154 
155  randomness::mt_rng rng1(cfg);
156  randomness::mt_rng rng2(cfg);
157 
158  BOOST_CHECK(rng1 == rng2);
159  for (int i = 0; i < 10 ; i++) {
160  BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
161  }
162 }
163 
164 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility3 )
165 {
166  randomness::mt_rng rng1;
167  config cfg;
168  cfg["random_seed"] = rng1.get_random_seed_str();
169  cfg["random_calls"] = rng1.get_random_calls();
170 
171  randomness::mt_rng rng2(cfg);
172 
173  BOOST_CHECK(rng1 == rng2);
174  for (int i = 0; i < 10 ; i++) {
175  BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
176  }
177 }
178 
179 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility4 )
180 {
181  randomness::mt_rng rng1;
182 
183  for (int i = 0; i < 5; i++) {
184  rng1.get_next_random();
185  }
186 
187  config cfg;
188  cfg["random_seed"] = rng1.get_random_seed_str();
189  cfg["random_calls"] = rng1.get_random_calls();
190 
191  randomness::mt_rng rng2(cfg);
192 
193  BOOST_CHECK(rng1 == rng2);
194  BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
195 }
196 
197 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility5 )
198 {
199  config cfg;
200  cfg["random_seed"] = "5eedc0de";
201  cfg["random_calls"] = 0;
202 
203  randomness::mt_rng rng(cfg);
204 
205  for (int i = 0; i < 9999 ; i++) {
206  rng.get_next_random();
207  }
208 
209  config cfg2;
210  cfg2["random_seed"] = rng.get_random_seed_str();
211  cfg2["random_calls"] = rng.get_random_calls();
212 
213  randomness::mt_rng rng2(cfg2);
214 
215  uint32_t result1 = rng.get_next_random();
216  uint32_t result2 = rng2.get_next_random();
217 
218  BOOST_CHECK (rng == rng2);
219  BOOST_CHECK (rng.get_random_seed_str() == rng2.get_random_seed_str());
220  BOOST_CHECK (rng.get_random_calls() == rng2.get_random_calls());
221  BOOST_CHECK (result1 == result2);
222 
223  config cfg_save;
224  cfg_save["random_seed"] = rng.get_random_seed_str();
225  cfg_save["random_calls"] = rng.get_random_calls();
226 
227  uint32_t result3 = rng.get_next_random();
228 
229  randomness::mt_rng rng3(cfg_save);
230  uint32_t result4 = rng3.get_next_random();
231 
232  BOOST_CHECK (rng == rng3);
233  BOOST_CHECK (rng.get_random_seed_str() == rng3.get_random_seed_str());
234  BOOST_CHECK (rng.get_random_calls() == rng3.get_random_calls());
235  BOOST_CHECK (result3 == result4);
236 }
237 
238 namespace {
239 
240 void validate_seed_string(const std::string& seed_str)
241 {
242  config cfg;
243  cfg["random_seed"] = seed_str;
244  cfg["random_calls"] = 0;
245 
246  randomness::mt_rng rng1(cfg);
247 
248  for (int i = 0; i < 9999 ; i++) {
249  rng1.get_next_random();
250  }
251 
252  config cfg2;
253  cfg2["random_seed"] = rng1.get_random_seed_str();
254  cfg2["random_calls"] = rng1.get_random_calls();
255 
256  randomness::mt_rng rng2(cfg2);
257 
258  for (int i = 0; i < 9999 ; i++) {
259  rng1.get_next_random();
260  rng2.get_next_random();
261  }
262 
263  BOOST_CHECK(rng1 == rng2);
264  BOOST_CHECK(rng1.get_next_random() == rng2.get_next_random());
265 
266 }
267 
268 }
269 
270 BOOST_AUTO_TEST_CASE( test_mt_rng_reproducibility_coverage )
271 {
272  validate_seed_string("0000badd");
273  validate_seed_string("00001234");
274  validate_seed_string("deadbeef");
275  validate_seed_string("12345678");
276  validate_seed_string("00009999");
277  validate_seed_string("ffffaaaa");
278  validate_seed_string("11110000");
279  validate_seed_string("10101010");
280  validate_seed_string("aaaa0000");
281 }
282 
283 namespace {
284 
285 std::string validate_get_random_int_seed_generator()
286 {
287  return "dada5eed";
288 }
289 
290 }
291 
292 #define validation_get_random_int_num_draws 19999
293 
294 #define validation_get_random_int_max 32000
295 
296 #define validation_get_random_int_correct_answer 10885
297 
298 /**
299  * This test and the next validate that we are getting the correct values
300  * from the get_random_int function, in the class random.
301  * We test both subclasses of random.
302  * If these tests fail but the seed manipulation tests all pass,
303  * and validate_mt19937 passes, then it suggests that the implementation
304  * of get_random_int may not be working properly on your platform.
305  */
306 BOOST_AUTO_TEST_CASE( validate_get_random_int )
307 {
308  config cfg;
309  cfg["random_seed"] = validate_get_random_int_seed_generator();
310  cfg["random_calls"] = validation_get_random_int_num_draws;
311 
312  randomness::mt_rng mt_(cfg);
313 
314  auto gen_ = std::make_shared<randomness::rng_deterministic>(mt_);
315 
316  int val = gen_->get_random_int(0, validation_get_random_int_max);
317  BOOST_CHECK_EQUAL ( val , validation_get_random_int_correct_answer );
318 }
319 
320 BOOST_AUTO_TEST_CASE( validate_get_random_int2 )
321 {
322  auto gen_ = std::make_shared<randomness::synced_rng>(validate_get_random_int_seed_generator);
323 
324  for (int i = 0; i < validation_get_random_int_num_draws; i++) {
325  gen_->next_random();
326  }
327 
328  int val = gen_->get_random_int(0,validation_get_random_int_max);
329  BOOST_CHECK_EQUAL ( val , validation_get_random_int_correct_answer );
330 }
331 
332 
333 BOOST_AUTO_TEST_SUITE_END()
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
std::string get_random_seed_str() const
Definition: mt_rng.cpp:100
uint32_t get_random_seed() const
Definition: mt_rng.hpp:54
uint32_t get_next_random()
Get a new random number.
Definition: mt_rng.cpp:63
void seed_random(const std::string &seed, const unsigned int call_count=0)
Same as uint32_t version, but uses a stringstream to convert given hex string.
Definition: mt_rng.cpp:89
unsigned int get_random_calls() const
Definition: mt_rng.hpp:56
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:1029
BOOST_AUTO_TEST_SUITE(filesystem)
BOOST_AUTO_TEST_CASE(validate_mt19937)
Definition: test_rng.cpp:30
#define validation_get_random_int_max
Definition: test_rng.cpp:294
#define validation_get_random_int_num_draws
Definition: test_rng.cpp:292
#define validation_get_random_int_correct_answer
Definition: test_rng.cpp:296