summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorDon Anderson <dda@ddanderson.com>2011-11-23 12:36:53 -0500
committerDon Anderson <dda@ddanderson.com>2011-11-23 12:36:53 -0500
commit0501970a5c529693c209d7629d35f30ed02015f3 (patch)
tree95281bf35cc8657b3d4f6641a5380472712c4883 /test
parentf3a29f050e8d0e2330a6f9c8723bd9b22dc3c8a5 (diff)
downloadmongo-0501970a5c529693c209d7629d35f30ed02015f3.tar.gz
random numbers and multiple scenarios with probabilities: refs #16
- added a random number generator that is fast and good enough, that is repeatable and each instance is independent, fine for MT tests. - scenarios that have 'P' in their dict use it as a probability (otherwise P=1.0). When 'multiplying' scenarios, we multiply their probabilities. We can prune a list of scenarios, randomly picking a set based on their probability. This allows us to generate tests based on a large set of independent scenarios, but not have the size of the final cross-product be overwhelming. Particular values in scenarios can be emphasized or even required (with P=1.0).
Diffstat (limited to 'test')
-rw-r--r--test/suite/suite_random.py65
-rw-r--r--test/suite/wtscenario.py51
2 files changed, 116 insertions, 0 deletions
diff --git a/test/suite/suite_random.py b/test/suite/suite_random.py
new file mode 100644
index 00000000000..f81ea2cfda3
--- /dev/null
+++ b/test/suite/suite_random.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# See the file LICENSE for redistribution information.
+#
+# Copyright (c) 2008-2011 WiredTiger, Inc.
+# All rights reserved.
+#
+# suite_random.py
+# A quick and predictable pseudo random number generator.
+#
+
+
+class suite_random:
+ """
+ Generate random 32 bit integers that are predictable,
+ and use no global state. We use the Multiply-with-carry
+ method invented by George Marsaglia, because it is quick
+ and easy to implement.
+ """
+ def __init__(self, *args):
+ arglen = len(args)
+ if arglen == 1:
+ self.seedw = int(args[0]) & 0xffffffff
+ self.seedz = int(args[0]) & 0Xffffffff
+ elif arglen == 2:
+ self.seedw = int(args[0]) & 0xffffffff
+ self.seedz = int(args[1]) & 0Xffffffff
+ else:
+ self.seedw = 0
+ self.seedz = 0
+
+ # The seeds cannot be 0
+ if self.seedw == 0:
+ self.seedw += 22233
+ if self.seedz == 0:
+ self.seedz += 11133
+
+ def rand32(self):
+ """
+ returns a random 32 bit integer
+ """
+ w = self.seedw
+ z = self.seedz
+ self.seedw = (18000 * (w & 0xffff) + (w >> 16)) & 0xffffffff
+ self.seedz = (36969 * (z & 0xffff) + (z >> 16)) & 0xffffffff
+ return ((z << 16) + w) & 0xffffffff
+
+ def rand_range(self, n, m):
+ """
+ returns a random integer in the range [N,M).
+ """
+ if m > 0xffffffff or n < 0:
+ raise ValueError("rand32_range expects args between 0 , 2^32")
+ if n >= m:
+ raise ValueError("rand32_range(n,m) expects n < m")
+ r = self.rand32()
+ return (r % (m - n)) + n
+
+ def rand_float(self):
+ """
+ returns a random floating point value between 0 and 1.0
+ The number returned does not encompass all possibilities,
+ only 2^32 values within the range.
+ """
+ return (self.rand32() + 0.0)/0x100000000
diff --git a/test/suite/wtscenario.py b/test/suite/wtscenario.py
index 0259828f4c4..727c7dc6c2c 100644
--- a/test/suite/wtscenario.py
+++ b/test/suite/wtscenario.py
@@ -10,6 +10,7 @@
#
import testscenarios
+import suite_random
def powerrange(start, stop, mult):
"""
@@ -45,6 +46,9 @@ def log2chr(val):
megabyte = 1024 * 1024
def multiply_scenarios(sep, *args):
+ """
+ Create the cross product of two lists of scenarios
+ """
result = None
for scenes in args:
if result == None:
@@ -53,14 +57,61 @@ def multiply_scenarios(sep, *args):
total = []
for scena in scenes:
for scenb in result:
+ # Create a merged scenario with a concatenated name
name = scena[0] + sep + scenb[0]
tdict = {}
tdict.update(scena[1])
tdict.update(scenb[1])
+
+ # If there is a 'P' value, it represents the
+ # probability that we want to use this scenario
+ # If both scenarios list a probability, multiply them.
+ if 'P' in scena[1] and 'P' in scenb[1]:
+ P = scena[1]['P'] * scenb[1]['P']
+ tdict['P'] = P
total.append((name, tdict))
result = total
return result
+def prune_scenarios(scenes):
+ """
+ Use listed probabilities for pruning the list of scenarios
+ """
+ r = suite_random.suite_random()
+ result = []
+ for scene in scenes:
+ if 'P' in scene[1]:
+ p = scene[1]['P']
+ if p < r.rand_float():
+ continue
+ result.append(scene)
+ return result
+
+def quick_scenarios(fieldname, values, probabilities):
+ """
+ Quickly build common scenarios, like:
+ [('foo', dict(somefieldname='foo')),
+ ('bar', dict(somefieldname='bar')),
+ ('boo', dict(somefieldname='boo'))]
+ via a call to:
+ quick_scenario('somefieldname', ['foo', 'bar', 'boo'])
+ """
+ result = []
+ if probabilities == None:
+ plen = 0
+ else:
+ plen = len(probabilities)
+ ppos = 0
+ for value in values:
+ if ppos >= plen:
+ d = dict([[fieldname, value]])
+ else:
+ p = probabilities[ppos]
+ ppos += 1
+ d = dict([[fieldname, value],['P', p]])
+ result.append((str(value), d))
+ return result
+
class wtscenario:
"""
A set of generators for different test scenarios