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