Ticket #5419: fuzz.diff

File fuzz.diff, 5.0 KB (added by Fraser Nevett, 17 years ago)
  • django/test/testcases.py

     
     1import random
    12import re
     3import sys
     4import types
    25import unittest
    36from urlparse import urlsplit
    47
     
    1114
    1215normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
    1316
     17def 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
    1441def to_list(value):
    1542    """
    1643    Puts value into a list if it's not already one.
     
    186213        self.failIf(template_name in template_names,
    187214            (u"Template '%s' was used unexpectedly in rendering the"
    188215             u" response") % template_name)
     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
Back to Top