hardpy 0.7.0__py3-none-any.whl → 0.9.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.
- hardpy/__init__.py +27 -27
- hardpy/cli/cli.py +1 -2
- hardpy/common/config.py +8 -8
- hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
- hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +1 -1
- hardpy/hardpy_panel/frontend/dist/static/js/main.403e9cd8.js +3 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.403e9cd8.js.map +1 -0
- hardpy/pytest_hardpy/db/__init__.py +1 -1
- hardpy/pytest_hardpy/db/base_connector.py +1 -1
- hardpy/pytest_hardpy/db/base_store.py +9 -0
- hardpy/pytest_hardpy/db/const.py +5 -0
- hardpy/pytest_hardpy/db/schema/v1.py +14 -7
- hardpy/pytest_hardpy/db/statestore.py +0 -11
- hardpy/pytest_hardpy/plugin.py +56 -64
- hardpy/pytest_hardpy/pytest_call.py +84 -44
- hardpy/pytest_hardpy/pytest_wrapper.py +4 -5
- hardpy/pytest_hardpy/reporter/__init__.py +1 -1
- hardpy/pytest_hardpy/reporter/hook_reporter.py +3 -2
- hardpy/pytest_hardpy/result/__init__.py +1 -1
- hardpy/pytest_hardpy/utils/__init__.py +16 -12
- hardpy/pytest_hardpy/utils/dialog_box.py +91 -54
- hardpy/pytest_hardpy/utils/exception.py +6 -0
- {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/METADATA +11 -8
- {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/RECORD +30 -29
- hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js +0 -3
- hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js.map +0 -1
- /hardpy/hardpy_panel/frontend/dist/static/js/{main.942e57d4.js.LICENSE.txt → main.403e9cd8.js.LICENSE.txt} +0 -0
- {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/WHEEL +0 -0
- {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from pycouchdb.client import Database
|
|
5
5
|
from pycouchdb.exceptions import Conflict, GenericError
|
|
6
|
-
from requests.exceptions import ConnectionError
|
|
6
|
+
from requests.exceptions import ConnectionError # noqa: A004
|
|
7
7
|
|
|
8
8
|
from hardpy.pytest_hardpy.db.base_server import BaseServer
|
|
9
9
|
|
|
@@ -69,6 +69,15 @@ class BaseStore(BaseConnector):
|
|
|
69
69
|
self._doc = self._db.get(self._doc_id)
|
|
70
70
|
return self._schema(**self._doc)
|
|
71
71
|
|
|
72
|
+
def clear(self) -> None:
|
|
73
|
+
"""Clear database."""
|
|
74
|
+
try:
|
|
75
|
+
# Clear statestore and runstore databases before each launch
|
|
76
|
+
self._db.delete(self._doc_id)
|
|
77
|
+
except (Conflict, NotFound):
|
|
78
|
+
self._log.debug("Database will be created for the first time")
|
|
79
|
+
self._doc: dict = self._init_doc()
|
|
80
|
+
|
|
72
81
|
def _init_doc(self) -> dict:
|
|
73
82
|
try:
|
|
74
83
|
doc = self._db.get(self._doc_id)
|
hardpy/pytest_hardpy/db/const.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import ClassVar
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
|
-
from hardpy.pytest_hardpy.utils import TestStatus as Status # noqa:
|
|
9
|
+
from hardpy.pytest_hardpy.utils import TestStatus as Status # noqa: TC001
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class IBaseResult(BaseModel):
|
|
@@ -42,7 +42,10 @@ class CaseStateStore(IBaseResult):
|
|
|
42
42
|
"text": "some text"
|
|
43
43
|
},
|
|
44
44
|
"type": "textinput"
|
|
45
|
-
}
|
|
45
|
+
},
|
|
46
|
+
visible: true,
|
|
47
|
+
id: "af6ac3e7-7ce8-4a6b-bb9d-88c3e10b5c7a",
|
|
48
|
+
font_size: 14
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
}
|
|
@@ -177,7 +180,7 @@ class TestStand(BaseModel):
|
|
|
177
180
|
"info": {
|
|
178
181
|
"geo": "Belgrade"
|
|
179
182
|
},
|
|
180
|
-
"timezone": "
|
|
183
|
+
"timezone": "Europe/Belgrade",
|
|
181
184
|
"drivers": {
|
|
182
185
|
"driver_1": "driver info",
|
|
183
186
|
"driver_2": {
|
|
@@ -228,7 +231,7 @@ class ResultStateStore(IBaseResult):
|
|
|
228
231
|
"info": {
|
|
229
232
|
"geo": "Belgrade"
|
|
230
233
|
},
|
|
231
|
-
"timezone": "
|
|
234
|
+
"timezone": "Europe/Belgrade",
|
|
232
235
|
"drivers": {
|
|
233
236
|
"driver_1": "driver info",
|
|
234
237
|
"driver_2": {
|
|
@@ -241,7 +244,8 @@ class ResultStateStore(IBaseResult):
|
|
|
241
244
|
"operator_msg": {
|
|
242
245
|
"msg": "Operator message",
|
|
243
246
|
"title": "Message",
|
|
244
|
-
"visible":
|
|
247
|
+
"visible": true,
|
|
248
|
+
"id": "f45ac1e7-2ce8-4a6b-bb9d-8863e30bcc78"
|
|
245
249
|
},
|
|
246
250
|
"modules": {
|
|
247
251
|
"test_1_a": {
|
|
@@ -266,7 +270,10 @@ class ResultStateStore(IBaseResult):
|
|
|
266
270
|
"text": "some text"
|
|
267
271
|
},
|
|
268
272
|
"type": "textinput"
|
|
269
|
-
}
|
|
273
|
+
},
|
|
274
|
+
visible: true,
|
|
275
|
+
id: "f45bc1e7-2c18-4a4b-2b9d-8863e30bcc78",
|
|
276
|
+
font_size: 14
|
|
270
277
|
}
|
|
271
278
|
},
|
|
272
279
|
"test_minute_parity": {
|
|
@@ -324,7 +331,7 @@ class ResultRunStore(IBaseResult):
|
|
|
324
331
|
"info": {
|
|
325
332
|
"geo": "Belgrade"
|
|
326
333
|
},
|
|
327
|
-
"timezone": "
|
|
334
|
+
"timezone": "Europe/Belgrade",
|
|
328
335
|
"drivers": {
|
|
329
336
|
"driver_1": "driver info",
|
|
330
337
|
"driver_2": {
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
from logging import getLogger
|
|
5
5
|
|
|
6
|
-
from pycouchdb.exceptions import Conflict, NotFound
|
|
7
|
-
|
|
8
6
|
from hardpy.pytest_hardpy.db.base_store import BaseStore
|
|
9
7
|
from hardpy.pytest_hardpy.db.schema import ResultStateStore
|
|
10
8
|
from hardpy.pytest_hardpy.utils import SingletonMeta
|
|
@@ -17,12 +15,3 @@ class StateStore(BaseStore, metaclass=SingletonMeta):
|
|
|
17
15
|
super().__init__("statestore")
|
|
18
16
|
self._log = getLogger(__name__)
|
|
19
17
|
self._schema = ResultStateStore
|
|
20
|
-
|
|
21
|
-
def clear(self) -> None:
|
|
22
|
-
"""Clear database."""
|
|
23
|
-
try:
|
|
24
|
-
# Clear the statestore database before each launch
|
|
25
|
-
self._db.delete(self._doc_id)
|
|
26
|
-
except (Conflict, NotFound):
|
|
27
|
-
self._log.debug("Statestore database will be created for the first time")
|
|
28
|
-
self._doc: dict = self._init_doc()
|
hardpy/pytest_hardpy/plugin.py
CHANGED
|
@@ -7,6 +7,7 @@ from logging import getLogger
|
|
|
7
7
|
from pathlib import Path, PurePath
|
|
8
8
|
from platform import system
|
|
9
9
|
from re import compile as re_compile
|
|
10
|
+
from time import sleep
|
|
10
11
|
from typing import Any, Callable
|
|
11
12
|
|
|
12
13
|
from _pytest._code.code import (
|
|
@@ -25,7 +26,7 @@ from pytest import (
|
|
|
25
26
|
Parser,
|
|
26
27
|
Session,
|
|
27
28
|
TestReport,
|
|
28
|
-
exit,
|
|
29
|
+
exit, # noqa: A004
|
|
29
30
|
fixture,
|
|
30
31
|
skip,
|
|
31
32
|
)
|
|
@@ -63,7 +64,7 @@ def pytest_addoption(parser: Parser) -> None:
|
|
|
63
64
|
)
|
|
64
65
|
parser.addoption(
|
|
65
66
|
"--hardpy-clear-database",
|
|
66
|
-
action="
|
|
67
|
+
action="store_true",
|
|
67
68
|
default=False,
|
|
68
69
|
help="clear hardpy local database",
|
|
69
70
|
)
|
|
@@ -116,7 +117,6 @@ class HardpyPlugin:
|
|
|
116
117
|
con_data.database_url = str(database_url) # type: ignore
|
|
117
118
|
|
|
118
119
|
is_clear_database = config.getoption("--hardpy-clear-database")
|
|
119
|
-
is_clear_statestore = is_clear_database == str(True)
|
|
120
120
|
|
|
121
121
|
socket_port = config.getoption("--hardpy-sp")
|
|
122
122
|
if socket_port:
|
|
@@ -133,7 +133,7 @@ class HardpyPlugin:
|
|
|
133
133
|
|
|
134
134
|
# must be init after config data is set
|
|
135
135
|
try:
|
|
136
|
-
self._reporter = HookReporter(
|
|
136
|
+
self._reporter = HookReporter(bool(is_clear_database))
|
|
137
137
|
except RuntimeError as exc:
|
|
138
138
|
exit(str(exc), 1)
|
|
139
139
|
|
|
@@ -215,21 +215,24 @@ class HardpyPlugin:
|
|
|
215
215
|
|
|
216
216
|
node_info = NodeInfo(item)
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
status = TestStatus.RUN
|
|
219
|
+
is_skip_test = self._is_skip_test(node_info)
|
|
220
|
+
if not is_skip_test:
|
|
221
|
+
self._reporter.set_module_start_time(node_info.module_id)
|
|
222
|
+
self._reporter.set_case_start_time(node_info.module_id, node_info.case_id)
|
|
223
|
+
else:
|
|
224
|
+
status = TestStatus.SKIPPED
|
|
225
|
+
self._results[node_info.module_id][node_info.case_id] = status
|
|
226
|
+
progress = self._progress.calculate(item.nodeid)
|
|
227
|
+
self._reporter.set_progress(progress)
|
|
219
228
|
|
|
220
|
-
self._reporter.set_module_status(node_info.module_id,
|
|
221
|
-
self._reporter.
|
|
222
|
-
self._reporter.set_case_status(
|
|
223
|
-
node_info.module_id,
|
|
224
|
-
node_info.case_id,
|
|
225
|
-
TestStatus.RUN,
|
|
226
|
-
)
|
|
227
|
-
self._reporter.set_case_start_time(
|
|
228
|
-
node_info.module_id,
|
|
229
|
-
node_info.case_id,
|
|
230
|
-
)
|
|
229
|
+
self._reporter.set_module_status(node_info.module_id, status)
|
|
230
|
+
self._reporter.set_case_status(node_info.module_id, node_info.case_id, status)
|
|
231
231
|
self._reporter.update_db_by_doc()
|
|
232
232
|
|
|
233
|
+
if is_skip_test:
|
|
234
|
+
skip(f"Test {item.nodeid} is skipped")
|
|
235
|
+
|
|
233
236
|
def pytest_runtest_call(self, item: Item) -> None:
|
|
234
237
|
"""Call the test item."""
|
|
235
238
|
node_info = NodeInfo(item)
|
|
@@ -242,7 +245,7 @@ class HardpyPlugin:
|
|
|
242
245
|
|
|
243
246
|
def pytest_runtest_makereport(self, item: Item, call: CallInfo) -> None:
|
|
244
247
|
"""Call after call of each test item."""
|
|
245
|
-
if call.when != "call":
|
|
248
|
+
if call.when != "call" or not call.excinfo:
|
|
246
249
|
return
|
|
247
250
|
|
|
248
251
|
node_info = NodeInfo(item)
|
|
@@ -250,25 +253,25 @@ class HardpyPlugin:
|
|
|
250
253
|
module_id = node_info.module_id
|
|
251
254
|
case_id = node_info.case_id
|
|
252
255
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
256
|
+
# first attempt was in pytest_runtest_call
|
|
257
|
+
for current_attempt in range(2, attempt + 1):
|
|
258
|
+
# add pause between attempts to verify STOP condition
|
|
259
|
+
sleep(1)
|
|
260
|
+
|
|
261
|
+
self._reporter.set_module_status(module_id, TestStatus.RUN)
|
|
262
|
+
self._reporter.set_case_status(module_id, case_id, TestStatus.RUN)
|
|
263
|
+
self._reporter.set_case_attempt(module_id, case_id, current_attempt)
|
|
264
|
+
self._reporter.update_db_by_doc()
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
item.runtest()
|
|
268
|
+
call.excinfo = None
|
|
269
|
+
self._reporter.set_case_status(module_id, case_id, TestStatus.PASSED)
|
|
270
|
+
break
|
|
271
|
+
except AssertionError:
|
|
272
|
+
self._reporter.set_case_status(module_id, case_id, TestStatus.FAILED)
|
|
273
|
+
if current_attempt == attempt:
|
|
274
|
+
return
|
|
272
275
|
|
|
273
276
|
# Reporting hooks
|
|
274
277
|
|
|
@@ -422,36 +425,25 @@ class HardpyPlugin:
|
|
|
422
425
|
case _:
|
|
423
426
|
return None
|
|
424
427
|
|
|
425
|
-
def
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
node_info.case_id,
|
|
430
|
-
),
|
|
428
|
+
def _is_skip_test(self, node_info: NodeInfo) -> bool:
|
|
429
|
+
"""Is need to skip a test because it depends on another test."""
|
|
430
|
+
is_dependency_test_exist = self._dependencies.get(
|
|
431
|
+
TestDependencyInfo(node_info.module_id, node_info.case_id),
|
|
431
432
|
)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
self._progress.calculate(f"{node_info.module_id}::{node_info.case_id}"),
|
|
437
|
-
)
|
|
438
|
-
skip(f"Test {node_info.module_id}::{node_info.case_id} is skipped")
|
|
439
|
-
|
|
440
|
-
def _is_dependency_failed(self, dependency: TestDependencyInfo) -> bool:
|
|
441
|
-
if isinstance(dependency, TestDependencyInfo):
|
|
442
|
-
incorrect_status = {
|
|
443
|
-
TestStatus.FAILED,
|
|
444
|
-
TestStatus.SKIPPED,
|
|
445
|
-
TestStatus.ERROR,
|
|
446
|
-
}
|
|
447
|
-
module_id, case_id = dependency
|
|
433
|
+
is_dependency_test_failed = False
|
|
434
|
+
if is_dependency_test_exist:
|
|
435
|
+
wrong_status = {TestStatus.FAILED, TestStatus.SKIPPED, TestStatus.ERROR}
|
|
436
|
+
module_id, case_id = is_dependency_test_exist
|
|
448
437
|
if case_id is not None:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
438
|
+
is_dependency_test_failed = (
|
|
439
|
+
self._results[module_id][case_id] in wrong_status
|
|
440
|
+
)
|
|
441
|
+
else:
|
|
442
|
+
result_set = set(self._results[module_id].values())
|
|
443
|
+
is_dependency_test_failed = any(
|
|
444
|
+
status in wrong_status for status in result_set
|
|
445
|
+
)
|
|
446
|
+
return bool(is_dependency_test_exist and is_dependency_test_failed)
|
|
455
447
|
|
|
456
448
|
def _add_dependency(self, node_info: NodeInfo, nodes: dict) -> None:
|
|
457
449
|
dependency = node_info.dependency
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import socket
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from os import environ
|
|
8
|
-
from
|
|
8
|
+
from select import select
|
|
9
9
|
from typing import Any
|
|
10
10
|
from uuid import uuid4
|
|
11
11
|
|
|
@@ -25,6 +25,7 @@ from hardpy.pytest_hardpy.utils import (
|
|
|
25
25
|
DuplicateSerialNumberError,
|
|
26
26
|
DuplicateTestStandLocationError,
|
|
27
27
|
DuplicateTestStandNameError,
|
|
28
|
+
ImageComponent,
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
|
|
@@ -79,7 +80,10 @@ def set_dut_serial_number(serial_number: str) -> None:
|
|
|
79
80
|
key = reporter.generate_key(DF.DUT, DF.SERIAL_NUMBER)
|
|
80
81
|
if reporter.get_field(key):
|
|
81
82
|
raise DuplicateSerialNumberError
|
|
82
|
-
reporter.set_doc_value(
|
|
83
|
+
reporter.set_doc_value(
|
|
84
|
+
key,
|
|
85
|
+
serial_number if isinstance(serial_number, str) else str(serial_number),
|
|
86
|
+
)
|
|
83
87
|
reporter.update_db_by_doc()
|
|
84
88
|
|
|
85
89
|
|
|
@@ -274,6 +278,7 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
|
274
278
|
- title_bar (str | None): The title bar of the dialog box.
|
|
275
279
|
If the title_bar field is missing, it is the case name.
|
|
276
280
|
- widget (DialogBoxWidget | None): Widget information.
|
|
281
|
+
- image (ImageComponent | None): Image information.
|
|
277
282
|
|
|
278
283
|
Returns:
|
|
279
284
|
Any: An object containing the user's response.
|
|
@@ -285,7 +290,6 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
|
285
290
|
- NUMERIC_INPUT: float.
|
|
286
291
|
- RADIOBUTTON: str.
|
|
287
292
|
- CHECKBOX: list[str].
|
|
288
|
-
- IMAGE: bool.
|
|
289
293
|
- MULTISTEP: bool.
|
|
290
294
|
|
|
291
295
|
Raises:
|
|
@@ -303,52 +307,76 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
|
303
307
|
current_test.case_id,
|
|
304
308
|
DF.DIALOG_BOX,
|
|
305
309
|
)
|
|
306
|
-
|
|
307
|
-
reporter.set_doc_value(key, {}, statestore_only=True)
|
|
308
|
-
reporter.update_db_by_doc()
|
|
309
|
-
debounce_time = 0.2
|
|
310
|
-
sleep(debounce_time)
|
|
310
|
+
_cleanup_widget(reporter, key)
|
|
311
311
|
|
|
312
312
|
reporter.set_doc_value(key, dialog_box_data.to_dict(), statestore_only=True)
|
|
313
313
|
reporter.update_db_by_doc()
|
|
314
314
|
|
|
315
|
+
# get socket data
|
|
315
316
|
input_dbx_data = _get_socket_raw_data()
|
|
316
317
|
|
|
317
|
-
|
|
318
|
-
reporter.set_doc_value(key, {}, statestore_only=True)
|
|
319
|
-
reporter.update_db_by_doc()
|
|
318
|
+
_cleanup_widget(reporter, key)
|
|
320
319
|
return dialog_box_data.widget.convert_data(input_dbx_data)
|
|
321
320
|
|
|
322
321
|
|
|
323
|
-
def set_operator_message(
|
|
322
|
+
def set_operator_message(
|
|
323
|
+
msg: str,
|
|
324
|
+
title: str | None = None,
|
|
325
|
+
block: bool = True,
|
|
326
|
+
image: ImageComponent | None = None,
|
|
327
|
+
font_size: int = 14,
|
|
328
|
+
) -> None:
|
|
324
329
|
"""Set operator message.
|
|
325
330
|
|
|
326
331
|
The function should be used to handle events outside of testing.
|
|
327
332
|
For messages to the operator during testing, there is the function `run_dialog_box`.
|
|
328
333
|
|
|
329
334
|
Args:
|
|
330
|
-
msg (str):
|
|
331
|
-
title (str | None):
|
|
335
|
+
msg (str): message
|
|
336
|
+
title (str | None): title
|
|
337
|
+
image (ImageComponent | None): operator message info
|
|
338
|
+
block (bool): if True, the function will block until the message is closed
|
|
339
|
+
font_size (int): font size
|
|
332
340
|
"""
|
|
333
341
|
reporter = RunnerReporter()
|
|
334
342
|
key = reporter.generate_key(DF.OPERATOR_MSG)
|
|
343
|
+
_cleanup_widget(reporter, key)
|
|
335
344
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
sleep(debounce_time)
|
|
345
|
+
if font_size < 1:
|
|
346
|
+
msg = "The 'font_size' argument cannot be less than 1"
|
|
347
|
+
raise ValueError(msg)
|
|
340
348
|
|
|
341
|
-
msg_data = {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
349
|
+
msg_data = {
|
|
350
|
+
DF.MSG: msg,
|
|
351
|
+
DF.TITLE: title,
|
|
352
|
+
DF.VISIBLE: True,
|
|
353
|
+
DF.IMAGE: image.to_dict() if image else None,
|
|
354
|
+
DF.ID: str(uuid4()),
|
|
355
|
+
DF.FONT_SIZE: int(font_size),
|
|
356
|
+
}
|
|
346
357
|
reporter.set_doc_value(key, msg_data, statestore_only=True)
|
|
347
358
|
reporter.update_db_by_doc()
|
|
348
359
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
360
|
+
if block:
|
|
361
|
+
# get socket data
|
|
362
|
+
is_msg_visible = _get_socket_raw_data()
|
|
363
|
+
|
|
364
|
+
msg_data[DF.VISIBLE] = is_msg_visible
|
|
365
|
+
reporter.set_doc_value(key, msg_data, statestore_only=True)
|
|
366
|
+
reporter.update_db_by_doc()
|
|
367
|
+
|
|
368
|
+
_cleanup_widget(reporter, key)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def clear_operator_message() -> None:
|
|
372
|
+
"""Clear operator message.
|
|
373
|
+
|
|
374
|
+
Clears the current message to the operator if it exists, otherwise does nothing.
|
|
375
|
+
"""
|
|
376
|
+
reporter = RunnerReporter()
|
|
377
|
+
key = reporter.generate_key(DF.OPERATOR_MSG)
|
|
378
|
+
|
|
379
|
+
_cleanup_widget(reporter, key)
|
|
352
380
|
|
|
353
381
|
|
|
354
382
|
def get_current_attempt() -> int:
|
|
@@ -388,25 +416,37 @@ def _get_current_test() -> CurrentTestInfo:
|
|
|
388
416
|
|
|
389
417
|
def _get_socket_raw_data() -> str:
|
|
390
418
|
# create socket connection
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
419
|
+
with socket.socket() as server:
|
|
420
|
+
server.setblocking(False)
|
|
421
|
+
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
422
|
+
|
|
423
|
+
con_data = ConnectionData()
|
|
424
|
+
try:
|
|
425
|
+
server.bind((con_data.socket_host, con_data.socket_port))
|
|
426
|
+
except OSError as exc:
|
|
427
|
+
msg = "Socket creating error"
|
|
428
|
+
server.close()
|
|
429
|
+
raise RuntimeError(msg) from exc
|
|
430
|
+
server.listen(1)
|
|
431
|
+
|
|
432
|
+
client = None
|
|
433
|
+
while True:
|
|
434
|
+
ready_to_read, _, _ = select([server], [], [], 0.5)
|
|
435
|
+
if ready_to_read:
|
|
436
|
+
client, _ = server.accept()
|
|
437
|
+
break
|
|
438
|
+
|
|
439
|
+
# receive data
|
|
440
|
+
max_input_data_len = 1024
|
|
441
|
+
socket_data = client.recv(max_input_data_len).decode("utf-8")
|
|
442
|
+
|
|
443
|
+
# close connection
|
|
444
|
+
client.close()
|
|
399
445
|
server.close()
|
|
400
|
-
raise RuntimeError(msg) from exc
|
|
401
|
-
server.listen(1)
|
|
402
|
-
client, _ = server.accept()
|
|
403
446
|
|
|
404
|
-
|
|
405
|
-
max_input_data_len = 1024
|
|
406
|
-
socket_data = client.recv(max_input_data_len).decode("utf-8")
|
|
447
|
+
return socket_data
|
|
407
448
|
|
|
408
|
-
# close connection
|
|
409
|
-
client.close()
|
|
410
|
-
server.close()
|
|
411
449
|
|
|
412
|
-
|
|
450
|
+
def _cleanup_widget(reporter: RunnerReporter, key: str) -> None:
|
|
451
|
+
reporter.set_doc_value(key, {}, statestore_only=True)
|
|
452
|
+
reporter.update_db_by_doc()
|
|
@@ -116,7 +116,6 @@ class PyTestWrapper:
|
|
|
116
116
|
|
|
117
117
|
if is_clear_database:
|
|
118
118
|
args.append("--hardpy-clear-database")
|
|
119
|
-
args.append(str(is_clear_database))
|
|
120
119
|
|
|
121
120
|
subprocess.Popen( # noqa: S603
|
|
122
121
|
[self.python_executable, *args],
|
|
@@ -135,10 +134,10 @@ class PyTestWrapper:
|
|
|
135
134
|
bool: True if dialog box was confirmed/closed, else False
|
|
136
135
|
"""
|
|
137
136
|
try:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
with socket() as client:
|
|
138
|
+
client.connect((self.config.socket.host, self.config.socket.port))
|
|
139
|
+
client.sendall(data.encode("utf-8"))
|
|
140
|
+
client.close()
|
|
142
141
|
except Exception: # noqa: BLE001
|
|
143
142
|
return False
|
|
144
143
|
return True
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from copy import deepcopy
|
|
6
|
-
from datetime import datetime
|
|
7
6
|
from logging import getLogger
|
|
8
7
|
from time import time
|
|
9
8
|
|
|
10
9
|
from natsort import natsorted
|
|
10
|
+
from tzlocal import get_localzone
|
|
11
11
|
|
|
12
12
|
from hardpy.pytest_hardpy.db import DatabaseField as DF # noqa: N817
|
|
13
13
|
from hardpy.pytest_hardpy.reporter.base import BaseReporter
|
|
@@ -21,6 +21,7 @@ class HookReporter(BaseReporter):
|
|
|
21
21
|
super().__init__()
|
|
22
22
|
if is_clear_database:
|
|
23
23
|
self._statestore.clear()
|
|
24
|
+
self._runstore.clear()
|
|
24
25
|
self._log = getLogger(__name__)
|
|
25
26
|
|
|
26
27
|
def init_doc(self, doc_name: str) -> None:
|
|
@@ -38,7 +39,7 @@ class HookReporter(BaseReporter):
|
|
|
38
39
|
self.set_doc_value(DF.OPERATOR_MSG, {}, statestore_only=True)
|
|
39
40
|
|
|
40
41
|
test_stand_tz = self.generate_key(DF.TEST_STAND, DF.TIMEZONE)
|
|
41
|
-
self.set_doc_value(test_stand_tz,
|
|
42
|
+
self.set_doc_value(test_stand_tz, str(get_localzone().key))
|
|
42
43
|
|
|
43
44
|
test_stand_id_key = self.generate_key(DF.TEST_STAND, DF.HW_ID)
|
|
44
45
|
self.set_doc_value(test_stand_id_key, machine_id())
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
from hardpy.pytest_hardpy.utils.connection_data import ConnectionData
|
|
5
5
|
from hardpy.pytest_hardpy.utils.const import TestStatus
|
|
6
6
|
from hardpy.pytest_hardpy.utils.dialog_box import (
|
|
7
|
+
BaseWidget,
|
|
7
8
|
CheckboxWidget,
|
|
8
9
|
DialogBox,
|
|
9
|
-
|
|
10
|
+
ImageComponent,
|
|
10
11
|
MultistepWidget,
|
|
11
12
|
NumericInputWidget,
|
|
12
13
|
RadiobuttonWidget,
|
|
@@ -18,6 +19,7 @@ from hardpy.pytest_hardpy.utils.exception import (
|
|
|
18
19
|
DuplicateSerialNumberError,
|
|
19
20
|
DuplicateTestStandLocationError,
|
|
20
21
|
DuplicateTestStandNameError,
|
|
22
|
+
ImageError,
|
|
21
23
|
WidgetInfoError,
|
|
22
24
|
)
|
|
23
25
|
from hardpy.pytest_hardpy.utils.machineid import machine_id
|
|
@@ -26,23 +28,25 @@ from hardpy.pytest_hardpy.utils.progress_calculator import ProgressCalculator
|
|
|
26
28
|
from hardpy.pytest_hardpy.utils.singleton import SingletonMeta
|
|
27
29
|
|
|
28
30
|
__all__ = [
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"TestStatus",
|
|
32
|
-
"SingletonMeta",
|
|
31
|
+
"BaseWidget",
|
|
32
|
+
"CheckboxWidget",
|
|
33
33
|
"ConnectionData",
|
|
34
|
-
"
|
|
34
|
+
"DialogBox",
|
|
35
35
|
"DuplicatePartNumberError",
|
|
36
|
+
"DuplicateSerialNumberError",
|
|
36
37
|
"DuplicateTestStandLocationError",
|
|
37
38
|
"DuplicateTestStandNameError",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"ImageComponent",
|
|
40
|
+
"ImageError",
|
|
41
|
+
"MultistepWidget",
|
|
42
|
+
"NodeInfo",
|
|
41
43
|
"NumericInputWidget",
|
|
42
|
-
"
|
|
44
|
+
"ProgressCalculator",
|
|
43
45
|
"RadiobuttonWidget",
|
|
44
|
-
"
|
|
45
|
-
"MultistepWidget",
|
|
46
|
+
"SingletonMeta",
|
|
46
47
|
"StepWidget",
|
|
48
|
+
"TestStatus",
|
|
49
|
+
"TextInputWidget",
|
|
50
|
+
"WidgetInfoError",
|
|
47
51
|
"machine_id",
|
|
48
52
|
]
|