| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346 |
- import unittest
- import unittest.mock
- import random
- import os
- import time
- import pickle
- import warnings
- import test.support
- from functools import partial
- from math import log, exp, pi, fsum, sin, factorial
- from test import support
- from fractions import Fraction
- from collections import abc, Counter
- class TestBasicOps:
- # Superclass with tests common to all generators.
- # Subclasses must arrange for self.gen to retrieve the Random instance
- # to be tested.
- def randomlist(self, n):
- """Helper function to make a list of random numbers"""
- return [self.gen.random() for i in range(n)]
- def test_autoseed(self):
- self.gen.seed()
- state1 = self.gen.getstate()
- time.sleep(0.1)
- self.gen.seed() # different seeds at different times
- state2 = self.gen.getstate()
- self.assertNotEqual(state1, state2)
- def test_saverestore(self):
- N = 1000
- self.gen.seed()
- state = self.gen.getstate()
- randseq = self.randomlist(N)
- self.gen.setstate(state) # should regenerate the same sequence
- self.assertEqual(randseq, self.randomlist(N))
- def test_seedargs(self):
- # Seed value with a negative hash.
- class MySeed(object):
- def __hash__(self):
- return -1729
- for arg in [None, 0, 1, -1, 10**20, -(10**20),
- False, True, 3.14, 'a']:
- self.gen.seed(arg)
- for arg in [1+2j, tuple('abc'), MySeed()]:
- with self.assertRaises(TypeError):
- self.gen.seed(arg)
- for arg in [list(range(3)), dict(one=1)]:
- self.assertRaises(TypeError, self.gen.seed, arg)
- self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
- self.assertRaises(TypeError, type(self.gen), [])
- def test_seed_no_mutate_bug_44018(self):
- a = bytearray(b'1234')
- self.gen.seed(a)
- self.assertEqual(a, bytearray(b'1234'))
- @unittest.mock.patch('random._urandom') # os.urandom
- def test_seed_when_randomness_source_not_found(self, urandom_mock):
- # Random.seed() uses time.time() when an operating system specific
- # randomness source is not found. To test this on machines where it
- # exists, run the above test, test_seedargs(), again after mocking
- # os.urandom() so that it raises the exception expected when the
- # randomness source is not available.
- urandom_mock.side_effect = NotImplementedError
- self.test_seedargs()
- def test_shuffle(self):
- shuffle = self.gen.shuffle
- lst = []
- shuffle(lst)
- self.assertEqual(lst, [])
- lst = [37]
- shuffle(lst)
- self.assertEqual(lst, [37])
- seqs = [list(range(n)) for n in range(10)]
- shuffled_seqs = [list(range(n)) for n in range(10)]
- for shuffled_seq in shuffled_seqs:
- shuffle(shuffled_seq)
- for (seq, shuffled_seq) in zip(seqs, shuffled_seqs):
- self.assertEqual(len(seq), len(shuffled_seq))
- self.assertEqual(set(seq), set(shuffled_seq))
- # The above tests all would pass if the shuffle was a
- # no-op. The following non-deterministic test covers that. It
- # asserts that the shuffled sequence of 1000 distinct elements
- # must be different from the original one. Although there is
- # mathematically a non-zero probability that this could
- # actually happen in a genuinely random shuffle, it is
- # completely negligible, given that the number of possible
- # permutations of 1000 objects is 1000! (factorial of 1000),
- # which is considerably larger than the number of atoms in the
- # universe...
- lst = list(range(1000))
- shuffled_lst = list(range(1000))
- shuffle(shuffled_lst)
- self.assertTrue(lst != shuffled_lst)
- shuffle(lst)
- self.assertTrue(lst != shuffled_lst)
- self.assertRaises(TypeError, shuffle, (1, 2, 3))
- def test_choice(self):
- choice = self.gen.choice
- with self.assertRaises(IndexError):
- choice([])
- self.assertEqual(choice([50]), 50)
- self.assertIn(choice([25, 75]), [25, 75])
- def test_choice_with_numpy(self):
- # Accommodation for NumPy arrays which have disabled __bool__().
- # See: https://github.com/python/cpython/issues/100805
- choice = self.gen.choice
- class NA(list):
- "Simulate numpy.array() behavior"
- def __bool__(self):
- raise RuntimeError
- with self.assertRaises(IndexError):
- choice(NA([]))
- self.assertEqual(choice(NA([50])), 50)
- self.assertIn(choice(NA([25, 75])), [25, 75])
- def test_sample(self):
- # For the entire allowable range of 0 <= k <= N, validate that
- # the sample is of the correct length and contains only unique items
- N = 100
- population = range(N)
- for k in range(N+1):
- s = self.gen.sample(population, k)
- self.assertEqual(len(s), k)
- uniq = set(s)
- self.assertEqual(len(uniq), k)
- self.assertTrue(uniq <= set(population))
- self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0
- # Exception raised if size of sample exceeds that of population
- self.assertRaises(ValueError, self.gen.sample, population, N+1)
- self.assertRaises(ValueError, self.gen.sample, [], -1)
- def test_sample_distribution(self):
- # For the entire allowable range of 0 <= k <= N, validate that
- # sample generates all possible permutations
- n = 5
- pop = range(n)
- trials = 10000 # large num prevents false negatives without slowing normal case
- for k in range(n):
- expected = factorial(n) // factorial(n-k)
- perms = {}
- for i in range(trials):
- perms[tuple(self.gen.sample(pop, k))] = None
- if len(perms) == expected:
- break
- else:
- self.fail()
- def test_sample_inputs(self):
- # SF bug #801342 -- population can be any iterable defining __len__()
- self.gen.sample(range(20), 2)
- self.gen.sample(range(20), 2)
- self.gen.sample(str('abcdefghijklmnopqrst'), 2)
- self.gen.sample(tuple('abcdefghijklmnopqrst'), 2)
- def test_sample_on_dicts(self):
- self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2)
- def test_sample_on_sets(self):
- with self.assertRaises(TypeError):
- population = {10, 20, 30, 40, 50, 60, 70}
- self.gen.sample(population, k=5)
- def test_sample_on_seqsets(self):
- class SeqSet(abc.Sequence, abc.Set):
- def __init__(self, items):
- self._items = items
- def __len__(self):
- return len(self._items)
- def __getitem__(self, index):
- return self._items[index]
- population = SeqSet([2, 4, 1, 3])
- with warnings.catch_warnings():
- warnings.simplefilter("error", DeprecationWarning)
- self.gen.sample(population, k=2)
- def test_sample_with_counts(self):
- sample = self.gen.sample
- # General case
- colors = ['red', 'green', 'blue', 'orange', 'black', 'brown', 'amber']
- counts = [500, 200, 20, 10, 5, 0, 1 ]
- k = 700
- summary = Counter(sample(colors, counts=counts, k=k))
- self.assertEqual(sum(summary.values()), k)
- for color, weight in zip(colors, counts):
- self.assertLessEqual(summary[color], weight)
- self.assertNotIn('brown', summary)
- # Case that exhausts the population
- k = sum(counts)
- summary = Counter(sample(colors, counts=counts, k=k))
- self.assertEqual(sum(summary.values()), k)
- for color, weight in zip(colors, counts):
- self.assertLessEqual(summary[color], weight)
- self.assertNotIn('brown', summary)
- # Case with population size of 1
- summary = Counter(sample(['x'], counts=[10], k=8))
- self.assertEqual(summary, Counter(x=8))
- # Case with all counts equal.
- nc = len(colors)
- summary = Counter(sample(colors, counts=[10]*nc, k=10*nc))
- self.assertEqual(summary, Counter(10*colors))
- # Test error handling
- with self.assertRaises(TypeError):
- sample(['red', 'green', 'blue'], counts=10, k=10) # counts not iterable
- with self.assertRaises(ValueError):
- sample(['red', 'green', 'blue'], counts=[-3, -7, -8], k=2) # counts are negative
- with self.assertRaises(ValueError):
- sample(['red', 'green', 'blue'], counts=[0, 0, 0], k=2) # counts are zero
- with self.assertRaises(ValueError):
- sample(['red', 'green'], counts=[10, 10], k=21) # population too small
- with self.assertRaises(ValueError):
- sample(['red', 'green', 'blue'], counts=[1, 2], k=2) # too few counts
- with self.assertRaises(ValueError):
- sample(['red', 'green', 'blue'], counts=[1, 2, 3, 4], k=2) # too many counts
- def test_choices(self):
- choices = self.gen.choices
- data = ['red', 'green', 'blue', 'yellow']
- str_data = 'abcd'
- range_data = range(4)
- set_data = set(range(4))
- # basic functionality
- for sample in [
- choices(data, k=5),
- choices(data, range(4), k=5),
- choices(k=5, population=data, weights=range(4)),
- choices(k=5, population=data, cum_weights=range(4)),
- ]:
- self.assertEqual(len(sample), 5)
- self.assertEqual(type(sample), list)
- self.assertTrue(set(sample) <= set(data))
- # test argument handling
- with self.assertRaises(TypeError): # missing arguments
- choices(2)
- self.assertEqual(choices(data, k=0), []) # k == 0
- self.assertEqual(choices(data, k=-1), []) # negative k behaves like ``[0] * -1``
- with self.assertRaises(TypeError):
- choices(data, k=2.5) # k is a float
- self.assertTrue(set(choices(str_data, k=5)) <= set(str_data)) # population is a string sequence
- self.assertTrue(set(choices(range_data, k=5)) <= set(range_data)) # population is a range
- with self.assertRaises(TypeError):
- choices(set_data, k=2) # population is not a sequence
- self.assertTrue(set(choices(data, None, k=5)) <= set(data)) # weights is None
- self.assertTrue(set(choices(data, weights=None, k=5)) <= set(data))
- with self.assertRaises(ValueError):
- choices(data, [1,2], k=5) # len(weights) != len(population)
- with self.assertRaises(TypeError):
- choices(data, 10, k=5) # non-iterable weights
- with self.assertRaises(TypeError):
- choices(data, [None]*4, k=5) # non-numeric weights
- for weights in [
- [15, 10, 25, 30], # integer weights
- [15.1, 10.2, 25.2, 30.3], # float weights
- [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights
- [True, False, True, False] # booleans (include / exclude)
- ]:
- self.assertTrue(set(choices(data, weights, k=5)) <= set(data))
- with self.assertRaises(ValueError):
- choices(data, cum_weights=[1,2], k=5) # len(weights) != len(population)
- with self.assertRaises(TypeError):
- choices(data, cum_weights=10, k=5) # non-iterable cum_weights
- with self.assertRaises(TypeError):
- choices(data, cum_weights=[None]*4, k=5) # non-numeric cum_weights
- with self.assertRaises(TypeError):
- choices(data, range(4), cum_weights=range(4), k=5) # both weights and cum_weights
- for weights in [
- [15, 10, 25, 30], # integer cum_weights
- [15.1, 10.2, 25.2, 30.3], # float cum_weights
- [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights
- ]:
- self.assertTrue(set(choices(data, cum_weights=weights, k=5)) <= set(data))
- # Test weight focused on a single element of the population
- self.assertEqual(choices('abcd', [1, 0, 0, 0]), ['a'])
- self.assertEqual(choices('abcd', [0, 1, 0, 0]), ['b'])
- self.assertEqual(choices('abcd', [0, 0, 1, 0]), ['c'])
- self.assertEqual(choices('abcd', [0, 0, 0, 1]), ['d'])
- # Test consistency with random.choice() for empty population
- with self.assertRaises(IndexError):
- choices([], k=1)
- with self.assertRaises(IndexError):
- choices([], weights=[], k=1)
- with self.assertRaises(IndexError):
- choices([], cum_weights=[], k=5)
- def test_choices_subnormal(self):
- # Subnormal weights would occasionally trigger an IndexError
- # in choices() when the value returned by random() was large
- # enough to make `random() * total` round up to the total.
- # See https://bugs.python.org/msg275594 for more detail.
- choices = self.gen.choices
- choices(population=[1, 2], weights=[1e-323, 1e-323], k=5000)
- def test_choices_with_all_zero_weights(self):
- # See issue #38881
- with self.assertRaises(ValueError):
- self.gen.choices('AB', [0.0, 0.0])
- def test_choices_negative_total(self):
- with self.assertRaises(ValueError):
- self.gen.choices('ABC', [3, -5, 1])
- def test_choices_infinite_total(self):
- with self.assertRaises(ValueError):
- self.gen.choices('A', [float('inf')])
- with self.assertRaises(ValueError):
- self.gen.choices('AB', [0.0, float('inf')])
- with self.assertRaises(ValueError):
- self.gen.choices('AB', [-float('inf'), 123])
- with self.assertRaises(ValueError):
- self.gen.choices('AB', [0.0, float('nan')])
- with self.assertRaises(ValueError):
- self.gen.choices('AB', [float('-inf'), float('inf')])
- def test_gauss(self):
- # Ensure that the seed() method initializes all the hidden state. In
- # particular, through 2.2.1 it failed to reset a piece of state used
- # by (and only by) the .gauss() method.
- for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
- self.gen.seed(seed)
- x1 = self.gen.random()
- y1 = self.gen.gauss(0, 1)
- self.gen.seed(seed)
- x2 = self.gen.random()
- y2 = self.gen.gauss(0, 1)
- self.assertEqual(x1, x2)
- self.assertEqual(y1, y2)
- def test_getrandbits(self):
- # Verify ranges
- for k in range(1, 1000):
- self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k)
- self.assertEqual(self.gen.getrandbits(0), 0)
- # Verify all bits active
- getbits = self.gen.getrandbits
- for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]:
- all_bits = 2**span-1
- cum = 0
- cpl_cum = 0
- for i in range(100):
- v = getbits(span)
- cum |= v
- cpl_cum |= all_bits ^ v
- self.assertEqual(cum, all_bits)
- self.assertEqual(cpl_cum, all_bits)
- # Verify argument checking
- self.assertRaises(TypeError, self.gen.getrandbits)
- self.assertRaises(TypeError, self.gen.getrandbits, 1, 2)
- self.assertRaises(ValueError, self.gen.getrandbits, -1)
- self.assertRaises(TypeError, self.gen.getrandbits, 10.1)
- def test_pickling(self):
- for proto in range(pickle.HIGHEST_PROTOCOL + 1):
- state = pickle.dumps(self.gen, proto)
- origseq = [self.gen.random() for i in range(10)]
- newgen = pickle.loads(state)
- restoredseq = [newgen.random() for i in range(10)]
- self.assertEqual(origseq, restoredseq)
- def test_bug_1727780(self):
- # verify that version-2-pickles can be loaded
- # fine, whether they are created on 32-bit or 64-bit
- # platforms, and that version-3-pickles load fine.
- files = [("randv2_32.pck", 780),
- ("randv2_64.pck", 866),
- ("randv3.pck", 343)]
- for file, value in files:
- with open(support.findfile(file),"rb") as f:
- r = pickle.load(f)
- self.assertEqual(int(r.random()*1000), value)
- def test_bug_9025(self):
- # Had problem with an uneven distribution in int(n*random())
- # Verify the fix by checking that distributions fall within expectations.
- n = 100000
- randrange = self.gen.randrange
- k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n))
- self.assertTrue(0.30 < k/n < .37, (k/n))
- def test_randbytes(self):
- # Verify ranges
- for n in range(1, 10):
- data = self.gen.randbytes(n)
- self.assertEqual(type(data), bytes)
- self.assertEqual(len(data), n)
- self.assertEqual(self.gen.randbytes(0), b'')
- # Verify argument checking
- self.assertRaises(TypeError, self.gen.randbytes)
- self.assertRaises(TypeError, self.gen.randbytes, 1, 2)
- self.assertRaises(ValueError, self.gen.randbytes, -1)
- self.assertRaises(TypeError, self.gen.randbytes, 1.0)
- def test_mu_sigma_default_args(self):
- self.assertIsInstance(self.gen.normalvariate(), float)
- self.assertIsInstance(self.gen.gauss(), float)
- try:
- random.SystemRandom().random()
- except NotImplementedError:
- SystemRandom_available = False
- else:
- SystemRandom_available = True
- @unittest.skipUnless(SystemRandom_available, "random.SystemRandom not available")
- class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
- gen = random.SystemRandom()
- def test_autoseed(self):
- # Doesn't need to do anything except not fail
- self.gen.seed()
- def test_saverestore(self):
- self.assertRaises(NotImplementedError, self.gen.getstate)
- self.assertRaises(NotImplementedError, self.gen.setstate, None)
- def test_seedargs(self):
- # Doesn't need to do anything except not fail
- self.gen.seed(100)
- def test_gauss(self):
- self.gen.gauss_next = None
- self.gen.seed(100)
- self.assertEqual(self.gen.gauss_next, None)
- def test_pickling(self):
- for proto in range(pickle.HIGHEST_PROTOCOL + 1):
- self.assertRaises(NotImplementedError, pickle.dumps, self.gen, proto)
- def test_53_bits_per_float(self):
- # This should pass whenever a C double has 53 bit precision.
- span = 2 ** 53
- cum = 0
- for i in range(100):
- cum |= int(self.gen.random() * span)
- self.assertEqual(cum, span-1)
- def test_bigrand(self):
- # The randrange routine should build-up the required number of bits
- # in stages so that all bit positions are active.
- span = 2 ** 500
- cum = 0
- for i in range(100):
- r = self.gen.randrange(span)
- self.assertTrue(0 <= r < span)
- cum |= r
- self.assertEqual(cum, span-1)
- def test_bigrand_ranges(self):
- for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
- start = self.gen.randrange(2 ** (i-2))
- stop = self.gen.randrange(2 ** i)
- if stop <= start:
- continue
- self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
- def test_rangelimits(self):
- for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
- self.assertEqual(set(range(start,stop)),
- set([self.gen.randrange(start,stop) for i in range(100)]))
- def test_randrange_nonunit_step(self):
- rint = self.gen.randrange(0, 10, 2)
- self.assertIn(rint, (0, 2, 4, 6, 8))
- rint = self.gen.randrange(0, 2, 2)
- self.assertEqual(rint, 0)
- def test_randrange_errors(self):
- raises = partial(self.assertRaises, ValueError, self.gen.randrange)
- # Empty range
- raises(3, 3)
- raises(-721)
- raises(0, 100, -12)
- # Non-integer start/stop
- self.assertWarns(DeprecationWarning, raises, 3.14159)
- self.assertWarns(DeprecationWarning, self.gen.randrange, 3.0)
- self.assertWarns(DeprecationWarning, self.gen.randrange, Fraction(3, 1))
- self.assertWarns(DeprecationWarning, raises, '3')
- self.assertWarns(DeprecationWarning, raises, 0, 2.71828)
- self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 2.0)
- self.assertWarns(DeprecationWarning, self.gen.randrange, 0, Fraction(2, 1))
- self.assertWarns(DeprecationWarning, raises, 0, '2')
- # Zero and non-integer step
- raises(0, 42, 0)
- self.assertWarns(DeprecationWarning, raises, 0, 42, 0.0)
- self.assertWarns(DeprecationWarning, raises, 0, 0, 0.0)
- self.assertWarns(DeprecationWarning, raises, 0, 42, 3.14159)
- self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 3.0)
- self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, Fraction(3, 1))
- self.assertWarns(DeprecationWarning, raises, 0, 42, '3')
- self.assertWarns(DeprecationWarning, self.gen.randrange, 0, 42, 1.0)
- self.assertWarns(DeprecationWarning, raises, 0, 0, 1.0)
- def test_randrange_argument_handling(self):
- randrange = self.gen.randrange
- with self.assertWarns(DeprecationWarning):
- randrange(10.0, 20, 2)
- with self.assertWarns(DeprecationWarning):
- randrange(10, 20.0, 2)
- with self.assertWarns(DeprecationWarning):
- randrange(10, 20, 1.0)
- with self.assertWarns(DeprecationWarning):
- randrange(10, 20, 2.0)
- with self.assertWarns(DeprecationWarning):
- with self.assertRaises(ValueError):
- randrange(10.5)
- with self.assertWarns(DeprecationWarning):
- with self.assertRaises(ValueError):
- randrange(10, 20.5)
- with self.assertWarns(DeprecationWarning):
- with self.assertRaises(ValueError):
- randrange(10, 20, 1.5)
- def test_randrange_step(self):
- # bpo-42772: When stop is None, the step argument was being ignored.
- randrange = self.gen.randrange
- with self.assertRaises(TypeError):
- randrange(1000, step=100)
- with self.assertRaises(TypeError):
- randrange(1000, None, step=100)
- 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))
- # is equal to or one greater than the number of bits in n
- for i in range(1, 1000):
- n = 1 << i # check an exact power of two
- numbits = i+1
- k = int(1.00001 + _log(n, 2))
- self.assertEqual(k, numbits)
- self.assertEqual(n, 2**(k-1))
- n += n - 1 # check 1 below the next power of two
- k = int(1.00001 + _log(n, 2))
- self.assertIn(k, [numbits, numbits+1])
- self.assertTrue(2**k > n > 2**(k-2))
- n -= n >> 15 # check a little farther below the next power of two
- k = int(1.00001 + _log(n, 2))
- self.assertEqual(k, numbits) # note the stronger assertion
- self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
- class TestRawMersenneTwister(unittest.TestCase):
- @test.support.cpython_only
- def test_bug_41052(self):
- # _random.Random should not be allowed to serialization
- import _random
- for proto in range(pickle.HIGHEST_PROTOCOL + 1):
- r = _random.Random()
- self.assertRaises(TypeError, pickle.dumps, r, proto)
- @test.support.cpython_only
- def test_bug_42008(self):
- # _random.Random should call seed with first element of arg tuple
- import _random
- r1 = _random.Random()
- r1.seed(8675309)
- r2 = _random.Random(8675309)
- self.assertEqual(r1.random(), r2.random())
- class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase):
- gen = random.Random()
- def test_guaranteed_stable(self):
- # These sequences are guaranteed to stay the same across versions of python
- self.gen.seed(3456147, version=1)
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1',
- '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1'])
- self.gen.seed("the quick brown fox", version=2)
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.1239ddfb11b7cp-3', '0x1.b3cbb5c51b120p-4',
- '0x1.8c4f55116b60fp-1', '0x1.63eb525174a27p-1'])
- def test_bug_27706(self):
- # Verify that version 1 seeds are unaffected by hash randomization
- self.gen.seed('nofar', version=1) # hash('nofar') == 5990528763808513177
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.8645314505ad7p-1', '0x1.afb1f82e40a40p-5',
- '0x1.2a59d2285e971p-1', '0x1.56977142a7880p-6'])
- self.gen.seed('rachel', version=1) # hash('rachel') == -9091735575445484789
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.0b294cc856fcdp-1', '0x1.2ad22d79e77b8p-3',
- '0x1.3052b9c072678p-2', '0x1.578f332106574p-3'])
- self.gen.seed('', version=1) # hash('') == 0
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.b0580f98a7dbep-1', '0x1.84129978f9c1ap-1',
- '0x1.aeaa51052e978p-2', '0x1.092178fb945a6p-2'])
- def test_bug_31478(self):
- # There shouldn't be an assertion failure in _random.Random.seed() in
- # case the argument has a bad __abs__() method.
- class BadInt(int):
- def __abs__(self):
- return None
- try:
- self.gen.seed(BadInt())
- except TypeError:
- pass
- def test_bug_31482(self):
- # Verify that version 1 seeds are unaffected by hash randomization
- # when the seeds are expressed as bytes rather than strings.
- # The hash(b) values listed are the Python2.7 hash() values
- # which were used for seeding.
- self.gen.seed(b'nofar', version=1) # hash('nofar') == 5990528763808513177
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.8645314505ad7p-1', '0x1.afb1f82e40a40p-5',
- '0x1.2a59d2285e971p-1', '0x1.56977142a7880p-6'])
- self.gen.seed(b'rachel', version=1) # hash('rachel') == -9091735575445484789
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.0b294cc856fcdp-1', '0x1.2ad22d79e77b8p-3',
- '0x1.3052b9c072678p-2', '0x1.578f332106574p-3'])
- self.gen.seed(b'', version=1) # hash('') == 0
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.b0580f98a7dbep-1', '0x1.84129978f9c1ap-1',
- '0x1.aeaa51052e978p-2', '0x1.092178fb945a6p-2'])
- b = b'\x00\x20\x40\x60\x80\xA0\xC0\xE0\xF0'
- self.gen.seed(b, version=1) # hash(b) == 5015594239749365497
- self.assertEqual([self.gen.random().hex() for i in range(4)],
- ['0x1.52c2fde444d23p-1', '0x1.875174f0daea4p-2',
- '0x1.9e9b2c50e5cd2p-1', '0x1.fa57768bd321cp-2'])
- def test_setstate_first_arg(self):
- self.assertRaises(ValueError, self.gen.setstate, (1, None, None))
- def test_setstate_middle_arg(self):
- start_state = self.gen.getstate()
- # Wrong type, s/b tuple
- self.assertRaises(TypeError, self.gen.setstate, (2, None, None))
- # Wrong length, s/b 625
- self.assertRaises(ValueError, self.gen.setstate, (2, (1,2,3), None))
- # Wrong type, s/b tuple of 625 ints
- self.assertRaises(TypeError, self.gen.setstate, (2, ('a',)*625, None))
- # Last element s/b an int also
- self.assertRaises(TypeError, self.gen.setstate, (2, (0,)*624+('a',), None))
- # Last element s/b between 0 and 624
- with self.assertRaises((ValueError, OverflowError)):
- self.gen.setstate((2, (1,)*624+(625,), None))
- with self.assertRaises((ValueError, OverflowError)):
- self.gen.setstate((2, (1,)*624+(-1,), None))
- # Failed calls to setstate() should not have changed the state.
- bits100 = self.gen.getrandbits(100)
- self.gen.setstate(start_state)
- self.assertEqual(self.gen.getrandbits(100), bits100)
- # Little trick to make "tuple(x % (2**32) for x in internalstate)"
- # raise ValueError. I cannot think of a simple way to achieve this, so
- # I am opting for using a generator as the middle argument of setstate
- # which attempts to cast a NaN to integer.
- state_values = self.gen.getstate()[1]
- state_values = list(state_values)
- state_values[-1] = float('nan')
- state = (int(x) for x in state_values)
- self.assertRaises(TypeError, self.gen.setstate, (2, state, None))
- def test_referenceImplementation(self):
- # Compare the python implementation with results from the original
- # code. Create 2000 53-bit precision random floats. Compare only
- # the last ten entries to show that the independent implementations
- # are tracking. Here is the main() function needed to create the
- # list of expected random numbers:
- # void main(void){
- # int i;
- # unsigned long init[4]={61731, 24903, 614, 42143}, length=4;
- # init_by_array(init, length);
- # for (i=0; i<2000; i++) {
- # printf("%.15f ", genrand_res53());
- # if (i%5==4) printf("\n");
- # }
- # }
- expected = [0.45839803073713259,
- 0.86057815201978782,
- 0.92848331726782152,
- 0.35932681119782461,
- 0.081823493762449573,
- 0.14332226470169329,
- 0.084297823823520024,
- 0.53814864671831453,
- 0.089215024911993401,
- 0.78486196105372907]
- self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
- actual = self.randomlist(2000)[-10:]
- for a, e in zip(actual, expected):
- self.assertAlmostEqual(a,e,places=14)
- def test_strong_reference_implementation(self):
- # Like test_referenceImplementation, but checks for exact bit-level
- # equality. This should pass on any box where C double contains
- # at least 53 bits of precision (the underlying algorithm suffers
- # no rounding errors -- all results are exact).
- from math import ldexp
- expected = [0x0eab3258d2231f,
- 0x1b89db315277a5,
- 0x1db622a5518016,
- 0x0b7f9af0d575bf,
- 0x029e4c4db82240,
- 0x04961892f5d673,
- 0x02b291598e4589,
- 0x11388382c15694,
- 0x02dad977c9e1fe,
- 0x191d96d4d334c6]
- self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96))
- actual = self.randomlist(2000)[-10:]
- for a, e in zip(actual, expected):
- self.assertEqual(int(ldexp(a, 53)), e)
- def test_long_seed(self):
- # This is most interesting to run in debug mode, just to make sure
- # nothing blows up. Under the covers, a dynamically resized array
- # is allocated, consuming space proportional to the number of bits
- # in the seed. Unfortunately, that's a quadratic-time algorithm,
- # so don't make this horribly big.
- seed = (1 << (10000 * 8)) - 1 # about 10K bytes
- self.gen.seed(seed)
- def test_53_bits_per_float(self):
- # This should pass whenever a C double has 53 bit precision.
- span = 2 ** 53
- cum = 0
- for i in range(100):
- cum |= int(self.gen.random() * span)
- self.assertEqual(cum, span-1)
- def test_bigrand(self):
- # The randrange routine should build-up the required number of bits
- # in stages so that all bit positions are active.
- span = 2 ** 500
- cum = 0
- for i in range(100):
- r = self.gen.randrange(span)
- self.assertTrue(0 <= r < span)
- cum |= r
- self.assertEqual(cum, span-1)
- def test_bigrand_ranges(self):
- for i in [40,80, 160, 200, 211, 250, 375, 512, 550]:
- start = self.gen.randrange(2 ** (i-2))
- stop = self.gen.randrange(2 ** i)
- if stop <= start:
- continue
- self.assertTrue(start <= self.gen.randrange(start, stop) < stop)
- def test_rangelimits(self):
- for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
- self.assertEqual(set(range(start,stop)),
- set([self.gen.randrange(start,stop) for i in range(100)]))
- def test_getrandbits(self):
- super().test_getrandbits()
- # Verify cross-platform repeatability
- self.gen.seed(1234567)
- self.assertEqual(self.gen.getrandbits(100),
- 97904845777343510404718956115)
- def test_randrange_uses_getrandbits(self):
- # Verify use of getrandbits by randrange
- # Use same seed as in the cross-platform repeatability test
- # in test_getrandbits 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))
- # is equal to or one greater than the number of bits in n
- for i in range(1, 1000):
- n = 1 << i # check an exact power of two
- numbits = i+1
- k = int(1.00001 + _log(n, 2))
- self.assertEqual(k, numbits)
- self.assertEqual(n, 2**(k-1))
- n += n - 1 # check 1 below the next power of two
- k = int(1.00001 + _log(n, 2))
- self.assertIn(k, [numbits, numbits+1])
- self.assertTrue(2**k > n > 2**(k-2))
- n -= n >> 15 # check a little farther below the next power of two
- k = int(1.00001 + _log(n, 2))
- self.assertEqual(k, numbits) # note the stronger assertion
- self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion
- 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.
- maxsize = 1<<random.BPF
- with warnings.catch_warnings():
- warnings.simplefilter("ignore", UserWarning)
- # Population range too large (n >= maxsize)
- self.gen._randbelow_without_getrandbits(
- maxsize+1, maxsize=maxsize
- )
- self.gen._randbelow_without_getrandbits(5640, 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:
- #
- # rem = maxsize % n
- # limit = (maxsize - rem) / maxsize
- # r = random()
- # while r >= limit:
- # r = random() # <== *This line* <==<
- #
- # Therefore, to guarantee that the while loop is executed at least
- # once, we need to mock random() so that it returns a number greater
- # than 'limit' the first time it gets called.
- n = 42
- epsilon = 0.01
- limit = (maxsize - (maxsize % n)) / 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
- stop = -100000000000000000000
- step = -200
- x = self.gen.randrange(start, stop, step)
- self.assertTrue(stop < x <= start)
- self.assertEqual((x+stop)%step, 0)
- def test_choices_algorithms(self):
- # The various ways of specifying weights should produce the same results
- choices = self.gen.choices
- n = 104729
- self.gen.seed(8675309)
- a = self.gen.choices(range(n), k=10000)
- self.gen.seed(8675309)
- b = self.gen.choices(range(n), [1]*n, k=10000)
- self.assertEqual(a, b)
- self.gen.seed(8675309)
- c = self.gen.choices(range(n), cum_weights=range(1, n+1), k=10000)
- self.assertEqual(a, c)
- # American Roulette
- population = ['Red', 'Black', 'Green']
- weights = [18, 18, 2]
- cum_weights = [18, 36, 38]
- expanded_population = ['Red'] * 18 + ['Black'] * 18 + ['Green'] * 2
- self.gen.seed(9035768)
- a = self.gen.choices(expanded_population, k=10000)
- self.gen.seed(9035768)
- b = self.gen.choices(population, weights, k=10000)
- self.assertEqual(a, b)
- self.gen.seed(9035768)
- c = self.gen.choices(population, cum_weights=cum_weights, k=10000)
- self.assertEqual(a, c)
- def test_randbytes(self):
- super().test_randbytes()
- # Mersenne Twister randbytes() is deterministic
- # and does not depend on the endian and bitness.
- seed = 8675309
- expected = b'3\xa8\xf9f\xf4\xa4\xd06\x19\x8f\x9f\x82\x02oe\xf0'
- self.gen.seed(seed)
- self.assertEqual(self.gen.randbytes(16), expected)
- # randbytes(0) must not consume any entropy
- self.gen.seed(seed)
- self.assertEqual(self.gen.randbytes(0), b'')
- self.assertEqual(self.gen.randbytes(16), expected)
- # Four randbytes(4) calls give the same output than randbytes(16)
- self.gen.seed(seed)
- self.assertEqual(b''.join([self.gen.randbytes(4) for _ in range(4)]),
- expected)
- # Each randbytes(1), randbytes(2) or randbytes(3) call consumes
- # 4 bytes of entropy
- self.gen.seed(seed)
- expected1 = expected[3::4]
- self.assertEqual(b''.join(self.gen.randbytes(1) for _ in range(4)),
- expected1)
- self.gen.seed(seed)
- expected2 = b''.join(expected[i + 2: i + 4]
- for i in range(0, len(expected), 4))
- self.assertEqual(b''.join(self.gen.randbytes(2) for _ in range(4)),
- expected2)
- self.gen.seed(seed)
- expected3 = b''.join(expected[i + 1: i + 4]
- for i in range(0, len(expected), 4))
- self.assertEqual(b''.join(self.gen.randbytes(3) for _ in range(4)),
- expected3)
- def test_randbytes_getrandbits(self):
- # There is a simple relation between randbytes() and getrandbits()
- seed = 2849427419
- gen2 = random.Random()
- self.gen.seed(seed)
- gen2.seed(seed)
- for n in range(9):
- self.assertEqual(self.gen.randbytes(n),
- gen2.getrandbits(n * 8).to_bytes(n, 'little'))
- def test_sample_counts_equivalence(self):
- # Test the documented strong equivalence to a sample with repeated elements.
- # We run this test on random.Random() which makes deterministic selections
- # for a given seed value.
- sample = self.gen.sample
- seed = self.gen.seed
- colors = ['red', 'green', 'blue', 'orange', 'black', 'amber']
- counts = [500, 200, 20, 10, 5, 1 ]
- k = 700
- seed(8675309)
- s1 = sample(colors, counts=counts, k=k)
- seed(8675309)
- expanded = [color for (color, count) in zip(colors, counts) for i in range(count)]
- self.assertEqual(len(expanded), sum(counts))
- s2 = sample(expanded, k=k)
- self.assertEqual(s1, s2)
- pop = 'abcdefghi'
- counts = [10, 9, 8, 7, 6, 5, 4, 3, 2]
- seed(8675309)
- s1 = ''.join(sample(pop, counts=counts, k=30))
- expanded = ''.join([letter for (letter, count) in zip(pop, counts) for i in range(count)])
- seed(8675309)
- s2 = ''.join(sample(expanded, k=30))
- self.assertEqual(s1, s2)
- def gamma(z, sqrt2pi=(2.0*pi)**0.5):
- # Reflection to right half of complex plane
- if z < 0.5:
- return pi / sin(pi*z) / gamma(1.0-z)
- # Lanczos approximation with g=7
- az = z + (7.0 - 0.5)
- return az ** (z-0.5) / exp(az) * sqrt2pi * fsum([
- 0.9999999999995183,
- 676.5203681218835 / z,
- -1259.139216722289 / (z+1.0),
- 771.3234287757674 / (z+2.0),
- -176.6150291498386 / (z+3.0),
- 12.50734324009056 / (z+4.0),
- -0.1385710331296526 / (z+5.0),
- 0.9934937113930748e-05 / (z+6.0),
- 0.1659470187408462e-06 / (z+7.0),
- ])
- class TestDistributions(unittest.TestCase):
- def test_zeroinputs(self):
- # Verify that distributions can handle a series of zero inputs'
- g = random.Random()
- x = [g.random() for i in range(50)] + [0.0]*5
- g.random = x[:].pop; g.uniform(1,10)
- g.random = x[:].pop; g.paretovariate(1.0)
- g.random = x[:].pop; g.expovariate(1.0)
- g.random = x[:].pop; g.weibullvariate(1.0, 1.0)
- g.random = x[:].pop; g.vonmisesvariate(1.0, 1.0)
- g.random = x[:].pop; g.normalvariate(0.0, 1.0)
- g.random = x[:].pop; g.gauss(0.0, 1.0)
- g.random = x[:].pop; g.lognormvariate(0.0, 1.0)
- g.random = x[:].pop; g.vonmisesvariate(0.0, 1.0)
- g.random = x[:].pop; g.gammavariate(0.01, 1.0)
- g.random = x[:].pop; g.gammavariate(1.0, 1.0)
- g.random = x[:].pop; g.gammavariate(200.0, 1.0)
- g.random = x[:].pop; g.betavariate(3.0, 3.0)
- g.random = x[:].pop; g.triangular(0.0, 1.0, 1.0/3.0)
- def test_avg_std(self):
- # Use integration to test distribution average and standard deviation.
- # Only works for distributions which do not consume variates in pairs
- g = random.Random()
- N = 5000
- x = [i/float(N) for i in range(1,N)]
- for variate, args, mu, sigmasqrd in [
- (g.uniform, (1.0,10.0), (10.0+1.0)/2, (10.0-1.0)**2/12),
- (g.triangular, (0.0, 1.0, 1.0/3.0), 4.0/9.0, 7.0/9.0/18.0),
- (g.expovariate, (1.5,), 1/1.5, 1/1.5**2),
- (g.vonmisesvariate, (1.23, 0), pi, pi**2/3),
- (g.paretovariate, (5.0,), 5.0/(5.0-1),
- 5.0/((5.0-1)**2*(5.0-2))),
- (g.weibullvariate, (1.0, 3.0), gamma(1+1/3.0),
- gamma(1+2/3.0)-gamma(1+1/3.0)**2) ]:
- g.random = x[:].pop
- y = []
- for i in range(len(x)):
- try:
- y.append(variate(*args))
- except IndexError:
- pass
- s1 = s2 = 0
- for e in y:
- s1 += e
- s2 += (e - mu) ** 2
- N = len(y)
- self.assertAlmostEqual(s1/N, mu, places=2,
- msg='%s%r' % (variate.__name__, args))
- self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2,
- msg='%s%r' % (variate.__name__, args))
- def test_constant(self):
- g = random.Random()
- N = 100
- for variate, args, expected in [
- (g.uniform, (10.0, 10.0), 10.0),
- (g.triangular, (10.0, 10.0), 10.0),
- (g.triangular, (10.0, 10.0, 10.0), 10.0),
- (g.expovariate, (float('inf'),), 0.0),
- (g.vonmisesvariate, (3.0, float('inf')), 3.0),
- (g.gauss, (10.0, 0.0), 10.0),
- (g.lognormvariate, (0.0, 0.0), 1.0),
- (g.lognormvariate, (-float('inf'), 0.0), 0.0),
- (g.normalvariate, (10.0, 0.0), 10.0),
- (g.paretovariate, (float('inf'),), 1.0),
- (g.weibullvariate, (10.0, float('inf')), 10.0),
- (g.weibullvariate, (0.0, 10.0), 0.0),
- ]:
- for i in range(N):
- self.assertEqual(variate(*args), expected)
- def test_von_mises_range(self):
- # Issue 17149: von mises variates were not consistently in the
- # range [0, 2*PI].
- g = random.Random()
- N = 100
- for mu in 0.0, 0.1, 3.1, 6.2:
- for kappa in 0.0, 2.3, 500.0:
- for _ in range(N):
- sample = g.vonmisesvariate(mu, kappa)
- self.assertTrue(
- 0 <= sample <= random.TWOPI,
- msg=("vonmisesvariate({}, {}) produced a result {} out"
- " of range [0, 2*pi]").format(mu, kappa, sample))
- def test_von_mises_large_kappa(self):
- # Issue #17141: vonmisesvariate() was hang for large kappas
- random.vonmisesvariate(0, 1e15)
- random.vonmisesvariate(0, 1e100)
- def test_gammavariate_errors(self):
- # Both alpha and beta must be > 0.0
- self.assertRaises(ValueError, random.gammavariate, -1, 3)
- self.assertRaises(ValueError, random.gammavariate, 0, 2)
- self.assertRaises(ValueError, random.gammavariate, 2, 0)
- self.assertRaises(ValueError, random.gammavariate, 1, -3)
- # There are three different possibilities in the current implementation
- # of random.gammavariate(), depending on the value of 'alpha'. What we
- # are going to do here is to fix the values returned by random() to
- # generate test cases that provide 100% line coverage of the method.
- @unittest.mock.patch('random.Random.random')
- def test_gammavariate_alpha_greater_one(self, random_mock):
- # #1: alpha > 1.0.
- # We want the first random number to be outside the
- # [1e-7, .9999999] range, so that the continue statement executes
- # once. The values of u1 and u2 will be 0.5 and 0.3, respectively.
- random_mock.side_effect = [1e-8, 0.5, 0.3]
- returned_value = random.gammavariate(1.1, 2.3)
- self.assertAlmostEqual(returned_value, 2.53)
- @unittest.mock.patch('random.Random.random')
- def test_gammavariate_alpha_equal_one(self, random_mock):
- # #2.a: alpha == 1.
- # The execution body of the while loop executes once.
- # Then random.random() returns 0.45,
- # which causes while to stop looping and the algorithm to terminate.
- random_mock.side_effect = [0.45]
- returned_value = random.gammavariate(1.0, 3.14)
- self.assertAlmostEqual(returned_value, 1.877208182372648)
- @unittest.mock.patch('random.Random.random')
- def test_gammavariate_alpha_equal_one_equals_expovariate(self, random_mock):
- # #2.b: alpha == 1.
- # It must be equivalent of calling expovariate(1.0 / beta).
- beta = 3.14
- random_mock.side_effect = [1e-8, 1e-8]
- gammavariate_returned_value = random.gammavariate(1.0, beta)
- expovariate_returned_value = random.expovariate(1.0 / beta)
- self.assertAlmostEqual(gammavariate_returned_value, expovariate_returned_value)
- @unittest.mock.patch('random.Random.random')
- def test_gammavariate_alpha_between_zero_and_one(self, random_mock):
- # #3: 0 < alpha < 1.
- # This is the most complex region of code to cover,
- # as there are multiple if-else statements. Let's take a look at the
- # source code, and determine the values that we need accordingly:
- #
- # while 1:
- # u = random()
- # b = (_e + alpha)/_e
- # p = b*u
- # if p <= 1.0: # <=== (A)
- # x = p ** (1.0/alpha)
- # else: # <=== (B)
- # x = -_log((b-p)/alpha)
- # u1 = random()
- # if p > 1.0: # <=== (C)
- # if u1 <= x ** (alpha - 1.0): # <=== (D)
- # break
- # elif u1 <= _exp(-x): # <=== (E)
- # break
- # return x * beta
- #
- # First, we want (A) to be True. For that we need that:
- # b*random() <= 1.0
- # r1 = random() <= 1.0 / b
- #
- # We now get to the second if-else branch, and here, since p <= 1.0,
- # (C) is False and we take the elif branch, (E). For it to be True,
- # so that the break is executed, we need that:
- # r2 = random() <= _exp(-x)
- # r2 <= _exp(-(p ** (1.0/alpha)))
- # r2 <= _exp(-((b*r1) ** (1.0/alpha)))
- _e = random._e
- _exp = random._exp
- _log = random._log
- alpha = 0.35
- beta = 1.45
- b = (_e + alpha)/_e
- epsilon = 0.01
- r1 = 0.8859296441566 # 1.0 / b
- r2 = 0.3678794411714 # _exp(-((b*r1) ** (1.0/alpha)))
- # These four "random" values result in the following trace:
- # (A) True, (E) False --> [next iteration of while]
- # (A) True, (E) True --> [while loop breaks]
- random_mock.side_effect = [r1, r2 + epsilon, r1, r2]
- returned_value = random.gammavariate(alpha, beta)
- self.assertAlmostEqual(returned_value, 1.4499999999997544)
- # Let's now make (A) be False. If this is the case, when we get to the
- # second if-else 'p' is greater than 1, so (C) evaluates to True. We
- # now encounter a second if statement, (D), which in order to execute
- # must satisfy the following condition:
- # r2 <= x ** (alpha - 1.0)
- # r2 <= (-_log((b-p)/alpha)) ** (alpha - 1.0)
- # r2 <= (-_log((b-(b*r1))/alpha)) ** (alpha - 1.0)
- r1 = 0.8959296441566 # (1.0 / b) + epsilon -- so that (A) is False
- r2 = 0.9445400408898141
- # And these four values result in the following trace:
- # (B) and (C) True, (D) False --> [next iteration of while]
- # (B) and (C) True, (D) True [while loop breaks]
- random_mock.side_effect = [r1, r2 + epsilon, r1, r2]
- returned_value = random.gammavariate(alpha, beta)
- self.assertAlmostEqual(returned_value, 1.5830349561760781)
- @unittest.mock.patch('random.Random.gammavariate')
- def test_betavariate_return_zero(self, gammavariate_mock):
- # betavariate() returns zero when the Gamma distribution
- # that it uses internally returns this same value.
- 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):
- called.add('SubClass1.random')
- return random.Random.random(self)
- def getrandbits(self, n):
- called.add('SubClass1.getrandbits')
- return random.Random.getrandbits(self, n)
- called = set()
- SubClass1().randrange(42)
- self.assertEqual(called, {'SubClass1.getrandbits'})
- # subclass providing only random => can only use random for randrange
- class SubClass2(random.Random):
- def random(self):
- called.add('SubClass2.random')
- return random.Random.random(self)
- called = set()
- SubClass2().randrange(42)
- self.assertEqual(called, {'SubClass2.random'})
- # subclass defining getrandbits to complement its inherited random
- # => can now rely on getrandbits for randrange again
- class SubClass3(SubClass2):
- def getrandbits(self, n):
- called.add('SubClass3.getrandbits')
- return random.Random.getrandbits(self, n)
- called = set()
- SubClass3().randrange(42)
- self.assertEqual(called, {'SubClass3.getrandbits'})
- # subclass providing only random and inherited getrandbits
- # => random takes precedence
- class SubClass4(SubClass3):
- def random(self):
- called.add('SubClass4.random')
- return random.Random.random(self)
- called = set()
- SubClass4().randrange(42)
- self.assertEqual(called, {'SubClass4.random'})
- # Following subclasses don't define random or getrandbits directly,
- # but inherit them from classes which are not subclasses of Random
- class Mixin1:
- def random(self):
- called.add('Mixin1.random')
- return random.Random.random(self)
- class Mixin2:
- def getrandbits(self, n):
- called.add('Mixin2.getrandbits')
- return random.Random.getrandbits(self, n)
- class SubClass5(Mixin1, random.Random):
- pass
- called = set()
- SubClass5().randrange(42)
- self.assertEqual(called, {'Mixin1.random'})
- class SubClass6(Mixin2, random.Random):
- pass
- called = set()
- SubClass6().randrange(42)
- self.assertEqual(called, {'Mixin2.getrandbits'})
- class SubClass7(Mixin1, Mixin2, random.Random):
- pass
- called = set()
- SubClass7().randrange(42)
- self.assertEqual(called, {'Mixin1.random'})
- class SubClass8(Mixin2, Mixin1, random.Random):
- pass
- called = set()
- SubClass8().randrange(42)
- self.assertEqual(called, {'Mixin2.getrandbits'})
- class TestModule(unittest.TestCase):
- def testMagicConstants(self):
- self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141)
- self.assertAlmostEqual(random.TWOPI, 6.28318530718)
- self.assertAlmostEqual(random.LOG4, 1.38629436111989)
- self.assertAlmostEqual(random.SG_MAGICCONST, 2.50407739677627)
- def test__all__(self):
- # tests validity but not completeness of the __all__ list
- self.assertTrue(set(random.__all__) <= set(dir(random)))
- @test.support.requires_fork()
- def test_after_fork(self):
- # Test the global Random instance gets reseeded in child
- r, w = os.pipe()
- pid = os.fork()
- if pid == 0:
- # child process
- try:
- val = random.getrandbits(128)
- with open(w, "w") as f:
- f.write(str(val))
- finally:
- os._exit(0)
- else:
- # parent process
- os.close(w)
- val = random.getrandbits(128)
- with open(r, "r") as f:
- child_val = eval(f.read())
- self.assertNotEqual(val, child_val)
- support.wait_process(pid, exitcode=0)
- if __name__ == "__main__":
- unittest.main()
|