Package lxml :: Package tests :: Module test_isoschematron
[hide private]
[frames] | no frames]

Source Code for Module lxml.tests.test_isoschematron

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ 
  4  Test cases related to ISO-Schematron parsing and validation 
  5  """ 
  6   
  7  from __future__ import absolute_import 
  8   
  9  import unittest 
 10  from lxml import isoschematron 
 11   
 12  from .common_imports import etree, HelperTestCase, fileInTestDir, doctest, make_doctest 
 13   
 14   
15 -class ETreeISOSchematronTestCase(HelperTestCase):
16 - def test_schematron(self):
17 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 18 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 19 schema = self.parse('''\ 20 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 21 <pattern id="OpenModel"> 22 <title>Open Model</title> 23 <rule context="AAA"> 24 <assert test="BBB"> BBB element is not present</assert> 25 <assert test="CCC"> CCC element is not present</assert> 26 </rule> 27 </pattern> 28 <pattern id="ClosedModel"> 29 <title>Closed model"</title> 30 <rule context="AAA"> 31 <assert test="BBB"> BBB element is not present</assert> 32 <assert test="CCC"> CCC element is not present</assert> 33 <assert test="count(BBB|CCC) = count (*)">There is an extra element</assert> 34 </rule> 35 </pattern> 36 </schema> 37 ''') 38 39 schema = isoschematron.Schematron(schema) 40 self.assertTrue(schema.validate(tree_valid)) 41 self.assertTrue(not schema.validate(tree_invalid))
42
44 self.assertRaises(ValueError, isoschematron.Schematron, etree.ElementTree())
45 46 # an empty pattern is valid in iso schematron
48 schema = self.parse('''\ 49 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 50 <pattern id="OpenModel"> 51 <title>Open model</title> 52 </pattern> 53 </schema> 54 ''') 55 schema = isoschematron.Schematron(schema) 56 self.assertTrue(schema)
57
59 schema = self.parse('''\ 60 <schema xmlns="http://purl.oclc.org/dsdl/schematron" /> 61 ''') 62 self.assertRaises(etree.SchematronParseError, 63 isoschematron.Schematron, schema)
64
66 schema = self.parse('''\ 67 <schema xmlns="mynamespace" /> 68 ''') 69 self.assertRaises(etree.SchematronParseError, 70 isoschematron.Schematron, schema)
71
73 schema = self.parse('''\ 74 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 75 <sch:pattern id="number_of_entries"> 76 <sch:title>mandatory number_of_entries tests</sch:title> 77 <sch:rule context="number_of_entries"> 78 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 79 </sch:rule> 80 </sch:pattern> 81 </sch:schema> 82 ''') 83 schematron = isoschematron.Schematron(schema) 84 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
85
87 schema = self.parse('''\ 88 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 89 <sch:pattern id="number_of_entries"> 90 <sch:title>mandatory number_of_entries tests</sch:title> 91 <sch:rule context="number_of_entries"> 92 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 93 </sch:rule> 94 </sch:pattern> 95 </sch:schema> 96 ''') 97 schematron = isoschematron.Schematron(schema.getroot()) 98 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
99
101 schematron = isoschematron.Schematron(file=fileInTestDir('test.sch')) 102 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
103
104 - def test_schematron_call(self):
105 schema = self.parse('''\ 106 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 107 <sch:pattern id="number_of_entries"> 108 <sch:title>mandatory number_of_entries tests</sch:title> 109 <sch:rule context="number_of_entries"> 110 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 111 </sch:rule> 112 </sch:pattern> 113 </sch:schema> 114 ''') 115 tree_valid = self.parse('''\ 116 <message> 117 <number_of_entries>0</number_of_entries> 118 <entries> 119 </entries> 120 </message> 121 ''') 122 tree_invalid = self.parse('''\ 123 <message> 124 <number_of_entries>3</number_of_entries> 125 <entries> 126 <entry>Entry 1</entry> 127 <entry>Entry 2</entry> 128 </entries> 129 </message> 130 ''') 131 schematron = isoschematron.Schematron(schema) 132 self.assertTrue(schematron(tree_valid), schematron.error_log) 133 valid = schematron(tree_invalid) 134 self.assertTrue(not valid)
135
136 - def test_schematron_validate(self):
137 schema = self.parse('''\ 138 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 139 <sch:pattern id="number_of_entries"> 140 <sch:title>mandatory number_of_entries tests</sch:title> 141 <sch:rule context="number_of_entries"> 142 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 143 </sch:rule> 144 </sch:pattern> 145 </sch:schema> 146 ''') 147 tree_valid = self.parse('''\ 148 <message> 149 <number_of_entries>0</number_of_entries> 150 <entries> 151 </entries> 152 </message> 153 ''') 154 tree_invalid = self.parse('''\ 155 <message> 156 <number_of_entries>3</number_of_entries> 157 <entries> 158 <entry>Entry 1</entry> 159 <entry>Entry 2</entry> 160 </entries> 161 </message> 162 ''') 163 schematron = isoschematron.Schematron(schema) 164 self.assertTrue(schematron.validate(tree_valid), schematron.error_log) 165 valid = schematron.validate(tree_invalid) 166 self.assertTrue(not valid)
167
169 schema = self.parse('''\ 170 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 171 <sch:pattern id="number_of_entries"> 172 <sch:title>mandatory number_of_entries tests</sch:title> 173 <sch:rule context="number_of_entries"> 174 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 175 </sch:rule> 176 </sch:pattern> 177 </sch:schema> 178 ''') 179 tree_valid = self.parse('''\ 180 <message> 181 <number_of_entries>0</number_of_entries> 182 <entries> 183 </entries> 184 </message> 185 ''') 186 tree_invalid = self.parse('''\ 187 <message> 188 <number_of_entries>3</number_of_entries> 189 <entries> 190 <entry>Entry 1</entry> 191 <entry>Entry 2</entry> 192 </entries> 193 </message> 194 ''') 195 schematron = isoschematron.Schematron(schema) 196 self.assertTrue(schematron(tree_valid), schematron.error_log) 197 self.assertRaises(etree.DocumentInvalid, schematron.assertValid, 198 tree_invalid)
199
201 schema = self.parse('''\ 202 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 203 <sch:pattern id="number_of_entries"> 204 <sch:title>mandatory number_of_entries tests</sch:title> 205 <sch:rule context="number_of_entries"> 206 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 207 </sch:rule> 208 </sch:pattern> 209 </sch:schema> 210 ''') 211 tree_valid = self.parse('''\ 212 <message> 213 <number_of_entries>0</number_of_entries> 214 <entries> 215 </entries> 216 </message> 217 ''') 218 tree_invalid = self.parse('''\ 219 <message> 220 <number_of_entries>3</number_of_entries> 221 <entries> 222 <entry>Entry 1</entry> 223 <entry>Entry 2</entry> 224 </entries> 225 </message> 226 ''') 227 schematron = isoschematron.Schematron(schema) 228 self.assertTrue(schematron(tree_valid), schematron.error_log) 229 valid = schematron(tree_invalid) 230 self.assertTrue(not valid) 231 self.assertEqual(len(schematron.error_log), 1, 232 'expected single error: %s (%s errors)' % 233 (schematron.error_log, len(schematron.error_log)))
234
236 schema = self.parse('''\ 237 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 238 <sch:pattern id="number_of_entries"> 239 <sch:title>mandatory number_of_entries tests</sch:title> 240 <sch:rule context="number_of_entries"> 241 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 242 </sch:rule> 243 </sch:pattern> 244 </sch:schema> 245 ''') 246 tree_valid = self.parse('''\ 247 <message> 248 <number_of_entries>0</number_of_entries> 249 <entries> 250 </entries> 251 </message> 252 ''') 253 tree_invalid = self.parse('''\ 254 <message> 255 <number_of_entries>3</number_of_entries> 256 <entries> 257 <entry>Entry 1</entry> 258 <entry>Entry 2</entry> 259 </entries> 260 </message> 261 ''') 262 schematron = isoschematron.Schematron(schema, store_report=True) 263 self.assertTrue(schematron(tree_valid), schematron.error_log) 264 valid = schematron(tree_invalid) 265 self.assertTrue(not valid) 266 self.assertTrue( 267 isinstance(schematron.validation_report, etree._ElementTree), 268 'expected a validation report result tree, got: %s' % schematron.validation_report) 269 270 schematron = isoschematron.Schematron(schema, store_report=False) 271 self.assertTrue(schematron(tree_valid), schematron.error_log) 272 valid = schematron(tree_invalid) 273 self.assertTrue(not valid) 274 self.assertTrue(schematron.validation_report is None, 275 'validation reporting switched off, still: %s' % schematron.validation_report)
276
278 schema = self.parse('''\ 279 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 280 <sch:pattern id="number_of_entries"> 281 <sch:title>mandatory number_of_entries tests</sch:title> 282 <sch:rule context="number_of_entries"> 283 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 284 </sch:rule> 285 </sch:pattern> 286 </sch:schema> 287 ''') 288 schematron = isoschematron.Schematron(schema) 289 self.assertTrue(schematron.validator_xslt is None) 290 291 schematron = isoschematron.Schematron(schema, store_schematron=True) 292 self.assertTrue(isinstance(schematron.schematron, etree._ElementTree), 293 'expected schematron schema to be stored')
294
296 schema = self.parse('''\ 297 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 298 <sch:pattern id="number_of_entries"> 299 <sch:title>mandatory number_of_entries tests</sch:title> 300 <sch:rule context="number_of_entries"> 301 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 302 </sch:rule> 303 </sch:pattern> 304 </sch:schema> 305 ''') 306 schematron = isoschematron.Schematron(schema) 307 self.assertTrue(schematron.validator_xslt is None) 308 309 schematron = isoschematron.Schematron(schema, store_xslt=True) 310 self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree), 311 'expected validator xslt to be stored')
312
313 - def test_schematron_abstract(self):
314 schema = self.parse('''\ 315 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 316 <sch:title>iso schematron validation</sch:title> 317 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 318 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 319 320 <!-- of course, these only really make sense when combined with a schema that 321 ensures datatype xs:dateTime --> 322 323 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 324 <sch:rule context="$datetime"> 325 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 326 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 327 <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 328 </sch:rule> 329 </sch:pattern> 330 331 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 332 <sch:rule context="$datetime"> 333 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 334 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 335 <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 336 </sch:rule> 337 </sch:pattern> 338 339 <sch:pattern is-a="abstract.dateTime.tz_utc" id="datetime" > 340 <sch:param name="datetime" value="datetime"/> 341 </sch:pattern> 342 343 <sch:pattern is-a="abstract.dateTime.tz_utc_nillable" id="nillableDatetime"> 344 <sch:param name="datetime" value="nillableDatetime"/> 345 </sch:pattern> 346 347 </sch:schema> 348 ''') 349 valid_trees = [ 350 self.parse('''\ 351 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 352 <datetime>2009-12-10T15:21:00Z</datetime> 353 <nillableDatetime xsi:nil="true"/> 354 </root> 355 '''), 356 self.parse('''\ 357 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 358 <datetime>2009-12-10T15:21:00Z</datetime> 359 <nillableDatetime>2009-12-10T15:21:00Z</nillableDatetime> 360 </root> 361 '''), 362 self.parse('''\ 363 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 364 <datetime>2009-12-10T15:21:00+00:00</datetime> 365 <nillableDatetime>2009-12-10T15:21:00-00:00</nillableDatetime> 366 </root> 367 '''), 368 ] 369 370 schematron = isoschematron.Schematron(schema) 371 for tree_valid in valid_trees: 372 self.assertTrue(schematron(tree_valid), schematron.error_log) 373 374 tree_invalid = self.parse('''\ 375 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 376 <datetime>2009-12-10T16:21:00+01:00</datetime> 377 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 378 </root> 379 ''') 380 expected = 2 381 valid = schematron(tree_invalid) 382 self.assertTrue(not valid) 383 self.assertEqual( 384 len(schematron.error_log), expected, 385 'expected %s errors: %s (%s errors)' % 386 (expected, schematron.error_log, len(schematron.error_log))) 387 388 tree_invalid = self.parse('''\ 389 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 390 <datetime xsi:nil="true"/> 391 <nillableDatetime>2009-12-10T16:21:00Z</nillableDatetime> 392 </root> 393 ''') 394 expected = 1 395 valid = schematron(tree_invalid) 396 self.assertTrue(not valid) 397 self.assertEqual( 398 len(schematron.error_log), expected, 399 'expected %s errors: %s (%s errors)' % 400 (expected, schematron.error_log, len(schematron.error_log)))
401
402 - def test_schematron_phases(self):
403 schema = self.parse('''\ 404 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 405 <sch:title>iso schematron validation</sch:title> 406 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 407 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 408 409 <sch:phase id="mandatory"> 410 <sch:active pattern="number_of_entries"/> 411 </sch:phase> 412 413 <sch:phase id="datetime_checks"> 414 <sch:active pattern="datetime"/> 415 <sch:active pattern="nillableDatetime"/> 416 </sch:phase> 417 418 <sch:phase id="full"> 419 <sch:active pattern="number_of_entries"/> 420 <sch:active pattern="datetime"/> 421 <sch:active pattern="nillableDatetime"/> 422 </sch:phase> 423 424 <!-- of course, these only really make sense when combined with a schema that 425 ensures datatype xs:dateTime --> 426 427 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 428 <sch:rule context="$datetime"> 429 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 430 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 431 <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 432 </sch:rule> 433 </sch:pattern> 434 435 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 436 <sch:rule context="$datetime"> 437 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 438 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 439 <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 440 </sch:rule> 441 </sch:pattern> 442 443 <sch:pattern id="number_of_entries"> 444 <sch:title>mandatory number_of_entries test</sch:title> 445 <sch:rule context="number_of_entries"> 446 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 447 </sch:rule> 448 </sch:pattern> 449 450 <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> 451 <sch:param name="datetime" value="datetime"/> 452 </sch:pattern> 453 454 <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 455 <sch:param name="datetime" value="nillableDatetime"/> 456 </sch:pattern> 457 458 </sch:schema> 459 ''') 460 tree_valid = self.parse('''\ 461 <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 462 <datetime>2009-12-10T15:21:00Z</datetime> 463 <nillableDatetime xsi:nil="true"/> 464 <number_of_entries>0</number_of_entries> 465 <entries> 466 </entries> 467 </message> 468 ''') 469 tree_invalid = self.parse('''\ 470 <message> 471 <datetime>2009-12-10T16:21:00+01:00</datetime> 472 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 473 <number_of_entries>3</number_of_entries> 474 <entries> 475 <entry>Entry 1</entry> 476 <entry>Entry 2</entry> 477 </entries> 478 </message> 479 ''') 480 # check everything (default phase #ALL) 481 schematron = isoschematron.Schematron(schema) 482 self.assertTrue(schematron(tree_valid), schematron.error_log) 483 expected = 3 484 valid = schematron(tree_invalid) 485 self.assertTrue(not valid) 486 self.assertEqual( 487 len(schematron.error_log), expected, 488 'expected %s errors: %s (%s errors)' % 489 (expected, schematron.error_log, len(schematron.error_log))) 490 491 # check phase mandatory 492 schematron = isoschematron.Schematron( 493 schema, compile_params={'phase': 'mandatory'}) 494 self.assertTrue(schematron(tree_valid), schematron.error_log) 495 expected = 1 496 valid = schematron(tree_invalid) 497 self.assertTrue(not valid) 498 self.assertEqual( 499 len(schematron.error_log), expected, 500 'expected %s errors: %s (%s errors)' % 501 (expected, schematron.error_log, len(schematron.error_log))) 502 503 # check phase datetime_checks 504 schematron = isoschematron.Schematron( 505 schema, compile_params={'phase': 'datetime_checks'}) 506 self.assertTrue(schematron(tree_valid), schematron.error_log) 507 expected = 2 508 valid = schematron(tree_invalid) 509 self.assertTrue(not valid) 510 self.assertEqual( 511 len(schematron.error_log), expected, 512 'expected %s errors: %s (%s errors)' % 513 (expected, schematron.error_log, len(schematron.error_log))) 514 515 # check phase full 516 schematron = isoschematron.Schematron( 517 schema, compile_params={'phase': 'full'}) 518 self.assertTrue(schematron(tree_valid), schematron.error_log) 519 expected = 3 520 valid = schematron(tree_invalid) 521 self.assertTrue(not valid) 522 self.assertEqual( 523 len(schematron.error_log), expected, 524 'expected %s errors: %s (%s errors)' % 525 (expected, schematron.error_log, len(schematron.error_log)))
526
528 schema = self.parse('''\ 529 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 530 <sch:title>iso schematron validation</sch:title> 531 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 532 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 533 534 <sch:phase id="mandatory"> 535 <sch:active pattern="number_of_entries"/> 536 </sch:phase> 537 538 <sch:phase id="datetime_checks"> 539 <sch:active pattern="datetime"/> 540 <sch:active pattern="nillableDatetime"/> 541 </sch:phase> 542 543 <sch:phase id="full"> 544 <sch:active pattern="number_of_entries"/> 545 <sch:active pattern="datetime"/> 546 <sch:active pattern="nillableDatetime"/> 547 </sch:phase> 548 549 <!-- of course, these only really make sense when combined with a schema that 550 ensures datatype xs:dateTime --> 551 552 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 553 <sch:rule context="$datetime"> 554 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 555 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 556 <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 557 </sch:rule> 558 </sch:pattern> 559 560 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 561 <sch:rule context="$datetime"> 562 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 563 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 564 <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 565 </sch:rule> 566 </sch:pattern> 567 568 <sch:pattern id="number_of_entries"> 569 <sch:title>mandatory number_of_entries test</sch:title> 570 <sch:rule context="number_of_entries"> 571 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 572 </sch:rule> 573 </sch:pattern> 574 575 <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> 576 <sch:param name="datetime" value="datetime"/> 577 </sch:pattern> 578 579 <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 580 <sch:param name="datetime" value="nillableDatetime"/> 581 </sch:pattern> 582 583 </sch:schema> 584 ''') 585 tree_valid = self.parse('''\ 586 <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 587 <datetime>2009-12-10T15:21:00Z</datetime> 588 <nillableDatetime xsi:nil="true"/> 589 <number_of_entries>0</number_of_entries> 590 <entries> 591 </entries> 592 </message> 593 ''') 594 tree_invalid = self.parse('''\ 595 <message> 596 <datetime>2009-12-10T16:21:00+01:00</datetime> 597 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 598 <number_of_entries>3</number_of_entries> 599 <entries> 600 <entry>Entry 1</entry> 601 <entry>Entry 2</entry> 602 </entries> 603 </message> 604 ''') 605 # check everything (default phase #ALL) 606 schematron = isoschematron.Schematron(schema) 607 self.assertTrue(schematron(tree_valid), schematron.error_log) 608 expected = 3 609 valid = schematron(tree_invalid) 610 self.assertTrue(not valid) 611 self.assertEqual( 612 len(schematron.error_log), expected, 613 'expected %s errors: %s (%s errors)' % 614 (expected, schematron.error_log, len(schematron.error_log))) 615 616 # check phase mandatory 617 schematron = isoschematron.Schematron(schema, phase='mandatory') 618 self.assertTrue(schematron(tree_valid), schematron.error_log) 619 expected = 1 620 valid = schematron(tree_invalid) 621 self.assertTrue(not valid) 622 self.assertEqual( 623 len(schematron.error_log), expected, 624 'expected %s errors: %s (%s errors)' % 625 (expected, schematron.error_log, len(schematron.error_log))) 626 627 # check phase datetime_checks 628 schematron = isoschematron.Schematron(schema, phase='datetime_checks') 629 self.assertTrue(schematron(tree_valid), schematron.error_log) 630 expected = 2 631 valid = schematron(tree_invalid) 632 self.assertTrue(not valid) 633 self.assertEqual( 634 len(schematron.error_log), expected, 635 'expected %s errors: %s (%s errors)' % 636 (expected, schematron.error_log, len(schematron.error_log))) 637 638 # check phase full 639 schematron = isoschematron.Schematron(schema, phase='full') 640 self.assertTrue(schematron(tree_valid), schematron.error_log) 641 expected = 3 642 valid = schematron(tree_invalid) 643 self.assertTrue(not valid) 644 self.assertEqual( 645 len(schematron.error_log), expected, 'expected %s errors: %s (%s errors)' % 646 (expected, schematron.error_log, len(schematron.error_log)))
647
649 schema = self.parse('''\ 650 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 651 xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 652 <xs:element name="message"> 653 <xs:complexType> 654 <xs:sequence> 655 <xs:element name="number_of_entries" type="xs:positiveInteger"> 656 <xs:annotation> 657 <xs:appinfo> 658 <sch:pattern id="number_of_entries"> 659 <sch:title>mandatory number_of_entries tests</sch:title> 660 <sch:rule context="number_of_entries"> 661 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 662 </sch:rule> 663 </sch:pattern> 664 </xs:appinfo> 665 </xs:annotation> 666 </xs:element> 667 <xs:element name="entries"> 668 <xs:complexType> 669 <xs:sequence> 670 <xs:element name="entry" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> 671 </xs:sequence> 672 </xs:complexType> 673 </xs:element> 674 </xs:sequence> 675 </xs:complexType> 676 </xs:element> 677 </xs:schema> 678 ''') 679 tree_valid = self.parse('''\ 680 <message> 681 <number_of_entries>2</number_of_entries> 682 <entries> 683 <entry>Entry 1</entry> 684 <entry>Entry 2</entry> 685 </entries> 686 </message> 687 ''') 688 tree_invalid = self.parse('''\ 689 <message> 690 <number_of_entries>1</number_of_entries> 691 <entries> 692 <entry>Entry 1</entry> 693 <entry>Entry 2</entry> 694 </entries> 695 </message> 696 ''') 697 xmlschema = etree.XMLSchema(schema) 698 schematron = isoschematron.Schematron(schema) 699 # fwiw, this must also be XMLSchema-valid 700 self.assertTrue(xmlschema(tree_valid), xmlschema.error_log) 701 self.assertTrue(schematron(tree_valid)) 702 # still schema-valid 703 self.assertTrue(xmlschema(tree_invalid), xmlschema.error_log) 704 self.assertTrue(not schematron(tree_invalid))
705
707 schema = self.parse('''\ 708 <grammar xmlns="http://relaxng.org/ns/structure/1.0" 709 xmlns:sch="http://purl.oclc.org/dsdl/schematron" 710 datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> 711 <start> 712 <ref name="message"/> 713 </start> 714 <define name="message"> 715 <element name="message"> 716 <element name="number_of_entries"> 717 <!-- RelaxNG can be mixed freely with stuff from other namespaces --> 718 <sch:pattern id="number_of_entries"> 719 <sch:title>mandatory number_of_entries tests</sch:title> 720 <sch:rule context="number_of_entries"> 721 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 722 </sch:rule> 723 </sch:pattern> 724 <data type="positiveInteger"/> 725 </element> 726 <element name="entries"> 727 <zeroOrMore> 728 <element name="entry"><data type="string"/></element> 729 </zeroOrMore> 730 </element> 731 </element> 732 </define> 733 </grammar> 734 ''') 735 tree_valid = self.parse('''\ 736 <message> 737 <number_of_entries>2</number_of_entries> 738 <entries> 739 <entry>Entry 1</entry> 740 <entry>Entry 2</entry> 741 </entries> 742 </message> 743 ''') 744 tree_invalid = self.parse('''\ 745 <message> 746 <number_of_entries>1</number_of_entries> 747 <entries> 748 <entry>Entry 1</entry> 749 <entry>Entry 2</entry> 750 </entries> 751 </message> 752 ''') 753 relaxng = etree.RelaxNG(schema) 754 schematron = isoschematron.Schematron(schema) 755 # fwiw, this must also be RelaxNG-valid 756 self.assertTrue(relaxng(tree_valid), relaxng.error_log) 757 self.assertTrue(schematron(tree_valid)) 758 # still schema-valid 759 self.assertTrue(relaxng(tree_invalid), relaxng.error_log) 760 self.assertTrue(not schematron(tree_invalid))
761
763 schema = self.parse('''\ 764 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 765 <sch:pattern id="number_of_entries"> 766 <sch:title>mandatory number_of_entries tests</sch:title> 767 <sch:rule context="number_of_entries"> 768 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 769 </sch:rule> 770 </sch:pattern> 771 </sch:schema> 772 ''') 773 # handing phase as keyword arg will *not* raise the type error 774 self.assertRaises(TypeError, isoschematron.Schematron, schema, 775 compile_params={'phase': None})
776
778 class MySchematron(isoschematron.Schematron): 779 def _extract(self, root): 780 schematron = (root.xpath( 781 '//sch:schema', 782 namespaces={'sch': "http://purl.oclc.org/dsdl/schematron"}) 783 or [None])[0] 784 return schematron
785 786 def _include(self, schematron, **kwargs): 787 raise RuntimeError('inclusion unsupported')
788 789 def _expand(self, schematron, **kwargs): 790 raise RuntimeError('expansion unsupported') 791 792 def _validation_errors(self, validationReport): 793 valid = etree.XPath( 794 'count(//svrl:successful-report[@flag="critical"])=1', 795 namespaces={'svrl': isoschematron.SVRL_NS})( 796 validationReport) 797 if valid: 798 return [] 799 error = etree.Element('Error') 800 error.text = 'missing critical condition report' 801 return [error] 802 803 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 804 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 805 schema = self.parse('''\ 806 <schema xmlns="http://www.example.org/yet/another/schema/dialect"> 807 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 808 <pattern id="OpenModel"> 809 <title>Open Model</title> 810 <rule context="AAA"> 811 <report test="BBB" flag="info">BBB element must be present</report> 812 <report test="CCC" flag="info">CCC element must be present</report> 813 </rule> 814 </pattern> 815 <pattern id="ClosedModel"> 816 <title>Closed model"</title> 817 <rule context="AAA"> 818 <report test="BBB" flag="info">BBB element must be present</report> 819 <report test="CCC" flag="info">CCC element must be present</report> 820 <report test="count(BBB|CCC) = count(*)" flag="critical">Only BBB and CCC children must be present</report> 821 </rule> 822 </pattern> 823 </schema> 824 </schema> 825 ''') 826 # check if overridden _include is run 827 self.assertRaises(RuntimeError, MySchematron, schema, store_report=True) 828 # check if overridden _expand is run 829 self.assertRaises(RuntimeError, MySchematron, schema, store_report=True, 830 include=False) 831 832 schema = MySchematron(schema, store_report=True, include=False, 833 expand=False) 834 self.assertTrue(schema.validate(tree_valid)) 835 self.assertTrue(not schema.validate(tree_invalid)) 836 837 #TODO: test xslt parameters for inclusion, expand & compile steps (?) 838
839 - def test_schematron_fail_on_report(self):
840 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 841 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 842 schema = self.parse('''\ 843 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 844 <pattern id="OpenModel"> 845 <title>Simple Report</title> 846 <rule context="AAA"> 847 <report test="DDD"> DDD element must not be present</report> 848 </rule> 849 </pattern> 850 </schema> 851 ''') 852 schema_report = isoschematron.Schematron( 853 schema, error_finder=isoschematron.Schematron.ASSERTS_AND_REPORTS) 854 schema_no_report = isoschematron.Schematron(schema) 855 self.assertTrue(schema_report.validate(tree_valid)) 856 self.assertTrue(not schema_report.validate(tree_invalid)) 857 self.assertTrue(schema_no_report.validate(tree_valid)) 858 self.assertTrue(schema_no_report.validate(tree_invalid))
859 860
861 -def test_suite():
862 suite = unittest.TestSuite() 863 suite.addTests([unittest.makeSuite(ETreeISOSchematronTestCase)]) 864 suite.addTests(doctest.DocTestSuite(isoschematron)) 865 suite.addTests( 866 [make_doctest('../../../doc/validation.txt')]) 867 return suite
868 869 if __name__ == '__main__': 870 print('to test use test.py %s' % __file__) 871