diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..eed7743 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ +**Description** + +A brief description of the PR. + +**Main changes** + +1. First change +2. Second change + +**How was the PR tested?** + +1. Unit-test with some sample data. All unit tests passed. +2. Print output values. The printed outputs look reasonable. +3. Executed container locally and it worked. + +**Notes** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..acde199 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,18 @@ +name: Tests +on: push +jobs: + tests: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + os: [ubuntu-22.04, windows-2019] + name: ${{ matrix.os }} - Python ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - run: pip install -r requirements.txt -r requirements-tests.txt + - run: python -m pytest -vv diff --git a/.gitignore b/.gitignore index 004521a..7377849 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,20 @@ **/report.json **/report.xqar **/esmini -**/reports \ No newline at end of file +**/reports + +.vscode +*.pyc +__pycache__ +.venv* +env +dist +.mypy_cache +.idea +site +.coverage +htmlcov +.pytest_cache +coverage.xml +.coverage* +.python-version diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8280119 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: local + hooks: + - id: black + name: black + entry: black + language: system + types: [python] diff --git a/README.md b/README.md index 12f8c84..132426b 100644 --- a/README.md +++ b/README.md @@ -1 +1,97 @@ -# qc-openscenarioxml \ No newline at end of file +# qc-openscenarioxml + +This project implements the OpenScenario Checker for the ASAM Quality Checker project. + +## Installation + +To install the project, run: + +``` +pip install -r requirements.txt +``` + +This will install the needed dependencies to your local Python. + +## Usage + +The checker can be used as a Python script: + +``` +python main.py --help +usage: QC OpenScenario Checker [-h] (-d | -c CONFIG_PATH) +This is a collection of scripts for checking validity of OpenScenario (.xosc) files. +options: + -h, --help show this help message and exit + -d, --default_config + -c CONFIG_PATH, --config_path CONFIG_PATH +``` + +### Example + +- No issues found + +``` +python3 main.py -c example_config.xml +2024-06-12 15:14:11,864 - Initializing checks +2024-06-12 15:14:11,865 - Executing xml checks +2024-06-12 15:14:11,865 - Executing is_an_xml_document check +asam.net:xosc:0.9.0:is_an_xml_document +2024-06-12 15:14:11,865 - Issues found - 0 +2024-06-12 15:14:11,865 - Done +``` + + +- Issues found on file + +``` +python3 main.py -c example_config.xml +2024-06-12 15:19:45,139 - Initializing checks +2024-06-12 15:19:45,140 - Executing xml checks +2024-06-12 15:19:45,140 - Executing is_an_xml_document check +asam.net:xosc:0.9.0:is_an_xml_document +2024-06-12 15:19:45,140 - Issues found - 1 +2024-06-12 15:19:45,141 - Done + +``` + + +## Tests + +To run the tests, you need to have installed the main dependencies mentioned +at [Installation](#installation). + +Install Python tests and development dependencies: + +``` +pip install -r requirements-tests.txt +``` + +Execute tests: + +``` +python -m pytest -vv +``` + +They should output something similar to: + +``` +===================== test session starts ===================== +platform linux -- Python 3.11.9, pytest-8.2.2, pluggy-1.5.0 +``` + +You can check more options for pytest at its [own documentation](https://docs.pytest.org/). + +## Contributing + +For contributing, you need to install the development requirements besides the +test and installation requirements, for that run: + +``` +pip install -r requirements-dev.txt +``` + +You need to have pre-commit installed and install the hooks: + +``` +pre-commit install +``` diff --git a/checks/__init__.py b/checks/__init__.py deleted file mode 100644 index 6b671ad..0000000 --- a/checks/__init__.py +++ /dev/null @@ -1 +0,0 @@ -ORDER=['base_checks', 'schema_checks'] \ No newline at end of file diff --git a/checks/base_checks/__init__.py b/checks/base_checks/__init__.py deleted file mode 100644 index 9b78991..0000000 --- a/checks/base_checks/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -CHECKER_BUNDLE_NAME='Base' -CHECKER_BUNDLE_DESCRIPTION='Checks reading and parsing of OSC file.' -CHECKER_BUNDLE_VERSION='1.0' -ORDER=['check_file_exist', 'check_xml_parsing'] \ No newline at end of file diff --git a/checks/base_checks/check_file_exist.py b/checks/base_checks/check_file_exist.py deleted file mode 100644 index 42abd4c..0000000 --- a/checks/base_checks/check_file_exist.py +++ /dev/null @@ -1,27 +0,0 @@ -from result_report import IssueLevel, FileLocation -from checker_data import CheckerData - -import logging - - -def check_exists(checker_data: CheckerData) -> bool: - logging.info(get_checker_id()) - - if not checker_data.file.exists(): - checker_data.checker.gen_issue(IssueLevel.ERROR, 'Given file does not exist.', [FileLocation(checker_data.file)]) - return False - - if not checker_data.file.is_file(): - checker_data.checker.gen_issue(IssueLevel.ERROR, 'Given file is not a file.', [FileLocation(checker_data.file)]) - return False - -def get_checker_id(): - return 'check if file exists' - - -def get_description(): - return 'check if file exists and is valid.' - - -def check(checker_data: CheckerData) ->bool: - return check_exists(checker_data) \ No newline at end of file diff --git a/checks/base_checks/check_xml_parsing.py b/checks/base_checks/check_xml_parsing.py deleted file mode 100644 index 8e3a632..0000000 --- a/checks/base_checks/check_xml_parsing.py +++ /dev/null @@ -1,39 +0,0 @@ -from result_report import IssueLevel, FileLocation -from checker_data import CheckerData -from lxml import etree - -import logging - - -def check_read_XML(checker_data: CheckerData) -> bool: - logging.info(get_checker_id()) - - try: - with open(checker_data.file, 'r') as f: - _ = f.read() # set root for further test - except: - logging.exception(f'Cannot read file {checker_data.file.absolute()}') - checker_data.checker.gen_issue(IssueLevel.ERROR, 'Given file cannot be read.', [FileLocation(checker_data.file)]) - return False - - # run checks - try: - checker_data.data = etree.parse(str(checker_data.file), etree.XMLParser(dtd_validation=False)) - except: - logging.exception(f'Cannot parse XML from file {checker_data.file.absolute()}') - checker_data.checker.gen_issue(IssueLevel.ERROR, - 'Given file cannot be parsed to XML.', - [FileLocation(checker_data.file)]) - - return True - -def get_checker_id(): - return 'check if xml readable' - - -def get_description(): - return 'check if xml readable.' - - -def check(checker_data: CheckerData) ->bool: - return check_read_XML(checker_data) \ No newline at end of file diff --git a/checks/schema_checks/__init__.py b/checks/schema_checks/__init__.py deleted file mode 100644 index 80cd936..0000000 --- a/checks/schema_checks/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -CHECKER_BUNDLE_NAME='Schema' -CHECKER_BUNDLE_DESCRIPTION='Checks schema of OSC file.' -CHECKER_BUNDLE_VERSION='1.0' -ORDER=['check_version', 'check_schema'] \ No newline at end of file diff --git a/checks/schema_checks/check_conventions.py b/checks/schema_checks/check_conventions.py deleted file mode 100644 index cb083c9..0000000 --- a/checks/schema_checks/check_conventions.py +++ /dev/null @@ -1,17 +0,0 @@ -from checker_data import CheckerData - - -def check_catalogs(checker_data: CheckerData) -> None: - pass - - -def get_checker_id(): - return 'ConventionChecker' - - -def get_description(): - return 'Checks conventions in OSC file.' - - -def check(checker_data: CheckerData) ->None: - check_catalogs(checker_data) \ No newline at end of file diff --git a/checks/schema_checks/check_schema.py b/checks/schema_checks/check_schema.py deleted file mode 100644 index 368d20d..0000000 --- a/checks/schema_checks/check_schema.py +++ /dev/null @@ -1,43 +0,0 @@ -from xosc.checks.schema_checks.schema.schema_files import SCHEMA_FILES -from result_report import IssueLevel, create_location_from_error, get_IssueLevel_from_str -from checker_data import CheckerData -from pathlib import Path -from lxml import etree - -import logging - - -def check_schema(checker_data: CheckerData) -> None: - logging.info(get_checker_id()) - major, minor = major, minor = checker_data.version - - format_name = checker_data.format_settings['name'] - schema_file = SCHEMA_FILES[f'{major}.{minor}'][0] - schema_location = Path(__file__).parent / 'schema' / schema_file - if schema_location.exists(): - try: - schema = etree.XMLSchema(etree.parse(schema_location)) - result = schema.validate(checker_data.data) - if not result: - for error in schema.error_log: - issue_level = get_IssueLevel_from_str(error.level_name) - checker_data.checker.gen_issue(IssueLevel.ERROR, - issue_level, create_location_from_error(error)) - except: - logging.exception(f'Cannot validate schema with OSC {major}.{minor}') - checker_data.checker.gen_issue(IssueLevel.ERROR, f'Cannot check {format_name} schema.') - else: - logging.exception(f'Cannot find matching schema location for {format_name} version {major}.{minor} in {schema_location}') - checker_data.checker.gen_issue(IssueLevel.ERROR, f'Cannot check {format_name} schema.') - - -def get_checker_id(): - return 'check schema' - - -def get_description(): - return 'check schema in OpenSCENARIO file' - - -def check(checker_data: CheckerData) ->None: - check_schema(checker_data) \ No newline at end of file diff --git a/checks/schema_checks/check_version.py b/checks/schema_checks/check_version.py deleted file mode 100644 index 3499158..0000000 --- a/checks/schema_checks/check_version.py +++ /dev/null @@ -1,60 +0,0 @@ -from xodr.checks.schema_checks.schema.schema_files import SCHEMA_FILES -from result_report import IssueLevel, FileLocation, create_location_from_element -from checker_data import CheckerData - -import logging - - -def check_version(checker_data: CheckerData) -> bool: - logging.info(get_checker_id()) - - major, minor = None, None - format_name = checker_data.format_settings['name'] - - try: - header = checker_data.data.find('.//FileHeader') - if header is None: - checker_data.checker.gen_issue(IssueLevel.ERROR, - f'Cannot determine {format_name} version. Could not find
in file.', FileLocation(checker_data.file)) - return False - else: - if 'revMajor' not in header.attrib: - checker_data.checker.gen_issue(IssueLevel.ERROR, - f'Cannot determine {format_name} version.
has no attribute revMajor.', - create_location_from_element(header)) - return False - - if 'revMinor' not in header.attrib: - checker_data.checker.gen_issue(IssueLevel.ERROR, - f'Cannot determine {format_name} version.
has no attribute revMinor.', - create_location_from_element(header)) - return False - - major = header.attrib['revMajor'] - minor = header.attrib['revMinor'] - try: - major = int(major) - minor = int(minor) - except: - logging.exception(f'Could not determine {format_name} version.') - checker_data.checker.gen_issue(IssueLevel.ERROR, f'Cannot determine {format_name} version. Version is not parsable to int') - return False - except: - logging.exception(f'Could not determine {format_name} version.') - checker_data.checker.gen_issue(IssueLevel.ERROR, f'Cannot determine {format_name} version.') - return False - - checker_data.version = major, minor # set version in checker data - return True - - -def get_checker_id(): - return 'check get version' - - -def get_description(): - return f'check if file version is readable.' - - -def check(checker_data: CheckerData) -> bool: - return check_version(checker_data) diff --git a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_Catalog.xsd b/checks/schema_checks/schema/0.9.1/OpenSCENARIO_Catalog.xsd deleted file mode 100644 index 0b872f1..0000000 --- a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_Catalog.xsd +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - XML Schema Definition for OpenSCENARIO Catalog XML files - Version Draft 0.9.1, (c)2017 by VIRES Simulationstechnologie GmbH, Germany - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_TypeDefs.xsd b/checks/schema_checks/schema/0.9.1/OpenSCENARIO_TypeDefs.xsd deleted file mode 100644 index 6280796..0000000 --- a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_TypeDefs.xsd +++ /dev/null @@ -1,1455 +0,0 @@ - - - - - - XML Schema Type Definitions for OpenSCENARIO XML files - Version Draft 0.9.1, (c)2017 by VIRES Simulationstechnologie GmbH, Germany - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_v0.9.1.xsd b/checks/schema_checks/schema/0.9.1/OpenSCENARIO_v0.9.1.xsd deleted file mode 100644 index 2442b43..0000000 --- a/checks/schema_checks/schema/0.9.1/OpenSCENARIO_v0.9.1.xsd +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - - - - XML Schema Definition for OpenSCENARIO XML files - Version 0.9.1, (c)2017 by VIRES Simulationstechnologie GmbH, Germany - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/checks/schema_checks/schema/schema_files.py b/checks/schema_checks/schema/schema_files.py deleted file mode 100644 index 1b71167..0000000 --- a/checks/schema_checks/schema/schema_files.py +++ /dev/null @@ -1,5 +0,0 @@ -SCHEMA_FILES = { - '0.9': ['0.9.1/OpenSCENARIO_v0.9.1.xsd'], - '1.0': ['1.0.0/OpenSCENARIO.xsd'], - '1.1': ['1.1.0/OpenSCENARIO.xsd'] -} diff --git a/config.json b/config.json deleted file mode 100644 index 2c69ef1..0000000 --- a/config.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "Semantic": { - "CatalogChecker": { - - }, - "EntitiyChecker": { - - } - }, - "Geometry": { - "PositionChecker": { - - } - }, - "Schema": { - "ConventionChecker": { - - }, - "SchemaChecker": { - - } - } -} \ No newline at end of file diff --git a/doc/documentation_checks_OpenSCENARIO.docx b/doc/documentation_checks_OpenSCENARIO.docx deleted file mode 100644 index be50153..0000000 Binary files a/doc/documentation_checks_OpenSCENARIO.docx and /dev/null differ diff --git a/doc/tables_checks_OpenSCENARIO.pdf b/doc/tables_checks_OpenSCENARIO.pdf deleted file mode 100644 index 552a658..0000000 Binary files a/doc/tables_checks_OpenSCENARIO.pdf and /dev/null differ diff --git a/format.json b/format.json deleted file mode 100644 index 7991dfd..0000000 --- a/format.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "OpenSCENARIO", - "extension": "xosc", - "shortcut": "OSC" -} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..82d3769 --- /dev/null +++ b/main.py @@ -0,0 +1,64 @@ +import argparse +import logging +from datetime import datetime + +from qc_baselib import Configuration, Result + +from qc_openscenario import constants +from qc_openscenario.checks.schema_checker import schema_checker +from qc_openscenario.checks.basic_checker import basic_checker + +logging.basicConfig(format="%(asctime)s - %(message)s", level=logging.INFO) + + +def args_entrypoint() -> argparse.Namespace: + parser = argparse.ArgumentParser( + prog="QC OpenScenario Checker", + description="This is a collection of scripts for checking validity of OpenScenario (.xosc) files.", + ) + + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("-d", "--default_config", action="store_true") + group.add_argument("-c", "--config_path") + + return parser.parse_args() + + +def main(): + args = args_entrypoint() + + logging.info("Initializing checks") + + if args.default_config: + raise RuntimeError("Not implemented.") + else: + config = Configuration() + config.load_from_file(xml_file_path=args.config_path) + + result = Result() + result.register_checker_bundle( + name=constants.BUNDLE_NAME, + build_date=datetime.today().strftime("%Y-%m-%d"), + description="OpenScenario checker bundle", + version=constants.BUNDLE_VERSION, + summary="", + ) + result.set_result_version(version=constants.BUNDLE_VERSION) + + # 1. Run basic checks + checker_data = basic_checker.run_checks(config=config, result=result) + + # 2. Run xml checks + schema_checker.run_checks(checker_data) + + result.write_to_file( + config.get_checker_bundle_param( + checker_bundle_name=constants.BUNDLE_NAME, param_name="resultFile" + ) + ) + + logging.info("Done") + + +if __name__ == "__main__": + main() diff --git a/qc_openscenario/__init__.py b/qc_openscenario/__init__.py new file mode 100644 index 0000000..137b3cc --- /dev/null +++ b/qc_openscenario/__init__.py @@ -0,0 +1,2 @@ +from . import constants as constants +from . import checks as checks diff --git a/qc_openscenario/checks/__init__.py b/qc_openscenario/checks/__init__.py new file mode 100644 index 0000000..971bcc2 --- /dev/null +++ b/qc_openscenario/checks/__init__.py @@ -0,0 +1,3 @@ +from . import schema_checker as schema_checker +from . import basic_checker as basic_checker +from . import models as models diff --git a/qc_openscenario/checks/basic_checker/__init__.py b/qc_openscenario/checks/basic_checker/__init__.py new file mode 100644 index 0000000..268a06d --- /dev/null +++ b/qc_openscenario/checks/basic_checker/__init__.py @@ -0,0 +1,3 @@ +from . import basic_constants as basic_constants +from . import basic_checker as basic_checker +from . import valid_xml_document as valid_xml_document diff --git a/qc_openscenario/checks/basic_checker/basic_checker.py b/qc_openscenario/checks/basic_checker/basic_checker.py new file mode 100644 index 0000000..b12d7ec --- /dev/null +++ b/qc_openscenario/checks/basic_checker/basic_checker.py @@ -0,0 +1,62 @@ +import logging + +from lxml import etree + +from qc_baselib import Configuration, Result, StatusType + +from qc_openscenario import constants +from qc_openscenario.checks import utils, models + +from qc_openscenario.checks.basic_checker import ( + basic_constants, + valid_xml_document, +) + + +def run_checks(config: Configuration, result: Result) -> models.CheckerData: + logging.info("Executing basic checks") + + result.register_checker( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=basic_constants.CHECKER_ID, + description="Check if basic properties of input file are properly set", + summary="", + ) + + xml_file_path = config.get_config_param("XoscFile") + is_xml = valid_xml_document.check_rule(xml_file_path, result) + + checker_data = None + + if not is_xml: + logging.error("Error in input xml!") + checker_data = models.CheckerData( + input_file_xml_root=None, + config=config, + result=result, + schema_version=None, + ) + + else: + root = etree.parse(config.get_config_param("XoscFile")) + xosc_schema_version = utils.get_standard_schema_version(root) + + checker_data = models.CheckerData( + input_file_xml_root=root, + config=config, + result=result, + schema_version=xosc_schema_version, + ) + + logging.info( + f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=basic_constants.CHECKER_ID)}" + ) + + # TODO: Add logic to deal with error or to skip it + result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=basic_constants.CHECKER_ID, + status=StatusType.COMPLETED, + ) + + return checker_data diff --git a/qc_openscenario/checks/basic_checker/basic_constants.py b/qc_openscenario/checks/basic_checker/basic_constants.py new file mode 100644 index 0000000..9846036 --- /dev/null +++ b/qc_openscenario/checks/basic_checker/basic_constants.py @@ -0,0 +1 @@ +CHECKER_ID = "basic_xosc" diff --git a/qc_openscenario/checks/basic_checker/valid_xml_document.py b/qc_openscenario/checks/basic_checker/valid_xml_document.py new file mode 100644 index 0000000..a351732 --- /dev/null +++ b/qc_openscenario/checks/basic_checker/valid_xml_document.py @@ -0,0 +1,71 @@ +import logging + +from dataclasses import dataclass +from typing import List + +from lxml import etree + +from qc_baselib import Configuration, Result, IssueSeverity + +from qc_openscenario import constants +from qc_openscenario.checks import utils, models + +from qc_openscenario.checks.basic_checker import basic_constants + + +def _is_xml_doc(file_path: str) -> tuple[bool, tuple[int, int]]: + try: + with open(file_path, "rb") as file: + xml_content = file.read() + etree.fromstring(xml_content) + logging.info("- It is an xml document.") + return True, None + except etree.XMLSyntaxError as e: + logging.error(f"- Error: {e}") + logging.error(f"- Error occurred at line {e.lineno}, column {e.offset}") + return False, (e.lineno, e.offset) + + +def check_rule(input_xml_file_path: str, result: Result) -> bool: + """ + Implements a rule to check if input file is a valid xml document + + More info at + - https://github.com/asam-ev/qc-openscenarioxml/issues/1 + """ + logging.info("Executing valid_xml_document check") + + rule_uid = result.register_rule( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=basic_constants.CHECKER_ID, + emanating_entity="asam.net", + standard="xosc", + definition_setting="1.0.0", + rule_full_name="xml.valid_xml_document", + ) + + is_valid, error_location = _is_xml_doc(input_xml_file_path) + + if not is_valid: + + issue_id = result.register_issue( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=basic_constants.CHECKER_ID, + description="Issue flagging when input file is not a valid xml document", + level=IssueSeverity.ERROR, + rule_uid=rule_uid, + ) + + result.add_file_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=basic_constants.CHECKER_ID, + issue_id=issue_id, + row=error_location[0], + column=error_location[1], + file_type="xosc", + description=f"Invalid xml detected", + ) + + return False + + return True diff --git a/qc_openscenario/checks/models.py b/qc_openscenario/checks/models.py new file mode 100644 index 0000000..aadc41a --- /dev/null +++ b/qc_openscenario/checks/models.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass +from lxml import etree + +from qc_baselib import Configuration, Result + + +@dataclass +class CheckerData: + input_file_xml_root: etree._ElementTree + config: Configuration + result: Result + schema_version: str diff --git a/qc_openscenario/checks/schema_checker/__init__.py b/qc_openscenario/checks/schema_checker/__init__.py new file mode 100644 index 0000000..9a52ec3 --- /dev/null +++ b/qc_openscenario/checks/schema_checker/__init__.py @@ -0,0 +1,3 @@ +from . import schema_constants as schema_constants +from . import schema_checker as schema_checker +from . import valid_schema as valid_schema diff --git a/qc_openscenario/checks/schema_checker/schema_checker.py b/qc_openscenario/checks/schema_checker/schema_checker.py new file mode 100644 index 0000000..a155bb9 --- /dev/null +++ b/qc_openscenario/checks/schema_checker/schema_checker.py @@ -0,0 +1,64 @@ +import logging + +from lxml import etree + +from qc_baselib import Configuration, Result, StatusType + +from qc_openscenario import constants +from qc_openscenario.checks import utils, models +from qc_openscenario.schema import schema_files + +from qc_openscenario.checks.schema_checker import ( + schema_constants, + valid_schema, +) + + +def run_checks(checker_data: models.CheckerData) -> None: + logging.info("Executing schema checks") + + checker_data.result.register_checker( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + description="Check if xml properties of input file are properly set", + summary="", + ) + + if checker_data.input_file_xml_root is None: + logging.error( + f"Invalid xml input file. Checker {schema_constants.CHECKER_ID} skipped" + ) + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + status=StatusType.SKIPPED, + ) + return + + if checker_data.schema_version not in schema_files.SCHEMA_FILES: + + logging.error( + f"Version {checker_data.schema_version} unsupported. Checker {schema_constants.CHECKER_ID} skipped" + ) + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + status=StatusType.SKIPPED, + ) + return + + rule_list = [valid_schema.check_rule] + + for rule in rule_list: + rule(checker_data=checker_data) + + logging.info( + f"Issues found - {checker_data.result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=schema_constants.CHECKER_ID)}" + ) + + # TODO: Add logic to deal with error or to skip it + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + status=StatusType.COMPLETED, + ) diff --git a/qc_openscenario/checks/schema_checker/schema_constants.py b/qc_openscenario/checks/schema_checker/schema_constants.py new file mode 100644 index 0000000..2a09d9c --- /dev/null +++ b/qc_openscenario/checks/schema_checker/schema_constants.py @@ -0,0 +1 @@ +CHECKER_ID = "schema_xosc" diff --git a/qc_openscenario/checks/schema_checker/valid_schema.py b/qc_openscenario/checks/schema_checker/valid_schema.py new file mode 100644 index 0000000..7c7d6dc --- /dev/null +++ b/qc_openscenario/checks/schema_checker/valid_schema.py @@ -0,0 +1,95 @@ +import os, logging + +from dataclasses import dataclass +from typing import List + +from lxml import etree + +from qc_baselib import Configuration, Result, IssueSeverity + +from qc_openscenario import constants +from qc_openscenario.schema import schema_files +from qc_openscenario.checks import utils, models + +from qc_openscenario.checks.schema_checker import schema_constants + + +def _is_schema_compliant( + xml_tree: etree._ElementTree, schema_file: str +) -> tuple[bool, etree._ListErrorLog]: + """Check if input xml tree is valid against the input schema file (.xsd) + + Args: + xml_file (etree._ElementTree): XML tree to test + schema_file (str): XSD file path containing the schema for the validation + + Returns: + bool: True if file pointed by xml_file is valid w.r.t. input schema file. False otherwise + """ + with open(schema_file, "rb") as schema_f: + schema_doc = etree.parse(schema_f) + schema = etree.XMLSchema(schema_doc) + + if schema.validate(xml_tree): + logging.info("- XML is valid.") + return True, None + else: + logging.error("- XML is invalid!") + for error in schema.error_log: + logging.error(f"- Error: {error.message}") + logging.error(f"- Line: {error.line}, Column: {error.column}") + + return False, schema.error_log + + +def check_rule(checker_data: models.CheckerData) -> None: + """ + Implements a rule to check if input file is valid according to OpenSCENARIO schema + + More info at + - https://github.com/asam-ev/qc-openscenarioxml/issues/2 + """ + logging.info("Executing valid_schema check") + + schema_version = checker_data.schema_version + if schema_version is None: + logging.info(f"- Version not found in the file. Skipping check") + return + + rule_uid = checker_data.result.register_rule( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + emanating_entity="asam.net", + standard="xosc", + definition_setting="1.0.0", + rule_full_name="xml.valid_schema", + ) + + schema_files_dict = schema_files.SCHEMA_FILES + + xsd_file = schema_files_dict[schema_version] + xsd_file_path = os.path.join("qc_openscenario", "schema", xsd_file) + + schema_compliant, errors = _is_schema_compliant( + checker_data.input_file_xml_root, xsd_file_path + ) + + if not schema_compliant: + issue_id = checker_data.result.register_issue( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + description="Issue flagging when input file does not follow its version schema", + level=IssueSeverity.ERROR, + rule_uid=rule_uid, + ) + + for error in errors: + checker_data.result.add_file_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + issue_id=issue_id, + row=error.line, + column=error.column, + file_type="xosc", + description=error.message, + ) diff --git a/qc_openscenario/checks/utils.py b/qc_openscenario/checks/utils.py new file mode 100644 index 0000000..afc4a77 --- /dev/null +++ b/qc_openscenario/checks/utils.py @@ -0,0 +1,11 @@ +from lxml import etree +from typing import Union + + +def get_standard_schema_version(root: etree._ElementTree) -> Union[str, None]: + header = root.find("FileHeader") + if header is None: + return None + header_attrib = header.attrib + version = f"{header_attrib['revMajor']}.{header_attrib['revMinor']}.0" + return version diff --git a/qc_openscenario/constants.py b/qc_openscenario/constants.py new file mode 100644 index 0000000..ebbd862 --- /dev/null +++ b/qc_openscenario/constants.py @@ -0,0 +1,2 @@ +BUNDLE_NAME = "xoscBundle" +BUNDLE_VERSION = "0.1.0" diff --git a/checks/schema_checks/schema/1.0.0/OpenSCENARIO.xsd b/qc_openscenario/schema/1.0.0/OpenSCENARIO.xsd similarity index 99% rename from checks/schema_checks/schema/1.0.0/OpenSCENARIO.xsd rename to qc_openscenario/schema/1.0.0/OpenSCENARIO.xsd index 0ffe54c..2cbf9dc 100644 --- a/checks/schema_checks/schema/1.0.0/OpenSCENARIO.xsd +++ b/qc_openscenario/schema/1.0.0/OpenSCENARIO.xsd @@ -8,8 +8,8 @@ ASAM OpenSCENARIO V1.0.0 Description of dynamic content in driving simulations -Any use is limited to the scope described in the ASAM license terms. -This file is distributable in accordance with the ASAM license terms. +Any use is limited to the scope described in the ASAM license terms. +This file is distributable in accordance with the ASAM license terms. See www.asam.net/license.html for further details. --> diff --git a/checks/schema_checks/schema/1.1.0/OpenSCENARIO.xsd b/qc_openscenario/schema/1.1.0/OpenSCENARIO.xsd similarity index 99% rename from checks/schema_checks/schema/1.1.0/OpenSCENARIO.xsd rename to qc_openscenario/schema/1.1.0/OpenSCENARIO.xsd index 8df8ed1..b5737f3 100644 --- a/checks/schema_checks/schema/1.1.0/OpenSCENARIO.xsd +++ b/qc_openscenario/schema/1.1.0/OpenSCENARIO.xsd @@ -7,8 +7,8 @@ __(c)__ by ASAM e.V., 2021 Description of dynamic content in driving simulations -Any use is limited to the scope described in the ASAM license terms. -This file is distributable in accordance with the ASAM license terms. +Any use is limited to the scope described in the ASAM license terms. +This file is distributable in accordance with the ASAM license terms. See www.asam.net/license.html for further details. --> diff --git a/qc_openscenario/schema/1.1.1/OpenSCENARIO.xsd b/qc_openscenario/schema/1.1.1/OpenSCENARIO.xsd new file mode 100644 index 0000000..a3a9fa3 --- /dev/null +++ b/qc_openscenario/schema/1.1.1/OpenSCENARIO.xsd @@ -0,0 +1,1842 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qc_openscenario/schema/1.2.0/OpenSCENARIO.xsd b/qc_openscenario/schema/1.2.0/OpenSCENARIO.xsd new file mode 100644 index 0000000..c4b18f8 --- /dev/null +++ b/qc_openscenario/schema/1.2.0/OpenSCENARIO.xsd @@ -0,0 +1,2418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + deprecated + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + deprecated + + + + + + + + + + + + + + + deprecated + + + deprecated + + + deprecated + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + deprecated + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + diff --git a/qc_openscenario/schema/1.3.0/OpenSCENARIO.xsd b/qc_openscenario/schema/1.3.0/OpenSCENARIO.xsd new file mode 100644 index 0000000..182f524 --- /dev/null +++ b/qc_openscenario/schema/1.3.0/OpenSCENARIO.xsd @@ -0,0 +1,2603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + + + + + + + + + + + + + + + + + deprecated + + + deprecated + + + deprecated + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + deprecated + + + + + + + + + + + + + + + deprecated + + + deprecated + + + deprecated + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + deprecated + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + deprecated + + + + + + + deprecated + + + + + + + + + + deprecated + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deprecated + + + + + + + + + + + + + + + + + + diff --git a/qc_openscenario/schema/__init__.py b/qc_openscenario/schema/__init__.py new file mode 100644 index 0000000..5791eb4 --- /dev/null +++ b/qc_openscenario/schema/__init__.py @@ -0,0 +1 @@ +from . import schema_files as schema_files diff --git a/qc_openscenario/schema/schema_files.py b/qc_openscenario/schema/schema_files.py new file mode 100644 index 0000000..c945c5a --- /dev/null +++ b/qc_openscenario/schema/schema_files.py @@ -0,0 +1,7 @@ +SCHEMA_FILES = { + "1.0.0": "1.0.0/OpenSCENARIO.xsd", + "1.1.0": "1.1.0/OpenSCENARIO.xsd", + "1.1.1": "1.1.1/OpenSCENARIO.xsd", + "1.2.0": "1.2.0/OpenSCENARIO.xsd", + "1.3.0": "1.3.0/OpenSCENARIO.xsd", +} diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..b4f398b --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1 @@ +black==24.4.2 diff --git a/requirements-tests.txt b/requirements-tests.txt new file mode 100644 index 0000000..c75731e --- /dev/null +++ b/requirements-tests.txt @@ -0,0 +1 @@ +pytest==8.2.2 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8e54612 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +lxml==5.2.2 +qc_baselib @ git+https://github.com/asam-ev/qc-baselib-py@develop diff --git a/tests/data/valid_schema/invalid_schema.xosc b/tests/data/valid_schema/invalid_schema.xosc new file mode 100644 index 0000000..5864e61 --- /dev/null +++ b/tests/data/valid_schema/invalid_schema.xosc @@ -0,0 +1,42 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/valid_schema/unsupported_schema.xosc b/tests/data/valid_schema/unsupported_schema.xosc new file mode 100644 index 0000000..9f8fe03 --- /dev/null +++ b/tests/data/valid_schema/unsupported_schema.xosc @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/tests/data/valid_schema/xml.valid_schema.negative.xosc b/tests/data/valid_schema/xml.valid_schema.negative.xosc new file mode 100644 index 0000000..2535f67 --- /dev/null +++ b/tests/data/valid_schema/xml.valid_schema.negative.xosc @@ -0,0 +1,38 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/valid_schema/xml.valid_schema.positive.xosc b/tests/data/valid_schema/xml.valid_schema.positive.xosc new file mode 100644 index 0000000..3565490 --- /dev/null +++ b/tests/data/valid_schema/xml.valid_schema.positive.xosc @@ -0,0 +1,37 @@ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/valid_xml_document/xml.valid_xml_document.negative.xosc b/tests/data/valid_xml_document/xml.valid_xml_document.negative.xosc new file mode 100644 index 0000000..dc0f27e --- /dev/null +++ b/tests/data/valid_xml_document/xml.valid_xml_document.negative.xosc @@ -0,0 +1,38 @@ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/valid_xml_document/xml.valid_xml_document.positive.xosc b/tests/data/valid_xml_document/xml.valid_xml_document.positive.xosc new file mode 100644 index 0000000..3565490 --- /dev/null +++ b/tests/data/valid_xml_document/xml.valid_xml_document.positive.xosc @@ -0,0 +1,37 @@ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_basic_checks.py b/tests/test_basic_checks.py new file mode 100644 index 0000000..cd8c20a --- /dev/null +++ b/tests/test_basic_checks.py @@ -0,0 +1,50 @@ +import os +import pytest +import test_utils +from qc_openscenario import constants +from qc_openscenario.checks.basic_checker import basic_constants +from qc_baselib import Result, IssueSeverity + + +def test_valid_xml_document_positive( + monkeypatch, +) -> None: + base_path = "tests/data/valid_xml_document/" + target_file_name = f"xml.valid_xml_document.positive.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + assert ( + len(result.get_issues_by_rule_uid("asam.net:xosc:1.0.0:xml.valid_xml_document")) + == 0 + ) + + test_utils.cleanup_files() + + +def test_valid_xml_document_negative( + monkeypatch, +) -> None: + base_path = "tests/data/valid_xml_document/" + target_file_name = f"xml.valid_xml_document.negative.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + xml_doc_issues = result.get_issues_by_rule_uid( + "asam.net:xosc:1.0.0:xml.valid_xml_document" + ) + assert len(xml_doc_issues) == 1 + assert xml_doc_issues[0].level == IssueSeverity.ERROR + test_utils.cleanup_files() diff --git a/tests/test_schema_checks.py b/tests/test_schema_checks.py new file mode 100644 index 0000000..339f6da --- /dev/null +++ b/tests/test_schema_checks.py @@ -0,0 +1,101 @@ +import os +import pytest +import test_utils +from qc_openscenario import constants +from qc_openscenario.checks.schema_checker import schema_constants +from qc_baselib import Result, IssueSeverity + + +def test_valid_schema_positive( + monkeypatch, +) -> None: + base_path = "tests/data/valid_schema/" + target_file_name = f"xml.valid_schema.positive.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + assert ( + len(result.get_issues_by_rule_uid("asam.net:xosc:1.0.0:xml.valid_schema")) + == 0 + ) + + test_utils.cleanup_files() + + +def test_valid_schema_negative( + monkeypatch, +) -> None: + base_path = "tests/data/valid_schema/" + target_file_name = f"xml.valid_schema.negative.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + assert ( + len(result.get_issues_by_rule_uid("asam.net:xosc:1.0.0:xml.valid_schema")) + == 0 + ) + test_utils.cleanup_files() + + +def test_unsupported_schema_version( + monkeypatch, +) -> None: + base_path = "tests/data/valid_schema/" + target_file_name = f"unsupported_schema.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + checker_result = result.get_checker_result( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + ) + assert ( + len(result.get_issues_by_rule_uid("asam.net:xosc:1.0.0:xml.valid_schema")) + == 0 + ) + test_utils.cleanup_files() + + +def test_invalid_schema( + monkeypatch, +) -> None: + base_path = "tests/data/valid_schema/" + target_file_name = f"invalid_schema.xosc" + target_file_path = os.path.join(base_path, target_file_name) + + test_utils.create_test_config(target_file_path) + + test_utils.launch_main(monkeypatch) + + result = Result() + result.load_from_file(test_utils.REPORT_FILE_PATH) + + checker_result = result.get_checker_result( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=schema_constants.CHECKER_ID, + ) + + xml_schema_issues = result.get_issues_by_rule_uid( + "asam.net:xosc:1.0.0:xml.valid_schema" + ) + assert len(xml_schema_issues) == 1 + assert xml_schema_issues[0].level == IssueSeverity.ERROR + test_utils.cleanup_files() diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..ecabe27 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,42 @@ +import os +import sys +import pytest +from typing import List +import main +from qc_openscenario import constants, checks +from qc_baselib import Configuration, Result + + +CONFIG_FILE_PATH = "bundle_config.xml" +REPORT_FILE_PATH = "xosc_bundle_report.xqar" + + +def create_test_config(target_file_path: str): + test_config = Configuration() + test_config.set_config_param(name="XoscFile", value=target_file_path) + test_config.register_checker_bundle(checker_bundle_name=constants.BUNDLE_NAME) + test_config.set_checker_bundle_param( + checker_bundle_name=constants.BUNDLE_NAME, + name="resultFile", + value=REPORT_FILE_PATH, + ) + + test_config.write_to_file(CONFIG_FILE_PATH) + + +def launch_main(monkeypatch): + monkeypatch.setattr( + sys, + "argv", + [ + "main.py", + "-c", + CONFIG_FILE_PATH, + ], + ) + main.main() + + +def cleanup_files(): + os.remove(REPORT_FILE_PATH) + os.remove(CONFIG_FILE_PATH)