diff --git a/.github/workflows/build-and-push-demo-pipeline.yaml b/.github/workflows/build-and-push-demo-pipeline.yaml new file mode 100644 index 00000000..e0662150 --- /dev/null +++ b/.github/workflows/build-and-push-demo-pipeline.yaml @@ -0,0 +1,44 @@ +name: Build and push demo pipeline image + +on: + schedule: + - cron: '0 4 * * *' # 04:00 AM UTC every day + +jobs: + build-and-push-demo: + runs-on: ubuntu-22.04 + + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: develop # @TODO remove it later + + - name: Get current date + id: date + run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile.linux + push: true + target: demo_pipeline + tags: | + ghcr.io/${{ github.repository_owner }}/qc-framework:demo-pipeline-latest + ghcr.io/${{ github.repository_owner }}/qc-framework:demo-pipeline-${{ env.DATE }} diff --git a/.github/workflows/build-on-change-linux-bare.yaml b/.github/workflows/build-on-change-linux-bare.yaml new file mode 100644 index 00000000..0430faf1 --- /dev/null +++ b/.github/workflows/build-on-change-linux-bare.yaml @@ -0,0 +1,113 @@ +name: Build framework on Linux Bare + +on: + # execute on every PR made targeting the branches bellow + pull_request: + branches: + - main + - develop # can be removed on main merge + paths: # we only include paths critical for building to avoid unnecessary runs + - src/** + - include/** + - scripts/cmake/** + - test/** + - .github/workflows/** + - doc/** + - runtime/** + - docker/** + + # execute on every push made targeting the branches bellow + push: + branches: + - main + - develop # can be removed on main merge + paths: # we only include paths critical for building to avoid unnecessary runs + - src/** + - include/** + - scripts/cmake/** + - test/** + - .github/workflows/** + - doc/** + - runtime/** + - docker/** + +jobs: + build-linux: + runs-on: ubuntu-22.04 + env: + TEST_ENABLED: ${{ github.event_name == 'pull_request' && 'ON' || 'OFF' }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Create dependencies build space + run: mkdir dependencies + shell: bash + + - name: Install dependencies + working-directory: dependencies + run: | + echo "Installing Dependencies..." + sudo apt update + sudo apt install \ + g++ \ + g++-10 \ + make \ + build-essential \ + cmake \ + libgtest-dev \ + qtbase5-dev \ + libqt5xmlpatterns5-dev \ + libxerces-c-dev \ + pkg-config + echo "Dependencies installed." + shell: bash + + - name: Build framework + # Currently this is building without the XSD file. If we want to expose + # the build artifact after, we might as well need to add the XSD file. + run: | + echo Building framework... + cmake -G "Unix Makefiles" -B./build -S . \ + -DCMAKE_INSTALL_PREFIX="/home/$(whoami)/qc-build" \ + -DENABLE_FUNCTIONAL_TESTS=$TEST_ENABLED -DXERCES_ROOT="/usr" \ + -DQt5_DIR="/usr/lib/x86_64-linux-gnu/cmake/Qt5/" \ + -DQt5XmlPatterns_DIR="/usr/lib/x86_64-linux-gnu/cmake/Qt5XmlPatterns/" + cmake --build ./build --target install --config Release -j4 + cmake --install ./build + echo Done. + shell: bash + + - name: Unit test execution + if: github.event_name == 'pull_request' + run: | + echo Starting tests... + + ctest --test-dir build -C Release + + echo All tests done. + shell: bash + + - name: Archive test results + if: github.event_name == 'pull_request' && (success() || failure()) + uses: actions/upload-artifact@v4 + with: + name: unit-test-report + path: ${{ github.workspace }}/build/Testing/Temporary/LastTest.log + + - name: Runtime test execution + if: github.event_name == 'pull_request' + run: | + mv build out_build + cp -r /home/$(whoami)/qc-build/bin bin + cp out_build/examples/checker_bundle_example/DemoCheckerBundle bin/ + cd runtime + python3 -m pip install -r requirements.txt + python3 -m pytest -rA > runtime_test.log + + - name: Archive runtime test results + if: github.event_name == 'pull_request' && (success() || failure()) + uses: actions/upload-artifact@v4 + with: + name: runtime-test-report + path: ${{ github.workspace }}/runtime/runtime_test.log diff --git a/.github/workflows/build-on-change-linux.yaml b/.github/workflows/build-on-change-linux-docker.yaml similarity index 87% rename from .github/workflows/build-on-change-linux.yaml rename to .github/workflows/build-on-change-linux-docker.yaml index e03b885f..f682c0a1 100644 --- a/.github/workflows/build-on-change-linux.yaml +++ b/.github/workflows/build-on-change-linux-docker.yaml @@ -1,16 +1,16 @@ -name: Build framework on Linux +name: Build framework on Linux Docker on: # execute on every PR made targeting the branches bellow pull_request: branches: - - master - - develop # can be removed on master merge + - main + - develop # can be removed on main merge paths: # we only include paths critical for building to avoid unnecessary runs - src/** - include/** - scripts/cmake/** - - tests/** + - test/** - .github/workflows/** - doc/** - runtime/** @@ -19,12 +19,13 @@ on: # execute on every push made targeting the branches bellow push: branches: - - master - - develop # can be removed on master merge + - main + - develop # can be removed on main merge paths: # we only include paths critical for building to avoid unnecessary runs - src/** - include/** - scripts/cmake/** + - test/** - .github/workflows/** - doc/** - runtime/** @@ -39,9 +40,9 @@ jobs: - name: Docker Build run: | - docker build -f docker/Dockerfile.linux --target unit_test -t unit_test . - docker build -f docker/Dockerfile.linux --target runtime_test -t runtime_test . - + docker build -f docker/Dockerfile.linux --target unit_test -t unit_test . + docker build -f docker/Dockerfile.linux --target runtime_test -t runtime_test . + - name: Unit test execution if: github.event_name == 'pull_request' run: | @@ -53,16 +54,15 @@ jobs: with: name: unit-test-report path: ${{ github.workspace }}/LastTest.log - + - name: Runtime test execution if: github.event_name == 'pull_request' run: | docker run -v ${{ github.workspace }}:/out --rm --name runtime_test runtime_test - + - name: Archive runtime test results if: github.event_name == 'pull_request' && (success() || failure()) uses: actions/upload-artifact@v4 with: name: runtime-test-report path: ${{ github.workspace }}/runtime_test.log - diff --git a/.github/workflows/build-on-change-windows.yaml b/.github/workflows/build-on-change-windows.yaml index 19f5a70e..8e686d05 100644 --- a/.github/workflows/build-on-change-windows.yaml +++ b/.github/workflows/build-on-change-windows.yaml @@ -4,8 +4,8 @@ on: # execute on every PR made targeting the branches bellow pull_request: branches: - - master - - develop # can be removed on master merge + - main + - develop # can be removed on main merge paths: # we only include paths critical for building to avoid unnecessary runs - src/** - include/** @@ -19,12 +19,13 @@ on: # execute on every push made targeting the branches bellow push: branches: - - master - - develop # can be removed on master merge + - main + - develop # can be removed on main merge paths: # we only include paths critical for building to avoid unnecessary runs - src/** - include/** - scripts/cmake/** + - test/** - .github/workflows/** - doc/** - runtime/** @@ -124,7 +125,7 @@ jobs: Write-Output "All unit tests done." shell: pwsh - + - name: Archive test results if: github.event_name == 'pull_request' && (success() || failure()) uses: actions/upload-artifact@v4 @@ -138,15 +139,12 @@ jobs: Write-Output "Starting runtime tests..." Rename-Item -path "$env:WORKING_PATH\qc-framework\qc-framework\build" -NewName "$env:WORKING_PATH\qc-framework\qc-framework\out_build" - Copy-Item -Path "$env:WORKING_PATH\QC-Framework-Out" -Destination "$env:WORKING_PATH\qc-framework\qc-framework\build" -Recurse - Copy-Item -Path "$env:WORKING_PATH\qc-framework\qc-framework\out_build\examples\checker_bundle_example\Release\DemoCheckerBundle.exe" -Destination "$env:WORKING_PATH\qc-framework\qc-framework\build\bin" - + Copy-Item -Path "$env:WORKING_PATH\QC-Framework-Out\bin" -Destination "$env:WORKING_PATH\qc-framework\qc-framework\bin" -Recurse + Copy-Item -Path "$env:WORKING_PATH\qc-framework\qc-framework\out_build\examples\checker_bundle_example\Release\DemoCheckerBundle.exe" -Destination "$env:WORKING_PATH\qc-framework\qc-framework\bin" + cd "$env:WORKING_PATH\qc-framework\qc-framework\runtime" python3 -m pip install -r requirements.txt python3 -m pytest Write-Output "All runtime tests done." shell: pwsh - - - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..036def72 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: no-commit-to-branch + args: [--branch=main] + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v16.0.6 + hooks: + - id: clang-format + files: \.(c|cc|cxx|cpp|h|hpp|hxx)$ + args: ["--style=file", "-i"] diff --git a/README.md b/README.md index d7226703..b0316cd6 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,17 @@ An architecture overview and documentation is provided in the # Getting Started +## Using Docker-based demo pipeline + +A [Docker-based demo pipeline](demo_pipeline/README.md) is provided to help users try out the latest development +of the framework, as well as the [OpenDrive](https://github.com/asam-ev/qc-opendrive/tree/develop) +and [OpenScenario XML](https://github.com/asam-ev/qc-openscenarioxml/tree/develop) checker bundles. + +## Build the framework locally + +As the framework is still under development, it is not recommended to build +it locally. A complete build instruction will be available in the near future. + The software can be build for Windows and Linux. Currently there are no pre-built binaries available for the framework. Follow the [build instructions](INSTALL.md) to create a runnable binary on your machine. diff --git a/demo_pipeline/README.md b/demo_pipeline/README.md new file mode 100644 index 00000000..36b13af1 --- /dev/null +++ b/demo_pipeline/README.md @@ -0,0 +1,82 @@ +# Demo pipeline + +The demo pipeline allow users to process OpenDRIVE and OpenSCENARIO XML files with respecting checkers and inspect the resulting `xqar` and `txt` files. + +### Download and run + +The demo pipeline is provided as a public Docker image in the Github container registry. + +To process a file, the `docker run` command below can be used and the following information can be specified: +- The input folder which contains the input file. +- The name the input file. +- The output folder where the output files can be saved. + +``` +docker run \ + -e INPUT_FILENAME=YOUR_INPUT_FILENAME \ + -v YOUR_INPUT_FOLDER:/input_directory \ + -v YOUR_OUTPUT_FOLDER:/out \ + -e USER_ID=$(id -u) \ + -e GROUP_ID=$(id -g) \ + --rm --name demo_pipeline ghcr.io/asam-ev/qc-framework:demo-pipeline-latest +``` + +E.g. To process the file at `/home/user/xodr_files/test_ramp.xosc` + +``` +docker run \ + -e INPUT_FILENAME=test_ramp.xosc \ + -v /home/user/xodr_files:/input_directory \ + -v /home/user/output:/out \ + -e USER_ID=$(id -u) \ + -e GROUP_ID=$(id -g) \ + --rm --name demo_pipeline ghcr.io/asam-ev/qc-framework:demo-pipeline-latest +``` + +Alternatively, you can go to the input folder and execute the following command, which requires only the input file name to be specified. The output will be saved in the same folder. + +``` +cd /home/user/xodr_files + +docker run \ + -e INPUT_FILENAME=test_ramp.xosc \ + -v $(pwd):/input_directory \ + -v $(pwd):/out \ + -e USER_ID=$(id -u) \ + -e GROUP_ID=$(id -g) \ + --rm --name demo_pipeline ghcr.io/asam-ev/qc-framework:demo-pipeline-latest +``` + +The docker image will automatically: +- Detect the type of file passed as input. +- Create the specific config according to [config schema](../doc/schema/config_format.xsd). +- Execute the runtime with specific checker, result pooling and text result application. + +Currently the demo_pipeline will clone and execute: + +- [OpenDRIVE checker @ develop branch](https://github.com/asam-ev/qc-opendrive/tree/develop) +- [OpenSCENARIO XML checker @ develop branch](https://github.com/asam-ev/qc-openscenarioxml/tree/develop) + +After the execution, in the specified output folder you will find: + +- Specific CheckerBundle `xqar` result file. +- ResultPooling `Result.xqar` result file. +- TextReport `Report.txt` text file. + +Some OpenDrive and OpenScenario XML test files are available to try out. +- [OpenDrive test files](https://github.com/asam-ev/qc-opendrive/tree/develop/tests/data) +- [OpenScenario XML test files](https://github.com/asam-ev/qc-openscenarioxml/tree/develop/tests/data) + +### Local build instructions + +In case of local build of demo_pipeline docker image, you can execute: + +``` +cd .. + +DOCKER_BUILDKIT=1 \ + docker build \ + -f docker/Dockerfile.linux \ + --target demo_pipeline \ + -t demo_pipeline . +``` diff --git a/demo_pipeline/configuration_generator.py b/demo_pipeline/configuration_generator.py new file mode 100644 index 00000000..d49e22f9 --- /dev/null +++ b/demo_pipeline/configuration_generator.py @@ -0,0 +1,64 @@ +# Copyright 2024, ASAM e.V. +# This Source Code Form is subject to the terms of the Mozilla +# Public License, v. 2.0. If a copy of the MPL was not distributed +# with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import sys, os, glob +from lxml import etree + +XODR_TEMPLATE_PATH = "/app/demo_pipeline/templates/xodr_template.xml" +XOSC_TEMPLATE_PATH = "/app/demo_pipeline/templates/xosc_template.xml" +GENERATED_CONFIG_PATH = "/tmp/generated_config" + + +def update_param_value(xml_file, name, new_value, output_file): + # Parse the XML file + tree = etree.parse(xml_file) + root = tree.getroot() + + # Find the Param element with the name attribute and update its value attribute + for param in root.findall(f".//Param[@name='{name}']"): + param.set("value", new_value) + + # Write the updated XML to the output file + tree.write(output_file, encoding="utf-8", pretty_print=True, xml_declaration=True) + + +def main(): + input_directory = "/input_directory" + input_filename = os.getenv("INPUT_FILENAME") + + full_input_path = os.path.join(input_directory, input_filename) + + os.makedirs(GENERATED_CONFIG_PATH, exist_ok=True) + + if not os.path.isfile(full_input_path): + print("No file specified as input. Please provide xosc or xodr file. Exiting...") + return + + print("Input file: ", full_input_path) + _, input_file_extension = os.path.splitext(full_input_path) + + if input_file_extension == ".xosc": + print("XOSC selected") + update_param_value( + XOSC_TEMPLATE_PATH, + "XoscFile", + full_input_path, + os.path.join(GENERATED_CONFIG_PATH, "config.xml"), + ) + elif input_file_extension == ".xodr": + print("XODR selected") + update_param_value( + XODR_TEMPLATE_PATH, + "XodrFile", + full_input_path, + os.path.join(GENERATED_CONFIG_PATH, "config.xml"), + ) + else: + print(f"Error in input file extension. Unrecognized {input_file_extension}") + return + + +if __name__ == "__main__": + main() diff --git a/demo_pipeline/requirements.txt b/demo_pipeline/requirements.txt new file mode 100644 index 00000000..dc26150e --- /dev/null +++ b/demo_pipeline/requirements.txt @@ -0,0 +1 @@ +lxml==5.2.2 diff --git a/demo_pipeline/run_pipeline.sh b/demo_pipeline/run_pipeline.sh new file mode 100755 index 00000000..09502612 --- /dev/null +++ b/demo_pipeline/run_pipeline.sh @@ -0,0 +1,16 @@ +# Copyright 2024, ASAM e.V. +# This Source Code Form is subject to the terms of the Mozilla +# Public License, v. 2.0. If a copy of the MPL was not distributed +# with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +python3 /app/demo_pipeline/configuration_generator.py + +python3 /app/framework/runtime/runtime/runtime.py \ + --config "/tmp/generated_config/config.xml" \ + --install_dir "/app/framework/bin" \ + --schema_dir "/app/framework/doc/schema" + +mkdir -p /out/qc-result-$INPUT_FILENAME +cp /app/framework/bin/*.xqar /out/qc-result-$INPUT_FILENAME +cp /app/framework/bin/*.txt /out/qc-result-$INPUT_FILENAME +chown -R $USER_ID:$GROUP_ID /out/qc-result-$INPUT_FILENAME diff --git a/demo_pipeline/templates/xodr_template.xml b/demo_pipeline/templates/xodr_template.xml new file mode 100644 index 00000000..ea9e99f1 --- /dev/null +++ b/demo_pipeline/templates/xodr_template.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/demo_pipeline/templates/xosc_template.xml b/demo_pipeline/templates/xosc_template.xml new file mode 100644 index 00000000..1e953045 --- /dev/null +++ b/demo_pipeline/templates/xosc_template.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/demo_pipeline/xodrBundle b/demo_pipeline/xodrBundle new file mode 100644 index 00000000..8376b70f --- /dev/null +++ b/demo_pipeline/xodrBundle @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright 2024, ASAM e.V. +# This Source Code Form is subject to the terms of the Mozilla +# Public License, v. 2.0. If a copy of the MPL was not distributed +# with this file, You can obtain one at https://mozilla.org/MPL/2.0/. +readonly CONFIG_FILE=$1 + +source /app/opendrive-venv/bin/activate +cd /app/qc-opendrive +python main.py -c $CONFIG_FILE +cp *.xqar /app/framework/bin/ diff --git a/demo_pipeline/xoscBundle b/demo_pipeline/xoscBundle new file mode 100644 index 00000000..44f896ea --- /dev/null +++ b/demo_pipeline/xoscBundle @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright 2024, ASAM e.V. +# This Source Code Form is subject to the terms of the Mozilla +# Public License, v. 2.0. If a copy of the MPL was not distributed +# with this file, You can obtain one at https://mozilla.org/MPL/2.0/. +readonly CONFIG_FILE=$1 + +source /app/openscenario-venv/bin/activate +cd /app/qc-openscenarioxml +python main.py -c $CONFIG_FILE +cp *.xqar /app/framework/bin/ diff --git a/doc/manual/cpp_base_library.md b/doc/manual/cpp_base_library.md index 87c2df9a..b685b128 100644 --- a/doc/manual/cpp_base_library.md +++ b/doc/manual/cpp_base_library.md @@ -56,8 +56,8 @@ The results that a Checker or CheckerBundle defines as output are stored in a report file. Reports contain information about the defects found, which are called issues. At least a description and an identifier is assigned to an issue. Additional meta information can be added to an issue: file location ( -cFileLocation ), XML file location ( cXMLLocation ) or road information ( -cRoadLocation ). This information is relevant for the ReportModule, for +cFileLocation ), XML file location ( cXMLLocation ) or inertial location ( +cInertialLocation ). This information is relevant for the ReportModule, for example, to point out meaningful errors in a GUI. The report XML file has to follow the schema file doc/schema/xqar_report_format.xsd. diff --git a/doc/manual/file_formats.md b/doc/manual/file_formats.md index 8bd39b44..758d0609 100644 --- a/doc/manual/file_formats.md +++ b/doc/manual/file_formats.md @@ -64,13 +64,9 @@ or semantic flaws. - **XmlLocation** - Addressing in a XML file with help of a XPath expression - Example: `` - - **RoadLocation** - - Position in road coordinates, the angles are calculated based on the - road orientation - - Example: `` - **InertialLocation** - Position in inertial coordinates - - Example: `` - Optional external files (e. g. Images of generated graphs such as speed over distance). Currently not supported. @@ -98,7 +94,7 @@ one called SyntaxChecker and one SemanticChecker. - + diff --git a/doc/manual/viewer_interface.md b/doc/manual/viewer_interface.md index 9bb0e744..ddc24b44 100644 --- a/doc/manual/viewer_interface.md +++ b/doc/manual/viewer_interface.md @@ -51,7 +51,7 @@ If you start a viewer the following functions are called in this order: If an error occurs during the startup process, the ReportGUI will call GetLastErrorMessage to print out the error in the ReportGUI itself. -ShowIssue is triggered if you click on a RoadLocation issue in the ReportGUI. +ShowIssue is triggered if you click on a InertialLocation issue in the ReportGUI. It will send the clicked issue and its location to the viewer. If the ReportGUI is closed a currently active Viewer receives the closeViewer diff --git a/doc/schema/config_format.xsd b/doc/schema/config_format.xsd index 90af61a5..286523ad 100644 --- a/doc/schema/config_format.xsd +++ b/doc/schema/config_format.xsd @@ -8,47 +8,49 @@ with this file, You can obtain one at https://mozilla.org/MPL/2.0/. --> - - - - - - - - - - - - - + + + + + + + + - - - - - - + + + + - - - - - - - - - + + + + + + - - - - - - - - - + + + + + + + + - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/doc/schema/xqar_report_format.xsd b/doc/schema/xqar_report_format.xsd index c4296f82..be5e484d 100644 --- a/doc/schema/xqar_report_format.xsd +++ b/doc/schema/xqar_report_format.xsd @@ -1,84 +1,119 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker/Dockerfile.linux b/docker/Dockerfile.linux index 9278ac52..cfd20556 100644 --- a/docker/Dockerfile.linux +++ b/docker/Dockerfile.linux @@ -8,7 +8,7 @@ FROM ubuntu:22.04 AS framework_builder SHELL ["/bin/bash", "-c"] RUN echo "Installing Dependencies..." && \ - apt update && apt install -y \ + apt update && apt install -y \ g++ \ g++-10 \ make \ @@ -18,11 +18,15 @@ RUN echo "Installing Dependencies..." && \ qtbase5-dev \ libqt5xmlpatterns5-dev \ libxerces-c-dev \ - pkg-config && \ - echo "Dependencies installed." + pkg-config \ + python3.10-venv \ + git && \ + echo "Dependencies installed." RUN mkdir -p /app/framework +WORKDIR /app + COPY examples /app/framework/examples COPY include /app/framework/include COPY scripts /app/framework/scripts @@ -33,6 +37,7 @@ COPY CMakeLists.txt /app/framework/CMakeLists.txt COPY version /app/framework/version COPY licenses /app/framework/licenses COPY runtime /app/framework/runtime +COPY demo_pipeline /app/demo_pipeline RUN echo "Building framework..." && \ cd /app/framework && \ @@ -46,37 +51,105 @@ RUN echo "Building framework..." && \ echo "Done." +# Clone, configure venv, install by copying bash script to install dir +RUN git clone --single-branch --branch develop https://github.com/asam-ev/qc-openscenarioxml.git && \ + python3 -m venv openscenario-venv && \ + source openscenario-venv/bin/activate && \ + python3 -m pip install --no-cache-dir -r qc-openscenarioxml/requirements.txt && \ + chmod +x /app/demo_pipeline/xoscBundle && \ + cp /app/demo_pipeline/xoscBundle /home/root/qc-build/bin/xoscBundle && \ + rm -rf /app/qc-openscenarioxml/.git + +# Clone, configure venv, install by copying bash script to install dir +RUN git clone --single-branch --branch develop https://github.com/asam-ev/qc-opendrive.git && \ + python3 -m venv opendrive-venv && \ + source opendrive-venv/bin/activate && \ + python3 -m pip install --no-cache-dir -r qc-opendrive/requirements.txt && \ + chmod +x /app/demo_pipeline/xodrBundle && \ + cp /app/demo_pipeline/xodrBundle /home/root/qc-build/bin/xodrBundle && \ + rm -rf /app/qc-opendrive/.git + +# Create and setup demo and runtime virtual envs +RUN python3 -m venv demo-pipeline-venv && \ + source demo-pipeline-venv/bin/activate && \ + python3 -m pip install --no-cache-dir -r /app/demo_pipeline/requirements.txt + +RUN python3 -m venv runtime-venv && \ + source runtime-venv/bin/activate && \ + python3 -m pip install --no-cache-dir -r /app/framework/runtime/requirements.txt + + # Runtime stage -FROM python:3.11.9-slim-bookworm as runtime_test +FROM ubuntu:22.04 as runtime_test +# Dependancies installation currently required by ResultPooling and TextReport modules RUN echo "Installing Qt..." && \ - apt update && apt install -y \ + apt update && apt install -y \ qtbase5-dev \ libqt5xmlpatterns5-dev \ libxerces-c-dev \ pkg-config && \ - echo "Dependencies installed." + echo "Dependencies installed." RUN mkdir -p /app -COPY runtime/requirements.txt /app/ -RUN pip install -r /app/requirements.txt - # For testing files COPY --from=framework_builder /app/framework /app/framework # Copy install directory in runtime image -COPY --from=framework_builder /home/root/qc-build /app/framework/build - +COPY --from=framework_builder /home/root/qc-build/bin /app/framework/bin +COPY --from=framework_builder /app/framework/build/test /app/framework/test # @NOTE this is just because DemoCheckerBundle is not installed by default -COPY --from=framework_builder /app/framework/build/examples/checker_bundle_example/DemoCheckerBundle /app/framework/build/bin/ - -ENV PYTHONUNBUFFERED=1 +COPY --from=framework_builder /app/framework/build/examples/checker_bundle_example/DemoCheckerBundle /app/framework/bin/ +# Virtual envs +COPY --from=framework_builder /app/runtime-venv /app/runtime-venv WORKDIR /app/framework/runtime/ -CMD python3 -m pytest -rA > runtime_test.log && cp /app/framework/runtime/runtime_test.log /out/runtime_test.log +SHELL ["/bin/bash", "-c"] + +CMD source /app/runtime-venv/bin/activate && python3 -m pytest -rA > runtime_test.log && cp /app/framework/runtime/runtime_test.log /out/runtime_test.log # Runtime stage FROM framework_builder as unit_test - CMD ctest --test-dir /app/framework/build -C Release && cp /app/framework/build/Testing/Temporary/LastTest.log /out/ + + +FROM ubuntu:22.04 as demo_pipeline + +LABEL org.opencontainers.image.source="https://github.com/asam-ev/qc-framework" +LABEL org.opencontainers.image.description="QC Framework demo pipeline" +LABEL org.opencontainers.image.licenses="MPL-2.0" + +# Dependencies installation currently required by ResultPooling and TextReport modules +RUN apt update && apt install -y \ + libqt5xmlpatterns5-dev \ + libxerces-c-dev && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /app + +# Virtual envs +COPY --from=framework_builder /app/runtime-venv /app/runtime-venv +COPY --from=framework_builder /app/demo-pipeline-venv /app/demo-pipeline-venv +COPY --from=framework_builder /app/opendrive-venv /app/opendrive-venv +COPY --from=framework_builder /app/openscenario-venv /app/openscenario-venv + +# Framework components +COPY --from=framework_builder /home/root/qc-build/bin /app/framework/bin +COPY --from=framework_builder /app/demo_pipeline /app/demo_pipeline +COPY --from=framework_builder /app/framework/runtime /app/framework/runtime + +# Framework schemas +COPY --from=framework_builder /app/framework/doc/schema /app/framework/doc/schema + +# OpenScenario XML checker bundle +COPY --from=framework_builder /app/qc-openscenarioxml/main.py /app/qc-openscenarioxml/main.py +COPY --from=framework_builder /app/qc-openscenarioxml/qc_openscenario /app/qc-openscenarioxml/qc_openscenario + +# OpenDrive XML checker bundle +COPY --from=framework_builder /app/qc-opendrive/main.py /app/qc-opendrive/main.py +COPY --from=framework_builder /app/qc-opendrive/qc_opendrive /app/qc-opendrive/qc_opendrive + +SHELL ["/bin/bash", "-c"] + +CMD source /app/demo-pipeline-venv/bin/activate && /app/demo_pipeline/run_pipeline.sh diff --git a/examples/checker_bundle_example/src/main.cpp b/examples/checker_bundle_example/src/main.cpp index 988d4bf7..b5c9ce5d 100644 --- a/examples/checker_bundle_example/src/main.cpp +++ b/examples/checker_bundle_example/src/main.cpp @@ -10,12 +10,18 @@ #include "common/result_format/c_checker.h" #include "common/result_format/c_checker_bundle.h" +#include "common/result_format/c_domain_specific_info.h" +#include "common/result_format/c_inertial_location.h" +#include "common/result_format/c_locations_container.h" #include "common/result_format/c_parameter_container.h" #include "common/result_format/c_result_container.h" +#include "common/result_format/c_rule.h" #include "common/config_format/c_configuration.h" #include "common/config_format/c_configuration_checker_bundle.h" +#include +#include // Main Programm int main(int argc, char *argv[]) { @@ -112,6 +118,47 @@ void ShowHelp(const std::string &toolPath) std::cout << "\n\n"; } +DOMElement *getRootFromString(const std::string &inputStr) +{ + XMLPlatformUtils::Initialize(); + + XercesDOMParser *parser = new XercesDOMParser(); + ErrorHandler *errHandler = (ErrorHandler *)new XERCES_CPP_NAMESPACE::HandlerBase(); + parser->setErrorHandler(errHandler); + + XERCES_CPP_NAMESPACE::MemBufInputSource memBufIS((const XMLByte *)inputStr.c_str(), inputStr.length(), "xmlBuffer", + false); + + try + { + parser->parse(memBufIS); + } + catch (const XMLException &e) + { + char *message = XMLString::transcode(e.getMessage()); + std::cerr << "XMLException: " << message << std::endl; + XMLString::release(&message); + return nullptr; + } + catch (const DOMException &e) + { + char *message = XMLString::transcode(e.msg); + std::cerr << "DOMException: " << message << std::endl; + XMLString::release(&message); + return nullptr; + } + catch (...) + { + std::cerr << "Unexpected exception" << std::endl; + return nullptr; + } + + DOMDocument *doc = parser->getDocument(); + DOMElement *rootElement = doc->getDocumentElement(); + + return rootElement; +} + void RunChecks(const cParameterContainer &inputParams) { // Now we define a result container which contains our results. @@ -125,10 +172,49 @@ void RunChecks(const cParameterContainer &inputParams) // Create a checker with a factory in the checker bundle cChecker *pExampeChecker = pExampleCheckerBundle->CreateChecker("exampleChecker", "This is a description"); - // Lets add now an issue pExampeChecker->AddIssue(new cIssue("This is an information from the demo usecase", INFO_LVL)); + // Create a test checker with an inertial location + cChecker *pExampleInertialChecker = + pExampleCheckerBundle->CreateChecker("exampleInertialChecker", "This is a description of inertial checker"); + std::list listLoc; + listLoc.push_back(new cLocationsContainer("inertial position", new cInertialLocation(1.0, 2.0, 3.0))); + pExampleInertialChecker->AddIssue(new cIssue("This is an information from the demo usecase", INFO_LVL, listLoc)); + + // Create a test checker with RuleUID and metadata + cChecker *pExampleRuleUIDChecker = + pExampleCheckerBundle->CreateChecker("exampleRuleUIDChecker", "This is a description of ruleUID checker"); + pExampleRuleUIDChecker->AddRule(new cRule("test.com::qwerty.qwerty")); + pExampleRuleUIDChecker->AddMetadata( + new cMetadata("run date", "2024/06/06", "Date in which the checker was executed")); + pExampleRuleUIDChecker->AddMetadata( + new cMetadata("reference project", "project01", "Name of the project that created the checker")); + + // Create a test checker with Issue and RuleUID + cChecker *pExampleIssueRuleChecker = pExampleCheckerBundle->CreateChecker( + "exampleIssueRuleChecker", "This is a description of checker with issue and the involved ruleUID"); + + pExampleIssueRuleChecker->AddIssue( + new cIssue("This is an information from the demo usecase", ERROR_LVL, "test.com::qwerty.qwerty")); + + // Create a test checker with Issue and RuleUID + cChecker *pSkippedChecker = pExampleCheckerBundle->CreateChecker( + "exampleSkippedChecker", "This is a description of checker with skipped status", "Skipped execution", + "skipped"); + + // Create a test checker with an inertial location + cChecker *pExampleDomainChecker = pExampleCheckerBundle->CreateChecker( + "exampleDomainChecker", "This is a description of example domain info checker"); + std::list listDomainSpecificInfo; + + std::string xmlString = + ""; + + listDomainSpecificInfo.push_back(new cDomainSpecificInfo(getRootFromString(xmlString), "domain info test")); + pExampleDomainChecker->AddIssue( + new cIssue("This is an information from the demo usecase", INFO_LVL, listDomainSpecificInfo)); + // Lets add a summary for the checker bundle unsigned int issueCount = pExampleCheckerBundle->GetIssueCount(); std::stringstream ssSummaryString; diff --git a/include/common/result_format/c_checker.h b/include/common/result_format/c_checker.h index f45f7527..23ecf356 100644 --- a/include/common/result_format/c_checker.h +++ b/include/common/result_format/c_checker.h @@ -12,7 +12,9 @@ #include "../util.h" #include "../xml/util_xerces.h" #include "c_issue.h" +#include "c_metadata.h" #include "c_parameter_container.h" +#include "c_rule.h" #include #include @@ -20,6 +22,8 @@ // Forward declaration to avoid problems with circular dependencies (especially under Linux) class cCheckerBundle; class cIssue; +class cRule; +class cMetadata; /* * Definition of a basic checker @@ -28,12 +32,15 @@ class cChecker { friend class cCheckerBundle; friend class cIssue; + friend class cRule; + friend class cMetadata; public: static const XMLCh *TAG_CHECKER; static const XMLCh *ATTR_CHECKER_ID; static const XMLCh *ATTR_DESCRIPTION; static const XMLCh *ATTR_SUMMARY; + static const XMLCh *ATTR_STATUS; // Returns the checker id std::string GetCheckerID() const; @@ -41,6 +48,9 @@ class cChecker // Returns the summary std::string GetSummary() const; + // Returns the status + std::string GetStatus() const; + // Returns the description std::string GetDescription() const; @@ -50,12 +60,27 @@ class cChecker // sets the summary void SetSummary(const std::string &strSummary); + // sets the status + void SetStatus(const std::string &eStatus); + /* * Adds an issue to the checker results * \param instance if the result */ cIssue *AddIssue(cIssue *const issueToAdd); + /* + * Adds an rule to the checker results + * \param instance if the result + */ + cRule *AddRule(cRule *const ruleToAdd); + + /* + * Adds an metadata info to the checker results + * \param instance if the result + */ + cMetadata *AddMetadata(cMetadata *const metadataToAdd); + // Clears all issues from the container void Clear(); @@ -71,12 +96,24 @@ class cChecker // Counts the Issues unsigned int GetIssueCount(); + // Counts the Rules + unsigned int GetRuleCount(); + + // Counts the Rules + unsigned int GetMetadataCount(); + // Updates the summary void UpdateSummary(); // Returns the issues std::list GetIssues(); + // Returns the rules + std::list GetRules(); + + // Returns the rules + std::list GetMetadata(); + // Processes every issue and does a defined processing void DoProcessing(void (*funcIzteratorPtr)(cIssue *)); @@ -149,13 +186,15 @@ class cChecker protected: // Creates a new checker instance - cChecker(const std::string &strCheckerId, const std::string &strDescription, const std::string &strSummary) - : m_Bundle(nullptr), m_CheckerId(strCheckerId), m_Description(strDescription), m_Summary(strSummary) + cChecker(const std::string &strCheckerId, const std::string &strDescription, const std::string &strSummary, + const std::string &strStatus) + : m_Bundle(nullptr), m_CheckerId(strCheckerId), m_Description(strDescription), m_Summary(strSummary), + m_Status(strStatus) { } // Creates a new checker instance - cChecker() : m_Bundle(nullptr), m_CheckerId(""), m_Description(""), m_Summary("") + cChecker() : m_Bundle(nullptr), m_CheckerId(""), m_Description(""), m_Summary(""), m_Status("completed") { } @@ -170,9 +209,12 @@ class cChecker std::string m_CheckerId; std::string m_Description; std::string m_Summary; + std::string m_Status; cCheckerBundle *m_Bundle; std::list m_Issues; + std::list m_Rules; + std::list m_Metadata; cParameterContainer m_Params; }; diff --git a/include/common/result_format/c_checker_bundle.h b/include/common/result_format/c_checker_bundle.h index e6b3d525..12964b4c 100644 --- a/include/common/result_format/c_checker_bundle.h +++ b/include/common/result_format/c_checker_bundle.h @@ -12,6 +12,7 @@ #include "../util.h" #include "../xml/util_xerces.h" #include "c_parameter_container.h" +#include "common/result_format/c_checker.h" #include "common/result_format/c_issue.h" // Forward declaration to avoid problems with circular dependencies (especially under Linux) @@ -56,7 +57,7 @@ class cCheckerBundle // Adds a new checker cChecker *CreateChecker(const std::string &checkerId, const std::string &strDescription = "", - const std::string &strSummary = ""); + const std::string &strSummary = "", const std::string &strStatus = "completed"); /* * Adds an amout of issues to the checker bundle diff --git a/include/common/result_format/c_domain_specific_info.h b/include/common/result_format/c_domain_specific_info.h new file mode 100644 index 00000000..3963b9cd --- /dev/null +++ b/include/common/result_format/c_domain_specific_info.h @@ -0,0 +1,67 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef cDomainSpecificInfo_h__ +#define cDomainSpecificInfo_h__ + +#include "../xml/util_xerces.h" +#include "string" +#include + +XERCES_CPP_NAMESPACE_USE + +/* + * Definition of domain specific information. This can be used to represent custom info in result file + */ +class cDomainSpecificInfo +{ + + public: + static const XMLCh *TAG_DOMAIN_SPECIFIC_INFO; + static const XMLCh *ATTR_NAME; + /* + * Creates a new instance of cDomainSpecificInfo + * \param inputRoot: xml tree root for the initialization + * \param name: Name of the tag + */ + cDomainSpecificInfo(DOMElement *inputRoot, const std::string &name = "") : m_Name(name) + { + // Get the original document's implementation + DOMImplementation *impl = inputRoot->getOwnerDocument()->getImplementation(); + // Create a new document to own the cloned element + m_Doc = impl->createDocument(); + // Import the element into the new document, effectively cloning it + m_Root = dynamic_cast(m_Doc->importNode(inputRoot, true)); + } + + // Serialize this information + virtual XERCES_CPP_NAMESPACE::DOMElement *WriteXML(XERCES_CPP_NAMESPACE::DOMDocument *p_resultDocument); + + // Unserialize this information + static cDomainSpecificInfo *ParseFromXML(XERCES_CPP_NAMESPACE::DOMNode *pXMLNode, + XERCES_CPP_NAMESPACE::DOMElement *pXMLElement); + + // Returns the root + DOMElement *GetRoot() const; + // Returns the name + std::string GetName() const; + DOMDocument *GetDoc() const; + + ~cDomainSpecificInfo(); + + protected: + DOMElement *m_Root; + DOMDocument *m_Doc; + std::string m_Name; + + private: + cDomainSpecificInfo(); + cDomainSpecificInfo(const cDomainSpecificInfo &); +}; + +#endif diff --git a/include/common/result_format/c_inertial_location.h b/include/common/result_format/c_inertial_location.h index ebde1348..c0230b87 100644 --- a/include/common/result_format/c_inertial_location.h +++ b/include/common/result_format/c_inertial_location.h @@ -24,9 +24,6 @@ class cInertialLocation : public cExtendedInformation static const XMLCh *ATTR_X; static const XMLCh *ATTR_Y; static const XMLCh *ATTR_Z; - static const XMLCh *ATTR_H; - static const XMLCh *ATTR_P; - static const XMLCh *ATTR_R; /* * Creates a new instance of cInertialLocation @@ -36,19 +33,7 @@ class cInertialLocation : public cExtendedInformation * \param description: Additional description */ cInertialLocation(double x, double y, double z) - : cExtendedInformation("InertialLocation"), m_X(x), m_Y(y), m_Z(z), m_H(0.0), m_P(0.0), m_R(0.0) - { - } - - /* - * Creates a new instance of cInertialLocation - * \param x: X of the position in inertial coordinate system - * \param y: Y of the position in inertial coordinate system - * \param z: Z of the position in inertial coordinate system - * \param description: Additional description - */ - cInertialLocation(double x, double y, double z, double head, double pitch, double roll) - : cExtendedInformation("InertialLocation"), m_X(x), m_Y(y), m_Z(z), m_H(head), m_P(pitch), m_R(roll) + : cExtendedInformation("InertialLocation"), m_X(x), m_Y(y), m_Z(z) { } @@ -68,18 +53,10 @@ class cInertialLocation : public cExtendedInformation // Returns the Z double GetZ() const; - // Returns the Head - double GetHead() const; - - // Returns the Pitch - double GetPitch() const; - - // Returns the Roll - double GetRoll() const; + protected: double m_X, m_Y, m_Z; - double m_H, m_P, m_R; private: cInertialLocation(); diff --git a/include/common/result_format/c_issue.h b/include/common/result_format/c_issue.h index 2bd744ec..5299ee32 100644 --- a/include/common/result_format/c_issue.h +++ b/include/common/result_format/c_issue.h @@ -19,6 +19,7 @@ class cChecker; class cLocationsContainer; +class cDomainSpecificInfo; /* * Definition of issue levels @@ -42,6 +43,7 @@ class cIssue : public IResult static const XMLCh *ATTR_ISSUE_ID; static const XMLCh *ATTR_DESCRIPTION; static const XMLCh *ATTR_LEVEL; + static const XMLCh *ATTR_RULEUID; static const std::map issueLevelToString; @@ -49,7 +51,8 @@ class cIssue : public IResult * Creates a new Issue * */ - cIssue(const std::string &description, eIssueLevel infoLvl, cLocationsContainer *locationsContainer = nullptr); + cIssue(const std::string &description, eIssueLevel infoLvl, const std::string &ruleUID = "", + cLocationsContainer *locationsContainer = nullptr, cDomainSpecificInfo *domainSpecificInfo = nullptr); /* * Creates a new Issue @@ -57,14 +60,26 @@ class cIssue : public IResult */ cIssue(const std::string &description, eIssueLevel infoLvl, std::list listLoc); + cIssue(const std::string &description, eIssueLevel infoLvl, + std::list listDomainSpecificInfo); + + cIssue(const std::string &description, eIssueLevel infoLvl, const std::string &ruleUID, + std::list listLoc); + ~cIssue(); // Adds extendesd information to this issue void AddLocationsContainer(cLocationsContainer *locationsContainer); + // Adds domain specific info to this issue + void AddDomainSpecificInfo(cDomainSpecificInfo *domainSpecificInfo); + // Adds extendesd information to this issue void AddLocationsContainer(std::list listLoc); + // Adds domain specific info to this issue + void AddDomainSpecificInfo(std::list listDomainSpecificInfo); + // Write the xml for this issue virtual DOMElement *WriteXML(XERCES_CPP_NAMESPACE::DOMDocument *p_resultDocument); @@ -74,6 +89,8 @@ class cIssue : public IResult // Returns th count of locations std::size_t GetLocationsCount() const; + size_t GetDomainSpecificCount() const; + // Assigns an issue to a checker void AssignChecker(cChecker *checkerToAssign); @@ -95,9 +112,15 @@ class cIssue : public IResult // Sets the level void SetLevel(eIssueLevel level); + // Sets the RuleUID + void SetRuleUID(const std::string &strRuleUID); + // Returns the description std::string GetDescription() const; + // Returns the ruleUID + std::string GetRuleUID() const; + // Returns the issue level eIssueLevel GetIssueLevel() const; @@ -110,9 +133,14 @@ class cIssue : public IResult // Returns true if this issue has location containers bool HasLocations() const; + bool HasDomainSpecificInfo() const; + // Returns all extended informations std::list GetLocationsContainer() const; + // Returns all domain specific info + std::list GetDomainSpecificInfo() const; + // Returns the checker this issue belongs to cChecker *GetChecker() const; @@ -129,9 +157,11 @@ class cIssue : public IResult unsigned long long m_Id; std::string m_Description; eIssueLevel m_IssueLevel; + std::string m_RuleUID; cChecker *m_Checker; std::list m_Locations; + std::list m_DomainSpecificInfo; }; std::string PrintIssueLevel(const eIssueLevel); diff --git a/include/common/result_format/c_metadata.h b/include/common/result_format/c_metadata.h new file mode 100644 index 00000000..f44baaf3 --- /dev/null +++ b/include/common/result_format/c_metadata.h @@ -0,0 +1,64 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef cMetadata_h__ +#define cMetadata_h__ + +#include "../xml/util_xerces.h" +#include "string" + +class cChecker; + +class cMetadata +{ + + public: + static const XMLCh *TAG_NAME; + static const XMLCh *ATTR_KEY; + static const XMLCh *ATTR_VALUE; + static const XMLCh *ATTR_DESCRIPTION; + + /* + * Creates a new instance of cMetadata + * \param m_Key: metadata key + * \param m_Value: metadata value + * \param m_Description: metadata description + * \param description: Additional description + */ + cMetadata(const std::string &input_key, const std::string &input_value, const std::string &input_description) + : m_Key(input_key), m_Value(input_value), m_Description(input_description) + { + } + + // Serialize this information + virtual XERCES_CPP_NAMESPACE::DOMElement *WriteXML(XERCES_CPP_NAMESPACE::DOMDocument *p_resultDocument); + + // Unserialize this information + static cMetadata *ParseFromXML(XERCES_CPP_NAMESPACE::DOMNode *pXMLNode, + XERCES_CPP_NAMESPACE::DOMElement *pXMLElement, cChecker *checker); + + // Assigns an issue to a checker + void AssignChecker(cChecker *checkerToAssign); + + // Returns the Key + std::string GetKey() const; + // Returns the Value + std::string GetValue() const; + // Returns the Description + std::string GetDescription() const; + + protected: + std::string m_Key, m_Value, m_Description; + cChecker *m_Checker; + + private: + cMetadata(); + cMetadata(const cMetadata &); +}; + +#endif diff --git a/include/common/result_format/c_road_location.h b/include/common/result_format/c_road_location.h deleted file mode 100644 index ca5dcceb..00000000 --- a/include/common/result_format/c_road_location.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2023 CARIAD SE. - * - * This Source Code Form is subject to the terms of the Mozilla - * Public License, v. 2.0. If a copy of the MPL was not distributed - * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -#ifndef cRoadLocation_h__ -#define cRoadLocation_h__ - -#include "../xml/util_xerces.h" -#include "c_extended_information.h" - -/* - * Definition of additional road location. This can be used to debug special positions - * in dbqa framework. - */ -class cRoadLocation : public cExtendedInformation -{ - - public: - static const XMLCh *TAG_NAME; - static const XMLCh *ATTR_ROAD_ID; - static const XMLCh *ATTR_S; - static const XMLCh *ATTR_T; - - /* - * Creates a new instance of cRoadLocation - * \param roadId: ID of the road in OpenDrive - * \param s: S of the road in OpenDrive - * \param t: T of the road in OpenDrive - */ - cRoadLocation(const std::string &roadId, float s = 0.0f, float t = 0.0f) : cExtendedInformation("RoadLocation") - { - m_RoadID = roadId; - m_S = s; - m_T = t; - } - - // Serialize this information - virtual XERCES_CPP_NAMESPACE::DOMElement *WriteXML(XERCES_CPP_NAMESPACE::DOMDocument *p_resultDocument); - - // Unserialize this information - static cRoadLocation *ParseFromXML(XERCES_CPP_NAMESPACE::DOMNode *pXMLNode, - XERCES_CPP_NAMESPACE::DOMElement *pXMLElement); - - // Returns the xPath - std::string GetRoadID() const; - - // Returns the road Id as integer - void GetRoadID(int &roadId) const; - - // Returns the s of the road - float GetS() const; - - // Returns the t of the road - float GetT() const; - - protected: - std::string m_RoadID; - float m_S; - float m_T; - - private: - cRoadLocation(); - cRoadLocation(const cRoadLocation &); -}; - -#endif diff --git a/include/common/result_format/c_rule.h b/include/common/result_format/c_rule.h new file mode 100644 index 00000000..2e3bf79d --- /dev/null +++ b/include/common/result_format/c_rule.h @@ -0,0 +1,58 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef cRule_h__ +#define cRule_h__ + +#include "../xml/util_xerces.h" +#include "string" + +class cChecker; +/* + * Definition of additional interial location information. This can be used to debug special positions + * in dbqa framework + */ +class cRule +{ + + public: + static const XMLCh *TAG_NAME; + static const XMLCh *ATTR_RULE_UID; + + /* + * Creates a new instance of cRule + * \param m_RuleUID: rule id + * \param description: Additional description + */ + cRule(const std::string &input_string) : m_RuleUID(input_string) + { + } + + // Serialize this information + virtual XERCES_CPP_NAMESPACE::DOMElement *WriteXML(XERCES_CPP_NAMESPACE::DOMDocument *p_resultDocument); + + // Unserialize this information + static cRule *ParseFromXML(XERCES_CPP_NAMESPACE::DOMNode *pXMLNode, XERCES_CPP_NAMESPACE::DOMElement *pXMLElement, + cChecker *checker); + + // Assigns an issue to a checker + void AssignChecker(cChecker *checkerToAssign); + + // Returns the X + std::string GetRuleUID() const; + + protected: + std::string m_RuleUID; + cChecker *m_Checker; + + private: + cRule(); + cRule(const cRule &); +}; + +#endif diff --git a/runtime/tests/test_data/3steps_config.xml b/runtime/tests/test_data/3steps_config.xml new file mode 100644 index 00000000..74c0f058 --- /dev/null +++ b/runtime/tests/test_data/3steps_config.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/runtime/tests/test_runtime.py b/runtime/tests/test_runtime.py index 7b909fc8..80a68a8a 100644 --- a/runtime/tests/test_runtime.py +++ b/runtime/tests/test_runtime.py @@ -57,15 +57,16 @@ def check_node_exists(xml_file: str, node_name: str) -> bool: def test_runtime_execution(): - install_dir = os.path.join("..", "build", "bin") + start_wd = os.getcwd() + install_dir = os.path.join("..", "bin") os.chdir(install_dir) config_xml = os.path.join( - "..", "..", "runtime", "tests", "test_data", "DemoCheckerBundle_config.xml" + "..", "runtime", "tests", "test_data", "DemoCheckerBundle_config.xml" ) - schema_dir = os.path.join("..", "..", "doc", "schema") - runtime_script = os.path.join("..", "..", "runtime", "runtime", "runtime.py") + schema_dir = os.path.join("..", "doc", "schema") + runtime_script = os.path.join("..", "runtime", "runtime", "runtime.py") process = subprocess.Popen( f"python3 {runtime_script} --config={config_xml} --install_dir={os.getcwd()} --schema_dir={schema_dir}", @@ -93,3 +94,43 @@ def test_runtime_execution(): # Check that at least one node called "Issue" is present in the result node_name = "Issue" assert check_node_exists(result_file, node_name) + + os.chdir(start_wd) + + +def test_3steps_config(): + + start_wd = os.getcwd() + install_dir = os.path.join("..", "bin") + os.chdir(install_dir) + + config_xml = os.path.join("..", "runtime", "tests", "test_data", "3steps_config.xml") + + schema_dir = os.path.join("..", "doc", "schema") + runtime_script = os.path.join("..", "runtime", "runtime", "runtime.py") + + process = subprocess.Popen( + f"python3 {runtime_script} --config={config_xml} --install_dir={os.getcwd()} --schema_dir={schema_dir}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=os.getcwd(), + ) + stdout, stderr = process.communicate() + exit_code = process.returncode + if exit_code == 0: + print("Command executed successfully.") + print("Output:") + print(stdout.decode()) + else: + print("Error occurred while executing the command.") + print("Error message:") + print(stderr.decode()) + # Check that result file is correctly generated + result_file = os.path.join("Result.xqar") + assert os.path.isfile(result_file) + # Check that report txt file is correctly generated + result_file = os.path.join("Report.txt") + assert os.path.isfile(result_file) + + os.chdir(start_wd) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7f0f2589..9cfe7b49 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -21,7 +21,6 @@ add_library(qc4openx-common STATIC src/result_format/c_extended_information.cpp src/result_format/c_file_location.cpp src/result_format/c_inertial_location.cpp - src/result_format/c_road_location.cpp src/result_format/c_xml_location.cpp src/result_format/c_parameter_container.cpp src/result_format/c_locations_container.cpp @@ -30,6 +29,9 @@ add_library(qc4openx-common STATIC src/config_format/c_configuration_checker_bundle.cpp src/config_format/c_configuration_report_module.cpp src/xml/c_x_path_evaluator.cpp + src/result_format/c_rule.cpp + src/result_format/c_metadata.cpp + src/result_format/c_domain_specific_info.cpp ) target_include_directories(qc4openx-common PUBLIC ${PROJECT_SOURCE_DIR}/include diff --git a/src/common/src/config_format/c_configuration.cpp b/src/common/src/config_format/c_configuration.cpp index 55d86fd0..6b2d9222 100644 --- a/src/common/src/config_format/c_configuration.cpp +++ b/src/common/src/config_format/c_configuration.cpp @@ -21,7 +21,6 @@ cConfiguration::~cConfiguration() bool cConfiguration::ParseFromXML(cConfiguration *config, const std::string &configFilePath) { - XMLPlatformUtils::Initialize(); if (!CheckIfFileExists(configFilePath)) return false; @@ -70,9 +69,6 @@ bool cConfiguration::ParseFromXML(cConfiguration *config, const std::string &con } delete pDomParser; - - XMLPlatformUtils::Terminate(); - return true; } @@ -88,7 +84,6 @@ cConfiguration *cConfiguration::ParseFromXML(const std::string &configFilePath) void cConfiguration::WriteConfigurationToFile(const std::string &filePath) { - XMLPlatformUtils::Initialize(); DOMImplementation *p_DOMImplementationCore = DOMImplementationRegistry::getDOMImplementation(XMLString::transcode("core")); DOMLSSerializer *p_DOMSerializer = ((DOMImplementationLS *)p_DOMImplementationCore)->createLSSerializer(); @@ -130,8 +125,6 @@ void cConfiguration::WriteConfigurationToFile(const std::string &filePath) p_resultDocument->release(); theOutput->release(); p_DOMSerializer->release(); - - XMLPlatformUtils::Terminate(); } void cConfiguration::ProcessDomNode(DOMNode *const nodeToProcess, cConfiguration *const cConfig) const diff --git a/src/common/src/result_format/c_checker.cpp b/src/common/src/result_format/c_checker.cpp index bc00bc3e..03e510c2 100644 --- a/src/common/src/result_format/c_checker.cpp +++ b/src/common/src/result_format/c_checker.cpp @@ -12,6 +12,7 @@ const XMLCh *cChecker::TAG_CHECKER = CONST_XMLCH("Checker"); const XMLCh *cChecker::ATTR_CHECKER_ID = CONST_XMLCH("checkerId"); const XMLCh *cChecker::ATTR_DESCRIPTION = CONST_XMLCH("description"); const XMLCh *cChecker::ATTR_SUMMARY = CONST_XMLCH("summary"); +const XMLCh *cChecker::ATTR_STATUS = CONST_XMLCH("status"); XERCES_CPP_NAMESPACE_USE @@ -33,6 +34,12 @@ std::string cChecker::GetSummary() const return m_Summary; } +// Returns the status +std::string cChecker::GetStatus() const +{ + return m_Status; +} + // Returns the description std::string cChecker::GetDescription() const { @@ -45,6 +52,12 @@ void cChecker::SetDescription(const std::string &strDescription) m_Description = strDescription; } +// Sets the description +void cChecker::SetStatus(const std::string &eStatus) +{ + m_Status = eStatus; +} + // Write the xml for this issue DOMElement *cChecker::WriteXML(DOMDocument *pResultDocument) { @@ -63,6 +76,24 @@ DOMElement *cChecker::WriteXML(DOMDocument *pResultDocument) pCheckerNode->appendChild(p_DataElement); } + // Add Rules und cCheckerSummaries + for (std::list::const_iterator it = m_Rules.begin(); it != m_Rules.end(); ++it) + { + DOMElement *p_DataElement = (*it)->WriteXML(pResultDocument); + + if (nullptr != p_DataElement) + pCheckerNode->appendChild(p_DataElement); + } + + // Add Metadatas und cCheckerSummaries + for (std::list::const_iterator it = m_Metadata.begin(); it != m_Metadata.end(); ++it) + { + DOMElement *p_DataElement = (*it)->WriteXML(pResultDocument); + + if (nullptr != p_DataElement) + pCheckerNode->appendChild(p_DataElement); + } + return pCheckerNode; } @@ -77,9 +108,9 @@ cChecker *cChecker::ParseFromXML(DOMNode *pXMLNode, DOMElement *pXMLElement, cCh std::string strCheckerId = XMLString::transcode(pXMLElement->getAttribute(ATTR_CHECKER_ID)); std::string strSummary = XMLString::transcode(pXMLElement->getAttribute(ATTR_SUMMARY)); std::string strDescription = XMLString::transcode(pXMLElement->getAttribute(ATTR_DESCRIPTION)); + std::string strStatus = XMLString::transcode(pXMLElement->getAttribute(ATTR_STATUS)); - cChecker *pChecker = new cChecker(strCheckerId, strDescription, strSummary); - + cChecker *pChecker = new cChecker(strCheckerId, strDescription, strSummary, strStatus); pChecker->AssignCheckerBundle(checkerBundle); DOMNodeList *pIssueChildList = pXMLNode->getChildNodes(); @@ -123,14 +154,17 @@ DOMElement *cChecker::CreateNode(DOMDocument *pDOMDocResultDocument) XMLCh *pCheckerId = XMLString::transcode(m_CheckerId.c_str()); XMLCh *pDescription = XMLString::transcode(m_Description.c_str()); XMLCh *pSummary = XMLString::transcode(m_Summary.c_str()); + XMLCh *pStatus = XMLString::transcode(m_Status.c_str()); pBundleSummary->setAttribute(ATTR_CHECKER_ID, pCheckerId); pBundleSummary->setAttribute(ATTR_DESCRIPTION, pDescription); pBundleSummary->setAttribute(ATTR_SUMMARY, pSummary); + pBundleSummary->setAttribute(ATTR_STATUS, pStatus); XMLString::release(&pCheckerId); XMLString::release(&pDescription); XMLString::release(&pSummary); + XMLString::release(&pStatus); return pBundleSummary; } @@ -168,6 +202,49 @@ cIssue *cChecker::AddIssue(cIssue *const issueToAdd) return nullptr; } +cRule *cChecker::AddRule(cRule *const ruleToAdd) +{ + if (nullptr == m_Bundle) + { + // use runtime_error instead of exception for linux + throw std::runtime_error("Create the checker by using CheckerBundle::CreateChecker()!"); + } + else + { + ruleToAdd->AssignChecker(this); + m_Rules.push_back(ruleToAdd); + + return ruleToAdd; + } + return nullptr; +} + +cMetadata *cChecker::AddMetadata(cMetadata *const metadataToAdd) +{ + if (nullptr == m_Bundle) + { + // use runtime_error instead of exception for linux + throw std::runtime_error("Create the checker by using CheckerBundle::CreateChecker()!"); + } + else + { + metadataToAdd->AssignChecker(this); + m_Metadata.push_back(metadataToAdd); + + return metadataToAdd; + } + return nullptr; +} + +unsigned int cChecker::GetRuleCount() +{ + return (unsigned int)m_Rules.size(); +} + +unsigned int cChecker::GetMetadataCount() +{ + return (unsigned int)m_Metadata.size(); +} // Deletes all issues void cChecker::Clear() { @@ -175,6 +252,16 @@ void cChecker::Clear() delete *it; m_Issues.clear(); + + for (std::list::iterator it = m_Rules.begin(); it != m_Rules.end(); it++) + delete *it; + + m_Rules.clear(); + + for (std::list::iterator it = m_Metadata.begin(); it != m_Metadata.end(); it++) + delete *it; + + m_Metadata.clear(); } // Counts the Issues @@ -189,6 +276,16 @@ std::list cChecker::GetIssues() return m_Issues; } +std::list cChecker::GetRules() +{ + return m_Rules; +} + +std::list cChecker::GetMetadata() +{ + return m_Metadata; +} + // Assigns a specific bundle to the checker void cChecker::AssignCheckerBundle(cCheckerBundle *myBundle) { diff --git a/src/common/src/result_format/c_checker_bundle.cpp b/src/common/src/result_format/c_checker_bundle.cpp index 04a11881..ffb7a4f4 100644 --- a/src/common/src/result_format/c_checker_bundle.cpp +++ b/src/common/src/result_format/c_checker_bundle.cpp @@ -147,15 +147,15 @@ cChecker *cCheckerBundle::CreateChecker(cChecker *newChecker) // Adds a new checker cChecker *cCheckerBundle::CreateChecker(const std::string &checkerId, const std::string &strDescription, - const std::string &strSummary) + const std::string &strSummary, const std::string &strStatus) { - return CreateChecker(new cChecker(checkerId, strDescription, strSummary)); + return CreateChecker(new cChecker(checkerId, strDescription, strSummary, strStatus)); } cChecker *cCheckerBundle::CreateCheckerWithIssues(const std::string &strCheckerId, const std::string &strDescription, eIssueLevel issueLevel, std::map m_Issues) { - cChecker *pChecker = new cChecker(strCheckerId, strDescription, ""); + cChecker *pChecker = new cChecker(strCheckerId, strDescription, "", "completed"); CreateChecker(pChecker); for (std::map::const_iterator it = m_Issues.cbegin(); it != m_Issues.cend(); it++) diff --git a/src/common/src/result_format/c_domain_specific_info.cpp b/src/common/src/result_format/c_domain_specific_info.cpp new file mode 100644 index 00000000..ac34e38d --- /dev/null +++ b/src/common/src/result_format/c_domain_specific_info.cpp @@ -0,0 +1,62 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "common/result_format/c_domain_specific_info.h" +#include +#include +#include +#include +#include +#include +#include + +const XMLCh *cDomainSpecificInfo::TAG_DOMAIN_SPECIFIC_INFO = CONST_XMLCH("DomainSpecificInfo"); +const XMLCh *cDomainSpecificInfo::ATTR_NAME = CONST_XMLCH("name"); +// Returns the root +DOMElement *cDomainSpecificInfo::GetRoot() const +{ + return m_Root; +} +DOMDocument *cDomainSpecificInfo::GetDoc() const +{ + return m_Doc; +} +std::string cDomainSpecificInfo::GetName() const +{ + return m_Name; +} + +cDomainSpecificInfo::~cDomainSpecificInfo() +{ + m_Doc->release(); +} + +DOMElement *cDomainSpecificInfo::WriteXML(DOMDocument *p_resultDocument) +{ + DOMElement *p_DataElement = p_resultDocument->createElement(TAG_DOMAIN_SPECIFIC_INFO); + XMLCh *pName = XMLString::transcode(m_Name.c_str()); + p_DataElement->setAttribute(ATTR_NAME, pName); + + // Import the root element from the original document to the new document + DOMElement *importedRootElement = (DOMElement *)p_resultDocument->importNode(m_Root, true); + // Append the imported root element to the new document + p_DataElement->appendChild(importedRootElement); + // Return the appended root element + + XMLString::release(&pName); + + return importedRootElement; +} + +cDomainSpecificInfo *cDomainSpecificInfo::ParseFromXML(DOMNode *pXMLNode, DOMElement *pXMLElement) +{ + std::string strName = XMLString::transcode(pXMLElement->getAttribute(ATTR_NAME)); + + cDomainSpecificInfo *domainInfo = new cDomainSpecificInfo(pXMLElement, strName); + // Return the parsed instance + return domainInfo; +} diff --git a/src/common/src/result_format/c_inertial_location.cpp b/src/common/src/result_format/c_inertial_location.cpp index b9c02d03..3697ed78 100644 --- a/src/common/src/result_format/c_inertial_location.cpp +++ b/src/common/src/result_format/c_inertial_location.cpp @@ -13,9 +13,6 @@ const XMLCh *cInertialLocation::TAG_NAME = CONST_XMLCH("InertialLocation"); const XMLCh *cInertialLocation::ATTR_X = CONST_XMLCH("x"); const XMLCh *cInertialLocation::ATTR_Y = CONST_XMLCH("y"); const XMLCh *cInertialLocation::ATTR_Z = CONST_XMLCH("z"); -const XMLCh *cInertialLocation::ATTR_H = CONST_XMLCH("h"); -const XMLCh *cInertialLocation::ATTR_P = CONST_XMLCH("p"); -const XMLCh *cInertialLocation::ATTR_R = CONST_XMLCH("r"); DOMElement *cInertialLocation::WriteXML(DOMDocument *p_resultDocument) { @@ -24,23 +21,14 @@ DOMElement *cInertialLocation::WriteXML(DOMDocument *p_resultDocument) XMLCh *pX = XMLString::transcode(std::to_string(m_X).c_str()); XMLCh *pY = XMLString::transcode(std::to_string(m_Y).c_str()); XMLCh *pZ = XMLString::transcode(std::to_string(m_Z).c_str()); - XMLCh *pH = XMLString::transcode(std::to_string(m_H).c_str()); - XMLCh *pP = XMLString::transcode(std::to_string(m_P).c_str()); - XMLCh *pR = XMLString::transcode(std::to_string(m_R).c_str()); p_DataElement->setAttribute(ATTR_X, pX); p_DataElement->setAttribute(ATTR_Y, pY); p_DataElement->setAttribute(ATTR_Z, pZ); - p_DataElement->setAttribute(ATTR_H, pH); - p_DataElement->setAttribute(ATTR_P, pP); - p_DataElement->setAttribute(ATTR_R, pR); XMLString::release(&pX); XMLString::release(&pY); XMLString::release(&pZ); - XMLString::release(&pH); - XMLString::release(&pP); - XMLString::release(&pR); return p_DataElement; } @@ -50,12 +38,8 @@ cInertialLocation *cInertialLocation::ParseFromXML(DOMNode *, DOMElement *pXMLEl std::string strX = XMLString::transcode(pXMLElement->getAttribute(ATTR_X)); std::string strY = XMLString::transcode(pXMLElement->getAttribute(ATTR_Y)); std::string strZ = XMLString::transcode(pXMLElement->getAttribute(ATTR_Z)); - std::string strH = XMLString::transcode(pXMLElement->getAttribute(ATTR_H)); - std::string strP = XMLString::transcode(pXMLElement->getAttribute(ATTR_P)); - std::string strR = XMLString::transcode(pXMLElement->getAttribute(ATTR_R)); - cInertialLocation *result = new cInertialLocation(atof(strX.c_str()), atof(strY.c_str()), atof(strZ.c_str()), - atof(strH.c_str()), atof(strP.c_str()), atof(strR.c_str())); + cInertialLocation *result = new cInertialLocation(atof(strX.c_str()), atof(strY.c_str()), atof(strZ.c_str())); return result; } @@ -78,20 +62,3 @@ double cInertialLocation::GetZ() const return m_Z; } -// Returns the Head -double cInertialLocation::GetHead() const -{ - return m_H; -} - -// Returns the Pitch -double cInertialLocation::GetPitch() const -{ - return m_P; -} - -// Returns the Roll -double cInertialLocation::GetRoll() const -{ - return m_R; -} diff --git a/src/common/src/result_format/c_issue.cpp b/src/common/src/result_format/c_issue.cpp index 856cf3f3..d2fc0e03 100644 --- a/src/common/src/result_format/c_issue.cpp +++ b/src/common/src/result_format/c_issue.cpp @@ -9,6 +9,7 @@ #include "common/result_format/c_issue.h" #include "common/result_format/c_checker.h" #include "common/result_format/c_checker_bundle.h" +#include "common/result_format/c_domain_specific_info.h" #include "common/result_format/c_locations_container.h" #include "common/util.h" @@ -18,17 +19,20 @@ const XMLCh *cIssue::TAG_ISSUE = CONST_XMLCH("Issue"); const XMLCh *cIssue::ATTR_ISSUE_ID = CONST_XMLCH("issueId"); const XMLCh *cIssue::ATTR_DESCRIPTION = CONST_XMLCH("description"); const XMLCh *cIssue::ATTR_LEVEL = CONST_XMLCH("level"); +const XMLCh *cIssue::ATTR_RULEUID = CONST_XMLCH("ruleUID"); const std::map cIssue::issueLevelToString = { {eIssueLevel::INFO_LVL, "Info"}, {eIssueLevel::WARNING_LVL, "Warning"}, {eIssueLevel::ERROR_LVL, "Error"}}; -cIssue::cIssue(const std::string &description, eIssueLevel infoLvl, cLocationsContainer *locationsContainer) +cIssue::cIssue(const std::string &description, eIssueLevel infoLvl, const std::string &ruleUID, + cLocationsContainer *locationsContainer, cDomainSpecificInfo *domainSpecificInfo) { m_Description = description; m_IssueLevel = infoLvl; + m_RuleUID = ruleUID; m_Checker = nullptr; - AddLocationsContainer(locationsContainer); + AddDomainSpecificInfo(domainSpecificInfo); } cIssue::cIssue(const std::string &description, eIssueLevel infoLvl, std::list listLoc) @@ -37,6 +41,20 @@ cIssue::cIssue(const std::string &description, eIssueLevel infoLvl, std::list listDomainSpecificInfo) + : cIssue(description, infoLvl) +{ + AddDomainSpecificInfo(listDomainSpecificInfo); +} + +cIssue::cIssue(const std::string &description, eIssueLevel infoLvl, const std::string &ruleUID, + std::list listLoc) + : cIssue(description, infoLvl, ruleUID) +{ + AddLocationsContainer(listLoc); +} + cIssue::~cIssue() { m_Checker = nullptr; @@ -46,8 +64,14 @@ cIssue::~cIssue() { delete (*locIt); } + for (std::list::const_iterator domIt = m_DomainSpecificInfo.cbegin(); + domIt != m_DomainSpecificInfo.cend(); domIt++) + { + delete (*domIt); + } m_Locations.clear(); + m_DomainSpecificInfo.clear(); } void cIssue::AddLocationsContainer(cLocationsContainer *locationsContainer) @@ -56,11 +80,22 @@ void cIssue::AddLocationsContainer(cLocationsContainer *locationsContainer) m_Locations.push_back(locationsContainer); } +void cIssue::AddDomainSpecificInfo(cDomainSpecificInfo *domainSpecificInfo) +{ + if (nullptr != domainSpecificInfo) + m_DomainSpecificInfo.push_back(domainSpecificInfo); +} + void cIssue::AddLocationsContainer(std::list listLoc) { m_Locations.insert(m_Locations.end(), listLoc.begin(), listLoc.end()); } +void cIssue::AddDomainSpecificInfo(std::list listDomainSpecificInfo) +{ + m_DomainSpecificInfo.insert(m_DomainSpecificInfo.end(), listDomainSpecificInfo.begin(), + listDomainSpecificInfo.end()); +} void cIssue::AssignChecker(cChecker *checkerToAssign) { m_Checker = checkerToAssign; @@ -78,10 +113,12 @@ DOMElement *cIssue::WriteXML(DOMDocument *p_resultDocument) XMLCh *pIssueId = XMLString::transcode(std::to_string(m_Id).c_str()); XMLCh *pDescription = XMLString::transcode(m_Description.c_str()); XMLCh *pLevel = XMLString::transcode(std::to_string((int)m_IssueLevel).c_str()); + XMLCh *pRuleUID = XMLString::transcode(m_RuleUID.c_str()); p_DataElement->setAttribute(ATTR_ISSUE_ID, pIssueId); p_DataElement->setAttribute(ATTR_DESCRIPTION, pDescription); p_DataElement->setAttribute(ATTR_LEVEL, pLevel); + p_DataElement->setAttribute(ATTR_RULEUID, pRuleUID); // Write extended informations if (HasLocations()) @@ -94,9 +131,21 @@ DOMElement *cIssue::WriteXML(DOMDocument *p_resultDocument) } } + // Write domain specific info + if (HasDomainSpecificInfo()) + { + for (std::list::const_iterator domIt = m_DomainSpecificInfo.cbegin(); + domIt != m_DomainSpecificInfo.cend(); domIt++) + { + DOMElement *domainElement = (*domIt)->WriteXML(p_resultDocument); + p_DataElement->appendChild(domainElement); + } + } + XMLString::release(&pIssueId); XMLString::release(&pDescription); XMLString::release(&pLevel); + XMLString::release(&pRuleUID); return p_DataElement; } @@ -143,8 +192,9 @@ cIssue *cIssue::ParseFromXML(DOMNode *pXMLNode, DOMElement *pXMLElement, cChecke std::string strDescription = XMLString::transcode(pXMLElement->getAttribute(ATTR_DESCRIPTION)); std::string strID = XMLString::transcode(pXMLElement->getAttribute(ATTR_ISSUE_ID)); std::string strLevel = XMLString::transcode(pXMLElement->getAttribute(ATTR_LEVEL)); + std::string strRuleUID = XMLString::transcode(pXMLElement->getAttribute(ATTR_RULEUID)); - cIssue *issue = new cIssue(strDescription, GetIssueLevelFromStr(strLevel)); + cIssue *issue = new cIssue(strDescription, GetIssueLevelFromStr(strLevel), strRuleUID); issue->AssignChecker(checker); issue->SetIssueId(strID); @@ -168,6 +218,11 @@ cIssue *cIssue::ParseFromXML(DOMNode *pXMLNode, DOMElement *pXMLElement, cChecke issue->AddLocationsContainer( (cLocationsContainer *)cLocationsContainer::ParseFromXML(currentIssueNode, currentIssueElement)); } + // Parse cDomainSpecificInfo + if (Equals(currentTagName, XMLString::transcode(cDomainSpecificInfo::TAG_DOMAIN_SPECIFIC_INFO))) + { + issue->AddDomainSpecificInfo(cDomainSpecificInfo::ParseFromXML(currentIssueNode, currentIssueElement)); + } } } @@ -179,17 +234,32 @@ bool cIssue::HasLocations() const return (m_Locations.size() != 0); } +bool cIssue::HasDomainSpecificInfo() const +{ + return (m_DomainSpecificInfo.size() != 0); +} + size_t cIssue::GetLocationsCount() const { return m_Locations.size(); } +size_t cIssue::GetDomainSpecificCount() const +{ + return m_DomainSpecificInfo.size(); +} + // Returns all extended informations std::list cIssue::GetLocationsContainer() const { return m_Locations; } +std::list cIssue::GetDomainSpecificInfo() const +{ + return m_DomainSpecificInfo; +} + eIssueLevel cIssue::GetIssueLevelFromStr(const std::string &issueLevelString) { return (eIssueLevel)stoi(issueLevelString); @@ -200,6 +270,11 @@ void cIssue::SetDescription(const std::string &strDescription) m_Description = strDescription; } +void cIssue::SetRuleUID(const std::string &strRuleUID) +{ + m_RuleUID = strRuleUID; +} + void cIssue::SetLevel(eIssueLevel level) { m_IssueLevel = level; @@ -211,6 +286,11 @@ std::string cIssue::GetDescription() const return m_Description; } +std::string cIssue::GetRuleUID() const +{ + return m_RuleUID; +} + // Returns the issue level eIssueLevel cIssue::GetIssueLevel() const { diff --git a/src/common/src/result_format/c_locations_container.cpp b/src/common/src/result_format/c_locations_container.cpp index b69a5b71..2a691cd5 100644 --- a/src/common/src/result_format/c_locations_container.cpp +++ b/src/common/src/result_format/c_locations_container.cpp @@ -12,7 +12,6 @@ #include "common/result_format/c_extended_information.h" #include "common/result_format/c_file_location.h" #include "common/result_format/c_inertial_location.h" -#include "common/result_format/c_road_location.h" #include "common/result_format/c_xml_location.h" XERCES_CPP_NAMESPACE_USE @@ -107,12 +106,6 @@ cLocationsContainer *cLocationsContainer::ParseFromXML(DOMNode *pXMLNode, DOMEle subIssue->AddExtendedInformation( (cExtendedInformation *)cXMLLocation::ParseFromXML(currentIssueNode, currentIssueElement)); } - // Parse cRoadLocation - else if (Equals(currentTagName, XMLString::transcode(cRoadLocation::TAG_NAME))) - { - subIssue->AddExtendedInformation( - (cExtendedInformation *)cRoadLocation::ParseFromXML(currentIssueNode, currentIssueElement)); - } // Parse cInertialLocation else if (Equals(currentTagName, XMLString::transcode(cInertialLocation::TAG_NAME))) { diff --git a/src/common/src/result_format/c_metadata.cpp b/src/common/src/result_format/c_metadata.cpp new file mode 100644 index 00000000..1ef12638 --- /dev/null +++ b/src/common/src/result_format/c_metadata.cpp @@ -0,0 +1,67 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "common/result_format/c_metadata.h" + +XERCES_CPP_NAMESPACE_USE + +const XMLCh *cMetadata::TAG_NAME = CONST_XMLCH("Metadata"); +const XMLCh *cMetadata::ATTR_KEY = CONST_XMLCH("key"); +const XMLCh *cMetadata::ATTR_VALUE = CONST_XMLCH("value"); +const XMLCh *cMetadata::ATTR_DESCRIPTION = CONST_XMLCH("description"); + +DOMElement *cMetadata::WriteXML(DOMDocument *p_resultDocument) +{ + + DOMElement *p_DataElement = p_resultDocument->createElement(TAG_NAME); + XMLCh *pKey = XMLString::transcode(m_Key.c_str()); + XMLCh *pValue = XMLString::transcode(m_Value.c_str()); + XMLCh *pDescription = XMLString::transcode(m_Description.c_str()); + p_DataElement->setAttribute(ATTR_KEY, pKey); + p_DataElement->setAttribute(ATTR_VALUE, pValue); + p_DataElement->setAttribute(ATTR_DESCRIPTION, pDescription); + + XMLString::release(&pKey); + XMLString::release(&pValue); + XMLString::release(&pDescription); + + return p_DataElement; +} + +cMetadata *cMetadata::ParseFromXML(DOMNode *, DOMElement *pXMLElement, cChecker *checker) +{ + std::string strKey = XMLString::transcode(pXMLElement->getAttribute(ATTR_KEY)); + std::string strValue = XMLString::transcode(pXMLElement->getAttribute(ATTR_VALUE)); + std::string strDescription = XMLString::transcode(pXMLElement->getAttribute(ATTR_DESCRIPTION)); + + cMetadata *result = new cMetadata(strKey, strValue, strDescription); + result->AssignChecker(checker); + + return result; +} + +// Returns the key +std::string cMetadata::GetKey() const +{ + return m_Key; +} + +// Returns the value +std::string cMetadata::GetValue() const +{ + return m_Value; +} + +// Returns the description +std::string cMetadata::GetDescription() const +{ + return m_Description; +} +void cMetadata::AssignChecker(cChecker *checkerToAssign) +{ + m_Checker = checkerToAssign; +} diff --git a/src/common/src/result_format/c_result_container.cpp b/src/common/src/result_format/c_result_container.cpp index 9d4e42a7..613228e0 100644 --- a/src/common/src/result_format/c_result_container.cpp +++ b/src/common/src/result_format/c_result_container.cpp @@ -58,7 +58,6 @@ void cResultContainer::Clear() void cResultContainer::WriteResults(const std::string &path) const { - XMLPlatformUtils::Initialize(); DOMImplementation *p_DOMImplementationCore = DOMImplementationRegistry::getDOMImplementation(CONST_XMLCH("core")); // For storing a file, we need DOMImplementationLS @@ -97,7 +96,6 @@ void cResultContainer::WriteResults(const std::string &path) const p_DOMSerializer->release(); XMLString::release(&pPath); - XMLPlatformUtils::Terminate(); } /* @@ -118,7 +116,6 @@ void cResultContainer::AddResultsFromXML(const std::string &strXmlFilePath) } else { - XMLPlatformUtils::Initialize(); xercesc::XercesDOMParser *pDomParser = new xercesc::XercesDOMParser(); pDomParser->setValidationScheme(XercesDOMParser::Val_Never); @@ -165,7 +162,6 @@ void cResultContainer::AddResultsFromXML(const std::string &strXmlFilePath) } delete pDomParser; - XMLPlatformUtils::Terminate(); } } diff --git a/src/common/src/result_format/c_road_location.cpp b/src/common/src/result_format/c_road_location.cpp deleted file mode 100644 index d9e71c1d..00000000 --- a/src/common/src/result_format/c_road_location.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2023 CARIAD SE. - * - * This Source Code Form is subject to the terms of the Mozilla - * Public License, v. 2.0. If a copy of the MPL was not distributed - * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#include "common/result_format/c_road_location.h" - -XERCES_CPP_NAMESPACE_USE - -const XMLCh *cRoadLocation::TAG_NAME = CONST_XMLCH("RoadLocation"); -const XMLCh *cRoadLocation::ATTR_ROAD_ID = CONST_XMLCH("roadId"); -const XMLCh *cRoadLocation::ATTR_S = CONST_XMLCH("s"); -const XMLCh *cRoadLocation::ATTR_T = CONST_XMLCH("t"); - -DOMElement *cRoadLocation::WriteXML(DOMDocument *p_resultDocument) -{ - DOMElement *p_DataElement = CreateExtendedInformationXMLNode(p_resultDocument); - - XMLCh *pRoadId = XMLString::transcode(m_RoadID.c_str()); - XMLCh *pS = XMLString::transcode(std::to_string(m_S).c_str()); - XMLCh *pT = XMLString::transcode(std::to_string(m_T).c_str()); - - p_DataElement->setAttribute(ATTR_ROAD_ID, pRoadId); - p_DataElement->setAttribute(ATTR_S, pS); - p_DataElement->setAttribute(ATTR_T, pT); - - XMLString::release(&pRoadId); - XMLString::release(&pS); - XMLString::release(&pT); - - return p_DataElement; -} - -cRoadLocation *cRoadLocation::ParseFromXML(DOMNode *, DOMElement *pXMLElement) -{ - std::string strRoadId = XMLString::transcode(pXMLElement->getAttribute(ATTR_ROAD_ID)); - std::string strS = XMLString::transcode(pXMLElement->getAttribute(ATTR_S)); - std::string strT = XMLString::transcode(pXMLElement->getAttribute(ATTR_T)); - - cRoadLocation *result = new cRoadLocation(strRoadId, (float)atof(strS.c_str()), (float)atof(strT.c_str())); - - return result; -} - -// Returns the xPath -std::string cRoadLocation::GetRoadID() const -{ - return m_RoadID; -} - -// Returns the road Id as integer -void cRoadLocation::GetRoadID(int &roadId) const -{ - roadId = atoi(m_RoadID.c_str()); -} - -// Returns the s of the road -float cRoadLocation::GetS() const -{ - return m_S; -} - -// Returns the t of the road -float cRoadLocation::GetT() const -{ - return m_T; -} diff --git a/src/common/src/result_format/c_rule.cpp b/src/common/src/result_format/c_rule.cpp new file mode 100644 index 00000000..d6b4b1ef --- /dev/null +++ b/src/common/src/result_format/c_rule.cpp @@ -0,0 +1,46 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "common/result_format/c_rule.h" + +XERCES_CPP_NAMESPACE_USE + +const XMLCh *cRule::TAG_NAME = CONST_XMLCH("AddressedRule"); +const XMLCh *cRule::ATTR_RULE_UID = CONST_XMLCH("ruleUID"); + +DOMElement *cRule::WriteXML(DOMDocument *p_resultDocument) +{ + + DOMElement *p_DataElement = p_resultDocument->createElement(TAG_NAME); + XMLCh *pRuleUID = XMLString::transcode(m_RuleUID.c_str()); + p_DataElement->setAttribute(ATTR_RULE_UID, pRuleUID); + + XMLString::release(&pRuleUID); + + return p_DataElement; +} + +cRule *cRule::ParseFromXML(DOMNode *, DOMElement *pXMLElement, cChecker *checker) +{ + std::string strRuleUID = XMLString::transcode(pXMLElement->getAttribute(ATTR_RULE_UID)); + + cRule *result = new cRule(strRuleUID); + result->AssignChecker(checker); + + return result; +} + +// Returns the X +std::string cRule::GetRuleUID() const +{ + return m_RuleUID; +} + +void cRule::AssignChecker(cChecker *checkerToAssign) +{ + m_Checker = checkerToAssign; +} diff --git a/src/report_modules/report_module_gui/src/report_format_ui.cpp b/src/report_modules/report_module_gui/src/report_format_ui.cpp index 36d5bc4c..fc9b94bf 100644 --- a/src/report_modules/report_module_gui/src/report_format_ui.cpp +++ b/src/report_modules/report_module_gui/src/report_format_ui.cpp @@ -31,6 +31,7 @@ int main(int argc, char *argv[]) SetDllDirectory(currentPathW.c_str()); #endif + XMLPlatformUtils::Initialize(); std::string strToolpath = argv[0]; if (argc > 2) @@ -121,6 +122,7 @@ void ShowHelp(const std::string &toolPath) int RunReportGUI(const cParameterContainer &inputParams, const QApplication &app) { + XMLPlatformUtils::Initialize(); cResultContainer *pResultContainer = new cResultContainer(); std::string strXMLResultsPath = inputParams.GetParam("strInputFile"); @@ -150,6 +152,7 @@ int RunReportGUI(const cParameterContainer &inputParams, const QApplication &app pResultContainer->Clear(); delete pResultContainer; + XMLPlatformUtils::Terminate(); return execCode; } diff --git a/src/report_modules/report_module_gui/src/ui/c_checker_widget.cpp b/src/report_modules/report_module_gui/src/ui/c_checker_widget.cpp index 399f89f4..8b6fc8bd 100644 --- a/src/report_modules/report_module_gui/src/ui/c_checker_widget.cpp +++ b/src/report_modules/report_module_gui/src/ui/c_checker_widget.cpp @@ -23,7 +23,6 @@ #include "common/result_format/c_issue.h" #include "common/result_format/c_locations_container.h" #include "common/result_format/c_result_container.h" -#include "common/result_format/c_road_location.h" #include "common/result_format/c_xml_location.h" #include "common/util.h" @@ -330,8 +329,7 @@ void cCheckerWidget::FillIssueTreeItem(QTreeWidgetItem *treeItem, cIssue *const for (const auto subIssue : issue->GetLocationsContainer()) { - if (subIssue->HasExtendedInformation() || - subIssue->HasExtendedInformation()) + if (subIssue->HasExtendedInformation()) { isVisibleInViewer = true; } @@ -640,8 +638,8 @@ void cCheckerWidget::ShowIssue(cIssue *const itemToShow, const cLocationsContain ShowXOSCIssue(itemToShow, row); } - // Show RoadLocations and InertialLocations in Viewer - if (extInfo->IsType() || extInfo->IsType()) + // Show InertialLocations in Viewer + if (extInfo->IsType()) { ShowIssueIn3DViewer(itemToShow, locationToShow); } @@ -758,15 +756,6 @@ void cCheckerWidget::PrintExtendedInformationIntoStream(cExtendedInformation *it cXMLLocation *xmlLoc = (cXMLLocation *)item; *ssStream << std::endl << " XPath: " << xmlLoc->GetXPath(); } - else if (item->IsType()) - { - cRoadLocation *roadLoc = (cRoadLocation *)item; - - ssStream->setf(std::ios::fixed, std::ios::floatfield); - *ssStream << std::endl - << " Road: id=" << roadLoc->GetRoadID() << " s=" << std::setprecision(2) << roadLoc->GetS() - << " t=" << std::setprecision(2) << roadLoc->GetT(); - } else if (item->IsType()) { cInertialLocation *initialLoc = (cInertialLocation *)item; @@ -774,8 +763,6 @@ void cCheckerWidget::PrintExtendedInformationIntoStream(cExtendedInformation *it ssStream->setf(std::ios::fixed, std::ios::floatfield); *ssStream << std::endl << " Location: x=" << std::setprecision(2) << initialLoc->GetX() << " y=" << std::setprecision(2) - << initialLoc->GetY() << " z=" << std::setprecision(2) << initialLoc->GetZ() - << " heading=" << std::setprecision(2) << initialLoc->GetHead() << " pitch=" << std::setprecision(2) - << initialLoc->GetPitch() << " roll=" << std::setprecision(2) << initialLoc->GetRoll(); + << initialLoc->GetY() << " z=" << std::setprecision(2) << initialLoc->GetZ(); } } diff --git a/src/report_modules/report_module_text/src/report_format_text.cpp b/src/report_modules/report_module_text/src/report_format_text.cpp index fd78b0b5..c462a791 100644 --- a/src/report_modules/report_module_text/src/report_format_text.cpp +++ b/src/report_modules/report_module_text/src/report_format_text.cpp @@ -16,11 +16,11 @@ #include "common/result_format/c_locations_container.h" #include "common/result_format/c_parameter_container.h" #include "common/result_format/c_result_container.h" -#include "common/result_format/c_road_location.h" #include "common/result_format/c_xml_location.h" #include "stdafx.h" #include "common/qc4openx_filesystem.h" +#include XERCES_CPP_NAMESPACE_USE @@ -130,6 +130,8 @@ void ShowHelp(const std::string &toolPath) void RunTextReport(const cParameterContainer &inputParams) { + XMLPlatformUtils::Initialize(); + cResultContainer *pResultContainer = new cResultContainer(); try @@ -176,6 +178,9 @@ void WriteResults(const char *file, cResultContainer *ptrResultContainer) std::list bundles = ptrResultContainer->GetCheckerBundles(); std::list checkers; std::list issues; + std::list rules; + std::set violated_rules; + std::set addressed_rules; if (outFile.is_open()) { @@ -257,14 +262,43 @@ void WriteResults(const char *file, cResultContainer *ptrResultContainer) << (*it_Issue)->GetDescription(); PrintExtendedInformationIntoStream((*it_Issue), &ss); + if ((*it_Issue)->GetRuleUID() != "") + { + violated_rules.insert((*it_Issue)->GetRuleUID()); + } } ss << "\n"; } + // Get all rules and issues covered by the current checker + rules = (*itChecker)->GetRules(); + for (std::list::const_iterator it_Rule = rules.begin(); it_Rule != rules.end(); it_Rule++) + { + if ((*it_Rule)->GetRuleUID() != "") + { + addressed_rules.insert((*it_Rule)->GetRuleUID()); + } + } } ss << "\n" << BASIC_SEPARATOR_LINE << "\n"; } + ss << "Addressed vs Violated rules report \n\n"; + + ss << "\nTotal number of addressed rules: " << addressed_rules.size(); + for (const auto &str : addressed_rules) + { + ss << "\n\t-> Addressed RuleUID: " << str << "\n"; + } + + ss << "\nTotal number of violated rules: " << violated_rules.size(); + for (const auto &str : violated_rules) + { + ss << "\n\t-> Violated RuleUID: " << str << "\n"; + } + + ss << "\n" << BASIC_SEPARATOR_LINE << "\n"; + outFile << ss.rdbuf(); outFile.close(); } @@ -302,20 +336,12 @@ void PrintExtendedInformationIntoStream(cIssue *issue, std::stringstream *ssStre *ssStream << "\n " << " XPath: " << xmlLoc->GetXPath(); } - else if ((*extIt)->IsType()) - { - cRoadLocation *roadLoc = (cRoadLocation *)(*extIt); - *ssStream << "\n " - << " Road: id=" << roadLoc->GetRoadID() << " s=" << roadLoc->GetS() - << " t=" << roadLoc->GetT(); - } else if ((*extIt)->IsType()) { cInertialLocation *inertialLoc = (cInertialLocation *)(*extIt); *ssStream << "\n " << " Location: x=" << inertialLoc->GetX() << " y=" << inertialLoc->GetY() - << " z=" << inertialLoc->GetZ() << " heading=" << inertialLoc->GetHead() - << " pitch=" << inertialLoc->GetPitch() << " roll=" << inertialLoc->GetRoll(); + << " z=" << inertialLoc->GetZ(); } } } diff --git a/src/result_pooling/src/result_pooling.cpp b/src/result_pooling/src/result_pooling.cpp index a6f6e7f1..323f207f 100644 --- a/src/result_pooling/src/result_pooling.cpp +++ b/src/result_pooling/src/result_pooling.cpp @@ -14,7 +14,6 @@ #include "common/result_format/c_locations_container.h" #include "common/result_format/c_parameter_container.h" #include "common/result_format/c_result_container.h" -#include "common/result_format/c_road_location.h" #include "common/result_format/c_xml_location.h" #include "common/xml/c_x_path_evaluator.h" #include "stdafx.h" @@ -73,7 +72,6 @@ int main(int argc, char *argv[]) } RunResultPooling(inputParams, resultsDirectory); - return 0; } @@ -94,6 +92,7 @@ void ShowHelp(const std::string &toolPath) void RunResultPooling(const cParameterContainer &inputParams, const fs::path &resultsDirectory) { + XMLPlatformUtils::Initialize(); std::string strResultFile = inputParams.GetParam("strResultFile"); pResultContainer = new cResultContainer(); @@ -141,6 +140,7 @@ void RunResultPooling(const cParameterContainer &inputParams, const fs::path &re std::cout << "Finished." << std::endl; delete pResultContainer; + XMLPlatformUtils::Terminate(); } static void AddFileLocationsToIssues() diff --git a/test/function/CMakeLists.txt b/test/function/CMakeLists.txt index 53d1bd0f..d63cec33 100644 --- a/test/function/CMakeLists.txt +++ b/test/function/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(examples) add_subdirectory(report_modules) add_subdirectory(result_pooling/src) +add_subdirectory(result_format/src) if (WIN32) # FIXME: We need some adaptions that this works in Linux @@ -32,10 +33,15 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/runtime/files ${REFERENCE_FILES_INSTALL_DIR}/function/runtime - + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../../doc/schema ${REFERENCE_FILES_INSTALL_DIR}/doc/schema + + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/result_format/files + ${REFERENCE_FILES_INSTALL_DIR}/function/result_format + COMMENT "Copying test reference files..." ) diff --git a/test/function/_common/helper.cpp b/test/function/_common/helper.cpp index 95becd24..0a325d5f 100644 --- a/test/function/_common/helper.cpp +++ b/test/function/_common/helper.cpp @@ -158,8 +158,10 @@ TestResult ValidateXmlSchema(const std::string &xmlFile, const std::string &xsdF return TestResult::ERR_NOERROR; } -TestResult XmlContainsNode(const std::string &xmlFile, const std::string &nodeName) +std::vector getNodes(const std::string &xmlFile, const std::string &nodeName) { + std::vector nodeList; + try { xercesc::XMLPlatformUtils::Initialize(); @@ -167,39 +169,83 @@ TestResult XmlContainsNode(const std::string &xmlFile, const std::string &nodeNa catch (const xercesc::XMLException &e) { std::cerr << "Error during initialization! :\n" << xercesc::XMLString::transcode(e.getMessage()) << std::endl; - return TestResult::ERR_FAILED; + return nodeList; } xercesc::XercesDOMParser parser; parser.setValidationScheme(xercesc::XercesDOMParser::Val_Never); parser.setDoNamespaces(false); parser.setDoSchema(false); - parser.parse(xmlFile.c_str()); + + try + { + parser.parse(xmlFile.c_str()); + } + catch (const xercesc::XMLException &e) + { + std::cerr << "Error during parsing! :\n" << xercesc::XMLString::transcode(e.getMessage()) << std::endl; + xercesc::XMLPlatformUtils::Terminate(); + return nodeList; + } + catch (const xercesc::DOMException &e) + { + std::cerr << "DOM Error during parsing! :\n" << xercesc::XMLString::transcode(e.getMessage()) << std::endl; + xercesc::XMLPlatformUtils::Terminate(); + return nodeList; + } + catch (...) + { + std::cerr << "Unexpected error during parsing!" << std::endl; + xercesc::XMLPlatformUtils::Terminate(); + return nodeList; + } // Get the DOM document xercesc::DOMDocument *doc = parser.getDocument(); if (!doc) { - std::cerr << "Unable to get DOMDocument object " << std::endl; + std::cerr << "Unable to get DOMDocument object" << std::endl; xercesc::XMLPlatformUtils::Terminate(); - return TestResult::ERR_FILE_NOT_FOUND; + return nodeList; } // Convert nodeName to XMLCh* XMLCh *xmlNodeName = xercesc::XMLString::transcode(nodeName.c_str()); - // Find the node + // Find the nodes xercesc::DOMNodeList *nodes = doc->getElementsByTagName(xmlNodeName); xercesc::XMLString::release(&xmlNodeName); - if (nodes->getLength() > 0) + if (!nodes) { + std::cerr << "Unable to get DOMNodeList object" << std::endl; xercesc::XMLPlatformUtils::Terminate(); + return nodeList; + } + + // Store nodes in the vector + for (XMLSize_t i = 0; i < nodes->getLength(); ++i) + { + xercesc::DOMElement *element = static_cast(nodes->item(i)); + if (element) + { + nodeList.push_back(element); + } + } + + xercesc::XMLPlatformUtils::Terminate(); + return nodeList; +} + +TestResult XmlContainsNode(const std::string &xmlFile, const std::string &nodeName) +{ + std::vector nodes = getNodes(xmlFile, nodeName); + if (!nodes.empty()) + { return TestResult::ERR_NOERROR; } else { - xercesc::XMLPlatformUtils::Terminate(); return TestResult::ERR_UNKNOWN_FORMAT; } } diff --git a/test/function/_common/helper.h b/test/function/_common/helper.h index 7b6edd31..1efd7f88 100644 --- a/test/function/_common/helper.h +++ b/test/function/_common/helper.h @@ -8,12 +8,12 @@ #ifndef _HELPER_HEADER_ #define _HELPER_HEADER_ -#include #include "qc4openx_filesystem.h" #include "gtest/gtest.h" #include #include #include +#include #include #include #include @@ -39,7 +39,8 @@ */ #define GTEST_PRINTF(_message) \ { \ - std::cout << COLOR_GREEN << "[ ] " << COLOR_YELLOW << _message << COLOR_YELLOW << "\n" << COLOR_RESET; \ + std::cout << COLOR_GREEN << "[ ] " << COLOR_YELLOW << _message << COLOR_YELLOW << "\n" \ + << COLOR_RESET; \ } /** @@ -80,4 +81,5 @@ TestResult CheckFileExists(std::string &strResultMessage, const std::string strF TestResult ValidateXmlSchema(const std::string &xmlFile, const std::string &xsdFile); TestResult XmlContainsNode(const std::string &xmlFile, const std::string &nodeName); + #endif diff --git a/test/function/examples/example_checker_bundle/files/result_file_ok.xqar b/test/function/examples/example_checker_bundle/files/result_file_ok.xqar new file mode 100644 index 00000000..9816069f --- /dev/null +++ b/test/function/examples/example_checker_bundle/files/result_file_ok.xqar @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/function/examples/example_checker_bundle/files/result_file_wrong_status.xqar b/test/function/examples/example_checker_bundle/files/result_file_wrong_status.xqar new file mode 100644 index 00000000..0f7119a0 --- /dev/null +++ b/test/function/examples/example_checker_bundle/files/result_file_wrong_status.xqar @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/function/examples/example_checker_bundle/src/example_checker_bundle_tester.cpp b/test/function/examples/example_checker_bundle/src/example_checker_bundle_tester.cpp index a91bde75..fded92eb 100644 --- a/test/function/examples/example_checker_bundle/src/example_checker_bundle_tester.cpp +++ b/test/function/examples/example_checker_bundle/src/example_checker_bundle_tester.cpp @@ -120,3 +120,54 @@ TEST_F(cTesterExampleCheckerBundle, CmdTooMuchArguments) TestResult nRes = ExecuteCommand(strResultMessage, MODULE_NAME, "a b"); ASSERT_TRUE(nRes == TestResult::ERR_FAILED); } + +TEST_F(cTesterExampleCheckerBundle, CmdConfigContainsAddressedRuleAndMetadata) +{ + std::string strResultMessage; + + std::string strConfigFilePath = strTestFilesDir + "/" + std::string(MODULE_NAME) + "_config.xml"; + std::string strResultFilePath = strWorkingDir + "/" + std::string(MODULE_NAME) + ".xqar"; + + TestResult nRes = ExecuteCommand(strResultMessage, MODULE_NAME, strConfigFilePath); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + nRes |= CheckFileExists(strResultMessage, strResultFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + nRes |= XmlContainsNode(strResultFilePath, "AddressedRule"); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + nRes |= XmlContainsNode(strResultFilePath, "Metadata"); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + fs::remove(strResultFilePath.c_str()); +} + +TEST_F(cTesterExampleCheckerBundle, TestFileWrongStatus) +{ + std::string strResultMessage; + + std::string strFilePath = strTestFilesDir + "/result_file_wrong_status.xqar"; + std::string strXsdFilePath = strTestFilesDir + "/../../../doc/schema/xqar_report_format.xsd"; + + TestResult nRes = CheckFileExists(strResultMessage, strFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + nRes |= CheckFileExists(strResultMessage, strXsdFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + nRes |= ValidateXmlSchema(strFilePath, strXsdFilePath); + ASSERT_TRUE_EXT(nRes != TestResult::ERR_NOERROR, strResultMessage.c_str()); +} + +TEST_F(cTesterExampleCheckerBundle, TestFileOK) +{ + std::string strResultMessage; + + std::string strFilePath = strTestFilesDir + "/result_file_ok.xqar"; + std::string strXsdFilePath = strTestFilesDir + "/../../../doc/schema/xqar_report_format.xsd"; + + TestResult nRes = CheckFileExists(strResultMessage, strFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + nRes |= CheckFileExists(strResultMessage, strXsdFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + nRes |= ValidateXmlSchema(strFilePath, strXsdFilePath); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); +} diff --git a/test/function/result_format/files/result_domain_info.xqar b/test/function/result_format/files/result_domain_info.xqar new file mode 100644 index 00000000..ead85c5d --- /dev/null +++ b/test/function/result_format/files/result_domain_info.xqar @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/test/function/result_format/src/CMakeLists.txt b/test/function/result_format/src/CMakeLists.txt new file mode 100644 index 00000000..757d48b7 --- /dev/null +++ b/test/function/result_format/src/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright 2024, ASAM e.V. +# This Source Code Form is subject to the terms of the Mozilla +# Public License, v. 2.0. If a copy of the MPL was not distributed +# with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + + +set(TEST_NAME result_format_tester) + +set_property(GLOBAL PROPERTY USE_FOLDERS true) + +find_package(XercesC REQUIRED) + + +include_directories(${TEST_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../_common + ${XercesC_INCLUDE_DIRS}) + +add_executable(${TEST_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/../../_common/helper.cpp + ${TEST_NAME}.cpp) + +add_test(NAME ${TEST_NAME} + COMMAND ${TEST_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../ +) + +target_link_libraries(${TEST_NAME} + PRIVATE + GTest::gtest_main + $<$:stdc++fs> + qc4openx-common + + ${XercesC_LIBRARIES} +) + +target_compile_definitions(${TEST_NAME} + PRIVATE QC4OPENX_DBQA_BIN_DIR="${QC4OPENX_DBQA_DIR}/examples/checker_bundle_example/bin" + PRIVATE QC4OPENX_DBQA_RESULT_FORMAT_TEST_WORK_DIR="${CMAKE_CURRENT_BINARY_DIR}/../../" + PRIVATE QC4OPENX_DBQA_RESULT_FORMAT_TEST_REF_DIR="${REFERENCE_FILES_INSTALL_DIR}/function/result_format") + +set_target_properties(${TEST_NAME} PROPERTIES FOLDER test/function) diff --git a/test/function/result_format/src/result_format_tester.cpp b/test/function/result_format/src/result_format_tester.cpp new file mode 100644 index 00000000..a69291a8 --- /dev/null +++ b/test/function/result_format/src/result_format_tester.cpp @@ -0,0 +1,74 @@ +/** + * Copyright 2024, ASAM e.V. + * + * This Source Code Form is subject to the terms of the Mozilla + * Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "gtest/gtest.h" + +#include "common/result_format/c_checker_bundle.h" +#include "common/result_format/c_domain_specific_info.h" +#include "common/result_format/c_issue.h" +#include "common/result_format/c_result_container.h" +#include "helper.h" +#include + +#define MODULE_NAME "ResultFormat" + +class cTesterResultFormat : public ::testing::Test +{ + public: + std::string strTestFilesDir = std::string(QC4OPENX_DBQA_RESULT_FORMAT_TEST_REF_DIR); + std::string strWorkingDir = std::string(QC4OPENX_DBQA_RESULT_FORMAT_TEST_WORK_DIR); +}; + +TEST_F(cTesterResultFormat, DomainSpecificInfoReadWrite) +{ + XERCES_CPP_NAMESPACE::XMLPlatformUtils::Initialize(); + std::string strResultMessage; + std::string strFilePath = strTestFilesDir + "/result_domain_info.xqar"; + std::string strResultFile = strWorkingDir + "/output.xqar"; + std::string strXsdFilePath = strTestFilesDir + "/../../doc/schema/xqar_report_format.xsd"; + // Check if xsd file exists + TestResult nRes = CheckFileExists(strResultMessage, strXsdFilePath, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + cResultContainer *pResultContainer = new cResultContainer(); + // Parse results from xml + pResultContainer->AddResultsFromXML(strFilePath); + + // Check domain specifc if is parsed + int domain_specific_info_count = 0; + std::list checkerBundles = pResultContainer->GetCheckerBundles(); + for (std::list::const_iterator itCheckerBundle = checkerBundles.cbegin(); + itCheckerBundle != checkerBundles.cend(); itCheckerBundle++) + { + std::list issues = (*itCheckerBundle)->GetIssues(); + for (std::list::const_iterator itIssue = issues.cbegin(); itIssue != issues.cend(); itIssue++) + { + domain_specific_info_count += (*itIssue)->GetDomainSpecificCount(); + } + } + + std::cout << "Domain specific info count : " << domain_specific_info_count << std::endl; + ASSERT_TRUE_EXT(domain_specific_info_count > 0, "No domain specific info found"); + + // Write results to XML + pResultContainer->WriteResults(strResultFile); + + // Check if output file exists + nRes |= CheckFileExists(strResultMessage, strResultFile, false); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + // Check if result file is valid according to xsd + nRes |= ValidateXmlSchema(strResultFile, strXsdFilePath); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + // Check if result file contains a specific tag + nRes |= XmlContainsNode(strFilePath, "RoadLocation"); + ASSERT_TRUE_EXT(nRes == TestResult::ERR_NOERROR, strResultMessage.c_str()); + + delete pResultContainer; + XERCES_CPP_NAMESPACE::XMLPlatformUtils::Terminate(); +}