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