Ticket #5419: fuzz.2.patch

File fuzz.2.patch, 8.0 KB (added by Roman Gladkov, 12 years ago)
  • django/test/testcases.py

    diff --git a/django/test/testcases.py b/django/test/testcases.py
    index 1239275..ea806be 100644
    a b from django.test.html import HTMLParseError, parse_html  
    3838from django.test.signals import template_rendered
    3939from django.test.utils import (get_warnings_state, restore_warnings_state,
    4040    override_settings, compare_xml, strip_quotes)
    41 from django.test.utils import ContextList
     41from django.test.utils import ContextList, generate_fuzzy_value
    4242from django.utils import unittest as ut2
    4343from django.utils.encoding import force_text
    4444from django.utils import six
    class SimpleTestCase(ut2.TestCase):  
    391391                standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True))
    392392                self.fail(self._formatMessage(msg, standardMsg))
    393393
     394    def assertModelUnicodePassesFuzzTest(self, model, repeat=10):
     395        """
     396        Asserts that after having assigned random unicode data into each of
     397        the fields of an instance of the specified model that calling the
     398        __unicode__ method of the instance doesn't raise a UnicodeError.
     399        Random data is assigned and the __unicode__ method called repeat times.
     400        """
     401        instance = model()
     402        opts = model._meta
     403        for i in range(repeat):
     404            for field in opts.fields:
     405                setattr(instance, field.name, generate_fuzzy_value(str))
     406            try:
     407                if six.PY3:
     408                    six.u(instance)
     409                else:
     410                    unicode(instance)
     411            except UnicodeError as e:
     412                self.fail("Model '%s' unexpectedly raised %s when"
     413                          " converting to unicode: %s" % (
     414                          model.__name__, e.__class__.__name__, e))
     415
     416    def assertFunctionPassesFuzzTest(self, func, arg_types=(),
     417                                     permitted_return_types=(),
     418                                     permitted_exceptions=(), repeat=10):
     419        """
     420        Asserts that the function when called with random values of the types
     421        specified by arg_types that the return value has a type from
     422        permitted_return_types and that if an exception is raised it is one
     423        that is in permitted_exceptions. The function is called repeat times
     424        with random data.
     425        """
     426
     427        def isiterable(o):
     428            try:
     429                iter(o)
     430            except TypeError:
     431                return False
     432            return True
     433
     434        def cartesian_product(L, *lists):
     435            if not lists:
     436                for x in L:
     437                    yield (x,)
     438            else:
     439                for x in L:
     440                    for y in cartesian_product(lists[0], *lists[1:]):
     441                        yield (x,) + y
     442
     443        # Allow the use of None
     444        if (None in permitted_return_types) and (
     445            None not in permitted_return_types
     446        ):
     447            permitted_return_types = list(permitted_return_types)
     448            permitted_return_types += (None,)
     449
     450        arg_types = list(arg_types)
     451        # Ensure arg_types is a sequence of iterables
     452        for i, arg_type in enumerate(arg_types):
     453            if not isiterable(arg_type):
     454                arg_types[i] = (arg_type,)
     455
     456        for i in range(repeat):
     457            for atypes in cartesian_product(*arg_types):
     458                args = []
     459                for arg_type in atypes:
     460                    args.append(generate_fuzzy_value(arg_type))
     461                try:
     462                    return_value = func(*args)
     463                except:
     464                    if permitted_exceptions:
     465                        exc_type, exc_value = sys.exc_info()[:2]
     466                        if exc_type not in permitted_exceptions:
     467                            self.fail("Unexpected %s encountered when calling"
     468                                      " %s with inputs %r: %s" % (
     469                                      exc_type.__name__, func.__name__,
     470                                      args, exc_value))
     471                else:
     472                    if permitted_return_types and return_value and (
     473                        type(return_value) not in permitted_return_types
     474                    ):
     475                        self.fail("Unexpected return value type %s encountered"
     476                                  " when calling %s with inputs %r." % (
     477                                  return_value.__name__, func.__name__, args))
     478
    394479
    395480class TransactionTestCase(SimpleTestCase):
    396481
  • django/test/utils.py

    diff --git a/django/test/utils.py b/django/test/utils.py
    index f10d388..91109f8 100644
    a b  
     1import random
    12import re
     3import sys
    24import warnings
    35from xml.dom.minidom import parseString, Node
    46
    from django.utils.translation import deactivate  
    1113from django.utils.functional import wraps
    1214from django.utils import six
    1315
     16try:
     17    unichr
     18except NameError:  # Python 3
     19    unichr = chr
     20
    1421
    1522__all__ = (
    1623    'Approximate', 'ContextList',  'get_runner', 'override_settings',
    1724    'setup_test_environment', 'teardown_test_environment',
     25    'generate_fuzzy_value'
    1826)
    1927
    2028RESTORE_LOADERS_ATTR = '_original_template_source_loaders'
    def strip_quotes(want, got):  
    321329        got = got.strip()[2:-1]
    322330    return want, got
    323331
     332
    324333def str_prefix(s):
    325334    return s % {'_': '' if six.PY3 else 'u'}
     335
     336
     337def generate_fuzzy_value(value_type):
     338    """
     339    Returns a randome value of the specified type, or raises a ValueError if
     340    the type is not supported. If value_type is callable then it is called and
     341    its return value is returned. This allows you to specify a function that
     342    generates a random value of a type that isn't supported.
     343    """
     344    if value_type is None:
     345        return None
     346    if issubclass(value_type, bool):
     347        return random.choice((True, False))
     348    if issubclass(value_type, six.integer_types):
     349        return random.randint(-six.MAXSIZE - 1, six.MAXSIZE)
     350    if issubclass(value_type, float):
     351        return random.random() * generate_fuzzy_value(int)
     352    if issubclass(value_type, six.string_types):
     353        length = random.randint(0, 1000)
     354        return ''.join([unichr(random.randint(32, 255))
     355                        for _ in range(length)])
     356    if callable(value_type):
     357        return value_type()
     358    raise ValueError("Unsupported type: %s" % value_type)
  • tests/regressiontests/test_utils/models.py

    diff --git a/tests/regressiontests/test_utils/models.py b/tests/regressiontests/test_utils/models.py
    index 4da7a07..830b1b2 100644
    a b from django.db import models  
    33
    44class Person(models.Model):
    55    name = models.CharField(max_length=100)
     6
     7    def __unicode__(self):
     8        return unicode(self.name)
  • tests/regressiontests/test_utils/tests.py

    diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py
    index 95913b5..8a22d04 100644
    a b class XMLEqualTests(TestCase):  
    490490        self.assertXMLEqual(xml1, xml2)
    491491
    492492
     493class FuzzTests(TestCase):
     494
     495    def test_simple_model_unicode(self):
     496        self.assertModelUnicodePassesFuzzTest(Person)
     497
     498    def test_simple_call_func_with_random_data(self):
     499        def _test_func(intarg, intarg2, boolarg):
     500            if boolarg:
     501                return None
     502            elif not boolarg:
     503                raise ValueError('error')
     504            elif intarg > intarg2:
     505                return intarg
     506            elif intarg < intarg2:
     507                return intarg2
     508        self.assertFunctionPassesFuzzTest(_test_func,
     509                                          arg_types=((int, None),
     510                                                     (int, None), bool),
     511                                          permitted_return_types=(None, int),
     512                                          permitted_exceptions=(ValueError,),
     513                                          repeat=100)
     514
     515
    493516class SkippingExtraTests(TestCase):
    494517    fixtures = ['should_not_be_loaded.json']
    495518
Back to Top