Ticket #7441: doctest_xml_and_json_checkers.patch

File doctest_xml_and_json_checkers.patch, 5.7 KB (added by Leo Soto M., 16 years ago)
  • django/test/testcases.py

    diff -r 70fcef3980a0 django/test/testcases.py
    a b import re  
    11import re
    22import unittest
    33from urlparse import urlsplit, urlunsplit
     4from xml.dom.minidom import parseString, Node
    45
    56from django.http import QueryDict
    67from django.db import transaction
    from django.core.management import call_  
    89from django.core.management import call_command
    910from django.test import _doctest as doctest
    1011from django.test.client import Client
     12from django.utils import simplejson
    1113
    1214normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
    1315
    def to_list(value):  
    2527
    2628class OutputChecker(doctest.OutputChecker):
    2729    def check_output(self, want, got, optionflags):
    28         ok = doctest.OutputChecker.check_output(self, want, got, optionflags)
     30        checks = (self.check_output_default,
     31                  self.check_output_long,
     32                  self.check_output_xml,
     33                  self.check_output_json)
     34        for check in checks:
     35            if check(want, got, optionflags):
     36                return True
     37        return False
    2938
     39    def check_output_default(self, want, got, optionflags):
     40        return doctest.OutputChecker.check_output(self, want, got, optionflags)
     41
     42    def check_output_long(self, want, got, optionflags):
    3043        # Doctest does an exact string comparison of output, which means long
    3144        # integers aren't equal to normal integers ("22L" vs. "22"). The
    3245        # following code normalizes long integers so that they equal normal
    3346        # integers.
    34         if not ok:
    35             return normalize_long_ints(want) == normalize_long_ints(got)
    36         return ok
     47        return normalize_long_ints(want) == normalize_long_ints(got)
     48
     49    def check_output_xml(self, want, got, optionsflags):
     50        # Tries to do a 'xml-comparision' of want and got.  Plan string
     51        # comparision doesn't always work, because, for example, attribute
     52        # ordering should not be important.
     53        #
     54        # Based on http://codespeak.net/svn/lxml/trunk/src/lxml/doctestcompare.py
     55
     56        # We use this to distinguish repr()s from elements:
     57        _repr_re = re.compile(r'^<[^>]+ (at|object) ')
     58
     59        _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
     60        def norm_whitespace(v):
     61            return _norm_whitespace_re.sub(' ', v)
     62
     63        def looks_like_markup(s):
     64            s = s.strip()
     65            return (s.startswith('<')
     66                    and not _repr_re.search(s))
     67
     68        def child_text(element):
     69            return ''.join([c.data for c in element.childNodes
     70                            if c.nodeType == Node.TEXT_NODE])
     71
     72        def children(element):
     73            return [c for c in element.childNodes
     74                    if c.nodeType == Node.ELEMENT_NODE]
     75
     76        def norm_child_text(element):
     77            return norm_whitespace(child_text(element))
     78
     79        def attrs_dict(element):
     80            return dict(element.attributes.items())
     81
     82        def check_element(want_element, got_element):
     83            if want_element.tagName != got_element.tagName:
     84                return False
     85            if norm_child_text(want_element) != norm_child_text(got_element):
     86                return False
     87            if attrs_dict(want_element) != attrs_dict(got_element):
     88                return False
     89            want_children = children(want_element)
     90            got_children = children(got_element)
     91            if len(want_children) != len(got_children):
     92                return False
     93            for want, got in zip(want_children, got_children):
     94                if not check_element(want, got):
     95                    return False
     96            return True
     97
     98        want, got = self._strip_quotes(want, got)
     99        if not looks_like_markup(want):
     100            return False
     101
     102        # Wrapper to support XML fragments
     103        wrapper = u"<root>%s</root>"
     104        try:
     105            want_root = parseString(wrapper % want).firstChild
     106            got_root = parseString(wrapper % got).firstChild
     107        except:
     108            return False
     109        return check_element(want_root, got_root)
     110
     111    def check_output_json(self, want, got, optionsflags):
     112        # Tries do compare want and got as if they were JSON-encoded data
     113        want, got = self._strip_quotes(want, got)
     114        try:
     115            want_json = simplejson.loads(want)
     116            got_json = simplejson.loads(got)
     117        except:
     118            return False
     119        return  want_json == got_json
     120
     121    def _strip_quotes(self, want, got):
     122        """
     123        Strip quotes of doctests output values:
     124
     125        >>> o = OutputChecker()
     126        >>> o._strip_quotes("'foo'")
     127        "foo"
     128        >>> o._strip_quotes('"foo"')
     129        "foo"
     130        >>> o._strip_quotes("u'foo'")
     131        "foo"
     132        >>> o._strip_quotes('u"foo"')
     133        "foo"
     134        """
     135        def is_quoted_string(s):
     136            s = s.strip()
     137            return (len(s) >= 2
     138                    and s[0] == s[-1]
     139                    and s[0] in ('"', "'"))
     140
     141        def is_quoted_unicode(s):
     142            s = s.strip()
     143            return (len(s) >= 3
     144                    and s[0] == 'u'
     145                    and s[1] == s[-1]
     146                    and s[1] in ('"', "'"))
     147
     148        if is_quoted_string(want) and is_quoted_string(got):
     149            want = want.strip()[1:-1]
     150            got = got.strip()[1:-1]
     151        elif is_quoted_unicode(want) and is_quoted_unicode(got):
     152            want = want.strip()[2:-1]
     153            got = got.strip()[2:-1]
     154        return want, got
     155
    37156
    38157class DocTestRunner(doctest.DocTestRunner):
    39158    def __init__(self, *args, **kwargs):
Back to Top