Skip to content

Commit 4e9491e

Browse files
RonnyPfannschmidtbluetech
authored andcommitted
testing: some cleanups
ran: cherry-picked and adapted slightly.
1 parent 844b3da commit 4e9491e

File tree

6 files changed

+179
-219
lines changed

6 files changed

+179
-219
lines changed

testing/conftest.py

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import pathlib
21
import shutil
3-
import subprocess
42
import sys
3+
from functools import lru_cache
4+
from typing import Callable
5+
from typing import Iterator
56

67
import execnet
78
import pytest
@@ -23,10 +24,15 @@ def pytest_runtest_setup(item):
2324

2425

2526
@pytest.fixture
26-
def makegateway(request):
27+
def group_function() -> Iterator[execnet.Group]:
2728
group = execnet.Group()
28-
request.addfinalizer(lambda: group.terminate(0.5))
29-
return group.makegateway
29+
yield group
30+
group.terminate(0.5)
31+
32+
33+
@pytest.fixture
34+
def makegateway(group_function) -> Callable[[str], execnet.gateway.Gateway]:
35+
return group_function.makegateway
3036

3137

3238
pytest_plugins = ["pytester", "doctest"]
@@ -101,37 +107,16 @@ def pytest_generate_tests(metafunc):
101107
else:
102108
gwtypes = ["popen", "socket", "ssh", "proxy"]
103109
metafunc.parametrize("gw", gwtypes, indirect=True)
104-
elif "anypython" in metafunc.fixturenames:
105-
metafunc.parametrize(
106-
"anypython",
107-
indirect=True,
108-
argvalues=("sys.executable", "pypy3"),
109-
)
110110

111111

112-
def getexecutable(name, cache={}):
113-
try:
114-
return cache[name]
115-
except KeyError:
116-
if name == "sys.executable":
117-
return pathlib.Path(sys.executable)
118-
path = shutil.which(name)
119-
executable = pathlib.Path(path) if path is not None else None
120-
if executable:
121-
if name == "jython":
122-
popen = subprocess.Popen(
123-
[str(executable), "--version"],
124-
universal_newlines=True,
125-
stderr=subprocess.PIPE,
126-
)
127-
out, err = popen.communicate()
128-
if not err or "2.5" not in err:
129-
executable = None
130-
cache[name] = executable
131-
return executable
112+
@lru_cache()
113+
def getexecutable(name):
114+
if name == "sys.executable":
115+
return sys.executable
116+
return shutil.which(name)
132117

133118

134-
@pytest.fixture
119+
@pytest.fixture(params=("sys.executable", "pypy3"))
135120
def anypython(request):
136121
name = request.param
137122
executable = getexecutable(name)

testing/test_basics.py

Lines changed: 114 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
from __future__ import annotations
2+
13
import inspect
24
import os
35
import subprocess
46
import sys
57
import textwrap
8+
from dataclasses import dataclass
69
from io import BytesIO
10+
from pathlib import Path
11+
from typing import Any
712

813
import execnet
914
import pytest
@@ -21,19 +26,18 @@
2126
)
2227

2328

29+
@pytest.mark.parametrize("val", ["123", 42, [1, 2, 3], ["23", 25]])
2430
class TestSerializeAPI:
25-
pytestmark = [pytest.mark.parametrize("val", ["123", 42, [1, 2, 3], ["23", 25]])]
26-
2731
def test_serializer_api(self, val):
2832
dumped = execnet.dumps(val)
2933
val2 = execnet.loads(dumped)
3034
assert val == val2
3135

3236
def test_mmap(self, tmp_path, val):
3337
mmap = pytest.importorskip("mmap").mmap
34-
p = tmp_path / "data"
35-
with p.open("wb") as f:
36-
f.write(execnet.dumps(val))
38+
p = tmp_path / "data.bin"
39+
40+
p.write_bytes(execnet.dumps(val))
3741
with p.open("r+b") as f:
3842
m = mmap(f.fileno(), 0)
3943
val2 = execnet.load(m)
@@ -112,67 +116,83 @@ def read_write_loop():
112116
break
113117

114118

115-
def test_io_message(anypython, tmp_path, execmodel):
116-
check = tmp_path / "check.py"
117-
check.write_text(
118-
inspect.getsource(gateway_base)
119-
+ textwrap.dedent(
120-
"""
121-
from io import BytesIO
122-
import tempfile
123-
temp_out = BytesIO()
124-
temp_in = BytesIO()
125-
io = Popen2IO(temp_out, temp_in, get_execmodel({backend!r}))
126-
for i, handler in enumerate(Message._types):
127-
print ("checking %s %s" %(i, handler))
128-
for data in "hello", "hello".encode('ascii'):
129-
msg1 = Message(i, i, dumps(data))
130-
msg1.to_io(io)
131-
x = io.outfile.getvalue()
132-
io.outfile.truncate(0)
133-
io.outfile.seek(0)
134-
io.infile.seek(0)
135-
io.infile.write(x)
136-
io.infile.seek(0)
137-
msg2 = Message.from_io(io)
138-
assert msg1.channelid == msg2.channelid, (msg1, msg2)
139-
assert msg1.data == msg2.data, (msg1.data, msg2.data)
140-
assert msg1.msgcode == msg2.msgcode
141-
print ("all passed")
142-
""".format(
143-
backend=execmodel.backend
144-
),
119+
IO_MESSAGE_EXTRA_SOURCE = """
120+
import sys
121+
backend = sys.argv[1]
122+
try:
123+
from io import BytesIO
124+
except ImportError:
125+
from StringIO import StringIO as BytesIO
126+
import tempfile
127+
temp_out = BytesIO()
128+
temp_in = BytesIO()
129+
io = Popen2IO(temp_out, temp_in, get_execmodel(backend))
130+
for i, handler in enumerate(Message._types):
131+
print ("checking", i, handler)
132+
for data in "hello", "hello".encode('ascii'):
133+
msg1 = Message(i, i, dumps(data))
134+
msg1.to_io(io)
135+
x = io.outfile.getvalue()
136+
io.outfile.truncate(0)
137+
io.outfile.seek(0)
138+
io.infile.seek(0)
139+
io.infile.write(x)
140+
io.infile.seek(0)
141+
msg2 = Message.from_io(io)
142+
assert msg1.channelid == msg2.channelid, (msg1, msg2)
143+
assert msg1.data == msg2.data, (msg1.data, msg2.data)
144+
assert msg1.msgcode == msg2.msgcode
145+
print ("all passed")
146+
"""
147+
148+
149+
@dataclass
150+
class Checker:
151+
python: str
152+
path: Path
153+
idx: int = 0
154+
155+
def run_check(
156+
self, script: str, *extra_args: str, **process_args: Any
157+
) -> subprocess.CompletedProcess[str]:
158+
self.idx += 1
159+
check_path = self.path / f"check{self.idx}.py"
160+
check_path.write_text(script)
161+
return subprocess.run(
162+
[self.python, os.fspath(check_path), *extra_args],
163+
capture_output=True,
164+
text=True,
165+
check=True,
166+
**process_args,
145167
)
168+
169+
170+
@pytest.fixture
171+
def checker(anypython: str, tmp_path: Path) -> Checker:
172+
return Checker(python=anypython, path=tmp_path)
173+
174+
175+
def test_io_message(checker, execmodel):
176+
out = checker.run_check(
177+
inspect.getsource(gateway_base) + IO_MESSAGE_EXTRA_SOURCE, execmodel.backend
146178
)
147-
out = subprocess.run(
148-
[str(anypython), str(check)], text=True, capture_output=True, check=True
149-
).stdout
150-
print(out)
151-
assert "all passed" in out
179+
print(out.stdout)
180+
assert "all passed" in out.stdout
152181

153182

154-
def test_popen_io(anypython, tmp_path, execmodel):
155-
check = tmp_path / "check.py"
156-
check.write_text(
183+
def test_popen_io(checker, execmodel):
184+
out = checker.run_check(
157185
inspect.getsource(gateway_base)
158-
+ textwrap.dedent(
159-
f"""
160-
io = init_popen_io(get_execmodel({execmodel.backend!r}))
161-
io.write("hello".encode('ascii'))
162-
s = io.read(1)
163-
assert s == "x".encode('ascii')
164-
"""
165-
),
186+
+ f"""
187+
io = init_popen_io(get_execmodel({execmodel.backend!r}))
188+
io.write(b"hello")
189+
s = io.read(1)
190+
assert s == b"x"
191+
""",
192+
input="x",
166193
)
167-
from subprocess import Popen, PIPE
168-
169-
args = [str(anypython), str(check)]
170-
proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
171-
proc.stdin.write(b"x")
172-
stdout, stderr = proc.communicate()
173-
print(stderr)
174-
proc.wait()
175-
assert b"hello" in stdout
194+
print(out.stderr)
195+
assert "hello" in out.stdout
176196

177197

178198
def test_popen_io_readloop(monkeypatch, execmodel):
@@ -190,60 +210,45 @@ def newread(numbytes):
190210
assert result == b"tes"
191211

192212

193-
def test_rinfo_source(anypython, tmp_path):
194-
check = tmp_path / "check.py"
195-
check.write_text(
196-
textwrap.dedent(
197-
"""
198-
class Channel:
199-
def send(self, data):
200-
assert eval(repr(data), {}) == data
201-
channel = Channel()
202-
"""
203-
)
204-
+ inspect.getsource(gateway.rinfo_source)
205-
+ textwrap.dedent(
206-
"""
207-
print ('all passed')
208-
"""
209-
)
213+
def test_rinfo_source(checker):
214+
out = checker.run_check(
215+
f"""
216+
class Channel:
217+
def send(self, data):
218+
assert eval(repr(data), {{}}) == data
219+
channel = Channel()
220+
{inspect.getsource(gateway.rinfo_source)}
221+
print ('all passed')
222+
"""
210223
)
211-
out = subprocess.run(
212-
[str(anypython), str(check)], text=True, capture_output=True, check=True
213-
).stdout
214-
print(out)
215-
assert "all passed" in out
216224

225+
print(out.stdout)
226+
assert "all passed" in out.stdout
217227

218-
def test_geterrortext(anypython, tmp_path):
219-
check = tmp_path / "check.py"
220-
check.write_text(
228+
229+
def test_geterrortext(checker):
230+
out = checker.run_check(
221231
inspect.getsource(gateway_base)
222-
+ textwrap.dedent(
223-
"""
224-
class Arg:
225-
pass
226-
errortext = geterrortext((Arg, "1", 4))
227-
assert "Arg" in errortext
228-
import sys
229-
try:
230-
raise ValueError("17")
231-
except ValueError:
232-
excinfo = sys.exc_info()
233-
s = geterrortext(excinfo)
234-
assert "17" in s
235-
print ("all passed")
232+
+ """
233+
class Arg:
234+
pass
235+
errortext = geterrortext((Arg, "1", 4))
236+
assert "Arg" in errortext
237+
import sys
238+
try:
239+
raise ValueError("17")
240+
except ValueError:
241+
excinfo = sys.exc_info()
242+
s = geterrortext(excinfo)
243+
assert "17" in s
244+
print ("all passed")
236245
"""
237-
)
238246
)
239-
out = subprocess.run(
240-
[str(anypython), str(check)], text=True, capture_output=True, check=True
241-
).stdout
242-
print(out)
243-
assert "all passed" in out
247+
print(out.stdout)
248+
assert "all passed" in out.stdout
244249

245250

246-
@pytest.mark.skipif(not hasattr(os, "dup"), reason="no os.dup")
251+
@pytest.mark.skipif("not hasattr(os, 'dup')")
247252
def test_stdouterrin_setnull(execmodel, capfd):
248253
gateway_base.init_popen_io(execmodel)
249254
os.write(1, b"hello")
@@ -297,15 +302,15 @@ def test_wire_protocol(self):
297302
class TestPureChannel:
298303
@pytest.fixture
299304
def fac(self, execmodel):
300-
class Gateway:
305+
class FakeGateway:
301306
def _trace(self, *args):
302307
pass
303308

304309
def _send(self, *k):
305310
pass
306311

307-
Gateway.execmodel = execmodel
308-
return ChannelFactory(Gateway())
312+
FakeGateway.execmodel = execmodel
313+
return ChannelFactory(FakeGateway())
309314

310315
def test_factory_create(self, fac):
311316
chan1 = fac.new()

0 commit comments

Comments
 (0)