lxml.tests.test_isoschematron
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