diff --git a/ament_clang_format/ament_clang_format/main.py b/ament_clang_format/ament_clang_format/main.py index 87120628..671742f1 100755 --- a/ament_clang_format/ament_clang_format/main.py +++ b/ament_clang_format/ament_clang_format/main.py @@ -23,6 +23,7 @@ from xml.sax.saxutils import escape from xml.sax.saxutils import quoteattr +from ament_lint.filesystem_helpers import find_executable import yaml @@ -245,16 +246,6 @@ def main(argv=sys.argv[1:]): return rc -def find_executable(file_names): - paths = os.getenv('PATH').split(os.path.pathsep) - for file_name in file_names: - for path in paths: - file_path = os.path.join(path, file_name) - if os.path.isfile(file_path) and os.access(file_path, os.X_OK): - return file_path - return None - - def get_files(paths, extensions): files = [] for path in paths: diff --git a/ament_clang_format/package.xml b/ament_clang_format/package.xml index 68f97ef8..37a229f7 100644 --- a/ament_clang_format/package.xml +++ b/ament_clang_format/package.xml @@ -17,6 +17,7 @@ Dirk Thomas Michel Hidalgo + ament_lint clang-format python3-yaml diff --git a/ament_clang_format/test/test_execution.py b/ament_clang_format/test/test_execution.py new file mode 100644 index 00000000..3358460f --- /dev/null +++ b/ament_clang_format/test/test_execution.py @@ -0,0 +1,23 @@ +# Copyright 2025 Open Source Robotics Foundation, Inc. +# +# 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. + +from ament_clang_format.main import main +from ament_lint.test_helpers import TempFileWriter + + +def test_clang_format_execution(): + """Test that clang format can be executed on a simple C++ file, via ament_clang_format.""" + with TempFileWriter('int main() { return 0; }', 'test.cpp') as temp_file_path: + rc = main(argv=['ament_clang_format', temp_file_path]) + assert rc == 0, 'Clang format found issues' diff --git a/ament_clang_tidy/ament_clang_tidy/main.py b/ament_clang_tidy/ament_clang_tidy/main.py index 5faa8142..9d9c978d 100755 --- a/ament_clang_tidy/ament_clang_tidy/main.py +++ b/ament_clang_tidy/ament_clang_tidy/main.py @@ -28,6 +28,7 @@ from xml.sax.saxutils import escape from xml.sax.saxutils import quoteattr +from ament_lint.filesystem_helpers import find_executable import yaml @@ -264,15 +265,7 @@ def start_subprocess(full_cmd): with open(args.xunit_file, 'w') as f: f.write(xml) - -def find_executable(file_names): - paths = os.getenv('PATH').split(os.path.pathsep) - for file_name in file_names: - for path in paths: - file_path = os.path.join(path, file_name) - if os.path.isfile(file_path) and os.access(file_path, os.X_OK): - return file_path - return None + return 0 if all(len(v) == 0 for v in report.values()) else 1 def get_compilation_db_files(paths): diff --git a/ament_clang_tidy/package.xml b/ament_clang_tidy/package.xml index 7db3d8f9..ae160070 100644 --- a/ament_clang_tidy/package.xml +++ b/ament_clang_tidy/package.xml @@ -16,6 +16,7 @@ Claire Wang Michel Hidalgo + ament_lint clang-tidy python3-yaml diff --git a/ament_clang_tidy/test/test_execution.py b/ament_clang_tidy/test/test_execution.py new file mode 100644 index 00000000..62dc4064 --- /dev/null +++ b/ament_clang_tidy/test/test_execution.py @@ -0,0 +1,40 @@ +# Copyright 2025 Open Source Robotics Foundation, Inc. +# +# 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. + +import json +from pathlib import Path + +from ament_clang_tidy.main import main +from ament_lint.test_helpers import TempFileWriter + + +def test_clang_tidy_execution(): + """Test that clang tidy can be executed on a simple C++ file, via ament_clang_tidy.""" + with TempFileWriter('int main() { return 0; }', 'test.cpp') as temp_file_path: + temp_dir = Path(temp_file_path).parent + + # Create compile_commands.json + compile_commands = [ + { + 'directory': str(temp_dir), + 'command': 'c++ -c test.cpp', + 'file': str(temp_file_path), + } + ] + compile_commands_json = json.dumps(compile_commands) + with TempFileWriter( + compile_commands_json, 'compile_commands.json' + ) as compile_commands_path: + rc = main(argv=['ament_clang_tidy', str(compile_commands_path)]) + assert rc == 0, 'Clang tidy found issues' diff --git a/ament_lint/ament_lint/filesystem_helpers.py b/ament_lint/ament_lint/filesystem_helpers.py new file mode 100644 index 00000000..9bcfc5ab --- /dev/null +++ b/ament_lint/ament_lint/filesystem_helpers.py @@ -0,0 +1,22 @@ +# Copyright 2025 Open Source Robotics Foundation, Inc. +# +# 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. + +import shutil + +def find_executable(file_names: list[str]) -> str | None: + for name in file_names: + found = shutil.which(name) + if found: + return found + return None diff --git a/ament_lint/ament_lint/test_helpers.py b/ament_lint/ament_lint/test_helpers.py new file mode 100644 index 00000000..4ec77f68 --- /dev/null +++ b/ament_lint/ament_lint/test_helpers.py @@ -0,0 +1,44 @@ +# Copyright 2025 Open Source Robotics Foundation, Inc. +# +# 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. + +import tempfile +import os + +class TempFileWriter: + """Context manager that writes a string to a temp file and cleans up on exit.""" + + def __init__(self, content, filename): + self.content = content + self.filename = filename + self.temp_dir = None + self.temp_file = None + + def __enter__(self): + # Create a new temporary directory + self.temp_dir = tempfile.mkdtemp() + # Create the temp file path + self.temp_file = os.path.join(self.temp_dir, self.filename) + # Write content to the file + with open(self.temp_file, 'w') as f: + f.write(self.content) + return self.temp_file + + def __exit__(self, exc_type, exc_val, exc_tb): + # Remove the file + if self.temp_file and os.path.exists(self.temp_file): + os.remove(self.temp_file) + # Remove the directory + if self.temp_dir and os.path.exists(self.temp_dir): + os.rmdir(self.temp_dir) + return False