matlab-proxy 0.26.0__py3-none-any.whl → 0.27.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of matlab-proxy might be problematic. Click here for more details.
- matlab_proxy/app.py +13 -15
- matlab_proxy/app_state.py +8 -2
- matlab_proxy/default_configuration.py +2 -2
- matlab_proxy/gui/index.html +1 -1
- matlab_proxy/gui/static/js/{index.CZgGkMCD.js → index.BcDShXfH.js} +16 -16
- matlab_proxy/settings.py +3 -2
- matlab_proxy/util/list_servers.py +2 -2
- matlab_proxy/util/mwi/environment_variables.py +5 -0
- matlab_proxy/util/mwi/session_name.py +28 -0
- matlab_proxy/util/mwi/validators.py +2 -4
- {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.0.dist-info}/METADATA +37 -25
- {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.0.dist-info}/RECORD +19 -49
- {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.0.dist-info}/WHEEL +1 -2
- matlab_proxy_manager/README.md +85 -0
- matlab_proxy_manager/lib/README.md +53 -0
- matlab_proxy_manager/storage/README.md +54 -0
- matlab_proxy_manager/web/README.md +37 -0
- matlab_proxy-0.26.0.dist-info/top_level.txt +0 -3
- tests/integration/__init__.py +0 -1
- tests/integration/integration_tests_with_license/__init__.py +0 -1
- tests/integration/integration_tests_with_license/conftest.py +0 -47
- tests/integration/integration_tests_with_license/test_http_end_points.py +0 -397
- tests/integration/integration_tests_without_license/__init__.py +0 -1
- tests/integration/integration_tests_without_license/conftest.py +0 -116
- tests/integration/integration_tests_without_license/test_matlab_is_down_if_unlicensed.py +0 -49
- tests/integration/utils/__init__.py +0 -1
- tests/integration/utils/integration_tests_utils.py +0 -352
- tests/integration/utils/licensing.py +0 -152
- tests/unit/__init__.py +0 -1
- tests/unit/conftest.py +0 -66
- tests/unit/test_app.py +0 -1299
- tests/unit/test_app_state.py +0 -1094
- tests/unit/test_constants.py +0 -7
- tests/unit/test_ddux.py +0 -22
- tests/unit/test_devel.py +0 -246
- tests/unit/test_non_dev_mode.py +0 -169
- tests/unit/test_settings.py +0 -679
- tests/unit/util/__init__.py +0 -3
- tests/unit/util/mwi/__init__.py +0 -1
- tests/unit/util/mwi/embedded_connector/__init__.py +0 -1
- tests/unit/util/mwi/embedded_connector/test_helpers.py +0 -29
- tests/unit/util/mwi/embedded_connector/test_request.py +0 -64
- tests/unit/util/mwi/test_custom_http_headers.py +0 -281
- tests/unit/util/mwi/test_download.py +0 -152
- tests/unit/util/mwi/test_logger.py +0 -82
- tests/unit/util/mwi/test_token_auth.py +0 -303
- tests/unit/util/mwi/test_validators.py +0 -364
- tests/unit/util/test_cookie_jar.py +0 -252
- tests/unit/util/test_mw.py +0 -550
- tests/unit/util/test_util.py +0 -221
- tests/utils/__init__.py +0 -1
- tests/utils/logging_util.py +0 -81
- {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.0.dist-info}/entry_points.txt +0 -0
- {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.0.dist-info/licenses}/LICENSE.md +0 -0
tests/unit/test_app_state.py
DELETED
|
@@ -1,1094 +0,0 @@
|
|
|
1
|
-
# Copyright 2023-2025 The MathWorks, Inc.
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
|
-
|
|
10
|
-
import pytest
|
|
11
|
-
from matlab_proxy import settings
|
|
12
|
-
|
|
13
|
-
from matlab_proxy import settings
|
|
14
|
-
from matlab_proxy.app_state import AppState
|
|
15
|
-
from matlab_proxy.constants import MWI_AUTH_TOKEN_NAME_FOR_HTTP
|
|
16
|
-
from matlab_proxy.util.mwi.exceptions import (
|
|
17
|
-
LicensingError,
|
|
18
|
-
MatlabError,
|
|
19
|
-
MatlabInstallError,
|
|
20
|
-
)
|
|
21
|
-
from matlab_proxy.constants import (
|
|
22
|
-
CONNECTOR_SECUREPORT_FILENAME,
|
|
23
|
-
USER_CODE_OUTPUT_FILE_NAME,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
from tests.unit.util import MockResponse
|
|
27
|
-
from tests.unit.test_constants import CHECK_MATLAB_STATUS_INTERVAL, FIVE_MAX_TRIES
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@pytest.fixture
|
|
31
|
-
def sample_settings_fixture(tmp_path):
|
|
32
|
-
"""A pytest fixture which returns a dict containing sample settings for the AppState class.
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
tmp_path : Builtin pytest fixture
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
dict: A dictionary of sample settings
|
|
39
|
-
"""
|
|
40
|
-
tmp_file = tmp_path / "parent_1" / "parent_2" / "tmp_file.json"
|
|
41
|
-
return {
|
|
42
|
-
"error": None,
|
|
43
|
-
"warnings": [],
|
|
44
|
-
"matlab_config_file": tmp_file,
|
|
45
|
-
"is_xvfb_available": True,
|
|
46
|
-
"is_windowmanager_available": True,
|
|
47
|
-
"mwi_server_url": "dummy",
|
|
48
|
-
"mwi_logs_root_dir": Path(settings.get_mwi_config_folder(dev=True)),
|
|
49
|
-
"app_port": 12345,
|
|
50
|
-
"mwapikey": "asdf",
|
|
51
|
-
"has_custom_code_to_execute": False,
|
|
52
|
-
"mwi_idle_timeout": 100,
|
|
53
|
-
"mwi_is_token_auth_enabled": False,
|
|
54
|
-
"integration_name": "MATLAB Desktop",
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@pytest.fixture
|
|
59
|
-
def app_state_fixture(sample_settings_fixture, event_loop):
|
|
60
|
-
"""A pytest fixture which returns an instance of AppState class with no errors.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
sample_settings_fixture (dict): A dictionary of sample settings to be used by
|
|
64
|
-
event_loop : A pytest builtin fixture
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
AppState: An object of the AppState class
|
|
68
|
-
"""
|
|
69
|
-
app_state = AppState(settings=sample_settings_fixture)
|
|
70
|
-
app_state.processes = {"matlab": None, "xvfb": None}
|
|
71
|
-
app_state.licensing = {"type": "existing_license"}
|
|
72
|
-
|
|
73
|
-
yield app_state
|
|
74
|
-
|
|
75
|
-
event_loop.run_until_complete(app_state.stop_server_tasks())
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@pytest.fixture
|
|
79
|
-
def sample_token_headers_fixture():
|
|
80
|
-
return {MWI_AUTH_TOKEN_NAME_FOR_HTTP: "asdf"}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@pytest.fixture
|
|
84
|
-
def app_state_with_token_auth_fixture(
|
|
85
|
-
app_state_fixture, sample_token_headers_fixture, tmp_path
|
|
86
|
-
):
|
|
87
|
-
"""Pytest fixture which returns AppState instance with token authentication enabled.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
app_state_fixture (AppState): Pytest fixture
|
|
91
|
-
tmp_path (str): Built-in pytest fixture
|
|
92
|
-
|
|
93
|
-
Returns:
|
|
94
|
-
(AppState, dict): Instance of the AppState class with token authentication enabled and token headers
|
|
95
|
-
"""
|
|
96
|
-
tmp_matlab_ready_file = Path(tmp_path) / "tmp_file.txt"
|
|
97
|
-
tmp_matlab_ready_file.touch()
|
|
98
|
-
((mwi_auth_token_name, mwi_auth_token_hash),) = sample_token_headers_fixture.items()
|
|
99
|
-
app_state_fixture.matlab_session_files["matlab_ready_file"] = tmp_matlab_ready_file
|
|
100
|
-
app_state_fixture.settings["mwi_is_token_auth_enabled"] = True
|
|
101
|
-
app_state_fixture.settings["mwi_auth_token_name_for_env"] = mwi_auth_token_name
|
|
102
|
-
app_state_fixture.settings["mwi_auth_token_name_for_http"] = (
|
|
103
|
-
MWI_AUTH_TOKEN_NAME_FOR_HTTP
|
|
104
|
-
)
|
|
105
|
-
app_state_fixture.settings["mwi_auth_token_hash"] = mwi_auth_token_hash
|
|
106
|
-
app_state_fixture.settings["mwi_server_url"] = "http://localhost:8888"
|
|
107
|
-
|
|
108
|
-
return app_state_fixture
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@pytest.fixture
|
|
112
|
-
def mocker_os_patching_fixture(mocker, platform, event_loop):
|
|
113
|
-
"""A pytest fixture which patches the is_* functions in system.py module
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
mocker : Built in pytest fixture
|
|
117
|
-
platform (str): A string representing "windows", "linux" or "mac"
|
|
118
|
-
event_loop : A pytest builtin fixture
|
|
119
|
-
|
|
120
|
-
Returns:
|
|
121
|
-
mocker: Built in pytest fixture with patched calls to system.py module.
|
|
122
|
-
"""
|
|
123
|
-
mocker.patch("matlab_proxy.app_state.system.is_linux", return_value=False)
|
|
124
|
-
mocker.patch("matlab_proxy.app_state.system.is_windows", return_value=False)
|
|
125
|
-
mocker.patch("matlab_proxy.app_state.system.is_mac", return_value=False)
|
|
126
|
-
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)
|
|
128
|
-
|
|
129
|
-
if platform == "linux":
|
|
130
|
-
mocker.patch("matlab_proxy.app_state.system.is_linux", return_value=True)
|
|
131
|
-
mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=True)
|
|
132
|
-
|
|
133
|
-
elif platform == "windows":
|
|
134
|
-
mocker.patch("matlab_proxy.app_state.system.is_windows", return_value=True)
|
|
135
|
-
mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=False)
|
|
136
|
-
|
|
137
|
-
else:
|
|
138
|
-
mocker.patch("matlab_proxy.app_state.system.is_mac", return_value=True)
|
|
139
|
-
mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=True)
|
|
140
|
-
|
|
141
|
-
return mocker
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
@dataclass(frozen=True)
|
|
145
|
-
class Mock_xvfb:
|
|
146
|
-
"""An immutable dataclass representing a mocked Xvfb process"""
|
|
147
|
-
|
|
148
|
-
returncode: Optional[int]
|
|
149
|
-
pid: Optional[int]
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@dataclass(frozen=True)
|
|
153
|
-
class Mock_matlab:
|
|
154
|
-
"""An immutable dataclass representing a mocked MATLAB process"""
|
|
155
|
-
|
|
156
|
-
returncode: Optional[int]
|
|
157
|
-
pid: Optional[int]
|
|
158
|
-
|
|
159
|
-
def is_running(self) -> bool:
|
|
160
|
-
return self.returncode is None
|
|
161
|
-
|
|
162
|
-
def wait(self) -> int:
|
|
163
|
-
return self.returncode
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
@pytest.mark.parametrize(
|
|
167
|
-
"licensing, expected",
|
|
168
|
-
[
|
|
169
|
-
(None, False),
|
|
170
|
-
({"type": "nlm", "conn_str": "123@host"}, True),
|
|
171
|
-
({"type": "nlm"}, False),
|
|
172
|
-
({"type": "mhlm", "identity_token": "random_token"}, False),
|
|
173
|
-
(
|
|
174
|
-
{
|
|
175
|
-
"type": "mhlm",
|
|
176
|
-
"identity_token": "random_token",
|
|
177
|
-
"source_id": "dummy_id",
|
|
178
|
-
"expiry": "Jan 1, 1970",
|
|
179
|
-
"entitlement_id": "123456",
|
|
180
|
-
},
|
|
181
|
-
True,
|
|
182
|
-
),
|
|
183
|
-
({"type": "existing_license"}, True),
|
|
184
|
-
({"type": "invalid_type"}, False),
|
|
185
|
-
],
|
|
186
|
-
ids=[
|
|
187
|
-
"None licensing",
|
|
188
|
-
"happy path-nlm",
|
|
189
|
-
"incomplete nlm data",
|
|
190
|
-
"incomplete mhlm data",
|
|
191
|
-
"happy path-mhlm",
|
|
192
|
-
"happy path-existing license",
|
|
193
|
-
"invalid license",
|
|
194
|
-
],
|
|
195
|
-
)
|
|
196
|
-
def test_is_licensed(app_state_fixture, licensing, expected):
|
|
197
|
-
"""Test to check is_licensed()
|
|
198
|
-
|
|
199
|
-
Args:
|
|
200
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
201
|
-
licensing (dict): Represents licensing information
|
|
202
|
-
expected (bool): Expected return value.
|
|
203
|
-
"""
|
|
204
|
-
# Arrange
|
|
205
|
-
# Nothing to arrange
|
|
206
|
-
|
|
207
|
-
# Act
|
|
208
|
-
app_state_fixture.licensing = licensing
|
|
209
|
-
|
|
210
|
-
# Assert
|
|
211
|
-
assert app_state_fixture.is_licensed() == expected
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@pytest.mark.parametrize(
|
|
215
|
-
"err, expected_err",
|
|
216
|
-
[
|
|
217
|
-
(MatlabError(message="dummy error"), MatlabError(message="dummy")),
|
|
218
|
-
(LicensingError(message="license issue"), None),
|
|
219
|
-
],
|
|
220
|
-
ids=["Any error except licensing error", "licensing error"],
|
|
221
|
-
)
|
|
222
|
-
def test_unset_licensing(err, app_state_fixture, expected_err):
|
|
223
|
-
"""Test to check unset_liecnsing removes licensing from the AppState object
|
|
224
|
-
|
|
225
|
-
Args:
|
|
226
|
-
err (Exception): Custom exceptions defined in exceptions.py
|
|
227
|
-
licensing (bool): Whether licensing info is removed
|
|
228
|
-
expected_err (Exception): Expected exception
|
|
229
|
-
"""
|
|
230
|
-
# Arrange
|
|
231
|
-
app_state_fixture.error = err
|
|
232
|
-
|
|
233
|
-
# Act
|
|
234
|
-
app_state_fixture.unset_licensing()
|
|
235
|
-
|
|
236
|
-
# Assert
|
|
237
|
-
assert app_state_fixture.licensing == None
|
|
238
|
-
assert type(app_state_fixture.error) is type(expected_err)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# config file is deleted when licensing info is not set i.e. set to None
|
|
242
|
-
def test_persist_licensing_when_licensing_info_is_not_set(app_state_fixture):
|
|
243
|
-
"""Test to check if data is not persisted to a file if licensing info is not present
|
|
244
|
-
|
|
245
|
-
Args:
|
|
246
|
-
tmp_path (Path): Built in pytest fixture
|
|
247
|
-
"""
|
|
248
|
-
# Arrange
|
|
249
|
-
# Nothing to arrange
|
|
250
|
-
app_state_fixture.licensing = None
|
|
251
|
-
|
|
252
|
-
# Act
|
|
253
|
-
app_state_fixture.persist_config_data()
|
|
254
|
-
|
|
255
|
-
# Assert
|
|
256
|
-
assert os.path.exists(app_state_fixture.settings["matlab_config_file"]) is False
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
@pytest.mark.parametrize(
|
|
260
|
-
"licensing_data",
|
|
261
|
-
[
|
|
262
|
-
({"type": "nlm", "conn_str": "123@host"}),
|
|
263
|
-
(
|
|
264
|
-
{
|
|
265
|
-
"type": "mhlm",
|
|
266
|
-
"identity_token": "random_token",
|
|
267
|
-
"source_id": "dummy_id",
|
|
268
|
-
"expiry": "Jan 1, 1970",
|
|
269
|
-
"entitlement_id": "123456",
|
|
270
|
-
}
|
|
271
|
-
),
|
|
272
|
-
({"type": "existing_license"}),
|
|
273
|
-
],
|
|
274
|
-
ids=["nlm type", "mhlm type", "existing license type"],
|
|
275
|
-
)
|
|
276
|
-
def test_persist_config_data(licensing_data: dict, tmp_path):
|
|
277
|
-
"""Test to check if persist_licensing() writes data to the file system
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
data (dict): Represents matlab-proxy licensing data
|
|
281
|
-
tmp_path : Built-in pytest fixture.
|
|
282
|
-
"""
|
|
283
|
-
# Arrange
|
|
284
|
-
tmp_file = tmp_path / "parent_1" / "parent_2" / "tmp_file.json"
|
|
285
|
-
settings = {
|
|
286
|
-
"matlab_config_file": tmp_file,
|
|
287
|
-
"error": None,
|
|
288
|
-
"matlab_version": None,
|
|
289
|
-
"warnings": [],
|
|
290
|
-
"mwi_idle_timeout": None,
|
|
291
|
-
}
|
|
292
|
-
app_state = AppState(settings=settings)
|
|
293
|
-
app_state.licensing = licensing_data
|
|
294
|
-
|
|
295
|
-
cached_data = {"licensing": licensing_data, "matlab": {"version": None}}
|
|
296
|
-
|
|
297
|
-
# Act
|
|
298
|
-
app_state.persist_config_data()
|
|
299
|
-
with open(tmp_file, "r") as file:
|
|
300
|
-
got = file.read()
|
|
301
|
-
|
|
302
|
-
# Assert
|
|
303
|
-
assert json.loads(got) == cached_data
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
validate_required_processes_test_data = [
|
|
307
|
-
(None, None, "linux", False), # xvfb is None == True
|
|
308
|
-
(None, Mock_xvfb(None, 1), "linux", False), # matlab is None == True
|
|
309
|
-
(
|
|
310
|
-
Mock_matlab(None, 1),
|
|
311
|
-
Mock_xvfb(None, 1),
|
|
312
|
-
"linux",
|
|
313
|
-
True,
|
|
314
|
-
), # All branches are skipped and nothing returned
|
|
315
|
-
(
|
|
316
|
-
Mock_matlab(None, 1),
|
|
317
|
-
Mock_xvfb(123, 2),
|
|
318
|
-
"linux",
|
|
319
|
-
False,
|
|
320
|
-
), # xvfb.returncode is not None == True
|
|
321
|
-
(
|
|
322
|
-
Mock_matlab(123, 1),
|
|
323
|
-
Mock_xvfb(None, 2),
|
|
324
|
-
"linux",
|
|
325
|
-
False,
|
|
326
|
-
), # matlab.returncode is not None == True
|
|
327
|
-
(
|
|
328
|
-
Mock_matlab(None, 1),
|
|
329
|
-
None,
|
|
330
|
-
"linux",
|
|
331
|
-
True,
|
|
332
|
-
), # Xvfb not found on path
|
|
333
|
-
]
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
@pytest.mark.parametrize(
|
|
337
|
-
"matlab, xvfb, platform, expected",
|
|
338
|
-
validate_required_processes_test_data,
|
|
339
|
-
ids=[
|
|
340
|
-
"processes_not_running",
|
|
341
|
-
"matlab_not_running",
|
|
342
|
-
"All_required_processes_running",
|
|
343
|
-
"All_processes_running_with_xvfb_returning_non_zero_code",
|
|
344
|
-
"All_processes_running_with_matlab_returning_non_zero_code",
|
|
345
|
-
"xvfb_is_optional_matlab_starts_without_it",
|
|
346
|
-
],
|
|
347
|
-
)
|
|
348
|
-
def test_are_required_processes_ready(
|
|
349
|
-
app_state_fixture, mocker_os_patching_fixture, matlab, xvfb, expected
|
|
350
|
-
):
|
|
351
|
-
"""Test to check if required processes are ready
|
|
352
|
-
|
|
353
|
-
Args:
|
|
354
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
355
|
-
mocker_os_patching_fixture (mocker): Custom pytest fixture for mocking
|
|
356
|
-
matlab (Mock_matlab): Represents a mocked MATLAB process
|
|
357
|
-
xvfb (Mock_xvfb): Represents a mocked Xvfb process
|
|
358
|
-
expected (bool): Expected return value based on process return code
|
|
359
|
-
"""
|
|
360
|
-
# Arrange
|
|
361
|
-
app_state_fixture.processes = {"matlab": matlab, "xvfb": xvfb}
|
|
362
|
-
if not xvfb:
|
|
363
|
-
app_state_fixture.settings["is_xvfb_available"] = False
|
|
364
|
-
|
|
365
|
-
# Act
|
|
366
|
-
actual = app_state_fixture._are_required_processes_ready()
|
|
367
|
-
|
|
368
|
-
# Assert
|
|
369
|
-
assert actual == expected
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
# The test: test_track_embedded_connector has been split into:
|
|
373
|
-
# 1) test_track_embedded_connector_posix: Test to check if stop_matlab is called on posix systems.
|
|
374
|
-
# 2) test_track_embedded_connector : Test to check if stop_matlab is not called in windows.
|
|
375
|
-
|
|
376
|
-
# In windows, errors are shown as UI windows and calling stop_matlab() if MATLAB had not started in
|
|
377
|
-
# PROCESS_TIMEOUT seconds would remove the window thereby leaving the user without knowing why MATLAB
|
|
378
|
-
# failed to start.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@pytest.mark.parametrize("platform", [("linux"), ("mac")])
|
|
382
|
-
async def test_track_embedded_connector_posix(
|
|
383
|
-
mocker_os_patching_fixture, app_state_fixture
|
|
384
|
-
):
|
|
385
|
-
"""Test to check track_embedded_connector task for posix platforms.
|
|
386
|
-
|
|
387
|
-
Checks if stop_matlab() has been called when the embedded connector doesn't respond
|
|
388
|
-
even after PROCESS_TIMEOUT seconds of starting MATLAB.
|
|
389
|
-
|
|
390
|
-
Args:
|
|
391
|
-
mocker_os_patching_fixture (mocker): Custom pytest fixture for mocking
|
|
392
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
393
|
-
"""
|
|
394
|
-
|
|
395
|
-
# Arrange
|
|
396
|
-
# Patching embedded_connector_start_time to EPOCH+1 seconds and state to be "down".
|
|
397
|
-
|
|
398
|
-
# For this test, the embedded_connector_start_time can be patched to ant value 600(default PROCESS_TIMEOUT) seconds
|
|
399
|
-
# before the current time.
|
|
400
|
-
|
|
401
|
-
# To always ensure that the time difference between the embedded_connector_start_time
|
|
402
|
-
# and the current time is greater than PROCESS_TIMEOUT, the embedded_connector_start_time is patched to
|
|
403
|
-
# EPOCH + 1 seconds so that the time_diff = current_time - embedded_connector_start_time is greater
|
|
404
|
-
# than PROCESS_TIMEOUT always evaluates to True.
|
|
405
|
-
|
|
406
|
-
mocker_os_patching_fixture.patch.object(
|
|
407
|
-
app_state_fixture, "embedded_connector_start_time", new=float(1.0)
|
|
408
|
-
)
|
|
409
|
-
mocker_os_patching_fixture.patch.object(
|
|
410
|
-
app_state_fixture, "embedded_connector_state", return_value="down"
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
# verify that stop_matlab() is called once
|
|
414
|
-
spy = mocker_os_patching_fixture.spy(app_state_fixture, "stop_matlab")
|
|
415
|
-
|
|
416
|
-
# Act
|
|
417
|
-
await app_state_fixture._AppState__track_embedded_connector_state()
|
|
418
|
-
|
|
419
|
-
# Assert
|
|
420
|
-
spy.assert_called_once()
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
@pytest.mark.parametrize("platform", ["windows"])
|
|
424
|
-
async def test_track_embedded_connector(mocker_os_patching_fixture, app_state_fixture):
|
|
425
|
-
"""Test to check track_embedded_connector task on windows.
|
|
426
|
-
|
|
427
|
-
In windows, since errors are shown in native UI windows , calling stop_matlab() would remove them,
|
|
428
|
-
thereby not knowing the error with which MATLAB failed to start.
|
|
429
|
-
|
|
430
|
-
Hence, this test checks that stop_matlab() is not called.
|
|
431
|
-
|
|
432
|
-
Args:
|
|
433
|
-
mocker_os_patching_fixture (mocker): Custom pytest fixture for mocking
|
|
434
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
435
|
-
"""
|
|
436
|
-
# Arrange
|
|
437
|
-
# Patching embedded_connector_start_time to EPOCH+1 seconds and state to be "down".
|
|
438
|
-
|
|
439
|
-
# For this test, the embedded_connector_start_time can be patched to any value 600(default PROCESS_TIMEOUT) seconds
|
|
440
|
-
# before the current time.
|
|
441
|
-
|
|
442
|
-
# To always ensure that the time difference between the embedded_connector_start_time
|
|
443
|
-
# and the current time is greater than PROCESS_TIMEOUT, the embedded_connector_start_time is patched to
|
|
444
|
-
# EPOCH + 1 seconds so that the time_diff = current_time - embedded_connector_start_time is greater
|
|
445
|
-
# than PROCESS_TIMEOUT always evaluates to True.
|
|
446
|
-
|
|
447
|
-
mocker_os_patching_fixture.patch.object(
|
|
448
|
-
app_state_fixture, "embedded_connector_start_time", new=float(1.0)
|
|
449
|
-
)
|
|
450
|
-
mocker_os_patching_fixture.patch.object(
|
|
451
|
-
app_state_fixture, "embedded_connector_state", return_value="down"
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
spy = mocker_os_patching_fixture.spy(app_state_fixture, "stop_matlab")
|
|
455
|
-
|
|
456
|
-
# Act
|
|
457
|
-
|
|
458
|
-
# Unlike the posix test (test_track_embedded_connector_posix) where the task track_embedded_connector_state()
|
|
459
|
-
# would exit automatically after stopping MATLAB, in windows, the task will never exit(until the user checks the error
|
|
460
|
-
# manually and clicks on "Stop MATLAB").
|
|
461
|
-
|
|
462
|
-
# So, the task is manually stopped by raising a timeout error(set to 3 seconds). This is a generous amount of
|
|
463
|
-
# time for the error to be set as a MatlabError in CI systems.
|
|
464
|
-
with pytest.raises(asyncio.TimeoutError):
|
|
465
|
-
await asyncio.wait_for(
|
|
466
|
-
app_state_fixture._AppState__track_embedded_connector_state(),
|
|
467
|
-
timeout=3, # timeout of 3 seconds to account for CI systems. This is to wait for the error to be set as MatlabError.
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
# Assert
|
|
471
|
-
spy.assert_not_called() # In windows, MATLAB process should not be stopped so that the UI error window is not closed.
|
|
472
|
-
assert isinstance(app_state_fixture.error, MatlabError)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
@pytest.mark.parametrize(
|
|
476
|
-
"env_var_name, filter_prefix, is_filtered",
|
|
477
|
-
[("MWI_AUTH_TOKEN", "MWI_", None), ("MWIFOO_AUTH_TOKEN", "MWI_", "foo")],
|
|
478
|
-
ids=["env_var_is_filtered", "env_var_is_not_filtered"],
|
|
479
|
-
)
|
|
480
|
-
def test_env_variables_filtration_for_xvfb_process(
|
|
481
|
-
monkeypatch, env_var_name, filter_prefix, is_filtered
|
|
482
|
-
):
|
|
483
|
-
"""Test to check if __filter_env_variables filters environment variables with a certain prefix correctly.
|
|
484
|
-
|
|
485
|
-
Args:
|
|
486
|
-
monkeypatch (Object): Built-in pytest fixture for monkeypatching
|
|
487
|
-
env_var_name (str): Name of the environment variable
|
|
488
|
-
filter_prefix (str): Prefix to check for filtering
|
|
489
|
-
is_filtered (bool): To check if the env variable with specified prefix is filtered.
|
|
490
|
-
"""
|
|
491
|
-
# Arrange
|
|
492
|
-
env_var = env_var_name
|
|
493
|
-
monkeypatch.setenv(env_var, "foo")
|
|
494
|
-
|
|
495
|
-
# Act
|
|
496
|
-
filtered_env_vars: dict = AppState._AppState__filter_env_variables(
|
|
497
|
-
os.environ, filter_prefix
|
|
498
|
-
)
|
|
499
|
-
|
|
500
|
-
# Assert
|
|
501
|
-
assert filtered_env_vars.get(env_var) == is_filtered
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
@pytest.mark.parametrize(
|
|
505
|
-
"platform, expected_output",
|
|
506
|
-
[("linux", "stdout"), ("windows", "file"), ("mac", "stdout")],
|
|
507
|
-
)
|
|
508
|
-
async def test_setup_env_for_matlab(
|
|
509
|
-
mocker_os_patching_fixture, platform, expected_output, app_state_fixture, tmp_path
|
|
510
|
-
):
|
|
511
|
-
"""Test to check MW_DIAGNOSTIC_DEST is set appropriately for posix and non-posix systems
|
|
512
|
-
|
|
513
|
-
Args:
|
|
514
|
-
mocker_os_patching_fixture (mocker): Custom pytest fixture for mocking
|
|
515
|
-
platform (str): string describing a platform
|
|
516
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
517
|
-
tmp_path (Path): Built-in pytest fixture for temporary paths
|
|
518
|
-
"""
|
|
519
|
-
|
|
520
|
-
# Arrange
|
|
521
|
-
app_state_fixture.licensing = {"type": "existing_license"}
|
|
522
|
-
app_state_fixture.settings = {"mwapikey": None, "matlab_display": ":1"}
|
|
523
|
-
app_state_fixture.mwi_logs_dir = tmp_path
|
|
524
|
-
mocker_os_patching_fixture.patch(
|
|
525
|
-
"matlab_proxy.app_state.logger.isEnabledFor", return_value=True
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
# Act
|
|
529
|
-
matlab_env = await app_state_fixture._AppState__setup_env_for_matlab()
|
|
530
|
-
|
|
531
|
-
# Assert
|
|
532
|
-
assert expected_output in matlab_env["MW_DIAGNOSTIC_DEST"]
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
async def test_requests_sent_by_matlab_proxy_have_headers(
|
|
536
|
-
app_state_with_token_auth_fixture,
|
|
537
|
-
sample_token_headers_fixture,
|
|
538
|
-
mocker,
|
|
539
|
-
):
|
|
540
|
-
"""Test to check if token headers are included in requests sent by matlab-proxy when authentication is enabled.
|
|
541
|
-
Test checks if the headers are included in the request to stop matlab and get connector status.
|
|
542
|
-
|
|
543
|
-
Args:
|
|
544
|
-
app_state_fixture_with_token_auth (AppState): Instance of AppState class with token authentication enabled
|
|
545
|
-
sample_token_headers_fixture (dict): Dict which represents the token headers
|
|
546
|
-
mocker : Built-in pytest fixture
|
|
547
|
-
"""
|
|
548
|
-
# Arrange
|
|
549
|
-
mock_resp = MockResponse(
|
|
550
|
-
ok=True, payload={"messages": {"EvalResponse": [{"isError": None}]}}
|
|
551
|
-
)
|
|
552
|
-
mocked_req = mocker.patch("aiohttp.ClientSession.request", return_value=mock_resp)
|
|
553
|
-
|
|
554
|
-
# Patching to make _are_required_processes_ready() to return True
|
|
555
|
-
mocker.patch.object(
|
|
556
|
-
AppState,
|
|
557
|
-
"_are_required_processes_ready",
|
|
558
|
-
return_value=True,
|
|
559
|
-
)
|
|
560
|
-
# Patching to make get_matlab_state() to return up
|
|
561
|
-
mocker.patch.object(
|
|
562
|
-
AppState,
|
|
563
|
-
"get_matlab_state",
|
|
564
|
-
return_value="up",
|
|
565
|
-
)
|
|
566
|
-
# Wait for _update_matlab_connector_status to run
|
|
567
|
-
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
568
|
-
|
|
569
|
-
# Act
|
|
570
|
-
await app_state_with_token_auth_fixture._AppState__send_stop_request_to_matlab()
|
|
571
|
-
|
|
572
|
-
# Assert
|
|
573
|
-
|
|
574
|
-
# 1 request from _update_matlab_connector_status() and another from
|
|
575
|
-
# /stop_matlab request
|
|
576
|
-
connector_status_request_headers = list(mocked_req.call_args_list)[0].kwargs[
|
|
577
|
-
"headers"
|
|
578
|
-
]
|
|
579
|
-
send_stop_matlab_request_headers = list(mocked_req.call_args_list)[1].kwargs[
|
|
580
|
-
"headers"
|
|
581
|
-
]
|
|
582
|
-
assert sample_token_headers_fixture == connector_status_request_headers
|
|
583
|
-
assert sample_token_headers_fixture == send_stop_matlab_request_headers
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
async def test_start_matlab_without_xvfb(app_state_fixture, mocker):
|
|
587
|
-
"""Test to check if Matlab process starts without throwing errors when Xvfb is not present
|
|
588
|
-
|
|
589
|
-
Args:
|
|
590
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
591
|
-
mocker : Built-in pytest fixture
|
|
592
|
-
"""
|
|
593
|
-
# Arrange
|
|
594
|
-
app_state_fixture.settings["is_xvfb_available"] = False
|
|
595
|
-
mock_matlab = Mock_matlab(None, 1)
|
|
596
|
-
|
|
597
|
-
# Starting asyncio tasks related to matlab is not required here as only Xvfb check is required.
|
|
598
|
-
mocker.patch.object(
|
|
599
|
-
AppState, "_AppState__start_matlab_process", return_value=mock_matlab
|
|
600
|
-
)
|
|
601
|
-
mocker.patch.object(
|
|
602
|
-
AppState, "_AppState__matlab_stderr_reader_posix", return_value=None
|
|
603
|
-
)
|
|
604
|
-
mocker.patch.object(
|
|
605
|
-
AppState, "_AppState__track_embedded_connector_state", return_value=None
|
|
606
|
-
)
|
|
607
|
-
mocker.patch.object(AppState, "_AppState__update_matlab_port", return_value=None)
|
|
608
|
-
|
|
609
|
-
# Act
|
|
610
|
-
await app_state_fixture.start_matlab()
|
|
611
|
-
|
|
612
|
-
# Assert
|
|
613
|
-
# Check if Xvfb has not started
|
|
614
|
-
assert app_state_fixture.processes["xvfb"] is None
|
|
615
|
-
# Check if Matlab started
|
|
616
|
-
assert app_state_fixture.processes["matlab"] is mock_matlab
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
async def test_start_matlab_without_xvfb_and_matlab(app_state_fixture):
|
|
620
|
-
"""Test to check if MATLAB doesn't start and sets the error variable to MatlabInstallError when
|
|
621
|
-
there is not MATLAB on system PATH
|
|
622
|
-
|
|
623
|
-
Args:
|
|
624
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
625
|
-
"""
|
|
626
|
-
# Arrange
|
|
627
|
-
app_state_fixture.settings["is_xvfb_available"] = False
|
|
628
|
-
app_state_fixture.settings["matlab_cmd"] = None
|
|
629
|
-
|
|
630
|
-
# Act
|
|
631
|
-
await app_state_fixture.start_matlab()
|
|
632
|
-
|
|
633
|
-
# Assert
|
|
634
|
-
# Check if Xvfb has not started
|
|
635
|
-
assert app_state_fixture.processes["xvfb"] is None
|
|
636
|
-
# Check if Matlab has not started
|
|
637
|
-
assert app_state_fixture.processes["matlab"] is None
|
|
638
|
-
# Check if MatlabInstallError is set as the error
|
|
639
|
-
assert isinstance(app_state_fixture.error, MatlabInstallError)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
@pytest.mark.parametrize(
|
|
643
|
-
"is_desktop, client_id, is_client_id_present, expected_is_active_client",
|
|
644
|
-
[
|
|
645
|
-
(False, None, False, None),
|
|
646
|
-
(False, "mock_id", False, None),
|
|
647
|
-
(True, None, True, True),
|
|
648
|
-
(True, "mock_id", False, True),
|
|
649
|
-
],
|
|
650
|
-
ids=[
|
|
651
|
-
"request_from_non-desktop_client",
|
|
652
|
-
"request_from_non-desktop_client_having_mock_id",
|
|
653
|
-
"request_from_desktop_client",
|
|
654
|
-
"request_from_desktop_client_having_mock_id",
|
|
655
|
-
],
|
|
656
|
-
)
|
|
657
|
-
async def test_get_session_status(
|
|
658
|
-
app_state_fixture,
|
|
659
|
-
is_desktop,
|
|
660
|
-
client_id,
|
|
661
|
-
is_client_id_present,
|
|
662
|
-
expected_is_active_client,
|
|
663
|
-
):
|
|
664
|
-
"""Test to check if correnct session response is returned based on various conditions.
|
|
665
|
-
|
|
666
|
-
Args:
|
|
667
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
668
|
-
is_desktop (bool): A flag indicating whether the client is a desktop client.
|
|
669
|
-
client_id (str or None): The client ID. If None, a new client ID may be generated.
|
|
670
|
-
is_client_id_present (bool): Indicates whether the expected value of client_id is string or not.
|
|
671
|
-
expected_is_active_client (bool): Indicates the expected value of is_active_client
|
|
672
|
-
|
|
673
|
-
"""
|
|
674
|
-
# The value of transfer_session is a Don't Care condition as initially the value of client_id is always None.
|
|
675
|
-
output_client_id, output_is_active_client = app_state_fixture.get_session_status(
|
|
676
|
-
is_desktop, client_id, transfer_session=False
|
|
677
|
-
)
|
|
678
|
-
assert isinstance(output_client_id, str) == is_client_id_present, (
|
|
679
|
-
"Expected client_id to be a string got None"
|
|
680
|
-
if is_client_id_present
|
|
681
|
-
else "Expected client_id to be None got a string value"
|
|
682
|
-
)
|
|
683
|
-
assert (
|
|
684
|
-
output_is_active_client == expected_is_active_client
|
|
685
|
-
), f"Expected is_active_client to be {expected_is_active_client} got {output_is_active_client}"
|
|
686
|
-
# For clean up of task_detect_client_status
|
|
687
|
-
app_state_fixture.active_client = None
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
async def test_get_session_status_can_transfer_session(app_state_fixture):
|
|
691
|
-
"""Test to check whether transer session changes client id to the new id
|
|
692
|
-
|
|
693
|
-
Args:
|
|
694
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
695
|
-
"""
|
|
696
|
-
app_state_fixture.active_client = "mock_id"
|
|
697
|
-
app_state_fixture.get_session_status(
|
|
698
|
-
is_desktop=True, client_id="new_id", transfer_session=True
|
|
699
|
-
)
|
|
700
|
-
assert app_state_fixture.active_client == "new_id"
|
|
701
|
-
# For clean up of task_detect_client_status
|
|
702
|
-
app_state_fixture.active_client = None
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
async def test_detect_active_client_status_can_reset_active_client(app_state_fixture):
|
|
706
|
-
"""Test to check whether the value of active client is being reset due to the client inactivity.
|
|
707
|
-
|
|
708
|
-
Args:
|
|
709
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
710
|
-
"""
|
|
711
|
-
app_state_fixture.active_client = "mock_id"
|
|
712
|
-
await app_state_fixture.detect_active_client_status(
|
|
713
|
-
sleep_time=0, max_inactive_count=0
|
|
714
|
-
)
|
|
715
|
-
assert (
|
|
716
|
-
app_state_fixture.active_client == None
|
|
717
|
-
), f"Expected the active_client to be None"
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
@pytest.mark.parametrize(
|
|
721
|
-
"session_file_count, has_custom_code_to_execute", [(2, True), (1, False)]
|
|
722
|
-
)
|
|
723
|
-
def test_create_logs_dir_for_MATLAB(
|
|
724
|
-
app_state_fixture, session_file_count, has_custom_code_to_execute
|
|
725
|
-
):
|
|
726
|
-
"""Test to check create_logs_dir_for_MATLAB()
|
|
727
|
-
|
|
728
|
-
Args:
|
|
729
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
730
|
-
"""
|
|
731
|
-
# Arrange
|
|
732
|
-
app_state_fixture.settings["has_custom_code_to_execute"] = (
|
|
733
|
-
has_custom_code_to_execute
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
# Act
|
|
737
|
-
app_state_fixture.create_logs_dir_for_MATLAB()
|
|
738
|
-
|
|
739
|
-
# Assert
|
|
740
|
-
for _, session_file_path in app_state_fixture.matlab_session_files.items():
|
|
741
|
-
# Check session files are present in mwi logs directory
|
|
742
|
-
assert app_state_fixture.mwi_logs_dir == Path(session_file_path).parent
|
|
743
|
-
|
|
744
|
-
assert len(app_state_fixture.matlab_session_files) == session_file_count
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
async def test_check_idle_timer_started(app_state_fixture):
|
|
748
|
-
"""Test to check if the IDLE timer starts automatically
|
|
749
|
-
|
|
750
|
-
Args:
|
|
751
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
752
|
-
"""
|
|
753
|
-
# Arrange
|
|
754
|
-
# Nothing to arrange
|
|
755
|
-
|
|
756
|
-
# Act
|
|
757
|
-
# constructor is called automatically
|
|
758
|
-
|
|
759
|
-
# Assert
|
|
760
|
-
assert app_state_fixture.is_idle_timeout_enabled is True
|
|
761
|
-
assert "decrement_idle_timer" in app_state_fixture.server_tasks
|
|
762
|
-
assert app_state_fixture.idle_timeout_lock is not None
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
async def test_reset_timer(app_state_fixture):
|
|
766
|
-
"""Test to check if the IDLE timer is reset to its initial value
|
|
767
|
-
|
|
768
|
-
Args:
|
|
769
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
770
|
-
"""
|
|
771
|
-
# Arrange
|
|
772
|
-
# Sleep for 1 second for the decrement_timer task to decrease IDLE timer by
|
|
773
|
-
# more than 1 second. This is for decreasing flakiness of this test on different platforms.
|
|
774
|
-
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL + CHECK_MATLAB_STATUS_INTERVAL)
|
|
775
|
-
|
|
776
|
-
# Act
|
|
777
|
-
await app_state_fixture.reset_timer()
|
|
778
|
-
|
|
779
|
-
# Assert
|
|
780
|
-
assert (
|
|
781
|
-
app_state_fixture.get_remaining_idle_timeout()
|
|
782
|
-
== app_state_fixture.settings["mwi_idle_timeout"]
|
|
783
|
-
)
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
async def test_decrement_timer(app_state_fixture):
|
|
787
|
-
"""Test to check if the IDLE timer value decrements automatically
|
|
788
|
-
|
|
789
|
-
Args:
|
|
790
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
791
|
-
"""
|
|
792
|
-
# Arrange
|
|
793
|
-
# Nothing to arrange
|
|
794
|
-
# decrement_timer task is started automatically by the constructor
|
|
795
|
-
|
|
796
|
-
# Sleep for 1 second for the decrement_timer task to decrease IDLE timer by
|
|
797
|
-
# more than 1 second. This is for decreasing flakiness of this test on different platforms.
|
|
798
|
-
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL + CHECK_MATLAB_STATUS_INTERVAL)
|
|
799
|
-
|
|
800
|
-
# Act
|
|
801
|
-
# Nothing to act
|
|
802
|
-
|
|
803
|
-
# Assert
|
|
804
|
-
assert (
|
|
805
|
-
app_state_fixture.get_remaining_idle_timeout()
|
|
806
|
-
< app_state_fixture.settings["mwi_idle_timeout"]
|
|
807
|
-
)
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
async def test_decrement_timer_runs_out(sample_settings_fixture, mocker):
|
|
811
|
-
"""Test to check if the IDLE timer eventually runs out.
|
|
812
|
-
|
|
813
|
-
Args:
|
|
814
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
815
|
-
"""
|
|
816
|
-
# Arrange
|
|
817
|
-
# Set the IDLE timeout to a low value
|
|
818
|
-
idle_timeout = 1
|
|
819
|
-
sample_settings_fixture["mwi_idle_timeout"] = idle_timeout
|
|
820
|
-
app_state = AppState(settings=sample_settings_fixture)
|
|
821
|
-
app_state.processes = {"matlab": None, "xvfb": None}
|
|
822
|
-
app_state.licensing = {"type": "existing_license"}
|
|
823
|
-
|
|
824
|
-
# mock util.get_event_loop() to return a new event_loop for the test to assert
|
|
825
|
-
mock_loop = asyncio.new_event_loop()
|
|
826
|
-
mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=mock_loop)
|
|
827
|
-
|
|
828
|
-
# Act
|
|
829
|
-
# Wait for a little more time than idle_timeout to decrease flakiness of this test on different platforms.
|
|
830
|
-
# MATLAB state changes from down -> starting -> up -> down (idle timer runs out)
|
|
831
|
-
await asyncio.sleep(idle_timeout * FIVE_MAX_TRIES)
|
|
832
|
-
|
|
833
|
-
# Assert
|
|
834
|
-
assert not mock_loop.is_running()
|
|
835
|
-
assert app_state.get_matlab_state() == "down"
|
|
836
|
-
|
|
837
|
-
# Cleanup
|
|
838
|
-
mock_loop.stop()
|
|
839
|
-
await app_state.stop_server_tasks()
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
@pytest.mark.parametrize(
|
|
843
|
-
"connector_status, matlab_status",
|
|
844
|
-
[("down", "starting"), ("up", "up")],
|
|
845
|
-
ids=["connector_down", "connector_up"],
|
|
846
|
-
)
|
|
847
|
-
async def test_update_matlab_state_based_on_connector_state(
|
|
848
|
-
app_state_fixture, connector_status, matlab_status
|
|
849
|
-
):
|
|
850
|
-
"""Test to check if MATLAB state is updated correctly based on connector state
|
|
851
|
-
|
|
852
|
-
Args:
|
|
853
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
854
|
-
connector_status (str): Represents connector status
|
|
855
|
-
matlab_status (str): Represents expected MATLAB status
|
|
856
|
-
"""
|
|
857
|
-
# Arrange
|
|
858
|
-
app_state_fixture.embedded_connector_state = connector_status
|
|
859
|
-
|
|
860
|
-
# Act
|
|
861
|
-
await app_state_fixture._AppState__update_matlab_state_based_on_connector_state()
|
|
862
|
-
|
|
863
|
-
# Assert
|
|
864
|
-
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
@pytest.mark.parametrize(
|
|
868
|
-
"matlab_status, matlab_busy_status",
|
|
869
|
-
[("starting", None), ("up", "busy"), ("up", "idle")],
|
|
870
|
-
ids=["No response from busy status endpoint", "MATLAB is busy", "MATLAB is idle"],
|
|
871
|
-
)
|
|
872
|
-
async def test_update_matlab_state_using_busy_endpoint(
|
|
873
|
-
mocker, app_state_fixture, matlab_status, matlab_busy_status
|
|
874
|
-
):
|
|
875
|
-
"""Test to check if MATLAB and its busy status updates correctly when the
|
|
876
|
-
busy status endpoint is used.
|
|
877
|
-
|
|
878
|
-
Args:
|
|
879
|
-
mocker (mocker): Built-in pytest fixture
|
|
880
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
881
|
-
matlab_status (str): Represents MATLAB status
|
|
882
|
-
matlab_busy_status (str): Represents MATLAB busy status
|
|
883
|
-
"""
|
|
884
|
-
# Arrange
|
|
885
|
-
mocker.patch(
|
|
886
|
-
"matlab_proxy.app_state.mwi.embedded_connector.request.get_busy_state",
|
|
887
|
-
return_value=matlab_busy_status,
|
|
888
|
-
)
|
|
889
|
-
|
|
890
|
-
# Act
|
|
891
|
-
await app_state_fixture._AppState__update_matlab_state_using_busy_status_endpoint()
|
|
892
|
-
|
|
893
|
-
# Assert
|
|
894
|
-
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
895
|
-
assert app_state_fixture.matlab_busy_state == matlab_busy_status
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
@pytest.mark.parametrize(
|
|
899
|
-
"connector_status, matlab_status, matlab_busy_status",
|
|
900
|
-
[("down", "starting", None), ("up", "up", "busy")],
|
|
901
|
-
ids=["connector_down", "connector_up"],
|
|
902
|
-
)
|
|
903
|
-
async def test_update_matlab_state_using_ping_endpoint(
|
|
904
|
-
mocker, app_state_fixture, connector_status, matlab_status, matlab_busy_status
|
|
905
|
-
):
|
|
906
|
-
"""Test to check if MATLAB and its busy status updates correctly when the
|
|
907
|
-
ping endpoint is used.
|
|
908
|
-
|
|
909
|
-
Args:
|
|
910
|
-
mocker (mocker): Built-in pytest fixture
|
|
911
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
912
|
-
connector_status (str): Represents Connector status
|
|
913
|
-
matlab_status (str): Represents MATLAB status
|
|
914
|
-
matlab_busy_status (str): Represents MATLAB busy status
|
|
915
|
-
"""
|
|
916
|
-
# Arrange
|
|
917
|
-
mocker.patch(
|
|
918
|
-
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
919
|
-
return_value=connector_status,
|
|
920
|
-
)
|
|
921
|
-
|
|
922
|
-
# Act
|
|
923
|
-
await app_state_fixture._AppState__update_matlab_state_using_ping_endpoint()
|
|
924
|
-
|
|
925
|
-
# Assert
|
|
926
|
-
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
927
|
-
assert app_state_fixture.matlab_busy_state == matlab_busy_status
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
async def test_update_matlab_state_based_on_endpoint_to_use_required_processes_not_ready(
|
|
931
|
-
mocker, app_state_fixture
|
|
932
|
-
):
|
|
933
|
-
"""Test to check if MATLAB state is 'down' when the required processes are not ready
|
|
934
|
-
|
|
935
|
-
Args:
|
|
936
|
-
mocker (mocker): Built-in pytest fixture
|
|
937
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
938
|
-
"""
|
|
939
|
-
# Arrange
|
|
940
|
-
mocker.patch(
|
|
941
|
-
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
942
|
-
return_value="up",
|
|
943
|
-
)
|
|
944
|
-
|
|
945
|
-
await app_state_fixture._AppState__update_matlab_state_based_on_endpoint_to_use(
|
|
946
|
-
app_state_fixture._AppState__update_matlab_state_using_ping_endpoint
|
|
947
|
-
)
|
|
948
|
-
|
|
949
|
-
assert app_state_fixture.get_matlab_state() == "down"
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
async def test_update_matlab_state_based_on_endpoint_to_use_happy_path(
|
|
953
|
-
mocker, tmp_path, app_state_fixture
|
|
954
|
-
):
|
|
955
|
-
"""Test to check if MATLAB state is 'starting' when the required processes
|
|
956
|
-
are up but the ready file is not created yet.
|
|
957
|
-
|
|
958
|
-
Args:
|
|
959
|
-
mocker (mocker): Built-in pytest fixture
|
|
960
|
-
tmp_path (Path): Built-in pytest fixture
|
|
961
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
962
|
-
"""
|
|
963
|
-
# Arrange
|
|
964
|
-
mocker.patch.object(
|
|
965
|
-
AppState,
|
|
966
|
-
"_are_required_processes_ready",
|
|
967
|
-
return_value=True,
|
|
968
|
-
)
|
|
969
|
-
tmp_file = tmp_path / Path("dummy")
|
|
970
|
-
tmp_file.touch()
|
|
971
|
-
app_state_fixture.matlab_session_files["matlab_ready_file"] = tmp_file
|
|
972
|
-
|
|
973
|
-
# Act
|
|
974
|
-
await app_state_fixture._AppState__update_matlab_state_based_on_endpoint_to_use(
|
|
975
|
-
app_state_fixture._AppState__update_matlab_state_using_ping_endpoint
|
|
976
|
-
)
|
|
977
|
-
|
|
978
|
-
# Assert
|
|
979
|
-
await assert_matlab_state(app_state_fixture, "starting", FIVE_MAX_TRIES)
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
async def assert_matlab_state(app_state_fixture, expected_matlab_status, count):
|
|
983
|
-
"""Tries to assert the MATLAB state to expected_matlab_status for count times.
|
|
984
|
-
Will raise Assertion error after.
|
|
985
|
-
|
|
986
|
-
The count is needed to decrease flakiness of this tests when run on different platforms.
|
|
987
|
-
|
|
988
|
-
Args:
|
|
989
|
-
app_state_fixture (AppState): Instance of AppState class.
|
|
990
|
-
expected_matlab_status (str): Expected MATLAB status
|
|
991
|
-
count (int): Max tries for assertion before AssertionError is raised.
|
|
992
|
-
|
|
993
|
-
Raises:
|
|
994
|
-
AssertionError: Raised when assertion fails after 'count' tries
|
|
995
|
-
"""
|
|
996
|
-
i = 0
|
|
997
|
-
while i < count:
|
|
998
|
-
try:
|
|
999
|
-
assert app_state_fixture.get_matlab_state() == expected_matlab_status
|
|
1000
|
-
return
|
|
1001
|
-
|
|
1002
|
-
except:
|
|
1003
|
-
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
1004
|
-
|
|
1005
|
-
i += 1
|
|
1006
|
-
|
|
1007
|
-
raise AssertionError(
|
|
1008
|
-
f"MATLAB status failed to change to '{expected_matlab_status}'"
|
|
1009
|
-
)
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
@pytest.mark.parametrize(
|
|
1013
|
-
"matlab_ready_file, expected_matlab_status",
|
|
1014
|
-
[
|
|
1015
|
-
(None, "down"),
|
|
1016
|
-
(Path("dummy"), "starting"),
|
|
1017
|
-
],
|
|
1018
|
-
ids=[
|
|
1019
|
-
"no_matlab_ready_file_formed",
|
|
1020
|
-
"no_matlab_ready_file_created",
|
|
1021
|
-
],
|
|
1022
|
-
)
|
|
1023
|
-
async def test_check_matlab_connector_status_auto_updates_based_on_matlab_ready_file(
|
|
1024
|
-
mocker, app_state_fixture, matlab_ready_file, expected_matlab_status
|
|
1025
|
-
):
|
|
1026
|
-
"""Test to check if the status of MATLAB is updated automatically
|
|
1027
|
-
|
|
1028
|
-
Args:
|
|
1029
|
-
mocker (mocker): Built-in pytest fixture
|
|
1030
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
1031
|
-
matlab_ready_file (Path): Path to the ready file
|
|
1032
|
-
expected_matlab_status (str): Expected MATLAB status
|
|
1033
|
-
"""
|
|
1034
|
-
# Arrange
|
|
1035
|
-
mocker.patch.object(
|
|
1036
|
-
AppState,
|
|
1037
|
-
"_are_required_processes_ready",
|
|
1038
|
-
return_value=True,
|
|
1039
|
-
)
|
|
1040
|
-
app_state_fixture.matlab_session_files["matlab_ready_file"] = matlab_ready_file
|
|
1041
|
-
|
|
1042
|
-
# Act
|
|
1043
|
-
# Nothing to act upon as the _update_matlab_state() is started automatically in the constructor.
|
|
1044
|
-
# Have to wait here for the atleast the same interval as the __update_matlab_state_based_on_endpoint_to_use()
|
|
1045
|
-
# for the MATLAB status to update from 'down'
|
|
1046
|
-
|
|
1047
|
-
# Assert
|
|
1048
|
-
# MATLAB state should be 'down' first
|
|
1049
|
-
assert app_state_fixture.get_matlab_state() == "down"
|
|
1050
|
-
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
1051
|
-
|
|
1052
|
-
await assert_matlab_state(app_state_fixture, expected_matlab_status, FIVE_MAX_TRIES)
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
async def test_update_matlab_state_switches_to_busy_endpoint(
|
|
1056
|
-
mocker, tmp_path, app_state_fixture
|
|
1057
|
-
):
|
|
1058
|
-
"""Test to check if the endpoint to determine MATLAB state changes from the ping
|
|
1059
|
-
endpoint to busy status endpoint after the first successful ping request.
|
|
1060
|
-
|
|
1061
|
-
Args:
|
|
1062
|
-
mocker (mocker): Built-in pytest fixture
|
|
1063
|
-
tmp_path (Path): Built-in pytest fixture
|
|
1064
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
1065
|
-
"""
|
|
1066
|
-
# Arrange
|
|
1067
|
-
# Setup mocks for the first ping request to be successful
|
|
1068
|
-
mocker.patch.object(
|
|
1069
|
-
AppState,
|
|
1070
|
-
"_are_required_processes_ready",
|
|
1071
|
-
return_value=True,
|
|
1072
|
-
)
|
|
1073
|
-
mocker.patch(
|
|
1074
|
-
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
1075
|
-
return_value="up",
|
|
1076
|
-
)
|
|
1077
|
-
tmp_file = tmp_path / Path("dummy")
|
|
1078
|
-
tmp_file.touch()
|
|
1079
|
-
app_state_fixture.matlab_session_files["matlab_ready_file"] = tmp_file
|
|
1080
|
-
mocked_busy_status_endpoint_function = mocker.patch.object(
|
|
1081
|
-
app_state_fixture, "_AppState__update_matlab_state_using_busy_status_endpoint"
|
|
1082
|
-
)
|
|
1083
|
-
|
|
1084
|
-
# Act
|
|
1085
|
-
# Nothing to act upon as the _update_matlab_state() is started automatically in the constructor.
|
|
1086
|
-
# Have to wait here for the atleast the same interval as the __update_matlab_state_based_on_endpoint_to_use()
|
|
1087
|
-
# for the MATLAB status to update from 'down'
|
|
1088
|
-
|
|
1089
|
-
# Wait for the ping endpoint request. Waiting for more time than what
|
|
1090
|
-
# is needed to decrease flakiness of this test on different platforms.
|
|
1091
|
-
await asyncio.sleep(1 * FIVE_MAX_TRIES)
|
|
1092
|
-
|
|
1093
|
-
# Assert
|
|
1094
|
-
assert mocked_busy_status_endpoint_function.call_count > 1
|