blob: 0a370d504da7bc107fc5ae20bb75a92c1552b1c0 [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 McDonald05511c92020-05-01 16:45:56 -060023from chromite.lib import portage_util
Chris McDonald6ce00962020-04-14 04:05:21 -060024from chromite.lib import retry_stats
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
79
Chris McDonalde13f0242020-04-07 18:37:15 -060080@pytest.fixture
Chris McDonald320ef422020-04-18 04:57:04 -060081def singleton_manager(monkeypatch):
82 """Force tests to use a singleton Manager and automatically clean it up."""
83 m = parallel.Manager()
84
85 def our_manager():
86 return m
87
88 monkeypatch.setattr(parallel, 'Manager', our_manager)
89 yield
90 m.shutdown()
91
92
93@pytest.fixture
Chris McDonalde13f0242020-04-07 18:37:15 -060094def legacy_capture_output(request, capfd):
95 """Adds the `capfd` fixture to TestCase-style test classes.
96
97 This fixture should only be used on cros_test_lib.TestCase test classes, since
98 it doesn't yield anything and just attaches the built-in pytest `capfd`
99 fixture to the requesting class. Tests written as standalone functions should
100 use pytest's built-in `capfd` fixture instead of this. See the documentation
101 for more information on how to use the `capfd` fixture that this provides:
102 https://docs.pytest.org/en/latest/reference.html#capfd
103
104 See the following documentation for an explanation of why fixtures have to be
105 provided to TestCase classes in this manner:
106 https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
107 """
108 request.cls.capfd = capfd
Chris McDonald6ce00962020-04-14 04:05:21 -0600109
110
111@pytest.fixture
112def testcase_caplog(request, caplog):
113 """Adds the `caplog` fixture to TestCase-style test classes.
114
115 This fixture should only be used on cros_test_lib.TestCase test classes, since
116 it doesn't yield anything and just attaches the built-in pytest `caplog`
117 fixture to the requesting class. Tests written as standalone functions should
118 use pytest's built-in `caplog` fixture instead of this. See the documentation
119 for more information on how to use the `caplog` fixture that this provides:
120 https://docs.pytest.org/en/latest/reference.html#caplog
121
122 See the following documentation for an explanation of why fixtures have to be
123 provided to TestCase classes in this manner:
124 https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
125 """
126 request.cls.caplog = caplog
Chris McDonald05511c92020-05-01 16:45:56 -0600127
128
129def pytest_assertrepr_compare(op, left, right):
130 """Global hook for defining detailed explanations for failed assertions.
131
132 https://docs.pytest.org/en/latest/assert.html#defining-your-own-explanation-for-failed-assertions
133 """
134 if isinstance(left, portage_util.CPV) and isinstance(
135 right, cr.test.Overlay) and op == 'in':
136 package_path = right.path / left.category / left.package
137 return [
138 f'{left.pv}.ebuild exists in {right.path}',
139 'Ebuild does not exist in overlay.',
140 'Ebuilds found in overlay with same category and package:'
141 ] + sorted('\t' + str(p.relative_to(package_path))
142 for p in package_path.glob('*.ebuild'))
Chris McDonaldcdfd1132020-05-12 07:09:51 -0600143
144
145def pytest_addoption(parser):
146 """Adds additional options to the default pytest CLI args."""
147 parser.addoption(
148 '--no-chroot',
149 dest='chroot',
150 action='store_false',
151 help='Skip any tests that require a chroot to function.',
152 )
153
154
155def pytest_collection_modifyitems(config, items):
156 """Modifies the list of test items pytest has collected.
157
158 See the following link for full documentation on pytest collection hooks:
159 https://docs.pytest.org/en/latest/reference.html?highlight=pytest_collection_modifyitems#collection-hooks
160 """
161 if config.option.chroot:
162 return
163 skip_inside_only = pytest.mark.skip(reason='Test requires a chroot to run.')
164 for item in items:
165 if 'inside_only' in item.keywords:
166 item.add_marker(skip_inside_only)