lxml.tests.test_xmlschema

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