python (3.11.7)
1 import unittest
2 import unittest.mock
3 import random
4 import os
5 import time
6 import pickle
7 import warnings
8 import test.support
9
10 from functools import partial
11 from math import log, exp, pi, fsum, sin, factorial
12 from test import support
13 from fractions import Fraction
14 from collections import abc, Counter
15
16 class ESC[4;38;5;81mTestBasicOps:
17 # Superclass with tests common to all generators.
18 # Subclasses must arrange for self.gen to retrieve the Random instance
19 # to be tested.
20
21 def randomlist(self, n):
22 """Helper function to make a list of random numbers"""
23 return [self.gen.random() for i in range(n)]
24
25 def test_autoseed(self):
26 self.gen.seed()
27 state1 = self.gen.getstate()
28 time.sleep(0.1)
29 self.gen.seed() # different seeds at different times
30 state2 = self.gen.getstate()
31 self.assertNotEqual(state1, state2)
32
33 def test_saverestore(self):
34 N = 1000
35 self.gen.seed()
36 state = self.gen.getstate()
37 randseq = self.randomlist(N)
38 self.gen.setstate(state) # should regenerate the same sequence
39 self.assertEqual(randseq, self.randomlist(N))
40
41 def test_seedargs(self):
42 # Seed value with a negative hash.
43 class ESC[4;38;5;81mMySeed(ESC[4;38;5;149mobject):
44 def __hash__(self):
45 return -1729
46 for arg in [None, 0, 1, -1, 10**20, -(10**20),
47 False, True, 3.14, 'a']:
48 self.gen.seed(arg)
49
50 for arg in [1+2j, tuple('abc'), MySeed()]:
51 with self.assertRaises(TypeError):
52 self.gen.seed(arg)
53
54 for arg in [list(range(3)), dict(one=1)]:
55 self.assertRaises(TypeError, self.gen.seed, arg)
56 self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
57 self.assertRaises(TypeError, type(self.gen), [])
58
59 def test_seed_no_mutate_bug_44018(self):
60 a = bytearray(b'1234')
61 self.gen.seed(a)
62 self.assertEqual(a, bytearray(b'1234'))
63
64 @unittest.mock.patch('random._urandom') # os.urandom
65 def test_seed_when_randomness_source_not_found(self, urandom_mock):
66 # Random.seed() uses time.time() when an operating system specific
67 # randomness source is not found. To test this on machines where it
68 # exists, run the above test, test_seedargs(), again after mocking
69 # os.urandom() so that it raises the exception expected when the
70 # randomness source is not available.
71 urandom_mock.side_effect = NotImplementedError
72 self.test_seedargs()
73
74 def test_shuffle(self):
75 shuffle = self.gen.shuffle
76 lst = []
77 shuffle(lst)
78 self.assertEqual(lst, [])
79 lst = [37]
80 shuffle(lst)
81 self.assertEqual(lst, [37])
82 seqs = [list(range(n)) for n in range(10)]
83 shuffled_seqs = [list(range(n)) for n in range(10)]
84 for shuffled_seq in shuffled_seqs:
85 shuffle(shuffled_seq)
86 for (seq, shuffled_seq) in zip(seqs, shuffled_seqs):
87 self.assertEqual(len(seq), len(shuffled_seq))
88 self.assertEqual(set(seq), set(shuffled_seq))
89 # The above tests all would pass if the shuffle was a
90 # no-op. The following non-deterministic test covers that. It
91 # asserts that the shuffled sequence of 1000 distinct elements
92 # must be different from the original one. Although there is
93 # mathematically a non-zero probability that this could
94 # actually happen in a genuinely random shuffle, it is
95 # completely negligible, given that the number of possible
96 # permutations of 1000 objects is 1000! (factorial of 1000),
97 # which is considerably larger than the number of atoms in the
98 # universe...
99 lst = list(range(1000))
100 shuffled_lst = list(range(1000))
101 shuffle(shuffled_lst)
102 self.assertTrue(lst != shuffled_lst)
103 shuffle(lst)
104 self.assertTrue(lst != shuffled_lst)
105 self.assertRaises(TypeError, shuffle, (1, 2, 3))
106
107 def test_choice(self):
108 choice = self.gen.choice
109 with self.assertRaises(IndexError):
110 choice([])
111 self.assertEqual(choice([50]), 50)
112 self.assertIn(choice([25, 75]), [25, 75])
113
114 def test_choice_with_numpy(self):
115 # Accommodation for NumPy arrays which have disabled __bool__().
116 # See: https://github.com/python/cpython/issues/100805
117 choice = self.gen.choice
118
119 class ESC[4;38;5;81mNA(ESC[4;38;5;149mlist):
120 "Simulate numpy.array() behavior"
121 def __bool__(self):
122 raise RuntimeError
123
124 with self.assertRaises(IndexError):
125 choice(NA([]))
126 self.assertEqual(choice(NA([50])), 50)
127 self.assertIn(choice(NA([25, 75])), [25, 75])
128
129 def test_sample(self):
130 # For the entire allowable range of 0 <= k <= N, validate that
131 # the sample is of the correct length and contains only unique items
132 N = 100
133 population = range(N)
134 for k in range(N+1):
135 s = self.gen.sample(population, k)
136 self.assertEqual(len(s), k)
137 uniq = set(s)
138 self.assertEqual(len(uniq), k)
139 self.assertTrue(uniq <= set(population))
140 self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0
141 # Exception raised if size of sample exceeds that of population
142 self.assertRaises(ValueError, self.gen.sample, population, N+1)
143 self.assertRaises(ValueError, self.gen.sample, [], -1)
144
145 def test_sample_distribution(self):
146 # For the entire allowable range of 0 <= k <= N, validate that
147 # sample generates all possible permutations
148 n = 5
149 pop = range(n)
150 trials = 10000 # large num prevents false negatives without slowing normal case
151 for k in range(n):
152 expected = factorial(n) // factorial(n-k)
153 perms = {}
154 for i in range(trials):
155 perms[tuple(self.gen.sample(pop, k))] = None
156 if len(perms) == expected:
157 break
158 else:
159 self.fail()
160
161 def test_sample_inputs(self):
162 # SF bug #801342 -- population can be any iterable defining __len__()
163 self.gen.sample(range(20), 2)
164 self.gen.sample(range(20), 2)
165 self.gen.sample(str('abcdefghijklmnopqrst'), 2)
166 self.gen.sample(tuple('abcdefghijklmnopqrst'), 2)
167
168 def test_sample_on_dicts(self):
169 self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2)
170
171 def test_sample_on_sets(self):
172 with self.assertRaises(TypeError):
173 population = {10, 20, 30, 40, 50, 60, 70}
174 self.gen.sample(population, k=5)
175
176 def test_sample_on_seqsets(self):
177 class ESC[4;38;5;81mSeqSet(ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mSequence, ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mSet):
178 def __init__(self, items):
179 self._items = items
180
181 def __len__(self):
182 return len(self._items)
183
184 def __getitem__(self, index):
185 return self._items[index]
186
187 population = SeqSet([2, 4, 1, 3])
188 with warnings.catch_warnings():
189 warnings.simplefilter("error", DeprecationWarning)
190 self.gen.sample(population, k=2)
191
192 def test_sample_with_counts(self):
193 sample = self.gen.sample
194
195 # General case
196 colors = ['red', 'green', 'blue', 'orange', 'black', 'brown', 'amber']
197 counts = [500, 200, 20, 10, 5, 0, 1 ]
198 k = 700
199 summary = Counter(sample(colors, counts=counts, k=k))
200 self.assertEqual(sum(summary.values()), k)
201 for color, weight in zip(colors, counts):
202 self.assertLessEqual(summary[color], weight)
203 self.assertNotIn('brown', summary)
204
205 # Case that exhausts the population
206 k = sum(counts)
207 summary = Counter(sample(colors, counts=counts, k=k))
208 self.assertEqual(sum(summary.values()), k)
209 for color, weight in zip(colors, counts):
210 self.assertLessEqual(summary[color], weight)
211 self.assertNotIn('brown', summary)
212
213 # Case with population size of 1
214 summary = Counter(sample(['x'], counts=[10], k=8))
215 self.assertEqual(summary, Counter(x=8))
216
217 # Case with all counts equal.
218 nc = len(colors)
219 summary = Counter(sample(colors, counts=[10]*nc, k=10*nc))
220 self.assertEqual(summary, Counter(10*colors))
221
222 # Test error handling
223 with self.assertRaises(TypeError):
224 sample(['red', 'green', 'blue'], counts=10, k=10) # counts not iterable
225 with self.assertRaises(ValueError):
226 sample(['red', 'green', 'blue'], counts=[-3, -7, -8], k=2) # counts are negative
227 with self.assertRaises(ValueError):
228 sample(['red', 'green', 'blue'], counts=[0, 0, 0], k=2) # counts are zero
229 with self.assertRaises(ValueError):
230 sample(['red', 'green'], counts=[10, 10], k=21) # population too small
231 with self.assertRaises(ValueError):
232 sample(['red', 'green', 'blue'], counts=[1, 2], k=2) # too few counts
233 with self.assertRaises(ValueError):
234 sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) # too many counts
235
236 def test_choices(self):
237 choices = self.gen.choices
238 data = ['red', 'green', 'blue', 'yellow']
239 str_data = 'abcd'
240 range_data = range(4)
241 set_data = set(range(4))
242
243 # basic functionality
244 for sample in [
245 choices(data, k=5),
246 choices(data, range(4), k=5),
247 choices(k=5, population=data, weights=range(4)),
248 choices(k=5, population=data, cum_weights=range(4)),
249 ]:
250 self.assertEqual(len(sample), 5)
251 self.assertEqual(type(sample), list)
252 self.assertTrue(set(sample) <= set(data))
253
254 # test argument handling
255 with self.assertRaises(TypeError): # missing arguments
256 choices(2)
257
258 self.assertEqual(choices(data, k=0), []) # k == 0
259 self.assertEqual(choices(data, k=-1), []) # negative k behaves like ``[0] * -1``
260 with self.assertRaises(TypeError):
261 choices(data, k=2.5) # k is a float
262
263 self.assertTrue(set(choices(str_data, k=5)) <= set(str_data)) # population is a string sequence
264 self.assertTrue(set(choices(range_data, k=5)) <= set(range_data)) # population is a range
265 with self.assertRaises(TypeError):
266 choices(set_data, k=2) # population is not a sequence
267
268 self.assertTrue(set(choices(data, None, k=5)) <= set(data)) # weights is None
269 self.assertTrue(set(choices(data, weights=None, k=5)) <= set(data))
270 with self.assertRaises(ValueError):
271 choices(data, [1,2], k=5) # len(weights) != len(population)
272 with self.assertRaises(TypeError):
273 choices(data, 10, k=5) # non-iterable weights
274 with self.assertRaises(TypeError):
275 choices(data, [None]*4, k=5) # non-numeric weights
276 for weights in [
277 [15, 10, 25, 30], # integer weights
278 [15.1, 10.2, 25.2, 30.3], # float weights
279 [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights
280 [True, False, True, False] # booleans (include / exclude)
281 ]:
282 self.assertTrue(set(choices(data, weights, k=5)) <= set(data))
283
284 with self.assertRaises(ValueError):
285 choices(data, cum_weights=[1,2], k=5) # len(weights) != len(population)
286 with self.assertRaises(TypeError):
287 choices(data, cum_weights=10, k=5) # non-iterable cum_weights
288 with self.assertRaises(TypeError):
289 choices(data, cum_weights=[None]*4, k=5) # non-numeric cum_weights
290 with self.assertRaises(TypeError):
291 choices(data, range(4), cum_weights=range(4), k=5) # both weights and cum_weights
292 for weights in [
293 [15, 10, 25, 30], # integer cum_weights
294 [15.1, 10.2, 25.2, 30.3], # float cum_weights
295 [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights
296 ]:
297 self.assertTrue(set(choices(data, cum_weights=weights, k=5)) <= set(data))
298
299 # Test weight focused on a single element of the population
300 self.assertEqual(choices('abcd', [1, 0, 0, 0]), ['a'])
301 self.assertEqual(choices('abcd', [0, 1, 0, 0]), ['b'])
302 self.assertEqual(choices('abcd', [0, 0, 1, 0]), ['c'])
303 self.assertEqual(choices('abcd', [0, 0, 0, 1]), ['d'])
304
305 # Test consistency with random.choice() for empty population
306 with self.assertRaises(IndexError):
307 choices([], k=1)
308 with self.assertRaises(IndexError):
309 choices([], weights=[], k=1)
310 with self.assertRaises(IndexError):
311 choices([], cum_weights=[], k=5)
312
313 def test_choices_subnormal(self):
314 # Subnormal weights would occasionally trigger an IndexError
315 # in choices() when the value returned by random() was large
316 # enough to make `random() * total` round up to the total.
317 # See https://bugs.python.org/msg275594 for more detail.
318 choices = self.gen.choices
319 choices(population=[1, 2], weights=[1e-323, 1e-323], k=5000)
320
321 def test_choices_with_all_zero_weights(self):
322 # See issue #38881
323 with self.assertRaises(ValueError):
324 self.gen.choices('AB', [0.0, 0.0])
325
326 def test_choices_negative_total(self):
327 with self.assertRaises(ValueError):
328 self.gen.choices('ABC', [3, -5, 1])
329
330 def test_choices_infinite_total(self):
331 with self.assertRaises(ValueError):
332 self.gen.choices('A', [float('inf')])
333 with self.assertRaises(ValueError):
334 self.gen.choices('AB', [0.0, float('inf')])
335 with self.assertRaises(ValueError):
336 self.gen.choices('AB', [-float('inf'), 123])
337 with self.assertRaises(ValueError):
338 self.gen.choices('AB', [0.0, float('nan')])
339 with self.assertRaises(ValueError):
340 self.gen.choices('AB', [float('-inf'), float('inf')])
341
342 def test_gauss(self):
343 # Ensure that the seed() method initializes all the hidden state. In
344 # particular, through 2.2.1 it failed to reset a piece of state used
345 # by (and only by) the .gauss() method.
346
347 for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
348 self.gen.seed(seed)
349 x1 = self.gen.random()
350 y1 = self.gen.gauss(0, 1)
351
352 self.gen.seed(seed)
353 x2 = self.gen.random()
354 y2 = self.gen.gauss(0, 1)
355
356 self.assertEqual(x1, x2)
357 self.assertEqual(y1, y2)
358
359 def test_getrandbits(self):
360 # Verify ranges
361 for k in range(1, 1000):
362 self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k)
363 self.assertEqual(self.gen.getrandbits(0), 0)
364
365 # Verify all bits active
366 getbits = self.gen.getrandbits
367 for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]:
368 all_bits = 2**span-1
369 cum = 0
370 cpl_cum = 0
371 for i in range(100):
372 v = getbits(span)
373 cum |= v
374 cpl_cum |= all_bits ^ v
375 self.assertEqual(cum, all_bits)
376 self.assertEqual(cpl_cum, all_bits)
377
378 # Verify argument checking
379 self.assertRaises(TypeError, self.gen.getrandbits)
380 self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
381 self.assertRaises(ValueError, self.gen.getrandbits, -1)
382 self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
383
384 def test_pickling(self):
385 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
386 state = pickle.dumps(self.gen, proto)
387 origseq = [self.gen.random() for i in range(10)]
388 newgen = pickle.loads(state)
389 restoredseq = [newgen.random() for i in range(10)]
390 self.assertEqual(origseq, restoredseq)
391
392 def test_bug_1727780(self):
393 # verify that version-2-pickles can be loaded
394 # fine, whether they are created on 32-bit or 64-bit
395 # platforms, and that version-3-pickles load fine.
396 files = [("randv2_32.pck", 780),
397 ("randv2_64.pck", 866),
398 ("randv3.pck", 343)]
399 for file, value in files:
400 with open(support.findfile(file),"rb") as f:
401 r = pickle.load(f)
402 self.assertEqual(int(r.random()*1000), value)
403
404 def test_bug_9025(self):
405 # Had problem with an uneven distribution in int(n*random())
406 # Verify the fix by checking that distributions fall within expectations.
407 n = 100000
408 randrange = self.gen.randrange
409 k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n))
410 self.assertTrue(0.30 < k/n < .37, (k/n))
411
412 def test_randbytes(self):
413 # Verify ranges
414 for n in range(1, 10):
415 data = self.gen.randbytes(n)
416 self.assertEqual(type(data), bytes)
417 self.assertEqual(len(data), n)
418
419 self.assertEqual(self.gen.randbytes(0), b'')
420
421 # Verify argument checking
422 self.assertRaises(TypeError, self.gen.randbytes)
423 self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
424 self.assertRaises(ValueError, self.gen.randbytes, -1)
425 self.assertRaises(TypeError, self.gen.randbytes, 1.0)
426
427 def test_mu_sigma_default_args(self):
428 self.assertIsInstance(self.gen.normalvariate(), float)
429 self.assertIsInstance(self.gen.gauss(), float)
430
431
432 try:
433 random.SystemRandom().random()
434 except NotImplementedError:
435 SystemRandom_available = False
436 else:
437 SystemRandom_available = True
438
439 @unittest.skipUnless(SystemRandom_available, "random.SystemRandom not available")
440 class ESC[4;38;5;81mSystemRandom_TestBasicOps(ESC[4;38;5;149mTestBasicOps, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
441 gen = random.SystemRandom()
442
443 def test_autoseed(self):
444 # Doesn't need to do anything except not fail
445 self.gen.seed()
446
447 def test_saverestore(self):
448 self.assertRaises(NotImplementedError, self.gen.getstate)
449 self.assertRaises(NotImplementedError, self.gen.setstate, None)
450
451 def test_seedargs(self):
452 # Doesn't need to do anything except not fail
453 self.gen.seed(100)
454
455 def test_gauss(self):
456 self.gen.gauss_next = None
457 self.gen.seed(100)
458 self.assertEqual(self.gen.gauss_next, None)
459
460 def test_pickling(self):
461 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
462 self.assertRaises(NotImplementedError, pickle.dumps, self.gen, proto)
463
464 def test_53_bits_per_float(self):
465 # This should pass whenever a C double has 53 bit precision.
466 span = 2 ** 53
467 cum = 0
468 for i in range(100):
469 cum |= int(self.gen.random() * span)
470 self.assertEqual(cum, span-1)
471
472 def test_bigrand(self):
473 # The randrange routine should build-up the required number of bits
474 # in stages so that all bit positions are active.
475 span = 2 ** 500
476 cum = 0
477 for i in range(100):
478 r = self.gen.randrange(span)
479 self.assertTrue(0 <= r < span)
480 cum |= r
481 self.assertEqual(cum, span-1)
482
483 def test_bigrand_ranges(self):
484 for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
485 start = self.gen.randrange(2 ** (i-2))
486 stop = self.gen.randrange(2 ** i)
487 if stop <= start:
488 continue
489 self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
490
491 def test_rangelimits(self):
492 for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
493 self.assertEqual(set(range(start,stop)),
494 set([self.gen.randrange(start,stop) for i in range(100)]))
495
496 def test_randrange_nonunit_step(self):
497 rint = self.gen.randrange(0, 10, 2)
498 self.assertIn(rint, (0, 2, 4, 6, 8))
499 rint = self.gen.randrange(0, 2, 2)
500 self.assertEqual(rint, 0)
501
502 def test_randrange_errors(self):
503 raises = partial(self.assertRaises, ValueError, self.gen.randrange)
504 # Empty range
505 raises(3, 3)
506 raises(-721)
507 raises(0, 100, -12)
508 # Non-integer start/stop
509 self.assertWarns(DeprecationWarning, raises, 3.14159)
510 self.assertWarns(DeprecationWarning, self.gen.randrange, 3.0)
511 self.assertWarns(DeprecationWarning, self.gen.randrange, Fraction(3, 1))
512 self.assertWarns(DeprecationWarning, raises, '3')
513 self.assertWarns(DeprecationWarning, raises, 0, 2.71828)
514 self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 2.0)
515 self.assertWarns(DeprecationWarning, self.gen.randrange, 0, Fraction(2, 1))
516 self.assertWarns(DeprecationWarning, raises, 0, '2')
517 # Zero and non-integer step
518 raises(0, 42, 0)
519 self.assertWarns(DeprecationWarning, raises, 0, 42, 0.0)
520 self.assertWarns(DeprecationWarning, raises, 0, 0, 0.0)
521 self.assertWarns(DeprecationWarning, raises, 0, 42, 3.14159)
522 self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 3.0)
523 self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, Fraction(3, 1))
524 self.assertWarns(DeprecationWarning, raises, 0, 42, '3')
525 self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 1.0)
526 self.assertWarns(DeprecationWarning, raises, 0, 0, 1.0)
527
528 def test_randrange_argument_handling(self):
529 randrange = self.gen.randrange
530 with self.assertWarns(DeprecationWarning):
531 randrange(10.0, 20, 2)
532 with self.assertWarns(DeprecationWarning):
533 randrange(10, 20.0, 2)
534 with self.assertWarns(DeprecationWarning):
535 randrange(10, 20, 1.0)
536 with self.assertWarns(DeprecationWarning):
537 randrange(10, 20, 2.0)
538 with self.assertWarns(DeprecationWarning):
539 with self.assertRaises(ValueError):
540 randrange(10.5)
541 with self.assertWarns(DeprecationWarning):
542 with self.assertRaises(ValueError):
543 randrange(10, 20.5)
544 with self.assertWarns(DeprecationWarning):
545 with self.assertRaises(ValueError):
546 randrange(10, 20, 1.5)
547
548 def test_randrange_step(self):
549 # bpo-42772: When stop is None, the step argument was being ignored.
550 randrange = self.gen.randrange
551 with self.assertRaises(TypeError):
552 randrange(1000, step=100)
553 with self.assertRaises(TypeError):
554 randrange(1000, None, step=100)
555
556 def test_randbelow_logic(self, _log=log, int=int):
557 # check bitcount transition points: 2**i and 2**(i+1)-1
558 # show that: k = int(1.001 + _log(n, 2))
559 # is equal to or one greater than the number of bits in n
560 for i in range(1, 1000):
561 n = 1 << i # check an exact power of two
562 numbits = i+1
563 k = int(1.00001 + _log(n, 2))
564 self.assertEqual(k, numbits)
565 self.assertEqual(n, 2**(k-1))
566
567 n += n - 1 # check 1 below the next power of two
568 k = int(1.00001 + _log(n, 2))
569 self.assertIn(k, [numbits, numbits+1])
570 self.assertTrue(2**k > n > 2**(k-2))
571
572 n -= n >> 15 # check a little farther below the next power of two
573 k = int(1.00001 + _log(n, 2))
574 self.assertEqual(k, numbits) # note the stronger assertion
575 self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
576
577
578 class ESC[4;38;5;81mTestRawMersenneTwister(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
579 @test.support.cpython_only
580 def test_bug_41052(self):
581 # _random.Random should not be allowed to serialization
582 import _random
583 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
584 r = _random.Random()
585 self.assertRaises(TypeError, pickle.dumps, r, proto)
586
587 @test.support.cpython_only
588 def test_bug_42008(self):
589 # _random.Random should call seed with first element of arg tuple
590 import _random
591 r1 = _random.Random()
592 r1.seed(8675309)
593 r2 = _random.Random(8675309)
594 self.assertEqual(r1.random(), r2.random())
595
596
597 class ESC[4;38;5;81mMersenneTwister_TestBasicOps(ESC[4;38;5;149mTestBasicOps, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
598 gen = random.Random()
599
600 def test_guaranteed_stable(self):
601 # These sequences are guaranteed to stay the same across versions of python
602 self.gen.seed(3456147, version=1)
603 self.assertEqual([self.gen.random().hex() for i in range(4)],
604 ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1',
605 '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1'])
606 self.gen.seed("the quick brown fox", version=2)
607 self.assertEqual([self.gen.random().hex() for i in range(4)],
608 ['0x1.1239ddfb11b7cp-3', '0x1.b3cbb5c51b120p-4',
609 '0x1.8c4f55116b60fp-1', '0x1.63eb525174a27p-1'])
610
611 def test_bug_27706(self):
612 # Verify that version 1 seeds are unaffected by hash randomization
613
614 self.gen.seed('nofar', version=1) # hash('nofar') == 5990528763808513177
615 self.assertEqual([self.gen.random().hex() for i in range(4)],
616 ['0x1.8645314505ad7p-1', '0x1.afb1f82e40a40p-5',
617 '0x1.2a59d2285e971p-1', '0x1.56977142a7880p-6'])
618
619 self.gen.seed('rachel', version=1) # hash('rachel') == -9091735575445484789
620 self.assertEqual([self.gen.random().hex() for i in range(4)],
621 ['0x1.0b294cc856fcdp-1', '0x1.2ad22d79e77b8p-3',
622 '0x1.3052b9c072678p-2', '0x1.578f332106574p-3'])
623
624 self.gen.seed('', version=1) # hash('') == 0
625 self.assertEqual([self.gen.random().hex() for i in range(4)],
626 ['0x1.b0580f98a7dbep-1', '0x1.84129978f9c1ap-1',
627 '0x1.aeaa51052e978p-2', '0x1.092178fb945a6p-2'])
628
629 def test_bug_31478(self):
630 # There shouldn't be an assertion failure in _random.Random.seed() in
631 # case the argument has a bad __abs__() method.
632 class ESC[4;38;5;81mBadInt(ESC[4;38;5;149mint):
633 def __abs__(self):
634 return None
635 try:
636 self.gen.seed(BadInt())
637 except TypeError:
638 pass
639
640 def test_bug_31482(self):
641 # Verify that version 1 seeds are unaffected by hash randomization
642 # when the seeds are expressed as bytes rather than strings.
643 # The hash(b) values listed are the Python2.7 hash() values
644 # which were used for seeding.
645
646 self.gen.seed(b'nofar', version=1) # hash('nofar') == 5990528763808513177
647 self.assertEqual([self.gen.random().hex() for i in range(4)],
648 ['0x1.8645314505ad7p-1', '0x1.afb1f82e40a40p-5',
649 '0x1.2a59d2285e971p-1', '0x1.56977142a7880p-6'])
650
651 self.gen.seed(b'rachel', version=1) # hash('rachel') == -9091735575445484789
652 self.assertEqual([self.gen.random().hex() for i in range(4)],
653 ['0x1.0b294cc856fcdp-1', '0x1.2ad22d79e77b8p-3',
654 '0x1.3052b9c072678p-2', '0x1.578f332106574p-3'])
655
656 self.gen.seed(b'', version=1) # hash('') == 0
657 self.assertEqual([self.gen.random().hex() for i in range(4)],
658 ['0x1.b0580f98a7dbep-1', '0x1.84129978f9c1ap-1',
659 '0x1.aeaa51052e978p-2', '0x1.092178fb945a6p-2'])
660
661 b = b'\x00\x20\x40\x60\x80\xA0\xC0\xE0\xF0'
662 self.gen.seed(b, version=1) # hash(b) == 5015594239749365497
663 self.assertEqual([self.gen.random().hex() for i in range(4)],
664 ['0x1.52c2fde444d23p-1', '0x1.875174f0daea4p-2',
665 '0x1.9e9b2c50e5cd2p-1', '0x1.fa57768bd321cp-2'])
666
667 def test_setstate_first_arg(self):
668 self.assertRaises(ValueError, self.gen.setstate, (1, None, None))
669
670 def test_setstate_middle_arg(self):
671 start_state = self.gen.getstate()
672 # Wrong type, s/b tuple
673 self.assertRaises(TypeError, self.gen.setstate, (2, None, None))
674 # Wrong length, s/b 625
675 self.assertRaises(ValueError, self.gen.setstate, (2, (1,2,3), None))
676 # Wrong type, s/b tuple of 625 ints
677 self.assertRaises(TypeError, self.gen.setstate, (2, ('a',)*625, None))
678 # Last element s/b an int also
679 self.assertRaises(TypeError, self.gen.setstate, (2, (0,)*624+('a',), None))
680 # Last element s/b between 0 and 624
681 with self.assertRaises((ValueError, OverflowError)):
682 self.gen.setstate((2, (1,)*624+(625,), None))
683 with self.assertRaises((ValueError, OverflowError)):
684 self.gen.setstate((2, (1,)*624+(-1,), None))
685 # Failed calls to setstate() should not have changed the state.
686 bits100 = self.gen.getrandbits(100)
687 self.gen.setstate(start_state)
688 self.assertEqual(self.gen.getrandbits(100), bits100)
689
690 # Little trick to make "tuple(x % (2**32) for x in internalstate)"
691 # raise ValueError. I cannot think of a simple way to achieve this, so
692 # I am opting for using a generator as the middle argument of setstate
693 # which attempts to cast a NaN to integer.
694 state_values = self.gen.getstate()[1]
695 state_values = list(state_values)
696 state_values[-1] = float('nan')
697 state = (int(x) for x in state_values)
698 self.assertRaises(TypeError, self.gen.setstate, (2, state, None))
699
700 def test_referenceImplementation(self):
701 # Compare the python implementation with results from the original
702 # code. Create 2000 53-bit precision random floats. Compare only
703 # the last ten entries to show that the independent implementations
704 # are tracking. Here is the main() function needed to create the
705 # list of expected random numbers:
706 # void main(void){
707 # int i;
708 # unsigned long init[4]={61731, 24903, 614, 42143}, length=4;
709 # init_by_array(init, length);
710 # for (i=0; i<2000; i++) {
711 # printf("%.15f ", genrand_res53());
712 # if (i%5==4) printf("\n");
713 # }
714 # }
715 expected = [0.45839803073713259,
716 0.86057815201978782,
717 0.92848331726782152,
718 0.35932681119782461,
719 0.081823493762449573,
720 0.14332226470169329,
721 0.084297823823520024,
722 0.53814864671831453,
723 0.089215024911993401,
724 0.78486196105372907]
725
726 self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
727 actual = self.randomlist(2000)[-10:]
728 for a, e in zip(actual, expected):
729 self.assertAlmostEqual(a,e,places=14)
730
731 def test_strong_reference_implementation(self):
732 # Like test_referenceImplementation, but checks for exact bit-level
733 # equality. This should pass on any box where C double contains
734 # at least 53 bits of precision (the underlying algorithm suffers
735 # no rounding errors -- all results are exact).
736 from math import ldexp
737
738 expected = [0x0eab3258d2231f,
739 0x1b89db315277a5,
740 0x1db622a5518016,
741 0x0b7f9af0d575bf,
742 0x029e4c4db82240,
743 0x04961892f5d673,
744 0x02b291598e4589,
745 0x11388382c15694,
746 0x02dad977c9e1fe,
747 0x191d96d4d334c6]
748 self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
749 actual = self.randomlist(2000)[-10:]
750 for a, e in zip(actual, expected):
751 self.assertEqual(int(ldexp(a, 53)), e)
752
753 def test_long_seed(self):
754 # This is most interesting to run in debug mode, just to make sure
755 # nothing blows up. Under the covers, a dynamically resized array
756 # is allocated, consuming space proportional to the number of bits
757 # in the seed. Unfortunately, that's a quadratic-time algorithm,
758 # so don't make this horribly big.
759 seed = (1 << (10000 * 8)) - 1 # about 10K bytes
760 self.gen.seed(seed)
761
762 def test_53_bits_per_float(self):
763 # This should pass whenever a C double has 53 bit precision.
764 span = 2 ** 53
765 cum = 0
766 for i in range(100):
767 cum |= int(self.gen.random() * span)
768 self.assertEqual(cum, span-1)
769
770 def test_bigrand(self):
771 # The randrange routine should build-up the required number of bits
772 # in stages so that all bit positions are active.
773 span = 2 ** 500
774 cum = 0
775 for i in range(100):
776 r = self.gen.randrange(span)
777 self.assertTrue(0 <= r < span)
778 cum |= r
779 self.assertEqual(cum, span-1)
780
781 def test_bigrand_ranges(self):
782 for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
783 start = self.gen.randrange(2 ** (i-2))
784 stop = self.gen.randrange(2 ** i)
785 if stop <= start:
786 continue
787 self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
788
789 def test_rangelimits(self):
790 for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
791 self.assertEqual(set(range(start,stop)),
792 set([self.gen.randrange(start,stop) for i in range(100)]))
793
794 def test_getrandbits(self):
795 super().test_getrandbits()
796
797 # Verify cross-platform repeatability
798 self.gen.seed(1234567)
799 self.assertEqual(self.gen.getrandbits(100),
800 97904845777343510404718956115)
801
802 def test_randrange_uses_getrandbits(self):
803 # Verify use of getrandbits by randrange
804 # Use same seed as in the cross-platform repeatability test
805 # in test_getrandbits above.
806 self.gen.seed(1234567)
807 # If randrange uses getrandbits, it should pick getrandbits(100)
808 # when called with a 100-bits stop argument.
809 self.assertEqual(self.gen.randrange(2**99),
810 97904845777343510404718956115)
811
812 def test_randbelow_logic(self, _log=log, int=int):
813 # check bitcount transition points: 2**i and 2**(i+1)-1
814 # show that: k = int(1.001 + _log(n, 2))
815 # is equal to or one greater than the number of bits in n
816 for i in range(1, 1000):
817 n = 1 << i # check an exact power of two
818 numbits = i+1
819 k = int(1.00001 + _log(n, 2))
820 self.assertEqual(k, numbits)
821 self.assertEqual(n, 2**(k-1))
822
823 n += n - 1 # check 1 below the next power of two
824 k = int(1.00001 + _log(n, 2))
825 self.assertIn(k, [numbits, numbits+1])
826 self.assertTrue(2**k > n > 2**(k-2))
827
828 n -= n >> 15 # check a little farther below the next power of two
829 k = int(1.00001 + _log(n, 2))
830 self.assertEqual(k, numbits) # note the stronger assertion
831 self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
832
833 def test_randbelow_without_getrandbits(self):
834 # Random._randbelow() can only use random() when the built-in one
835 # has been overridden but no new getrandbits() method was supplied.
836 maxsize = 1<<random.BPF
837 with warnings.catch_warnings():
838 warnings.simplefilter("ignore", UserWarning)
839 # Population range too large (n >= maxsize)
840 self.gen._randbelow_without_getrandbits(
841 maxsize+1, maxsize=maxsize
842 )
843 self.gen._randbelow_without_getrandbits(5640, maxsize=maxsize)
844
845 # This might be going too far to test a single line, but because of our
846 # noble aim of achieving 100% test coverage we need to write a case in
847 # which the following line in Random._randbelow() gets executed:
848 #
849 # rem = maxsize % n
850 # limit = (maxsize - rem) / maxsize
851 # r = random()
852 # while r >= limit:
853 # r = random() # <== *This line* <==<
854 #
855 # Therefore, to guarantee that the while loop is executed at least
856 # once, we need to mock random() so that it returns a number greater
857 # than 'limit' the first time it gets called.
858
859 n = 42
860 epsilon = 0.01
861 limit = (maxsize - (maxsize % n)) / maxsize
862 with unittest.mock.patch.object(random.Random, 'random') as random_mock:
863 random_mock.side_effect = [limit + epsilon, limit - epsilon]
864 self.gen._randbelow_without_getrandbits(n, maxsize=maxsize)
865 self.assertEqual(random_mock.call_count, 2)
866
867 def test_randrange_bug_1590891(self):
868 start = 1000000000000
869 stop = -100000000000000000000
870 step = -200
871 x = self.gen.randrange(start, stop, step)
872 self.assertTrue(stop < x <= start)
873 self.assertEqual((x+stop)%step, 0)
874
875 def test_choices_algorithms(self):
876 # The various ways of specifying weights should produce the same results
877 choices = self.gen.choices
878 n = 104729
879
880 self.gen.seed(8675309)
881 a = self.gen.choices(range(n), k=10000)
882
883 self.gen.seed(8675309)
884 b = self.gen.choices(range(n), [1]*n, k=10000)
885 self.assertEqual(a, b)
886
887 self.gen.seed(8675309)
888 c = self.gen.choices(range(n), cum_weights=range(1, n+1), k=10000)
889 self.assertEqual(a, c)
890
891 # American Roulette
892 population = ['Red', 'Black', 'Green']
893 weights = [18, 18, 2]
894 cum_weights = [18, 36, 38]
895 expanded_population = ['Red'] * 18 + ['Black'] * 18 + ['Green'] * 2
896
897 self.gen.seed(9035768)
898 a = self.gen.choices(expanded_population, k=10000)
899
900 self.gen.seed(9035768)
901 b = self.gen.choices(population, weights, k=10000)
902 self.assertEqual(a, b)
903
904 self.gen.seed(9035768)
905 c = self.gen.choices(population, cum_weights=cum_weights, k=10000)
906 self.assertEqual(a, c)
907
908 def test_randbytes(self):
909 super().test_randbytes()
910
911 # Mersenne Twister randbytes() is deterministic
912 # and does not depend on the endian and bitness.
913 seed = 8675309
914 expected = b'3\xa8\xf9f\xf4\xa4\xd06\x19\x8f\x9f\x82\x02oe\xf0'
915
916 self.gen.seed(seed)
917 self.assertEqual(self.gen.randbytes(16), expected)
918
919 # randbytes(0) must not consume any entropy
920 self.gen.seed(seed)
921 self.assertEqual(self.gen.randbytes(0), b'')
922 self.assertEqual(self.gen.randbytes(16), expected)
923
924 # Four randbytes(4) calls give the same output than randbytes(16)
925 self.gen.seed(seed)
926 self.assertEqual(b''.join([self.gen.randbytes(4) for _ in range(4)]),
927 expected)
928
929 # Each randbytes(1), randbytes(2) or randbytes(3) call consumes
930 # 4 bytes of entropy
931 self.gen.seed(seed)
932 expected1 = expected[3::4]
933 self.assertEqual(b''.join(self.gen.randbytes(1) for _ in range(4)),
934 expected1)
935
936 self.gen.seed(seed)
937 expected2 = b''.join(expected[i + 2: i + 4]
938 for i in range(0, len(expected), 4))
939 self.assertEqual(b''.join(self.gen.randbytes(2) for _ in range(4)),
940 expected2)
941
942 self.gen.seed(seed)
943 expected3 = b''.join(expected[i + 1: i + 4]
944 for i in range(0, len(expected), 4))
945 self.assertEqual(b''.join(self.gen.randbytes(3) for _ in range(4)),
946 expected3)
947
948 def test_randbytes_getrandbits(self):
949 # There is a simple relation between randbytes() and getrandbits()
950 seed = 2849427419
951 gen2 = random.Random()
952 self.gen.seed(seed)
953 gen2.seed(seed)
954 for n in range(9):
955 self.assertEqual(self.gen.randbytes(n),
956 gen2.getrandbits(n * 8).to_bytes(n, 'little'))
957
958 def test_sample_counts_equivalence(self):
959 # Test the documented strong equivalence to a sample with repeated elements.
960 # We run this test on random.Random() which makes deterministic selections
961 # for a given seed value.
962 sample = self.gen.sample
963 seed = self.gen.seed
964
965 colors = ['red', 'green', 'blue', 'orange', 'black', 'amber']
966 counts = [500, 200, 20, 10, 5, 1 ]
967 k = 700
968 seed(8675309)
969 s1 = sample(colors, counts=counts, k=k)
970 seed(8675309)
971 expanded = [color for (color, count) in zip(colors, counts) for i in range(count)]
972 self.assertEqual(len(expanded), sum(counts))
973 s2 = sample(expanded, k=k)
974 self.assertEqual(s1, s2)
975
976 pop = 'abcdefghi'
977 counts = [10, 9, 8, 7, 6, 5, 4, 3, 2]
978 seed(8675309)
979 s1 = ''.join(sample(pop, counts=counts, k=30))
980 expanded = ''.join([letter for (letter, count) in zip(pop, counts) for i in range(count)])
981 seed(8675309)
982 s2 = ''.join(sample(expanded, k=30))
983 self.assertEqual(s1, s2)
984
985
986 def gamma(z, sqrt2pi=(2.0*pi)**0.5):
987 # Reflection to right half of complex plane
988 if z < 0.5:
989 return pi / sin(pi*z) / gamma(1.0-z)
990 # Lanczos approximation with g=7
991 az = z + (7.0 - 0.5)
992 return az ** (z-0.5) / exp(az) * sqrt2pi * fsum([
993 0.9999999999995183,
994 676.5203681218835 / z,
995 -1259.139216722289 / (z+1.0),
996 771.3234287757674 / (z+2.0),
997 -176.6150291498386 / (z+3.0),
998 12.50734324009056 / (z+4.0),
999 -0.1385710331296526 / (z+5.0),
1000 0.9934937113930748e-05 / (z+6.0),
1001 0.1659470187408462e-06 / (z+7.0),
1002 ])
1003
1004 class ESC[4;38;5;81mTestDistributions(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1005 def test_zeroinputs(self):
1006 # Verify that distributions can handle a series of zero inputs'
1007 g = random.Random()
1008 x = [g.random() for i in range(50)] + [0.0]*5
1009 g.random = x[:].pop; g.uniform(1,10)
1010 g.random = x[:].pop; g.paretovariate(1.0)
1011 g.random = x[:].pop; g.expovariate(1.0)
1012 g.random = x[:].pop; g.weibullvariate(1.0, 1.0)
1013 g.random = x[:].pop; g.vonmisesvariate(1.0, 1.0)
1014 g.random = x[:].pop; g.normalvariate(0.0, 1.0)
1015 g.random = x[:].pop; g.gauss(0.0, 1.0)
1016 g.random = x[:].pop; g.lognormvariate(0.0, 1.0)
1017 g.random = x[:].pop; g.vonmisesvariate(0.0, 1.0)
1018 g.random = x[:].pop; g.gammavariate(0.01, 1.0)
1019 g.random = x[:].pop; g.gammavariate(1.0, 1.0)
1020 g.random = x[:].pop; g.gammavariate(200.0, 1.0)
1021 g.random = x[:].pop; g.betavariate(3.0, 3.0)
1022 g.random = x[:].pop; g.triangular(0.0, 1.0, 1.0/3.0)
1023
1024 def test_avg_std(self):
1025 # Use integration to test distribution average and standard deviation.
1026 # Only works for distributions which do not consume variates in pairs
1027 g = random.Random()
1028 N = 5000
1029 x = [i/float(N) for i in range(1,N)]
1030 for variate, args, mu, sigmasqrd in [
1031 (g.uniform, (1.0,10.0), (10.0+1.0)/2, (10.0-1.0)**2/12),
1032 (g.triangular, (0.0, 1.0, 1.0/3.0), 4.0/9.0, 7.0/9.0/18.0),
1033 (g.expovariate, (1.5,), 1/1.5, 1/1.5**2),
1034 (g.vonmisesvariate, (1.23, 0), pi, pi**2/3),
1035 (g.paretovariate, (5.0,), 5.0/(5.0-1),
1036 5.0/((5.0-1)**2*(5.0-2))),
1037 (g.weibullvariate, (1.0, 3.0), gamma(1+1/3.0),
1038 gamma(1+2/3.0)-gamma(1+1/3.0)**2) ]:
1039 g.random = x[:].pop
1040 y = []
1041 for i in range(len(x)):
1042 try:
1043 y.append(variate(*args))
1044 except IndexError:
1045 pass
1046 s1 = s2 = 0
1047 for e in y:
1048 s1 += e
1049 s2 += (e - mu) ** 2
1050 N = len(y)
1051 self.assertAlmostEqual(s1/N, mu, places=2,
1052 msg='%s%r' % (variate.__name__, args))
1053 self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2,
1054 msg='%s%r' % (variate.__name__, args))
1055
1056 def test_constant(self):
1057 g = random.Random()
1058 N = 100
1059 for variate, args, expected in [
1060 (g.uniform, (10.0, 10.0), 10.0),
1061 (g.triangular, (10.0, 10.0), 10.0),
1062 (g.triangular, (10.0, 10.0, 10.0), 10.0),
1063 (g.expovariate, (float('inf'),), 0.0),
1064 (g.vonmisesvariate, (3.0, float('inf')), 3.0),
1065 (g.gauss, (10.0, 0.0), 10.0),
1066 (g.lognormvariate, (0.0, 0.0), 1.0),
1067 (g.lognormvariate, (-float('inf'), 0.0), 0.0),
1068 (g.normalvariate, (10.0, 0.0), 10.0),
1069 (g.paretovariate, (float('inf'),), 1.0),
1070 (g.weibullvariate, (10.0, float('inf')), 10.0),
1071 (g.weibullvariate, (0.0, 10.0), 0.0),
1072 ]:
1073 for i in range(N):
1074 self.assertEqual(variate(*args), expected)
1075
1076 def test_von_mises_range(self):
1077 # Issue 17149: von mises variates were not consistently in the
1078 # range [0, 2*PI].
1079 g = random.Random()
1080 N = 100
1081 for mu in 0.0, 0.1, 3.1, 6.2:
1082 for kappa in 0.0, 2.3, 500.0:
1083 for _ in range(N):
1084 sample = g.vonmisesvariate(mu, kappa)
1085 self.assertTrue(
1086 0 <= sample <= random.TWOPI,
1087 msg=("vonmisesvariate({}, {}) produced a result {} out"
1088 " of range [0, 2*pi]").format(mu, kappa, sample))
1089
1090 def test_von_mises_large_kappa(self):
1091 # Issue #17141: vonmisesvariate() was hang for large kappas
1092 random.vonmisesvariate(0, 1e15)
1093 random.vonmisesvariate(0, 1e100)
1094
1095 def test_gammavariate_errors(self):
1096 # Both alpha and beta must be > 0.0
1097 self.assertRaises(ValueError, random.gammavariate, -1, 3)
1098 self.assertRaises(ValueError, random.gammavariate, 0, 2)
1099 self.assertRaises(ValueError, random.gammavariate, 2, 0)
1100 self.assertRaises(ValueError, random.gammavariate, 1, -3)
1101
1102 # There are three different possibilities in the current implementation
1103 # of random.gammavariate(), depending on the value of 'alpha'. What we
1104 # are going to do here is to fix the values returned by random() to
1105 # generate test cases that provide 100% line coverage of the method.
1106 @unittest.mock.patch('random.Random.random')
1107 def test_gammavariate_alpha_greater_one(self, random_mock):
1108
1109 # #1: alpha > 1.0.
1110 # We want the first random number to be outside the
1111 # [1e-7, .9999999] range, so that the continue statement executes
1112 # once. The values of u1 and u2 will be 0.5 and 0.3, respectively.
1113 random_mock.side_effect = [1e-8, 0.5, 0.3]
1114 returned_value = random.gammavariate(1.1, 2.3)
1115 self.assertAlmostEqual(returned_value, 2.53)
1116
1117 @unittest.mock.patch('random.Random.random')
1118 def test_gammavariate_alpha_equal_one(self, random_mock):
1119
1120 # #2.a: alpha == 1.
1121 # The execution body of the while loop executes once.
1122 # Then random.random() returns 0.45,
1123 # which causes while to stop looping and the algorithm to terminate.
1124 random_mock.side_effect = [0.45]
1125 returned_value = random.gammavariate(1.0, 3.14)
1126 self.assertAlmostEqual(returned_value, 1.877208182372648)
1127
1128 @unittest.mock.patch('random.Random.random')
1129 def test_gammavariate_alpha_equal_one_equals_expovariate(self, random_mock):
1130
1131 # #2.b: alpha == 1.
1132 # It must be equivalent of calling expovariate(1.0 / beta).
1133 beta = 3.14
1134 random_mock.side_effect = [1e-8, 1e-8]
1135 gammavariate_returned_value = random.gammavariate(1.0, beta)
1136 expovariate_returned_value = random.expovariate(1.0 / beta)
1137 self.assertAlmostEqual(gammavariate_returned_value, expovariate_returned_value)
1138
1139 @unittest.mock.patch('random.Random.random')
1140 def test_gammavariate_alpha_between_zero_and_one(self, random_mock):
1141
1142 # #3: 0 < alpha < 1.
1143 # This is the most complex region of code to cover,
1144 # as there are multiple if-else statements. Let's take a look at the
1145 # source code, and determine the values that we need accordingly:
1146 #
1147 # while 1:
1148 # u = random()
1149 # b = (_e + alpha)/_e
1150 # p = b*u
1151 # if p <= 1.0: # <=== (A)
1152 # x = p ** (1.0/alpha)
1153 # else: # <=== (B)
1154 # x = -_log((b-p)/alpha)
1155 # u1 = random()
1156 # if p > 1.0: # <=== (C)
1157 # if u1 <= x ** (alpha - 1.0): # <=== (D)
1158 # break
1159 # elif u1 <= _exp(-x): # <=== (E)
1160 # break
1161 # return x * beta
1162 #
1163 # First, we want (A) to be True. For that we need that:
1164 # b*random() <= 1.0
1165 # r1 = random() <= 1.0 / b
1166 #
1167 # We now get to the second if-else branch, and here, since p <= 1.0,
1168 # (C) is False and we take the elif branch, (E). For it to be True,
1169 # so that the break is executed, we need that:
1170 # r2 = random() <= _exp(-x)
1171 # r2 <= _exp(-(p ** (1.0/alpha)))
1172 # r2 <= _exp(-((b*r1) ** (1.0/alpha)))
1173
1174 _e = random._e
1175 _exp = random._exp
1176 _log = random._log
1177 alpha = 0.35
1178 beta = 1.45
1179 b = (_e + alpha)/_e
1180 epsilon = 0.01
1181
1182 r1 = 0.8859296441566 # 1.0 / b
1183 r2 = 0.3678794411714 # _exp(-((b*r1) ** (1.0/alpha)))
1184
1185 # These four "random" values result in the following trace:
1186 # (A) True, (E) False --> [next iteration of while]
1187 # (A) True, (E) True --> [while loop breaks]
1188 random_mock.side_effect = [r1, r2 + epsilon, r1, r2]
1189 returned_value = random.gammavariate(alpha, beta)
1190 self.assertAlmostEqual(returned_value, 1.4499999999997544)
1191
1192 # Let's now make (A) be False. If this is the case, when we get to the
1193 # second if-else 'p' is greater than 1, so (C) evaluates to True. We
1194 # now encounter a second if statement, (D), which in order to execute
1195 # must satisfy the following condition:
1196 # r2 <= x ** (alpha - 1.0)
1197 # r2 <= (-_log((b-p)/alpha)) ** (alpha - 1.0)
1198 # r2 <= (-_log((b-(b*r1))/alpha)) ** (alpha - 1.0)
1199 r1 = 0.8959296441566 # (1.0 / b) + epsilon -- so that (A) is False
1200 r2 = 0.9445400408898141
1201
1202 # And these four values result in the following trace:
1203 # (B) and (C) True, (D) False --> [next iteration of while]
1204 # (B) and (C) True, (D) True [while loop breaks]
1205 random_mock.side_effect = [r1, r2 + epsilon, r1, r2]
1206 returned_value = random.gammavariate(alpha, beta)
1207 self.assertAlmostEqual(returned_value, 1.5830349561760781)
1208
1209 @unittest.mock.patch('random.Random.gammavariate')
1210 def test_betavariate_return_zero(self, gammavariate_mock):
1211 # betavariate() returns zero when the Gamma distribution
1212 # that it uses internally returns this same value.
1213 gammavariate_mock.return_value = 0.0
1214 self.assertEqual(0.0, random.betavariate(2.71828, 3.14159))
1215
1216
1217 class ESC[4;38;5;81mTestRandomSubclassing(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1218 def test_random_subclass_with_kwargs(self):
1219 # SF bug #1486663 -- this used to erroneously raise a TypeError
1220 class ESC[4;38;5;81mSubclass(ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1221 def __init__(self, newarg=None):
1222 random.Random.__init__(self)
1223 Subclass(newarg=1)
1224
1225 def test_subclasses_overriding_methods(self):
1226 # Subclasses with an overridden random, but only the original
1227 # getrandbits method should not rely on getrandbits in for randrange,
1228 # but should use a getrandbits-independent implementation instead.
1229
1230 # subclass providing its own random **and** getrandbits methods
1231 # like random.SystemRandom does => keep relying on getrandbits for
1232 # randrange
1233 class ESC[4;38;5;81mSubClass1(ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1234 def random(self):
1235 called.add('SubClass1.random')
1236 return random.Random.random(self)
1237
1238 def getrandbits(self, n):
1239 called.add('SubClass1.getrandbits')
1240 return random.Random.getrandbits(self, n)
1241 called = set()
1242 SubClass1().randrange(42)
1243 self.assertEqual(called, {'SubClass1.getrandbits'})
1244
1245 # subclass providing only random => can only use random for randrange
1246 class ESC[4;38;5;81mSubClass2(ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1247 def random(self):
1248 called.add('SubClass2.random')
1249 return random.Random.random(self)
1250 called = set()
1251 SubClass2().randrange(42)
1252 self.assertEqual(called, {'SubClass2.random'})
1253
1254 # subclass defining getrandbits to complement its inherited random
1255 # => can now rely on getrandbits for randrange again
1256 class ESC[4;38;5;81mSubClass3(ESC[4;38;5;149mSubClass2):
1257 def getrandbits(self, n):
1258 called.add('SubClass3.getrandbits')
1259 return random.Random.getrandbits(self, n)
1260 called = set()
1261 SubClass3().randrange(42)
1262 self.assertEqual(called, {'SubClass3.getrandbits'})
1263
1264 # subclass providing only random and inherited getrandbits
1265 # => random takes precedence
1266 class ESC[4;38;5;81mSubClass4(ESC[4;38;5;149mSubClass3):
1267 def random(self):
1268 called.add('SubClass4.random')
1269 return random.Random.random(self)
1270 called = set()
1271 SubClass4().randrange(42)
1272 self.assertEqual(called, {'SubClass4.random'})
1273
1274 # Following subclasses don't define random or getrandbits directly,
1275 # but inherit them from classes which are not subclasses of Random
1276 class ESC[4;38;5;81mMixin1:
1277 def random(self):
1278 called.add('Mixin1.random')
1279 return random.Random.random(self)
1280 class ESC[4;38;5;81mMixin2:
1281 def getrandbits(self, n):
1282 called.add('Mixin2.getrandbits')
1283 return random.Random.getrandbits(self, n)
1284
1285 class ESC[4;38;5;81mSubClass5(ESC[4;38;5;149mMixin1, ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1286 pass
1287 called = set()
1288 SubClass5().randrange(42)
1289 self.assertEqual(called, {'Mixin1.random'})
1290
1291 class ESC[4;38;5;81mSubClass6(ESC[4;38;5;149mMixin2, ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1292 pass
1293 called = set()
1294 SubClass6().randrange(42)
1295 self.assertEqual(called, {'Mixin2.getrandbits'})
1296
1297 class ESC[4;38;5;81mSubClass7(ESC[4;38;5;149mMixin1, ESC[4;38;5;149mMixin2, ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1298 pass
1299 called = set()
1300 SubClass7().randrange(42)
1301 self.assertEqual(called, {'Mixin1.random'})
1302
1303 class ESC[4;38;5;81mSubClass8(ESC[4;38;5;149mMixin2, ESC[4;38;5;149mMixin1, ESC[4;38;5;149mrandomESC[4;38;5;149m.ESC[4;38;5;149mRandom):
1304 pass
1305 called = set()
1306 SubClass8().randrange(42)
1307 self.assertEqual(called, {'Mixin2.getrandbits'})
1308
1309
1310 class ESC[4;38;5;81mTestModule(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1311 def testMagicConstants(self):
1312 self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141)
1313 self.assertAlmostEqual(random.TWOPI, 6.28318530718)
1314 self.assertAlmostEqual(random.LOG4, 1.38629436111989)
1315 self.assertAlmostEqual(random.SG_MAGICCONST, 2.50407739677627)
1316
1317 def test__all__(self):
1318 # tests validity but not completeness of the __all__ list
1319 self.assertTrue(set(random.__all__) <= set(dir(random)))
1320
1321 @test.support.requires_fork()
1322 def test_after_fork(self):
1323 # Test the global Random instance gets reseeded in child
1324 r, w = os.pipe()
1325 pid = os.fork()
1326 if pid == 0:
1327 # child process
1328 try:
1329 val = random.getrandbits(128)
1330 with open(w, "w") as f:
1331 f.write(str(val))
1332 finally:
1333 os._exit(0)
1334 else:
1335 # parent process
1336 os.close(w)
1337 val = random.getrandbits(128)
1338 with open(r, "r") as f:
1339 child_val = eval(f.read())
1340 self.assertNotEqual(val, child_val)
1341
1342 support.wait_process(pid, exitcode=0)
1343
1344
1345 if __name__ == "__main__":
1346 unittest.main()