diff --git a/.github/workflows/foss.yaml b/.github/workflows/foss.yaml index 3a9c0e3f..5808108c 100644 --- a/.github/workflows/foss.yaml +++ b/.github/workflows/foss.yaml @@ -25,7 +25,6 @@ concurrency: jobs: # TODO: Generalize to handle multiple projects - # TODO: Add script to run tests locally foss_ubuntu_test: name: "Test rules on FOSS project: yaml-cpp" runs-on: ubuntu-24.04 diff --git a/.gitignore b/.gitignore index eece9fdc..8bdb1869 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ __pycache__ # Ignore Python virtual environment venv + +# Ignore FOSS project clone dirs +/test/foss/*/test-proj/ diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..0644f898 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,10 @@ +[pytest] +# PyTest should not recurse into the project directories, they may contain +# other unit tests. +testpaths = test/foss +norecursedirs = + test/foss/*/test-proj + # This is why the template dir is named different in `unit` and `foss` + templates + __pycache__ + .pytest_cache diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..e6deb66a --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,24 @@ +# 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 and environment variables for the functional tests. +""" + +import os +import sys + +# Allow relative imports within the test project to work as expected +# Without it no module (test) would be able to import the common library +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)))) diff --git a/test/unit/common/__init__.py b/test/common/__init__.py similarity index 100% rename from test/unit/common/__init__.py rename to test/common/__init__.py diff --git a/test/unit/common/base.py b/test/common/base.py similarity index 100% rename from test/unit/common/base.py rename to test/common/base.py diff --git a/test/foss/__init__.py b/test/foss/__init__.py new file mode 100644 index 00000000..78bab5f1 --- /dev/null +++ b/test/foss/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/test/foss/test_foss.py b/test/foss/test_foss.py new file mode 100644 index 00000000..02ca7881 --- /dev/null +++ b/test/foss/test_foss.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. + +import logging +import sys +import unittest +import os +from types import FunctionType +from common.base import TestBase + +ROOT_DIR = f"{os.path.dirname(os.path.abspath(__file__))}/" +NOT_PROJECT_FOLDERS = ["templates", "__pycache__", ".pytest_cache"] + + +def get_test_dirs() -> list[str]: + dirs = [] + for entry in os.listdir(ROOT_DIR): + full_path = os.path.join(ROOT_DIR, entry) + if os.path.isdir(full_path) and entry not in NOT_PROJECT_FOLDERS: + dirs.append(entry) + return dirs + + +PROJECT_DIRS = get_test_dirs() + + +# This will contain the generated tests. +class FOSSTestCollector(TestBase): + # Set working directory + __test_path__ = os.path.dirname(os.path.abspath(__file__)) + # These are irrelevant for these kind of tests + BAZEL_BIN_DIR = os.path.join("") + BAZEL_TESTLOGS_DIR = os.path.join("") + + +# Creates test functions with the parameter: directory_name. Based on: +# https://eli.thegreenplace.net/2014/04/02/dynamically-generating-python-test-cases +def create_test_method(directory_name: str) -> FunctionType: + """ + Returns a function pointer that points to a function for the given directory + """ + def test_runner(self) -> None: + project_root = os.path.join(ROOT_DIR, directory_name) + + self.assertTrue( + os.path.exists(os.path.join(project_root, "init.sh")), + f"Missing 'init.sh' in {directory_name}\n" + \ + "Please consult with the README on how to add a new FOSS project", + ) + project_working_dir = os.path.join(project_root, "test-proj") + if not os.path.exists(project_working_dir): + ret, _, _ = self.run_command("sh init.sh", project_root) + self.assertTrue(os.path.exists(project_working_dir)) + ret, _, _ = self.run_command( + "bazel build :codechecker_test", project_working_dir + ) + self.assertEqual(ret, 0) + ret, _, _ = self.run_command( + "bazel build :code_checker_test", project_working_dir + ) + self.assertEqual(ret, 0) + + return test_runner + +# Dynamically add a test method for each project +# For each project directory it adds a new test function to the class +# This must be outside of the __main__ if, pytest doesn't run it that way +for dir_name in PROJECT_DIRS: + test_name = f"test_{dir_name}" + setattr(FOSSTestCollector, test_name, create_test_method(dir_name)) + +if __name__ == "__main__": + unittest.main() diff --git a/test/unit/__init__.py b/test/unit/__init__.py index e6deb66a..e69de29b 100644 --- a/test/unit/__init__.py +++ b/test/unit/__init__.py @@ -1,24 +0,0 @@ -# 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 and environment variables for the functional tests. -""" - -import os -import sys - -# Allow relative imports within the test project to work as expected -# Without it no module (test) would be able to import the common library -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__))))