Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
73a7233
Create common lib
furtib Aug 15, 2025
664914f
Make unit a package
furtib Aug 15, 2025
2d5a753
Remove unnecessary
furtib Aug 15, 2025
209f01a
Add logging to base class
furtib Aug 15, 2025
d66e804
Edit comment
furtib Aug 15, 2025
c3ebf7e
Add readme, how to add tests
furtib Aug 15, 2025
7f7af80
Fix a couple mistakes
furtib Aug 15, 2025
75e6e65
Extend readme with BUILD file instructions
furtib Aug 15, 2025
6502650
Remove whitespace
furtib Aug 15, 2025
9aa9ab8
Add __init__.py
furtib Aug 15, 2025
982ff23
Add warnings for execution path
furtib Aug 15, 2025
6eef54d
Add test template file
furtib Aug 15, 2025
9ad69c5
Add param for passing working directory
furtib Aug 15, 2025
92efa45
Add warning for current working directory change
furtib Aug 15, 2025
6c791e8
Add current dir const to template
furtib Aug 15, 2025
e4816ba
Update template to run as test
furtib Aug 15, 2025
90e3c74
Fix path problems
furtib Aug 18, 2025
4588e05
Add linefeed
furtib Aug 18, 2025
8397ae5
Add way to pass path to base class, move folder change before environ…
furtib Aug 18, 2025
72c23ec
Format
furtib Aug 18, 2025
0444c7b
Edit to new path setting
furtib Aug 18, 2025
a39b2f8
Remove warnings
furtib Aug 18, 2025
4a4164b
Fix warning
furtib Aug 18, 2025
66af78f
Change comment
furtib Aug 18, 2025
80674ab
Move template into unittest folder
furtib Aug 18, 2025
9b8778f
Update README
furtib Aug 18, 2025
624d466
Change template place
furtib Aug 18, 2025
696f8ef
Add license header
furtib Aug 18, 2025
8e98a4c
Add explanation to __init__.py
furtib Aug 18, 2025
98ef377
Enforce setting __test_path__ BAZEL_BIN_DIR and BAZEL_TESTLOGS_DIR
furtib Aug 18, 2025
28bf71b
Rewrite check command to act more like run_command
furtib Aug 18, 2025
2d170dc
Update comment
furtib Aug 18, 2025
3ae1711
Revert "Move template into unittest folder"
furtib Aug 19, 2025
2054666
Revert "Change template place"
furtib Aug 19, 2025
cee4d66
Add setup, teardown to subclass
furtib Aug 19, 2025
f59eb15
Add comments, and teardown method
furtib Aug 19, 2025
a02e792
Edit grep to not cause fail
furtib Aug 19, 2025
b167950
Move clean command to only run once per suite
furtib Aug 19, 2025
217dc53
Change run command to class method
furtib Aug 19, 2025
468fdd9
Add typing
furtib Aug 21, 2025
8cc0436
Create constains_regex_in_files
furtib Aug 21, 2025
acf7f54
change it so contains regex works for only one file and gives back bo…
furtib Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Tests:

## Running Tests

Our projects use both **`pytest`** and **`unittest`** frameworks.
You can run tests using either method.
The **`-vvv`** flag is used for **verbosity**, which provides more detailed output and is very helpful for debugging.

### To run all tests, use one of the following command:
* **Using Pytest:**
```bash
pytest unit -vvv
```

* **Using Unittest:**
```bash
python3 -m unittest discover unit -vvv
```

### Running a Subset of Tests
Specify the directory containing your desired tests. For example, to run tests in `my_test_dir`:

```bash
pytest unit/my_test_dir -vvv
# OR
python3 -m unittest discover unit/my_test_dir -vvv
```

## Adding New Unit Tests

1. **Create a Test Folder**
Inside the `unit` directory, create a folder for your new test. This folder should contain:
- All source/header files needed for the test
- `BUILD`
- A python test script
- `__init__.py`

2. **Creating the BUILD File**
- Make sure that all failing test targets get the `"manual"` tag. For example:
```
# This is a test I expect to fail
codechecker_test(
name = "codechecker_fail",
tags = [
"manual",
],
targets = [
"test_fail",
],
)
```

2. **Creating the Test File**
- Your test script must follow the naming convention:
```text
test_*.py
```
- At the top of your test file, include the following snippet to correctly handle module imports:
```python
from common.base import TestBase
```
- Create your test class by extending `TestBase` and implement your test methods.
> [!WARNING]
> You should include this line in your test class, this sets the current working directory:
> ```python
> __test_path__ = os.path.dirname(os.path.abspath(__file__))
> ```

**For a test template look into unit/template**
24 changes: 24 additions & 0 deletions test/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -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.

"""
Comment thread
Szelethus marked this conversation as resolved.
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__))))
Empty file added test/unit/common/__init__.py
Empty file.
126 changes: 126 additions & 0 deletions test/unit/common/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# 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.

"""
Base for unit and functional tests
"""

import logging
import os
import re
import shlex
import subprocess
import unittest
import sys


class TestBase(unittest.TestCase):
"""Unittest base abstract class"""

# This variable must be overwritten in each subclass!
__test_path__: str = None
BAZEL_BIN_DIR: str = None
BAZEL_TESTLOGS_DIR: str = None

@classmethod
def setUpClass(cls):
"""Load module, save environment"""
ErrorCollector: list[str] = []
if cls.__test_path__ == None:
ErrorCollector.append(
"Test path must be overwritten! Use:"
"\n__test_path__ = os.path.dirname(os.path.abspath(__file__))"
)
if cls.BAZEL_BIN_DIR == None:
ErrorCollector.append(
"Bazel bin directory must be overwritten! Use:"
"../../../bazel-bin/test/unit/my_test_folder"
)
if cls.BAZEL_TESTLOGS_DIR == None:
ErrorCollector.append(
"Bazel test logs directory must be overwritten! Use:"
"../../../bazel-testlogs/test/unit/my_test_folder"
)
if ErrorCollector:
raise NotImplementedError("\n".join(ErrorCollector))
# Enable debug logs for tests if "super verbose" flag is provided
if "-vvv" in sys.argv:
logging.basicConfig(
level=logging.DEBUG, format="[TEST] %(levelname)5s: %(message)s"
)
# Move to test dir
cls.test_dir = cls.__test_path__
os.chdir(cls.test_dir)
# Save environment and location
cls.save_env = os.environ
cls.save_cwd = os.getcwd()

@classmethod
def tearDownClass(cls):
"""Restore environment"""
os.chdir(cls.save_cwd)
os.environ = cls.save_env

def setUp(self):
"""Before every test"""
logging.debug("\n%s", "-" * 70)

@classmethod
def run_command(self, cmd: str, working_dir:str=None) -> tuple[int, str, str]:
"""
Run shell command.
returns:
- exit code
- stdout
- stderr
"""
logging.debug("Running: %s", cmd)
commands = shlex.split(cmd)
with subprocess.Popen(
commands,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=working_dir,
) as process:
stdout, stderr = process.communicate()
return (
process.returncode,
f"stdout: {stdout.decode('utf-8')}",
f"stderr: {stderr.decode('utf-8')}",
)

@classmethod
def grep_file(self, filename, regex):
"""
Grep given filename.
Returns list of matched lines.
Returns empty list if no match is found
"""
results : list[str] = []
pattern = re.compile(regex)
logging.debug("RegEx = r'%s'", regex)
with open(filename, "r", encoding="utf-8") as fileobj:
for line in fileobj:
if pattern.search(line):
logging.debug(line)
results.append(line)
return results
Comment thread
Szelethus marked this conversation as resolved.

@classmethod
def contains_regex_in_file(self, file_path: str, regex: str) -> bool:
"""
Returns a boolean, whether the specified file contains the regex or not.
"""
return self.grep_file(file_path, regex) != []
Empty file added test/unit/template/__init__.py
Empty file.
61 changes: 61 additions & 0 deletions test/unit/template/test_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 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.

"""
Comment thread
Szelethus marked this conversation as resolved.
TODO: Describe what this file does
"""
import os
import unittest
from typing import final
from common.base import TestBase


class TestTemplate(TestBase):
"""TODO: Add a description"""
# Set working directory
__test_path__ = os.path.dirname(os.path.abspath(__file__))
# TODO: fix folder name
BAZEL_BIN_DIR = os.path.join("../../..", "bazel-bin", "test",
"unit", "my_test_folder")
BAZEL_TESTLOGS_DIR = os.path.join("../../..", "bazel-testlogs", "test",
"unit", "my_test_folder")

@final
@classmethod
def setUpClass(cls):
"""TODO: Define set up before the test suite"""
super().setUpClass()
cls.run_command("bazel clean")

@final
@classmethod
def tearDownClass(cls):
"""TODO: Define clean up after the test suite"""
super().tearDownClass()

def setUp(self):
"""TODO: Define clean up before every test"""
super().setUp()

def tearDown(self):
"""TODO: Define clean up after every test"""
return super().tearDown()

def test_template(self):
"""Test: TODO: describe your test"""
self.assertTrue(True)


if __name__ == "__main__":
unittest.main(buffer=True)