diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 73b47f6..c2ae356 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -27,10 +27,10 @@ jobs: uv venv && uv pip install -e '.[dev]' coverage - name: Run tests with coverage - run: uv run coverage run -m pytest + run: uv run coverage run -m pytest --junitxml="junit.xml" - name: Print coverage summary - run: uv run coverage report -m --fail-under=70 + run: uv run coverage report -m --fail-under=80 - name: Generate coverage XML run: uv run coverage xml -o coverage.xml @@ -40,6 +40,13 @@ jobs: uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} - flags: connectors + flags: pyoaev fail_ci_if_error: false verbose: true + + - name: Upload test results to Codecov + if: ${{ !cancelled() && hashFiles('junit.xml') != '' }} + uses: codecov/codecov-action@v7 + with: + token: ${{ secrets.CODECOV_TOKEN }} + report_type: test_results diff --git a/.gitignore b/.gitignore index 0b76752..82c5d07 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ coverage.xml .hypothesis/ .pytest_cache/ cover/ +junit.xml # Translations *.mo @@ -136,4 +137,4 @@ dmypy.json cython_debug/ # testing -test.py \ No newline at end of file +test.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17851e5..0b0d564 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,15 @@ developing features or fixing bugs yourself. * If you are interested in contributing code, fork the repository, create a branch, and open a pull request. +## Code quality and testing + +In order to maintain a certain standard regarding code quality, this repository CI will run various tools against expectations: +- `isort` will be used to check for proper imports sorting, and the CI will fail if the checks fail +- `black` will be used to check for proper code formatting, and the CI will fail if the checks fail +- `flake8` will be used to check for proper code styling, and the CI will fail if the checks fail +- `pytest` will be used to run various levels of testing, and the CI will fail if the tests fail +- `coverage` with `codecov` will be used to check for proper code coverage, and the CI will fail if the level is below 80% for the current diff (a warning will be emitted if the level falls below 80% for the overall project) + ## Commit, pull request & issue conventions diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..92b4a7f --- /dev/null +++ b/codecov.yml @@ -0,0 +1,19 @@ +coverage: + status: + project: + default: + target: auto + informational: true + patch: + default: + target: 80% + informational: false + +comment: + layout: "diff, files, condensed_footer" + hide_project_coverage: true # set to true + require_changes: "coverage_drop AND uncovered_patch" + +ignore: + # Ignore any folder whose name starts with "test" anywhere in the tree + - "**/test*/**" \ No newline at end of file diff --git a/test/daemons/test_base_daemon.py b/test/daemons/test_base_daemon.py index d3d7a19..d980506 100644 --- a/test/daemons/test_base_daemon.py +++ b/test/daemons/test_base_daemon.py @@ -51,17 +51,21 @@ def bound_method(self): ) +@unittest.mock.patch("argparse.ArgumentParser") class TestBaseDaemon(unittest.TestCase): - def test_when_no_callback_when_complete_config_ctor_ok(self): + def test_when_no_callback_when_complete_config_ctor_ok(self, _): daemon = DaemonForTest(configuration=TEST_DAEMON_CONFIGURATION) self.assertIsInstance(daemon, BaseDaemon) - def test_when_no_callback_when_lacking_config_key_ctor_throws(self): + def test_when_no_callback_when_lacking_config_key_ctor_throws(self, _): with self.assertRaises(Exception): DaemonForTest(configuration=Configuration(config_hints={})) - def test_when_no_callback_daemon_cant_start(self): + def test_when_no_callback_daemon_cant_start(self, m_argparser): + m_argparser.return_value.parse_args.return_value = unittest.mock.MagicMock( + dump_config_schema=False + ) daemon, mock_setup, mock_start_loop, _ = create_mock_daemon() with self.assertRaises(OpenAEVError): @@ -70,7 +74,10 @@ def test_when_no_callback_daemon_cant_start(self): mock_setup.assert_not_called() mock_start_loop.assert_not_called() - def test_when_callback_daemon_can_start(self): + def test_when_callback_daemon_can_start(self, m_argparser): + m_argparser.return_value.parse_args.return_value = unittest.mock.MagicMock( + dump_config_schema=False + ) daemon, mock_setup, mock_start_loop, _ = create_mock_daemon(lambda: None) daemon.start() @@ -78,7 +85,7 @@ def test_when_callback_daemon_can_start(self): mock_setup.assert_called_once() mock_start_loop.assert_called_once() - def test_when_callback_is_bound_method_daemon_can_call(self): + def test_when_callback_is_bound_method_daemon_can_call(self, _): daemon, mock_setup, mock_start_loop, inner_mock_func = create_mock_daemon() daemon.set_callback(daemon.bound_method) @@ -86,7 +93,7 @@ def test_when_callback_is_bound_method_daemon_can_call(self): inner_mock_func.assert_called_once() - def test_when_callback_is_func_with_collector_parameter_daemon_can_call(self): + def test_when_callback_is_func_with_collector_parameter_daemon_can_call(self, _): daemon, mock_setup, mock_start_loop, _ = create_mock_daemon() inner_mock_func = unittest.mock.MagicMock() daemon.set_callback(lambda collector: inner_mock_func()) @@ -95,7 +102,7 @@ def test_when_callback_is_func_with_collector_parameter_daemon_can_call(self): inner_mock_func.assert_called_once() - def test_when_callback_is_func_with_other_parameter_daemon_cant_call(self): + def test_when_callback_is_func_with_other_parameter_daemon_cant_call(self, _): daemon, mock_setup, mock_start_loop, _ = create_mock_daemon() inner_mock_func = unittest.mock.MagicMock() daemon.set_callback(lambda other_parameter: inner_mock_func())