From a4f3b80d473bd1ac416a6e744d39a801184a4502 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 11:10:03 +0200 Subject: [PATCH 1/7] 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 4860a283..11571167 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -205,10 +205,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 017b4ba8ab6baf9f6a1be6c4741f4f82778168f2 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 14:44:33 +0200 Subject: [PATCH 2/7] 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 11571167..4860a283 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -205,10 +205,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 2ab6fda393fcebcb40852311a0a0cf0c8813663f Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 14:44:46 +0200 Subject: [PATCH 3/7] 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 6cc70758e439919f1cbc052e029b9fa370bb987b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Tue, 14 Oct 2025 13:08:38 +0200 Subject: [PATCH 4/7] 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 4860a283..f4e1b00c 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -205,6 +205,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:") @@ -215,9 +224,7 @@ def fix_bazel_paths(): for filename in files: fullpath = os.path.join(root, filename) with open(fullpath, "rt", encoding="utf-8") 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", encoding="utf-8") 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 8f9b7fa063d685ed581143ad761894d971be75e4 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 20 Oct 2025 14:57:09 +0200 Subject: [PATCH 5/7] 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 f4e1b00c..ce63f26b 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -207,8 +207,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 8070414a40e926ba2e669aef73394c4a55add5d5 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 20 Oct 2025 15:01:36 +0200 Subject: [PATCH 6/7] 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 From d4ce1b3217608ac2af41dbea4adcbbff0760d26b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Thu, 30 Apr 2026 20:45:44 +0200 Subject: [PATCH 7/7] Fix pylint issues --- .pylintrc | 2 +- test/unit/plist_res/test_path_resolution.py | 27 +++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.pylintrc b/.pylintrc index 8edda601..a8a353ac 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,6 +1,6 @@ [MASTER] # To find common lib for tests -init-hook='import sys; sys.path.append("test")' +init-hook='import sys; import os; sys.path.append("test"); sys.path.append(os.getcwd())' ignore = test_template.py [FORMAT] diff --git a/test/unit/plist_res/test_path_resolution.py b/test/unit/plist_res/test_path_resolution.py index b34a8a15..df2e76c9 100644 --- a/test/unit/plist_res/test_path_resolution.py +++ b/test/unit/plist_res/test_path_resolution.py @@ -16,9 +16,8 @@ Tests regex resolution from remote executor absolute path to local relative paths """ + import os -import re -import shutil import unittest from typing import Dict from common.base import TestBase @@ -45,11 +44,25 @@ def test_remote_worker_path_resolution(self): """ 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" + ( + "/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():