From 9aee822ec589d6a1aa3480375b0883d10971493e Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Thu, 14 Mar 2024 22:33:18 -0500 Subject: [PATCH 1/4] Add duration scripts and logic from conda repo --- .github/workflows/durations.yml | 63 +++++++++++++++++++++++ .github/workflows/tests.yml | 90 +++++++++++++++++++-------------- tests/requirements-ci.txt | 1 + tools/durations/Linux.json | 2 + tools/durations/Windows.json | 2 + tools/durations/combine.py | 58 +++++++++++++++++++++ tools/durations/macOS.json | 2 + 7 files changed, 180 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/durations.yml create mode 100644 tools/durations/Linux.json create mode 100644 tools/durations/Windows.json create mode 100644 tools/durations/combine.py create mode 100644 tools/durations/macOS.json diff --git a/.github/workflows/durations.yml b/.github/workflows/durations.yml new file mode 100644 index 0000000000..d296d4c3e7 --- /dev/null +++ b/.github/workflows/durations.yml @@ -0,0 +1,63 @@ +name: Update Durations + +on: + # every Sunday at 00:00 UTC + # https://crontab.guru/#0_0_*_*_0 + schedule: + - cron: '0 0 * * 0' + + # https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_dispatch + workflow_dispatch: + +jobs: + update-durations: + runs-on: ubuntu-latest + permissions: + # necessary to open PR + # https://github.com/peter-evans/create-pull-request#action-inputs + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v3 + + - name: download recent artifacts + run: | + gh run list \ + --branch main \ + --workflow tests \ + --limit 10 \ + --json databaseId \ + --jq '.[].databaseId' \ + | xargs \ + -n 1 \ + gh run download \ + --dir ${{ runner.temp }}/artifacts/ \ + --pattern '*-all' \ + || true + env: + GITHUB_TOKEN: ${{ github.token }} + + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: combine recent durations from artifacts + run: python ./tools/durations/combine.py ${{ runner.temp }}/artifacts/ + + - name: create updated durations PR + uses: peter-evans/create-pull-request@v5 + with: + push-to-fork: conda-bot/conda + token: ${{ secrets.DURATIONS_TOKEN }} + branch: update-durations + delete-branch: true + commit-message: Update test durations + author: Conda Bot <18747875+conda-bot@users.noreply.github.com> + committer: Conda Bot <18747875+conda-bot@users.noreply.github.com> + title: 🤖 Update test durations + body: | + Aggregate recent test durations for each test and update the durations file. + + [durations.yml]: ${{ github.server_url }}/${{ github.repository }}/blob/main/.github/workflows/durations.yml + + This PR was created automatically by the [`durations.yml`][durations.yml] workflow. diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 090c389a6b..9364e3afe7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,28 +77,37 @@ jobs: fail-fast: false matrix: # test all lower versions (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.9', '3.10', '3.11'] - conda-version: [release] - test-type: [serial, parallel] - include: - # minimum Python/conda combo - - python-version: '3.8' + python-version: ['3.8', 3.9', '3.10', '3.11', '3.12'] + conda-version: [23.5.0, release, canary] + test-group: [1, 2, 3] + exclude: + # testing oldest supported conda with Python 3.8 + - python-version: '3.9' conda-version: 23.5.0 - test-type: serial - - python-version: '3.8' + - python-version: '3.10' + conda-version: 23.5.0 + - python-version: '3.11' conda-version: 23.5.0 - test-type: parallel - # maximum Python/conda combo - python-version: '3.12' - conda-version: canary - test-type: serial + conda-version: 23.5.0 + # testing stable conda with Python 3.9-3.11 + - python-version: '3.8' + conda-version: release - python-version: '3.12' + conda-version: release + # testing canary conda with Python 3.12 + - python-version: '3.8' + conda-version: canary + - python-version: '3.9' + conda-version: canary + - python-version: '3.10' + conda-version: canary + - python-version: '3.11' conda-version: canary - test-type: parallel env: CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev::' || '' }} CONDA_VERSION: ${{ contains('canary|release', matrix.conda-version) && 'conda' || format('conda={0}', matrix.conda-version) }} - PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} + PYTEST_SPLITS: 3 steps: - name: Checkout Source @@ -107,7 +116,7 @@ jobs: fetch-depth: 0 - name: Hash + Timestamp - run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-type }}-$(date -u "+%Y%m")" >> $GITHUB_ENV + run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-group }}-$(date -u "+%Y%m")" >> $GITHUB_ENV - name: Cache Conda uses: actions/cache@v4 @@ -143,8 +152,10 @@ jobs: - name: Run Tests run: pytest --cov=conda_build + --durations-path=tools/durations/${{ runner.os }}.json + --group=${{ matrix.test-group }} + --splits=${{ env.PYTEST_SPLITS }} -n auto - -m "${{ env.PYTEST_MARKER }}" - name: Upload Coverage uses: codecov/codecov-action@v4 @@ -158,6 +169,7 @@ jobs: name: test-results-${{ env.HASH }} path: | .coverage + tools/durations/${{ runner.os }}.json test-report.xml retention-days: 1 # temporary, combined in aggregate below @@ -172,20 +184,18 @@ jobs: fail-fast: false matrix: # test lower version (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.8'] - conda-version: [release] - test-type: [serial, parallel] - include: - - python-version: '3.12' + python-version: ['3.8', '3.12'] + conda-version: [release, canary] + test-group: [1, 2, 3] + exclude: + - python-version: '3.8' conda-version: canary - test-type: serial - python-version: '3.12' - conda-version: canary - test-type: parallel + conda-version: release env: ErrorActionPreference: Stop # powershell exit on first error CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'defaults' }} - PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial and not slow' }} + PYTEST_SPLITS: 3 steps: - name: Checkout Source @@ -195,7 +205,7 @@ jobs: - name: Hash + Timestamp shell: bash # use bash to run date command - run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-type }}-$(date -u "+%Y%m")" >> $GITHUB_ENV + run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-group }}-$(date -u "+%Y%m")" >> $GITHUB_ENV - name: Cache Conda uses: actions/cache@v4 @@ -237,8 +247,10 @@ jobs: run: pytest --cov=conda_build --basetemp=${{ runner.temp }} + --durations-path=tools\durations\${{ runner.os }}.json + --group=${{ matrix.test-group }} + --splits=${{ env.PYTEST_SPLITS }} -n auto - -m "${{ env.PYTEST_MARKER }}" - name: Upload Coverage uses: codecov/codecov-action@v4 @@ -252,6 +264,7 @@ jobs: name: test-results-${{ env.HASH }} path: | .coverage + tools\durations\${{ runner.os }}.json test-report.xml retention-days: 1 # temporary, combined in aggregate below @@ -270,19 +283,17 @@ jobs: fail-fast: false matrix: # test lower version (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.8'] - conda-version: [release] - test-type: [serial, parallel] - include: - - python-version: '3.12' + python-version: ['3.8', '3.12'] + conda-version: [release, canary] + test-group: [1, 2, 3] + exclude: + - python-version: '3.8' conda-version: canary - test-type: serial - python-version: '3.12' - conda-version: canary - test-type: parallel + conda-version: release env: CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'defaults' }} - PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} + PYTEST_SPLITS: 3 steps: - name: Checkout Source @@ -291,7 +302,7 @@ jobs: fetch-depth: 0 - name: Hash + Timestamp - run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-type }}-$(date -u "+%Y%m")" >> $GITHUB_ENV + run: echo "HASH=${{ runner.os }}-${{ runner.arch }}-Py${{ matrix.python-version }}-${{ matrix.conda-version }}-${{ matrix.test-group }}-$(date -u "+%Y%m")" >> $GITHUB_ENV - name: Cache Conda uses: actions/cache@v4 @@ -330,8 +341,10 @@ jobs: - name: Run Tests run: pytest --cov=conda_build + --durations-path=tools/durations/${{ runner.os }}.json + --group=${{ matrix.test-group }} + --splits=${{ env.PYTEST_SPLITS }} -n auto - -m "${{ env.PYTEST_MARKER }}" - name: Upload Coverage uses: codecov/codecov-action@v4 @@ -345,6 +358,7 @@ jobs: name: test-results-${{ env.HASH }} path: | .coverage + tools/durations/${{ runner.os }}.json test-report.xml retention-days: 1 # temporary, combined in aggregate below diff --git a/tests/requirements-ci.txt b/tests/requirements-ci.txt index 23d78bb0b2..2d062e7439 100644 --- a/tests/requirements-ci.txt +++ b/tests/requirements-ci.txt @@ -1,4 +1,5 @@ anaconda-client +conda-forge::pytest-split conda-forge::xdoctest conda-verify contextlib2 diff --git a/tools/durations/Linux.json b/tools/durations/Linux.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tools/durations/Linux.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tools/durations/Windows.json b/tools/durations/Windows.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tools/durations/Windows.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tools/durations/combine.py b/tools/durations/combine.py new file mode 100644 index 0000000000..5a6b1f73e7 --- /dev/null +++ b/tools/durations/combine.py @@ -0,0 +1,58 @@ +# Copyright (C) 2014 Anaconda, Inc +# SPDX-License-Identifier: BSD-3-Clause +""" +Script to combine test durations from all runs. + +If the tests splits are looking uneven or the test suite has +siginificantly changed, update ./tools/durations/${OS}.json in the root of the +repository and pytest-split may work better. + +``` +$ gh run list --branch +$ gh run download --dir ./artifacts/ +$ python ./tools/durations/combine.py ./artifacts/ +$ git add ./tools/durations/ +$ git commit -m "Update test durations" +$ git push +``` +""" +from __future__ import annotations + +import json +from pathlib import Path +from statistics import fmean +from sys import argv + +combined: dict[str, dict[str, list[float]]] = {} + +durations_dir = Path(__file__).parent +artifacts_dir = Path(argv[-1]).expanduser().resolve() + +# aggregate all new durations +for path in artifacts_dir.glob("**/*.json"): + os = path.stem + combined_os = combined.setdefault(os, {}) + + data = json.loads(path.read_text()) + for key, value in data.items(): + combined_os.setdefault(key, []).append(value) + +# aggregate new and old durations while discarding durations that no longer exist +for path in durations_dir.glob("**/*.json"): + os = path.stem + combined_os = combined.setdefault(os, {}) + + data = json.loads(path.read_text()) + for key in set(combined_os).intersection(durations_dir.glob("**/*.json")): + combined_os.setdefault(key, []).append(data[key]) + +# write out averaged durations +for os, combined_os in combined.items(): + (durations_dir / f"{os}.json").write_text( + json.dumps( + {key: fmean(values) for key, values in combined_os.items()}, + indent=4, + sort_keys=True, + ) + + "\n" # include trailing newline + ) diff --git a/tools/durations/macOS.json b/tools/durations/macOS.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tools/durations/macOS.json @@ -0,0 +1,2 @@ +{ +} From 6e7265eb54bdcdc04779255d1c64cf1ae22f4c1a Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Thu, 14 Mar 2024 22:35:22 -0500 Subject: [PATCH 2/4] Fix missing quote --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9364e3afe7..8462c774d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,7 +77,7 @@ jobs: fail-fast: false matrix: # test all lower versions (w/ stable conda) and upper version (w/ canary conda) - python-version: ['3.8', 3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] conda-version: [23.5.0, release, canary] test-group: [1, 2, 3] exclude: From e47c871c62d43ea91487dde278e36adbde57e470 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Fri, 15 Mar 2024 11:25:10 -0500 Subject: [PATCH 3/4] Remove os.chdir --- tests/conftest.py | 10 +++------- tests/test_api_build.py | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f347317d90..34bd10b1b2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,16 +55,12 @@ def testing_workdir(monkeypatch: MonkeyPatch, tmp_path: Path) -> Iterator[str]: @pytest.fixture(scope="function") -def testing_homedir() -> Iterator[Path]: +def testing_homedir(monkeypatch: MonkeyPatch) -> Iterator[Path]: """Create a temporary testing directory in the users home directory; cd into dir before test, cd out after.""" - saved = Path.cwd() try: with tempfile.TemporaryDirectory(dir=Path.home(), prefix=".pytest_") as home: - os.chdir(home) - - yield home - - os.chdir(saved) + monkeypatch.chdir(home) + yield Path(home) except OSError: pytest.xfail( f"failed to create temporary directory () in {'%HOME%' if on_win else '${HOME}'} " diff --git a/tests/test_api_build.py b/tests/test_api_build.py index 0d2bd3b5f0..dbb619e8ab 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -674,7 +674,7 @@ def test_relative_git_url_submodule_clone(testing_workdir, testing_config, monke FNULL.close() for tag in range(2): - os.chdir(absolute_sub) + monkeypatch.chdir(absolute_sub) if tag == 0: check_call_env([git, "init"], env=sys_git_env) with open("absolute", "w") as f: @@ -682,7 +682,7 @@ def test_relative_git_url_submodule_clone(testing_workdir, testing_config, monke check_call_env([git, "add", "absolute"], env=sys_git_env) check_call_env([git, "commit", "-m", f"absolute{tag}"], env=sys_git_env) - os.chdir(relative_sub) + monkeypatch.chdir(relative_sub) if tag == 0: check_call_env([git, "init"], env=sys_git_env) with open("relative", "w") as f: @@ -690,7 +690,7 @@ def test_relative_git_url_submodule_clone(testing_workdir, testing_config, monke check_call_env([git, "add", "relative"], env=sys_git_env) check_call_env([git, "commit", "-m", f"relative{tag}"], env=sys_git_env) - os.chdir(toplevel) + monkeypatch.chdir(toplevel) if tag == 0: check_call_env([git, "init"], env=sys_git_env) with open("toplevel", "w") as f: From 4b87612930fe4619bc67b3f0162d9853a28c7df9 Mon Sep 17 00:00:00 2001 From: Ken Odegard Date: Fri, 15 Mar 2024 12:33:05 -0500 Subject: [PATCH 4/4] Ensure caplog is used correctly --- tests/cli/test_main_render.py | 19 +++++++++++++++++-- tests/conftest.py | 2 +- tests/test_api_build.py | 11 ++++++----- tests/test_api_skeleton.py | 6 +++--- tests/test_post.py | 26 +++++++++++++++++++++++--- tests/test_subpackages.py | 19 +++++++++++++++---- tests/test_utils.py | 13 ++++++++----- tests/test_variants.py | 31 +++++++++++++++++++++++-------- 8 files changed, 96 insertions(+), 31 deletions(-) diff --git a/tests/cli/test_main_render.py b/tests/cli/test_main_render.py index 59fff7901c..471b2c39b5 100644 --- a/tests/cli/test_main_render.py +++ b/tests/cli/test_main_render.py @@ -1,7 +1,10 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import os import sys +from typing import TYPE_CHECKING import pytest import yaml @@ -12,6 +15,12 @@ from ..utils import metadata_dir +if TYPE_CHECKING: + from pytest import CaptureFixture + + from conda_build.config import Config + from conda_build.metadata import MetaData + def test_render_add_channel(): """This recipe requires the conda_build_test_requirement package, which is @@ -67,7 +76,10 @@ def test_render_without_channel_fails(tmp_path): def test_render_output_build_path( - testing_workdir, testing_config, testing_metadata, capfd, caplog + testing_workdir: str, + testing_config: Config, + testing_metadata: MetaData, + capfd: CaptureFixture, ): api.output_yaml(testing_metadata, "meta.yaml") args = ["--output", testing_workdir] @@ -83,7 +95,10 @@ def test_render_output_build_path( def test_render_output_build_path_and_file( - testing_workdir, testing_config, testing_metadata, capfd, caplog + testing_workdir: str, + testing_config: Config, + testing_metadata: MetaData, + capfd: CaptureFixture, ): api.output_yaml(testing_metadata, "meta.yaml") rendered_filename = "out.yaml" diff --git a/tests/conftest.py b/tests/conftest.py index 34bd10b1b2..a67fe48799 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -69,7 +69,7 @@ def testing_homedir(monkeypatch: MonkeyPatch) -> Iterator[Path]: @pytest.fixture(scope="function") -def testing_config(testing_workdir): +def testing_config(testing_workdir: str) -> Config: def boolify(v): return v == "true" diff --git a/tests/test_api_build.py b/tests/test_api_build.py index dbb619e8ab..6a837c3024 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -70,7 +70,7 @@ ) if TYPE_CHECKING: - from pytest import FixtureRequest, MonkeyPatch + from pytest import FixtureRequest, LogCaptureFixture, MonkeyPatch from pytest_mock import MockerFixture from conda_build.metadata import MetaData @@ -1108,7 +1108,7 @@ def test_build_expands_wildcards(mocker): @pytest.mark.parametrize("set_build_id", [True, False]) -def test_remove_workdir_default(testing_config, caplog, set_build_id): +def test_remove_workdir_default(testing_config: Config, set_build_id): recipe = os.path.join(metadata_dir, "_keep_work_dir") # make a metadata object - otherwise the build folder is computed within the build, but does # not alter the config object that is passed in. This is by design - we always make copies @@ -1148,7 +1148,7 @@ def test_keep_workdir_and_dirty_reuse(testing_config, capfd): @pytest.mark.sanity -def test_workdir_removal_warning(testing_config, caplog): +def test_workdir_removal_warning(testing_config: Config): recipe = os.path.join(metadata_dir, "_test_uses_src_dir") with pytest.raises(ValueError) as exc: api.build(recipe, config=testing_config) @@ -1858,11 +1858,12 @@ def test_ignore_verify_codes(testing_config): @pytest.mark.sanity -def test_extra_meta(testing_config, caplog): +def test_extra_meta(testing_config: Config, caplog: LogCaptureFixture): recipe_dir = os.path.join(metadata_dir, "_extra_meta") extra_meta_data = {"foo": "bar"} testing_config.extra_meta = extra_meta_data - outputs = api.build(recipe_dir, config=testing_config) + with caplog.at_level(logging.INFO): + outputs = api.build(recipe_dir, config=testing_config) about = json.loads(package_has_file(outputs[0], "info/about.json")) assert "foo" in about["extra"] and about["extra"]["foo"] == "bar" assert ( diff --git a/tests/test_api_skeleton.py b/tests/test_api_skeleton.py index a8273492b0..85f2029571 100644 --- a/tests/test_api_skeleton.py +++ b/tests/test_api_skeleton.py @@ -399,7 +399,7 @@ def test_pypi_with_version_arg(tmp_path: Path): @pytest.mark.slow -def test_pypi_with_extra_specs(tmp_path: Path, testing_config): +def test_pypi_with_extra_specs(tmp_path: Path, testing_config: Config): # regression test for https://github.com/conda/conda-build/issues/1697 # For mpi4py: testing_config.channel_urls.append("https://repo.anaconda.com/pkgs/free") @@ -422,13 +422,13 @@ def test_pypi_with_extra_specs(tmp_path: Path, testing_config): @pytest.mark.slow -def test_pypi_with_version_inconsistency(tmp_path: Path, testing_config): +def test_pypi_with_version_inconsistency(tmp_path: Path, testing_config: Config): # regression test for https://github.com/conda/conda-build/issues/189 # For mpi4py: + testing_config.channel_urls.append("https://repo.anaconda.com/pkgs/free") extra_specs = ["mpi4py"] if not on_win: extra_specs.append("nomkl") - testing_config.channel_urls.append("https://repo.anaconda.com/pkgs/free") api.skeletonize( "mpi4py_test", "pypi", diff --git a/tests/test_post.py b/tests/test_post.py index 97ef1448fc..aba0719690 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -1,11 +1,14 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import json import logging import os import shutil import sys from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -20,6 +23,11 @@ from .utils import add_mangling, metadata_dir +if TYPE_CHECKING: + from pytest import LogCaptureFixture + + from conda_build.config import Config + @pytest.mark.skipif( sys.version_info >= (3, 10), @@ -102,7 +110,11 @@ def test_pypi_installer_metadata(testing_config): assert "conda" == (package_has_file(pkg, expected_installer, refresh_mode="forced")) -def test_menuinst_validation_ok(testing_config, caplog, tmp_path): +def test_menuinst_validation_ok( + testing_config: Config, + caplog: LogCaptureFixture, + tmp_path: Path, +): "1st check - validation passes with recipe as is" recipe = Path(metadata_dir, "_menu_json_validation") recipe_tmp = tmp_path / "_menu_json_validation" @@ -118,7 +130,11 @@ def test_menuinst_validation_ok(testing_config, caplog, tmp_path): assert package_has_file(pkg, "Menu/menu_json_validation.json") -def test_menuinst_validation_fails_bad_schema(testing_config, caplog, tmp_path): +def test_menuinst_validation_fails_bad_schema( + testing_config: Config, + caplog: LogCaptureFixture, + tmp_path: Path, +): "2nd check - valid JSON but invalid content fails validation" recipe = Path(metadata_dir, "_menu_json_validation") recipe_tmp = tmp_path / "_menu_json_validation" @@ -138,7 +154,11 @@ def test_menuinst_validation_fails_bad_schema(testing_config, caplog, tmp_path): assert "ValidationError" in captured_text -def test_menuinst_validation_fails_bad_json(testing_config, caplog, tmp_path): +def test_menuinst_validation_fails_bad_json( + testing_config: Config, + caplog: LogCaptureFixture, + tmp_path: Path, +): "3rd check - non-parsable JSON fails validation" recipe = Path(metadata_dir, "_menu_json_validation") recipe_tmp = tmp_path / "_menu_json_validation" diff --git a/tests/test_subpackages.py b/tests/test_subpackages.py index 3c3b011c58..2138d017ec 100644 --- a/tests/test_subpackages.py +++ b/tests/test_subpackages.py @@ -1,11 +1,14 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import json +import logging import os import re import sys from glob import glob -from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -15,6 +18,13 @@ from .utils import get_valid_recipes, subpackage_dir +if TYPE_CHECKING: + from pathlib import Path + + from pytest import LogCaptureFixture + + from conda_build.config import Config + @pytest.mark.slow @pytest.mark.parametrize( @@ -260,12 +270,13 @@ def test_subpackage_hash_inputs(testing_config): assert utils.package_has_file(out, "info/recipe/meta.yaml") -def test_overlapping_files(testing_config, caplog): +def test_overlapping_files(testing_config: Config, caplog: LogCaptureFixture): recipe_dir = os.path.join(subpackage_dir, "_overlapping_files") utils.reset_deduplicator() - outputs = api.build(recipe_dir, config=testing_config) + with caplog.at_level(logging.WARNING): + outputs = api.build(recipe_dir, config=testing_config) assert len(outputs) == 3 - assert sum(int("Exact overlap" in rec.message) for rec in caplog.records) == 1 + assert sum("Exact overlap" in rec.message for rec in caplog.records) == 1 @pytest.mark.sanity diff --git a/tests/test_utils.py b/tests/test_utils.py index d245e65796..46e9c0748f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,18 +1,23 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + +import logging import os import subprocess import sys from pathlib import Path -from typing import NamedTuple +from typing import TYPE_CHECKING, NamedTuple import filelock import pytest -from pytest import MonkeyPatch import conda_build.utils as utils from conda_build.exceptions import BuildLockError +if TYPE_CHECKING: + from pytest import CaptureFixture, LogCaptureFixture, MonkeyPatch + @pytest.mark.skipif( utils.on_win, reason="only unix has python version in site-packages path" @@ -155,9 +160,7 @@ def test_filter_files(): @pytest.mark.serial -def test_logger_filtering(caplog, capfd): - import logging - +def test_logger_filtering(caplog: LogCaptureFixture, capfd: CaptureFixture): log = utils.get_logger(__name__, level=logging.DEBUG) log.debug("test debug message") log.info("test info message") diff --git a/tests/test_variants.py b/tests/test_variants.py index 50e9cea4f2..714b9277ac 100644 --- a/tests/test_variants.py +++ b/tests/test_variants.py @@ -1,11 +1,15 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + import json +import logging import os import platform import re import sys from pathlib import Path +from typing import TYPE_CHECKING import pytest import yaml @@ -23,6 +27,11 @@ from .utils import variants_dir +if TYPE_CHECKING: + from pytest import LogCaptureFixture + + from conda_build.config import Config + @pytest.mark.parametrize( "variants", @@ -272,11 +281,16 @@ def test_variant_input_with_zip_keys_keeps_zip_keys_list(): @pytest.mark.serial @pytest.mark.xfail(sys.platform == "win32", reason="console readout issues on appveyor") -def test_ensure_valid_spec_on_run_and_test(testing_config, caplog): +def test_ensure_valid_spec_on_run_and_test( + testing_config: Config, + caplog: LogCaptureFixture, +): testing_config.debug = True testing_config.verbose = True recipe = os.path.join(variants_dir, "14_variant_in_run_and_test") - api.render(recipe, config=testing_config) + + with caplog.at_level(logging.WARNING): + api.render(recipe, config=testing_config) text = caplog.text assert "Adding .* to spec 'pytest 3.2'" in text @@ -422,13 +436,14 @@ def test_variants_used_in_jinja2_conditionals(): assert sum(m.config.variant["blas_impl"] == "openblas" for m, _, _ in ms) == 1 -def test_build_run_exports_act_on_host(caplog): +def test_build_run_exports_act_on_host(caplog: LogCaptureFixture): """Regression test for https://github.com/conda/conda-build/issues/2559""" - api.render( - os.path.join(variants_dir, "22_run_exports_rerendered_for_other_variants"), - platform="win", - arch="64", - ) + with caplog.at_level(logging.WARNING): + api.render( + os.path.join(variants_dir, "22_run_exports_rerendered_for_other_variants"), + platform="win", + arch="64", + ) assert "failed to get package records, retrying" not in caplog.text