diff options
Diffstat (limited to 'src/third_party/wiredtiger/test/suite/wtscenario.py')
-rw-r--r-- | src/third_party/wiredtiger/test/suite/wtscenario.py | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/third_party/wiredtiger/test/suite/wtscenario.py b/src/third_party/wiredtiger/test/suite/wtscenario.py new file mode 100644 index 00000000000..7fad7c228fb --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/wtscenario.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +# +# Public Domain 2014-2016 MongoDB, Inc. +# Public Domain 2008-2014 WiredTiger, Inc. +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +import testscenarios +import suite_random + +# wtscenarios.py +# Support scenarios based testing +def powerrange(start, stop, mult): + """ + Like xrange, generates a range from start to stop. + Unlike xrange, the range is inclusive of stop, + each step is multiplicative, and as a special case, + the stop value is returned as the last item. + """ + val = start + while val <= stop: + yield val + newval = val * mult + if val < stop and newval > stop: + val = stop + else: + val = newval + +def log2chr(val): + """ + For the log-base 2 of val, return the numeral or letter + corresponding to val (which is < 36). Hence, 1 return '0', + 2 return '1', 2*15 returns 'f', 2*16 returns 'g', etc. + """ + p = 0 + while val >= 2: + p += 1 + val /= 2 + if p < 10: + return chr(ord('0') + p) + else: + return chr(ord('a') + p - 10) + +megabyte = 1024 * 1024 + +def check_scenarios(scenes): + """ + Make sure all scenarios have unique names + """ + assert len(scenes) == len(dict(scenes)) + return scenes + +def multiply_scenarios(sep, *args): + """ + Create the cross product of two lists of scenarios + """ + result = None + for scenes in args: + if result == None: + result = scenes + else: + 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 check_scenarios(result) + +def prune_sorter_key(scene): + """ + Used by prune_scenerios to extract key for sorting. + The key is the saved random value multiplied by + the probability of choosing. + """ + p = 1.0 + if 'P' in scene[1]: + p = scene[1]['P'] + return p * scene[1]['_rand'] + +def prune_resort_key(scene): + """ + Used by prune_scenerios to extract the original ordering key for sorting. + """ + return scene[1]['_order'] + +def set_long_run(islong): + global _is_long_run + _is_long_run = islong + +def prune_scenarios(scenes, default_count = -1, long_count = -1): + """ + Use listed probabilities for pruning the list of scenarios. + That is, the highest probability (value of P in the scendario) + are chosen more often. With just one argument, only scenarios + with P > .5 are returned half the time, etc. A second argument + limits the number of scenarios. When a third argument is present, + it is a separate limit for a long run. + """ + global _is_long_run + r = suite_random.suite_random() + result = [] + if default_count == -1: + # Missing second arg - return those with P == .3 at + # 30% probability, for example. + for scene in scenes: + if 'P' in scene[1]: + p = scene[1]['P'] + if p < r.rand_float(): + continue + result.append(scene) + return result + else: + # With at least a second arg present, we'll want a specific count + # of items returned. So we'll sort them all and choose + # the top number. Not the most efficient solution, + # but it's easy. + if _is_long_run and long_count != -1: + count = long_count + else: + count = default_count + + l = len(scenes) + if l <= count: + return scenes + if count == 0: + return [] + order = 0 + for scene in scenes: + scene[1]['_rand'] = r.rand_float() + scene[1]['_order'] = order + order += 1 + scenes = sorted(scenes, key=prune_sorter_key) # random sort driven by P + scenes = scenes[l-count:l] # truncate to get best + scenes = sorted(scenes, key=prune_resort_key) # original order + for scene in scenes: + del scene[1]['_rand'] + del scene[1]['_order'] + return check_scenarios(scenes) + +def number_scenarios(scenes): + """ + Add a 'scenario_number' and 'scenario_name' variable to each scenario. + The hash table for each scenario is altered! + """ + count = 0 + for scene in scenes: + scene[1]['scenario_name'] = scene[0] + scene[1]['scenario_number'] = count + count += 1 + return check_scenarios(scenes) + +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 + """ + + @staticmethod + def session_create_scenario(): + """ + Return a set of scenarios with the name of this method + 'session_create_scenario' as the name of instance + variable containing a wtscenario object. The wtscenario + object can be queried to get a config string. + Each scenario is named according to the shortName() method. + """ + s = [ + ('default', dict(session_create_scenario=wtscenario())) ] + for imin in powerrange(512, 512*megabyte, 1024): + for imax in powerrange(imin, 512*megabyte, 1024): + for lmin in powerrange(512, 512*megabyte, 1024): + for lmax in powerrange(lmin, 512*megabyte, 1024): + for cache in [megabyte, 32*megabyte, 1000*megabyte]: + scen = wtscenario() + scen.ioverflow = max(imin / 40, 40) + scen.imax = imax + scen.loverflow = max(lmin / 40, 40) + scen.lmax = lmax + scen.cache_size = cache + s.append((scen.shortName(), dict(session_create_scenario=scen))) + return s + + def shortName(self): + """ + Return a name of a scenario, based on the 'log2chr-ed numerals' + representing the four values for {internal,leaf} {minimum, maximum} + page size. + """ + return 'scen_' + log2chr(self.ioverflow) + log2chr(self.imax) + log2chr(self.loverflow) + log2chr(self.lmax) + log2chr(self.cache_size) + + def configString(self): + """ + Return the associated configuration string + """ + res = '' + if hasattr(self, 'ioverflow'): + res += ',internal_item_max=' + str(self.ioverflow) + if hasattr(self, 'imax'): + res += ',internal_page_max=' + str(self.imax) + if self.imax < 4*1024: + res += ',allocation_size=512' + if hasattr(self, 'loverflow'): + res += ',leaf_item_max=' + str(self.loverflow) + if hasattr(self, 'lmax'): + res += ',leaf_page_max=' + str(self.lmax) + if self.lmax < 4*1024: + res += ',allocation_size=512' + return res |