Skip to content

Commit 33422b0

Browse files
gh-135376: Fix and improve test_random
* Remove duplicated code. Tests for Random and SystemRandom now share the code. * Move implementation agnostic tests that was only run for SystemRandom, so they are now run for Random too. * Add tests for __index__() support. * Add tests for randint().
1 parent 5ae669f commit 33422b0

File tree

1 file changed

+133
-161
lines changed

1 file changed

+133
-161
lines changed

Lib/test/test_random.py

Lines changed: 133 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def test_sample(self):
151151
# Exception raised if size of sample exceeds that of population
152152
self.assertRaises(ValueError, self.gen.sample, population, N+1)
153153
self.assertRaises(ValueError, self.gen.sample, [], -1)
154+
self.assertRaises(TypeError, self.gen.sample, population, 1.0)
154155

155156
def test_sample_distribution(self):
156157
# For the entire allowable range of 0 <= k <= N, validate that
@@ -378,122 +379,40 @@ def test_gauss(self):
378379
self.assertEqual(x1, x2)
379380
self.assertEqual(y1, y2)
380381

382+
def test_53_bits_per_float(self):
383+
# This should pass whenever a C double has 53 bit precision.
384+
span = 2 ** 53
385+
cum = 0
386+
for i in range(100):
387+
cum |= int(self.gen.random() * span)
388+
self.assertEqual(cum, span-1)
389+
381390
def test_getrandbits(self):
391+
getrandbits = self.gen.getrandbits
382392
# Verify ranges
383393
for k in range(1, 1000):
384-
self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k)
385-
self.assertEqual(self.gen.getrandbits(0), 0)
394+
self.assertTrue(0 <= getrandbits(k) < 2**k)
395+
self.assertEqual(getrandbits(0), 0)
386396

387397
# Verify all bits active
388-
getbits = self.gen.getrandbits
389398
for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]:
390399
all_bits = 2**span-1
391400
cum = 0
392401
cpl_cum = 0
393402
for i in range(100):
394-
v = getbits(span)
403+
v = getrandbits(span)
395404
cum |= v
396405
cpl_cum |= all_bits ^ v
397406
self.assertEqual(cum, all_bits)
398407
self.assertEqual(cpl_cum, all_bits)
399408

400409
# Verify argument checking
401-
self.assertRaises(TypeError, self.gen.getrandbits)
402-
self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
403-
self.assertRaises(ValueError, self.gen.getrandbits, -1)
404-
self.assertRaises(OverflowError, self.gen.getrandbits, 1<<1000)
405-
self.assertRaises(ValueError, self.gen.getrandbits, -1<<1000)
406-
self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
407-
408-
def test_pickling(self):
409-
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
410-
state = pickle.dumps(self.gen, proto)
411-
origseq = [self.gen.random() for i in range(10)]
412-
newgen = pickle.loads(state)
413-
restoredseq = [newgen.random() for i in range(10)]
414-
self.assertEqual(origseq, restoredseq)
415-
416-
def test_bug_1727780(self):
417-
# verify that version-2-pickles can be loaded
418-
# fine, whether they are created on 32-bit or 64-bit
419-
# platforms, and that version-3-pickles load fine.
420-
files = [("randv2_32.pck", 780),
421-
("randv2_64.pck", 866),
422-
("randv3.pck", 343)]
423-
for file, value in files:
424-
with open(support.findfile(file),"rb") as f:
425-
r = pickle.load(f)
426-
self.assertEqual(int(r.random()*1000), value)
427-
428-
def test_bug_9025(self):
429-
# Had problem with an uneven distribution in int(n*random())
430-
# Verify the fix by checking that distributions fall within expectations.
431-
n = 100000
432-
randrange = self.gen.randrange
433-
k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n))
434-
self.assertTrue(0.30 < k/n < .37, (k/n))
435-
436-
def test_randbytes(self):
437-
# Verify ranges
438-
for n in range(1, 10):
439-
data = self.gen.randbytes(n)
440-
self.assertEqual(type(data), bytes)
441-
self.assertEqual(len(data), n)
442-
443-
self.assertEqual(self.gen.randbytes(0), b'')
444-
445-
# Verify argument checking
446-
self.assertRaises(TypeError, self.gen.randbytes)
447-
self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
448-
self.assertRaises(ValueError, self.gen.randbytes, -1)
449-
self.assertRaises(OverflowError, self.gen.randbytes, 1<<1000)
450-
self.assertRaises((ValueError, OverflowError), self.gen.randbytes, -1<<1000)
451-
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
452-
453-
def test_mu_sigma_default_args(self):
454-
self.assertIsInstance(self.gen.normalvariate(), float)
455-
self.assertIsInstance(self.gen.gauss(), float)
456-
457-
458-
try:
459-
random.SystemRandom().random()
460-
except NotImplementedError:
461-
SystemRandom_available = False
462-
else:
463-
SystemRandom_available = True
464-
465-
@unittest.skipUnless(SystemRandom_available, "random.SystemRandom not available")
466-
class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
467-
gen = random.SystemRandom()
468-
469-
def test_autoseed(self):
470-
# Doesn't need to do anything except not fail
471-
self.gen.seed()
472-
473-
def test_saverestore(self):
474-
self.assertRaises(NotImplementedError, self.gen.getstate)
475-
self.assertRaises(NotImplementedError, self.gen.setstate, None)
476-
477-
def test_seedargs(self):
478-
# Doesn't need to do anything except not fail
479-
self.gen.seed(100)
480-
481-
def test_gauss(self):
482-
self.gen.gauss_next = None
483-
self.gen.seed(100)
484-
self.assertEqual(self.gen.gauss_next, None)
485-
486-
def test_pickling(self):
487-
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
488-
self.assertRaises(NotImplementedError, pickle.dumps, self.gen, proto)
489-
490-
def test_53_bits_per_float(self):
491-
# This should pass whenever a C double has 53 bit precision.
492-
span = 2 ** 53
493-
cum = 0
494-
for i in range(100):
495-
cum |= int(self.gen.random() * span)
496-
self.assertEqual(cum, span-1)
410+
self.assertRaises(TypeError, getrandbits)
411+
self.assertRaises(TypeError, getrandbits, 1, 2)
412+
self.assertRaises(ValueError, getrandbits, -1)
413+
self.assertRaises(OverflowError, getrandbits, 1<<1000)
414+
self.assertRaises(ValueError, getrandbits, -1<<1000)
415+
self.assertRaises(TypeError, getrandbits, 10.1)
497416

498417
def test_bigrand(self):
499418
# The randrange routine should build-up the required number of bits
@@ -572,6 +491,10 @@ def test_randrange_step(self):
572491
randrange(1000, step=100)
573492
with self.assertRaises(TypeError):
574493
randrange(1000, None, step=100)
494+
with self.assertRaises(TypeError):
495+
randrange(1000, step=MyIndex(1))
496+
with self.assertRaises(TypeError):
497+
randrange(1000, None, step=MyIndex(1))
575498

576499
def test_randbelow_logic(self, _log=log, int=int):
577500
# check bitcount transition points: 2**i and 2**(i+1)-1
@@ -594,6 +517,116 @@ def test_randbelow_logic(self, _log=log, int=int):
594517
self.assertEqual(k, numbits) # note the stronger assertion
595518
self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
596519

520+
def test_randrange_index(self):
521+
randrange = self.gen.randrange
522+
self.assertIn(randrange(MyIndex(5)), range(5))
523+
self.assertIn(randrange(MyIndex(2), MyIndex(7)), range(2, 7))
524+
self.assertIn(randrange(MyIndex(5), MyIndex(15), MyIndex(2)), range(5, 15, 2))
525+
526+
def test_randint(self):
527+
randint = self.gen.randint
528+
self.assertIn(randint(2, 5), (2, 3, 4, 5))
529+
self.assertEqual(randint(2, 2), 2)
530+
self.assertIn(randint(MyIndex(2), MyIndex(5)), (2, 3, 4, 5))
531+
self.assertEqual(randint(MyIndex(2), MyIndex(2)), 2)
532+
533+
self.assertRaises(ValueError, randint, 5, 2)
534+
self.assertRaises(TypeError, randint)
535+
self.assertRaises(TypeError, randint, 2)
536+
self.assertRaises(TypeError, randint, 2, 5, 1)
537+
self.assertRaises(TypeError, randint, 2.0, 5)
538+
self.assertRaises(TypeError, randint, 2, 5.0)
539+
540+
def test_pickling(self):
541+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
542+
state = pickle.dumps(self.gen, proto)
543+
origseq = [self.gen.random() for i in range(10)]
544+
newgen = pickle.loads(state)
545+
restoredseq = [newgen.random() for i in range(10)]
546+
self.assertEqual(origseq, restoredseq)
547+
548+
def test_bug_1727780(self):
549+
# verify that version-2-pickles can be loaded
550+
# fine, whether they are created on 32-bit or 64-bit
551+
# platforms, and that version-3-pickles load fine.
552+
files = [("randv2_32.pck", 780),
553+
("randv2_64.pck", 866),
554+
("randv3.pck", 343)]
555+
for file, value in files:
556+
with open(support.findfile(file),"rb") as f:
557+
r = pickle.load(f)
558+
self.assertEqual(int(r.random()*1000), value)
559+
560+
def test_bug_9025(self):
561+
# Had problem with an uneven distribution in int(n*random())
562+
# Verify the fix by checking that distributions fall within expectations.
563+
n = 100000
564+
randrange = self.gen.randrange
565+
k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n))
566+
self.assertTrue(0.30 < k/n < .37, (k/n))
567+
568+
def test_randrange_bug_1590891(self):
569+
start = 1000000000000
570+
stop = -100000000000000000000
571+
step = -200
572+
x = self.gen.randrange(start, stop, step)
573+
self.assertTrue(stop < x <= start)
574+
self.assertEqual((x+stop)%step, 0)
575+
576+
def test_randbytes(self):
577+
# Verify ranges
578+
for n in range(1, 10):
579+
data = self.gen.randbytes(n)
580+
self.assertEqual(type(data), bytes)
581+
self.assertEqual(len(data), n)
582+
583+
self.assertEqual(self.gen.randbytes(0), b'')
584+
585+
# Verify argument checking
586+
self.assertRaises(TypeError, self.gen.randbytes)
587+
self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
588+
self.assertRaises(ValueError, self.gen.randbytes, -1)
589+
self.assertRaises(OverflowError, self.gen.randbytes, 1<<1000)
590+
self.assertRaises((ValueError, OverflowError), self.gen.randbytes, -1<<1000)
591+
self.assertRaises(TypeError, self.gen.randbytes, 1.0)
592+
593+
def test_mu_sigma_default_args(self):
594+
self.assertIsInstance(self.gen.normalvariate(), float)
595+
self.assertIsInstance(self.gen.gauss(), float)
596+
597+
598+
try:
599+
random.SystemRandom().random()
600+
except NotImplementedError:
601+
SystemRandom_available = False
602+
else:
603+
SystemRandom_available = True
604+
605+
@unittest.skipUnless(SystemRandom_available, "random.SystemRandom not available")
606+
class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
607+
gen = random.SystemRandom()
608+
609+
def test_autoseed(self):
610+
# Doesn't need to do anything except not fail
611+
self.gen.seed()
612+
613+
def test_saverestore(self):
614+
self.assertRaises(NotImplementedError, self.gen.getstate)
615+
self.assertRaises(NotImplementedError, self.gen.setstate, None)
616+
617+
def test_seedargs(self):
618+
# Doesn't need to do anything except not fail
619+
self.gen.seed(100)
620+
621+
def test_gauss(self):
622+
self.gen.gauss_next = None
623+
self.gen.seed(100)
624+
self.assertEqual(self.gen.gauss_next, None)
625+
626+
def test_pickling(self):
627+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
628+
self.assertRaises(NotImplementedError, pickle.dumps, self.gen, proto)
629+
597630

598631
class TestRawMersenneTwister(unittest.TestCase):
599632
@test.support.cpython_only
@@ -779,38 +812,6 @@ def test_long_seed(self):
779812
seed = (1 << (10000 * 8)) - 1 # about 10K bytes
780813
self.gen.seed(seed)
781814

782-
def test_53_bits_per_float(self):
783-
# This should pass whenever a C double has 53 bit precision.
784-
span = 2 ** 53
785-
cum = 0
786-
for i in range(100):
787-
cum |= int(self.gen.random() * span)
788-
self.assertEqual(cum, span-1)
789-
790-
def test_bigrand(self):
791-
# The randrange routine should build-up the required number of bits
792-
# in stages so that all bit positions are active.
793-
span = 2 ** 500
794-
cum = 0
795-
for i in range(100):
796-
r = self.gen.randrange(span)
797-
self.assertTrue(0 <= r < span)
798-
cum |= r
799-
self.assertEqual(cum, span-1)
800-
801-
def test_bigrand_ranges(self):
802-
for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
803-
start = self.gen.randrange(2 ** (i-2))
804-
stop = self.gen.randrange(2 ** i)
805-
if stop <= start:
806-
continue
807-
self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
808-
809-
def test_rangelimits(self):
810-
for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
811-
self.assertEqual(set(range(start,stop)),
812-
set([self.gen.randrange(start,stop) for i in range(100)]))
813-
814815
def test_getrandbits(self):
815816
super().test_getrandbits()
816817

@@ -848,27 +849,6 @@ def test_randrange_uses_getrandbits(self):
848849
self.assertEqual(self.gen.randrange(2**99),
849850
97904845777343510404718956115)
850851

851-
def test_randbelow_logic(self, _log=log, int=int):
852-
# check bitcount transition points: 2**i and 2**(i+1)-1
853-
# show that: k = int(1.001 + _log(n, 2))
854-
# is equal to or one greater than the number of bits in n
855-
for i in range(1, 1000):
856-
n = 1 << i # check an exact power of two
857-
numbits = i+1
858-
k = int(1.00001 + _log(n, 2))
859-
self.assertEqual(k, numbits)
860-
self.assertEqual(n, 2**(k-1))
861-
862-
n += n - 1 # check 1 below the next power of two
863-
k = int(1.00001 + _log(n, 2))
864-
self.assertIn(k, [numbits, numbits+1])
865-
self.assertTrue(2**k > n > 2**(k-2))
866-
867-
n -= n >> 15 # check a little farther below the next power of two
868-
k = int(1.00001 + _log(n, 2))
869-
self.assertEqual(k, numbits) # note the stronger assertion
870-
self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
871-
872852
def test_randbelow_without_getrandbits(self):
873853
# Random._randbelow() can only use random() when the built-in one
874854
# has been overridden but no new getrandbits() method was supplied.
@@ -903,14 +883,6 @@ def test_randbelow_without_getrandbits(self):
903883
self.gen._randbelow_without_getrandbits(n, maxsize=maxsize)
904884
self.assertEqual(random_mock.call_count, 2)
905885

906-
def test_randrange_bug_1590891(self):
907-
start = 1000000000000
908-
stop = -100000000000000000000
909-
step = -200
910-
x = self.gen.randrange(start, stop, step)
911-
self.assertTrue(stop < x <= start)
912-
self.assertEqual((x+stop)%step, 0)
913-
914886
def test_choices_algorithms(self):
915887
# The various ways of specifying weights should produce the same results
916888
choices = self.gen.choices

0 commit comments

Comments
 (0)