1   
  2   
  3  """ 
  4  Test cases related to ISO-Schematron parsing and validation 
  5  """ 
  6   
  7  import unittest, sys, os.path 
  8  from lxml import isoschematron 
  9   
 10  this_dir = os.path.dirname(__file__) 
 11  if this_dir not in sys.path: 
 12      sys.path.insert(0, this_dir)  
 13   
 14  from common_imports import etree, HelperTestCase, fileInTestDir 
 15  from common_imports import doctest, make_doctest 
 16   
 17   
 20          tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 
 21          tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 
 22          schema = self.parse('''\ 
 23  <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 
 24      <pattern id="OpenModel"> 
 25          <title>Open Model</title> 
 26          <rule context="AAA"> 
 27              <assert test="BBB"> BBB element is not present</assert> 
 28              <assert test="CCC"> CCC element is not present</assert> 
 29          </rule> 
 30      </pattern> 
 31      <pattern id="ClosedModel"> 
 32          <title>Closed model"</title> 
 33          <rule context="AAA"> 
 34              <assert test="BBB"> BBB element is not present</assert> 
 35              <assert test="CCC"> CCC element is not present</assert> 
 36              <assert test="count(BBB|CCC) = count (*)">There is an extra element</assert> 
 37          </rule> 
 38      </pattern> 
 39  </schema> 
 40  ''') 
 41   
 42          schema = isoschematron.Schematron(schema) 
 43          self.assertTrue(schema.validate(tree_valid)) 
 44          self.assertTrue(not schema.validate(tree_invalid)) 
  45   
 48   
 49       
 51          schema = self.parse('''\ 
 52  <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 
 53      <pattern id="OpenModel"> 
 54          <title>Open model</title> 
 55      </pattern> 
 56  </schema> 
 57  ''') 
 58          schema = isoschematron.Schematron(schema) 
 59          self.assertTrue(schema) 
  60   
 67   
 74   
 76          schema = self.parse('''\ 
 77  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
 78    <sch:pattern id="number_of_entries"> 
 79      <sch:title>mandatory number_of_entries tests</sch:title> 
 80      <sch:rule context="number_of_entries"> 
 81        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
 82      </sch:rule> 
 83    </sch:pattern> 
 84  </sch:schema> 
 85  ''') 
 86          schematron = isoschematron.Schematron(schema) 
 87          self.assertTrue(isinstance(schematron, isoschematron.Schematron)) 
  88   
 90          schema = self.parse('''\ 
 91  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
 92    <sch:pattern id="number_of_entries"> 
 93      <sch:title>mandatory number_of_entries tests</sch:title> 
 94      <sch:rule context="number_of_entries"> 
 95        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
 96      </sch:rule> 
 97    </sch:pattern> 
 98  </sch:schema> 
 99  ''') 
100          schematron = isoschematron.Schematron(schema.getroot()) 
101          self.assertTrue(isinstance(schematron, isoschematron.Schematron)) 
 102   
106   
108          schema = self.parse('''\ 
109  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
110    <sch:pattern id="number_of_entries"> 
111      <sch:title>mandatory number_of_entries tests</sch:title> 
112      <sch:rule context="number_of_entries"> 
113        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
114      </sch:rule> 
115    </sch:pattern> 
116  </sch:schema> 
117  ''') 
118          tree_valid = self.parse('''\ 
119  <message> 
120    <number_of_entries>0</number_of_entries> 
121    <entries> 
122    </entries> 
123  </message> 
124  ''') 
125          tree_invalid = self.parse('''\ 
126  <message> 
127    <number_of_entries>3</number_of_entries> 
128    <entries> 
129      <entry>Entry 1</entry> 
130      <entry>Entry 2</entry> 
131    </entries> 
132  </message> 
133  ''') 
134          schematron = isoschematron.Schematron(schema) 
135          self.assertTrue(schematron(tree_valid), schematron.error_log) 
136          valid = schematron(tree_invalid) 
137          self.assertTrue(not valid) 
 138   
140          schema = self.parse('''\ 
141  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
142    <sch:pattern id="number_of_entries"> 
143      <sch:title>mandatory number_of_entries tests</sch:title> 
144      <sch:rule context="number_of_entries"> 
145        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
146      </sch:rule> 
147    </sch:pattern> 
148  </sch:schema> 
149  ''') 
150          tree_valid = self.parse('''\ 
151  <message> 
152    <number_of_entries>0</number_of_entries> 
153    <entries> 
154    </entries> 
155  </message> 
156  ''') 
157          tree_invalid = self.parse('''\ 
158  <message> 
159    <number_of_entries>3</number_of_entries> 
160    <entries> 
161      <entry>Entry 1</entry> 
162      <entry>Entry 2</entry> 
163    </entries> 
164  </message> 
165  ''') 
166          schematron = isoschematron.Schematron(schema) 
167          self.assertTrue(schematron.validate(tree_valid), schematron.error_log) 
168          valid = schematron.validate(tree_invalid) 
169          self.assertTrue(not valid) 
 170   
172          schema = self.parse('''\ 
173  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
174    <sch:pattern id="number_of_entries"> 
175      <sch:title>mandatory number_of_entries tests</sch:title> 
176      <sch:rule context="number_of_entries"> 
177        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
178      </sch:rule> 
179    </sch:pattern> 
180  </sch:schema> 
181  ''') 
182          tree_valid = self.parse('''\ 
183  <message> 
184    <number_of_entries>0</number_of_entries> 
185    <entries> 
186    </entries> 
187  </message> 
188  ''') 
189          tree_invalid = self.parse('''\ 
190  <message> 
191    <number_of_entries>3</number_of_entries> 
192    <entries> 
193      <entry>Entry 1</entry> 
194      <entry>Entry 2</entry> 
195    </entries> 
196  </message> 
197  ''') 
198          schematron = isoschematron.Schematron(schema) 
199          self.assertTrue(schematron(tree_valid), schematron.error_log) 
200          self.assertRaises(etree.DocumentInvalid, schematron.assertValid, 
201                            tree_invalid) 
 202   
204          schema = self.parse('''\ 
205  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
206    <sch:pattern id="number_of_entries"> 
207      <sch:title>mandatory number_of_entries tests</sch:title> 
208      <sch:rule context="number_of_entries"> 
209        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
210      </sch:rule> 
211    </sch:pattern> 
212  </sch:schema> 
213  ''') 
214          tree_valid = self.parse('''\ 
215  <message> 
216    <number_of_entries>0</number_of_entries> 
217    <entries> 
218    </entries> 
219  </message> 
220  ''') 
221          tree_invalid = self.parse('''\ 
222  <message> 
223    <number_of_entries>3</number_of_entries> 
224    <entries> 
225      <entry>Entry 1</entry> 
226      <entry>Entry 2</entry> 
227    </entries> 
228  </message> 
229  ''') 
230          schematron = isoschematron.Schematron(schema) 
231          self.assertTrue(schematron(tree_valid), schematron.error_log) 
232          valid = schematron(tree_invalid) 
233          self.assertTrue(not valid) 
234          self.assertEqual(len(schematron.error_log), 1, 
235                            'expected single error: %s (%s errors)' % 
236                            (schematron.error_log, len(schematron.error_log))) 
 237   
239          schema = self.parse('''\ 
240  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
241    <sch:pattern id="number_of_entries"> 
242      <sch:title>mandatory number_of_entries tests</sch:title> 
243      <sch:rule context="number_of_entries"> 
244        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
245      </sch:rule> 
246    </sch:pattern> 
247  </sch:schema> 
248  ''') 
249          tree_valid = self.parse('''\ 
250  <message> 
251    <number_of_entries>0</number_of_entries> 
252    <entries> 
253    </entries> 
254  </message> 
255  ''') 
256          tree_invalid = self.parse('''\ 
257  <message> 
258    <number_of_entries>3</number_of_entries> 
259    <entries> 
260      <entry>Entry 1</entry> 
261      <entry>Entry 2</entry> 
262    </entries> 
263  </message> 
264  ''') 
265          schematron = isoschematron.Schematron(schema, store_report=True) 
266          self.assertTrue(schematron(tree_valid), schematron.error_log) 
267          valid = schematron(tree_invalid) 
268          self.assertTrue(not valid) 
269          self.assertTrue( 
270              isinstance(schematron.validation_report, etree._ElementTree), 
271              'expected a validation report result tree, got: %s' % schematron.validation_report) 
272   
273          schematron = isoschematron.Schematron(schema, store_report=False) 
274          self.assertTrue(schematron(tree_valid), schematron.error_log) 
275          valid = schematron(tree_invalid) 
276          self.assertTrue(not valid) 
277          self.assertTrue(schematron.validation_report is None, 
278              'validation reporting switched off, still: %s' % schematron.validation_report) 
 279   
281          schema = self.parse('''\ 
282  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
283    <sch:pattern id="number_of_entries"> 
284      <sch:title>mandatory number_of_entries tests</sch:title> 
285      <sch:rule context="number_of_entries"> 
286        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
287      </sch:rule> 
288    </sch:pattern> 
289  </sch:schema> 
290  ''') 
291          schematron = isoschematron.Schematron(schema) 
292          self.assertTrue(schematron.validator_xslt is None) 
293   
294          schematron = isoschematron.Schematron(schema, store_schematron=True) 
295          self.assertTrue(isinstance(schematron.schematron, etree._ElementTree), 
296                       'expected schematron schema to be stored') 
 297   
299          schema = self.parse('''\ 
300  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
301    <sch:pattern id="number_of_entries"> 
302      <sch:title>mandatory number_of_entries tests</sch:title> 
303      <sch:rule context="number_of_entries"> 
304        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
305      </sch:rule> 
306    </sch:pattern> 
307  </sch:schema> 
308  ''') 
309          schematron = isoschematron.Schematron(schema) 
310          self.assertTrue(schematron.validator_xslt is None) 
311   
312          schematron = isoschematron.Schematron(schema, store_xslt=True) 
313          self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree), 
314                       'expected validator xslt to be stored') 
 315   
317          schema = self.parse('''\ 
318  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
319    <sch:title>iso schematron validation</sch:title> 
320    <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 
321    <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 
322   
323    <!-- of course, these only really make sense when combined with a schema that 
324         ensures datatype xs:dateTime --> 
325   
326    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 
327      <sch:rule context="$datetime"> 
328        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
329        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
330        <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
331      </sch:rule> 
332    </sch:pattern> 
333   
334    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 
335      <sch:rule context="$datetime"> 
336        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
337        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
338        <sch:assert test="@xsi:nil='true'  or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
339      </sch:rule> 
340    </sch:pattern> 
341   
342    <sch:pattern is-a="abstract.dateTime.tz_utc" id="datetime" > 
343      <sch:param name="datetime" value="datetime"/> 
344    </sch:pattern> 
345   
346    <sch:pattern is-a="abstract.dateTime.tz_utc_nillable" id="nillableDatetime"> 
347      <sch:param name="datetime" value="nillableDatetime"/> 
348    </sch:pattern> 
349   
350  </sch:schema> 
351  ''') 
352          valid_trees = [ 
353              self.parse('''\ 
354  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
355    <datetime>2009-12-10T15:21:00Z</datetime> 
356    <nillableDatetime xsi:nil="true"/> 
357  </root> 
358  '''), 
359              self.parse('''\ 
360  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
361    <datetime>2009-12-10T15:21:00Z</datetime> 
362    <nillableDatetime>2009-12-10T15:21:00Z</nillableDatetime> 
363  </root> 
364  '''), 
365              self.parse('''\ 
366  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
367    <datetime>2009-12-10T15:21:00+00:00</datetime> 
368    <nillableDatetime>2009-12-10T15:21:00-00:00</nillableDatetime> 
369  </root> 
370  '''), 
371              ] 
372   
373          schematron = isoschematron.Schematron(schema) 
374          for tree_valid in valid_trees: 
375              self.assertTrue(schematron(tree_valid), schematron.error_log) 
376   
377          tree_invalid = self.parse('''\ 
378  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
379    <datetime>2009-12-10T16:21:00+01:00</datetime> 
380    <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 
381  </root> 
382  ''') 
383          expected = 2 
384          valid = schematron(tree_invalid) 
385          self.assertTrue(not valid) 
386          self.assertEqual( 
387              len(schematron.error_log), expected, 
388              'expected %s errors: %s (%s errors)' % 
389              (expected, schematron.error_log, len(schematron.error_log))) 
390   
391          tree_invalid = self.parse('''\ 
392  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
393    <datetime xsi:nil="true"/> 
394    <nillableDatetime>2009-12-10T16:21:00Z</nillableDatetime> 
395  </root> 
396  ''') 
397          expected = 1 
398          valid = schematron(tree_invalid) 
399          self.assertTrue(not valid) 
400          self.assertEqual( 
401              len(schematron.error_log), expected, 
402              'expected %s errors: %s (%s errors)' % 
403              (expected, schematron.error_log, len(schematron.error_log))) 
 404   
406          schema = self.parse('''\ 
407  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
408    <sch:title>iso schematron validation</sch:title> 
409    <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 
410    <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 
411   
412    <sch:phase id="mandatory"> 
413      <sch:active pattern="number_of_entries"/> 
414    </sch:phase> 
415   
416    <sch:phase id="datetime_checks"> 
417      <sch:active pattern="datetime"/> 
418      <sch:active pattern="nillableDatetime"/> 
419    </sch:phase> 
420   
421    <sch:phase id="full"> 
422      <sch:active pattern="number_of_entries"/> 
423      <sch:active pattern="datetime"/> 
424      <sch:active pattern="nillableDatetime"/> 
425    </sch:phase> 
426   
427    <!-- of course, these only really make sense when combined with a schema that 
428         ensures datatype xs:dateTime --> 
429   
430    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 
431      <sch:rule context="$datetime"> 
432        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
433        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
434        <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
435      </sch:rule> 
436    </sch:pattern> 
437   
438    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 
439      <sch:rule context="$datetime"> 
440        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
441        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
442        <sch:assert test="@xsi:nil='true'  or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
443      </sch:rule> 
444    </sch:pattern> 
445   
446    <sch:pattern id="number_of_entries"> 
447      <sch:title>mandatory number_of_entries test</sch:title> 
448      <sch:rule context="number_of_entries"> 
449        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
450      </sch:rule> 
451    </sch:pattern> 
452   
453    <sch:pattern  id="datetime" is-a="abstract.dateTime.tz_utc"> 
454      <sch:param name="datetime" value="datetime"/> 
455    </sch:pattern> 
456   
457    <sch:pattern  id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 
458      <sch:param name="datetime" value="nillableDatetime"/> 
459    </sch:pattern> 
460   
461  </sch:schema> 
462  ''') 
463          tree_valid = self.parse('''\ 
464  <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
465    <datetime>2009-12-10T15:21:00Z</datetime> 
466    <nillableDatetime xsi:nil="true"/> 
467    <number_of_entries>0</number_of_entries> 
468    <entries> 
469    </entries> 
470  </message> 
471  ''') 
472          tree_invalid = self.parse('''\ 
473  <message> 
474    <datetime>2009-12-10T16:21:00+01:00</datetime> 
475    <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 
476    <number_of_entries>3</number_of_entries> 
477    <entries> 
478      <entry>Entry 1</entry> 
479      <entry>Entry 2</entry> 
480    </entries> 
481  </message> 
482  ''') 
483           
484          schematron = isoschematron.Schematron(schema) 
485          self.assertTrue(schematron(tree_valid), schematron.error_log) 
486          expected = 3 
487          valid = schematron(tree_invalid) 
488          self.assertTrue(not valid) 
489          self.assertEqual( 
490              len(schematron.error_log), expected, 
491              'expected %s errors: %s (%s errors)' % 
492              (expected, schematron.error_log, len(schematron.error_log))) 
493   
494           
495          schematron = isoschematron.Schematron( 
496              schema, compile_params={'phase': 'mandatory'}) 
497          self.assertTrue(schematron(tree_valid), schematron.error_log) 
498          expected = 1 
499          valid = schematron(tree_invalid) 
500          self.assertTrue(not valid) 
501          self.assertEqual( 
502              len(schematron.error_log), expected, 
503              'expected %s errors: %s (%s errors)' % 
504              (expected, schematron.error_log, len(schematron.error_log))) 
505   
506           
507          schematron = isoschematron.Schematron( 
508              schema, compile_params={'phase': 'datetime_checks'}) 
509          self.assertTrue(schematron(tree_valid), schematron.error_log) 
510          expected = 2 
511          valid = schematron(tree_invalid) 
512          self.assertTrue(not valid) 
513          self.assertEqual( 
514              len(schematron.error_log), expected, 
515              'expected %s errors: %s (%s errors)' % 
516              (expected, schematron.error_log, len(schematron.error_log))) 
517   
518           
519          schematron = isoschematron.Schematron( 
520              schema, compile_params={'phase': 'full'}) 
521          self.assertTrue(schematron(tree_valid), schematron.error_log) 
522          expected = 3 
523          valid = schematron(tree_invalid) 
524          self.assertTrue(not valid) 
525          self.assertEqual( 
526              len(schematron.error_log), expected, 
527              'expected %s errors: %s (%s errors)' % 
528              (expected, schematron.error_log, len(schematron.error_log))) 
 529   
531          schema = self.parse('''\ 
532  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
533    <sch:title>iso schematron validation</sch:title> 
534    <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> 
535    <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> 
536   
537    <sch:phase id="mandatory"> 
538      <sch:active pattern="number_of_entries"/> 
539    </sch:phase> 
540   
541    <sch:phase id="datetime_checks"> 
542      <sch:active pattern="datetime"/> 
543      <sch:active pattern="nillableDatetime"/> 
544    </sch:phase> 
545   
546    <sch:phase id="full"> 
547      <sch:active pattern="number_of_entries"/> 
548      <sch:active pattern="datetime"/> 
549      <sch:active pattern="nillableDatetime"/> 
550    </sch:phase> 
551   
552    <!-- of course, these only really make sense when combined with a schema that 
553         ensures datatype xs:dateTime --> 
554   
555    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> 
556      <sch:rule context="$datetime"> 
557        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
558        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
559        <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
560      </sch:rule> 
561    </sch:pattern> 
562   
563    <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> 
564      <sch:rule context="$datetime"> 
565        <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> 
566        <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> 
567        <sch:assert test="@xsi:nil='true'  or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> 
568      </sch:rule> 
569    </sch:pattern> 
570   
571    <sch:pattern id="number_of_entries"> 
572      <sch:title>mandatory number_of_entries test</sch:title> 
573      <sch:rule context="number_of_entries"> 
574        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
575      </sch:rule> 
576    </sch:pattern> 
577   
578    <sch:pattern  id="datetime" is-a="abstract.dateTime.tz_utc"> 
579      <sch:param name="datetime" value="datetime"/> 
580    </sch:pattern> 
581   
582    <sch:pattern  id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> 
583      <sch:param name="datetime" value="nillableDatetime"/> 
584    </sch:pattern> 
585   
586  </sch:schema> 
587  ''') 
588          tree_valid = self.parse('''\ 
589  <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
590    <datetime>2009-12-10T15:21:00Z</datetime> 
591    <nillableDatetime xsi:nil="true"/> 
592    <number_of_entries>0</number_of_entries> 
593    <entries> 
594    </entries> 
595  </message> 
596  ''') 
597          tree_invalid = self.parse('''\ 
598  <message> 
599    <datetime>2009-12-10T16:21:00+01:00</datetime> 
600    <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> 
601    <number_of_entries>3</number_of_entries> 
602    <entries> 
603      <entry>Entry 1</entry> 
604      <entry>Entry 2</entry> 
605    </entries> 
606  </message> 
607  ''') 
608           
609          schematron = isoschematron.Schematron(schema) 
610          self.assertTrue(schematron(tree_valid), schematron.error_log) 
611          expected = 3 
612          valid = schematron(tree_invalid) 
613          self.assertTrue(not valid) 
614          self.assertEqual( 
615              len(schematron.error_log), expected, 
616              'expected %s errors: %s (%s errors)' % 
617              (expected, schematron.error_log, len(schematron.error_log))) 
618   
619           
620          schematron = isoschematron.Schematron(schema, phase='mandatory') 
621          self.assertTrue(schematron(tree_valid), schematron.error_log) 
622          expected = 1 
623          valid = schematron(tree_invalid) 
624          self.assertTrue(not valid) 
625          self.assertEqual( 
626              len(schematron.error_log), expected, 
627              'expected %s errors: %s (%s errors)' % 
628              (expected, schematron.error_log, len(schematron.error_log))) 
629   
630           
631          schematron = isoschematron.Schematron(schema, phase='datetime_checks') 
632          self.assertTrue(schematron(tree_valid), schematron.error_log) 
633          expected = 2 
634          valid = schematron(tree_invalid) 
635          self.assertTrue(not valid) 
636          self.assertEqual( 
637              len(schematron.error_log), expected, 
638              'expected %s errors: %s (%s errors)' % 
639              (expected, schematron.error_log, len(schematron.error_log))) 
640   
641           
642          schematron = isoschematron.Schematron(schema, phase='full') 
643          self.assertTrue(schematron(tree_valid), schematron.error_log) 
644          expected = 3 
645          valid = schematron(tree_invalid) 
646          self.assertTrue(not valid) 
647          self.assertEqual( 
648              len(schematron.error_log), expected, 'expected %s errors: %s (%s errors)' % 
649              (expected, schematron.error_log, len(schematron.error_log))) 
 650   
652          schema = self.parse('''\ 
653  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
654      xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
655      <xs:element name="message"> 
656          <xs:complexType> 
657              <xs:sequence> 
658                  <xs:element name="number_of_entries" type="xs:positiveInteger"> 
659                      <xs:annotation> 
660                          <xs:appinfo> 
661                              <sch:pattern id="number_of_entries"> 
662                                  <sch:title>mandatory number_of_entries tests</sch:title> 
663                                  <sch:rule context="number_of_entries"> 
664                                      <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
665                                  </sch:rule> 
666                              </sch:pattern> 
667                          </xs:appinfo> 
668                      </xs:annotation> 
669                  </xs:element> 
670                  <xs:element name="entries"> 
671                      <xs:complexType> 
672                          <xs:sequence> 
673                              <xs:element name="entry" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> 
674                          </xs:sequence> 
675                      </xs:complexType> 
676                  </xs:element> 
677              </xs:sequence> 
678          </xs:complexType> 
679      </xs:element> 
680  </xs:schema> 
681  ''') 
682          tree_valid = self.parse('''\ 
683  <message> 
684    <number_of_entries>2</number_of_entries> 
685    <entries> 
686      <entry>Entry 1</entry> 
687      <entry>Entry 2</entry> 
688    </entries> 
689  </message> 
690  ''') 
691          tree_invalid = self.parse('''\ 
692  <message> 
693    <number_of_entries>1</number_of_entries> 
694    <entries> 
695      <entry>Entry 1</entry> 
696      <entry>Entry 2</entry> 
697    </entries> 
698  </message> 
699  ''') 
700          xmlschema = etree.XMLSchema(schema) 
701          schematron = isoschematron.Schematron(schema) 
702           
703          self.assertTrue(xmlschema(tree_valid), xmlschema.error_log) 
704          self.assertTrue(schematron(tree_valid)) 
705           
706          self.assertTrue(xmlschema(tree_invalid), xmlschema.error_log) 
707          self.assertTrue(not schematron(tree_invalid)) 
 708   
710          schema = self.parse('''\ 
711  <grammar xmlns="http://relaxng.org/ns/structure/1.0" 
712    xmlns:sch="http://purl.oclc.org/dsdl/schematron" 
713    datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> 
714    <start> 
715      <ref name="message"/> 
716    </start> 
717    <define name="message"> 
718      <element name="message"> 
719        <element name="number_of_entries"> 
720          <!-- RelaxNG can be mixed freely with stuff from other namespaces --> 
721          <sch:pattern id="number_of_entries"> 
722            <sch:title>mandatory number_of_entries tests</sch:title> 
723            <sch:rule context="number_of_entries"> 
724              <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
725            </sch:rule> 
726          </sch:pattern> 
727          <data type="positiveInteger"/> 
728        </element> 
729        <element name="entries"> 
730          <zeroOrMore> 
731            <element name="entry"><data type="string"/></element> 
732          </zeroOrMore> 
733        </element> 
734      </element> 
735    </define> 
736  </grammar> 
737  ''') 
738          tree_valid = self.parse('''\ 
739  <message> 
740    <number_of_entries>2</number_of_entries> 
741    <entries> 
742      <entry>Entry 1</entry> 
743      <entry>Entry 2</entry> 
744    </entries> 
745  </message> 
746  ''') 
747          tree_invalid = self.parse('''\ 
748  <message> 
749    <number_of_entries>1</number_of_entries> 
750    <entries> 
751      <entry>Entry 1</entry> 
752      <entry>Entry 2</entry> 
753    </entries> 
754  </message> 
755  ''') 
756          relaxng = etree.RelaxNG(schema) 
757          schematron = isoschematron.Schematron(schema) 
758           
759          self.assertTrue(relaxng(tree_valid), relaxng.error_log) 
760          self.assertTrue(schematron(tree_valid)) 
761           
762          self.assertTrue(relaxng(tree_invalid), relaxng.error_log) 
763          self.assertTrue(not schematron(tree_invalid)) 
 764   
766          schema = self.parse('''\ 
767  <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
768    <sch:pattern id="number_of_entries"> 
769      <sch:title>mandatory number_of_entries tests</sch:title> 
770      <sch:rule context="number_of_entries"> 
771        <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> 
772      </sch:rule> 
773    </sch:pattern> 
774  </sch:schema> 
775  ''') 
776           
777          self.assertRaises(TypeError, isoschematron.Schematron, schema, 
778                            compile_params={'phase': None}) 
 779   
781          class MySchematron(isoschematron.Schematron): 
782              def _extract(self, root): 
783                  schematron = (root.xpath( 
784                      '//sch:schema', 
785                      namespaces={'sch': "http://purl.oclc.org/dsdl/schematron"}) 
786                      or [None])[0] 
787                  return schematron 
 788   
789              def _include(self, schematron, **kwargs): 
790                  raise RuntimeError('inclusion unsupported') 
843          tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') 
844          tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') 
845          schema = self.parse('''\ 
846  <schema xmlns="http://purl.oclc.org/dsdl/schematron" > 
847      <pattern id="OpenModel"> 
848          <title>Simple Report</title> 
849          <rule context="AAA"> 
850              <report test="DDD"> DDD element must not be present</report> 
851          </rule> 
852      </pattern> 
853  </schema> 
854  ''') 
855          schema_report = isoschematron.Schematron( 
856              schema, error_finder=isoschematron.Schematron.ASSERTS_AND_REPORTS) 
857          schema_no_report = isoschematron.Schematron(schema) 
858          self.assertTrue(schema_report.validate(tree_valid)) 
859          self.assertTrue(not schema_report.validate(tree_invalid)) 
860          self.assertTrue(schema_no_report.validate(tree_valid)) 
861          self.assertTrue(schema_no_report.validate(tree_invalid)) 
 862   
863   
871   
872  if __name__ == '__main__': 
873      print('to test use test.py %s' % __file__) 
874