blob: c7404020a4cfa2b5de329132547851ed05d2791a [file] [log] [blame]
Chris McDonald2e9a09c2020-04-03 16:09:32 -06001# Copyright 2020 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Configuration and fixtures for pytest.
6
7See the following doc link for an explanation of conftest.py and how it is used
8by pytest:
Mike Frysinger43eb3ec2021-05-25 22:43:38 -04009https://docs.pytest.org/en/latest/explanation/fixtures.html
Chris McDonald2e9a09c2020-04-03 16:09:32 -060010"""
11
Chris McDonald05511c92020-05-01 16:45:56 -060012from __future__ import division
Chris McDonald2e9a09c2020-04-03 16:09:32 -060013
Chris McDonaldffe2a412020-04-15 02:27:25 -060014import multiprocessing
15
Chris McDonaldee2fbda2020-04-30 18:10:01 -060016import pytest
Chris McDonald2e9a09c2020-04-03 16:09:32 -060017
Chris McDonald05511c92020-05-01 16:45:56 -060018import chromite as cr
Chris McDonald2e9a09c2020-04-03 16:09:32 -060019from chromite.lib import cidb
Chris McDonald320ef422020-04-18 04:57:04 -060020from chromite.lib import parallel
Chris McDonald6ce00962020-04-14 04:05:21 -060021from chromite.lib import retry_stats
Alex Klein75df1792020-06-11 14:42:49 -060022from chromite.lib.parser import package_info
Chris McDonald2e9a09c2020-04-03 16:09:32 -060023
Chris McDonaldee2fbda2020-04-30 18:10:01 -060024# We use wildcard imports here to make fixtures defined in the test module
25# globally visible.
26# pylint: disable=unused-wildcard-import, wildcard-import
Mike Frysinger94cfdef2019-08-25 00:04:27 -040027#
Chris McDonaldee2fbda2020-04-30 18:10:01 -060028# Importing from *_fixtures.py files into conftest.py is the only time a
29# module should use a wildcard import. *_fixtures.py files should ensure
30# that the only items visible to a wildcard import are pytest fixtures,
31# usually by declaring __all__ if necessary.
32from chromite.test.portage_fixtures import *
33
Chris McDonald2e9a09c2020-04-03 16:09:32 -060034
Alex Klein1699fab2022-09-08 08:46:06 -060035@pytest.fixture(scope="class", autouse=True)
Chris McDonald2e9a09c2020-04-03 16:09:32 -060036def mock_cidb_connection():
Alex Klein1699fab2022-09-08 08:46:06 -060037 """Ensure that the CIDB connection factory is initialized as a mock.
Chris McDonald2e9a09c2020-04-03 16:09:32 -060038
Alex Klein1699fab2022-09-08 08:46:06 -060039 Unit tests should never connect to any live instances of CIDB and this
40 initialization ensures that they only ever get a mock connection instance.
Chris McDonald2e9a09c2020-04-03 16:09:32 -060041
Alex Klein1699fab2022-09-08 08:46:06 -060042 Previously cros_test_lib.TestProgram.runTests was responsible for globally
43 initializing this mock and multiple tests are flaky if this mock connection
44 is not initialized before any tests are run.
45 """
46 # pylint: disable=protected-access
47 cidb.CIDBConnectionFactory._ClearCIDBSetup()
48 cidb.CIDBConnectionFactory.SetupMockCidb()
Chris McDonaldffe2a412020-04-15 02:27:25 -060049
50
Alex Klein1699fab2022-09-08 08:46:06 -060051@pytest.fixture(scope="class", autouse=True)
Chris McDonaldffe2a412020-04-15 02:27:25 -060052def assert_no_zombies():
Alex Klein1699fab2022-09-08 08:46:06 -060053 """Assert that tests have no active child processes after completion.
Chris McDonaldffe2a412020-04-15 02:27:25 -060054
Alex Klein1699fab2022-09-08 08:46:06 -060055 This assertion runs after class tearDown methods because of the scope='class'
56 declaration.
57 """
58 yield
59 children = multiprocessing.active_children()
60 if children:
61 pytest.fail(
62 "Test has %s active child processes after tearDown: %s"
63 % (len(children), children)
64 )
Chris McDonalde13f0242020-04-07 18:37:15 -060065
66
Alex Klein1699fab2022-09-08 08:46:06 -060067@pytest.fixture(scope="class", autouse=True)
Chris McDonald6ce00962020-04-14 04:05:21 -060068def clear_retry_stats_manager():
Alex Klein1699fab2022-09-08 08:46:06 -060069 """Reset the global state of the stats manager before every test.
Chris McDonald6ce00962020-04-14 04:05:21 -060070
Alex Klein1699fab2022-09-08 08:46:06 -060071 Without this fixture many tests fail due to this global value being set and
72 then not cleared. The child manager process may have been killed but this
73 module level variable is still pointing at it, leading to the test trying to
74 write to a closed pipe.
75 """
76 # pylint: disable=protected-access
77 retry_stats._STATS_COLLECTION = None
78
Chris McDonald6ce00962020-04-14 04:05:21 -060079
Chris McDonald3e1ef0a2020-10-16 13:13:13 -060080@pytest.fixture(autouse=True)
81def set_testing_environment_variable(monkeypatch):
Alex Klein1699fab2022-09-08 08:46:06 -060082 """Set an environment marker to relax certain strict checks for test code."""
83 monkeypatch.setenv("CHROMITE_INSIDE_PYTEST", "1")
84
Chris McDonald6ce00962020-04-14 04:05:21 -060085
Chris McDonalde13f0242020-04-07 18:37:15 -060086@pytest.fixture
Chris McDonald320ef422020-04-18 04:57:04 -060087def singleton_manager(monkeypatch):
Alex Klein1699fab2022-09-08 08:46:06 -060088 """Force tests to use a singleton Manager and automatically clean it up."""
89 m = parallel.Manager()
Chris McDonald320ef422020-04-18 04:57:04 -060090
Alex Klein1699fab2022-09-08 08:46:06 -060091 def our_manager():
92 return m
Chris McDonald320ef422020-04-18 04:57:04 -060093
Alex Klein1699fab2022-09-08 08:46:06 -060094 monkeypatch.setattr(parallel, "Manager", our_manager)
95 yield
96 m.shutdown()
Chris McDonald320ef422020-04-18 04:57:04 -060097
98
99@pytest.fixture
Chris McDonalde13f0242020-04-07 18:37:15 -0600100def legacy_capture_output(request, capfd):
Alex Klein1699fab2022-09-08 08:46:06 -0600101 """Adds the `capfd` fixture to TestCase-style test classes.
Chris McDonalde13f0242020-04-07 18:37:15 -0600102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 This fixture should only be used on cros_test_lib.TestCase test classes, since
104 it doesn't yield anything and just attaches the built-in pytest `capfd`
105 fixture to the requesting class. Tests written as standalone functions should
106 use pytest's built-in `capfd` fixture instead of this. See the documentation
107 for more information on how to use the `capfd` fixture that this provides:
108 https://docs.pytest.org/en/latest/reference/reference.html#std-fixture-capfd
Chris McDonalde13f0242020-04-07 18:37:15 -0600109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 See the following documentation for an explanation of why fixtures have to be
111 provided to TestCase classes in this manner:
112 https://docs.pytest.org/en/latest/how-to/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
113 """
114 request.cls.capfd = capfd
Chris McDonald6ce00962020-04-14 04:05:21 -0600115
116
117@pytest.fixture
118def testcase_caplog(request, caplog):
Alex Klein1699fab2022-09-08 08:46:06 -0600119 """Adds the `caplog` fixture to TestCase-style test classes.
Chris McDonald6ce00962020-04-14 04:05:21 -0600120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 This fixture should only be used on cros_test_lib.TestCase test classes, since
122 it doesn't yield anything and just attaches the built-in pytest `caplog`
123 fixture to the requesting class. Tests written as standalone functions should
124 use pytest's built-in `caplog` fixture instead of this. See the documentation
125 for more information on how to use the `caplog` fixture that this provides:
126 https://docs.pytest.org/en/latest/reference/reference.html#caplog
Chris McDonald6ce00962020-04-14 04:05:21 -0600127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 See the following documentation for an explanation of why fixtures have to be
129 provided to TestCase classes in this manner:
130 https://docs.pytest.org/en/latest/how-to/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
131 """
132 request.cls.caplog = caplog
Chris McDonald05511c92020-05-01 16:45:56 -0600133
134
Michael Mortensende716a12020-05-15 11:27:00 -0600135@pytest.fixture
136def testcase_monkeypatch(request, monkeypatch):
Alex Klein1699fab2022-09-08 08:46:06 -0600137 """Adds the `monkeypatch` fixture to TestCase-style test classes.
Michael Mortensende716a12020-05-15 11:27:00 -0600138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 This fixture should only be used on cros_test_lib.TestCase test classes, since
140 it doesn't yield anything and just attaches the built-in pytest `monkeypatch`
141 fixture to the requesting class. Tests written as standalone functions should
142 use pytest's built-in `monkeypatch` fixture instead of this. See the
143 documentation for more information on how to use the `monkeypatch` fixture
144 that this provides:
145 https://docs.pytest.org/en/latest/reference/reference.html#monkeypatch
Michael Mortensende716a12020-05-15 11:27:00 -0600146
Alex Klein1699fab2022-09-08 08:46:06 -0600147 See the following documentation for an explanation of why fixtures have to be
148 provided to TestCase classes in this manner:
149 https://docs.pytest.org/en/latest/how-to/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
150 """
151 request.cls.monkeypatch = monkeypatch
Michael Mortensende716a12020-05-15 11:27:00 -0600152
153
Chris McDonald05511c92020-05-01 16:45:56 -0600154def pytest_assertrepr_compare(op, left, right):
Alex Klein1699fab2022-09-08 08:46:06 -0600155 """Global hook for defining detailed explanations for failed assertions.
Chris McDonald05511c92020-05-01 16:45:56 -0600156
Alex Klein1699fab2022-09-08 08:46:06 -0600157 https://docs.pytest.org/en/latest/how-to/assert.html
158 """
159 if (
160 isinstance(left, package_info.CPV)
161 and isinstance(right, cr.test.Overlay)
162 and op == "in"
163 ):
164 package_path = right.path / left.category / left.package
165 return [
166 f"{left.pv}.ebuild exists in {right.path}",
167 "Ebuild does not exist in overlay.",
168 "Ebuilds found in overlay with same category and package:",
169 ] + sorted(
170 "\t" + str(p.relative_to(package_path))
171 for p in package_path.glob("*.ebuild")
172 )
Chris McDonaldcdfd1132020-05-12 07:09:51 -0600173
174
175def pytest_addoption(parser):
Alex Klein1699fab2022-09-08 08:46:06 -0600176 """Adds additional options to the default pytest CLI args."""
177 parser.addoption(
178 "--no-chroot",
179 dest="chroot",
180 action="store_false",
181 help="Skip any tests that require a chroot to function.",
182 )
Chris McDonaldcdfd1132020-05-12 07:09:51 -0600183
184
185def pytest_collection_modifyitems(config, items):
Alex Klein1699fab2022-09-08 08:46:06 -0600186 """Modifies the list of test items pytest has collected.
Chris McDonaldcdfd1132020-05-12 07:09:51 -0600187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 See the following link for full documentation on pytest collection hooks:
189 https://docs.pytest.org/en/latest/reference/reference.html#collection-hooks
190 """
191 if config.option.chroot:
192 return
193 skip_inside_only = pytest.mark.skip(reason="Test requires a chroot to run.")
194 for item in items:
195 if "inside_only" in item.keywords:
196 item.add_marker(skip_inside_only)