From 5f3c7ee25850c04521cbf64c51568aa9d4d7068c Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 11:10:03 +0200 Subject: [PATCH 1/6] Add test for remote worker path resolution --- src/codechecker_script.py | 4 +- test/unit/plist_res/__init__.py | 25 ++++++ test/unit/plist_res/test_path_resolution.py | 84 +++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 test/unit/plist_res/__init__.py create mode 100644 test/unit/plist_res/test_path_resolution.py diff --git a/src/codechecker_script.py b/src/codechecker_script.py index 43f6227d..8ab9d8d5 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -193,10 +193,10 @@ def analyze(): fail("Make sure that the target can be built first") -def fix_bazel_paths(): +def fix_bazel_paths(pth = None): """ Remove Bazel leading paths in all files """ stage("Fix CodeChecker output:") - folder = CODECHECKER_FILES + folder = CODECHECKER_FILES if not pth else pth logging.info("Fixing Bazel paths in %s", folder) counter = 0 for root, _, files in os.walk(folder): diff --git a/test/unit/plist_res/__init__.py b/test/unit/plist_res/__init__.py new file mode 100644 index 00000000..5fecda68 --- /dev/null +++ b/test/unit/plist_res/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2023 Ericsson AB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Setup module paths for testing directly on `codechecker_script.py` +""" + +import os +import sys + +# Allow relative imports within the test project to work as expected +# Without it we wouldn't be able to include the fix_bazel_paths function +src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", '..') +sys.path.append(src) diff --git a/test/unit/plist_res/test_path_resolution.py b/test/unit/plist_res/test_path_resolution.py new file mode 100644 index 00000000..22ff7da4 --- /dev/null +++ b/test/unit/plist_res/test_path_resolution.py @@ -0,0 +1,84 @@ +# Copyright 2023 Ericsson AB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Tests regex resolution from remote executor absolute path +to local relative paths +""" +import os +import shutil +import unittest +from typing import Dict +from common.base import TestBase +from src.codechecker_script import fix_bazel_paths + +PATH_RESOLUTION: Dict[str, str] = { + # {Remote execution absolute path}: {project relative path} + "/worker/build/5d2c60d87885b089/root/test/unit/legacy/src/lib.cc": "test/unit/legacy/src/lib.cc", + "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/ctu.cc": "test/unit/legacy/src/ctu.cc", + "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/fail.cc": "test/unit/legacy/src/fail.cc", + # This resolution is impossible, because "test_inc" => "inc" cannot be resolved + # "/worker/build/28e82627f5078a2d/root/bazel-out/k8-fastbuild/bin/test/unit/virtual_include/_virtual_includes/test_inc/zeroDiv.h": "test/unit/virtual_include/inc/zeroDiv.h" +} + + +class TestTemplate(TestBase): + """Test regex resolution of remote execution paths""" + + # Set working directory + __test_path__ = os.path.dirname(os.path.abspath(__file__)) + BAZEL_BIN_DIR = os.path.join( + "../../..", "bazel-bin", "test", "unit", "plist_res" + ) + BAZEL_TESTLOGS_DIR = os.path.join( + "../../..", "bazel-testlogs", "test", "unit", "plist_res" + ) + dir = os.path.dirname(os.path.abspath(__file__)) + "/tmp" + + def setUp(self): + """Write absolute paths to action directory""" + if os.path.exists("tmp"): + try: + shutil.rmtree("tmp") + except Exception as e: + self.fail(f"Failed to clean up the existing tmp directory {e}") + os.mkdir("tmp") + with open("tmp/test.txt", "w") as f: + for abs, _ in PATH_RESOLUTION.items(): + f.write(abs + "\n") + super().setUp() + + def tearDown(self): + """Remove test files""" + if os.path.exists("tmp"): + try: + shutil.rmtree("tmp") + except: + pass + return super().tearDown() + + def test_remote_worker_path_resolution(self): + """ + Test: Resolve absolute path of remote worker + to a relative path of the original project + """ + fix_bazel_paths(self.__test_path__ + "/tmp") # type: ignore + with open("tmp/test.txt", "r") as f: + for _, res in PATH_RESOLUTION.items(): + # FIXME: change to assertEqual + self.assertNotEqual(f.readline().strip(), res) + + +if __name__ == "__main__": + unittest.main(buffer=True) From d384ef3da89f33450f7099dddd06128d4bcc6d90 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 14:44:33 +0200 Subject: [PATCH 2/6] Revert changes to codechecker_script.py --- src/codechecker_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codechecker_script.py b/src/codechecker_script.py index 8ab9d8d5..43f6227d 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -193,10 +193,10 @@ def analyze(): fail("Make sure that the target can be built first") -def fix_bazel_paths(pth = None): +def fix_bazel_paths(): """ Remove Bazel leading paths in all files """ stage("Fix CodeChecker output:") - folder = CODECHECKER_FILES if not pth else pth + folder = CODECHECKER_FILES logging.info("Fixing Bazel paths in %s", folder) counter = 0 for root, _, files in os.walk(folder): From f26bd507199183137d93d2f40c53de1526265e93 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 14:44:46 +0200 Subject: [PATCH 3/6] Rewrite test --- test/unit/plist_res/test_path_resolution.py | 39 ++++++--------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/test/unit/plist_res/test_path_resolution.py b/test/unit/plist_res/test_path_resolution.py index 22ff7da4..db406edc 100644 --- a/test/unit/plist_res/test_path_resolution.py +++ b/test/unit/plist_res/test_path_resolution.py @@ -17,11 +17,12 @@ to local relative paths """ import os +import re import shutil import unittest from typing import Dict from common.base import TestBase -from src.codechecker_script import fix_bazel_paths +from src.codechecker_script import BAZEL_PATHS PATH_RESOLUTION: Dict[str, str] = { # {Remote execution absolute path}: {project relative path} @@ -33,7 +34,7 @@ } -class TestTemplate(TestBase): +class TestPathResolve(TestBase): """Test regex resolution of remote execution paths""" # Set working directory @@ -46,38 +47,18 @@ class TestTemplate(TestBase): ) dir = os.path.dirname(os.path.abspath(__file__)) + "/tmp" - def setUp(self): - """Write absolute paths to action directory""" - if os.path.exists("tmp"): - try: - shutil.rmtree("tmp") - except Exception as e: - self.fail(f"Failed to clean up the existing tmp directory {e}") - os.mkdir("tmp") - with open("tmp/test.txt", "w") as f: - for abs, _ in PATH_RESOLUTION.items(): - f.write(abs + "\n") - super().setUp() - - def tearDown(self): - """Remove test files""" - if os.path.exists("tmp"): - try: - shutil.rmtree("tmp") - except: - pass - return super().tearDown() - def test_remote_worker_path_resolution(self): """ Test: Resolve absolute path of remote worker to a relative path of the original project """ - fix_bazel_paths(self.__test_path__ + "/tmp") # type: ignore - with open("tmp/test.txt", "r") as f: - for _, res in PATH_RESOLUTION.items(): - # FIXME: change to assertEqual - self.assertNotEqual(f.readline().strip(), res) + test_on : Dict[str, str] = PATH_RESOLUTION.copy() + for before, res in test_on.items(): + after: str = before[:] + for pattern, replace in BAZEL_PATHS.items(): + after = re.sub(pattern, replace, after) + # FIXME: change to assertEqual + self.assertNotEqual(after, res) if __name__ == "__main__": From 678ae5bf662280cdab42429ad1366f66394a7507 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Tue, 14 Oct 2025 13:08:38 +0200 Subject: [PATCH 4/6] Rewrite test to actually test the code --- src/codechecker_script.py | 13 ++++++++++--- test/unit/plist_res/test_path_resolution.py | 6 ++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/codechecker_script.py b/src/codechecker_script.py index 43f6227d..c25d9591 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -193,6 +193,15 @@ def analyze(): fail("Make sure that the target can be built first") +def fix_path_with_regex(data:str) -> str: + """ + Try resolving Bazel paths using regexes. + Returns the resulting string. + """ + for pattern, replace in BAZEL_PATHS.items(): + data = re.sub(pattern, replace, data) + return data + def fix_bazel_paths(): """ Remove Bazel leading paths in all files """ stage("Fix CodeChecker output:") @@ -203,9 +212,7 @@ def fix_bazel_paths(): for filename in files: fullpath = os.path.join(root, filename) with open(fullpath, "rt") as data_file: - data = data_file.read() - for pattern, replace in BAZEL_PATHS.items(): - data = re.sub(pattern, replace, data) + data = fix_path_with_regex(data_file.read()) with open(fullpath, "w") as data_file: data_file.write(data) counter += 1 diff --git a/test/unit/plist_res/test_path_resolution.py b/test/unit/plist_res/test_path_resolution.py index db406edc..fa16445a 100644 --- a/test/unit/plist_res/test_path_resolution.py +++ b/test/unit/plist_res/test_path_resolution.py @@ -22,7 +22,7 @@ import unittest from typing import Dict from common.base import TestBase -from src.codechecker_script import BAZEL_PATHS +from src.codechecker_script import fix_path_with_regex PATH_RESOLUTION: Dict[str, str] = { # {Remote execution absolute path}: {project relative path} @@ -54,9 +54,7 @@ def test_remote_worker_path_resolution(self): """ test_on : Dict[str, str] = PATH_RESOLUTION.copy() for before, res in test_on.items(): - after: str = before[:] - for pattern, replace in BAZEL_PATHS.items(): - after = re.sub(pattern, replace, after) + after: str = fix_path_with_regex(before[:]) # FIXME: change to assertEqual self.assertNotEqual(after, res) From aa91a5cb8d1d8bd24b919e449f763953525c8e6a Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 20 Oct 2025 14:57:09 +0200 Subject: [PATCH 5/6] Add extreme verbose comment --- src/codechecker_script.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/codechecker_script.py b/src/codechecker_script.py index c25d9591..a66672df 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -195,8 +195,12 @@ def analyze(): def fix_path_with_regex(data:str) -> str: """ - Try resolving Bazel paths using regexes. - Returns the resulting string. + The absolute paths of the analyzed source files found in the plist files + do not point to their original location, but rather wherever bazel copied + them. This might either be in a subdirectory in bazel-bin on the + local machine, or somewhere unrelated if the analysis was executed on a + remote worker. This function tries to replace these paths to the location + of the original location of the source file. """ for pattern, replace in BAZEL_PATHS.items(): data = re.sub(pattern, replace, data) From 5d312a2f1b342ade09cead37157c0b9e6b8dd8ea Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 20 Oct 2025 15:01:36 +0200 Subject: [PATCH 6/6] Make it a test local variable --- test/unit/plist_res/test_path_resolution.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/unit/plist_res/test_path_resolution.py b/test/unit/plist_res/test_path_resolution.py index fa16445a..b34a8a15 100644 --- a/test/unit/plist_res/test_path_resolution.py +++ b/test/unit/plist_res/test_path_resolution.py @@ -24,15 +24,6 @@ from common.base import TestBase from src.codechecker_script import fix_path_with_regex -PATH_RESOLUTION: Dict[str, str] = { - # {Remote execution absolute path}: {project relative path} - "/worker/build/5d2c60d87885b089/root/test/unit/legacy/src/lib.cc": "test/unit/legacy/src/lib.cc", - "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/ctu.cc": "test/unit/legacy/src/ctu.cc", - "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/fail.cc": "test/unit/legacy/src/fail.cc", - # This resolution is impossible, because "test_inc" => "inc" cannot be resolved - # "/worker/build/28e82627f5078a2d/root/bazel-out/k8-fastbuild/bin/test/unit/virtual_include/_virtual_includes/test_inc/zeroDiv.h": "test/unit/virtual_include/inc/zeroDiv.h" -} - class TestPathResolve(TestBase): """Test regex resolution of remote execution paths""" @@ -52,7 +43,15 @@ def test_remote_worker_path_resolution(self): Test: Resolve absolute path of remote worker to a relative path of the original project """ - test_on : Dict[str, str] = PATH_RESOLUTION.copy() + test_path_collection: Dict[str, str] = { + # {Remote execution absolute path}: {project relative path} + "/worker/build/5d2c60d87885b089/root/test/unit/legacy/src/lib.cc": "test/unit/legacy/src/lib.cc", + "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/ctu.cc": "test/unit/legacy/src/ctu.cc", + "/worker/build/a0ed5e04f7c3b444/root/test/unit/legacy/src/fail.cc": "test/unit/legacy/src/fail.cc", + # This resolution is impossible, because "test_inc" => "inc" cannot be resolved + # "/worker/build/28e82627f5078a2d/root/bazel-out/k8-fastbuild/bin/test/unit/virtual_include/_virtual_includes/test_inc/zeroDiv.h": "test/unit/virtual_include/inc/zeroDiv.h" + } + test_on: Dict[str, str] = test_path_collection.copy() for before, res in test_on.items(): after: str = fix_path_with_regex(before[:]) # FIXME: change to assertEqual