| 17 | def generate_fuzzy_value(value_type): |
| 18 | """ |
| 19 | Returns a randome value of the specified type, or raises a ValueError if |
| 20 | the type is not supported. If value_type is callable then it is called and |
| 21 | its return value is returned. This allows you to specify a function that |
| 22 | generates a random value of a type that isn't supported. |
| 23 | """ |
| 24 | if value_type is None or issubclass(value_type, types.NoneType): |
| 25 | return None |
| 26 | if issubclass(value_type, types.BooleanType): |
| 27 | return random.choice((True, False)) |
| 28 | if issubclass(value_type, types.IntType): |
| 29 | return random.randint(-sys.maxint - 1, sys.maxint) |
| 30 | if issubclass(value_type, types.LongType): |
| 31 | return long(generate_fuzzy_value(int) * generate_fuzzy_value(int)) |
| 32 | if issubclass(value_type, types.FloatType): |
| 33 | return random.random() * generate_fuzzy_value(int) |
| 34 | if issubclass(value_type, basestring): |
| 35 | length = random.randint(0, 1000) |
| 36 | return u''.join([unichr(random.randint(32, 65534)) for i in range(length)]) |
| 37 | if callable(value_type): |
| 38 | return value_type() |
| 39 | raise ValueError("Unsupported type: %s" % value_type) |
| 40 | |
| 216 | |
| 217 | def assertModelUnicodePassesFuzzTest(self, model, repeat=10): |
| 218 | """ |
| 219 | Asserts that after having assigned random unicode data into each of |
| 220 | the fields of an instance of the specified model that calling the |
| 221 | __unicode__ method of the instance doesn't raise a UnicodeError. |
| 222 | Random data is assigned and the __unicode__ method called repeat times. |
| 223 | """ |
| 224 | instance = model() |
| 225 | opts = model._meta |
| 226 | for i in range(repeat): |
| 227 | for field in opts.fields: |
| 228 | setattr(instance, field.name, generate_fuzzy_value(str)) |
| 229 | try: |
| 230 | unicode(instance) |
| 231 | except UnicodeError, e: |
| 232 | self.fail(u"Model '%s' unexpectedly raised %s when converting to unicode: %s" % |
| 233 | (model.__name__, e.__class__.__name__, e)) |
| 234 | |
| 235 | def assertFunctionPassesFuzzTest(self, func, arg_types=(), |
| 236 | permitted_return_types=(), permitted_exceptions=(), repeat=10): |
| 237 | """ |
| 238 | Asserts that the function when called with random values of the types |
| 239 | specified by arg_types that the return value has a type from |
| 240 | permitted_return_types and that if an exception is raised it is one |
| 241 | that is in permitted_exceptions. The function is called repeat times |
| 242 | with random data. |
| 243 | """ |
| 244 | |
| 245 | def isiterable(o): |
| 246 | try: |
| 247 | iter(o) |
| 248 | except TypeError: |
| 249 | return False |
| 250 | return True |
| 251 | |
| 252 | def cartesian_product(L, *lists): |
| 253 | if not lists: |
| 254 | for x in L: |
| 255 | yield (x,) |
| 256 | else: |
| 257 | for x in L: |
| 258 | for y in cartesian_product(lists[0], *lists[1:]): |
| 259 | yield (x,) + y |
| 260 | |
| 261 | # Allow the use of None instead of types.NoneType |
| 262 | if None in permitted_return_types and types.NoneType not in permitted_return_types: |
| 263 | permitted_return_types = tuple(permitted_return_types) |
| 264 | permitted_return_types += (types.NoneType,) |
| 265 | |
| 266 | # Ensure arg_types is a sequence of iterables |
| 267 | for i, arg_type in enumerate(arg_types): |
| 268 | if not isiterable(arg_type): arg_types[i] = [arg_type] |
| 269 | |
| 270 | for i in range(repeat): |
| 271 | for atypes in cartesian_product(*arg_types): |
| 272 | args = [] |
| 273 | for arg_type in atypes: |
| 274 | args.append(generate_fuzzy_value(arg_type)) |
| 275 | try: |
| 276 | return_value = func(*args) |
| 277 | except: |
| 278 | if permitted_exceptions: |
| 279 | exc_type, exc_value = sys.exc_info()[:2] |
| 280 | if exc_type not in permitted_exceptions: |
| 281 | self.fail(u"Unexpected %s encountered when calling %s with inputs %r: %s" % (exc_type.__name__, func.__name__, args, exc_value)) |
| 282 | else: |
| 283 | if permitted_return_types and type(return_value) not in permitted_return_types: |
| 284 | self.fail(u"Unexpected return value type %s encountered when calling %s with inputs %r." % (return_value.__name__, func.__name__, args)) |
| 285 | No newline at end of file |