import logging from pathlib import Path from xmlschema.etree import etree_tostring from xmlschema import XMLSchema, XMLSchemaParseError def BuildSchemaDictonary (fileList): if len(fileList) == 0: logging.info("No schema files provided") return [] logging.info("Schema locations:") schemaLocations = [] for schemaFile in fileList: try: xs = XMLSchema(schemaFile, validation='skip') schemaLocations.append((xs.default_namespace, str(Path(schemaFile).resolve()))) logging.info(" [ {0} -> {1} ]".format(xs.default_namespace, schemaFile)) except XMLSchemaParseError as ex: logging.warning (" [ {0} failed to parse: {1} ]".format(schemaFile, ex)) return schemaLocations def BuildSchema (coreFile, fileList = None): schemaLocations = [] if fileList and len(fileList) > 0: schemaLocations = BuildSchemaDictonary(fileList) coreSchema = XMLSchema(str(Path(coreFile)), locations=schemaLocations) return coreSchema def ValidateXSDFiles (fileList): if len(fileList) == 0: logging.info("No schema files provided") return {} schemaLocations = BuildSchemaDictonary(fileList) errors = {} logging.info("Schema validation:") for schemaFile in fileList: try: schema = XMLSchema(schemaFile, locations = schemaLocations, validation="lax") logging.info(schemaFile + ": OK") errors[schemaFile] = [f"{etree_tostring(e.elem, e.namespaces, ' ', 20)} - {e.message}" for e in schema.all_errors] except XMLSchemaParseError as ex: logging.warning(schemaFile + ": Failed validation ({0})".format(ex.message)) if (ex.schema_url) and (ex.schema_url != ex.origin_url): logging.warning(" Error comes from {0}, suppressing".format(ex.schema_url)) errors[schemaFile] = [] else: errors[schemaFile] = [ex] return errors def ValidateAllXSDFilesInPath (path): schemaGlob = [str(f) for f in Path(path).rglob("*.xsd")] return ValidateXSDFiles(schemaGlob) def ValidateInstanceDocuments (coreFile, supportingSchemas, instanceDocs): if (instanceDocs is None) or len(instanceDocs) == 0: logging.warning ("No instance documents provided") return [] schema = BuildSchema(coreFile, supportingSchemas) errors = [] for instanceDoc in instanceDocs: try: schema.validate(instanceDoc) logging.info ("{0} passed validation".format(instanceDoc)) except Exception as ex: logging.error ("{0} failed validation: {1}".format(instanceDoc, ex)) return errors def processResults (results, stageName): """ Counts the number of errors and writes out the output per filename :param results: List of filenames (str or Pathlib Path) :param stageName: Name to decorate the output with :returns: The number of files which had errors """ print("") errorCount = sum([1 for r in results.values() if not r['ok']]) logging.info(f"{errorCount} {stageName} errors encountered") print(f"{'-':-<60}") print(f"{stageName} results:") print(f"{'-':-<60}") for filename, result in results.items(): print(f" {filename:.<55}{'..OK' if result['ok'] else 'FAIL'}") if not result['ok']: if isinstance(result['message'], list): for thing in result['message']: print(f" {thing['message']}") else: print(f" {result['message']}") print(f"{'-':-<60}") print(f"{stageName} errors: {errorCount}") print(f"{'-':-<60}") return errorCount if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) logging.info('Searching for XSD files') fileList = list(Path(".").rglob("*.xsd")) + list(Path(".").rglob("*.xsd")) logging.info(f'{len(fileList)} XSD files found') for file in fileList: logging.debug(f' {file}') ignoreList = Path('testing/xsd_ignore.txt').read_text().splitlines() ignoredFiles = [] for ignore in ignoreList: logging.debug(f'Ignoring pattern {ignore}') for file in fileList: if ignore in str(file): ignoredFiles.append(file) logging.debug(f" Ignoring {str(file)} as contains {ignore}") ignoredFiles = list(set(ignoredFiles)) logging.info(f'{len(ignoredFiles)} files ignored') for file in ignoredFiles: logging.debug(f' {file}') fileList = [file for file in fileList if file not in ignoredFiles] logging.info(f'{len(fileList)} files to process') for file in fileList: logging.debug(f' {file}') if len(fileList) == 0: logging.warning ("No files specified") exit(0) logging.info("Parsing ASN1 files") parseResults = syntaxCheckXSD(fileList) if processResults(parseResults, "Parsing") > 0: exit(-1) logging.info ("Getting compile targets") compileTargets = json.loads(Path('testing/asn_compile_targets.json').read_text()) logging.info (f"{len(compileTargets)} compile targets found") compileResults = compileAllTargets(compileTargets) if processResults(compileResults, "Compiling") > 0: exit(-1)