Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce flakiness of CI test runs #4653

Merged
merged 7 commits into from
Nov 29, 2022

Conversation

mbargull
Copy link
Member

@mbargull mbargull commented Nov 25, 2022

Description

I started to include unrelated test fixes in #4650 to easier inspect failing/passing of relevant tests.
Those started to grow and it's cleaner to have them separated, anyway, so here we go.


What is the main change/result? (See details/motivation below.)

I went back an forth between having a fixture to add config=testing_config either completely implicit (autouse=True) or have it explicitly added to each and every test to make its use obvious.

Now I went the middle ground with pytestmark = pytest.mark.usefixtures("api_default_testing_config") to explicitly request the fixture for a test module but not on the single test level. If one wants to deactivate it for a single test but still use it for all other tests in a module, they can @pytest.mark.no_api_default_testing_config such test.


What is the overall motivation and why this back and forth, then middle ground, anyway?

The main cause of the tests' flakiness (apart from CRAN connection and occasional pip %TEMP% permission errors on Windows) is that the parallel tests ought to write to separate locations but in reality most don't.
The mechanism to have them use separate locations is the testing_config fixture which sets Config.croot to a temporary directory.
So nearly all tests should explicitly run function(..., config=testing_config) for all functions that take a config parameter.
But actually, many don't, so they can occasionally run into concurrent write conflicts (see #4653 (comment) ).

Hence, the obvious fix is to add config=testing_config to all those tests.
That results in a large change set with somewhat verbose code, but is also not future-proof (since I expect it'll be forgotten to be added for newer tests again in the future).
The other solution is to have config=testing_config always unconditionally, implicitly used.
The downside with that is that it adds more hidden/non-obvious behavior.

The proposed change is to not go full explicit- but also not full-implicit mode by adding a fixture that can be

  • explicitly added to single tests via @pytest.mark.usefixtures("api_default_testing_config"),
  • implicitly added to all of a module's tests via pytestmark = pytest.mark.usefixtures("api_default_testing_config"),
  • explicitly deactivated for a single test via @pytest.mark.no_api_default_testing_config.

Checklist - did you ...

  • Add a file to the news directory (using the template) for the next release's release notes?
  • Add / update necessary tests?
  • Add / update outdated documentation?

@conda-bot conda-bot added the cla-signed [bot] added once the contributor has signed the CLA label Nov 25, 2022
@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch 2 times, most recently from 5d89c95 to 65abf81 Compare November 25, 2022 20:48
@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch 3 times, most recently from a565200 to 330a777 Compare November 26, 2022 13:29
@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch from 330a777 to 8f6bfb4 Compare November 26, 2022 15:13
@mbargull mbargull mentioned this pull request Nov 26, 2022
3 tasks
@mbargull
Copy link
Member Author

Comments copied from gh-4652:

I didn't add a news entry because this is just chore that doesn't touch anything outside the test code. If you'd still like to have a news entry, let me know.

Re: config=testing_config: Previously, tests failed because (even when only rendering) concurrent writes/deletions around the {croot}/work happened at conda_build.config.Config.compute_build_id (at the end in its if old_dir != work_dir: branch) happened. I didn't dig into it, but it might just be that another test creates the work (without a timestamp) folder and then compute_build_id wants to move it. In any case, testing_config sets the croot to testing_workdir and as such the single tests use their own root dir without seeing others' work folders.

@mbargull
Copy link
Member Author

(Added description/motivation to the OP.)

jezdez
jezdez previously requested changes Nov 29, 2022
Copy link
Member

@jezdez jezdez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this @mbargull!

I don't think your approach with rewriting the function defaults for conda_build.api is a good pattern and could be achieved by a more mundane monkey patching of the conda_build.config.get_or_merge_config function, to be automatically returning the appropriate test config.

While I enjoy seeing the use of pytestmark, I agree that it'll likely be forgotten by future contributors when adding new tests, creating rather unpleasant implicit differences in behavior again, increasing the risk of concurrency issues.

I would suggest to indeed use autouse=True to automatically enable the test config, if you think the majority of test modules need it (as it seems) and instead try to find a way to disable it when needed.

tests/conftest.py Outdated Show resolved Hide resolved
@mbargull
Copy link
Member Author

mbargull commented Nov 29, 2022

I don't think your approach with rewriting the function defaults for conda_build.api is a good pattern and could be achieved by a more mundane monkey patching of the conda_build.config.get_or_merge_config function, to be automatically returning the appropriate test config.

Haha, and that's what I get to only look at level n but not level n+1 :) -- I didn't even see/look for that all those functions use get_or_merge_config.
So, yes, very much agreed, I'll change it to monkeypatch get_or_merge_config; much cleaner.

I would suggest to indeed use autouse=True to automatically enable the test config, if you think the majority of test modules need it (as it seems) and instead try to find a way to disable it when needed.

Okay, I guess autouse=True is fine since we can opt out via the added @pytest.mark.no_<...>.
Mind you, this would mean that we create a temporary directory for every single test then (through testing_config -> testing_workdir -> tmpdir fixture requests).
We then probably want to set pytestmark = pytest.mark.no_default_testing_config for some test modules like test_api_consistency.py.

@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch from 2ba8bad to efe302f Compare November 29, 2022 10:19
@jezdez
Copy link
Member

jezdez commented Nov 29, 2022

I don't think your approach with rewriting the function defaults for conda_build.api is a good pattern and could be achieved by a more mundane monkey patching of the conda_build.config.get_or_merge_config function, to be automatically returning the appropriate test config.

Haha, and that's what I get to only look at level n but not level n+1 :) -- I didn't even see/look for that all those functions use get_or_merge_config. So, yes, very much agreed, I'll change it to monkeypatch get_or_merge_config; much cleaner.

Oops, conda-build keeps on giving 🤣

I would suggest to indeed use autouse=True to automatically enable the test config, if you think the majority of test modules need it (as it seems) and instead try to find a way to disable it when needed.

Okay, I guess autouse=True is fine since we can opt out via the added @pytest.mark.no_<...>. Mind you, this would mean that we create a temporary directory for every single test then (through testing_config -> testing_workdir -> tmpdir fixture requests). We then probably want to set pytestmark = pytest.mark.no_default_testing_config for some test modules like test_api_consistency.py.

Hmm, yeah, let's see what the impact on runtime is for that

@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch 2 times, most recently from 25293a5 to a29a4be Compare November 29, 2022 10:33
@mbargull mbargull force-pushed the monkeypatch-api-testing_config branch from a29a4be to 0f4becf Compare November 29, 2022 10:45
@jezdez
Copy link
Member

jezdez commented Nov 29, 2022

Seems like it's only one test failing now! test_skeleton_pypi_arguments_work

@jezdez jezdez added this to the 3.23.2 milestone Nov 29, 2022
msumastro has been updated a couple of days ago and versions >=1.2 now
store the metadata in setup.cfg which is not handled by skeletons.pypi.
It is easy to fix this if we reuse the code from _load_setup_py_data but
we might as well use/advertize newer projects like grayskull instead.
@mbargull
Copy link
Member Author

Seems like it's only one test failing now! test_skeleton_pypi_arguments_work

Ha! That was a sneaky PyPI update since Saturday 😁 .
I fixed the test to use the same package version as before. The the commit message from c6463d9 for details.

@mbargull
Copy link
Member Author

This is now a much smaller and cleaner change set with which I'm content :).
The test run time is not noticeably affected since we have rather heavy test cases that dominate all other running times.

@jezdez, thanks for your thorough and thoughtful review, I appreciate it!
Let me know if there's anything else you'd want me to change.

@mbargull
Copy link
Member Author

Remaining failure is the permission error on Windows during pip install for peppercorn again.

@kenodegard kenodegard mentioned this pull request Nov 29, 2022
3 tasks
Copy link
Contributor

@kenodegard kenodegard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reran failed test, seems to be passing now

@kenodegard kenodegard dismissed jezdez’s stale review November 29, 2022 23:04

concerns have been resolved

@kenodegard kenodegard merged commit 1053650 into conda:main Nov 29, 2022
@mbargull
Copy link
Member Author

mbargull commented Dec 5, 2022

Looks like

@pytest.fixture(scope="function", autouse=True)
def default_testing_config(testing_config, monkeypatch, request):

doesn't work (reliably/at all?): #4665 (comment)

mbargull added a commit to mbargull/conda-build that referenced this pull request Nov 10, 2023
The default_testing_config monkeypatching fixture was added in condagh-4653
but did not consider "from .config import get_or_merge_config" cases in
which get_or_merge_config is already bound and thus not patched.

Signed-off-by: Marcel Bargull <marcel.bargull@udo.edu>
mbargull added a commit to mbargull/conda-build that referenced this pull request Nov 16, 2023
The default_testing_config monkeypatching fixture was added in condagh-4653
but did not consider "from .config import get_or_merge_config" cases in
which get_or_merge_config is already bound and thus not patched.

Signed-off-by: Marcel Bargull <marcel.bargull@udo.edu>
kenodegard pushed a commit that referenced this pull request Nov 17, 2023
* Fix testing config monkeypatch for concurrent test flakiness

The default_testing_config monkeypatching fixture was added in gh-4653
but did not consider "from .config import get_or_merge_config" cases in
which get_or_merge_config is already bound and thus not patched.

* main_build: Construct config via get_or_merge_config

Helps tests with default_testing_config monkeypatch.

* Tests: Don't preset values for get_or_merge_config

Otherwise default values set for testing_config before, e.g.,
environment variable-dependent config can be set in the tests.

Example: tests/cli/test_main_build.py::test_conda_py_no_period failed
since it sets CONDA_PY=36 but testing_config already had set it before.

---------

Signed-off-by: Marcel Bargull <marcel.bargull@udo.edu>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
@github-actions github-actions bot added the locked [bot] locked due to inactivity label Dec 6, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla-signed [bot] added once the contributor has signed the CLA locked [bot] locked due to inactivity
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants