Skip to content

Commit fb04706

Browse files
Kumar Pallavkrisctl
authored andcommitted
Removes upperbound limitation on pytest-asyncio dependency version.
1 parent 03a10f0 commit fb04706

File tree

8 files changed

+124
-69
lines changed

8 files changed

+124
-69
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ test = [
6262
"psutil",
6363
"urllib3",
6464
"pytest-playwright",
65-
"pytest-asyncio==0.24.0",
65+
"pytest-asyncio",
6666
]
6767
dev = [
6868
"aiohttp-devtools",

tests/integration/integration_tests_with_license/test_http_end_points.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,19 @@ class RealMATLABServer:
170170
Setting up the server in the context of Pytest.
171171
"""
172172

173-
def __init__(self, event_loop):
174-
self.event_loop = event_loop
175-
176-
def __enter__(self):
173+
def __init__(self):
174+
self.proc = None
175+
self.dpipe = None
176+
self.mwi_app_port = None
177+
self.matlab_config_file_path = None
178+
self.temp_dir_path = None
179+
self.temp_dir_name = None
180+
self.mwi_base_url = None
181+
self.headers = None
182+
self.connection_scheme = None
183+
self.url = None
184+
185+
async def __aenter__(self):
177186
# Store the matlab proxy logs in os.pipe for testing
178187
# os.pipe2 is only supported in Linux systems
179188
_logger.info("Setting up MATLAB Server for integration test")
@@ -196,8 +205,8 @@ def __enter__(self):
196205
"MWI_BASE_URL": self.mwi_base_url,
197206
}
198207

199-
self.proc = self.event_loop.run_until_complete(
200-
utils.start_matlab_proxy_app(out=self.dpipe[1], input_env=input_env)
208+
self.proc = await utils.start_matlab_proxy_app(
209+
out=self.dpipe[1], input_env=input_env
201210
)
202211

203212
utils.wait_server_info_ready(self.mwi_app_port)
@@ -237,28 +246,23 @@ async def _terminate_process(self, timeout=0):
237246
await process.wait()
238247
_logger.debug("Killed the MATLAB process after timeout.")
239248

240-
def __exit__(self, exc_type, exc_value, exc_traceback):
249+
async def __aexit__(self, exc_type, exc_value, exc_traceback):
241250
_logger.info("Tearing down the MATLAB Server.")
242-
self.event_loop.run_until_complete(self._terminate_process(timeout=10))
251+
await self._terminate_process(timeout=10)
243252
_logger.debug("Terminated the MATLAB process.")
244253

245254

246255
# Fixtures
247256
@pytest.fixture
248-
def matlab_proxy_app_fixture(
249-
event_loop,
250-
):
257+
async def matlab_proxy_app_fixture():
251258
"""A pytest fixture which yields a real matlab server to be used by tests.
252259
253-
Args:
254-
event_loop (Event loop): The built-in event loop provided by pytest.
255-
256260
Yields:
257261
real_matlab_server : A real matlab web server used by tests.
258262
"""
259263

260264
try:
261-
with RealMATLABServer(event_loop) as matlab_proxy_app:
265+
async with RealMATLABServer() as matlab_proxy_app:
262266
yield matlab_proxy_app
263267
except ProcessLookupError as e:
264268
_logger.debug("ProcessLookupError found in matlab proxy app fixture")

tests/unit/test_app.py

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_configure_no_proxy_in_env(monkeypatch, no_proxy_user_configuration):
6868
)
6969

7070

71-
def test_create_app(event_loop):
71+
async def test_create_app():
7272
"""Test if aiohttp server is being created successfully.
7373
7474
Checks if the aiohttp server is created successfully, routes, startup and cleanup
@@ -83,7 +83,7 @@ def test_create_app(event_loop):
8383
# By default there is 1 for clean up task
8484
assert len(test_server.on_shutdown) == 1
8585
assert len(test_server.on_cleanup) == 1
86-
event_loop.run_until_complete(test_server["state"].stop_server_tasks())
86+
await test_server["state"].stop_server_tasks()
8787

8888

8989
def get_email():
@@ -231,6 +231,60 @@ def test_marshal_error(actual_error, expected_error):
231231
assert app.marshal_error(actual_error) == expected_error
232232

233233

234+
async def async_configure_and_start(app_instance):
235+
"""Async version of app.configure_and_start
236+
This async version is necessary for pytest testing because:
237+
1. In the test environment, we're already running inside an event loop managed by pytest-aiohttp
238+
2. The app.py configure_and_start uses loop.run_until_complete() which would block the test event loop
239+
3. Using 'await' instead allows the test event loop to continue processing other tasks
240+
"""
241+
# Get web logger
242+
web_logger = None if not mwi_env.is_web_logging_enabled() else app.logger
243+
244+
# Setup the session storage,
245+
# Uniqified per session to prevent multiple proxy servers on the same FQDN from interfering with each other.
246+
uniqify_session_cookie = app.secrets.token_hex()
247+
fernet_key = app.fernet.Fernet.generate_key()
248+
f = app.fernet.Fernet(fernet_key)
249+
app.aiohttp_session_setup(
250+
app_instance,
251+
app.EncryptedCookieStorage(
252+
f, cookie_name="matlab-proxy-session-" + uniqify_session_cookie
253+
),
254+
)
255+
256+
# Setup runner
257+
runner = app.web.AppRunner(app_instance, access_log=web_logger)
258+
259+
await runner.setup()
260+
261+
# Prepare site to start, then set port of the app.
262+
site = app.util.prepare_site(app_instance, runner)
263+
264+
# This would be required when MWI_APP_PORT env variable is not set and the site starts on a random port.
265+
app_instance["settings"]["app_port"] = site._port
266+
267+
# Update the site origin in settings.
268+
# The origin will be used for communicating with the Embedded connector.
269+
app_instance["settings"]["mwi_server_url"] = app.util.get_access_url(app_instance)
270+
await site.start()
271+
272+
app.logger.debug("Starting MATLAB proxy app")
273+
app.logger.debug(
274+
f" with base_url: {app_instance['settings']['base_url']} and app_port:{app_instance['settings']['app_port']}."
275+
)
276+
277+
app_instance["state"].create_server_info_file()
278+
279+
# Startup tasks are being done here as app.on_startup leads
280+
# to a race condition for mwi_server_url information which is
281+
# extracted from the site info.
282+
# Use await instead of run_until_complete
283+
await app.start_background_tasks(app_instance)
284+
285+
return app_instance
286+
287+
234288
class FakeServer:
235289
"""Context Manager class which returns a web server wrapped in aiohttp_client pytest fixture
236290
for testing.
@@ -239,18 +293,20 @@ class FakeServer:
239293
Setting up the server in the context of Pytest.
240294
"""
241295

242-
def __init__(self, event_loop, aiohttp_client):
243-
self.loop = event_loop
244-
self.aiohttp_client = aiohttp_client
296+
def __init__(self, aiohttp_client):
297+
self.aiohttp_client = aiohttp_client # This is the pytest fixture (factory function) that is passed to the constructor
298+
self.server = None # This will hold the created server instance
299+
self.client = None # This will hold the actual client instance created by calling the factory function with our server
245300

246-
def __enter__(self):
247-
server = app.create_app()
248-
self.server = app.configure_and_start(server)
249-
return self.loop.run_until_complete(self.aiohttp_client(self.server))
301+
async def __aenter__(self):
302+
self.server = app.create_app()
303+
self.server = await async_configure_and_start(self.server)
304+
self.client = await self.aiohttp_client(self.server)
305+
return self.client
250306

251-
def __exit__(self, exc_type, exc_value, exc_traceback):
252-
self.loop.run_until_complete(self.server.shutdown())
253-
self.loop.run_until_complete(self.server.cleanup())
307+
async def __aexit__(self, exc_type, exc_value, exc_traceback):
308+
await self.server.shutdown()
309+
await self.server.cleanup()
254310

255311

256312
@pytest.fixture
@@ -281,12 +337,11 @@ def mock_messages(mocker):
281337
]
282338

283339

284-
@pytest.fixture(name="test_server")
285-
def test_server_fixture(event_loop, aiohttp_client, monkeypatch, request):
340+
@pytest.fixture
341+
async def test_server(aiohttp_client, monkeypatch, request):
286342
"""A pytest fixture which yields a test server to be used by tests.
287343
288344
Args:
289-
loop (Event loop): The built-in event loop provided by pytest.
290345
aiohttp_client (aiohttp_client): Built-in pytest fixture used as a wrapper to the aiohttp web server.
291346
292347
Yields:
@@ -305,7 +360,7 @@ def test_server_fixture(event_loop, aiohttp_client, monkeypatch, request):
305360
monkeypatch.setenv(env_var_name, env_var_value)
306361

307362
try:
308-
with FakeServer(event_loop, aiohttp_client) as test_server:
363+
async with FakeServer(aiohttp_client) as test_server:
309364
yield test_server
310365

311366
except ProcessLookupError:

tests/unit/test_app_state.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ def sample_settings_fixture(tmp_path):
5656

5757

5858
@pytest.fixture
59-
def app_state_fixture(sample_settings_fixture, event_loop):
59+
async def app_state_fixture(sample_settings_fixture):
6060
"""A pytest fixture which returns an instance of AppState class with no errors.
6161
6262
Args:
6363
sample_settings_fixture (dict): A dictionary of sample settings to be used by
64-
event_loop : A pytest builtin fixture
6564
6665
Returns:
6766
AppState: An object of the AppState class
@@ -72,7 +71,7 @@ def app_state_fixture(sample_settings_fixture, event_loop):
7271

7372
yield app_state
7473

75-
event_loop.run_until_complete(app_state.stop_server_tasks())
74+
await app_state.stop_server_tasks()
7675

7776

7877
@pytest.fixture
@@ -109,13 +108,12 @@ def app_state_with_token_auth_fixture(
109108

110109

111110
@pytest.fixture
112-
def mocker_os_patching_fixture(mocker, platform, event_loop):
111+
async def mocker_os_patching_fixture(mocker, platform):
113112
"""A pytest fixture which patches the is_* functions in system.py module
114113
115114
Args:
116115
mocker : Built in pytest fixture
117116
platform (str): A string representing "windows", "linux" or "mac"
118-
event_loop : A pytest builtin fixture
119117
120118
Returns:
121119
mocker: Built in pytest fixture with patched calls to system.py module.
@@ -124,7 +122,6 @@ def mocker_os_patching_fixture(mocker, platform, event_loop):
124122
mocker.patch("matlab_proxy.app_state.system.is_windows", return_value=False)
125123
mocker.patch("matlab_proxy.app_state.system.is_mac", return_value=False)
126124
mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=False)
127-
mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=event_loop)
128125

129126
if platform == "linux":
130127
mocker.patch("matlab_proxy.app_state.system.is_linux", return_value=True)

tests/unit/test_non_dev_mode.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,8 @@ def mock_settings_get_fixture(mocker):
124124
)
125125

126126

127-
@pytest.fixture(name="test_server")
128-
def test_server_fixture(
129-
event_loop,
127+
@pytest.fixture
128+
async def test_server(
130129
aiohttp_client,
131130
build_frontend,
132131
matlab_port_setup,
@@ -140,7 +139,6 @@ def test_server_fixture(
140139
This fixture 'initializes' the test server with different constraints from the test server in test_app.py
141140
142141
Args:
143-
event_loop : Event loop
144142
aiohttp_client : A built-in pytest fixture
145143
build_frontend: Pytest fixture which generates the directory structure of static files with some placeholder content
146144
matlab_port_setup: Pytest fixture which monkeypatches 'MWI_DEV' env to False. This is required for the test_server to add static content
@@ -150,7 +148,7 @@ def test_server_fixture(
150148
[aiohttp_client]: A aiohttp_client to send HTTP requests.
151149
"""
152150

153-
with FakeServer(event_loop, aiohttp_client) as test_server:
151+
async with FakeServer(aiohttp_client) as test_server:
154152
yield test_server
155153

156154

tests/unit/util/mwi/test_token_auth.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ async def fake_endpoint(request):
8989

9090

9191
@pytest.fixture
92-
def fake_server_with_auth_enabled(
93-
event_loop, aiohttp_client, monkeypatch, get_custom_auth_token_str
92+
async def fake_server_with_auth_enabled(
93+
aiohttp_client, monkeypatch, get_custom_auth_token_str
9494
):
9595
auth_token = get_custom_auth_token_str
9696
auth_enablement = "True"
@@ -120,7 +120,7 @@ def fake_server_with_auth_enabled(
120120
aiohttp_session_setup(
121121
app, EncryptedCookieStorage(f, cookie_name="matlab-proxy-session")
122122
)
123-
return event_loop.run_until_complete(aiohttp_client(app))
123+
return await aiohttp_client(app)
124124

125125

126126
async def test_set_value_with_token(
@@ -256,7 +256,7 @@ async def test_get_value_with_token_in_query_params(
256256

257257

258258
@pytest.fixture
259-
def fake_server_without_auth_enabled(event_loop, aiohttp_client, monkeypatch):
259+
async def fake_server_without_auth_enabled(aiohttp_client, monkeypatch):
260260
auth_enablement = "False"
261261
monkeypatch.setenv(
262262
mwi_env.get_env_name_enable_mwi_auth_token(), str(auth_enablement)
@@ -281,7 +281,7 @@ def fake_server_without_auth_enabled(event_loop, aiohttp_client, monkeypatch):
281281
aiohttp_session_setup(
282282
app, EncryptedCookieStorage(f, cookie_name="matlab-proxy-session")
283283
)
284-
return event_loop.run_until_complete(aiohttp_client(app))
284+
return await aiohttp_client(app)
285285

286286

287287
async def test_get_value(fake_server_without_auth_enabled):

tests/unit/util/test_mw.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ def test_range_matlab_connector_ports():
519519
not system.is_linux(),
520520
reason="Xvfb is only required on linux based operating systems",
521521
)
522-
async def test_create_xvfb_process(event_loop):
522+
async def test_create_xvfb_process():
523523
"""Test to check if more than 1 xvfb process can be created with -displayfd flag
524524
525525
Creates 2 xvfb processes with '-displayfd' flag and checks if the processes are

tests/unit/util/test_util.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,27 @@ def test_get_supported_termination_signals():
1919
assert len(system.get_supported_termination_signals()) >= 1
2020

2121

22-
def test_add_signal_handlers(event_loop: asyncio.AbstractEventLoop):
23-
"""Test to check if signal handlers are being added to asyncio event_loop
24-
25-
Args:
26-
event_loop (asyncio event loop): built-in pytest fixture.
27-
"""
28-
29-
event_loop = add_signal_handlers(event_loop)
30-
31-
# In posix systems, event loop is modified with new signal handlers
32-
if system.is_posix():
33-
assert event_loop._signal_handlers is not None
34-
assert event_loop._signal_handlers.items() is not None
35-
36-
else:
37-
import signal
38-
39-
# In a windows system, the signal handlers are added to the 'signal' package.
40-
for interrupt_signal in system.get_supported_termination_signals():
41-
assert signal.getsignal(interrupt_signal) is not None
22+
def test_add_signal_handlers():
23+
"""Test to check if signal handlers are being added to asyncio event_loop"""
24+
25+
test_loop = asyncio.new_event_loop()
26+
test_loop = add_signal_handlers(test_loop)
27+
try:
28+
# In posix systems, event loop is modified with new signal handlers
29+
if system.is_posix():
30+
assert test_loop._signal_handlers is not None
31+
# Check that the signal handlers dictionary is not empty
32+
assert len(test_loop._signal_handlers) > 0
33+
34+
else:
35+
import signal
36+
37+
# In a windows system, the signal handlers are added to the 'signal' package.
38+
for interrupt_signal in system.get_supported_termination_signals():
39+
assert signal.getsignal(interrupt_signal) is not None
40+
41+
finally:
42+
test_loop.close()
4243

4344

4445
def test_get_child_processes_no_children_initially(mocker):

0 commit comments

Comments
 (0)