Skip to content

Commit 4d9024e

Browse files
authored
tests: cleanup and ci hardening (#2397)
* tests: refactor and cleanup * refactor: more consistent * tests: vendor six * tests: more xfails, nicer system * tests: simplify to info * tests: suggestions from @YannickJadoul and @bstaletic * tests: restore some pypy tests that now pass * tests: rename info to env * tests: strict False/True * tests: drop explicit strict=True again * tests: reduce minimum PyTest to 3.1
1 parent 3618bea commit 4d9024e

26 files changed

+158
-171
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ jobs:
1717
runs-on: [ubuntu-latest, windows-latest, macos-latest]
1818
arch: [x64]
1919
max-cxx-std: [17]
20+
dev: [false]
2021
python:
2122
- 2.7
2223
- 3.5
2324
- 3.8
24-
- 3.9-dev
2525
- pypy2
2626
- pypy3
2727

@@ -30,46 +30,68 @@ jobs:
3030
python: 3.6
3131
arch: x64
3232
max-cxx-std: 17
33+
dev: false
3334
- runs-on: macos-latest
3435
python: 3.7
3536
arch: x64
3637
max-cxx-std: 17
38+
dev: false
3739
- runs-on: windows-2016
3840
python: 3.7
3941
arch: x86
4042
max-cxx-std: 14
43+
dev: false
4144
- runs-on: windows-latest
4245
python: 3.6
4346
arch: x64
4447
max-cxx-std: 17
48+
dev: false
4549
- runs-on: windows-latest
4650
python: 3.7
4751
arch: x64
4852
max-cxx-std: 17
53+
dev: false
54+
55+
- runs-on: ubuntu-latest
56+
python: 3.9-dev
57+
arch: x64
58+
max-cxx-std: 17
59+
dev: true
60+
- runs-on: macos-latest
61+
python: 3.9-dev
62+
arch: x64
63+
max-cxx-std: 17
64+
dev: true
4965

5066
exclude:
5167
# Currently 32bit only, and we build 64bit
5268
- runs-on: windows-latest
5369
python: pypy2
5470
arch: x64
5571
max-cxx-std: 17
72+
dev: false
5673
- runs-on: windows-latest
5774
python: pypy3
5875
arch: x64
5976
max-cxx-std: 17
77+
dev: false
6078

6179
# Currently broken on embed_test
6280
- runs-on: windows-latest
6381
python: 3.8
6482
arch: x64
6583
max-cxx-std: 17
84+
dev: false
6685
- runs-on: windows-latest
6786
python: 3.9-dev
6887
arch: x64
6988
max-cxx-std: 17
89+
dev: false
90+
7091

7192
name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • ${{ matrix.arch }}"
7293
runs-on: ${{ matrix.runs-on }}
94+
continue-on-error: ${{ matrix.dev }}
7395

7496
steps:
7597
- uses: actions/checkout@v2
@@ -289,7 +311,8 @@ jobs:
289311
- name: Install requirements
290312
run: |
291313
apt-get update
292-
apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip python3-pytest
314+
apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip
315+
pip3 install "pytest==3.1.*"
293316
294317
- name: Configure for install
295318
run: >

tests/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ if(NOT PYBIND11_PYTEST_FOUND)
266266
if(pytest_not_found)
267267
message(FATAL_ERROR "Running the tests requires pytest. Please install it manually"
268268
" (try: ${PYTHON_EXECUTABLE} -m pip install pytest)")
269-
elseif(pytest_version VERSION_LESS 3.0)
270-
message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}"
269+
elseif(pytest_version VERSION_LESS 3.1)
270+
message(FATAL_ERROR "Running the tests requires pytest >= 3.1. Found: ${pytest_version}"
271271
"Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)")
272272
endif()
273273
set(PYBIND11_PYTEST_FOUND

tests/conftest.py

Lines changed: 8 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,21 @@
55
Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences.
66
"""
77

8-
import pytest
9-
import textwrap
10-
import difflib
11-
import re
12-
import sys
138
import contextlib
14-
import platform
9+
import difflib
1510
import gc
11+
import re
12+
import textwrap
13+
14+
import pytest
15+
16+
# Early diagnostic for failed imports
17+
import pybind11_tests # noqa: F401
1618

1719
_unicode_marker = re.compile(r'u(\'[^\']*\')')
1820
_long_marker = re.compile(r'([0-9])L')
1921
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')
2022

21-
# test_async.py requires support for async and await
22-
collect_ignore = []
23-
if sys.version_info[:2] < (3, 5):
24-
collect_ignore.append("test_async.py")
25-
2623

2724
def _strip_and_dedent(s):
2825
"""For triple-quote strings"""
@@ -192,63 +189,5 @@ def gc_collect():
192189

193190

194191
def pytest_configure():
195-
"""Add import suppression and test requirements to `pytest` namespace"""
196-
try:
197-
import numpy as np
198-
except ImportError:
199-
np = None
200-
try:
201-
import scipy
202-
except ImportError:
203-
scipy = None
204-
try:
205-
from pybind11_tests.eigen import have_eigen
206-
except ImportError:
207-
have_eigen = False
208-
209-
# Provide simple `six`-like aliases.
210-
pytest.PY2 = (sys.version_info.major == 2)
211-
pytest.CPYTHON = (platform.python_implementation() == "CPython")
212-
pytest.PYPY = (platform.python_implementation() == "PyPy")
213-
214-
skipif = pytest.mark.skipif
215192
pytest.suppress = suppress
216-
pytest.requires_numpy = skipif(not np, reason="numpy is not installed")
217-
pytest.requires_scipy = skipif(not np, reason="scipy is not installed")
218-
pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np,
219-
reason="eigen and/or numpy are not installed")
220-
pytest.requires_eigen_and_scipy = skipif(
221-
not have_eigen or not scipy, reason="eigen and/or scipy are not installed")
222-
pytest.unsupported_on_pypy = skipif(pytest.PYPY, reason="unsupported on PyPy")
223-
pytest.bug_in_pypy = pytest.mark.xfail(pytest.PYPY, reason="bug in PyPy")
224-
pytest.unsupported_on_pypy3 = skipif(pytest.PYPY and not pytest.PY2,
225-
reason="unsupported on PyPy3")
226-
pytest.unsupported_on_pypy_lt_6 = skipif(pytest.PYPY and sys.pypy_version_info[0] < 6,
227-
reason="unsupported on PyPy<6")
228-
pytest.unsupported_on_py2 = skipif(pytest.PY2,
229-
reason="unsupported on Python 2.x")
230193
pytest.gc_collect = gc_collect
231-
232-
233-
def _test_import_pybind11():
234-
"""Early diagnostic for test module initialization errors
235-
236-
When there is an error during initialization, the first import will report the
237-
real error while all subsequent imports will report nonsense. This import test
238-
is done early (in the pytest configuration file, before any tests) in order to
239-
avoid the noise of having all tests fail with identical error messages.
240-
241-
Any possible exception is caught here and reported manually *without* the stack
242-
trace. This further reduces noise since the trace would only show pytest internals
243-
which are not useful for debugging pybind11 module issues.
244-
"""
245-
# noinspection PyBroadException
246-
try:
247-
import pybind11_tests # noqa: F401 imported but unused
248-
except Exception as e:
249-
print("Failed to import pybind11_tests from pytest:")
250-
print(" {}: {}".format(type(e).__name__, e))
251-
sys.exit(1)
252-
253-
254-
_test_import_pybind11()

tests/env.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# -*- coding: utf-8 -*-
2+
import platform
3+
import sys
4+
5+
LINUX = sys.platform.startswith("linux")
6+
MACOS = sys.platform.startswith("darwin")
7+
WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
8+
9+
CPYTHON = platform.python_implementation() == "CPython"
10+
PYPY = platform.python_implementation() == "PyPy"
11+
12+
PY2 = sys.version_info.major == 2

tests/pybind11_tests.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,4 @@ PYBIND11_MODULE(pybind11_tests, m) {
8888

8989
for (const auto &initializer : initializers())
9090
initializer(m);
91-
92-
if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false;
9391
}

tests/pytest.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
[pytest]
2-
minversion = 3.0
2+
minversion = 3.1
33
norecursedirs = test_cmake_build test_embed
4+
xfail_strict = True
45
addopts =
56
# show summary of skipped tests
67
-rs
78
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
89
--capture=sys
10+
# enable all warnings
11+
-Wa
912
filterwarnings =
1013
# make warnings into errors but ignore certain third-party extension issues
1114
error

tests/test_async.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# -*- coding: utf-8 -*-
2-
import asyncio
32
import pytest
4-
from pybind11_tests import async_module as m
3+
4+
asyncio = pytest.importorskip("asyncio")
5+
m = pytest.importorskip("pybind11_tests.async_module")
56

67

78
@pytest.fixture

tests/test_buffers.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55
import pytest
66

7+
import env # noqa: F401
8+
79
from pybind11_tests import buffers as m
810
from pybind11_tests import ConstructorStats
911

10-
pytestmark = pytest.requires_numpy
11-
12-
with pytest.suppress(ImportError):
13-
import numpy as np
12+
np = pytest.importorskip("numpy")
1413

1514

1615
def test_from_python():
@@ -36,9 +35,7 @@ def test_from_python():
3635
assert cstats.move_assignments == 0
3736

3837

39-
# PyPy: Memory leak in the "np.array(m, copy=False)" call
40-
# https://bitbucket.org/pypy/pypy/issues/2444
41-
@pytest.unsupported_on_pypy
38+
# https://foss.heptapod.net/pypy/pypy/-/issues/2444
4239
def test_to_python():
4340
mat = m.Matrix(5, 4)
4441
assert memoryview(mat).shape == (5, 4)
@@ -73,7 +70,6 @@ def test_to_python():
7370
assert cstats.move_assignments == 0
7471

7572

76-
@pytest.unsupported_on_pypy
7773
def test_inherited_protocol():
7874
"""SquareMatrix is derived from Matrix and inherits the buffer protocol"""
7975

@@ -82,7 +78,6 @@ def test_inherited_protocol():
8278
assert np.asarray(matrix).shape == (5, 5)
8379

8480

85-
@pytest.unsupported_on_pypy
8681
def test_pointer_to_member_fn():
8782
for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]:
8883
buf = cls()
@@ -91,26 +86,24 @@ def test_pointer_to_member_fn():
9186
assert value == 0x12345678
9287

9388

94-
@pytest.unsupported_on_pypy
9589
def test_readonly_buffer():
9690
buf = m.BufferReadOnly(0x64)
9791
view = memoryview(buf)
98-
assert view[0] == b'd' if pytest.PY2 else 0x64
92+
assert view[0] == b'd' if env.PY2 else 0x64
9993
assert view.readonly
10094

10195

102-
@pytest.unsupported_on_pypy
10396
def test_selective_readonly_buffer():
10497
buf = m.BufferReadOnlySelect()
10598

106-
memoryview(buf)[0] = b'd' if pytest.PY2 else 0x64
99+
memoryview(buf)[0] = b'd' if env.PY2 else 0x64
107100
assert buf.value == 0x64
108101

109102
io.BytesIO(b'A').readinto(buf)
110103
assert buf.value == ord(b'A')
111104

112105
buf.readonly = True
113106
with pytest.raises(TypeError):
114-
memoryview(buf)[0] = b'\0' if pytest.PY2 else 0
107+
memoryview(buf)[0] = b'\0' if env.PY2 else 0
115108
with pytest.raises(TypeError):
116109
io.BytesIO(b'1').readinto(buf)

tests/test_builtin_casters.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# -*- coding: utf-8 -*-
22
import pytest
33

4+
import env # noqa: F401
5+
46
from pybind11_tests import builtin_casters as m
57
from pybind11_tests import UserType, IncType
68

@@ -117,10 +119,7 @@ def test_bytes_to_string():
117119
# Issue #816
118120

119121
def to_bytes(s):
120-
if pytest.PY2:
121-
b = s
122-
else:
123-
b = s.encode("utf8")
122+
b = s if env.PY2 else s.encode("utf8")
124123
assert isinstance(b, bytes)
125124
return b
126125

@@ -197,7 +196,7 @@ def test_integer_casting():
197196
assert m.i64_str(-1) == "-1"
198197
assert m.i32_str(2000000000) == "2000000000"
199198
assert m.u32_str(2000000000) == "2000000000"
200-
if pytest.PY2:
199+
if env.PY2:
201200
assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
202201
assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
203202
assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name
@@ -219,7 +218,7 @@ def test_integer_casting():
219218
m.i32_str(3000000000)
220219
assert "incompatible function arguments" in str(excinfo.value)
221220

222-
if pytest.PY2:
221+
if env.PY2:
223222
with pytest.raises(TypeError) as excinfo:
224223
m.u32_str(long(-1)) # noqa: F821 undefined name 'long'
225224
assert "incompatible function arguments" in str(excinfo.value)
@@ -360,9 +359,9 @@ class B(object):
360359
assert convert(A(False)) is False
361360

362361

363-
@pytest.requires_numpy
364362
def test_numpy_bool():
365-
import numpy as np
363+
np = pytest.importorskip("numpy")
364+
366365
convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert
367366

368367
def cant_convert(v):

tests/test_call_policies.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# -*- coding: utf-8 -*-
22
import pytest
3+
4+
import env # noqa: F401
5+
36
from pybind11_tests import call_policies as m
47
from pybind11_tests import ConstructorStats
58

69

10+
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
711
def test_keep_alive_argument(capture):
812
n_inst = ConstructorStats.detail_reg_inst()
913
with capture:
@@ -70,8 +74,8 @@ def test_keep_alive_return_value(capture):
7074
"""
7175

7276

73-
# https://bitbucket.org/pypy/pypy/issues/2447
74-
@pytest.unsupported_on_pypy
77+
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
78+
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
7579
def test_alive_gc(capture):
7680
n_inst = ConstructorStats.detail_reg_inst()
7781
p = m.ParentGC()

0 commit comments

Comments
 (0)