blob: b1281abb11ed43b243974712cbc09e7104e7eb4e [file] [log] [blame]
Chris McDonald2e9a09c2020-04-03 16:09:32 -06001# -*- coding: utf-8 -*-
2# Copyright 2020 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Configuration and fixtures for pytest.
7
8See the following doc link for an explanation of conftest.py and how it is used
9by pytest:
10https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions
11"""
12
Chris McDonald05511c92020-05-01 16:45:56 -060013from __future__ import division
Chris McDonald2e9a09c2020-04-03 16:09:32 -060014from __future__ import print_function
15
Chris McDonaldffe2a412020-04-15 02:27:25 -060016import multiprocessing
17
Chris McDonaldee2fbda2020-04-30 18:10:01 -060018import pytest
Chris McDonald2e9a09c2020-04-03 16:09:32 -060019
Chris McDonald05511c92020-05-01 16:45:56 -060020import chromite as cr
Chris McDonald2e9a09c2020-04-03 16:09:32 -060021from chromite.lib import cidb
Chris McDonald320ef422020-04-18 04:57:04 -060022from chromite.lib import parallel
Chris McDonald6ce00962020-04-14 04:05:21 -060023from chromite.lib import retry_stats
Alex Klein75df1792020-06-11 14:42:49 -060024from chromite.lib.parser import package_info
Chris McDonald2e9a09c2020-04-03 16:09:32 -060025
Chris McDonaldee2fbda2020-04-30 18:10:01 -060026# We use wildcard imports here to make fixtures defined in the test module
27# globally visible.
28# pylint: disable=unused-wildcard-import, wildcard-import
29
30# Importing from *_fixtures.py files into conftest.py is the only time a
31# module should use a wildcard import. *_fixtures.py files should ensure
32# that the only items visible to a wildcard import are pytest fixtures,
33# usually by declaring __all__ if necessary.
34from chromite.test.portage_fixtures import *
35
Chris McDonald2e9a09c2020-04-03 16:09:32 -060036
Chris McDonalde53dde12020-04-07 13:43:14 -060037@pytest.fixture(scope='class', autouse=True)
Chris McDonald2e9a09c2020-04-03 16:09:32 -060038def mock_cidb_connection():
39 """Ensure that the CIDB connection factory is initialized as a mock.
40
41 Unit tests should never connect to any live instances of CIDB and this
42 initialization ensures that they only ever get a mock connection instance.
43
44 Previously cros_test_lib.TestProgram.runTests was responsible for globally
45 initializing this mock and multiple tests are flaky if this mock connection
46 is not initialized before any tests are run.
47 """
Chris McDonalde53dde12020-04-07 13:43:14 -060048 # pylint: disable=protected-access
49 cidb.CIDBConnectionFactory._ClearCIDBSetup()
Chris McDonald2e9a09c2020-04-03 16:09:32 -060050 cidb.CIDBConnectionFactory.SetupMockCidb()
Chris McDonaldffe2a412020-04-15 02:27:25 -060051
52
53@pytest.fixture(scope='class', autouse=True)
54def assert_no_zombies():
55 """Assert that tests have no active child processes after completion.
56
57 This assertion runs after class tearDown methods because of the scope='class'
58 declaration.
59 """
60 yield
61 children = multiprocessing.active_children()
62 if children:
63 pytest.fail('Test has %s active child processes after tearDown: %s' %
64 (len(children), children))
Chris McDonalde13f0242020-04-07 18:37:15 -060065
66
Chris McDonald6ce00962020-04-14 04:05:21 -060067@pytest.fixture(scope='class', autouse=True)
68def clear_retry_stats_manager():
69 """Reset the global state of the stats manager before every test.
70
71 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 McDonald3e1ef0a2020-10-16 13:13:13 -060079@pytest.fixture(autouse=True)
80def set_testing_environment_variable(monkeypatch):
81 """Set an environment marker to relax certain strict checks for test code."""
82 monkeypatch.setenv('CHROMITE_INSIDE_PYTEST', '1')
Chris McDonald6ce00962020-04-14 04:05:21 -060083
Chris McDonalde13f0242020-04-07 18:37:15 -060084@pytest.fixture
Chris McDonald320ef422020-04-18 04:57:04 -060085def singleton_manager(monkeypatch):
86 """Force tests to use a singleton Manager and automatically clean it up."""
87 m = parallel.Manager()
88
89 def our_manager():
90 return m
91
92 monkeypatch.setattr(parallel, 'Manager', our_manager)
93 yield
94 m.shutdown()
95
96
97@pytest.fixture
Chris McDonalde13f0242020-04-07 18:37:15 -060098def legacy_capture_output(request, capfd):
99 """Adds the `capfd` fixture to TestCase-style test classes.
100
101 This fixture should only be used on cros_test_lib.TestCase test classes, since
102 it doesn't yield anything and just attaches the built-in pytest `capfd`
103 fixture to the requesting class. Tests written as standalone functions should
104 use pytest's built-in `capfd` fixture instead of this. See the documentation
105 for more information on how to use the `capfd` fixture that this provides:
106 https://docs.pytest.org/en/latest/reference.html#capfd
107
108 See the following documentation for an explanation of why fixtures have to be
109 provided to TestCase classes in this manner:
110 https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
111 """
112 request.cls.capfd = capfd
Chris McDonald6ce00962020-04-14 04:05:21 -0600113
114
115@pytest.fixture
116def testcase_caplog(request, caplog):
117 """Adds the `caplog` fixture to TestCase-style test classes.
118
119 This fixture should only be used on cros_test_lib.TestCase test classes, since
120 it doesn't yield anything and just attaches the built-in pytest `caplog`
121 fixture to the requesting class. Tests written as standalone functions should
122 use pytest's built-in `caplog` fixture instead of this. See the documentation
123 for more information on how to use the `caplog` fixture that this provides:
124 https://docs.pytest.org/en/latest/reference.html#caplog
125
126 See the following documentation for an explanation of why fixtures have to be
127 provided to TestCase classes in this manner:
128 https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
129 """
130 request.cls.caplog = caplog
Chris McDonald05511c92020-05-01 16:45:56 -0600131
132
Michael Mortensende716a12020-05-15 11:27:00 -0600133@pytest.fixture
134def testcase_monkeypatch(request, monkeypatch):
135 """Adds the `monkeypatch` fixture to TestCase-style test classes.
136
137 This fixture should only be used on cros_test_lib.TestCase test classes, since
138 it doesn't yield anything and just attaches the built-in pytest `monkeypatch`
139 fixture to the requesting class. Tests written as standalone functions should
140 use pytest's built-in `monkeypatch` fixture instead of this. See the
141 documentation for more information on how to use the `monkeypatch` fixture
142 that this provides:
143 https://docs.pytest.org/en/latest/reference.html#monkeypatch
144
145 See the following documentation for an explanation of why fixtures have to be
146 provided to TestCase classes in this manner:
147 https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
148 """
149 request.cls.monkeypatch = monkeypatch
150
151
Chris McDonald05511c92020-05-01 16:45:56 -0600152def pytest_assertrepr_compare(op, left, right):
153 """Global hook for defining detailed explanations for failed assertions.
154
155 https://docs.pytest.org/en/latest/assert.html#defining-your-own-explanation-for-failed-assertions
156 """
Alex Klein75df1792020-06-11 14:42:49 -0600157 if isinstance(left, package_info.CPV) and isinstance(
Chris McDonald05511c92020-05-01 16:45:56 -0600158 right, cr.test.Overlay) and op == 'in':
159 package_path = right.path / left.category / left.package
160 return [
161 f'{left.pv}.ebuild exists in {right.path}',
162 'Ebuild does not exist in overlay.',
163 'Ebuilds found in overlay with same category and package:'
164 ] + sorted('\t' + str(p.relative_to(package_path))
165 for p in package_path.glob('*.ebuild'))
Chris McDonaldcdfd1132020-05-12 07:09:51 -0600166
167
168def pytest_addoption(parser):
169 """Adds additional options to the default pytest CLI args."""
170 parser.addoption(
171 '--no-chroot',
172 dest='chroot',
173 action='store_false',
174 help='Skip any tests that require a chroot to function.',
175 )
176
177
178def pytest_collection_modifyitems(config, items):
179 """Modifies the list of test items pytest has collected.
180
181 See the following link for full documentation on pytest collection hooks:
182 https://docs.pytest.org/en/latest/reference.html?highlight=pytest_collection_modifyitems#collection-hooks
183 """
184 if config.option.chroot:
185 return
186 skip_inside_only = pytest.mark.skip(reason='Test requires a chroot to run.')
187 for item in items:
188 if 'inside_only' in item.keywords:
189 item.add_marker(skip_inside_only)