00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 import random
00016 import traceback
00017 import datetime
00018 import re
00019 import sys
00020 sys.path[0:0] = [""]
00021
00022 from bson.binary import Binary
00023 from bson.dbref import DBRef
00024 from bson.objectid import ObjectId
00025 from bson.son import SON
00026
00027 gen_target = 100
00028 reduction_attempts = 10
00029 examples = 5
00030
00031
00032 def lift(value):
00033 return lambda: value
00034
00035
00036 def choose_lifted(generator_list):
00037 return lambda: random.choice(generator_list)
00038
00039
00040 def map(generator, function):
00041 return lambda: function(generator())
00042
00043
00044 def choose(list):
00045 return lambda: random.choice(list)()
00046
00047
00048 def gen_range(start, stop):
00049 return lambda: random.randint(start, stop)
00050
00051
00052 def gen_int():
00053 max_int = 2147483647
00054 return lambda: random.randint(-max_int - 1, max_int)
00055
00056
00057 def gen_float():
00058 return lambda: (random.random() - 0.5) * sys.maxint
00059
00060
00061 def gen_boolean():
00062 return lambda: random.choice([True, False])
00063
00064
00065 def gen_printable_char():
00066 return lambda: chr(random.randint(32, 126))
00067
00068
00069 def gen_printable_string(gen_length):
00070 return lambda: "".join(gen_list(gen_printable_char(), gen_length)())
00071
00072
00073 def gen_char(set=None):
00074 return lambda: chr(random.randint(0, 255))
00075
00076
00077 def gen_string(gen_length):
00078 return lambda: "".join(gen_list(gen_char(), gen_length)())
00079
00080
00081 def gen_unichar():
00082 return lambda: unichr(random.randint(1, 0xFFF))
00083
00084
00085 def gen_unicode(gen_length):
00086 return lambda: u"".join([x for x in
00087 gen_list(gen_unichar(), gen_length)() if
00088 x not in ".$"])
00089
00090
00091 def gen_list(generator, gen_length):
00092 return lambda: [generator() for _ in range(gen_length())]
00093
00094
00095 def gen_datetime():
00096 return lambda: datetime.datetime(random.randint(1970, 2037),
00097 random.randint(1, 12),
00098 random.randint(1, 28),
00099 random.randint(0, 23),
00100 random.randint(0, 59),
00101 random.randint(0, 59),
00102 random.randint(0, 999) * 1000)
00103
00104
00105 def gen_dict(gen_key, gen_value, gen_length):
00106
00107 def a_dict(gen_key, gen_value, length):
00108 result = {}
00109 for _ in range(length):
00110 result[gen_key()] = gen_value()
00111 return result
00112 return lambda: a_dict(gen_key, gen_value, gen_length())
00113
00114
00115 def gen_regexp(gen_length):
00116
00117
00118
00119 pattern = lambda: u"".join(gen_list(choose_lifted(u"a"), gen_length)())
00120
00121 def gen_flags():
00122 flags = 0
00123 if random.random() > 0.5:
00124 flags = flags | re.IGNORECASE
00125 if random.random() > 0.5:
00126 flags = flags | re.MULTILINE
00127 if random.random() > 0.5:
00128 flags = flags | re.VERBOSE
00129
00130 return flags
00131 return lambda: re.compile(pattern(), gen_flags())
00132
00133
00134 def gen_objectid():
00135 return lambda: ObjectId()
00136
00137
00138 def gen_dbref():
00139 collection = gen_unicode(gen_range(0, 20))
00140 return lambda: DBRef(collection(), gen_mongo_value(1, True)())
00141
00142
00143 def gen_mongo_value(depth, ref):
00144 choices = [gen_unicode(gen_range(0, 50)),
00145 gen_printable_string(gen_range(0, 50)),
00146 map(gen_string(gen_range(0, 1000)), Binary),
00147 gen_int(),
00148 gen_float(),
00149 gen_boolean(),
00150 gen_datetime(),
00151 gen_objectid(),
00152 lift(None)]
00153 if ref:
00154 choices.append(gen_dbref())
00155 if depth > 0:
00156 choices.append(gen_mongo_list(depth, ref))
00157 choices.append(gen_mongo_dict(depth, ref))
00158 return choose(choices)
00159
00160
00161 def gen_mongo_list(depth, ref):
00162 return gen_list(gen_mongo_value(depth - 1, ref), gen_range(0, 10))
00163
00164
00165 def gen_mongo_dict(depth, ref=True):
00166 return map(gen_dict(gen_unicode(gen_range(0, 20)),
00167 gen_mongo_value(depth - 1, ref),
00168 gen_range(0, 10)), SON)
00169
00170
00171 def simplify(case):
00172 if isinstance(case, SON) and "$ref" not in case:
00173 simplified = SON(case)
00174 if random.choice([True, False]):
00175
00176 if not len(simplified.keys()):
00177 return (False, case)
00178 del simplified[random.choice(simplified.keys())]
00179 return (True, simplified)
00180 else:
00181
00182 if not len(simplified.items()):
00183 return (False, case)
00184 (key, value) = random.choice(simplified.items())
00185 (success, value) = simplify(value)
00186 simplified[key] = value
00187 return (success, success and simplified or case)
00188 if isinstance(case, list):
00189 simplified = list(case)
00190 if random.choice([True, False]):
00191
00192 if not len(simplified):
00193 return (False, case)
00194 simplified.pop(random.randrange(len(simplified)))
00195 return (True, simplified)
00196 else:
00197
00198 if not len(simplified):
00199 return (False, case)
00200 index = random.randrange(len(simplified))
00201 (success, value) = simplify(simplified[index])
00202 simplified[index] = value
00203 return (success, success and simplified or case)
00204 return (False, case)
00205
00206
00207 def reduce(case, predicate, reductions=0):
00208 for _ in range(reduction_attempts):
00209 (reduced, simplified) = simplify(case)
00210 if reduced and not predicate(simplified):
00211 return reduce(simplified, predicate, reductions + 1)
00212 return (reductions, case)
00213
00214
00215 def isnt(predicate):
00216 return lambda x: not predicate(x)
00217
00218
00219 def check(predicate, generator):
00220 counter_examples = []
00221 for _ in range(gen_target):
00222 case = generator()
00223 try:
00224 if not predicate(case):
00225 reduction = reduce(case, predicate)
00226 counter_examples.append("after %s reductions: %r" % reduction)
00227 except:
00228 counter_examples.append("%r : %s" % (case, traceback.format_exc()))
00229 return counter_examples
00230
00231
00232 def check_unittest(test, predicate, generator):
00233 counter_examples = check(predicate, generator)
00234 if counter_examples:
00235 failures = len(counter_examples)
00236 message = "\n".join([" -> %s" % f for f in
00237 counter_examples[:examples]])
00238 message = ("found %d counter examples, displaying first %d:\n%s" %
00239 (failures, min(failures, examples), message))
00240 test.fail(message)