1# Copyright (C) 2021 Apple Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1. Redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer.
8# 2. Redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23import warnings
24import types
25import sys
26
27import pytest
28
29
30def pytest_configure(config):
31 config.addinivalue_line("markers", "serial: tests that must be run in serial")
32 config.addinivalue_line("markers", "integration: integration tests")
33
34
35def pytest_addoption(parser):
36 parser.addoption(
37 "--run-integration",
38 action="store_true",
39 default=False,
40 help="run integration tests",
41 )
42
43
44@pytest.hookimpl(tryfirst=True)
45def pytest_pycollect_makeitem(collector, name, obj):
46 try:
47 ut = sys.modules["unittest"]
48 if not issubclass(obj, ut.TestCase):
49 return None
50 except Exception:
51 return None
52
53 if getattr(obj, "__pytest_no_rewrite__", False):
54 return None
55
56 for attr_name in set(dir(obj)):
57 serial = False
58 integration = False
59 if attr_name.startswith("serial_integration_test_"):
60 serial = True
61 integration = True
62 elif attr_name.startswith("serial_test_"):
63 serial = True
64 elif attr_name.startswith("integration_test_"):
65 integration = True
66 else:
67 continue
68
69 method = getattr(obj, attr_name)
70 if not callable(method):
71 continue
72
73 new_attr_name = "test_" + attr_name
74
75 existing_attr = getattr(obj, new_attr_name, None)
76 if existing_attr:
77 if method != existing_attr:
78 warnings.warn(
79 "attribute %r already defined on %r; %r might hide %r"
80 % (new_attr_name, obj, method, existing_attr)
81 )
82
83 if sys.version_info < (3,) and isinstance(method, types.MethodType):
84 method = method.im_func
85
86 if serial:
87 method = pytest.mark.serial(method)
88
89 if integration:
90 method = pytest.mark.integration(method)
91
92 setattr(obj, new_attr_name, method)
93
94 return None
95
96
97def pytest_collection_modifyitems(config, items):
98 if hasattr(config, "workerinput"):
99 skip_serial = pytest.mark.skip(reason="cannot run in parallel")
100 for item in items:
101 if "serial" in item.keywords:
102 item.add_marker(skip_serial)
103
104 if not config.getoption("--run-integration"):
105 skip_integration = pytest.mark.skip(
106 reason="need --run-integration option to run"
107 )
108 for item in items:
109 if "integration" in item.keywords:
110 item.add_marker(skip_integration)