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' % 272 (schematron.validation_report)) 273 274 schematron = isoschematron.Schematron(schema, store_report=False) 275 self.assertTrue(schematron(tree_valid), schematron.error_log) 276 valid = schematron(tree_invalid) 277 self.assertTrue(not valid) 278 self.assertTrue(schematron.validation_report is None, 279 'validation reporting switched off, still: %s' % 280 (schematron.validation_report))
281
283 schema = self.parse('''\ 284 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 285 <sch:pattern id="number_of_entries"> 286 <sch:title>mandatory number_of_entries tests</sch:title> 287 <sch:rule context="number_of_entries"> 288 <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> 289 </sch:rule> 290 </sch:pattern> 291 </sch:schema> 292 ''') 293 schematron = isoschematron.Schematron(schema) 294 self.assertTrue(schematron.validator_xslt is None) 295 296 schematron = isoschematron.Schematron(schema, store_schematron=True) 297 self.assertTrue(isinstance(schematron.schematron, etree._ElementTree), 298 'expected schematron schema to be stored')
299
301 schema = self.parse('''\ 302 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 303 <sch:pattern id="number_of_entries"> 304 <sch:title>mandatory number_of_entries tests</sch:title> 305 <sch:rule context="number_of_entries"> 306 <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> 307 </sch:rule> 308 </sch:pattern> 309 </sch:schema> 310 ''') 311 schematron = isoschematron.Schematron(schema) 312 self.assertTrue(schematron.validator_xslt is None) 313 314 schematron = isoschematron.Schematron(schema, store_xslt=True) 315 self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree), 316 'expected validator xslt to be stored')
317
318 - def test_schematron_abstract(self):
319 schema = self.parse('''\ 320 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 321 <sch:title>iso schematron validation</sch:title> 322 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 323 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 324 325 <!-- of course, these only really make sense when combined with a schema that 326 ensures datatype xs:dateTime --> 327 328 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 329 <sch:rule context="$datetime"> 330 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 331 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 332 <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> 333 </sch:rule> 334 </sch:pattern> 335 336 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 337 <sch:rule context="$datetime"> 338 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 339 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 340 <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> 341 </sch:rule> 342 </sch:pattern> 343 344 <sch:pattern is-a="abstract.dateTime.tz_utc" id="datetime" > 345 <sch:param name="datetime" value="datetime"/> 346 </sch:pattern> 347 348 <sch:pattern is-a="abstract.dateTime.tz_utc_nillable" id="nillableDatetime"> 349 <sch:param name="datetime" value="nillableDatetime"/> 350 </sch:pattern> 351 352 </sch:schema> 353 ''') 354 valid_trees = [ 355 self.parse('''\ 356 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 357 <datetime>2009-12-10T15:21:00Z</datetime> 358 <nillableDatetime xsi:nil="true"/> 359 </root> 360 '''), 361 self.parse('''\ 362 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 363 <datetime>2009-12-10T15:21:00Z</datetime> 364 <nillableDatetime>2009-12-10T15:21:00Z</nillableDatetime> 365 </root> 366 '''), 367 self.parse('''\ 368 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 369 <datetime>2009-12-10T15:21:00+00:00</datetime> 370 <nillableDatetime>2009-12-10T15:21:00-00:00</nillableDatetime> 371 </root> 372 '''), 373 ] 374 375 schematron = isoschematron.Schematron(schema) 376 for tree_valid in valid_trees: 377 self.assertTrue(schematron(tree_valid), schematron.error_log) 378 379 tree_invalid = self.parse('''\ 380 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 381 <datetime>2009-12-10T16:21:00+01:00</datetime> 382 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 383 </root> 384 ''') 385 expected = 2 386 valid = schematron(tree_invalid) 387 self.assertTrue(not valid) 388 self.assertEqual( 389 len(schematron.error_log), expected, 390 'expected %s errors: %s (%s errors)' % 391 (expected, schematron.error_log, len(schematron.error_log))) 392 393 tree_invalid = self.parse('''\ 394 <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 395 <datetime xsi:nil="true"/> 396 <nillableDatetime>2009-12-10T16:21:00Z</nillableDatetime> 397 </root> 398 ''') 399 expected = 1 400 valid = schematron(tree_invalid) 401 self.assertTrue(not valid) 402 self.assertEqual( 403 len(schematron.error_log), expected, 404 'expected %s errors: %s (%s errors)' % 405 (expected, schematron.error_log, len(schematron.error_log)))
406
407 - def test_schematron_phases(self):
408 schema = self.parse('''\ 409 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 410 <sch:title>iso schematron validation</sch:title> 411 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 412 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 413 414 <sch:phase id="mandatory"> 415 <sch:active pattern="number_of_entries"/> 416 </sch:phase> 417 418 <sch:phase id="datetime_checks"> 419 <sch:active pattern="datetime"/> 420 <sch:active pattern="nillableDatetime"/> 421 </sch:phase> 422 423 <sch:phase id="full"> 424 <sch:active pattern="number_of_entries"/> 425 <sch:active pattern="datetime"/> 426 <sch:active pattern="nillableDatetime"/> 427 </sch:phase> 428 429 <!-- of course, these only really make sense when combined with a schema that 430 ensures datatype xs:dateTime --> 431 432 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 433 <sch:rule context="$datetime"> 434 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 435 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 436 <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> 437 </sch:rule> 438 </sch:pattern> 439 440 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 441 <sch:rule context="$datetime"> 442 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 443 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 444 <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> 445 </sch:rule> 446 </sch:pattern> 447 448 <sch:pattern id="number_of_entries"> 449 <sch:title>mandatory number_of_entries test</sch:title> 450 <sch:rule context="number_of_entries"> 451 <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> 452 </sch:rule> 453 </sch:pattern> 454 455 <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> 456 <sch:param name="datetime" value="datetime"/> 457 </sch:pattern> 458 459 <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 460 <sch:param name="datetime" value="nillableDatetime"/> 461 </sch:pattern> 462 463 </sch:schema> 464 ''') 465 tree_valid = self.parse('''\ 466 <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 467 <datetime>2009-12-10T15:21:00Z</datetime> 468 <nillableDatetime xsi:nil="true"/> 469 <number_of_entries>0</number_of_entries> 470 <entries> 471 </entries> 472 </message> 473 ''') 474 tree_invalid = self.parse('''\ 475 <message> 476 <datetime>2009-12-10T16:21:00+01:00</datetime> 477 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 478 <number_of_entries>3</number_of_entries> 479 <entries> 480 <entry>Entry 1</entry> 481 <entry>Entry 2</entry> 482 </entries> 483 </message> 484 ''') 485 # check everything (default phase #ALL) 486 schematron = isoschematron.Schematron(schema) 487 self.assertTrue(schematron(tree_valid), schematron.error_log) 488 expected = 3 489 valid = schematron(tree_invalid) 490 self.assertTrue(not valid) 491 self.assertEqual( 492 len(schematron.error_log), expected, 493 'expected %s errors: %s (%s errors)' % 494 (expected, schematron.error_log, len(schematron.error_log))) 495 496 # check phase mandatory 497 schematron = isoschematron.Schematron( 498 schema, compile_params={'phase': 'mandatory'}) 499 self.assertTrue(schematron(tree_valid), schematron.error_log) 500 expected = 1 501 valid = schematron(tree_invalid) 502 self.assertTrue(not valid) 503 self.assertEqual( 504 len(schematron.error_log), expected, 505 'expected %s errors: %s (%s errors)' % 506 (expected, schematron.error_log, len(schematron.error_log))) 507 508 # check phase datetime_checks 509 schematron = isoschematron.Schematron( 510 schema, compile_params={'phase': 'datetime_checks'}) 511 self.assertTrue(schematron(tree_valid), schematron.error_log) 512 expected = 2 513 valid = schematron(tree_invalid) 514 self.assertTrue(not valid) 515 self.assertEqual( 516 len(schematron.error_log), expected, 517 'expected %s errors: %s (%s errors)' % 518 (expected, schematron.error_log, len(schematron.error_log))) 519 520 # check phase full 521 schematron = isoschematron.Schematron( 522 schema, compile_params={'phase': 'full'}) 523 self.assertTrue(schematron(tree_valid), schematron.error_log) 524 expected = 3 525 valid = schematron(tree_invalid) 526 self.assertTrue(not valid) 527 self.assertEqual( 528 len(schematron.error_log), expected, 529 'expected %s errors: %s (%s errors)' % 530 (expected, schematron.error_log, len(schematron.error_log)))
531
533 schema = self.parse('''\ 534 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 535 <sch:title>iso schematron validation</sch:title> 536 <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 537 <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 538 539 <sch:phase id="mandatory"> 540 <sch:active pattern="number_of_entries"/> 541 </sch:phase> 542 543 <sch:phase id="datetime_checks"> 544 <sch:active pattern="datetime"/> 545 <sch:active pattern="nillableDatetime"/> 546 </sch:phase> 547 548 <sch:phase id="full"> 549 <sch:active pattern="number_of_entries"/> 550 <sch:active pattern="datetime"/> 551 <sch:active pattern="nillableDatetime"/> 552 </sch:phase> 553 554 <!-- of course, these only really make sense when combined with a schema that 555 ensures datatype xs:dateTime --> 556 557 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 558 <sch:rule context="$datetime"> 559 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 560 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 561 <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> 562 </sch:rule> 563 </sch:pattern> 564 565 <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 566 <sch:rule context="$datetime"> 567 <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 568 <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 569 <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> 570 </sch:rule> 571 </sch:pattern> 572 573 <sch:pattern id="number_of_entries"> 574 <sch:title>mandatory number_of_entries test</sch:title> 575 <sch:rule context="number_of_entries"> 576 <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> 577 </sch:rule> 578 </sch:pattern> 579 580 <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> 581 <sch:param name="datetime" value="datetime"/> 582 </sch:pattern> 583 584 <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 585 <sch:param name="datetime" value="nillableDatetime"/> 586 </sch:pattern> 587 588 </sch:schema> 589 ''') 590 tree_valid = self.parse('''\ 591 <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 592 <datetime>2009-12-10T15:21:00Z</datetime> 593 <nillableDatetime xsi:nil="true"/> 594 <number_of_entries>0</number_of_entries> 595 <entries> 596 </entries> 597 </message> 598 ''') 599 tree_invalid = self.parse('''\ 600 <message> 601 <datetime>2009-12-10T16:21:00+01:00</datetime> 602 <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 603 <number_of_entries>3</number_of_entries> 604 <entries> 605 <entry>Entry 1</entry> 606 <entry>Entry 2</entry> 607 </entries> 608 </message> 609 ''') 610 # check everything (default phase #ALL) 611 schematron = isoschematron.Schematron(schema) 612 self.assertTrue(schematron(tree_valid), schematron.error_log) 613 expected = 3 614 valid = schematron(tree_invalid) 615 self.assertTrue(not valid) 616 self.assertEqual( 617 len(schematron.error_log), expected, 618 'expected %s errors: %s (%s errors)' % 619 (expected, schematron.error_log, len(schematron.error_log))) 620 621 # check phase mandatory 622 schematron = isoschematron.Schematron(schema, phase='mandatory') 623 self.assertTrue(schematron(tree_valid), schematron.error_log) 624 expected = 1 625 valid = schematron(tree_invalid) 626 self.assertTrue(not valid) 627 self.assertEqual( 628 len(schematron.error_log), expected, 629 'expected %s errors: %s (%s errors)' % 630 (expected, schematron.error_log, len(schematron.error_log))) 631 632 # check phase datetime_checks 633 schematron = isoschematron.Schematron(schema, phase='datetime_checks') 634 self.assertTrue(schematron(tree_valid), schematron.error_log) 635 expected = 2 636 valid = schematron(tree_invalid) 637 self.assertTrue(not valid) 638 self.assertEqual( 639 len(schematron.error_log), expected, 640 'expected %s errors: %s (%s errors)' % 641 (expected, schematron.error_log, len(schematron.error_log))) 642 643 # check phase full 644 schematron = isoschematron.Schematron(schema, phase='full') 645 self.assertTrue(schematron(tree_valid), schematron.error_log) 646 expected = 3 647 valid = schematron(tree_invalid) 648 self.assertTrue(not valid) 649 self.assertEqual( 650 len(schematron.error_log), expected, 'expected %s errors: %s (%s errors)' % 651 (expected, schematron.error_log, len(schematron.error_log)))
652
654 schema = self.parse('''\ 655 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 656 xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 657 <xs:element name="message"> 658 <xs:complexType> 659 <xs:sequence> 660 <xs:element name="number_of_entries" type="xs:positiveInteger"> 661 <xs:annotation> 662 <xs:appinfo> 663 <sch:pattern id="number_of_entries"> 664 <sch:title>mandatory number_of_entries tests</sch:title> 665 <sch:rule context="number_of_entries"> 666 <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> 667 </sch:rule> 668 </sch:pattern> 669 </xs:appinfo> 670 </xs:annotation> 671 </xs:element> 672 <xs:element name="entries"> 673 <xs:complexType> 674 <xs:sequence> 675 <xs:element name="entry" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> 676 </xs:sequence> 677 </xs:complexType> 678 </xs:element> 679 </xs:sequence> 680 </xs:complexType> 681 </xs:element> 682 </xs:schema> 683 ''') 684 tree_valid = self.parse('''\ 685 <message> 686 <number_of_entries>2</number_of_entries> 687 <entries> 688 <entry>Entry 1</entry> 689 <entry>Entry 2</entry> 690 </entries> 691 </message> 692 ''') 693 tree_invalid = self.parse('''\ 694 <message> 695 <number_of_entries>1</number_of_entries> 696 <entries> 697 <entry>Entry 1</entry> 698 <entry>Entry 2</entry> 699 </entries> 700 </message> 701 ''') 702 xmlschema = etree.XMLSchema(schema) 703 schematron = isoschematron.Schematron(schema) 704 # fwiw, this must also be XMLSchema-valid 705 self.assertTrue(xmlschema(tree_valid), xmlschema.error_log) 706 self.assertTrue(schematron(tree_valid)) 707 # still schema-valid 708 self.assertTrue(xmlschema(tree_invalid), xmlschema.error_log) 709 self.assertTrue(not schematron(tree_invalid))
710
712 schema = self.parse('''\ 713 <grammar xmlns="http://relaxng.org/ns/structure/1.0" 714 xmlns:sch="http://purl.oclc.org/dsdl/schematron" 715 datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> 716 <start> 717 <ref name="message"/> 718 </start> 719 <define name="message"> 720 <element name="message"> 721 <element name="number_of_entries"> 722 <!-- RelaxNG can be mixed freely with stuff from other namespaces --> 723 <sch:pattern id="number_of_entries"> 724 <sch:title>mandatory number_of_entries tests</sch:title> 725 <sch:rule context="number_of_entries"> 726 <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> 727 </sch:rule> 728 </sch:pattern> 729 <data type="positiveInteger"/> 730 </element> 731 <element name="entries"> 732 <zeroOrMore> 733 <element name="entry"><data type="string"/></element> 734 </zeroOrMore> 735 </element> 736 </element> 737 </define> 738 </grammar> 739 ''') 740 tree_valid = self.parse('''\ 741 <message> 742 <number_of_entries>2</number_of_entries> 743 <entries> 744 <entry>Entry 1</entry> 745 <entry>Entry 2</entry> 746 </entries> 747 </message> 748 ''') 749 tree_invalid = self.parse('''\ 750 <message> 751 <number_of_entries>1</number_of_entries> 752 <entries> 753 <entry>Entry 1</entry> 754 <entry>Entry 2</entry> 755 </entries> 756 </message> 757 ''') 758 relaxng = etree.RelaxNG(schema) 759 schematron = isoschematron.Schematron(schema) 760 # fwiw, this must also be RelaxNG-valid 761 self.assertTrue(relaxng(tree_valid), relaxng.error_log) 762 self.assertTrue(schematron(tree_valid)) 763 # still schema-valid 764 self.assertTrue(relaxng(tree_invalid), relaxng.error_log) 765 self.assertTrue(not schematron(tree_invalid))
766
768 schema = self.parse('''\ 769 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 770 <sch:pattern id="number_of_entries"> 771 <sch:title>mandatory number_of_entries tests</sch:title> 772 <sch:rule context="number_of_entries"> 773 <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> 774 </sch:rule> 775 </sch:pattern> 776 </sch:schema> 777 ''') 778 # handing phase as keyword arg will *not* raise the type error 779 self.assertRaises(TypeError, isoschematron.Schematron, schema, 780 compile_params={'phase': None})
781
783 class MySchematron(isoschematron.Schematron): 784 def _extract(self, root): 785 schematron = (root.xpath( 786 '//sch:schema', 787 namespaces={'sch': "http://purl.oclc.org/dsdl/schematron"}) 788 or [None])[0] 789 return schematron
790 791 def _include(self, schematron, **kwargs): 792 raise RuntimeError('inclusion unsupported')
793 794 def _expand(self, schematron, **kwargs): 795 raise RuntimeError('expansion unsupported') 796 797 def _validation_errors(self, validationReport): 798 valid = etree.XPath( 799 'count(//svrl:successful-report[@flag="critical"])=1', 800 namespaces={'svrl': isoschematron.SVRL_NS})( 801 validationReport) 802 if valid: 803 return [] 804 error = etree.Element('Error') 805 error.text = 'missing critical condition report' 806 return [error] 807 808 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 809 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 810 schema = self.parse('''\ 811 <schema xmlns="http://www.example.org/yet/another/schema/dialect"> 812 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 813 <pattern id="OpenModel"> 814 <title>Open Model</title> 815 <rule context="AAA"> 816 <report test="BBB" flag="info">BBB element must be present</report> 817 <report test="CCC" flag="info">CCC element must be present</report> 818 </rule> 819 </pattern> 820 <pattern id="ClosedModel"> 821 <title>Closed model"</title> 822 <rule context="AAA"> 823 <report test="BBB" flag="info">BBB element must be present</report> 824 <report test="CCC" flag="info">CCC element must be present</report> 825 <report test="count(BBB|CCC) = count(*)" flag="critical">Only BBB and CCC children must be present</report> 826 </rule> 827 </pattern> 828 </schema> 829 </schema> 830 ''') 831 # check if overridden _include is run 832 self.assertRaises(RuntimeError, MySchematron, schema, store_report=True) 833 # check if overridden _expand is run 834 self.assertRaises(RuntimeError, MySchematron, schema, store_report=True, 835 include=False) 836 837 schema = MySchematron(schema, store_report=True, include=False, 838 expand=False) 839 self.assertTrue(schema.validate(tree_valid)) 840 self.assertTrue(not schema.validate(tree_invalid)) 841 842 #TODO: test xslt parameters for inclusion, expand & compile steps (?) 843
844 - def test_schematron_fail_on_report(self):
845 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 846 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 847 schema = self.parse('''\ 848 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 849 <pattern id="OpenModel"> 850 <title>Simple Report</title> 851 <rule context="AAA"> 852 <report test="DDD"> DDD element must not be present</report> 853 </rule> 854 </pattern> 855 </schema> 856 ''') 857 schema_report = isoschematron.Schematron( 858 schema, error_finder=isoschematron.Schematron.ASSERTS_AND_REPORTS) 859 schema_no_report = isoschematron.Schematron(schema) 860 self.assertTrue(schema_report.validate(tree_valid)) 861 self.assertTrue(not schema_report.validate(tree_invalid)) 862 self.assertTrue(schema_no_report.validate(tree_valid)) 863 self.assertTrue(schema_no_report.validate(tree_invalid))
864 865
866 -def test_suite():
867 suite = unittest.TestSuite() 868 suite.addTests([unittest.makeSuite(ETreeISOSchematronTestCase)]) 869 suite.addTests(doctest.DocTestSuite(isoschematron)) 870 suite.addTests( 871 [make_doctest('../../../doc/validation.txt')]) 872 return suite
873 874 if __name__ == '__main__': 875 print('to test use test.py %s' % __file__) 876