Skip to content

Commit 43743a5

Browse files
authored
Merge pull request #58 from pytest-dev/55-reduce-live-server-start-time
2 parents f01e17d + ec1a363 commit 43743a5

File tree

5 files changed

+107
-16
lines changed

5 files changed

+107
-16
lines changed

docs/changelog.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
Changelog
44
=========
55

6+
1.1.0 (UNRELEASED)
7+
------------------
8+
9+
- Speedup live server start time. Use `socket` instead of server
10+
pulling (`#58`_) to check server availability and add new
11+
``--live-server-wait`` option to set the live server wait timeout.
12+
Thanks to `@jadkik`_.
13+
14+
615
1.0.0 (2020-03-03)
716
------------------
817

@@ -104,7 +113,9 @@ Changelog
104113
.. _#48: https://github.com/pytest-dev/pytest-flask/pull/48
105114
.. _#50: https://github.com/pytest-dev/pytest-flask/pull/50
106115
.. _#56: https://github.com/pytest-dev/pytest-flask/pull/56
116+
.. _#58: steenzouthttps://github.com/pytest-dev/pytest-flask/pull/58
107117
.. _@danstender: https://github.com/danstender
118+
.. _@jadkik: https://github.com/jadkik
108119
.. _@jineshpaloor: https://github.com/jineshpaloor
109120
.. _@mattwbarry: https://github.com/mattwbarry
110121
.. _@steenzout: https://github.com/steenzout

docs/features.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ example, in your project’s ``pytest.ini`` file)::
174174
assert b'got it' in res.read()
175175
176176
177+
``--live-server-wait`` - the live server wait timeout (5 seconds)
178+
`````````````````````````````````````````````````````````````````
179+
180+
The timeout after which test case is aborted if live server is not started.
177181
``--live-server-port`` - use a fixed port
178182
`````````````````````````````````````````
179183

pytest_flask/fixtures.py

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,35 @@
11
#!/usr/bin/env python
2+
import functools
23
import logging
34
import multiprocessing
45
import os
56
import signal
67
import socket
78
import time
8-
from urllib.error import URLError
9-
from urllib.request import urlopen
9+
import warnings
1010

1111
import pytest
1212
from flask import _request_ctx_stack
1313

1414

15+
def deprecated(reason):
16+
"""Decorator which can be used to mark function or method as deprecated.
17+
It will result a warning being emmitted when the function is called.
18+
"""
19+
20+
def decorator(func):
21+
@functools.wraps(func)
22+
def deprecated_call(*args, **kwargs):
23+
warnings.simplefilter("always", DeprecationWarning)
24+
warnings.warn(reason, DeprecationWarning, stacklevel=2)
25+
warnings.simplefilter("default", DeprecationWarning)
26+
return func(*args, **kwargs)
27+
28+
return deprecated_call
29+
30+
return decorator
31+
32+
1533
@pytest.yield_fixture
1634
def client(app):
1735
"""A Flask test client. An instance of :class:`flask.testing.TestClient`
@@ -47,12 +65,15 @@ class LiveServer:
4765
:param app: The application to run.
4866
:param host: The host where to listen (default localhost).
4967
:param port: The port to run application.
68+
:param wait: The timeout after which test case is aborted if
69+
application is not started.
5070
"""
5171

52-
def __init__(self, app, host, port, clean_stop=False):
72+
def __init__(self, app, host, port, wait, clean_stop=False):
5373
self.app = app
5474
self.port = port
5575
self.host = host
76+
self.wait = wait
5677
self.clean_stop = clean_stop
5778
self._process = None
5879

@@ -65,22 +86,45 @@ def worker(app, host, port):
6586
self._process = multiprocessing.Process(
6687
target=worker, args=(self.app, self.host, self.port)
6788
)
89+
self._process.daemon = True
6890
self._process.start()
6991

70-
# We must wait for the server to start listening with a maximum
71-
# timeout of 5 seconds.
72-
timeout = 5
73-
while timeout > 0:
74-
time.sleep(1)
75-
try:
76-
urlopen(self.url())
77-
timeout = 0
78-
except URLError:
79-
timeout -= 1
80-
92+
keep_trying = True
93+
start_time = time.time()
94+
while keep_trying:
95+
elapsed_time = time.time() - start_time
96+
if elapsed_time > self.wait:
97+
pytest.fail(
98+
"Failed to start the server after {!s} "
99+
"seconds.".format(self.wait)
100+
)
101+
if self._is_ready():
102+
keep_trying = False
103+
104+
def _is_ready(self):
105+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106+
try:
107+
sock.connect((self.host, self.port))
108+
except socket.error:
109+
ret = False
110+
else:
111+
ret = True
112+
finally:
113+
sock.close()
114+
return ret
115+
116+
@deprecated(
117+
reason=(
118+
'The "live_server.url" method is deprecated and will '
119+
"be removed in the future. Please use "
120+
'the "flask.url_for" function instead.',
121+
)
122+
)
81123
def url(self, url=""):
82124
"""Returns the complete url based on server options."""
83-
return "http://%s:%d%s" % (self.host, self.port, url)
125+
return "http://{host!s}:{port!s}{url!s}".format(
126+
host=self.host, port=self.port, url=url
127+
)
84128

85129
def stop(self):
86130
"""Stop application process."""
@@ -155,8 +199,9 @@ def test_server_is_up_and_running(live_server):
155199
final_server_name = _rewrite_server_name(original_server_name, str(port))
156200
app.config["SERVER_NAME"] = final_server_name
157201

202+
wait = request.config.getvalue("live_server_wait")
158203
clean_stop = request.config.getvalue("live_server_clean_stop")
159-
server = LiveServer(app, host, port, clean_stop)
204+
server = LiveServer(app, host, port, wait, clean_stop)
160205
if request.config.getvalue("start_live_server"):
161206
server.start()
162207

pytest_flask/plugin.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ def pytest_addoption(parser):
165165
dest="start_live_server",
166166
help="don't start server automatically when live_server " "fixture is applied.",
167167
)
168+
group.addoption(
169+
"--live-server-wait",
170+
action="store",
171+
dest="live_server_wait",
172+
default=5,
173+
type=float,
174+
help="the timeout after which test case is aborted if live server is "
175+
" not started.",
176+
)
168177
group.addoption(
169178
"--live-server-clean-stop",
170179
action="store_true",

tests/test_live_server.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111

1212
class TestLiveServer:
13+
def test_init(self, live_server):
14+
assert live_server.port
15+
assert live_server.host == "localhost"
16+
1317
def test_server_is_alive(self, live_server):
1418
assert live_server._process
1519
assert live_server._process.is_alive()
@@ -18,7 +22,12 @@ def test_server_url(self, live_server):
1822
assert live_server.url() == "http://localhost:%d" % live_server.port
1923
assert live_server.url("/ping") == "http://localhost:%d/ping" % live_server.port
2024

25+
def test_server_url_is_deprecated(self, live_server):
26+
assert pytest.deprecated_call(live_server.url)
27+
2128
def test_server_listening(self, live_server):
29+
# need to test both external and external? why external here?
30+
# res = urlopen(url_for('ping', _external=True))
2231
res = urlopen(live_server.url("/ping"))
2332
assert res.code == 200
2433
assert b"pong" in res.read()
@@ -207,3 +216,16 @@ def test_port(live_server):
207216
result = appdir.runpytest("-v", "--live-server-host", str(host))
208217
result.stdout.fnmatch_lines(["*PASSED*"])
209218
assert result.ret == 0
219+
220+
def test_respect_wait_timeout(self, appdir):
221+
appdir.create_test_module(
222+
"""
223+
import pytest
224+
225+
def test_should_fail(live_server):
226+
assert live_server._process.is_alive()
227+
"""
228+
)
229+
result = appdir.runpytest("-v", "--live-server-wait=0.00000001")
230+
result.stdout.fnmatch_lines(["**ERROR**"])
231+
assert result.ret == 1

0 commit comments

Comments
 (0)