140 lines
7.3 KiB
Python
140 lines
7.3 KiB
Python
|
import os
|
||
|
|
||
|
from lxml import etree
|
||
|
from nose.tools import assert_equal, assert_in
|
||
|
|
||
|
from ckanext.spatial import validation
|
||
|
|
||
|
# other validation tests are in test_harvest.py
|
||
|
|
||
|
class TestValidation:
|
||
|
|
||
|
def get_validation_errors(self, validator, validation_test_filename):
|
||
|
validation_test_filepath = os.path.join(os.path.dirname(__file__),
|
||
|
'xml',
|
||
|
validation_test_filename)
|
||
|
xml = etree.parse(validation_test_filepath)
|
||
|
is_valid, errors = validator.is_valid(xml)
|
||
|
return ';'.join(errors)
|
||
|
|
||
|
def test_iso19139_failure(self):
|
||
|
errors = self.get_validation_errors(validation.ISO19139Schema,
|
||
|
'iso19139/dataset-invalid.xml')
|
||
|
|
||
|
assert len(errors) > 0
|
||
|
assert_in('Dataset schema (gmx.xsd)', errors)
|
||
|
assert_in('{http://www.isotc211.org/2005/gmd}nosuchelement\': This element is not expected.', errors)
|
||
|
|
||
|
def test_iso19139_pass(self):
|
||
|
errors = self.get_validation_errors(validation.ISO19139Schema,
|
||
|
'iso19139/dataset.xml')
|
||
|
assert_equal(errors, '')
|
||
|
|
||
|
# Gemini2.1 tests are basically the same as those in test_harvest.py but
|
||
|
# a few little differences make it worth not removing them in
|
||
|
# test_harvest
|
||
|
|
||
|
def test_01_dataset_fail_iso19139_schema(self):
|
||
|
errors = self.get_validation_errors(validation.ISO19139EdenSchema,
|
||
|
'gemini2.1/validation/01_Dataset_Invalid_XSD_No_Such_Element.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('(gmx.xsd)', errors)
|
||
|
assert_in('\'{http://www.isotc211.org/2005/gmd}nosuchelement\': This element is not expected.', errors)
|
||
|
|
||
|
def test_02_dataset_fail_constraints_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.ConstraintsSchematron,
|
||
|
'gemini2.1/validation/02_Dataset_Invalid_19139_Missing_Data_Format.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('MD_Distribution / MD_Format: count(distributionFormat + distributorFormat) > 0', errors)
|
||
|
|
||
|
def test_03_dataset_fail_gemini_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.Gemini2Schematron,
|
||
|
'gemini2.1/validation/03_Dataset_Invalid_GEMINI_Missing_Keyword.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('Descriptive keywords are mandatory', errors)
|
||
|
|
||
|
def assert_passes_all_gemini2_1_validation(self, xml_filepath):
|
||
|
errs = self.get_validation_errors(validation.ISO19139EdenSchema,
|
||
|
xml_filepath)
|
||
|
assert not errs, 'ISO19139EdenSchema: ' + errs
|
||
|
errs = self.get_validation_errors(validation.ConstraintsSchematron,
|
||
|
xml_filepath)
|
||
|
assert not errs, 'ConstraintsSchematron: ' + errs
|
||
|
errs = self.get_validation_errors(validation.Gemini2Schematron,
|
||
|
xml_filepath)
|
||
|
assert not errs, 'Gemini2Schematron: ' + errs
|
||
|
|
||
|
def test_04_dataset_valid(self):
|
||
|
self.assert_passes_all_gemini2_1_validation('gemini2.1/validation/04_Dataset_Valid.xml')
|
||
|
|
||
|
def test_05_series_fail_iso19139_schema(self):
|
||
|
errors = self.get_validation_errors(validation.ISO19139EdenSchema,
|
||
|
'gemini2.1/validation/05_Series_Invalid_XSD_No_Such_Element.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('(gmx.xsd)', errors)
|
||
|
assert_in('\'{http://www.isotc211.org/2005/gmd}nosuchelement\': This element is not expected.', errors)
|
||
|
|
||
|
def test_06_series_fail_constraints_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.ConstraintsSchematron,
|
||
|
'gemini2.1/validation/06_Series_Invalid_19139_Missing_Data_Format.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('MD_Distribution / MD_Format: count(distributionFormat + distributorFormat) > 0', errors)
|
||
|
|
||
|
def test_07_series_fail_gemini_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.Gemini2Schematron,
|
||
|
'gemini2.1/validation/07_Series_Invalid_GEMINI_Missing_Keyword.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('Descriptive keywords are mandatory', errors)
|
||
|
|
||
|
def test_08_series_valid(self):
|
||
|
self.assert_passes_all_gemini2_1_validation('gemini2.1/validation/08_Series_Valid.xml')
|
||
|
|
||
|
def test_09_service_fail_iso19139_schema(self):
|
||
|
errors = self.get_validation_errors(validation.ISO19139EdenSchema,
|
||
|
'gemini2.1/validation/09_Service_Invalid_No_Such_Element.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('(gmx.xsd & srv.xsd)', errors)
|
||
|
assert_in('\'{http://www.isotc211.org/2005/gmd}nosuchelement\': This element is not expected.', errors)
|
||
|
|
||
|
def test_10_service_fail_constraints_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.ConstraintsSchematron,
|
||
|
'gemini2.1/validation/10_Service_Invalid_19139_Level_Description.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in("DQ_Scope: 'levelDescription' is mandatory if 'level' notEqual 'dataset' or 'series'.", errors)
|
||
|
|
||
|
def test_11_service_fail_gemini_schematron(self):
|
||
|
errors = self.get_validation_errors(validation.Gemini2Schematron,
|
||
|
'gemini2.1/validation/11_Service_Invalid_GEMINI_Service_Type.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in("Service type shall be one of 'discovery', 'view', 'download', 'transformation', 'invoke' or 'other' following INSPIRE generic names.", errors)
|
||
|
|
||
|
def test_12_service_valid(self):
|
||
|
self.assert_passes_all_gemini2_1_validation('gemini2.1/validation/12_Service_Valid.xml')
|
||
|
|
||
|
def test_13_dataset_fail_iso19139_schema_2(self):
|
||
|
# This test Dataset has srv tags and only Service metadata should.
|
||
|
errors = self.get_validation_errors(validation.ISO19139EdenSchema,
|
||
|
'gemini2.1/validation/13_Dataset_Invalid_Element_srv.xml')
|
||
|
assert len(errors) > 0
|
||
|
assert_in('(gmx.xsd)', errors)
|
||
|
assert_in('(u"Element \'{http://www.isotc211.org/2005/srv}SV_ServiceIdentification\': This element is not expected.', errors)
|
||
|
|
||
|
def test_schematron_error_extraction(self):
|
||
|
validation_error_xml = '''
|
||
|
<root xmlns:svrl="http://purl.oclc.org/dsdl/svrl">
|
||
|
<svrl:failed-assert test="srv:serviceType/*[1] = 'discovery' or srv:serviceType/*[1] = 'view' or srv:serviceType/*[1] = 'download' or srv:serviceType/*[1] = 'transformation' or srv:serviceType/*[1] = 'invoke' or srv:serviceType/*[1] = 'other'" location="/*[local-name()='MD_Metadata' and namespace-uri()='http://www.isotc211.org/2005/gmd']/*[local-name()='identificationInfo' and namespace-uri()='http://www.isotc211.org/2005/gmd']/*[local-name()='SV_ServiceIdentification' and namespace-uri()='http://www.isotc211.org/2005/srv']">
|
||
|
<svrl:text>
|
||
|
Service type shall be one of 'discovery', 'view', 'download', 'transformation', 'invoke' or 'other' following INSPIRE generic names.
|
||
|
</svrl:text>
|
||
|
</svrl:failed-assert>
|
||
|
</root>
|
||
|
'''
|
||
|
failure_xml = etree.fromstring(validation_error_xml)
|
||
|
fail_element = failure_xml.getchildren()[0]
|
||
|
details = validation.SchematronValidator.extract_error_details(fail_element)
|
||
|
if isinstance(details, tuple):
|
||
|
details = details[1]
|
||
|
assert_in("srv:serviceType/*[1] = 'discovery'", details)
|
||
|
assert_in("/*[local-name()='MD_Metadata'", details)
|
||
|
assert_in("Service type shall be one of 'discovery'", details)
|