From 65f2582ae47992d5de12d79805efe09e97745f69 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 11:27:04 +0100 Subject: [PATCH 1/8] Testing workflow --- .github/workflows/test_and_docs.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 542bc6da9..9dbf763f9 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -38,13 +38,13 @@ jobs: bazel-docs-verify-target: "//:docs_check" # This is the user configurable part of the workflow - consumer-tests: - uses: ./.github/workflows/consumer_test.yml + unit-tests: + uses: ./.github/workflows/test.yml secrets: inherit docs-build: # Waits for consumer-tests but run only when docs verification succeeded - needs: [docs-verify, consumer-tests] + needs: [docs-verify, unit-tests] if: ${{ always() && needs.docs-verify.result == 'success' }} uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main permissions: @@ -55,4 +55,3 @@ jobs: with: bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 - tests-report-artifact: tests-report From d171adfae869692683c613007adb30f107a8ed9b Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 11:28:34 +0100 Subject: [PATCH 2/8] Missing thing --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31ea1171d..0ec162d4e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ name: Run Bazel Tests on: pull_request: types: [opened, reopened, synchronize] + workflow_call: jobs: code: runs-on: ubuntu-latest From e9a5ae3496ee96b9172d3bbb9af60b520c89e2dd Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 12:04:32 +0100 Subject: [PATCH 3/8] Maybe fixed? --- .github/workflows/test_and_docs.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 9dbf763f9..4ba68340c 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -38,20 +38,18 @@ jobs: bazel-docs-verify-target: "//:docs_check" # This is the user configurable part of the workflow - unit-tests: - uses: ./.github/workflows/test.yml - secrets: inherit docs-build: # Waits for consumer-tests but run only when docs verification succeeded needs: [docs-verify, unit-tests] if: ${{ always() && needs.docs-verify.result == 'success' }} - uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main + uses: maximiliansoerenpollak/cicd-workflows/.github/workflows/docs.yml@main permissions: contents: write pages: write pull-requests: write id-token: write with: + pre-build-command: "bazel run //:ide_support && bazel test //src/..." bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 From c54afbd4adf9a1499417d6299be516cc6c68a287 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 12:06:04 +0100 Subject: [PATCH 4/8] fixed --- .github/workflows/test_and_docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 4ba68340c..619a81093 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -41,7 +41,7 @@ jobs: docs-build: # Waits for consumer-tests but run only when docs verification succeeded - needs: [docs-verify, unit-tests] + needs: [docs-verify] if: ${{ always() && needs.docs-verify.result == 'success' }} uses: maximiliansoerenpollak/cicd-workflows/.github/workflows/docs.yml@main permissions: From 586bfe0cb252290e1fcb1d1b58482c62807dd470 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 12:32:42 +0100 Subject: [PATCH 5/8] Reference other workflow --- .github/workflows/test_and_docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 619a81093..2ce5ad8ba 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -43,13 +43,13 @@ jobs: # Waits for consumer-tests but run only when docs verification succeeded needs: [docs-verify] if: ${{ always() && needs.docs-verify.result == 'success' }} - uses: maximiliansoerenpollak/cicd-workflows/.github/workflows/docs.yml@main + uses: maximiliansoerenpollak/cicd-workflows/.github/workflows/docs_with_test.yml@main permissions: contents: write pages: write pull-requests: write id-token: write with: - pre-build-command: "bazel run //:ide_support && bazel test //src/..." + bazel-test-target: "//src/..." bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 From 28a4878d901e1fe0c67c0e1497a8fb9a661df5e4 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Mon, 12 Jan 2026 12:40:32 +0100 Subject: [PATCH 6/8] Adding venv target --- .github/workflows/test_and_docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 2ce5ad8ba..474030e45 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -50,6 +50,7 @@ jobs: pull-requests: write id-token: write with: + bazel-venv-target: "//:ide_support" bazel-test-target: "//src/..." bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 From 5a13adde724df577791a92119bed7fdb030e46f2 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Tue, 13 Jan 2026 10:57:36 +0100 Subject: [PATCH 7/8] Testing tests-reports generation --- .github/workflows/test.yml | 22 +++++++++++++++++++ .github/workflows/test_and_docs.yml | 10 +++++---- .../score_source_code_linker/xml_parser.py | 14 +++++++++--- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ec162d4e..dd665e8b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,3 +40,25 @@ jobs: run: | bazel run //:ide_support bazel test //src/... + + - name: Prepare bundled consumer report + if: always() + run: | + # 1. Create the target directory + mkdir -p tests-report + + # 2. Sync the testlogs to our artifact folder + # We use -L to follow the symlink and --include filters + # to only grab the actual results (XMLs and Logs) + rsync -amL --include='*/' --include='test.xml' --include='test.log' --exclude='*' bazel-testlogs/ tests-report/ + + # 3. Optional: If your tool prefers the folder name to match the Consumer Tests + # you can rename specific subfolders here, but rsync preserves the package paths. + + - name: Upload bundled consumer report + if: always() + uses: actions/upload-artifact@v4 + with: + name: tests-report + path: tests-report + diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index 474030e45..c52438950 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -38,19 +38,21 @@ jobs: bazel-docs-verify-target: "//:docs_check" # This is the user configurable part of the workflow + unit-tests: + uses: ./.github/workflows/test.yml + secrets: inherit docs-build: # Waits for consumer-tests but run only when docs verification succeeded - needs: [docs-verify] + needs: [docs-verify, unit-tests] if: ${{ always() && needs.docs-verify.result == 'success' }} - uses: maximiliansoerenpollak/cicd-workflows/.github/workflows/docs_with_test.yml@main + uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main permissions: contents: write pages: write pull-requests: write id-token: write with: - bazel-venv-target: "//:ide_support" - bazel-test-target: "//src/..." bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 + tests-report-artifact: tests-report diff --git a/src/extensions/score_source_code_linker/xml_parser.py b/src/extensions/score_source_code_linker/xml_parser.py index 53c18b23e..c875e9721 100644 --- a/src/extensions/score_source_code_linker/xml_parser.py +++ b/src/extensions/score_source_code_linker/xml_parser.py @@ -161,7 +161,7 @@ def find_xml_files(dir: Path) -> list[Path]: Returns: - list[Path] => Paths to all found 'test.xml' files. """ - + # Note: This maybe in the future should also be able to be customized test_file_name = "test.xml" xml_paths: list[Path] = [] @@ -179,8 +179,16 @@ def run_xml_parser(app: Sphinx, env: BuildEnvironment): """ ws_root = find_ws_root() assert ws_root is not None - bazel_testlogs = ws_root / "bazel-testlogs" - xml_file_paths = find_xml_files(bazel_testlogs) + # We also have to allow for 'tests-report' to be viable path + if os.path.isdir(ws_root/"tests-report"): + testlogs_dir = ws_root / "tests-report" + elif os.path.isdir(ws_root/"bazel-testlogs"): + testlogs_dir = ws_root / "bazel-testlogs" + else: + # TODO: Figure out if this brings issues with it. + logger.info("could not find tests-report or bazel-testlogs to parse testcases") + return + xml_file_paths = find_xml_files(testlogs_dir) test_case_needs = build_test_needs_from_files(app, env, xml_file_paths) # Saving the test case needs for cache store_data_of_test_case_json( From 478e1ce437ea524edea916be14f1dc466d21fa57 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Tue, 13 Jan 2026 17:01:04 +0100 Subject: [PATCH 8/8] Test fixing? --- .../tests/test_xml_parser.py | 386 +++++++++++------- .../score_source_code_linker/xml_parser.py | 26 +- 2 files changed, 244 insertions(+), 168 deletions(-) diff --git a/src/extensions/score_source_code_linker/tests/test_xml_parser.py b/src/extensions/score_source_code_linker/tests/test_xml_parser.py index c234e08bf..c875e9721 100644 --- a/src/extensions/score_source_code_linker/tests/test_xml_parser.py +++ b/src/extensions/score_source_code_linker/tests/test_xml_parser.py @@ -11,175 +11,249 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* """ -Tests for the xml_parser.py file. -Keep in mind that this is with the 'assertions' inside xml_parser disabled so far. -Once we enable those we will need to change the tests +This file deals with finding and parsing of test.xml files that get created during +`bazel test`. It also generates external needs out of the parsed testcases to enable +linking to requirements &gathering statistics """ +# req-Id: tool_req__docs_test_link_testcase + +import base64 +import contextlib +import hashlib +import itertools +import os import xml.etree.ElementTree as ET from pathlib import Path from typing import Any +from xml.etree.ElementTree import Element + +from sphinx.application import Sphinx +from sphinx.environment import BuildEnvironment +from sphinx_needs import logging +from sphinx_needs.api import add_external_need + +from src.extensions.score_source_code_linker.testlink import ( + DataOfTestCase, + store_data_of_test_case_json, + store_test_xml_parsed_json, +) +from src.helper_lib import find_ws_root +from src.helper_lib.additional_functions import get_github_link -import pytest - -# This depends on the `attribute_plugin` in our tooling repository -from attribute_plugin import add_test_properties # type: ignore[import-untyped] - -import src.extensions.score_source_code_linker.xml_parser as xml_parser -from src.extensions.score_source_code_linker.testlink import DataOfTestCase - - -# Unsure if I should make these last a session or not -@pytest.fixture -def tmp_xml_dirs(tmp_path: Path) -> tuple[Path, Path, Path]: - root: Path = tmp_path / "bazel-testlogs" - dir1: Path = root / "with_props" - dir2: Path = root / "no_props" - dir1.mkdir(parents=True) - dir2.mkdir(parents=True) - - def write(file_path: Path, testcases: list[ET.Element]): - ts = ET.Element("testsuites") - suite = ET.SubElement(ts, "testsuite") - for tc in testcases: - suite.append(tc) - tree = ET.ElementTree(ts) - tree.write(file_path, encoding="utf-8", xml_declaration=True) - - def make_tc( - name: str, - result: str = "", - props: dict[str, str] | None = None, - file: str = "", - line: int = 0, - ): - tc = ET.Element("testcase", {"name": name}) - if file: - tc.set("file", file) - if line: - tc.set("line", str(line)) - if result == "failed": - ET.SubElement(tc, "failure", {"message": "failmsg"}) - elif result == "skipped": - ET.SubElement(tc, "skipped", {"message": "skipmsg"}) - if props: - props_el = ET.SubElement(tc, "properties") - for k, v in props.items(): - ET.SubElement(props_el, "property", {"name": k, "value": v}) - return tc - - # File with properties - tc1 = make_tc( - "tc_with_props", - result="failed", - props={ - "PartiallyVerifies": "REQ1", - "FullyVerifies": "", - "TestType": "type", - "DerivationTechnique": "tech", - "Description": "desc", - }, - file="path1", - line=10, +logger = logging.get_logger(__name__) +logger.setLevel("DEBUG") + + +def parse_testcase_result(testcase: ET.Element) -> tuple[str, str]: + """ + Returns 'result' and 'result_text' found in the 'message' + attribute of the result. + Example: + => + + Returns: + ("skipped", "Test skip message") + """ + skipped = testcase.find("skipped") + failed = testcase.find("failure") + status = testcase.get("status") + # NOTE: Special CPP case of 'disabled' + if status is not None and status == "notrun": + return "disabled", "" + if skipped is None and failed is None: + return "passed", "" + if failed is not None: + return "failed", failed.get("message", "") + if skipped is not None: + return "skipped", skipped.get("message", "") + # TODO: Test all possible permuations of this to find if this is unreachable + raise ValueError( + f"Testcase: {testcase.get('name')}. " + "Did not find 'failed', 'skipped' or 'passed' in test" ) - write(dir1 / "test.xml", [tc1]) - # File without properties - # HINT: Once the assertions in xml_parser are back and active, this should allow us - # to catch that the tests Need to be changed too. - tc2 = make_tc("tc_no_props", file="path2", line=20) - write(dir2 / "test.xml", [tc2]) - return root, dir1, dir2 +def parse_properties(case_properties: dict[str, Any], properties: Element): + for prop in properties: + prop_name = prop.get("name", "") + prop_value = prop.get("value", "") + # We ignore the Description of the test as a 'property'. + # Every language just needs to ensure each test does have a description. + # No matter where this resides. + if prop_name == "Description": + continue + case_properties[prop_name] = prop_value + return case_properties -@add_test_properties( - partially_verifies=["tool_req__docs_test_link_testcase"], - test_type="requirements-based", - derivation_technique="requirements-analysis", -) -def test_find_xml_files(tmp_xml_dirs: tuple[Path, Path, Path]): - """Ensure xml files are found as expected""" - root: Path - dir1: Path - dir2: Path - root, dir1, dir2 = tmp_xml_dirs - found = xml_parser.find_xml_files(root) - expected: set[Path] = {dir1 / "test.xml", dir2 / "test.xml"} - assert set(found) == expected - - -@add_test_properties( - partially_verifies=["tool_req__docs_test_link_testcase"], - test_type="requirements-based", - derivation_technique="requirements-analysis", -) -def test_parse_testcase_result(): - """Ensure Testcase results are parsed as intended""" - tc = ET.Element("testcase", {"name": "a"}) - assert xml_parser.parse_testcase_result(tc) == ("passed", "") +def read_test_xml_file(file: Path) -> tuple[list[DataOfTestCase], list[str]]: + """ + Reading & parsing the test.xml files into TestCaseNeeds - tc2 = ET.Element("testcase", {"name": "b", "status": "notrun"}) - assert xml_parser.parse_testcase_result(tc2) == ("disabled", "") + Returns: + tuple consisting of: + - list[TestCaseNeed] + - list[str] => Testcase Names that did not have the required properties. + """ + test_case_needs: list[DataOfTestCase] = [] + non_prop_tests: list[str] = [] + tree = ET.parse(file) + root = tree.getroot() - tc3 = ET.Element("testcase", {"name": "c"}) - ET.SubElement(tc3, "failure", {"message": "err"}) - assert xml_parser.parse_testcase_result(tc3) == ("failed", "err") + for testsuite in root.findall("testsuite"): + for testcase in testsuite.findall("testcase"): + case_properties = {} + testname = testcase.get("name") + assert testname is not None, ( + f"Testcase: {testcase} does not have a 'name' attribute. " + "This is mandatory. This should not happen, something is wrong." + ) + test_file = testcase.get("file") + line = testcase.get("line") - tc4 = ET.Element("testcase", {"name": "d"}) - ET.SubElement(tc4, "skipped", {"message": "skp"}) - assert xml_parser.parse_testcase_result(tc4) == ("skipped", "skp") + # ╭──────────────────────────────────────╮ + # │ Assert worldview that mandatory │ + # │ things are actually there │ + # │ Disabled temporarily │ + # ╰──────────────────────────────────────╯ + # assert test_file is not None, ( + # f"Testcase: {testname} does not have a 'file' attribute. " + # "This is mandatory" + # ) + # assert lineNr is not None, ( + # f"Testcase: {testname} located in {test_file} does not have a " + # "'lineNr' attribute. This is mandatory" + # ) + case_properties["name"] = testname + case_properties["file"] = test_file + case_properties["line"] = line + case_properties["result"], case_properties["result_text"] = ( + parse_testcase_result(testcase) + ) -@add_test_properties( - partially_verifies=["tool_req__docs_test_link_testcase"], - test_type="requirements-based", - derivation_technique="requirements-analysis", -) -def test_parse_properties(): - """Ensure properties of testcases are parsed as intended""" - cp: dict[str, Any] = {} - props_el = ET.Element("properties") - ET.SubElement(props_el, "property", {"name": "A", "value": "1"}) - ET.SubElement(props_el, "property", {"name": "Description", "value": "ignored"}) - res = xml_parser.parse_properties(cp, props_el) - assert res["A"] == "1" - assert "Description" not in res - - -@add_test_properties( - partially_verifies=["tool_req__docs_test_link_testcase"], - test_type="requirements-based", - derivation_technique="requirements-analysis", -) -def test_read_test_xml_file(tmp_xml_dirs: tuple[Path, Path, Path]): - """Ensure a whole pre-defined xml file is parsed correctly""" - _: Path - dir1: Path - dir2: Path - _, dir1, dir2 = tmp_xml_dirs - - needs1, no_props1 = xml_parser.read_test_xml_file(dir1 / "test.xml") - assert isinstance(needs1, list) and len(needs1) == 1 - tcneed = needs1[0] - assert isinstance(tcneed, DataOfTestCase) - assert tcneed.result == "failed" - assert no_props1 == [] - - needs2, no_props2 = xml_parser.read_test_xml_file(dir2 / "test.xml") - assert needs2 == [] - assert no_props2 == ["tc_no_props"] - - -@add_test_properties( - partially_verifies=["tool_req__docs_test_link_testcase"], - test_type="requirements-based", - derivation_technique="requirements-analysis", -) -def test_short_hash_consistency_and_format(): - """Ensure shorthash has the intended format""" - h1 = xml_parser.short_hash("foo") - h2 = xml_parser.short_hash("foo") - assert h1 == h2 - assert h1.isalpha() - assert len(h1) == 5 + properties_element = testcase.find("properties") + # HINT: This list is hard coded here, might not be ideal to have that in the + # long run. + if properties_element is None: + non_prop_tests.append(testname) + continue + + # ╓ ╖ + # ║ Disabled Temporarily ║ + # ╙ ╜ + # assert properties_element is not None, ( + # f"Testcase: {testname} located in {test_file}:{lineNr}, does not " + # "have any properties. Properties 'TestType', 'DerivationTechnique' " + # "and either 'PartiallyVerifies' or 'FullyVerifies' are mandatory." + # ) + + case_properties = parse_properties(case_properties, properties_element) + test_case_needs.append(DataOfTestCase.from_dict(case_properties)) + return test_case_needs, non_prop_tests + + +def find_xml_files(dir: Path) -> list[Path]: + """ + Recursively search all test.xml files inside 'bazel-testlogs' + + Returns: + - list[Path] => Paths to all found 'test.xml' files. + """ + # Note: This maybe in the future should also be able to be customized + test_file_name = "test.xml" + + xml_paths: list[Path] = [] + for root, _, files in os.walk(dir): + if test_file_name in files: + xml_paths.append(Path(os.path.join(root, test_file_name))) + return xml_paths + + +def run_xml_parser(app: Sphinx, env: BuildEnvironment): + """ + This is the 'main' function for parsing test.xml's and + building testcase needs. + It gets called from the source_code_linker __init__ + """ + ws_root = find_ws_root() + assert ws_root is not None + # We also have to allow for 'tests-report' to be viable path + if os.path.isdir(ws_root/"tests-report"): + testlogs_dir = ws_root / "tests-report" + elif os.path.isdir(ws_root/"bazel-testlogs"): + testlogs_dir = ws_root / "bazel-testlogs" + else: + # TODO: Figure out if this brings issues with it. + logger.info("could not find tests-report or bazel-testlogs to parse testcases") + return + xml_file_paths = find_xml_files(testlogs_dir) + test_case_needs = build_test_needs_from_files(app, env, xml_file_paths) + # Saving the test case needs for cache + store_data_of_test_case_json( + app.outdir / "score_testcaseneeds_cache.json", test_case_needs + ) + output = list( + itertools.chain.from_iterable(tcn.get_test_links() for tcn in test_case_needs) + ) + # This is not ideal, due to duplication, but I can't think of a better solution + # right now + store_test_xml_parsed_json(app.outdir / "score_xml_parser_cache.json", output) + + +def build_test_needs_from_files( + app: Sphinx, env: BuildEnvironment, xml_paths: list[Path] +) -> list[DataOfTestCase]: + """ + Reading in all test.xml files, and building 'testcase' external need objects out of + them. + + Returns: + - list[TestCaseNeed] + """ + tcns: list[DataOfTestCase] = [] + for f in xml_paths: + b, z = read_test_xml_file(f) + non_prop_tests = ", ".join(n for n in z) + if non_prop_tests: + logger.info(f"Tests missing properties: {non_prop_tests}") + tcns.extend(b) + for c in b: + construct_and_add_need(app, c) + return tcns + + +def short_hash(input_str: str, length: int = 5) -> str: + # Get a stable hash + sha256 = hashlib.sha256(input_str.encode()).digest() + # Encode to base32 (A-Z + 2-7), decode to str, remove padding + b32 = base64.b32encode(sha256).decode("utf-8").rstrip("=") + # Keep only alphabetic characters + letters_only = "".join(filter(str.isalpha, b32)) + # Return the first `length` letters + return letters_only[:length].lower() + + +def construct_and_add_need(app: Sphinx, tn: DataOfTestCase): + # IDK if this is ideal or not + with contextlib.suppress(BaseException): + _ = add_external_need( + app=app, + need_type="testcase", + title=tn.name, + tags="TEST", + id=f"testcase__{tn.name}_{short_hash(tn.file + tn.name).upper()}", + name=tn.name, + external_url=get_github_link(tn), + fully_verifies=tn.FullyVerifies if tn.FullyVerifies is not None else "", + partially_verifies=tn.PartiallyVerifies + if tn.PartiallyVerifies is not None + else "", + test_type=tn.TestType, + derivation_technique=tn.DerivationTechnique, + file=tn.file, + line=tn.line, + result=tn.result, # We just want the 'failed' or whatever + result_text=tn.result_text if tn.result_text else "", + ) diff --git a/src/extensions/score_source_code_linker/xml_parser.py b/src/extensions/score_source_code_linker/xml_parser.py index c875e9721..d5a9b3b6b 100644 --- a/src/extensions/score_source_code_linker/xml_parser.py +++ b/src/extensions/score_source_code_linker/xml_parser.py @@ -170,24 +170,26 @@ def find_xml_files(dir: Path) -> list[Path]: xml_paths.append(Path(os.path.join(root, test_file_name))) return xml_paths +def find_test_folder(base_path: Path|None = None) -> Path|None: + ws_root = base_path if base_path is not None else find_ws_root() + assert ws_root is not None + if os.path.isdir(ws_root/"tests-report"): + return ws_root / "tests-report" + if os.path.isdir(ws_root/"bazel-testlogs"): + return ws_root / "bazel-testlogs" + logger.info("could not find tests-report or bazel-testlogs to parse testcases") + return None + def run_xml_parser(app: Sphinx, env: BuildEnvironment): """ This is the 'main' function for parsing test.xml's and building testcase needs. It gets called from the source_code_linker __init__ - """ - ws_root = find_ws_root() - assert ws_root is not None - # We also have to allow for 'tests-report' to be viable path - if os.path.isdir(ws_root/"tests-report"): - testlogs_dir = ws_root / "tests-report" - elif os.path.isdir(ws_root/"bazel-testlogs"): - testlogs_dir = ws_root / "bazel-testlogs" - else: - # TODO: Figure out if this brings issues with it. - logger.info("could not find tests-report or bazel-testlogs to parse testcases") - return + """ + testlogs_dir = find_test_folder() + if testlogs_dir is None: + return xml_file_paths = find_xml_files(testlogs_dir) test_case_needs = build_test_needs_from_files(app, env, xml_file_paths) # Saving the test case needs for cache