0001 # regression test for SAX 2.0 -*- coding: iso-8859-1 -*- 0002 # $Id: test_sax.py,v 1.27 2004/08/03 10:17:34 mwh Exp $ 0003 0004 from xml.sax import make_parser, ContentHandler, \ 0005 SAXException, SAXReaderNotAvailable, SAXParseException 0006 try: 0007 make_parser() 0008 except SAXReaderNotAvailable: 0009 # don't try to test this module if we cannot create a parser 0010 raise ImportError("no XML parsers available") 0011 from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \ 0012 XMLFilterBase 0013 from xml.sax.expatreader import create_parser 0014 from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl 0015 from cStringIO import StringIO 0016 from test.test_support import verify, verbose, TestFailed, findfile 0017 import os 0018 0019 # ===== Utilities 0020 0021 tests = 0 0022 failures = [] 0023 0024 def confirm(outcome, name): 0025 global tests 0026 0027 tests = tests + 1 0028 if outcome: 0029 if verbose: 0030 print "Passed", name 0031 else: 0032 failures.append(name) 0033 0034 def test_make_parser2(): 0035 try: 0036 # Creating parsers several times in a row should succeed. 0037 # Testing this because there have been failures of this kind 0038 # before. 0039 from xml.sax import make_parser 0040 p = make_parser() 0041 from xml.sax import make_parser 0042 p = make_parser() 0043 from xml.sax import make_parser 0044 p = make_parser() 0045 from xml.sax import make_parser 0046 p = make_parser() 0047 from xml.sax import make_parser 0048 p = make_parser() 0049 from xml.sax import make_parser 0050 p = make_parser() 0051 except: 0052 return 0 0053 else: 0054 return p 0055 0056 0057 # =========================================================================== 0058 # 0059 # saxutils tests 0060 # 0061 # =========================================================================== 0062 0063 # ===== escape 0064 0065 def test_escape_basic(): 0066 return escape("Donald Duck & Co") == "Donald Duck & Co" 0067 0068 def test_escape_all(): 0069 return escape("<Donald Duck & Co>") == "<Donald Duck & Co>" 0070 0071 def test_escape_extra(): 0072 return escape("Hei på deg", {"å" : "å"}) == "Hei på deg" 0073 0074 # ===== unescape 0075 0076 def test_unescape_basic(): 0077 return unescape("Donald Duck & Co") == "Donald Duck & Co" 0078 0079 def test_unescape_all(): 0080 return unescape("<Donald Duck & Co>") == "<Donald Duck & Co>" 0081 0082 def test_unescape_extra(): 0083 return unescape("Hei på deg", {"å" : "å"}) == "Hei på deg" 0084 0085 def test_unescape_amp_extra(): 0086 return unescape("&foo;", {"&foo;": "splat"}) == "&foo;" 0087 0088 # ===== quoteattr 0089 0090 def test_quoteattr_basic(): 0091 return quoteattr("Donald Duck & Co") == '"Donald Duck & Co"' 0092 0093 def test_single_quoteattr(): 0094 return (quoteattr('Includes "double" quotes') 0095 == '\'Includes "double" quotes\'') 0096 0097 def test_double_quoteattr(): 0098 return (quoteattr("Includes 'single' quotes") 0099 == "\"Includes 'single' quotes\"") 0100 0101 def test_single_double_quoteattr(): 0102 return (quoteattr("Includes 'single' and \"double\" quotes") 0103 == "\"Includes 'single' and "double" quotes\"") 0104 0105 # ===== make_parser 0106 0107 def test_make_parser(): 0108 try: 0109 # Creating a parser should succeed - it should fall back 0110 # to the expatreader 0111 p = make_parser(['xml.parsers.no_such_parser']) 0112 except: 0113 return 0 0114 else: 0115 return p 0116 0117 0118 # ===== XMLGenerator 0119 0120 start = '<?xml version="1.0" encoding="iso-8859-1"?>\n' 0121 0122 def test_xmlgen_basic(): 0123 result = StringIO() 0124 gen = XMLGenerator(result) 0125 gen.startDocument() 0126 gen.startElement("doc", {}) 0127 gen.endElement("doc") 0128 gen.endDocument() 0129 0130 return result.getvalue() == start + "<doc></doc>" 0131 0132 def test_xmlgen_content(): 0133 result = StringIO() 0134 gen = XMLGenerator(result) 0135 0136 gen.startDocument() 0137 gen.startElement("doc", {}) 0138 gen.characters("huhei") 0139 gen.endElement("doc") 0140 gen.endDocument() 0141 0142 return result.getvalue() == start + "<doc>huhei</doc>" 0143 0144 def test_xmlgen_pi(): 0145 result = StringIO() 0146 gen = XMLGenerator(result) 0147 0148 gen.startDocument() 0149 gen.processingInstruction("test", "data") 0150 gen.startElement("doc", {}) 0151 gen.endElement("doc") 0152 gen.endDocument() 0153 0154 return result.getvalue() == start + "<?test data?><doc></doc>" 0155 0156 def test_xmlgen_content_escape(): 0157 result = StringIO() 0158 gen = XMLGenerator(result) 0159 0160 gen.startDocument() 0161 gen.startElement("doc", {}) 0162 gen.characters("<huhei&") 0163 gen.endElement("doc") 0164 gen.endDocument() 0165 0166 return result.getvalue() == start + "<doc><huhei&</doc>" 0167 0168 def test_xmlgen_attr_escape(): 0169 result = StringIO() 0170 gen = XMLGenerator(result) 0171 0172 gen.startDocument() 0173 gen.startElement("doc", {"a": '"'}) 0174 gen.startElement("e", {"a": "'"}) 0175 gen.endElement("e") 0176 gen.startElement("e", {"a": "'\""}) 0177 gen.endElement("e") 0178 gen.endElement("doc") 0179 gen.endDocument() 0180 0181 return result.getvalue() == start \ 0182 + "<doc a='\"'><e a=\"'\"></e><e a=\"'"\"></e></doc>" 0183 0184 def test_xmlgen_ignorable(): 0185 result = StringIO() 0186 gen = XMLGenerator(result) 0187 0188 gen.startDocument() 0189 gen.startElement("doc", {}) 0190 gen.ignorableWhitespace(" ") 0191 gen.endElement("doc") 0192 gen.endDocument() 0193 0194 return result.getvalue() == start + "<doc> </doc>" 0195 0196 ns_uri = "http://www.python.org/xml-ns/saxtest/" 0197 0198 def test_xmlgen_ns(): 0199 result = StringIO() 0200 gen = XMLGenerator(result) 0201 0202 gen.startDocument() 0203 gen.startPrefixMapping("ns1", ns_uri) 0204 gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) 0205 # add an unqualified name 0206 gen.startElementNS((None, "udoc"), None, {}) 0207 gen.endElementNS((None, "udoc"), None) 0208 gen.endElementNS((ns_uri, "doc"), "ns1:doc") 0209 gen.endPrefixMapping("ns1") 0210 gen.endDocument() 0211 0212 return result.getvalue() == start + \ 0213 ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % 0214 ns_uri) 0215 0216 # ===== XMLFilterBase 0217 0218 def test_filter_basic(): 0219 result = StringIO() 0220 gen = XMLGenerator(result) 0221 filter = XMLFilterBase() 0222 filter.setContentHandler(gen) 0223 0224 filter.startDocument() 0225 filter.startElement("doc", {}) 0226 filter.characters("content") 0227 filter.ignorableWhitespace(" ") 0228 filter.endElement("doc") 0229 filter.endDocument() 0230 0231 return result.getvalue() == start + "<doc>content </doc>" 0232 0233 # =========================================================================== 0234 # 0235 # expatreader tests 0236 # 0237 # =========================================================================== 0238 0239 # ===== XMLReader support 0240 0241 def test_expat_file(): 0242 parser = create_parser() 0243 result = StringIO() 0244 xmlgen = XMLGenerator(result) 0245 0246 parser.setContentHandler(xmlgen) 0247 parser.parse(open(findfile("test"+os.extsep+"xml"))) 0248 0249 return result.getvalue() == xml_test_out 0250 0251 # ===== DTDHandler support 0252 0253 class TestDTDHandler: 0254 0255 def __init__(self): 0256 self._notations = [] 0257 self._entities = [] 0258 0259 def notationDecl(self, name, publicId, systemId): 0260 self._notations.append((name, publicId, systemId)) 0261 0262 def unparsedEntityDecl(self, name, publicId, systemId, ndata): 0263 self._entities.append((name, publicId, systemId, ndata)) 0264 0265 def test_expat_dtdhandler(): 0266 parser = create_parser() 0267 handler = TestDTDHandler() 0268 parser.setDTDHandler(handler) 0269 0270 parser.feed('<!DOCTYPE doc [\n') 0271 parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n') 0272 parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n') 0273 parser.feed(']>\n') 0274 parser.feed('<doc></doc>') 0275 parser.close() 0276 0277 return handler._notations == [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)] and \ 0278 handler._entities == [("img", None, "expat.gif", "GIF")] 0279 0280 # ===== EntityResolver support 0281 0282 class TestEntityResolver: 0283 0284 def resolveEntity(self, publicId, systemId): 0285 inpsrc = InputSource() 0286 inpsrc.setByteStream(StringIO("<entity/>")) 0287 return inpsrc 0288 0289 def test_expat_entityresolver(): 0290 parser = create_parser() 0291 parser.setEntityResolver(TestEntityResolver()) 0292 result = StringIO() 0293 parser.setContentHandler(XMLGenerator(result)) 0294 0295 parser.feed('<!DOCTYPE doc [\n') 0296 parser.feed(' <!ENTITY test SYSTEM "whatever">\n') 0297 parser.feed(']>\n') 0298 parser.feed('<doc>&test;</doc>') 0299 parser.close() 0300 0301 return result.getvalue() == start + "<doc><entity></entity></doc>" 0302 0303 # ===== Attributes support 0304 0305 class AttrGatherer(ContentHandler): 0306 0307 def startElement(self, name, attrs): 0308 self._attrs = attrs 0309 0310 def startElementNS(self, name, qname, attrs): 0311 self._attrs = attrs 0312 0313 def test_expat_attrs_empty(): 0314 parser = create_parser() 0315 gather = AttrGatherer() 0316 parser.setContentHandler(gather) 0317 0318 parser.feed("<doc/>") 0319 parser.close() 0320 0321 return verify_empty_attrs(gather._attrs) 0322 0323 def test_expat_attrs_wattr(): 0324 parser = create_parser() 0325 gather = AttrGatherer() 0326 parser.setContentHandler(gather) 0327 0328 parser.feed("<doc attr='val'/>") 0329 parser.close() 0330 0331 return verify_attrs_wattr(gather._attrs) 0332 0333 def test_expat_nsattrs_empty(): 0334 parser = create_parser(1) 0335 gather = AttrGatherer() 0336 parser.setContentHandler(gather) 0337 0338 parser.feed("<doc/>") 0339 parser.close() 0340 0341 return verify_empty_nsattrs(gather._attrs) 0342 0343 def test_expat_nsattrs_wattr(): 0344 parser = create_parser(1) 0345 gather = AttrGatherer() 0346 parser.setContentHandler(gather) 0347 0348 parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri) 0349 parser.close() 0350 0351 attrs = gather._attrs 0352 0353 return attrs.getLength() == 1 and \ 0354 attrs.getNames() == [(ns_uri, "attr")] and \ 0355 (attrs.getQNames() == [] or attrs.getQNames() == ["ns:attr"]) and \ 0356 len(attrs) == 1 and \ 0357 attrs.has_key((ns_uri, "attr")) and \ 0358 attrs.keys() == [(ns_uri, "attr")] and \ 0359 attrs.get((ns_uri, "attr")) == "val" and \ 0360 attrs.get((ns_uri, "attr"), 25) == "val" and \ 0361 attrs.items() == [((ns_uri, "attr"), "val")] and \ 0362 attrs.values() == ["val"] and \ 0363 attrs.getValue((ns_uri, "attr")) == "val" and \ 0364 attrs[(ns_uri, "attr")] == "val" 0365 0366 # ===== InputSource support 0367 0368 xml_test_out = open(findfile("test"+os.extsep+"xml"+os.extsep+"out")).read() 0369 0370 def test_expat_inpsource_filename(): 0371 parser = create_parser() 0372 result = StringIO() 0373 xmlgen = XMLGenerator(result) 0374 0375 parser.setContentHandler(xmlgen) 0376 parser.parse(findfile("test"+os.extsep+"xml")) 0377 0378 return result.getvalue() == xml_test_out 0379 0380 def test_expat_inpsource_sysid(): 0381 parser = create_parser() 0382 result = StringIO() 0383 xmlgen = XMLGenerator(result) 0384 0385 parser.setContentHandler(xmlgen) 0386 parser.parse(InputSource(findfile("test"+os.extsep+"xml"))) 0387 0388 return result.getvalue() == xml_test_out 0389 0390 def test_expat_inpsource_stream(): 0391 parser = create_parser() 0392 result = StringIO() 0393 xmlgen = XMLGenerator(result) 0394 0395 parser.setContentHandler(xmlgen) 0396 inpsrc = InputSource() 0397 inpsrc.setByteStream(open(findfile("test"+os.extsep+"xml"))) 0398 parser.parse(inpsrc) 0399 0400 return result.getvalue() == xml_test_out 0401 0402 # ===== IncrementalParser support 0403 0404 def test_expat_incremental(): 0405 result = StringIO() 0406 xmlgen = XMLGenerator(result) 0407 parser = create_parser() 0408 parser.setContentHandler(xmlgen) 0409 0410 parser.feed("<doc>") 0411 parser.feed("</doc>") 0412 parser.close() 0413 0414 return result.getvalue() == start + "<doc></doc>" 0415 0416 def test_expat_incremental_reset(): 0417 result = StringIO() 0418 xmlgen = XMLGenerator(result) 0419 parser = create_parser() 0420 parser.setContentHandler(xmlgen) 0421 0422 parser.feed("<doc>") 0423 parser.feed("text") 0424 0425 result = StringIO() 0426 xmlgen = XMLGenerator(result) 0427 parser.setContentHandler(xmlgen) 0428 parser.reset() 0429 0430 parser.feed("<doc>") 0431 parser.feed("text") 0432 parser.feed("</doc>") 0433 parser.close() 0434 0435 return result.getvalue() == start + "<doc>text</doc>" 0436 0437 # ===== Locator support 0438 0439 def test_expat_locator_noinfo(): 0440 result = StringIO() 0441 xmlgen = XMLGenerator(result) 0442 parser = create_parser() 0443 parser.setContentHandler(xmlgen) 0444 0445 parser.feed("<doc>") 0446 parser.feed("</doc>") 0447 parser.close() 0448 0449 return parser.getSystemId() is None and \ 0450 parser.getPublicId() is None and \ 0451 parser.getLineNumber() == 1 0452 0453 def test_expat_locator_withinfo(): 0454 result = StringIO() 0455 xmlgen = XMLGenerator(result) 0456 parser = create_parser() 0457 parser.setContentHandler(xmlgen) 0458 parser.parse(findfile("test.xml")) 0459 0460 return parser.getSystemId() == findfile("test.xml") and \ 0461 parser.getPublicId() is None 0462 0463 0464 # =========================================================================== 0465 # 0466 # error reporting 0467 # 0468 # =========================================================================== 0469 0470 def test_expat_inpsource_location(): 0471 parser = create_parser() 0472 parser.setContentHandler(ContentHandler()) # do nothing 0473 source = InputSource() 0474 source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed 0475 name = "a file name" 0476 source.setSystemId(name) 0477 try: 0478 parser.parse(source) 0479 except SAXException, e: 0480 return e.getSystemId() == name 0481 0482 def test_expat_incomplete(): 0483 parser = create_parser() 0484 parser.setContentHandler(ContentHandler()) # do nothing 0485 try: 0486 parser.parse(StringIO("<foo>")) 0487 except SAXParseException: 0488 return 1 # ok, error found 0489 else: 0490 return 0 0491 0492 def test_sax_parse_exception_str(): 0493 # pass various values from a locator to the SAXParseException to 0494 # make sure that the __str__() doesn't fall apart when None is 0495 # passed instead of an integer line and column number 0496 # 0497 # use "normal" values for the locator: 0498 str(SAXParseException("message", None, 0499 DummyLocator(1, 1))) 0500 # use None for the line number: 0501 str(SAXParseException("message", None, 0502 DummyLocator(None, 1))) 0503 # use None for the column number: 0504 str(SAXParseException("message", None, 0505 DummyLocator(1, None))) 0506 # use None for both: 0507 str(SAXParseException("message", None, 0508 DummyLocator(None, None))) 0509 return 1 0510 0511 class DummyLocator: 0512 def __init__(self, lineno, colno): 0513 self._lineno = lineno 0514 self._colno = colno 0515 0516 def getPublicId(self): 0517 return "pubid" 0518 0519 def getSystemId(self): 0520 return "sysid" 0521 0522 def getLineNumber(self): 0523 return self._lineno 0524 0525 def getColumnNumber(self): 0526 return self._colno 0527 0528 # =========================================================================== 0529 # 0530 # xmlreader tests 0531 # 0532 # =========================================================================== 0533 0534 # ===== AttributesImpl 0535 0536 def verify_empty_attrs(attrs): 0537 try: 0538 attrs.getValue("attr") 0539 gvk = 0 0540 except KeyError: 0541 gvk = 1 0542 0543 try: 0544 attrs.getValueByQName("attr") 0545 gvqk = 0 0546 except KeyError: 0547 gvqk = 1 0548 0549 try: 0550 attrs.getNameByQName("attr") 0551 gnqk = 0 0552 except KeyError: 0553 gnqk = 1 0554 0555 try: 0556 attrs.getQNameByName("attr") 0557 gqnk = 0 0558 except KeyError: 0559 gqnk = 1 0560 0561 try: 0562 attrs["attr"] 0563 gik = 0 0564 except KeyError: 0565 gik = 1 0566 0567 return attrs.getLength() == 0 and \ 0568 attrs.getNames() == [] and \ 0569 attrs.getQNames() == [] and \ 0570 len(attrs) == 0 and \ 0571 not attrs.has_key("attr") and \ 0572 attrs.keys() == [] and \ 0573 attrs.get("attrs") is None and \ 0574 attrs.get("attrs", 25) == 25 and \ 0575 attrs.items() == [] and \ 0576 attrs.values() == [] and \ 0577 gvk and gvqk and gnqk and gik and gqnk 0578 0579 def verify_attrs_wattr(attrs): 0580 return attrs.getLength() == 1 and \ 0581 attrs.getNames() == ["attr"] and \ 0582 attrs.getQNames() == ["attr"] and \ 0583 len(attrs) == 1 and \ 0584 attrs.has_key("attr") and \ 0585 attrs.keys() == ["attr"] and \ 0586 attrs.get("attr") == "val" and \ 0587 attrs.get("attr", 25) == "val" and \ 0588 attrs.items() == [("attr", "val")] and \ 0589 attrs.values() == ["val"] and \ 0590 attrs.getValue("attr") == "val" and \ 0591 attrs.getValueByQName("attr") == "val" and \ 0592 attrs.getNameByQName("attr") == "attr" and \ 0593 attrs["attr"] == "val" and \ 0594 attrs.getQNameByName("attr") == "attr" 0595 0596 def test_attrs_empty(): 0597 return verify_empty_attrs(AttributesImpl({})) 0598 0599 def test_attrs_wattr(): 0600 return verify_attrs_wattr(AttributesImpl({"attr" : "val"})) 0601 0602 # ===== AttributesImpl 0603 0604 def verify_empty_nsattrs(attrs): 0605 try: 0606 attrs.getValue((ns_uri, "attr")) 0607 gvk = 0 0608 except KeyError: 0609 gvk = 1 0610 0611 try: 0612 attrs.getValueByQName("ns:attr") 0613 gvqk = 0 0614 except KeyError: 0615 gvqk = 1 0616 0617 try: 0618 attrs.getNameByQName("ns:attr") 0619 gnqk = 0 0620 except KeyError: 0621 gnqk = 1 0622 0623 try: 0624 attrs.getQNameByName((ns_uri, "attr")) 0625 gqnk = 0 0626 except KeyError: 0627 gqnk = 1 0628 0629 try: 0630 attrs[(ns_uri, "attr")] 0631 gik = 0 0632 except KeyError: 0633 gik = 1 0634 0635 return attrs.getLength() == 0 and \ 0636 attrs.getNames() == [] and \ 0637 attrs.getQNames() == [] and \ 0638 len(attrs) == 0 and \ 0639 not attrs.has_key((ns_uri, "attr")) and \ 0640 attrs.keys() == [] and \ 0641 attrs.get((ns_uri, "attr")) is None and \ 0642 attrs.get((ns_uri, "attr"), 25) == 25 and \ 0643 attrs.items() == [] and \ 0644 attrs.values() == [] and \ 0645 gvk and gvqk and gnqk and gik and gqnk 0646 0647 def test_nsattrs_empty(): 0648 return verify_empty_nsattrs(AttributesNSImpl({}, {})) 0649 0650 def test_nsattrs_wattr(): 0651 attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, 0652 {(ns_uri, "attr") : "ns:attr"}) 0653 0654 return attrs.getLength() == 1 and \ 0655 attrs.getNames() == [(ns_uri, "attr")] and \ 0656 attrs.getQNames() == ["ns:attr"] and \ 0657 len(attrs) == 1 and \ 0658 attrs.has_key((ns_uri, "attr")) and \ 0659 attrs.keys() == [(ns_uri, "attr")] and \ 0660 attrs.get((ns_uri, "attr")) == "val" and \ 0661 attrs.get((ns_uri, "attr"), 25) == "val" and \ 0662 attrs.items() == [((ns_uri, "attr"), "val")] and \ 0663 attrs.values() == ["val"] and \ 0664 attrs.getValue((ns_uri, "attr")) == "val" and \ 0665 attrs.getValueByQName("ns:attr") == "val" and \ 0666 attrs.getNameByQName("ns:attr") == (ns_uri, "attr") and \ 0667 attrs[(ns_uri, "attr")] == "val" and \ 0668 attrs.getQNameByName((ns_uri, "attr")) == "ns:attr" 0669 0670 0671 # ===== Main program 0672 0673 def make_test_output(): 0674 parser = create_parser() 0675 result = StringIO() 0676 xmlgen = XMLGenerator(result) 0677 0678 parser.setContentHandler(xmlgen) 0679 parser.parse(findfile("test"+os.extsep+"xml")) 0680 0681 outf = open(findfile("test"+os.extsep+"xml"+os.extsep+"out"), "w") 0682 outf.write(result.getvalue()) 0683 outf.close() 0684 0685 items = locals().items() 0686 items.sort() 0687 for (name, value) in items: 0688 if name[ : 5] == "test_": 0689 confirm(value(), name) 0690 # We delete the items variable so that the assignment to items above 0691 # doesn't pick up the old value of items (which messes with attempts 0692 # to find reference leaks). 0693 del items 0694 0695 if verbose: 0696 print "%d tests, %d failures" % (tests, len(failures)) 0697 if failures: 0698 raise TestFailed("%d of %d tests failed: %s" 0699 % (len(failures), tests, ", ".join(failures))) 0700
Generated by PyXR 0.9.4