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 -class ETreeISOSchematronTestCase(HelperTestCase):
18 - def test_schematron(self):
19 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 20 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 21 schema = self.parse('''\ 22 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 23 <pattern id="OpenModel"> 24 <title>Open Model</title> 25 <rule context="AAA"> 26 <assert test="BBB"> BBB element is not present</assert> 27 <assert test="CCC"> CCC element is not present</assert> 28 </rule> 29 </pattern> 30 <pattern id="ClosedModel"> 31 <title>Closed model"</title> 32 <rule context="AAA"> 33 <assert test="BBB"> BBB element is not present</assert> 34 <assert test="CCC"> CCC element is not present</assert> 35 <assert test="count(BBB|CCC) = count (*)">There is an extra element</assert> 36 </rule> 37 </pattern> 38 </schema> 39 ''') 40 schema = isoschematron.Schematron(schema) 41 self.assertTrue(schema.validate(tree_valid)) 42 self.assertTrue(not schema.validate(tree_invalid))
43
45 self.assertRaises(ValueError, isoschematron.Schematron, etree.ElementTree())
46 47 # an empty pattern is valid in iso schematron
49 schema = self.parse('''\ 50 <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 51 <pattern id="OpenModel"> 52 <title>Open model</title> 53 </pattern> 54 </schema> 55 ''') 56 schema = isoschematron.Schematron(schema) 57 self.assertTrue(schema)
58
60 schema = self.parse('''\ 61 <schema xmlns="http://purl.oclc.org/dsdl/schematron" /> 62 ''') 63 self.assertRaises(etree.SchematronParseError, 64 isoschematron.Schematron, schema)
65
67 schema = self.parse('''\ 68 <schema xmlns="mynamespace" /> 69 ''') 70 self.assertRaises(etree.SchematronParseError, 71 isoschematron.Schematron, schema)
72
74 schema = self.parse('''\ 75 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 76 <sch:pattern id="number_of_entries"> 77 <sch:title>mandatory number_of_entries tests</sch:title> 78 <sch:rule context="number_of_entries"> 79 <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> 80 </sch:rule> 81 </sch:pattern> 82 </sch:schema> 83 ''') 84 schematron = isoschematron.Schematron(schema) 85 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
86
88 schema = self.parse('''\ 89 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 90 <sch:pattern id="number_of_entries"> 91 <sch:title>mandatory number_of_entries tests</sch:title> 92 <sch:rule context="number_of_entries"> 93 <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> 94 </sch:rule> 95 </sch:pattern> 96 </sch:schema> 97 ''') 98 schematron = isoschematron.Schematron(schema.getroot()) 99 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
100
102 schematron = isoschematron.Schematron(file=fileInTestDir('test.sch')) 103 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
104
105 - def test_schematron_call(self):
106 schema = self.parse('''\ 107 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 108 <sch:pattern id="number_of_entries"> 109 <sch:title>mandatory number_of_entries tests</sch:title> 110 <sch:rule context="number_of_entries"> 111 <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> 112 </sch:rule> 113 </sch:pattern> 114 </sch:schema> 115 ''') 116 tree_valid = self.parse('''\ 117 <message> 118 <number_of_entries>0</number_of_entries> 119 <entries> 120 </entries> 121 </message> 122 ''') 123 tree_invalid = self.parse('''\ 124 <message> 125 <number_of_entries>3</number_of_entries> 126 <entries> 127 <entry>Entry 1</entry> 128 <entry>Entry 2</entry> 129 </entries> 130 </message> 131 ''') 132 schematron = isoschematron.Schematron(schema) 133 self.assertTrue(schematron(tree_valid), schematron.error_log) 134 valid = schematron(tree_invalid) 135 self.assertTrue(not valid)
136
137 - def test_schematron_validate(self):
138 schema = self.parse('''\ 139 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 140 <sch:pattern id="number_of_entries"> 141 <sch:title>mandatory number_of_entries tests</sch:title> 142 <sch:rule context="number_of_entries"> 143 <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> 144 </sch:rule> 145 </sch:pattern> 146 </sch:schema> 147 ''') 148 tree_valid = self.parse('''\ 149 <message> 150 <number_of_entries>0</number_of_entries> 151 <entries> 152 </entries> 153 </message> 154 ''') 155 tree_invalid = self.parse('''\ 156 <message> 157 <number_of_entries>3</number_of_entries> 158 <entries> 159 <entry>Entry 1</entry> 160 <entry>Entry 2</entry> 161 </entries> 162 </message> 163 ''') 164 schematron = isoschematron.Schematron(schema) 165 self.assertTrue(schematron.validate(tree_valid), schematron.error_log) 166 valid = schematron.validate(tree_invalid) 167 self.assertTrue(not valid)
168
170 schema = self.parse('''\ 171 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 172 <sch:pattern id="number_of_entries"> 173 <sch:title>mandatory number_of_entries tests</sch:title> 174 <sch:rule context="number_of_entries"> 175 <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> 176 </sch:rule> 177 </sch:pattern> 178 </sch:schema> 179 ''') 180 tree_valid = self.parse('''\ 181 <message> 182 <number_of_entries>0</number_of_entries> 183 <entries> 184 </entries> 185 </message> 186 ''') 187 tree_invalid = self.parse('''\ 188 <message> 189 <number_of_entries>3</number_of_entries> 190 <entries> 191 <entry>Entry 1</entry> 192 <entry>Entry 2</entry> 193 </entries> 194 </message> 195 ''') 196 schematron = isoschematron.Schematron(schema) 197 self.assertTrue(schematron(tree_valid), schematron.error_log) 198 self.assertRaises(etree.DocumentInvalid, schematron.assertValid, 199 tree_invalid)
200
202 schema = self.parse('''\ 203 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 204 <sch:pattern id="number_of_entries"> 205 <sch:title>mandatory number_of_entries tests</sch:title> 206 <sch:rule context="number_of_entries"> 207 <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> 208 </sch:rule> 209 </sch:pattern> 210 </sch:schema> 211 ''') 212 tree_valid = self.parse('''\ 213 <message> 214 <number_of_entries>0</number_of_entries> 215 <entries> 216 </entries> 217 </message> 218 ''') 219 tree_invalid = self.parse('''\ 220 <message> 221 <number_of_entries>3</number_of_entries> 222 <entries> 223 <entry>Entry 1</entry> 224 <entry>Entry 2</entry> 225 </entries> 226 </message> 227 ''') 228 schematron = isoschematron.Schematron(schema) 229 self.assertTrue(schematron(tree_valid), schematron.error_log) 230 valid = schematron(tree_invalid) 231 self.assertTrue(not valid) 232 self.assertEqual(len(schematron.error_log), 1, 233 'expected single error: %s (%s errors)' % 234 (schematron.error_log, len(schematron.error_log)))
235
237 schema = self.parse('''\ 238 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 239 <sch:pattern id="number_of_entries"> 240 <sch:title>mandatory number_of_entries tests</sch:title> 241 <sch:rule context="number_of_entries"> 242 <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> 243 </sch:rule> 244 </sch:pattern> 245 </sch:schema> 246 ''') 247 tree_valid = self.parse('''\ 248 <message> 249 <number_of_entries>0</number_of_entries> 250 <entries> 251 </entries> 252 </message> 253 ''') 254 tree_invalid = self.parse('''\ 255 <message> 256 <number_of_entries>3</number_of_entries> 257 <entries> 258 <entry>Entry 1</entry> 259 <entry>Entry 2</entry> 260 </entries> 261 </message> 262 ''') 263 schematron = isoschematron.Schematron(schema, store_report=True) 264 self.assertTrue(schematron(tree_valid), schematron.error_log) 265 valid = schematron(tree_invalid) 266 self.assertTrue(not valid) 267 self.assertTrue( 268 isinstance(schematron.validation_report, etree._ElementTree), 269 'expected a validation report result tree, got: %s' % 270 (schematron.validation_report)) 271 272 schematron = isoschematron.Schematron(schema, store_report=False) 273 self.assertTrue(schematron(tree_valid), schematron.error_log) 274 valid = schematron(tree_invalid) 275 self.assertTrue(not valid) 276 self.assertTrue(schematron.validation_report is None, 277 'validation reporting switched off, still: %s' % 278 (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
843 -def test_suite():
844 suite = unittest.TestSuite() 845 suite.addTests([unittest.makeSuite(ETreeISOSchematronTestCase)]) 846 suite.addTests( 847 [make_doctest('../../../doc/validation.txt')]) 848 return suite
849 850 if __name__ == '__main__': 851 print('to test use test.py %s' % __file__) 852