diff options
Diffstat (limited to 'Lib/test/test_htmlparser.py')
-rw-r--r-- | Lib/test/test_htmlparser.py | 520 |
1 files changed, 326 insertions, 194 deletions
diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 8cc461f1ee3..c4f80cca30e 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -1,17 +1,17 @@ """Tests for HTMLParser.py.""" -import HTMLParser +import html.parser import pprint import unittest -from test import test_support +from test import support -class EventCollector(HTMLParser.HTMLParser): +class EventCollector(html.parser.HTMLParser): - def __init__(self): + def __init__(self, *args, **kw): self.events = [] self.append = self.events.append - HTMLParser.HTMLParser.__init__(self) + html.parser.HTMLParser.__init__(self, *args, **kw) def get_events(self): # Normalize the list of events so that buffer artefacts don't @@ -72,8 +72,13 @@ class EventCollectorExtra(EventCollector): class TestCaseBase(unittest.TestCase): - def _run_check(self, source, expected_events, collector=EventCollector): - parser = collector() + def get_collector(self): + raise NotImplementedError + + def _run_check(self, source, expected_events, collector=None): + if collector is None: + collector = self.get_collector() + parser = collector for s in source: parser.feed(s) parser.close() @@ -84,17 +89,20 @@ class TestCaseBase(unittest.TestCase): "\nReceived:\n" + pprint.pformat(events)) def _run_check_extra(self, source, events): - self._run_check(source, events, EventCollectorExtra) + self._run_check(source, events, EventCollectorExtra()) def _parse_error(self, source): def parse(source=source): - parser = HTMLParser.HTMLParser() + parser = self.get_collector() parser.feed(source) parser.close() - self.assertRaises(HTMLParser.HTMLParseError, parse) + self.assertRaises(html.parser.HTMLParseError, parse) + +class HTMLParserStrictTestCase(TestCaseBase): -class HTMLParserTestCase(TestCaseBase): + def get_collector(self): + return EventCollector(strict=True) def test_processing_instruction_only(self): self._run_check("<?processing instruction>", [ @@ -136,6 +144,13 @@ text ("data", "\n"), ]) + def test_malformatted_charref(self): + self._run_check("<p>&#bad;</p>", [ + ("starttag", "p", []), + ("data", "&#bad;"), + ("endtag", "p"), + ]) + def test_unclosed_entityref(self): self._run_check("&entityref foo", [ ("entityref", "entityref"), @@ -164,8 +179,7 @@ text ]) def test_illegal_declarations(self): - self._run_check('<!spacer type="block" height="25">', - [('comment', 'spacer type="block" height="25"')]) + self._parse_error('<!spacer type="block" height="25">') def test_starttag_end_boundary(self): self._run_check("""<a b='<'>""", [("starttag", "a", [("b", "<")])]) @@ -202,19 +216,18 @@ text self._run_check(["<!--abc-->", ""], output) def test_starttag_junk_chars(self): - self._run_check("</>", []) - self._run_check("</$>", [('comment', '$')]) - self._run_check("</", [('data', '</')]) - self._run_check("</a", [('data', '</a')]) - # XXX this might be wrong - self._run_check("<a<a>", [('data', '<a'), ('starttag', 'a', [])]) - self._run_check("</a<a>", [('endtag', 'a<a')]) - self._run_check("<!", [('data', '<!')]) - self._run_check("<a", [('data', '<a')]) - self._run_check("<a foo='bar'", [('data', "<a foo='bar'")]) - self._run_check("<a foo='bar", [('data', "<a foo='bar")]) - self._run_check("<a foo='>'", [('data', "<a foo='>'")]) - self._run_check("<a foo='>", [('data', "<a foo='>")]) + self._parse_error("</>") + self._parse_error("</$>") + self._parse_error("</") + self._parse_error("</a") + self._parse_error("<a<a>") + self._parse_error("</a<a>") + self._parse_error("<!") + self._parse_error("<a") + self._parse_error("<a foo='bar'") + self._parse_error("<a foo='bar") + self._parse_error("<a foo='>'") + self._parse_error("<a foo='>") def test_valid_doctypes(self): # from http://www.w3.org/QA/2002/04/valid-dtd-list.html @@ -240,6 +253,142 @@ text self._run_check("<!DOCTYPE %s>" % dtd, [('decl', 'DOCTYPE ' + dtd)]) + def test_declaration_junk_chars(self): + self._parse_error("<!DOCTYPE foo $ >") + + def test_startendtag(self): + self._run_check("<p/>", [ + ("startendtag", "p", []), + ]) + self._run_check("<p></p>", [ + ("starttag", "p", []), + ("endtag", "p"), + ]) + self._run_check("<p><img src='foo' /></p>", [ + ("starttag", "p", []), + ("startendtag", "img", [("src", "foo")]), + ("endtag", "p"), + ]) + + def test_get_starttag_text(self): + s = """<foo:bar \n one="1"\ttwo=2 >""" + self._run_check_extra(s, [ + ("starttag", "foo:bar", [("one", "1"), ("two", "2")]), + ("starttag_text", s)]) + + def test_cdata_content(self): + contents = [ + '<!-- not a comment --> ¬-an-entity-ref;', + "<not a='start tag'>", + '<a href="" /> <p> <span></span>', + 'foo = "</scr" + "ipt>";', + 'foo = "</SCRIPT" + ">";', + 'foo = <\n/script> ', + '<!-- document.write("</scr" + "ipt>"); -->', + ('\n//<![CDATA[\n' + 'document.write(\'<s\'+\'cript type="text/javascript" ' + 'src="http://www.example.org/r=\'+new ' + 'Date().getTime()+\'"><\\/s\'+\'cript>\');\n//]]>'), + '\n<!-- //\nvar foo = 3.14;\n// -->\n', + 'foo = "</sty" + "le>";', + '<!-- \u2603 -->', + # these two should be invalid according to the HTML 5 spec, + # section 8.1.2.2 + #'foo = </\nscript>', + #'foo = </ script>', + ] + elements = ['script', 'style', 'SCRIPT', 'STYLE', 'Script', 'Style'] + for content in contents: + for element in elements: + element_lower = element.lower() + s = '<{element}>{content}</{element}>'.format(element=element, + content=content) + self._run_check(s, [("starttag", element_lower, []), + ("data", content), + ("endtag", element_lower)]) + + def test_cdata_with_closing_tags(self): + # see issue #13358 + # make sure that HTMLParser calls handle_data only once for each CDATA. + # The normal event collector normalizes the events in get_events, + # so we override it to return the original list of events. + class Collector(EventCollector): + def get_events(self): + return self.events + + content = """<!-- not a comment --> ¬-an-entity-ref; + <a href="" /> </p><p> <span></span></style> + '</script' + '>'""" + for element in [' script', 'script ', ' script ', + '\nscript', 'script\n', '\nscript\n']: + element_lower = element.lower().strip() + s = '<script>{content}</{element}>'.format(element=element, + content=content) + self._run_check(s, [("starttag", element_lower, []), + ("data", content), + ("endtag", element_lower)], + collector=Collector()) + + def test_comments(self): + html = ("<!-- I'm a valid comment -->" + '<!--me too!-->' + '<!------>' + '<!---->' + '<!----I have many hyphens---->' + '<!-- I have a > in the middle -->' + '<!-- and I have -- in the middle! -->') + expected = [('comment', " I'm a valid comment "), + ('comment', 'me too!'), + ('comment', '--'), + ('comment', ''), + ('comment', '--I have many hyphens--'), + ('comment', ' I have a > in the middle '), + ('comment', ' and I have -- in the middle! ')] + self._run_check(html, expected) + + def test_condcoms(self): + html = ('<!--[if IE & !(lte IE 8)]>aren\'t<![endif]-->' + '<!--[if IE 8]>condcoms<![endif]-->' + '<!--[if lte IE 7]>pretty?<![endif]-->') + expected = [('comment', "[if IE & !(lte IE 8)]>aren't<![endif]"), + ('comment', '[if IE 8]>condcoms<![endif]'), + ('comment', '[if lte IE 7]>pretty?<![endif]')] + self._run_check(html, expected) + + +class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): + + def get_collector(self): + return EventCollector(strict=False) + + def test_tolerant_parsing(self): + self._run_check('<html <html>te>>xt&a<<bc</a></html>\n' + '<img src="URL><//img></html</html>', [ + ('starttag', 'html', [('<html', None)]), + ('data', 'te>>xt'), + ('entityref', 'a'), + ('data', '<<bc'), + ('endtag', 'a'), + ('endtag', 'html'), + ('data', '\n<img src="URL>'), + ('comment', '/img'), + ('endtag', 'html<')]) + + def test_starttag_junk_chars(self): + self._run_check("</>", []) + self._run_check("</$>", [('comment', '$')]) + self._run_check("</", [('data', '</')]) + self._run_check("</a", [('data', '</a')]) + # XXX this might be wrong + self._run_check("<a<a>", [('data', '<a'), ('starttag', 'a', [])]) + self._run_check("</a<a>", [('endtag', 'a<a')]) + self._run_check("<!", [('data', '<!')]) + self._run_check("<a", [('data', '<a')]) + self._run_check("<a foo='bar'", [('data', "<a foo='bar'")]) + self._run_check("<a foo='bar", [('data', "<a foo='bar")]) + self._run_check("<a foo='>'", [('data', "<a foo='>'")]) + self._run_check("<a foo='>", [('data', "<a foo='>")]) + def test_slashes_in_starttag(self): self._run_check('<a foo="var"/>', [('startendtag', 'a', [('foo', 'var')])]) html = ('<img width=902 height=250px ' @@ -274,19 +423,45 @@ text def test_declaration_junk_chars(self): self._run_check("<!DOCTYPE foo $ >", [('decl', 'DOCTYPE foo $ ')]) - def test_startendtag(self): - self._run_check("<p/>", [ - ("startendtag", "p", []), - ]) - self._run_check("<p></p>", [ - ("starttag", "p", []), - ("endtag", "p"), - ]) - self._run_check("<p><img src='foo' /></p>", [ - ("starttag", "p", []), - ("startendtag", "img", [("src", "foo")]), - ("endtag", "p"), - ]) + def test_illegal_declarations(self): + self._run_check('<!spacer type="block" height="25">', + [('comment', 'spacer type="block" height="25"')]) + + def test_with_unquoted_attributes(self): + # see #12008 + html = ("<html><body bgcolor=d0ca90 text='181008'>" + "<table cellspacing=0 cellpadding=1 width=100% ><tr>" + "<td align=left><font size=-1>" + "- <a href=/rabota/><span class=en> software-and-i</span></a>" + "- <a href='/1/'><span class=en> library</span></a></table>") + expected = [ + ('starttag', 'html', []), + ('starttag', 'body', [('bgcolor', 'd0ca90'), ('text', '181008')]), + ('starttag', 'table', + [('cellspacing', '0'), ('cellpadding', '1'), ('width', '100%')]), + ('starttag', 'tr', []), + ('starttag', 'td', [('align', 'left')]), + ('starttag', 'font', [('size', '-1')]), + ('data', '- '), ('starttag', 'a', [('href', '/rabota/')]), + ('starttag', 'span', [('class', 'en')]), ('data', ' software-and-i'), + ('endtag', 'span'), ('endtag', 'a'), + ('data', '- '), ('starttag', 'a', [('href', '/1/')]), + ('starttag', 'span', [('class', 'en')]), ('data', ' library'), + ('endtag', 'span'), ('endtag', 'a'), ('endtag', 'table') + ] + self._run_check(html, expected) + + def test_comma_between_attributes(self): + self._run_check('<form action="/xxx.php?a=1&b=2&", ' + 'method="post">', [ + ('starttag', 'form', + [('action', '/xxx.php?a=1&b=2&'), + (',', None), ('method', 'post')])]) + + def test_weird_chars_in_unquoted_attribute_values(self): + self._run_check('<form action=bogus|&#()value>', [ + ('starttag', 'form', + [('action', 'bogus|&#()value')])]) def test_invalid_end_tags(self): # A collection of broken end tags. <br> is used as separator. @@ -326,79 +501,100 @@ text ('data', '"> confuses the parser')] self._run_check(html, expected) - def test_get_starttag_text(self): - s = """<foo:bar \n one="1"\ttwo=2 >""" - self._run_check_extra(s, [ - ("starttag", "foo:bar", [("one", "1"), ("two", "2")]), - ("starttag_text", s)]) - - def test_cdata_content(self): - contents = [ - '<!-- not a comment --> ¬-an-entity-ref;', - "<not a='start tag'>", - '<a href="" /> <p> <span></span>', - 'foo = "</scr" + "ipt>";', - 'foo = "</SCRIPT" + ">";', - 'foo = <\n/script> ', - '<!-- document.write("</scr" + "ipt>"); -->', - ('\n//<![CDATA[\n' - 'document.write(\'<s\'+\'cript type="text/javascript" ' - 'src="http://www.example.org/r=\'+new ' - 'Date().getTime()+\'"><\\/s\'+\'cript>\');\n//]]>'), - '\n<!-- //\nvar foo = 3.14;\n// -->\n', - 'foo = "</sty" + "le>";', - u'<!-- \u2603 -->', - # these two should be invalid according to the HTML 5 spec, - # section 8.1.2.2 - #'foo = </\nscript>', - #'foo = </ script>', + def test_correct_detection_of_start_tags(self): + # see #13273 + html = ('<div style="" ><b>The <a href="some_url">rain</a> ' + '<br /> in <span>Spain</span></b></div>') + expected = [ + ('starttag', 'div', [('style', '')]), + ('starttag', 'b', []), + ('data', 'The '), + ('starttag', 'a', [('href', 'some_url')]), + ('data', 'rain'), + ('endtag', 'a'), + ('data', ' '), + ('startendtag', 'br', []), + ('data', ' in '), + ('starttag', 'span', []), + ('data', 'Spain'), + ('endtag', 'span'), + ('endtag', 'b'), + ('endtag', 'div') ] - elements = ['script', 'style', 'SCRIPT', 'STYLE', 'Script', 'Style'] - for content in contents: - for element in elements: - element_lower = element.lower() - s = u'<{element}>{content}</{element}>'.format(element=element, - content=content) - self._run_check(s, [("starttag", element_lower, []), - ("data", content), - ("endtag", element_lower)]) + self._run_check(html, expected) - def test_cdata_with_closing_tags(self): - # see issue #13358 - # make sure that HTMLParser calls handle_data only once for each CDATA. - # The normal event collector normalizes the events in get_events, - # so we override it to return the original list of events. - class Collector(EventCollector): - def get_events(self): - return self.events + html = '<div style="", foo = "bar" ><b>The <a href="some_url">rain</a>' + expected = [ + ('starttag', 'div', [('style', ''), (',', None), ('foo', 'bar')]), + ('starttag', 'b', []), + ('data', 'The '), + ('starttag', 'a', [('href', 'some_url')]), + ('data', 'rain'), + ('endtag', 'a'), + ] + self._run_check(html, expected) - content = """<!-- not a comment --> ¬-an-entity-ref; - <a href="" /> </p><p> & <span></span></style> - '</script' + '>' </html> </head> </scripter>!""" - for element in [' script', 'script ', ' script ', - '\nscript', 'script\n', '\nscript\n']: - s = u'<script>{content}</{element}>'.format(element=element, - content=content) - self._run_check(s, [("starttag", "script", []), - ("data", content), - ("endtag", "script")], - collector=Collector) + def test_unescape_function(self): + p = self.get_collector() + self.assertEqual(p.unescape('&#bad;'),'&#bad;') + self.assertEqual(p.unescape('&'),'&') + # see #12888 + self.assertEqual(p.unescape('{ ' * 1050), '{ ' * 1050) - def test_malformatted_charref(self): - self._run_check("<p>&#bad;</p>", [ - ("starttag", "p", []), - ("data", "&#bad;"), - ("endtag", "p"), - ]) + def test_broken_comments(self): + html = ('<! not really a comment >' + '<! not a comment either -->' + '<! -- close enough -->' + '<!><!<-- this was an empty comment>' + '<!!! another bogus comment !!!>') + expected = [ + ('comment', ' not really a comment '), + ('comment', ' not a comment either --'), + ('comment', ' -- close enough --'), + ('comment', ''), + ('comment', '<-- this was an empty comment'), + ('comment', '!! another bogus comment !!!'), + ] + self._run_check(html, expected) - def test_unescape_function(self): - parser = HTMLParser.HTMLParser() - self.assertEqual(parser.unescape('&#bad;'),'&#bad;') - self.assertEqual(parser.unescape('&'),'&') + def test_broken_condcoms(self): + # these condcoms are missing the '--' after '<!' and before the '>' + html = ('<![if !(IE)]>broken condcom<![endif]>' + '<![if ! IE]><link href="favicon.tiff"/><![endif]>' + '<![if !IE 6]><img src="firefox.png" /><![endif]>' + '<![if !ie 6]><b>foo</b><![endif]>' + '<![if (!IE)|(lt IE 9)]><img src="mammoth.bmp" /><![endif]>') + # According to the HTML5 specs sections "8.2.4.44 Bogus comment state" + # and "8.2.4.45 Markup declaration open state", comment tokens should + # be emitted instead of 'unknown decl', but calling unknown_decl + # provides more flexibility. + # See also Lib/_markupbase.py:parse_declaration + expected = [ + ('unknown decl', 'if !(IE)'), + ('data', 'broken condcom'), + ('unknown decl', 'endif'), + ('unknown decl', 'if ! IE'), + ('startendtag', 'link', [('href', 'favicon.tiff')]), + ('unknown decl', 'endif'), + ('unknown decl', 'if !IE 6'), + ('startendtag', 'img', [('src', 'firefox.png')]), + ('unknown decl', 'endif'), + ('unknown decl', 'if !ie 6'), + ('starttag', 'b', []), + ('data', 'foo'), + ('endtag', 'b'), + ('unknown decl', 'endif'), + ('unknown decl', 'if (!IE)|(lt IE 9)'), + ('startendtag', 'img', [('src', 'mammoth.bmp')]), + ('unknown decl', 'endif') + ] + self._run_check(html, expected) +class AttributesStrictTestCase(TestCaseBase): -class AttributesTestCase(TestCaseBase): + def get_collector(self): + return EventCollector(strict=True) def test_attr_syntax(self): output = [ @@ -427,17 +623,17 @@ class AttributesTestCase(TestCaseBase): def test_attr_nonascii(self): # see issue 7311 self._run_check( - u"<img src=/foo/bar.png alt=\u4e2d\u6587>", + "<img src=/foo/bar.png alt=\u4e2d\u6587>", [("starttag", "img", [("src", "/foo/bar.png"), - ("alt", u"\u4e2d\u6587")])]) + ("alt", "\u4e2d\u6587")])]) self._run_check( - u"<a title='\u30c6\u30b9\u30c8' href='\u30c6\u30b9\u30c8.html'>", - [("starttag", "a", [("title", u"\u30c6\u30b9\u30c8"), - ("href", u"\u30c6\u30b9\u30c8.html")])]) + "<a title='\u30c6\u30b9\u30c8' href='\u30c6\u30b9\u30c8.html'>", + [("starttag", "a", [("title", "\u30c6\u30b9\u30c8"), + ("href", "\u30c6\u30b9\u30c8.html")])]) self._run_check( - u'<a title="\u30c6\u30b9\u30c8" href="\u30c6\u30b9\u30c8.html">', - [("starttag", "a", [("title", u"\u30c6\u30b9\u30c8"), - ("href", u"\u30c6\u30b9\u30c8.html")])]) + '<a title="\u30c6\u30b9\u30c8" href="\u30c6\u30b9\u30c8.html">', + [("starttag", "a", [("title", "\u30c6\u30b9\u30c8"), + ("href", "\u30c6\u30b9\u30c8.html")])]) def test_attr_entity_replacement(self): self._run_check( @@ -448,17 +644,26 @@ class AttributesTestCase(TestCaseBase): self._run_check( "<a a.b='v' c:d=v e-f=v>", [("starttag", "a", [("a.b", "v"), ("c:d", "v"), ("e-f", "v")])]) + + def test_entityrefs_in_attributes(self): + self._run_check( + "<html foo='€&aa&unsupported;'>", + [("starttag", "html", [("foo", "\u20AC&aa&unsupported;")])]) + + + +class AttributesTolerantTestCase(AttributesStrictTestCase): + + def get_collector(self): + return EventCollector(strict=False) + + def test_attr_funky_names2(self): self._run_check( "<a $><b $=%><c \=/>", [("starttag", "a", [("$", None)]), ("starttag", "b", [("$", "%")]), ("starttag", "c", [("\\", "/")])]) - def test_entityrefs_in_attributes(self): - self._run_check( - "<html foo='€&aa&unsupported;'>", - [("starttag", "html", [("foo", u"\u20AC&aa&unsupported;")])]) - def test_entities_in_attribute_value(self): # see #1200313 for entity in ['&', '&', '&', '&']: @@ -482,9 +687,9 @@ class AttributesTestCase(TestCaseBase): ('data', 'test - bad1'), ('endtag', 'a'), ('starttag', 'a', [('href', "test'+style='color:red;ba2'")]), ('data', 'test - bad2'), ('endtag', 'a'), - ('starttag', 'a', [('href', u"test'\xa0style='color:red;bad3'")]), + ('starttag', 'a', [('href', "test'\xa0style='color:red;bad3'")]), ('data', 'test - bad3'), ('endtag', 'a'), - ('starttag', 'a', [('href', u"test'\xa0style='color:red;bad4'")]), + ('starttag', 'a', [('href', "test'\xa0style='color:red;bad4'")]), ('data', 'test - bad4'), ('endtag', 'a') ] self._run_check(html, expected) @@ -526,84 +731,11 @@ class AttributesTestCase(TestCaseBase): [("href", "http://www.example.org/\">;")]), ("data", "spam"), ("endtag", "a")]) - def test_comments(self): - html = ("<!-- I'm a valid comment -->" - '<!--me too!-->' - '<!------>' - '<!---->' - '<!----I have many hyphens---->' - '<!-- I have a > in the middle -->' - '<!-- and I have -- in the middle! -->') - expected = [('comment', " I'm a valid comment "), - ('comment', 'me too!'), - ('comment', '--'), - ('comment', ''), - ('comment', '--I have many hyphens--'), - ('comment', ' I have a > in the middle '), - ('comment', ' and I have -- in the middle! ')] - self._run_check(html, expected) - - def test_broken_comments(self): - html = ('<! not really a comment >' - '<! not a comment either -->' - '<! -- close enough -->' - '<!><!<-- this was an empty comment>' - '<!!! another bogus comment !!!>') - expected = [ - ('comment', ' not really a comment '), - ('comment', ' not a comment either --'), - ('comment', ' -- close enough --'), - ('comment', ''), - ('comment', '<-- this was an empty comment'), - ('comment', '!! another bogus comment !!!'), - ] - self._run_check(html, expected) - - def test_condcoms(self): - html = ('<!--[if IE & !(lte IE 8)]>aren\'t<![endif]-->' - '<!--[if IE 8]>condcoms<![endif]-->' - '<!--[if lte IE 7]>pretty?<![endif]-->') - expected = [('comment', "[if IE & !(lte IE 8)]>aren't<![endif]"), - ('comment', '[if IE 8]>condcoms<![endif]'), - ('comment', '[if lte IE 7]>pretty?<![endif]')] - self._run_check(html, expected) - - def test_broken_condcoms(self): - # these condcoms are missing the '--' after '<!' and before the '>' - html = ('<![if !(IE)]>broken condcom<![endif]>' - '<![if ! IE]><link href="favicon.tiff"/><![endif]>' - '<![if !IE 6]><img src="firefox.png" /><![endif]>' - '<![if !ie 6]><b>foo</b><![endif]>' - '<![if (!IE)|(lt IE 9)]><img src="mammoth.bmp" /><![endif]>') - # According to the HTML5 specs sections "8.2.4.44 Bogus comment state" - # and "8.2.4.45 Markup declaration open state", comment tokens should - # be emitted instead of 'unknown decl', but calling unknown_decl - # provides more flexibility. - # See also Lib/_markupbase.py:parse_declaration - expected = [ - ('unknown decl', 'if !(IE)'), - ('data', 'broken condcom'), - ('unknown decl', 'endif'), - ('unknown decl', 'if ! IE'), - ('startendtag', 'link', [('href', 'favicon.tiff')]), - ('unknown decl', 'endif'), - ('unknown decl', 'if !IE 6'), - ('startendtag', 'img', [('src', 'firefox.png')]), - ('unknown decl', 'endif'), - ('unknown decl', 'if !ie 6'), - ('starttag', 'b', []), - ('data', 'foo'), - ('endtag', 'b'), - ('unknown decl', 'endif'), - ('unknown decl', 'if (!IE)|(lt IE 9)'), - ('startendtag', 'img', [('src', 'mammoth.bmp')]), - ('unknown decl', 'endif') - ] - self._run_check(html, expected) def test_main(): - test_support.run_unittest(HTMLParserTestCase, AttributesTestCase) + support.run_unittest(HTMLParserStrictTestCase, HTMLParserTolerantTestCase, + AttributesStrictTestCase, AttributesTolerantTestCase) if __name__ == "__main__": |