# -*- coding: utf-8 -*-
"""
Test cases related to ISO-Schematron parsing and validation
"""
from __future__ import absolute_import
import unittest
from lxml import isoschematron
from .common_imports import etree, HelperTestCase, fileInTestDir, doctest, make_doctest
class ETreeISOSchematronTestCase(HelperTestCase):
def test_schematron(self):
tree_valid = self.parse('')
tree_invalid = self.parse('')
schema = self.parse('''\
Open Model
BBB element is not present
CCC element is not present
Closed model"
BBB element is not present
CCC element is not present
There is an extra element
''')
schema = isoschematron.Schematron(schema)
self.assertTrue(schema.validate(tree_valid))
self.assertTrue(not schema.validate(tree_invalid))
def test_schematron_elementtree_error(self):
self.assertRaises(ValueError, isoschematron.Schematron, etree.ElementTree())
# an empty pattern is valid in iso schematron
def test_schematron_empty_pattern(self):
schema = self.parse('''\
Open model
''')
schema = isoschematron.Schematron(schema)
self.assertTrue(schema)
def test_schematron_invalid_schema_empty(self):
schema = self.parse('''\
''')
self.assertRaises(etree.SchematronParseError,
isoschematron.Schematron, schema)
def test_schematron_invalid_schema_namespace(self):
schema = self.parse('''\
''')
self.assertRaises(etree.SchematronParseError,
isoschematron.Schematron, schema)
def test_schematron_from_tree(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(isinstance(schematron, isoschematron.Schematron))
def test_schematron_from_element(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
schematron = isoschematron.Schematron(schema.getroot())
self.assertTrue(isinstance(schematron, isoschematron.Schematron))
def test_schematron_from_file(self):
schematron = isoschematron.Schematron(file=fileInTestDir('test.sch'))
self.assertTrue(isinstance(schematron, isoschematron.Schematron))
def test_schematron_call(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
0
''')
tree_invalid = self.parse('''\
3
Entry 1
Entry 2
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron(tree_valid), schematron.error_log)
valid = schematron(tree_invalid)
self.assertTrue(not valid)
def test_schematron_validate(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
0
''')
tree_invalid = self.parse('''\
3
Entry 1
Entry 2
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron.validate(tree_valid), schematron.error_log)
valid = schematron.validate(tree_invalid)
self.assertTrue(not valid)
def test_schematron_assertValid(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
0
''')
tree_invalid = self.parse('''\
3
Entry 1
Entry 2
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron(tree_valid), schematron.error_log)
self.assertRaises(etree.DocumentInvalid, schematron.assertValid,
tree_invalid)
def test_schematron_error_log(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
0
''')
tree_invalid = self.parse('''\
3
Entry 1
Entry 2
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron(tree_valid), schematron.error_log)
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(len(schematron.error_log), 1,
'expected single error: %s (%s errors)' %
(schematron.error_log, len(schematron.error_log)))
def test_schematron_result_report(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
0
''')
tree_invalid = self.parse('''\
3
Entry 1
Entry 2
''')
schematron = isoschematron.Schematron(schema, store_report=True)
self.assertTrue(schematron(tree_valid), schematron.error_log)
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertTrue(
isinstance(schematron.validation_report, etree._ElementTree),
'expected a validation report result tree, got: %s' % schematron.validation_report)
schematron = isoschematron.Schematron(schema, store_report=False)
self.assertTrue(schematron(tree_valid), schematron.error_log)
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertTrue(schematron.validation_report is None,
'validation reporting switched off, still: %s' % schematron.validation_report)
def test_schematron_store_schematron(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron.validator_xslt is None)
schematron = isoschematron.Schematron(schema, store_schematron=True)
self.assertTrue(isinstance(schematron.schematron, etree._ElementTree),
'expected schematron schema to be stored')
def test_schematron_store_xslt(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron.validator_xslt is None)
schematron = isoschematron.Schematron(schema, store_xslt=True)
self.assertTrue(isinstance(schematron.validator_xslt, etree._ElementTree),
'expected validator xslt to be stored')
def test_schematron_abstract(self):
schema = self.parse('''\
iso schematron validation
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
''')
valid_trees = [
self.parse('''\
2009-12-10T15:21:00Z
'''),
self.parse('''\
2009-12-10T15:21:00Z
2009-12-10T15:21:00Z
'''),
self.parse('''\
2009-12-10T15:21:00+00:00
2009-12-10T15:21:00-00:00
'''),
]
schematron = isoschematron.Schematron(schema)
for tree_valid in valid_trees:
self.assertTrue(schematron(tree_valid), schematron.error_log)
tree_invalid = self.parse('''\
2009-12-10T16:21:00+01:00
2009-12-10T16:21:00+01:00
''')
expected = 2
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
tree_invalid = self.parse('''\
2009-12-10T16:21:00Z
''')
expected = 1
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
def test_schematron_phases(self):
schema = self.parse('''\
iso schematron validation
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
mandatory number_of_entries test
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
2009-12-10T15:21:00Z
0
''')
tree_invalid = self.parse('''\
2009-12-10T16:21:00+01:00
2009-12-10T16:21:00+01:00
3
Entry 1
Entry 2
''')
# check everything (default phase #ALL)
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 3
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase mandatory
schematron = isoschematron.Schematron(
schema, compile_params={'phase': 'mandatory'})
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 1
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase datetime_checks
schematron = isoschematron.Schematron(
schema, compile_params={'phase': 'datetime_checks'})
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 2
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase full
schematron = isoschematron.Schematron(
schema, compile_params={'phase': 'full'})
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 3
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
def test_schematron_phases_kwarg(self):
schema = self.parse('''\
iso schematron validation
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
[ERROR] element () dateTime value () is not qualified as UTC (tz: )
mandatory number_of_entries test
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
2009-12-10T15:21:00Z
0
''')
tree_invalid = self.parse('''\
2009-12-10T16:21:00+01:00
2009-12-10T16:21:00+01:00
3
Entry 1
Entry 2
''')
# check everything (default phase #ALL)
schematron = isoschematron.Schematron(schema)
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 3
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase mandatory
schematron = isoschematron.Schematron(schema, phase='mandatory')
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 1
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase datetime_checks
schematron = isoschematron.Schematron(schema, phase='datetime_checks')
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 2
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected,
'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
# check phase full
schematron = isoschematron.Schematron(schema, phase='full')
self.assertTrue(schematron(tree_valid), schematron.error_log)
expected = 3
valid = schematron(tree_invalid)
self.assertTrue(not valid)
self.assertEqual(
len(schematron.error_log), expected, 'expected %s errors: %s (%s errors)' %
(expected, schematron.error_log, len(schematron.error_log)))
def test_schematron_xmlschema_embedded(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
2
Entry 1
Entry 2
''')
tree_invalid = self.parse('''\
1
Entry 1
Entry 2
''')
xmlschema = etree.XMLSchema(schema)
schematron = isoschematron.Schematron(schema)
# fwiw, this must also be XMLSchema-valid
self.assertTrue(xmlschema(tree_valid), xmlschema.error_log)
self.assertTrue(schematron(tree_valid))
# still schema-valid
self.assertTrue(xmlschema(tree_invalid), xmlschema.error_log)
self.assertTrue(not schematron(tree_invalid))
def test_schematron_relaxng_embedded(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
tree_valid = self.parse('''\
2
Entry 1
Entry 2
''')
tree_invalid = self.parse('''\
1
Entry 1
Entry 2
''')
relaxng = etree.RelaxNG(schema)
schematron = isoschematron.Schematron(schema)
# fwiw, this must also be RelaxNG-valid
self.assertTrue(relaxng(tree_valid), relaxng.error_log)
self.assertTrue(schematron(tree_valid))
# still schema-valid
self.assertTrue(relaxng(tree_invalid), relaxng.error_log)
self.assertTrue(not schematron(tree_invalid))
def test_schematron_invalid_args(self):
schema = self.parse('''\
mandatory number_of_entries tests
[ERROR] number_of_entries () must equal the number of entries/entry elements ()
''')
# handing phase as keyword arg will *not* raise the type error
self.assertRaises(TypeError, isoschematron.Schematron, schema,
compile_params={'phase': None})
def test_schematron_customization(self):
class MySchematron(isoschematron.Schematron):
def _extract(self, root):
schematron = (root.xpath(
'//sch:schema',
namespaces={'sch': "http://purl.oclc.org/dsdl/schematron"})
or [None])[0]
return schematron
def _include(self, schematron, **kwargs):
raise RuntimeError('inclusion unsupported')
def _expand(self, schematron, **kwargs):
raise RuntimeError('expansion unsupported')
def _validation_errors(self, validationReport):
valid = etree.XPath(
'count(//svrl:successful-report[@flag="critical"])=1',
namespaces={'svrl': isoschematron.SVRL_NS})(
validationReport)
if valid:
return []
error = etree.Element('Error')
error.text = 'missing critical condition report'
return [error]
tree_valid = self.parse('')
tree_invalid = self.parse('')
schema = self.parse('''\
Open Model
BBB element must be present
CCC element must be present
Closed model"
BBB element must be present
CCC element must be present
Only BBB and CCC children must be present
''')
# check if overridden _include is run
self.assertRaises(RuntimeError, MySchematron, schema, store_report=True)
# check if overridden _expand is run
self.assertRaises(RuntimeError, MySchematron, schema, store_report=True,
include=False)
schema = MySchematron(schema, store_report=True, include=False,
expand=False)
self.assertTrue(schema.validate(tree_valid))
self.assertTrue(not schema.validate(tree_invalid))
#TODO: test xslt parameters for inclusion, expand & compile steps (?)
def test_schematron_fail_on_report(self):
tree_valid = self.parse('')
tree_invalid = self.parse('')
schema = self.parse('''\
Simple Report
DDD element must not be present
''')
schema_report = isoschematron.Schematron(
schema, error_finder=isoschematron.Schematron.ASSERTS_AND_REPORTS)
schema_no_report = isoschematron.Schematron(schema)
self.assertTrue(schema_report.validate(tree_valid))
self.assertTrue(not schema_report.validate(tree_invalid))
self.assertTrue(schema_no_report.validate(tree_valid))
self.assertTrue(schema_no_report.validate(tree_invalid))
def test_suite():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(ETreeISOSchematronTestCase)])
suite.addTests(doctest.DocTestSuite(isoschematron))
suite.addTests(
[make_doctest('../../../doc/validation.txt')])
return suite
if __name__ == '__main__':
print('to test use test.py %s' % __file__)