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') 
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