From d49946100566f5303acd82bc08d4de773552d843 Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Mon, 3 Feb 2025 10:07:15 +0100 Subject: [PATCH 1/5] build: update package metadata --- pyproject.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 611560b..c8421c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,13 +10,15 @@ license = {file = "LICENSE"} classifiers = [ "License :: OSI Approved :: Apache Software License", "Framework :: Pytest", - "Development Status :: 2 - Pre-Alpha", + "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] dynamic = ["version", "description"] requires-python = ">=3.7" @@ -39,6 +41,11 @@ doc = [ "sphinxcontrib-mermaid", # mermaid graph support ] +[project.urls] +Homepage = "https://github.com/espressif/pytest-ignore-test-results" +Repository = "https://github.com/espressif/pytest-ignore-test-results.git" +Changelog = "https://github.com/espressif/pytest-ignore-test-results/blob/main/CHANGELOG.md" + [project.entry-points."pytest11"] pytest_ignore_test_results = "pytest_ignore_test_results.plugin" From 847f9467874b37c2d6165329888c7831c7a04d03 Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Mon, 3 Feb 2025 10:10:59 +0100 Subject: [PATCH 2/5] chore: update dev tools --- .pre-commit-config.yaml | 15 ++++----- docs/conf.py | 4 +-- pyproject.toml | 72 ++++++++++++----------------------------- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b3ec9f5..820a18e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -8,7 +8,7 @@ repos: args: [ '-f=lf' ] - id: double-quote-string-fixer - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.1 + rev: v1.5.5 hooks: - id: insert-license files: \.py$ @@ -17,15 +17,12 @@ repos: - license_header.txt # defaults to: LICENSE.txt - --use-current-year - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 6.0.0 hooks: - id: isort - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.270' + rev: 'v0.9.4' hooks: - id: ruff - args: ['--fix'] + args: [ '--fix' ] + - id: ruff-format diff --git a/docs/conf.py b/docs/conf.py index 2276428..c6665db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # Configuration file for the Sphinx documentation builder. @@ -11,7 +11,7 @@ project = 'pytest-ignore-test-results' project_homepage = 'https://github.com/espressif/pytest-ignore-test-results' -copyright = '2023, Espressif Systems (Shanghai) Co., Ltd.' +copyright = '2023, Espressif Systems (Shanghai) Co., Ltd.' # noqa author = 'Fu Hanxi' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index c8421c4..011e9ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,63 +59,31 @@ version_files = [ [tool.isort] profile = 'black' +force_grid_wrap = 1 -[tool.black] +[tool.ruff] line-length = 120 -target-version = ['py37'] -skip-string-normalization = true +target-version = "py37" -[tool.ruff] +[tool.ruff.lint] select = [ - 'F', # Pyflakes - 'E', # pycodestyle - 'W', # pycodestyle -# 'C90', # mccabe -# 'I', # isort -# 'N', # pep8-naming -# 'D', # pydocstyle -# 'UP', # pyupgrade -# 'YTT', # flake8-2020 -# 'ANN', # flake8-annotations -# 'S', # flake8-bandit -# 'BLE', # flake8-blind-except -# 'FBT', # flake8-boolean-trap -# 'B', # flake8-bugbear -# 'A', # flake8-builtins -# 'COM', # flake8-commas -# 'C4', # flake8-comprehensions -# 'DTZ', # flake8-datetimez -# 'T10', # flake8-debugger -# 'DJ', # flake8-django -# 'EM', # flake8-errmsg -# 'EXE', # flake8-executable -# 'ISC', # flake8-implicit-str-concat -# 'ICN', # flake8-import-conventions -# 'G', # flake8-logging-format -# 'INP', # flake8-no-pep420 -# 'PIE', # flake8-pie -# 'T20', # flake8-print -# 'PYI', # flake8-pyi -# 'PT', # flake8-pytest-style -# 'Q', # flake8-quotes -# 'RSE', # flake8-raise -# 'RET', # flake8-return -# 'SLF', # flake8-self -# 'SIM', # flake8-simplify -# 'TID', # flake8-tidy-imports -# 'TCH', # flake8-type-checking -# 'ARG', # flake8-unused-arguments -# 'PTH', # flake8-use-pathlib -# 'ERA', # eradicate -# 'PD', # pandas-vet -# 'PGH', # pygrep-hooks -# 'PL', # Pylint -# 'TRY', # tryceratops -# 'NPY', # NumPy-specific rules -# 'RUF', # Ruff-specific rules + 'F', # Pyflakes + 'E', # pycodestyle + 'W', # pycodestyle + 'I', # isort + 'UP', # pyupgrade + 'YTT', # flake8-2020 + 'A', # flake8-builtins + 'ARG', # flake8-unused-arguments + 'RUF', # ruff ] -line-length = 120 -target-version = "py37" + +[tool.ruff.lint.flake8-unused-arguments] +ignore-variadic-names = true + +[tool.ruff.format] +quote-style = "single" +docstring-code-format = true [tool.pytest.ini_options] addopts = "-s" From e478c375e89a24eb3658417d49d160f7365c393c Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Mon, 3 Feb 2025 10:13:03 +0100 Subject: [PATCH 3/5] feat: support --ignore-no-tests-collected --- pytest_ignore_test_results/ignore_results.py | 52 +++++++++++++++----- pytest_ignore_test_results/plugin.py | 24 +++++++-- tests/test_plugin.py | 25 ++++++++-- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/pytest_ignore_test_results/ignore_results.py b/pytest_ignore_test_results/ignore_results.py index 5e36b32..814f029 100644 --- a/pytest_ignore_test_results/ignore_results.py +++ b/pytest_ignore_test_results/ignore_results.py @@ -1,20 +1,41 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 - +import logging import typing as t -from fnmatch import fnmatch +from fnmatch import ( + fnmatch, +) import pytest -from _pytest.config import Config -from _pytest.python import Function -from _pytest.reports import BaseReport, TestReport -from _pytest.stash import StashKey -from _pytest.terminal import TerminalReporter - -from .utils import ExitCode, parse_ignore_results_files +from _pytest.config import ( + Config, +) +from _pytest.main import ( + Session, +) +from _pytest.python import ( + Function, +) +from _pytest.reports import ( + BaseReport, + TestReport, +) +from _pytest.stash import ( + StashKey, +) +from _pytest.terminal import ( + TerminalReporter, +) + +from .utils import ( + ExitCode, + parse_ignore_results_files, +) if t.TYPE_CHECKING: - from typing import Literal + from typing import ( + Literal, + ) class ChildCase: @@ -40,6 +61,7 @@ def __init__( ignore_cases: t.Optional[t.List[str]] = None, ignore_files: t.Optional[t.List[str]] = None, strict_exit_code: bool = False, + ignore_no_tests_collected_error: bool = False, ): self.config = config self.config.stash.setdefault(ChildCasesStashKey, {}) @@ -47,6 +69,7 @@ def __init__( self.ignore_result_patterns = set(ignore_cases or []) self.ignore_result_patterns.update(parse_ignore_results_files(ignore_files or [])) self.strict_exit_code = strict_exit_code + self.ignore_no_tests_collected_error = ignore_no_tests_collected_error # record the test cases, since in each test case, there may be child cases as well self._failed_test_cases: t.Dict[str, bool] = {} # nodeid, is_result_ignored @@ -138,7 +161,12 @@ def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: ) terminalreporter.line('\n'.join(self.failed_cases)) - def pytest_sessionfinish(self, session): + def pytest_sessionfinish(self, session: Session, exitstatus: int) -> None: + if exitstatus == ExitCode.NO_TESTS_COLLECTED and self.ignore_no_tests_collected_error: + logging.debug('Ignoring the error if no tests are collected') + session.exitstatus = ExitCode.OK + return + if self.failed_cases: session.exitstatus = ExitCode.TESTS_FAILED elif self.ignored_result_cases: # only ignored test cases are failed diff --git a/pytest_ignore_test_results/plugin.py b/pytest_ignore_test_results/plugin.py index c0dc821..702fe3f 100644 --- a/pytest_ignore_test_results/plugin.py +++ b/pytest_ignore_test_results/plugin.py @@ -1,10 +1,16 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 -from _pytest.config import Config -from _pytest.stash import StashKey +from _pytest.config import ( + Config, +) +from _pytest.stash import ( + StashKey, +) -from .ignore_results import IgnoreTestResultsPlugin +from .ignore_results import ( + IgnoreTestResultsPlugin, +) def pytest_addoption(parser): @@ -24,13 +30,20 @@ def pytest_addoption(parser): action='store_true', help='Set the Exit code to 6 if only ignored test cases are failed. If not set, the exit code will be 0', ) + group.addoption( + '--ignore-no-tests-collected-error', + action='store_true', + help='Ignore the error if no tests are collected', + ) ignore_result_key = StashKey[IgnoreTestResultsPlugin]() def pytest_addhooks(pluginmanager): - from . import hooks + from . import ( + hooks, + ) pluginmanager.add_hookspecs(hooks) @@ -41,6 +54,7 @@ def pytest_configure(config: Config) -> None: ignore_cases=config.getoption('ignore_result_cases'), ignore_files=config.getoption('ignore_result_files'), strict_exit_code=config.getoption('strict_exit_code'), + ignore_no_tests_collected_error=config.getoption('ignore_no_tests_collected_error'), ) config.pluginmanager.register(config.stash[ignore_result_key]) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index d5ad15d..6f3ee4c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1,11 +1,15 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import re -from conftest import assert_outcomes +from conftest import ( + assert_outcomes, +) -from pytest_ignore_test_results.utils import ExitCode +from pytest_ignore_test_results.utils import ( + ExitCode, +) def test_basic_script(pytester): @@ -140,3 +144,18 @@ def pytest_runtest_makereport(item, call): 'test_script.py::test_failed_2', ) assert_outcomes(result.parseoutcomes(), failed=0, passed=0, skipped=0, xfailed=0, ignored=1, deselected=7) + + +def test_ignore_no_collect_error(pytester): + result = pytester.runpytest( + '-k', + 'not_exists', + ) + assert result.ret == ExitCode.NO_TESTS_COLLECTED + + result = pytester.runpytest( + '-k', + 'not_exists', + '--ignore-no-tests-collected-error', + ) + assert result.ret == ExitCode.OK From 7216e7f3539f8e8e849b279799616caa8c6cd945 Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Mon, 3 Feb 2025 10:26:20 +0100 Subject: [PATCH 4/5] docs: update README --- README.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 918a662..dd75f50 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,79 @@ # pytest-ignore-test-results -A pytest plugin to ignore test results. +A pytest plugin that enables selective test result ignoring while maintaining test execution. + +## Installation + +```bash +pip install -U pytest-ignore-test-results +``` + +## Features + +- Ignore specific test case results using exact names or patterns +- Load ignore patterns from files +- Custom exit codes for different failure scenarios +- Support for child test cases +- Option to ignore "no tests collected" errors + +## Usage + +### Ignore Test Results by Case Name or Pattern + +When using this plugin, test cases will execute normally but their results can be selectively ignored. You have two options for specifying which test results to ignore: + +1. Using exact names or patterns directly: + +```bash +pytest --ignore-result-cases "test_feature_1" "test_feature_2" +``` + +```bash +pytest --ignore-result-cases "test_feature_*" "test_integration_*" +``` + +2. Specifying patterns in files: + +```bash +pytest --ignore-result-files ignore_list.txt another_list.txt +``` + +Example ignore file content (ignore_list.txt): + +``` +test_feature_1 # This is a comment +test_feature_* +test_integration_suite::test_case_1 +``` + +### Control Exit Codes + +The plugin provides fine-grained control over exit codes through two options: + +1. `--strict-exit-code`: When enabled, the plugin will return exit code 6 if all failed test cases are ignored. Otherwise, it maintains pytest's original exit code behavior. + +```bash +pytest --ignore-result-cases "test_feature_*" --strict-exit-code +``` + +2. `--ignore-no-tests-collected`: When enabled, this option suppresses "no tests collected" errors and returns exit code 0. + +```bash +pytest --ignore-no-tests-collected +``` + +### Custom Test Case Names + +The plugin supports test case name customization through the `pytest_custom_test_case_name` hook: + +```python +def pytest_custom_test_case_name(item): + """ + Args: + item: pytest item + + Returns: + str: Custom name for the test case + """ + return f"custom_name::{item.name}" +``` From e283913dd77b6ba254a6594edf74914ea413c4b0 Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Mon, 3 Feb 2025 10:36:41 +0100 Subject: [PATCH 5/5] ci: update tests --- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/test-python.yml | 45 +++++++++++++++++------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 2b83df2..ccd27b7 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 7664911..68390f8 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -2,7 +2,7 @@ name: Test Python Packages on: push: - branches: [ "main" ] + branches: ["main"] pull_request: paths: - "**/*.py" @@ -10,12 +10,12 @@ on: jobs: test-build: timeout-minutes: 10 - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.7' + python-version: "3.7" - run: | pip install -U pip pip install flit @@ -23,25 +23,30 @@ jobs: test-package: timeout-minutes: 10 - runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7', '3.11' ] + include: + - python-version: "3.7" + os: ubuntu-20.04 + - python-version: "3.13" + os: ubuntu-24.04 fail-fast: false + runs-on: ${{ matrix.os }} container: - image: python:${{ matrix.python-version }}-bullseye + image: python:${{ matrix.python-version }} steps: - - uses: actions/checkout@v3 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install pytest pytest-cov - - name: Test with pytest - run: | - pytest --cov pytest_ignore_test_results --cov-report term-missing --cov-report xml --junitxml=results.xml - - name: Pytest coverage comment - uses: MishaKav/pytest-coverage-comment@main - with: - pytest-coverage-path: coverage.xml - junitxml-path: results.xml + - uses: actions/checkout@v3 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install pytest pytest-cov + - name: Test with pytest + run: | + pytest --cov pytest_ignore_test_results --cov-report term-missing --cov-report xml --junitxml=results.xml + - name: Pytest coverage comment + if: ${{ github.event_name == 'pull_request' && matrix.python-version == '3.13' }} + uses: MishaKav/pytest-coverage-comment@main + with: + pytest-coverage-path: coverage.xml + junitxml-path: results.xml