matlab-proxy 0.18.1__py3-none-any.whl → 0.19.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 +54 -43
- matlab_proxy/app_state.py +370 -155
- matlab_proxy/constants.py +3 -0
- matlab_proxy/gui/asset-manifest.json +6 -6
- matlab_proxy/gui/index.html +1 -1
- matlab_proxy/gui/static/css/{main.47712126.css → main.da9c4eb8.css} +2 -2
- matlab_proxy/gui/static/css/main.da9c4eb8.css.map +1 -0
- matlab_proxy/gui/static/js/{main.5b5ca2f2.js → main.e07799e7.js} +3 -3
- matlab_proxy/gui/static/js/main.e07799e7.js.map +1 -0
- matlab_proxy/matlab/startup.m +0 -20
- matlab_proxy/settings.py +28 -3
- matlab_proxy/util/__init__.py +101 -1
- matlab_proxy/util/event_loop.py +28 -10
- matlab_proxy/util/mwi/embedded_connector/__init__.py +1 -1
- matlab_proxy/util/mwi/embedded_connector/helpers.py +9 -0
- matlab_proxy/util/mwi/embedded_connector/request.py +51 -21
- matlab_proxy/util/mwi/environment_variables.py +6 -1
- matlab_proxy/util/mwi/exceptions.py +16 -1
- matlab_proxy/util/mwi/validators.py +33 -0
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/METADATA +1 -1
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/RECORD +31 -31
- tests/unit/test_app.py +45 -22
- tests/unit/test_app_state.py +404 -111
- tests/unit/test_constants.py +1 -0
- tests/unit/util/mwi/test_validators.py +30 -1
- tests/unit/util/test_util.py +83 -0
- matlab_proxy/gui/static/css/main.47712126.css.map +0 -1
- matlab_proxy/gui/static/js/main.5b5ca2f2.js.map +0 -1
- /matlab_proxy/gui/static/js/{main.5b5ca2f2.js.LICENSE.txt → main.e07799e7.js.LICENSE.txt} +0 -0
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/LICENSE.md +0 -0
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/WHEEL +0 -0
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/entry_points.txt +0 -0
- {matlab_proxy-0.18.1.dist-info → matlab_proxy-0.19.0.dist-info}/top_level.txt +0 -0
tests/unit/test_app_state.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright 2023-2024 The MathWorks, Inc.
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
import os
|
|
5
6
|
from dataclasses import dataclass
|
|
@@ -13,13 +14,14 @@ from matlab_proxy import settings
|
|
|
13
14
|
from matlab_proxy.app_state import AppState
|
|
14
15
|
from matlab_proxy.constants import MWI_AUTH_TOKEN_NAME_FOR_HTTP
|
|
15
16
|
from matlab_proxy.util.mwi.exceptions import LicensingError, MatlabError
|
|
16
|
-
from tests.unit.util import MockResponse
|
|
17
|
-
|
|
18
17
|
from matlab_proxy.constants import (
|
|
19
18
|
CONNECTOR_SECUREPORT_FILENAME,
|
|
20
19
|
USER_CODE_OUTPUT_FILE_NAME,
|
|
21
20
|
)
|
|
22
21
|
|
|
22
|
+
from tests.unit.util import MockResponse
|
|
23
|
+
from tests.unit.test_constants import CHECK_MATLAB_STATUS_INTERVAL, FIVE_MAX_TRIES
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
@pytest.fixture
|
|
25
27
|
def sample_settings_fixture(tmp_path):
|
|
@@ -42,15 +44,19 @@ def sample_settings_fixture(tmp_path):
|
|
|
42
44
|
"app_port": 12345,
|
|
43
45
|
"mwapikey": "asdf",
|
|
44
46
|
"has_custom_code_to_execute": False,
|
|
47
|
+
"mwi_idle_timeout": 100,
|
|
48
|
+
"mwi_is_token_auth_enabled": False,
|
|
49
|
+
"integration_name": "MATLAB Desktop",
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
|
|
48
53
|
@pytest.fixture
|
|
49
|
-
def app_state_fixture(sample_settings_fixture):
|
|
54
|
+
def app_state_fixture(sample_settings_fixture, loop):
|
|
50
55
|
"""A pytest fixture which returns an instance of AppState class with no errors.
|
|
51
56
|
|
|
52
57
|
Args:
|
|
53
58
|
sample_settings_fixture (dict): A dictionary of sample settings to be used by
|
|
59
|
+
loop : A pytest builtin fixture
|
|
54
60
|
|
|
55
61
|
Returns:
|
|
56
62
|
AppState: An object of the AppState class
|
|
@@ -58,7 +64,10 @@ def app_state_fixture(sample_settings_fixture):
|
|
|
58
64
|
app_state = AppState(settings=sample_settings_fixture)
|
|
59
65
|
app_state.processes = {"matlab": None, "xvfb": None}
|
|
60
66
|
app_state.licensing = {"type": "existing_license"}
|
|
61
|
-
|
|
67
|
+
|
|
68
|
+
yield app_state
|
|
69
|
+
|
|
70
|
+
loop.run_until_complete(app_state.stop_server_tasks())
|
|
62
71
|
|
|
63
72
|
|
|
64
73
|
@pytest.fixture
|
|
@@ -95,12 +104,13 @@ def app_state_with_token_auth_fixture(
|
|
|
95
104
|
|
|
96
105
|
|
|
97
106
|
@pytest.fixture
|
|
98
|
-
def mocker_os_patching_fixture(mocker, platform):
|
|
107
|
+
def mocker_os_patching_fixture(mocker, platform, loop):
|
|
99
108
|
"""A pytest fixture which patches the is_* functions in system.py module
|
|
100
109
|
|
|
101
110
|
Args:
|
|
102
111
|
mocker : Built in pytest fixture
|
|
103
112
|
platform (str): A string representing "windows", "linux" or "mac"
|
|
113
|
+
loop : A pytest builtin fixture
|
|
104
114
|
|
|
105
115
|
Returns:
|
|
106
116
|
mocker: Built in pytest fixture with patched calls to system.py module.
|
|
@@ -109,6 +119,7 @@ def mocker_os_patching_fixture(mocker, platform):
|
|
|
109
119
|
mocker.patch("matlab_proxy.app_state.system.is_windows", return_value=False)
|
|
110
120
|
mocker.patch("matlab_proxy.app_state.system.is_mac", return_value=False)
|
|
111
121
|
mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=False)
|
|
122
|
+
mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=loop)
|
|
112
123
|
|
|
113
124
|
if platform == "linux":
|
|
114
125
|
mocker.patch("matlab_proxy.app_state.system.is_linux", return_value=True)
|
|
@@ -140,6 +151,12 @@ class Mock_matlab:
|
|
|
140
151
|
returncode: Optional[int]
|
|
141
152
|
pid: Optional[int]
|
|
142
153
|
|
|
154
|
+
def is_running(self) -> bool:
|
|
155
|
+
return self.returncode is None
|
|
156
|
+
|
|
157
|
+
def wait(self) -> int:
|
|
158
|
+
return self.returncode
|
|
159
|
+
|
|
143
160
|
|
|
144
161
|
@pytest.mark.parametrize(
|
|
145
162
|
"licensing, expected",
|
|
@@ -265,6 +282,7 @@ def test_persist_config_data(licensing_data: dict, tmp_path):
|
|
|
265
282
|
"error": None,
|
|
266
283
|
"matlab_version": None,
|
|
267
284
|
"warnings": [],
|
|
285
|
+
"mwi_idle_timeout": None,
|
|
268
286
|
}
|
|
269
287
|
app_state = AppState(settings=settings)
|
|
270
288
|
app_state.licensing = licensing_data
|
|
@@ -346,89 +364,6 @@ def test_are_required_processes_ready(
|
|
|
346
364
|
assert actual == expected
|
|
347
365
|
|
|
348
366
|
|
|
349
|
-
get_matlab_status_based_on_connector_status_test_data = [
|
|
350
|
-
("up", True, "up"),
|
|
351
|
-
("down", True, "starting"),
|
|
352
|
-
("up", False, "starting"),
|
|
353
|
-
]
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
@pytest.mark.parametrize(
|
|
357
|
-
"connector_status, ready_file_present, matlab_status",
|
|
358
|
-
get_matlab_status_based_on_connector_status_test_data,
|
|
359
|
-
ids=["connector_up", "connector_down", "connector_up_ready_file_not_present"],
|
|
360
|
-
)
|
|
361
|
-
async def test_get_matlab_status_based_on_connector_status(
|
|
362
|
-
mocker, app_state_fixture, connector_status, ready_file_present, matlab_status
|
|
363
|
-
):
|
|
364
|
-
"""Test to check matlab status based on connector status
|
|
365
|
-
|
|
366
|
-
Args:
|
|
367
|
-
mocker : Built in pytest fixture.
|
|
368
|
-
connector_status (str): Status of Embedded Connector.
|
|
369
|
-
ready_file_present (bool): Represents if the ready file has been created or not.
|
|
370
|
-
matlab_status (str): Represents the status of MATLAB process.
|
|
371
|
-
"""
|
|
372
|
-
# Arrange
|
|
373
|
-
mocker.patch(
|
|
374
|
-
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
375
|
-
return_value=connector_status,
|
|
376
|
-
)
|
|
377
|
-
mocker.patch.object(Path, "exists", return_value=ready_file_present)
|
|
378
|
-
app_state_fixture.settings["mwi_is_token_auth_enabled"] = False
|
|
379
|
-
app_state_fixture.matlab_session_files["matlab_ready_file"] = Path("dummy")
|
|
380
|
-
|
|
381
|
-
# Act
|
|
382
|
-
actual_matlab_status = await app_state_fixture._get_matlab_connector_status()
|
|
383
|
-
|
|
384
|
-
# Assert
|
|
385
|
-
assert actual_matlab_status == matlab_status
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
@pytest.mark.parametrize(
|
|
389
|
-
"valid_processes, connector_status, expected",
|
|
390
|
-
[
|
|
391
|
-
(True, "up", "up"),
|
|
392
|
-
(False, "up", "down"),
|
|
393
|
-
(True, "down", "down"),
|
|
394
|
-
],
|
|
395
|
-
ids=[
|
|
396
|
-
"valid_processes_connector_up",
|
|
397
|
-
"invalid_processes_connector_up",
|
|
398
|
-
"valid_processes_connector_down",
|
|
399
|
-
],
|
|
400
|
-
)
|
|
401
|
-
async def test_get_matlab_state(
|
|
402
|
-
app_state_fixture, mocker, valid_processes, connector_status, expected
|
|
403
|
-
):
|
|
404
|
-
"""Test to check get_matlab_state returns the correct MATLAB state based on the connector status
|
|
405
|
-
|
|
406
|
-
Args:
|
|
407
|
-
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
408
|
-
mocker : Built in pytest fixture
|
|
409
|
-
valid_processes (bool): Represents if the processes are valid or not
|
|
410
|
-
connector_status (str): Status of Embedded Connector.
|
|
411
|
-
expected (str): Expected status of MATLAB process.
|
|
412
|
-
"""
|
|
413
|
-
# Arrange
|
|
414
|
-
mocker.patch.object(
|
|
415
|
-
AppState,
|
|
416
|
-
"_are_required_processes_ready",
|
|
417
|
-
return_value=valid_processes,
|
|
418
|
-
)
|
|
419
|
-
mocker.patch.object(
|
|
420
|
-
AppState,
|
|
421
|
-
"_get_matlab_connector_status",
|
|
422
|
-
return_value=connector_status,
|
|
423
|
-
)
|
|
424
|
-
|
|
425
|
-
# Act
|
|
426
|
-
actual_state = await app_state_fixture.get_matlab_state()
|
|
427
|
-
|
|
428
|
-
# Assert
|
|
429
|
-
assert actual_state == expected
|
|
430
|
-
|
|
431
|
-
|
|
432
367
|
@pytest.mark.parametrize("platform", [("linux"), ("windows"), ("mac")])
|
|
433
368
|
async def test_track_embedded_connector(mocker_os_patching_fixture, app_state_fixture):
|
|
434
369
|
"""Test to check track_embedded_connector task
|
|
@@ -517,47 +452,55 @@ async def test_setup_env_for_matlab(
|
|
|
517
452
|
assert expected_output in matlab_env["MW_DIAGNOSTIC_DEST"]
|
|
518
453
|
|
|
519
454
|
|
|
520
|
-
@pytest.mark.parametrize(
|
|
521
|
-
"function_to_call ,mock_response",
|
|
522
|
-
[
|
|
523
|
-
("_get_matlab_connector_status", MockResponse(ok=True)),
|
|
524
|
-
(
|
|
525
|
-
"_AppState__send_stop_request_to_matlab",
|
|
526
|
-
MockResponse(
|
|
527
|
-
ok=True, payload={"messages": {"EvalResponse": [{"isError": None}]}}
|
|
528
|
-
),
|
|
529
|
-
),
|
|
530
|
-
],
|
|
531
|
-
ids=["request matlab connector status", "send request to stop matlab"],
|
|
532
|
-
)
|
|
533
455
|
async def test_requests_sent_by_matlab_proxy_have_headers(
|
|
534
456
|
app_state_with_token_auth_fixture,
|
|
535
|
-
function_to_call,
|
|
536
|
-
mock_response,
|
|
537
|
-
mocker,
|
|
538
457
|
sample_token_headers_fixture,
|
|
458
|
+
mocker,
|
|
539
459
|
):
|
|
540
|
-
"""Test to check if token headers are included in requests sent by matlab-proxy when authentication is enabled
|
|
460
|
+
"""Test to check if token headers are included in requests sent by matlab-proxy when authentication is enabled.
|
|
461
|
+
Test checks if the headers are included in the request to stop matlab and get connector status.
|
|
541
462
|
|
|
542
463
|
Args:
|
|
543
464
|
app_state_fixture_with_token_auth (AppState): Instance of AppState class with token authentication enabled
|
|
465
|
+
sample_token_headers_fixture (dict): Dict which represents the token headers
|
|
544
466
|
mocker : Built-in pytest fixture
|
|
545
467
|
"""
|
|
546
468
|
# Arrange
|
|
547
|
-
|
|
548
|
-
"
|
|
469
|
+
mock_resp = MockResponse(
|
|
470
|
+
ok=True, payload={"messages": {"EvalResponse": [{"isError": None}]}}
|
|
471
|
+
)
|
|
472
|
+
mocked_req = mocker.patch("aiohttp.ClientSession.request", return_value=mock_resp)
|
|
473
|
+
|
|
474
|
+
# Patching to make _are_required_processes_ready() to return True
|
|
475
|
+
mocker.patch.object(
|
|
476
|
+
AppState,
|
|
477
|
+
"_are_required_processes_ready",
|
|
478
|
+
return_value=True,
|
|
479
|
+
)
|
|
480
|
+
# Patching to make get_matlab_state() to return up
|
|
481
|
+
mocker.patch.object(
|
|
482
|
+
AppState,
|
|
483
|
+
"get_matlab_state",
|
|
484
|
+
return_value="up",
|
|
549
485
|
)
|
|
486
|
+
# Wait for _update_matlab_connector_status to run
|
|
487
|
+
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
550
488
|
|
|
551
489
|
# Act
|
|
552
|
-
|
|
553
|
-
method = getattr(app_state_with_token_auth_fixture, function_to_call)
|
|
554
|
-
_ = await method()
|
|
490
|
+
await app_state_with_token_auth_fixture._AppState__send_stop_request_to_matlab()
|
|
555
491
|
|
|
556
492
|
# Assert
|
|
557
|
-
|
|
493
|
+
|
|
494
|
+
# 1 request from _update_matlab_connector_status() and another from
|
|
495
|
+
# /stop_matlab request
|
|
496
|
+
connector_status_request_headers = list(mocked_req.call_args_list)[0].kwargs[
|
|
497
|
+
"headers"
|
|
498
|
+
]
|
|
499
|
+
send_stop_matlab_request_headers = list(mocked_req.call_args_list)[1].kwargs[
|
|
558
500
|
"headers"
|
|
559
501
|
]
|
|
560
502
|
assert sample_token_headers_fixture == connector_status_request_headers
|
|
503
|
+
assert sample_token_headers_fixture == send_stop_matlab_request_headers
|
|
561
504
|
|
|
562
505
|
|
|
563
506
|
async def test_start_matlab_without_xvfb(app_state_fixture, mocker):
|
|
@@ -696,3 +639,353 @@ def test_create_logs_dir_for_MATLAB(
|
|
|
696
639
|
assert app_state_fixture.mwi_logs_dir == Path(session_file_path).parent
|
|
697
640
|
|
|
698
641
|
assert len(app_state_fixture.matlab_session_files) == session_file_count
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
async def test_check_idle_timer_started(app_state_fixture):
|
|
645
|
+
"""Test to check if the IDLE timer starts automatically
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
649
|
+
"""
|
|
650
|
+
# Arrange
|
|
651
|
+
# Nothing to arrange
|
|
652
|
+
|
|
653
|
+
# Act
|
|
654
|
+
# constructor is called automatically
|
|
655
|
+
|
|
656
|
+
# Assert
|
|
657
|
+
assert app_state_fixture.is_idle_timeout_enabled is True
|
|
658
|
+
assert "decrement_idle_timer" in app_state_fixture.server_tasks
|
|
659
|
+
assert app_state_fixture.idle_timeout_lock is not None
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
async def test_reset_timer(app_state_fixture):
|
|
663
|
+
"""Test to check if the IDLE timer is reset to its initial value
|
|
664
|
+
|
|
665
|
+
Args:
|
|
666
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
667
|
+
"""
|
|
668
|
+
# Arrange
|
|
669
|
+
# Sleep for 1 second for the decrement_timer task to decrease IDLE timer by
|
|
670
|
+
# more than 1 second. This is for decreasing flakiness of this test on different platforms.
|
|
671
|
+
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL + CHECK_MATLAB_STATUS_INTERVAL)
|
|
672
|
+
|
|
673
|
+
# Act
|
|
674
|
+
await app_state_fixture.reset_timer()
|
|
675
|
+
|
|
676
|
+
# Assert
|
|
677
|
+
assert (
|
|
678
|
+
app_state_fixture.get_remaining_idle_timeout()
|
|
679
|
+
== app_state_fixture.settings["mwi_idle_timeout"]
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
async def test_decrement_timer(app_state_fixture):
|
|
684
|
+
"""Test to check if the IDLE timer value decrements automatically
|
|
685
|
+
|
|
686
|
+
Args:
|
|
687
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
688
|
+
"""
|
|
689
|
+
# Arrange
|
|
690
|
+
# Nothing to arrange
|
|
691
|
+
# decrement_timer task is started automatically by the constructor
|
|
692
|
+
|
|
693
|
+
# Sleep for 1 second for the decrement_timer task to decrease IDLE timer by
|
|
694
|
+
# more than 1 second. This is for decreasing flakiness of this test on different platforms.
|
|
695
|
+
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL + CHECK_MATLAB_STATUS_INTERVAL)
|
|
696
|
+
|
|
697
|
+
# Act
|
|
698
|
+
# Nothing to act
|
|
699
|
+
|
|
700
|
+
# Assert
|
|
701
|
+
assert (
|
|
702
|
+
app_state_fixture.get_remaining_idle_timeout()
|
|
703
|
+
< app_state_fixture.settings["mwi_idle_timeout"]
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
async def test_decrement_timer_runs_out(sample_settings_fixture, mocker):
|
|
708
|
+
"""Test to check if the IDLE timer eventually runs out.
|
|
709
|
+
|
|
710
|
+
Args:
|
|
711
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
712
|
+
"""
|
|
713
|
+
# Arrange
|
|
714
|
+
# Set the IDLE timeout to a low value
|
|
715
|
+
idle_timeout = 1
|
|
716
|
+
sample_settings_fixture["mwi_idle_timeout"] = idle_timeout
|
|
717
|
+
app_state = AppState(settings=sample_settings_fixture)
|
|
718
|
+
app_state.processes = {"matlab": None, "xvfb": None}
|
|
719
|
+
app_state.licensing = {"type": "existing_license"}
|
|
720
|
+
|
|
721
|
+
# mock util.get_event_loop() to return a new loop for the test to assert
|
|
722
|
+
mock_loop = asyncio.new_event_loop()
|
|
723
|
+
mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=mock_loop)
|
|
724
|
+
|
|
725
|
+
# Act
|
|
726
|
+
# Wait for a little more time than idle_timeout to decrease flakiness of this test on different platforms.
|
|
727
|
+
# MATLAB state changes from down -> starting -> up -> down (idle timer runs out)
|
|
728
|
+
await asyncio.sleep(idle_timeout * FIVE_MAX_TRIES)
|
|
729
|
+
|
|
730
|
+
# Assert
|
|
731
|
+
assert not mock_loop.is_running()
|
|
732
|
+
assert app_state.get_matlab_state() == "down"
|
|
733
|
+
|
|
734
|
+
# Cleanup
|
|
735
|
+
mock_loop.stop()
|
|
736
|
+
await app_state.stop_server_tasks()
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
@pytest.mark.parametrize(
|
|
740
|
+
"connector_status, matlab_status",
|
|
741
|
+
[("down", "starting"), ("up", "up")],
|
|
742
|
+
ids=["connector_down", "connector_up"],
|
|
743
|
+
)
|
|
744
|
+
async def test_update_matlab_state_based_on_connector_state(
|
|
745
|
+
app_state_fixture, connector_status, matlab_status
|
|
746
|
+
):
|
|
747
|
+
"""Test to check if MATLAB state is updated correctly based on connector state
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
751
|
+
connector_status (str): Represents connector status
|
|
752
|
+
matlab_status (str): Represents expected MATLAB status
|
|
753
|
+
"""
|
|
754
|
+
# Arrange
|
|
755
|
+
app_state_fixture.embedded_connector_state = connector_status
|
|
756
|
+
|
|
757
|
+
# Act
|
|
758
|
+
await app_state_fixture._AppState__update_matlab_state_based_on_connector_state()
|
|
759
|
+
|
|
760
|
+
# Assert
|
|
761
|
+
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
@pytest.mark.parametrize(
|
|
765
|
+
"matlab_status, matlab_busy_status",
|
|
766
|
+
[("starting", None), ("up", "busy"), ("up", "idle")],
|
|
767
|
+
ids=["No response from busy status endpoint", "MATLAB is busy", "MATLAB is idle"],
|
|
768
|
+
)
|
|
769
|
+
async def test_update_matlab_state_using_busy_endpoint(
|
|
770
|
+
mocker, app_state_fixture, matlab_status, matlab_busy_status
|
|
771
|
+
):
|
|
772
|
+
"""Test to check if MATLAB and its busy status updates correctly when the
|
|
773
|
+
busy status endpoint is used.
|
|
774
|
+
|
|
775
|
+
Args:
|
|
776
|
+
mocker (mocker): Built-in pytest fixture
|
|
777
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
778
|
+
matlab_status (str): Represents MATLAB status
|
|
779
|
+
matlab_busy_status (str): Represents MATLAB busy status
|
|
780
|
+
"""
|
|
781
|
+
# Arrange
|
|
782
|
+
mocker.patch(
|
|
783
|
+
"matlab_proxy.app_state.mwi.embedded_connector.request.get_busy_state",
|
|
784
|
+
return_value=matlab_busy_status,
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
# Act
|
|
788
|
+
await app_state_fixture._AppState__update_matlab_state_using_busy_status_endpoint()
|
|
789
|
+
|
|
790
|
+
# Assert
|
|
791
|
+
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
792
|
+
assert app_state_fixture.matlab_busy_state == matlab_busy_status
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
@pytest.mark.parametrize(
|
|
796
|
+
"connector_status, matlab_status, matlab_busy_status",
|
|
797
|
+
[("down", "starting", None), ("up", "up", "busy")],
|
|
798
|
+
ids=["connector_down", "connector_up"],
|
|
799
|
+
)
|
|
800
|
+
async def test_update_matlab_state_using_ping_endpoint(
|
|
801
|
+
mocker, app_state_fixture, connector_status, matlab_status, matlab_busy_status
|
|
802
|
+
):
|
|
803
|
+
"""Test to check if MATLAB and its busy status updates correctly when the
|
|
804
|
+
ping endpoint is used.
|
|
805
|
+
|
|
806
|
+
Args:
|
|
807
|
+
mocker (mocker): Built-in pytest fixture
|
|
808
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
809
|
+
connector_status (str): Represents Connector status
|
|
810
|
+
matlab_status (str): Represents MATLAB status
|
|
811
|
+
matlab_busy_status (str): Represents MATLAB busy status
|
|
812
|
+
"""
|
|
813
|
+
# Arrange
|
|
814
|
+
mocker.patch(
|
|
815
|
+
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
816
|
+
return_value=connector_status,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# Act
|
|
820
|
+
await app_state_fixture._AppState__update_matlab_state_using_ping_endpoint()
|
|
821
|
+
|
|
822
|
+
# Assert
|
|
823
|
+
assert app_state_fixture.get_matlab_state() == matlab_status
|
|
824
|
+
assert app_state_fixture.matlab_busy_state == matlab_busy_status
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
async def test_update_matlab_state_based_on_endpoint_to_use_required_processes_not_ready(
|
|
828
|
+
mocker, app_state_fixture
|
|
829
|
+
):
|
|
830
|
+
"""Test to check if MATLAB state is 'down' when the required processes are not ready
|
|
831
|
+
|
|
832
|
+
Args:
|
|
833
|
+
mocker (mocker): Built-in pytest fixture
|
|
834
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
835
|
+
"""
|
|
836
|
+
# Arrange
|
|
837
|
+
mocker.patch(
|
|
838
|
+
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
839
|
+
return_value="up",
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
await app_state_fixture._AppState__update_matlab_state_based_on_endpoint_to_use(
|
|
843
|
+
app_state_fixture._AppState__update_matlab_state_using_ping_endpoint
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
assert app_state_fixture.get_matlab_state() == "down"
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
async def test_update_matlab_state_based_on_endpoint_to_use_happy_path(
|
|
850
|
+
mocker, tmp_path, app_state_fixture
|
|
851
|
+
):
|
|
852
|
+
"""Test to check if MATLAB state is 'starting' when the required processes
|
|
853
|
+
are up but the ready file is not created yet.
|
|
854
|
+
|
|
855
|
+
Args:
|
|
856
|
+
mocker (mocker): Built-in pytest fixture
|
|
857
|
+
tmp_path (Path): Built-in pytest fixture
|
|
858
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
859
|
+
"""
|
|
860
|
+
# Arrange
|
|
861
|
+
mocker.patch.object(
|
|
862
|
+
AppState,
|
|
863
|
+
"_are_required_processes_ready",
|
|
864
|
+
return_value=True,
|
|
865
|
+
)
|
|
866
|
+
tmp_file = tmp_path / Path("dummy")
|
|
867
|
+
tmp_file.touch()
|
|
868
|
+
app_state_fixture.matlab_session_files["matlab_ready_file"] = tmp_file
|
|
869
|
+
|
|
870
|
+
# Act
|
|
871
|
+
await app_state_fixture._AppState__update_matlab_state_based_on_endpoint_to_use(
|
|
872
|
+
app_state_fixture._AppState__update_matlab_state_using_ping_endpoint
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
# Assert
|
|
876
|
+
await assert_matlab_state(app_state_fixture, "starting", FIVE_MAX_TRIES)
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
async def assert_matlab_state(app_state_fixture, expected_matlab_status, count):
|
|
880
|
+
"""Tries to assert the MATLAB state to expected_matlab_status for count times.
|
|
881
|
+
Will raise Assertion error after.
|
|
882
|
+
|
|
883
|
+
The count is needed to decrease flakiness of this tests when run on different platforms.
|
|
884
|
+
|
|
885
|
+
Args:
|
|
886
|
+
app_state_fixture (AppState): Instance of AppState class.
|
|
887
|
+
expected_matlab_status (str): Expected MATLAB status
|
|
888
|
+
count (int): Max tries for assertion before AssertionError is raised.
|
|
889
|
+
|
|
890
|
+
Raises:
|
|
891
|
+
AssertionError: Raised when assertion fails after 'count' tries
|
|
892
|
+
"""
|
|
893
|
+
i = 0
|
|
894
|
+
while i < count:
|
|
895
|
+
try:
|
|
896
|
+
assert app_state_fixture.get_matlab_state() == expected_matlab_status
|
|
897
|
+
return
|
|
898
|
+
|
|
899
|
+
except:
|
|
900
|
+
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
901
|
+
|
|
902
|
+
i += 1
|
|
903
|
+
|
|
904
|
+
raise AssertionError(
|
|
905
|
+
f"MATLAB status failed to change to '{expected_matlab_status}'"
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
@pytest.mark.parametrize(
|
|
910
|
+
"matlab_ready_file, expected_matlab_status",
|
|
911
|
+
[
|
|
912
|
+
(None, "down"),
|
|
913
|
+
(Path("dummy"), "starting"),
|
|
914
|
+
],
|
|
915
|
+
ids=[
|
|
916
|
+
"no_matlab_ready_file_formed",
|
|
917
|
+
"no_matlab_ready_file_created",
|
|
918
|
+
],
|
|
919
|
+
)
|
|
920
|
+
async def test_check_matlab_connector_status_auto_updates_based_on_matlab_ready_file(
|
|
921
|
+
mocker, app_state_fixture, matlab_ready_file, expected_matlab_status
|
|
922
|
+
):
|
|
923
|
+
"""Test to check if the status of MATLAB is updated automatically
|
|
924
|
+
|
|
925
|
+
Args:
|
|
926
|
+
mocker (mocker): Built-in pytest fixture
|
|
927
|
+
app_state_fixture (AppState): Object of AppState class with defaults set
|
|
928
|
+
matlab_ready_file (Path): Path to the ready file
|
|
929
|
+
expected_matlab_status (str): Expected MATLAB status
|
|
930
|
+
"""
|
|
931
|
+
# Arrange
|
|
932
|
+
mocker.patch.object(
|
|
933
|
+
AppState,
|
|
934
|
+
"_are_required_processes_ready",
|
|
935
|
+
return_value=True,
|
|
936
|
+
)
|
|
937
|
+
app_state_fixture.matlab_session_files["matlab_ready_file"] = matlab_ready_file
|
|
938
|
+
|
|
939
|
+
# Act
|
|
940
|
+
# Nothing to act upon as the _update_matlab_state() is started automatically in the constructor.
|
|
941
|
+
# Have to wait here for the atleast the same interval as the __update_matlab_state_based_on_endpoint_to_use()
|
|
942
|
+
# for the MATLAB status to update from 'down'
|
|
943
|
+
|
|
944
|
+
# Assert
|
|
945
|
+
# MATLAB state should be 'down' first
|
|
946
|
+
assert app_state_fixture.get_matlab_state() == "down"
|
|
947
|
+
await asyncio.sleep(CHECK_MATLAB_STATUS_INTERVAL)
|
|
948
|
+
|
|
949
|
+
await assert_matlab_state(app_state_fixture, expected_matlab_status, FIVE_MAX_TRIES)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
async def test_update_matlab_state_switches_to_busy_endpoint(
|
|
953
|
+
mocker, tmp_path, app_state_fixture
|
|
954
|
+
):
|
|
955
|
+
"""Test to check if the endpoint to determine MATLAB state changes from the ping
|
|
956
|
+
endpoint to busy status endpoint after the first successful ping request.
|
|
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
|
+
# Setup mocks for the first ping request to be successful
|
|
965
|
+
mocker.patch.object(
|
|
966
|
+
AppState,
|
|
967
|
+
"_are_required_processes_ready",
|
|
968
|
+
return_value=True,
|
|
969
|
+
)
|
|
970
|
+
mocker.patch(
|
|
971
|
+
"matlab_proxy.app_state.mwi.embedded_connector.request.get_state",
|
|
972
|
+
return_value="up",
|
|
973
|
+
)
|
|
974
|
+
tmp_file = tmp_path / Path("dummy")
|
|
975
|
+
tmp_file.touch()
|
|
976
|
+
app_state_fixture.matlab_session_files["matlab_ready_file"] = tmp_file
|
|
977
|
+
mocked_busy_status_endpoint_function = mocker.patch.object(
|
|
978
|
+
app_state_fixture, "_AppState__update_matlab_state_using_busy_status_endpoint"
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
# Act
|
|
982
|
+
# Nothing to act upon as the _update_matlab_state() is started automatically in the constructor.
|
|
983
|
+
# Have to wait here for the atleast the same interval as the __update_matlab_state_based_on_endpoint_to_use()
|
|
984
|
+
# for the MATLAB status to update from 'down'
|
|
985
|
+
|
|
986
|
+
# Wait for the ping endpoint request. Waiting for more time than what
|
|
987
|
+
# is needed to decrease flakiness of this test on different platforms.
|
|
988
|
+
await asyncio.sleep(1 * FIVE_MAX_TRIES)
|
|
989
|
+
|
|
990
|
+
# Assert
|
|
991
|
+
assert mocked_busy_status_endpoint_function.call_count > 1
|
tests/unit/test_constants.py
CHANGED
|
@@ -7,7 +7,10 @@ import os
|
|
|
7
7
|
import random
|
|
8
8
|
import socket
|
|
9
9
|
import tempfile
|
|
10
|
-
from matlab_proxy.util.mwi.validators import
|
|
10
|
+
from matlab_proxy.util.mwi.validators import (
|
|
11
|
+
validate_idle_timeout,
|
|
12
|
+
validate_matlab_root_path,
|
|
13
|
+
)
|
|
11
14
|
from matlab_proxy import constants
|
|
12
15
|
from pathlib import Path
|
|
13
16
|
|
|
@@ -329,3 +332,29 @@ def test_validate_matlab_root_path_non_existent_versioninfo_file(tmp_path):
|
|
|
329
332
|
# Assert
|
|
330
333
|
assert actual_matlab_root is None
|
|
331
334
|
assert actual_matlab_root_custom is None
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@pytest.mark.parametrize(
|
|
338
|
+
"timeout, validated_timeout",
|
|
339
|
+
[
|
|
340
|
+
(None, None),
|
|
341
|
+
("abc", None),
|
|
342
|
+
(-10, None),
|
|
343
|
+
(123, 60 * 123),
|
|
344
|
+
],
|
|
345
|
+
ids=[
|
|
346
|
+
"No IDLE timeout specified",
|
|
347
|
+
"Invalid IDLE timeout specified",
|
|
348
|
+
"Negative number supplied as IDLE timeout",
|
|
349
|
+
"Valid IDLE timeout specified",
|
|
350
|
+
],
|
|
351
|
+
)
|
|
352
|
+
def test_validate_idle_timeout(timeout, validated_timeout):
|
|
353
|
+
# Arrange
|
|
354
|
+
# Nothing to arrange
|
|
355
|
+
|
|
356
|
+
# Act
|
|
357
|
+
actual_timeout = validate_idle_timeout(timeout)
|
|
358
|
+
|
|
359
|
+
# Assert
|
|
360
|
+
assert actual_timeout == validated_timeout
|