Package lxml :: Package tests :: Module test_xmlschema

[hide private]
[frames] | no frames]

# Source Code for Module lxml.tests.test_xmlschema

      1  # -*- coding: utf-8 -*-
      2
      3  """
      4  Test cases related to XML Schema parsing and validation
      5  """
      6
      7  import unittest, sys, os.path
      8
      9  this_dir = os.path.dirname(__file__)
     10  if this_dir not in sys.path:
     11      sys.path.insert(0, this_dir) # needed for Py3
     12
     13  from common_imports import etree, BytesIO, HelperTestCase, fileInTestDir
     14  from common_imports import doctest, make_doctest
     15
     16
     17 -class ETreeXMLSchemaTestCase(HelperTestCase):
     18 -    def test_xmlschema(self):
     19          tree_valid = self.parse('<a><b></b></a>')
     20          tree_invalid = self.parse('<a><c></c></a>')
     21          schema = self.parse('''
     22  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     23    <xsd:element name="a" type="AType"/>
     24    <xsd:complexType name="AType">
     25      <xsd:sequence>
     26        <xsd:element name="b" type="xsd:string" />
     27      </xsd:sequence>
     28    </xsd:complexType>
     29  </xsd:schema>
     30  ''')
     31          schema = etree.XMLSchema(schema)
     32          self.assertTrue(schema.validate(tree_valid))
     33          self.assertFalse(schema.validate(tree_invalid))
     34          self.assertTrue(schema.validate(tree_valid))     # retry valid
     35          self.assertFalse(schema.validate(tree_invalid))  # retry invalid
     36
     37 -    def test_xmlschema_error_log(self):
     38          tree_valid = self.parse('<a><b></b></a>')
     39          tree_invalid = self.parse('<a><c></c></a>')
     40          schema = self.parse('''
     41  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     42    <xsd:element name="a" type="AType"/>
     43    <xsd:complexType name="AType">
     44      <xsd:sequence>
     45        <xsd:element name="b" type="xsd:string" />
     46      </xsd:sequence>
     47    </xsd:complexType>
     48  </xsd:schema>
     49  ''')
     50          schema = etree.XMLSchema(schema)
     51          self.assertTrue(schema.validate(tree_valid))
     52          self.assertFalse(schema.error_log.filter_from_errors())
     53
     54          self.assertFalse(schema.validate(tree_invalid))
     55          self.assertTrue(schema.error_log.filter_from_errors())
     56          self.assertTrue(schema.error_log.filter_types(
     57              etree.ErrorTypes.SCHEMAV_ELEMENT_CONTENT))
     58
     59          self.assertTrue(schema.validate(tree_valid))
     60          self.assertFalse(schema.error_log.filter_from_errors())
     61
     62          self.assertFalse(schema.validate(tree_invalid))
     63          self.assertTrue(schema.error_log.filter_from_errors())
     64          self.assertTrue(schema.error_log.filter_types(
     65              etree.ErrorTypes.SCHEMAV_ELEMENT_CONTENT))
     66
     67 -    def test_xmlschema_error_log_path(self):
     68          """We don't have a guarantee that there will always be a path
     69          for a _LogEntry object (or even a node for which to determina
     70          a path), but at least when this test was created schema validation
     71          errors always got a node and an XPath value. If that ever changes,
     72          we can modify this test to something like:
     73              self.assertTrue(error_path is None or tree_path == error_path)
     74          That way, we can at least verify that if we did get a path value
     75          it wasn't bogus.
     76          """
     77          tree = self.parse('<a><b>42</b><b>dada</b></a>')
     78          schema = self.parse('''
     79  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     80    <xsd:element name="a" type="AType"/>
     81    <xsd:complexType name="AType">
     82      <xsd:sequence>
     83        <xsd:element name="b" type="xsd:integer" maxOccurs="2"/>
     84      </xsd:sequence>
     85    </xsd:complexType>
     86  </xsd:schema>
     87  ''')
     88          schema = etree.XMLSchema(schema)
     89          schema.validate(tree)
     90          tree_path = tree.getpath(tree.findall('b')[1])
     91          error_path = schema.error_log[0].path
     92          self.assertTrue(tree_path == error_path)
     93
     94 -    def test_xmlschema_default_attributes(self):
     95          schema = self.parse('''
     96  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     97    <xsd:element name="a" type="AType"/>
     98    <xsd:complexType name="AType">
     99      <xsd:sequence minOccurs="4" maxOccurs="4">
    100        <xsd:element name="b" type="BType" />
    101      </xsd:sequence>
    102    </xsd:complexType>
    103    <xsd:complexType name="BType">
    104      <xsd:attribute name="hardy" type="xsd:string" default="hey" />
    105    </xsd:complexType>
    106  </xsd:schema>
    107  ''')
    108          schema = etree.XMLSchema(schema, attribute_defaults=True)
    109
    110          tree = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>')
    111
    112          root = tree.getroot()
    113          self.assertEqual('ho', root[0].get('hardy'))
    114          self.assertEqual(None, root[1].get('hardy'))
    115          self.assertEqual('ho', root[2].get('hardy'))
    116          self.assertEqual(None, root[3].get('hardy'))
    117
    118          self.assertTrue(schema(tree))
    119
    120          root = tree.getroot()
    121          self.assertEqual('ho', root[0].get('hardy'))
    122          self.assertEqual('hey', root[1].get('hardy'))
    123          self.assertEqual('ho', root[2].get('hardy'))
    124          self.assertEqual('hey', root[3].get('hardy'))
    125
    126 -    def test_xmlschema_parse(self):
    127          schema = self.parse('''
    128  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    129    <xsd:element name="a" type="AType"/>
    130    <xsd:complexType name="AType">
    131      <xsd:sequence>
    132        <xsd:element name="b" type="xsd:string" />
    133      </xsd:sequence>
    134    </xsd:complexType>
    135  </xsd:schema>
    136  ''')
    137          schema = etree.XMLSchema(schema)
    138          parser = etree.XMLParser(schema=schema)
    139
    140          tree_valid = self.parse('<a><b></b></a>', parser=parser)
    141          self.assertEqual('a', tree_valid.getroot().tag)
    142
    143          self.assertRaises(etree.XMLSyntaxError,
    144                            self.parse, '<a><c></c></a>', parser=parser)
    145
    146 -    def test_xmlschema_parse_default_attributes(self):
    147          # does not work as of libxml2 2.7.3
    148          schema = self.parse('''
    149  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    150    <xsd:element name="a" type="AType"/>
    151    <xsd:complexType name="AType">
    152      <xsd:sequence minOccurs="4" maxOccurs="4">
    153        <xsd:element name="b" type="BType" />
    154      </xsd:sequence>
    155    </xsd:complexType>
    156    <xsd:complexType name="BType">
    157      <xsd:attribute name="hardy" type="xsd:string" default="hey" />
    158    </xsd:complexType>
    159  </xsd:schema>
    160  ''')
    161          schema = etree.XMLSchema(schema)
    162          parser = etree.XMLParser(schema=schema, attribute_defaults=True)
    163
    164          tree_valid = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>',
    165                                  parser=parser)
    166          root = tree_valid.getroot()
    167          self.assertEqual('ho', root[0].get('hardy'))
    168          self.assertEqual('hey', root[1].get('hardy'))
    169          self.assertEqual('ho', root[2].get('hardy'))
    170          self.assertEqual('hey', root[3].get('hardy'))
    171
    172 -    def test_xmlschema_parse_default_attributes_schema_config(self):
    173          # does not work as of libxml2 2.7.3
    174          schema = self.parse('''
    175  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    176    <xsd:element name="a" type="AType"/>
    177    <xsd:complexType name="AType">
    178      <xsd:sequence minOccurs="4" maxOccurs="4">
    179        <xsd:element name="b" type="BType" />
    180      </xsd:sequence>
    181    </xsd:complexType>
    182    <xsd:complexType name="BType">
    183      <xsd:attribute name="hardy" type="xsd:string" default="hey" />
    184    </xsd:complexType>
    185  </xsd:schema>
    186  ''')
    187          schema = etree.XMLSchema(schema, attribute_defaults=True)
    188          parser = etree.XMLParser(schema=schema)
    189
    190          tree_valid = self.parse('<a><b hardy="ho"/><b/><b hardy="ho"/><b/></a>',
    191                                  parser=parser)
    192          root = tree_valid.getroot()
    193          self.assertEqual('ho', root[0].get('hardy'))
    194          self.assertEqual('hey', root[1].get('hardy'))
    195          self.assertEqual('ho', root[2].get('hardy'))
    196          self.assertEqual('hey', root[3].get('hardy'))
    197
    198 -    def test_xmlschema_parse_fixed_attributes(self):
    199          # does not work as of libxml2 2.7.3
    200          schema = self.parse('''
    201  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    202    <xsd:element name="a" type="AType"/>
    203    <xsd:complexType name="AType">
    204      <xsd:sequence minOccurs="3" maxOccurs="3">
    205        <xsd:element name="b" type="BType" />
    206      </xsd:sequence>
    207    </xsd:complexType>
    208    <xsd:complexType name="BType">
    209      <xsd:attribute name="hardy" type="xsd:string" fixed="hey" />
    210    </xsd:complexType>
    211  </xsd:schema>
    212  ''')
    213          schema = etree.XMLSchema(schema)
    214          parser = etree.XMLParser(schema=schema, attribute_defaults=True)
    215
    216          tree_valid = self.parse('<a><b/><b hardy="hey"/><b/></a>',
    217                                  parser=parser)
    218          root = tree_valid.getroot()
    219          self.assertEqual('hey', root[0].get('hardy'))
    220          self.assertEqual('hey', root[1].get('hardy'))
    221          self.assertEqual('hey', root[2].get('hardy'))
    222
    223 -    def test_xmlschema_stringio(self):
    224          schema_file = BytesIO('''
    225  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    226    <xsd:element name="a" type="AType"/>
    227    <xsd:complexType name="AType">
    228      <xsd:sequence>
    229        <xsd:element name="b" type="xsd:string" />
    230      </xsd:sequence>
    231    </xsd:complexType>
    232  </xsd:schema>
    233  ''')
    234          schema = etree.XMLSchema(file=schema_file)
    235          parser = etree.XMLParser(schema=schema)
    236
    237          tree_valid = self.parse('<a><b></b></a>', parser=parser)
    238          self.assertEqual('a', tree_valid.getroot().tag)
    239
    240          self.assertRaises(etree.XMLSyntaxError,
    241                            self.parse, '<a><c></c></a>', parser=parser)
    242
    243 -    def test_xmlschema_iterparse(self):
    244          schema = self.parse('''
    245  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    246    <xsd:element name="a" type="AType"/>
    247    <xsd:complexType name="AType">
    248      <xsd:sequence>
    249        <xsd:element name="b" type="xsd:string" />
    250      </xsd:sequence>
    251    </xsd:complexType>
    252  </xsd:schema>
    253  ''')
    254          schema = etree.XMLSchema(schema)
    255          xml = BytesIO('<a><b></b></a>')
    256          events = [ (event, el.tag)
    257                     for (event, el) in etree.iterparse(xml, schema=schema) ]
    258
    259          self.assertEqual([('end', 'b'), ('end', 'a')],
    260                            events)
    261
    262 -    def test_xmlschema_iterparse_incomplete(self):
    263          schema = self.parse('''
    264  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    265    <xsd:element name="a" type="AType"/>
    266    <xsd:complexType name="AType">
    267      <xsd:sequence>
    268        <xsd:element name="b" type="xsd:string" />
    269      </xsd:sequence>
    270    </xsd:complexType>
    271  </xsd:schema>
    272  ''')
    273          schema = etree.XMLSchema(schema)
    274          xml = BytesIO('<a><b></b></a>')
    275          event, element = next(iter(etree.iterparse(xml, schema=schema)))
    276          self.assertEqual('end', event)
    277          self.assertEqual('b', element.tag)
    278
    279 -    def test_xmlschema_iterparse_fail(self):
    280          schema = self.parse('''
    281  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    282    <xsd:element name="a" type="AType"/>
    283    <xsd:complexType name="AType">
    284      <xsd:sequence>
    285        <xsd:element name="b" type="xsd:string" />
    286      </xsd:sequence>
    287    </xsd:complexType>
    288  </xsd:schema>
    289  ''')
    290          schema = etree.XMLSchema(schema)
    291          self.assertRaises(
    292              etree.XMLSyntaxError,
    293              list, etree.iterparse(BytesIO('<a><c></c></a>'), schema=schema))
    294
    295 -    def test_xmlschema_elementtree_error(self):
    296          self.assertRaises(ValueError, etree.XMLSchema, etree.ElementTree())
    297
    298 -    def test_xmlschema_comment_error(self):
    299          self.assertRaises(ValueError, etree.XMLSchema, etree.Comment('TEST'))
    300
    301 -    def test_xmlschema_illegal_validation_error(self):
    302          schema = self.parse('''
    303  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    304    <xsd:element name="a" type="xsd:string"/>
    305  </xsd:schema>
    306  ''')
    307          schema = etree.XMLSchema(schema)
    308
    309          root = etree.Element('a')
    310          root.text = 'TEST'
    311          self.assertTrue(schema(root))
    312
    313          self.assertRaises(ValueError, schema, etree.Comment('TEST'))
    314          self.assertRaises(ValueError, schema, etree.PI('a', 'text'))
    315          self.assertRaises(ValueError, schema, etree.Entity('text'))
    316
    317 -    def test_xmlschema_invalid_schema1(self):
    318          schema = self.parse('''\
    319  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    320    <element name="a" type="AType"/>
    321    <xsd:complexType name="AType">
    322      <xsd:sequence>
    323        <xsd:element name="b" type="xsd:string" />
    324      </xsd:sequence>
    325    </xsd:complexType>
    326  </xsd:schema>
    327  ''')
    328          self.assertRaises(etree.XMLSchemaParseError,
    329                            etree.XMLSchema, schema)
    330
    331 -    def test_xmlschema_invalid_schema2(self):
    332          schema = self.parse('<test/>')
    333          self.assertRaises(etree.XMLSchemaParseError,
    334                            etree.XMLSchema, schema)
    335
    336 -    def test_xmlschema_file(self):
    337          # this will only work if we access the file through path or
    338          # file object..
    339          f = open(fileInTestDir('test.xsd'), 'rb')
    340          try:
    341              schema = etree.XMLSchema(file=f)
    342          finally:
    343              f.close()
    344          tree_valid = self.parse('<a><b></b></a>')
    345          self.assertTrue(schema.validate(tree_valid))
    346
    347 -    def test_xmlschema_import_file(self):
    348          # this will only work if we access the file through path or
    349          # file object..
    350          schema = etree.XMLSchema(file=fileInTestDir('test_import.xsd'))
    351          tree_valid = self.parse(
    352              '<a:x xmlns:a="http://codespeak.net/lxml/schema/ns1"><b></b></a:x>')
    353          self.assertTrue(schema.validate(tree_valid))
    354
    355 -    def test_xmlschema_shortcut(self):
    356          tree_valid = self.parse('<a><b></b></a>')
    357          tree_invalid = self.parse('<a><c></c></a>')
    358          schema = self.parse('''\
    359  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    360    <xsd:element name="a" type="AType"/>
    361    <xsd:complexType name="AType">
    362      <xsd:sequence>
    363        <xsd:element name="b" type="xsd:string" />
    364      </xsd:sequence>
    365    </xsd:complexType>
    366  </xsd:schema>
    367  ''')
    368          self.assertTrue(tree_valid.xmlschema(schema))
    369          self.assertFalse(tree_invalid.xmlschema(schema))
    370
    371 -    def test_create_from_partial_doc(self):
    372          # this used to crash because the schema part was not properly copied out
    373          wsdl = self.parse('''\
    374  <wsdl:definitions
    375     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    376     xmlns:xs="http://www.w3.org/2001/XMLSchema">
    377   <wsdl:types>
    378    <xs:schema>
    379    </xs:schema>
    380   </wsdl:types>
    381  </wsdl:definitions>
    382          ''')
    383          schema_element = wsdl.find(
    384              "{http://schemas.xmlsoap.org/wsdl/}types/"
    385              "{http://www.w3.org/2001/XMLSchema}schema"
    386          )
    387          etree.XMLSchema(schema_element)
    388          etree.XMLSchema(schema_element)
    389          etree.XMLSchema(schema_element)
    390
    391
    392 -class ETreeXMLSchemaResolversTestCase(HelperTestCase):
    393      resolver_schema_int = BytesIO("""\
    394  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    395      xmlns:etype="http://codespeak.net/lxml/test/external"
    396      targetNamespace="http://codespeak.net/lxml/test/internal">
    397          <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="XXX.xsd" />
    398          <xsd:element name="a" type="etype:AType"/>
    399  </xsd:schema>""")
    400
    401      resolver_schema_int2 = BytesIO("""\
    402  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    403      xmlns:etype="http://codespeak.net/lxml/test/external"
    404      targetNamespace="http://codespeak.net/lxml/test/internal">
    405          <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="YYY.xsd" />
    406          <xsd:element name="a" type="etype:AType"/>
    407  </xsd:schema>""")
    408
    409      resolver_schema_ext = """\
    410  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    411      targetNamespace="http://codespeak.net/lxml/test/external">
    412      <xsd:complexType name="AType">
    413        <xsd:sequence><xsd:element name="b" type="xsd:string" minOccurs="0" maxOccurs="unbounded" /></xsd:sequence>
    414      </xsd:complexType>
    415  </xsd:schema>"""
    416
    417 -    class simple_resolver(etree.Resolver):
    418 -        def __init__(self, schema):
    419              self.schema = schema
    420
    421 -        def resolve(self, url, id, context):
    422              assert url == 'XXX.xsd'
    423              return self.resolve_string(self.schema, context)
    424
    425      # tests:
    426
    427 -    def test_xmlschema_resolvers(self):
    428          # test that resolvers work with schema.
    429          parser = etree.XMLParser()
    430          parser.resolvers.add(self.simple_resolver(self.resolver_schema_ext))
    431          schema_doc = etree.parse(self.resolver_schema_int, parser = parser)
    432          schema = etree.XMLSchema(schema_doc)
    433
    434 -    def test_xmlschema_resolvers_root(self):
    435          # test that the default resolver will get called if there's no
    436          # specific parser resolver.
    437          root_resolver = self.simple_resolver(self.resolver_schema_ext)
    438          etree.get_default_parser().resolvers.add(root_resolver)
    439          schema_doc = etree.parse(self.resolver_schema_int)
    440          schema = etree.XMLSchema(schema_doc)
    441          etree.get_default_parser().resolvers.remove(root_resolver)
    442
    443 -    def test_xmlschema_resolvers_noroot(self):
    444          # test that the default resolver will not get called when a
    445          # more specific resolver is registered.
    446
    447          class res_root(etree.Resolver):
    448              def resolve(self, url, id, context):
    449                  assert False
    450                  return None
    451
    452          root_resolver = res_root()
    453          etree.get_default_parser().resolvers.add(root_resolver)
    454
    455          parser = etree.XMLParser()
    456          parser.resolvers.add(self.simple_resolver(self.resolver_schema_ext))
    457
    458          schema_doc = etree.parse(self.resolver_schema_int, parser = parser)
    459          schema = etree.XMLSchema(schema_doc)
    460          etree.get_default_parser().resolvers.remove(root_resolver)
    461
    462 -    def test_xmlschema_nested_resolvers(self):
    463          # test that resolvers work in a nested fashion.
    464
    465          resolver_schema = self.resolver_schema_ext
    466
    467          class res_nested(etree.Resolver):
    468              def __init__(self, ext_schema):
    469                  self.ext_schema = ext_schema
    470
    471              def resolve(self, url, id, context):
    472                  assert url == 'YYY.xsd'
    473                  return self.resolve_string(self.ext_schema, context)
    474
    475          class res(etree.Resolver):
    476              def __init__(self, ext_schema_1, ext_schema_2):
    477                  self.ext_schema_1 = ext_schema_1
    478                  self.ext_schema_2 = ext_schema_2
    479
    480              def resolve(self, url, id, context):
    481                  assert url == 'XXX.xsd'
    482
    483                  new_parser = etree.XMLParser()
    484                  new_parser.resolvers.add(res_nested(self.ext_schema_2))
    485                  new_schema_doc = etree.parse(self.ext_schema_1, parser = new_parser)
    486                  new_schema = etree.XMLSchema(new_schema_doc)
    487
    488                  return self.resolve_string(resolver_schema, context)
    489
    490          parser = etree.XMLParser()
    491          parser.resolvers.add(res(self.resolver_schema_int2, self.resolver_schema_ext))
    492          schema_doc = etree.parse(self.resolver_schema_int, parser = parser)
    493          schema = etree.XMLSchema(schema_doc)
    494
    495
    496 -def test_suite():
    497      suite = unittest.TestSuite()
    498      suite.addTests([unittest.makeSuite(ETreeXMLSchemaTestCase)])
    499      suite.addTests([unittest.makeSuite(ETreeXMLSchemaResolversTestCase)])
    500      suite.addTests(
    501          [make_doctest('../../../doc/validation.txt')])
    502      return suite
    503
    504
    505  if __name__ == '__main__':
    506      print('to test use test.py %s' % __file__)
    507