# Perforce Defect Tracking Integration Project # # # TEST_CHECK_XHTML.PY -- UNIT TEST FOR XHTML CHECKER # # Gareth Rees, Ravenbrook Limited, 2001-05-06 # # # 1. INTRODUCTION # # This Python module is a unit test case for the check_xhtml Python # module that checks XHTML documents against the XHTML 1.0 Transitional # specification. # # The tests are purely regression tests. The test cases feed documents # to the XHTML checker, collect the output of the checker, and compare # the actual output to the expected output. import StringIO import check_xhtml import os import string import tempfile import unittest import whrandom class case(unittest.TestCase): def __init__(self, name, text, errors, methodName='runTest'): self.name = name self.text = text self.errors = errors unittest.TestCase.__init__(self, methodName) def shortDescription(self): return self.name + " (test_check_xhtml)" def runTest(self): error_stream = StringIO.StringIO() h = check_xhtml.checker(error_stream = error_stream) if os.name != 'nt' and whrandom.randint(0,1): name = tempfile.mktemp() + '.html' stream = open(name, 'w') stream.write(self.text) stream.close() h.check(name) os.remove(name) else: text_stream = StringIO.StringIO(self.text) name = "Test" h.check_stream(name, text_stream) expected = string.join(map(lambda i, name=name: name + i + '\n', self.errors), '') found = error_stream.getvalue() if expected != found: self.fail("XHTML document:\n%s\n%s\n%s\nExpected errors:\n" "%s%s\nFound errors:\n%s" % ("-" * 70, self.text, "-" * 70, expected, "-" * 70, found)) start = ('\n' '\n' '\n' 'Test document\n' '\n') end = "\n" test_cases = [ ('Unknown element', start + "" + end, ["(6) [1] element is not legal in XHTML 1.0 " "Transitional."]), ('Element not allowed in element', start + "

    Foo

" + end, ["(6) [2] Element

appears in

    (not allowed)."]), ('Unknown attribute', start + '

    Foo

    ' + end, ["(6) [4] Attribute 'foo' is not allowed in

    ."]), ('Id attribute but no name', start + 'Foo' + end, ["(6) [5] element has 'id' attribute but no 'name' " "attribute."]), ('Name attribute but no id', start + 'Foo' + end, ["(6) [6] element has 'name' attribute but no 'id' " "attribute."]), ('Id and name attributes differ', start + 'Foo' + end, ["(6) [7] element has id 'bar' but name 'foo'."]), ('Empty element contains character data', start + '
    foo
    ' + end, ["(6) [8]
    element contains character data 'foo'."]), ('Element may not be empty', start + '

      ' + end, ["(6) [9]
        element is empty."]), ('Unknown value for attribute', start + '
        ' + end, ["(6) [10] Attribute 'clear' for element
        has value 'foo' " "(must be one of [left, all, right, none])."]), ('Attribute value not a number', start + '
        ' + end, ["(6) [11] Attribute 'value' for element
      1. has illegal " "value 'foo' (not a Number)."]), ('Duplicate id', start + 'Foo\n' 'Bar' + end, ["(7) [12] Duplicate id 'Foo' (original on line 6)."]), ('Missing required attribute', start + '' + end, ["(6) [13] Attribute 'alt' is required for but not " "present."]), ('Table element has two captions', start + '' '
        FooBar
        Foo
        ' + end, ["(6) [15] Contents of element [caption, caption, tr] " "doesn't match XHTML specification (caption?, (col*|colgroup*), " "thead?, tfoot?, (tbody+|tr+))."]), ('Table element has no rows', start + '
        Foo
        ' + end, ["(6) [15] Contents of element [caption] doesn't " "match XHTML specification (caption?, (col*|colgroup*), thead?, " "tfoot?, (tbody+|tr+))."]), ('Table element mixes col and colgroup', start + '
        ' '
        ' + end, ["(6) [15] Contents of element [col, colgroup, tbody] " "doesn't match XHTML specification (caption?, (col*|colgroup*), " "thead?, tfoot?, (tbody+|tr+))."]), ('Anchor in anchor', start + 'baz' + end, ["(6) [16] Element appears below on line 6 (not " "allowed)."]), ('Section anchor has no id', start + '

        Title

        \n

        Heading

        ' + end, ["(7) [17] Section anchor has no 'id' attribute."]), ('Section anchor inconsistent with heading level', start + '

        Title

        \n' '

        1. Heading

        \n' '

        Heading

        ' + end, ["(8) [18] Anchor for

        has id 'section-2': should look " "like 'section-1.1'."]), ('Missing section anchor', start + '

        Title

        \n' '

        1. Heading

        \n' '

        1.1. Head

        \n' '

        Example

        ' + end, ["(9) [19]

        has no section anchor."]), ('Table cell with valign attribute', start + '

        Foo
        ' + end, ["(6) [20] has valign attribute: better in the ."]), ('Wrong reference anchor', start + '

        Title

        \n' '

        A. References

        \n' '\n' '\n
        ' 'GDR 1971-01-02
        ' + end, ["(9) [21] Reference anchor has id 'GDR-1971-01-02': should " "start with 'ref-'."]), ('Cross reference has no target', start + 'Foo' + end, ["(6) [22] Cross-reference '#no-target' has no target."]), ('Cross reference should be link', start + '

        Title

        \n' '

        [GDR 1971-01-03]

        \n' '

        A. References

        \n' '\n' '\n
        [' 'GDR 1971-01-03]Foo
        ' + end, ["(7) [23] Cross-reference '#ref-GDR-1971-01-03' to " "references section should link to target 'http://foo.invalid/' " "instead."]), ('Inconsistent heading sequence', start + '

        Title

        \n' '

        2. Foo

        \n' '

        2.1.1. Bar' '

        ' + end, ["(8) [25]

        follows

        ."]), ('Missing attributes for img element', start + 'foo' + end, ["(6) [26] Attribute 'height' is recommended for but not " "present.", "(6) [26] Attribute 'width' is recommended for but not " "present."]), ('Missing title attribute for abbr element', start + 'P4DTI' + end, ["(6) [26] Attribute 'title' is recommended for but not " "present."]), ('Id reference has no target', start + '' '
        ' + end, ["(6) [27] IDREF 'no-such-header' has no target.", "(6) [27] IDREF 'no-sirree' has no target."]), ('Head element has two titles', '\n' '\n' '\n' 'Title oneTitle two' '', ["(4) [15] Contents of element [title, title] doesn't " "match XHTML specification (%head.misc;, ((title, %head.misc;, " "(base, %head.misc;)?) | (base, %head.misc;, (title, " "%head.misc;))))."]), ('HTML element has two bodies', '\n' '\n' '\n' 'Title one ', ["(4) [15] Contents of element [head, body, body] " "doesn't match XHTML specification (head, body)."]), ('Top-level element not HTML', '\n' '\n' '\n', ["(3) [3] Top-level element is (should be )."]), ('Mismatched closing tag', start + '

        Bold Bold-italic italic

        \n' + end, ["(6) Mismatched closing tag (opening tag was at " "line 6)."]), ] def tests(): suite = unittest.TestSuite() for (name, text, errors) in test_cases: suite.addTest(case(name, text, errors)) return suite if __name__ == "__main__": unittest.main(defaultTest="tests") # A. REFERENCES # # [PyUnit] "PyUnit - a unit testing framework for Python"; Steve # Purcell; . # # # B. DOCUMENT HISTORY # # 2001-05-06 GDR Created. # # 2001-12-14 GDR Added tests for wrong top-level element and mismatched # closing tag. Check the file interface as well as the stream # interface. # # 2001-12-25 GDR Added BSD license. # # # C. COPYRIGHT AND LICENSE # # Copyright 2001 Gareth Rees. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # # $Id: //info.ravenbrook.com/project/p4dti/version/2.1/test/test_check_xhtml.py#1 $