diff options
author | Wolfgang Maier <wolfgang.maier@biologie.uni-freiburg.de> | 2018-04-17 17:16:17 +0200 |
---|---|---|
committer | Raymond Hettinger <rhettinger@users.noreply.github.com> | 2018-04-17 08:16:17 -0700 |
commit | ba3a87aca37cec5b1ee32cf68f4a254fa0bb2bec (patch) | |
tree | 4352a84a350b5170a7ec2c519e4c49906b8bc339 /Lib/test/test_random.py | |
parent | 28e8b66d6c632552765b5fb4573b7f3c9decc3c1 (diff) | |
download | cpython-git-ba3a87aca37cec5b1ee32cf68f4a254fa0bb2bec.tar.gz |
bpo-33144: random.Random and subclasses: split _randbelow implementation (GH-6291)
Diffstat (limited to 'Lib/test/test_random.py')
-rw-r--r-- | Lib/test/test_random.py | 80 |
1 files changed, 65 insertions, 15 deletions
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index eee245df48..d91908b03d 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -5,6 +5,7 @@ import os import time import pickle import warnings +import logging from functools import partial from math import log, exp, pi, fsum, sin, factorial from test import support @@ -619,6 +620,16 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): self.assertRaises(ValueError, self.gen.getrandbits, 0) self.assertRaises(ValueError, self.gen.getrandbits, -1) + def test_randrange_uses_getrandbits(self): + # Verify use of getrandbits by randrange + # Use same seed as in the cross-platform repeatability test + # in test_genrandbits above. + self.gen.seed(1234567) + # If randrange uses getrandbits, it should pick getrandbits(100) + # when called with a 100-bits stop argument. + self.assertEqual(self.gen.randrange(2**99), + 97904845777343510404718956115) + def test_randbelow_logic(self, _log=log, int=int): # check bitcount transition points: 2**i and 2**(i+1)-1 # show that: k = int(1.001 + _log(n, 2)) @@ -640,21 +651,22 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): self.assertEqual(k, numbits) # note the stronger assertion self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion - @unittest.mock.patch('random.Random.random') - def test_randbelow_overridden_random(self, random_mock): + def test_randbelow_without_getrandbits(self): # Random._randbelow() can only use random() when the built-in one # has been overridden but no new getrandbits() method was supplied. - random_mock.side_effect = random.SystemRandom().random maxsize = 1<<random.BPF with warnings.catch_warnings(): warnings.simplefilter("ignore", UserWarning) # Population range too large (n >= maxsize) - self.gen._randbelow(maxsize+1, maxsize = maxsize) - self.gen._randbelow(5640, maxsize = maxsize) + self.gen._randbelow_without_getrandbits( + maxsize+1, maxsize=maxsize + ) + self.gen._randbelow_without_getrandbits(5640, maxsize=maxsize) # issue 33203: test that _randbelow raises ValueError on # n == 0 also in its getrandbits-independent branch. with self.assertRaises(ValueError): - self.gen._randbelow(0, maxsize=maxsize) + self.gen._randbelow_without_getrandbits(0, maxsize=maxsize) + # This might be going too far to test a single line, but because of our # noble aim of achieving 100% test coverage we need to write a case in # which the following line in Random._randbelow() gets executed: @@ -672,8 +684,10 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): n = 42 epsilon = 0.01 limit = (maxsize - (maxsize % n)) / maxsize - random_mock.side_effect = [limit + epsilon, limit - epsilon] - self.gen._randbelow(n, maxsize = maxsize) + with unittest.mock.patch.object(random.Random, 'random') as random_mock: + random_mock.side_effect = [limit + epsilon, limit - epsilon] + self.gen._randbelow_without_getrandbits(n, maxsize=maxsize) + self.assertEqual(random_mock.call_count, 2) def test_randrange_bug_1590891(self): start = 1000000000000 @@ -926,6 +940,49 @@ class TestDistributions(unittest.TestCase): gammavariate_mock.return_value = 0.0 self.assertEqual(0.0, random.betavariate(2.71828, 3.14159)) +class TestRandomSubclassing(unittest.TestCase): + def test_random_subclass_with_kwargs(self): + # SF bug #1486663 -- this used to erroneously raise a TypeError + class Subclass(random.Random): + def __init__(self, newarg=None): + random.Random.__init__(self) + Subclass(newarg=1) + + def test_subclasses_overriding_methods(self): + # Subclasses with an overridden random, but only the original + # getrandbits method should not rely on getrandbits in for randrange, + # but should use a getrandbits-independent implementation instead. + + # subclass providing its own random **and** getrandbits methods + # like random.SystemRandom does => keep relying on getrandbits for + # randrange + class SubClass1(random.Random): + def random(self): + return super().random() + + def getrandbits(self, n): + logging.getLogger('getrandbits').info('used getrandbits') + return super().getrandbits(n) + with self.assertLogs('getrandbits'): + SubClass1().randrange(42) + + # subclass providing only random => can only use random for randrange + class SubClass2(random.Random): + def random(self): + logging.getLogger('random').info('used random') + return super().random() + with self.assertLogs('random'): + SubClass2().randrange(42) + + # subclass defining getrandbits to complement its inherited random + # => can now rely on getrandbits for randrange again + class SubClass3(SubClass2): + def getrandbits(self, n): + logging.getLogger('getrandbits').info('used getrandbits') + return super().getrandbits(n) + with self.assertLogs('getrandbits'): + SubClass3().randrange(42) + class TestModule(unittest.TestCase): def testMagicConstants(self): self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141) @@ -937,13 +994,6 @@ class TestModule(unittest.TestCase): # tests validity but not completeness of the __all__ list self.assertTrue(set(random.__all__) <= set(dir(random))) - def test_random_subclass_with_kwargs(self): - # SF bug #1486663 -- this used to erroneously raise a TypeError - class Subclass(random.Random): - def __init__(self, newarg=None): - random.Random.__init__(self) - Subclass(newarg=1) - @unittest.skipUnless(hasattr(os, "fork"), "fork() required") def test_after_fork(self): # Test the global Random instance gets reseeded in child |