1
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)
13
14 from common_imports import etree, HelperTestCase, fileInTestDir
15 from common_imports import doctest, make_doctest
16
17
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
48
49
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
67
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
106
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
140 schema = self.parse('''\
141 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
142 <sch:pattern id="number_of_entries">
143 <sch:title>mandatory number_of_entries tests</sch:title>
144 <sch:rule context="number_of_entries">
145 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
146 </sch:rule>
147 </sch:pattern>
148 </sch:schema>
149 ''')
150 tree_valid = self.parse('''\
151 <message>
152 <number_of_entries>0</number_of_entries>
153 <entries>
154 </entries>
155 </message>
156 ''')
157 tree_invalid = self.parse('''\
158 <message>
159 <number_of_entries>3</number_of_entries>
160 <entries>
161 <entry>Entry 1</entry>
162 <entry>Entry 2</entry>
163 </entries>
164 </message>
165 ''')
166 schematron = isoschematron.Schematron(schema)
167 self.assertTrue(schematron.validate(tree_valid), schematron.error_log)
168 valid = schematron.validate(tree_invalid)
169 self.assertTrue(not valid)
170
172 schema = self.parse('''\
173 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
174 <sch:pattern id="number_of_entries">
175 <sch:title>mandatory number_of_entries tests</sch:title>
176 <sch:rule context="number_of_entries">
177 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
178 </sch:rule>
179 </sch:pattern>
180 </sch:schema>
181 ''')
182 tree_valid = self.parse('''\
183 <message>
184 <number_of_entries>0</number_of_entries>
185 <entries>
186 </entries>
187 </message>
188 ''')
189 tree_invalid = self.parse('''\
190 <message>
191 <number_of_entries>3</number_of_entries>
192 <entries>
193 <entry>Entry 1</entry>
194 <entry>Entry 2</entry>
195 </entries>
196 </message>
197 ''')
198 schematron = isoschematron.Schematron(schema)
199 self.assertTrue(schematron(tree_valid), schematron.error_log)
200 self.assertRaises(etree.DocumentInvalid, schematron.assertValid,
201 tree_invalid)
202
204 schema = self.parse('''\
205 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
206 <sch:pattern id="number_of_entries">
207 <sch:title>mandatory number_of_entries tests</sch:title>
208 <sch:rule context="number_of_entries">
209 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
210 </sch:rule>
211 </sch:pattern>
212 </sch:schema>
213 ''')
214 tree_valid = self.parse('''\
215 <message>
216 <number_of_entries>0</number_of_entries>
217 <entries>
218 </entries>
219 </message>
220 ''')
221 tree_invalid = self.parse('''\
222 <message>
223 <number_of_entries>3</number_of_entries>
224 <entries>
225 <entry>Entry 1</entry>
226 <entry>Entry 2</entry>
227 </entries>
228 </message>
229 ''')
230 schematron = isoschematron.Schematron(schema)
231 self.assertTrue(schematron(tree_valid), schematron.error_log)
232 valid = schematron(tree_invalid)
233 self.assertTrue(not valid)
234 self.assertEqual(len(schematron.error_log), 1,
235 'expected single error: %s (%s errors)' %
236 (schematron.error_log, len(schematron.error_log)))
237
239 schema = self.parse('''\
240 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
241 <sch:pattern id="number_of_entries">
242 <sch:title>mandatory number_of_entries tests</sch:title>
243 <sch:rule context="number_of_entries">
244 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
245 </sch:rule>
246 </sch:pattern>
247 </sch:schema>
248 ''')
249 tree_valid = self.parse('''\
250 <message>
251 <number_of_entries>0</number_of_entries>
252 <entries>
253 </entries>
254 </message>
255 ''')
256 tree_invalid = self.parse('''\
257 <message>
258 <number_of_entries>3</number_of_entries>
259 <entries>
260 <entry>Entry 1</entry>
261 <entry>Entry 2</entry>
262 </entries>
263 </message>
264 ''')
265 schematron = isoschematron.Schematron(schema, store_report=True)
266 self.assertTrue(schematron(tree_valid), schematron.error_log)
267 valid = schematron(tree_invalid)
268 self.assertTrue(not valid)
269 self.assertTrue(
270 isinstance(schematron.validation_report, etree._ElementTree),
271 'expected a validation report result tree, got: %s' % schematron.validation_report)
272
273 schematron = isoschematron.Schematron(schema, store_report=False)
274 self.assertTrue(schematron(tree_valid), schematron.error_log)
275 valid = schematron(tree_invalid)
276 self.assertTrue(not valid)
277 self.assertTrue(schematron.validation_report is None,
278 'validation reporting switched off, still: %s' % schematron.validation_report)
279
281 schema = self.parse('''\
282 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
283 <sch:pattern id="number_of_entries">
284 <sch:title>mandatory number_of_entries tests</sch:title>
285 <sch:rule context="number_of_entries">
286 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
287 </sch:rule>
288 </sch:pattern>
289 </sch:schema>
290 ''')
291 schematron = isoschematron.Schematron(schema)
292 self.assertTrue(schematron.validator_xslt is None)
293
294 schematron = isoschematron.Schematron(schema, store_schematron=True)
295 self.assertTrue(isinstance(schematron.schematron, etree._ElementTree),
296 'expected schematron schema to be stored')
297
299 schema = self.parse('''\
300 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
301 <sch:pattern id="number_of_entries">
302 <sch:title>mandatory number_of_entries tests</sch:title>
303 <sch:rule context="number_of_entries">
304 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
305 </sch:rule>
306 </sch:pattern>
307 </sch:schema>
308 ''')
309 schematron = isoschematron.Schematron(schema)
310 self.assertTrue(schematron.validator_xslt is None)
311
312 schematron = isoschematron.Schematron(schema, store_xslt=True)
313 self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree),
314 'expected validator xslt to be stored')
315
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
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
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
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
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
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
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
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
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
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
703 self.assertTrue(xmlschema(tree_valid), xmlschema.error_log)
704 self.assertTrue(schematron(tree_valid))
705
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
759 self.assertTrue(relaxng(tree_valid), relaxng.error_log)
760 self.assertTrue(schematron(tree_valid))
761
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
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
830 self.assertRaises(RuntimeError, MySchematron, schema, store_report=True)
831
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
841
843 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>')
844 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>')
845 schema = self.parse('''\
846 <schema xmlns="http://purl.oclc.org/dsdl/schematron" >
847 <pattern id="OpenModel">
848 <title>Simple Report</title>
849 <rule context="AAA">
850 <report test="DDD"> DDD element must not be present</report>
851 </rule>
852 </pattern>
853 </schema>
854 ''')
855 schema_report = isoschematron.Schematron(
856 schema, error_finder=isoschematron.Schematron.ASSERTS_AND_REPORTS)
857 schema_no_report = isoschematron.Schematron(schema)
858 self.assertTrue(schema_report.validate(tree_valid))
859 self.assertTrue(not schema_report.validate(tree_invalid))
860 self.assertTrue(schema_no_report.validate(tree_valid))
861 self.assertTrue(schema_no_report.validate(tree_invalid))
862
863
871
872 if __name__ == '__main__':
873 print('to test use test.py %s' % __file__)
874