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
19 tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>')
20 tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>')
21 schema = self.parse('''\
22 <schema xmlns="http://purl.oclc.org/dsdl/schematron" >
23 <pattern id="OpenModel">
24 <title>Open Model</title>
25 <rule context="AAA">
26 <assert test="BBB"> BBB element is not present</assert>
27 <assert test="CCC"> CCC element is not present</assert>
28 </rule>
29 </pattern>
30 <pattern id="ClosedModel">
31 <title>Closed model"</title>
32 <rule context="AAA">
33 <assert test="BBB"> BBB element is not present</assert>
34 <assert test="CCC"> CCC element is not present</assert>
35 <assert test="count(BBB|CCC) = count (*)">There is an extra element</assert>
36 </rule>
37 </pattern>
38 </schema>
39 ''')
40 schema = isoschematron.Schematron(schema)
41 self.assertTrue(schema.validate(tree_valid))
42 self.assertTrue(not schema.validate(tree_invalid))
43
46
47
49 schema = self.parse('''\
50 <schema xmlns="http://purl.oclc.org/dsdl/schematron" >
51 <pattern id="OpenModel">
52 <title>Open model</title>
53 </pattern>
54 </schema>
55 ''')
56 schema = isoschematron.Schematron(schema)
57 self.assertTrue(schema)
58
65
72
74 schema = self.parse('''\
75 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
76 <sch:pattern id="number_of_entries">
77 <sch:title>mandatory number_of_entries tests</sch:title>
78 <sch:rule context="number_of_entries">
79 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
80 </sch:rule>
81 </sch:pattern>
82 </sch:schema>
83 ''')
84 schematron = isoschematron.Schematron(schema)
85 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
86
88 schema = self.parse('''\
89 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
90 <sch:pattern id="number_of_entries">
91 <sch:title>mandatory number_of_entries tests</sch:title>
92 <sch:rule context="number_of_entries">
93 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
94 </sch:rule>
95 </sch:pattern>
96 </sch:schema>
97 ''')
98 schematron = isoschematron.Schematron(schema.getroot())
99 self.assertTrue(isinstance(schematron, isoschematron.Schematron))
100
104
106 schema = self.parse('''\
107 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
108 <sch:pattern id="number_of_entries">
109 <sch:title>mandatory number_of_entries tests</sch:title>
110 <sch:rule context="number_of_entries">
111 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
112 </sch:rule>
113 </sch:pattern>
114 </sch:schema>
115 ''')
116 tree_valid = self.parse('''\
117 <message>
118 <number_of_entries>0</number_of_entries>
119 <entries>
120 </entries>
121 </message>
122 ''')
123 tree_invalid = self.parse('''\
124 <message>
125 <number_of_entries>3</number_of_entries>
126 <entries>
127 <entry>Entry 1</entry>
128 <entry>Entry 2</entry>
129 </entries>
130 </message>
131 ''')
132 schematron = isoschematron.Schematron(schema)
133 self.assertTrue(schematron(tree_valid), schematron.error_log)
134 valid = schematron(tree_invalid)
135 self.assertTrue(not valid)
136
138 schema = self.parse('''\
139 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
140 <sch:pattern id="number_of_entries">
141 <sch:title>mandatory number_of_entries tests</sch:title>
142 <sch:rule context="number_of_entries">
143 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
144 </sch:rule>
145 </sch:pattern>
146 </sch:schema>
147 ''')
148 tree_valid = self.parse('''\
149 <message>
150 <number_of_entries>0</number_of_entries>
151 <entries>
152 </entries>
153 </message>
154 ''')
155 tree_invalid = self.parse('''\
156 <message>
157 <number_of_entries>3</number_of_entries>
158 <entries>
159 <entry>Entry 1</entry>
160 <entry>Entry 2</entry>
161 </entries>
162 </message>
163 ''')
164 schematron = isoschematron.Schematron(schema)
165 self.assertTrue(schematron.validate(tree_valid), schematron.error_log)
166 valid = schematron.validate(tree_invalid)
167 self.assertTrue(not valid)
168
170 schema = self.parse('''\
171 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
172 <sch:pattern id="number_of_entries">
173 <sch:title>mandatory number_of_entries tests</sch:title>
174 <sch:rule context="number_of_entries">
175 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
176 </sch:rule>
177 </sch:pattern>
178 </sch:schema>
179 ''')
180 tree_valid = self.parse('''\
181 <message>
182 <number_of_entries>0</number_of_entries>
183 <entries>
184 </entries>
185 </message>
186 ''')
187 tree_invalid = self.parse('''\
188 <message>
189 <number_of_entries>3</number_of_entries>
190 <entries>
191 <entry>Entry 1</entry>
192 <entry>Entry 2</entry>
193 </entries>
194 </message>
195 ''')
196 schematron = isoschematron.Schematron(schema)
197 self.assertTrue(schematron(tree_valid), schematron.error_log)
198 self.assertRaises(etree.DocumentInvalid, schematron.assertValid,
199 tree_invalid)
200
202 schema = self.parse('''\
203 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
204 <sch:pattern id="number_of_entries">
205 <sch:title>mandatory number_of_entries tests</sch:title>
206 <sch:rule context="number_of_entries">
207 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
208 </sch:rule>
209 </sch:pattern>
210 </sch:schema>
211 ''')
212 tree_valid = self.parse('''\
213 <message>
214 <number_of_entries>0</number_of_entries>
215 <entries>
216 </entries>
217 </message>
218 ''')
219 tree_invalid = self.parse('''\
220 <message>
221 <number_of_entries>3</number_of_entries>
222 <entries>
223 <entry>Entry 1</entry>
224 <entry>Entry 2</entry>
225 </entries>
226 </message>
227 ''')
228 schematron = isoschematron.Schematron(schema)
229 self.assertTrue(schematron(tree_valid), schematron.error_log)
230 valid = schematron(tree_invalid)
231 self.assertTrue(not valid)
232 self.assertEqual(len(schematron.error_log), 1,
233 'expected single error: %s (%s errors)' %
234 (schematron.error_log, len(schematron.error_log)))
235
237 schema = self.parse('''\
238 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
239 <sch:pattern id="number_of_entries">
240 <sch:title>mandatory number_of_entries tests</sch:title>
241 <sch:rule context="number_of_entries">
242 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
243 </sch:rule>
244 </sch:pattern>
245 </sch:schema>
246 ''')
247 tree_valid = self.parse('''\
248 <message>
249 <number_of_entries>0</number_of_entries>
250 <entries>
251 </entries>
252 </message>
253 ''')
254 tree_invalid = self.parse('''\
255 <message>
256 <number_of_entries>3</number_of_entries>
257 <entries>
258 <entry>Entry 1</entry>
259 <entry>Entry 2</entry>
260 </entries>
261 </message>
262 ''')
263 schematron = isoschematron.Schematron(schema, store_report=True)
264 self.assertTrue(schematron(tree_valid), schematron.error_log)
265 valid = schematron(tree_invalid)
266 self.assertTrue(not valid)
267 self.assertTrue(
268 isinstance(schematron.validation_report, etree._ElementTree),
269 'expected a validation report result tree, got: %s' %
270 (schematron.validation_report))
271
272 schematron = isoschematron.Schematron(schema, store_report=False)
273 self.assertTrue(schematron(tree_valid), schematron.error_log)
274 valid = schematron(tree_invalid)
275 self.assertTrue(not valid)
276 self.assertTrue(schematron.validation_report is None,
277 'validation reporting switched off, still: %s' %
278 (schematron.validation_report))
279
281 schema = self.parse('''\
282 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
283 <sch:pattern id="number_of_entries">
284 <sch:title>mandatory number_of_entries tests</sch:title>
285 <sch:rule context="number_of_entries">
286 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
287 </sch:rule>
288 </sch:pattern>
289 </sch:schema>
290 ''')
291 schematron = isoschematron.Schematron(schema)
292 self.assertTrue(schematron.validator_xslt is None)
293
294 schematron = isoschematron.Schematron(schema, store_schematron=True)
295 self.assertTrue(isinstance(schematron.schematron, etree._ElementTree),
296 'expected schematron schema to be stored')
297
299 schema = self.parse('''\
300 <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
301 <sch:pattern id="number_of_entries">
302 <sch:title>mandatory number_of_entries tests</sch:title>
303 <sch:rule context="number_of_entries">
304 <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert>
305 </sch:rule>
306 </sch:pattern>
307 </sch:schema>
308 ''')
309 schematron = isoschematron.Schematron(schema)
310 self.assertTrue(schematron.validator_xslt is None)
311
312 schematron = isoschematron.Schematron(schema, store_xslt=True)
313 self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree),
314 'expected validator xslt to be stored')
315
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
842
849
850 if __name__ == '__main__':
851 print('to test use test.py %s' % __file__)
852