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