Skip to content

Commit e74a7f6

Browse files
authored
test: attaching via socket, tcp, stdio #544
On windows somehow socket and stdio tests are failing, which should be fixed sometimes later.
1 parent a699fe7 commit e74a7f6

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
tests_require = [
2222
'pytest',
23+
'pytest_timeout',
2324
]
2425

2526
extras_require = {

test/test_attach.py

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""Tests other session_types than subprocess Nvim."""
2+
3+
import contextlib
4+
import os.path
5+
import socket
6+
import subprocess
7+
import tempfile
8+
import time
9+
from typing import Generator
10+
11+
import pytest
12+
import pytest_timeout # pylint: disable=unused-import # noqa
13+
14+
import pynvim
15+
from pynvim.api import Nvim
16+
17+
# pylint: disable=consider-using-with
18+
# pylint: disable=redefined-outer-name
19+
20+
21+
xfail_on_windows = pytest.mark.xfail(
22+
"os.name == 'nt'", reason="Broken in Windows, see #544")
23+
24+
25+
@pytest.fixture
26+
def tmp_socket() -> Generator[str, None, None]:
27+
"""Get a temporary UNIX socket file."""
28+
# see cpython#93914
29+
addr = tempfile.mktemp(prefix="test_python_", suffix='.sock',
30+
dir=os.path.curdir)
31+
try:
32+
yield addr
33+
finally:
34+
if os.path.exists(addr):
35+
with contextlib.suppress(OSError):
36+
os.unlink(addr)
37+
38+
39+
@xfail_on_windows
40+
def test_connect_socket(tmp_socket: str) -> None:
41+
"""Tests UNIX socket connection."""
42+
p = subprocess.Popen(["nvim", "--clean", "-n", "--headless",
43+
"--listen", tmp_socket])
44+
time.sleep(0.2) # wait a bit until nvim starts up
45+
46+
try:
47+
nvim: Nvim = pynvim.attach('socket', path=tmp_socket)
48+
assert 42 == nvim.eval('42')
49+
assert "?" == nvim.command_output('echo "?"')
50+
finally:
51+
with contextlib.suppress(OSError):
52+
p.terminate()
53+
54+
55+
def test_connect_socket_fail() -> None:
56+
"""Tests UNIX socket connection, when the sock file is not found."""
57+
with pytest.raises(FileNotFoundError):
58+
pynvim.attach('socket', path='/tmp/not-exist.socket')
59+
60+
61+
def find_free_port() -> int:
62+
"""Find a free, available port number."""
63+
with socket.socket() as sock:
64+
sock.bind(('', 0)) # Bind to a free port provided by the host.
65+
return sock.getsockname()[1]
66+
67+
68+
def test_connect_tcp() -> None:
69+
"""Tests TCP connection."""
70+
address = '127.0.0.1'
71+
port = find_free_port()
72+
p = subprocess.Popen(["nvim", "--clean", "-n", "--headless",
73+
"--listen", f"{address}:{port}"])
74+
time.sleep(0.2) # wait a bit until nvim starts up
75+
76+
try:
77+
nvim: Nvim = pynvim.attach('tcp', address=address, port=port)
78+
assert 42 == nvim.eval('42')
79+
assert "?" == nvim.command_output('echo "?"')
80+
finally:
81+
with contextlib.suppress(OSError):
82+
p.terminate()
83+
84+
85+
@pytest.mark.timeout(5.0)
86+
def test_connect_tcp_no_server() -> None:
87+
"""Tests TCP socket connection that fails; connection refused."""
88+
port = find_free_port()
89+
90+
with pytest.raises(ConnectionRefusedError):
91+
pynvim.attach('tcp', address='127.0.0.1', port=port)
92+
93+
94+
@xfail_on_windows
95+
def test_connect_stdio(vim: Nvim) -> None:
96+
"""Tests stdio connection, using jobstart(..., {'rpc': v:true})."""
97+
98+
def source(vim: Nvim, code: str) -> None:
99+
"""Source a vimscript code in the embedded nvim instance."""
100+
fd, fname = tempfile.mkstemp()
101+
try:
102+
with os.fdopen(fd, 'w') as f:
103+
f.write(code)
104+
vim.command('source ' + fname)
105+
finally:
106+
os.unlink(fname)
107+
108+
# A helper function for debugging that captures what pynvim writes to
109+
# stderr (e.g. python stacktrace): used as a |on_stderr| callback
110+
source(vim, """
111+
function! OutputHandler(j, lines, event_type)
112+
if a:event_type == 'stderr'
113+
for l:line in a:lines
114+
echom l:line
115+
endfor
116+
endif
117+
endfunction
118+
""")
119+
120+
remote_py_code = '\n'.join([
121+
'import pynvim',
122+
'nvim = pynvim.attach("stdio")',
123+
'print("rplugins can write to stdout")', # tests #377 (#60)
124+
'nvim.api.command("let g:success = 42")',
125+
])
126+
# see :help jobstart(), *jobstart-options* |msgpack-rpc|
127+
jobid = vim.funcs.jobstart([
128+
'python', '-c', remote_py_code,
129+
], {'rpc': True, 'on_stderr': 'OutputHandler'})
130+
assert jobid > 0
131+
exitcode = vim.funcs.jobwait([jobid], 500)[0]
132+
messages = vim.command_output('messages')
133+
assert exitcode == 0, ("the python process failed, :messages =>\n\n" +
134+
messages)
135+
136+
assert 42 == vim.eval('g:success')
137+
assert "rplugins can write to stdout" in messages

test/test_vim.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Tests interaction with neovim via Nvim API (with child process)."""
2+
13
import os
24
import sys
35
import tempfile

0 commit comments

Comments
 (0)