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.
Files changed (32) hide show
  1. hardpy/__init__.py +27 -27
  2. hardpy/cli/cli.py +1 -2
  3. hardpy/common/config.py +8 -8
  4. hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
  5. hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  6. hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
  7. hardpy/hardpy_panel/frontend/dist/static/css/main.e8a862f1.css.map +1 -1
  8. hardpy/hardpy_panel/frontend/dist/static/js/main.403e9cd8.js +3 -0
  9. hardpy/hardpy_panel/frontend/dist/static/js/main.403e9cd8.js.map +1 -0
  10. hardpy/pytest_hardpy/db/__init__.py +1 -1
  11. hardpy/pytest_hardpy/db/base_connector.py +1 -1
  12. hardpy/pytest_hardpy/db/base_store.py +9 -0
  13. hardpy/pytest_hardpy/db/const.py +5 -0
  14. hardpy/pytest_hardpy/db/schema/v1.py +14 -7
  15. hardpy/pytest_hardpy/db/statestore.py +0 -11
  16. hardpy/pytest_hardpy/plugin.py +56 -64
  17. hardpy/pytest_hardpy/pytest_call.py +84 -44
  18. hardpy/pytest_hardpy/pytest_wrapper.py +4 -5
  19. hardpy/pytest_hardpy/reporter/__init__.py +1 -1
  20. hardpy/pytest_hardpy/reporter/hook_reporter.py +3 -2
  21. hardpy/pytest_hardpy/result/__init__.py +1 -1
  22. hardpy/pytest_hardpy/utils/__init__.py +16 -12
  23. hardpy/pytest_hardpy/utils/dialog_box.py +91 -54
  24. hardpy/pytest_hardpy/utils/exception.py +6 -0
  25. {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/METADATA +11 -8
  26. {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/RECORD +30 -29
  27. hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js +0 -3
  28. hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js.map +0 -1
  29. /hardpy/hardpy_panel/frontend/dist/static/js/{main.942e57d4.js.LICENSE.txt → main.403e9cd8.js.LICENSE.txt} +0 -0
  30. {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/WHEEL +0 -0
  31. {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/entry_points.txt +0 -0
  32. {hardpy-0.7.0.dist-info → hardpy-0.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,6 +12,6 @@ __all__ = [
12
12
  "DatabaseField",
13
13
  "ResultRunStore",
14
14
  "ResultStateStore",
15
- "StateStore",
16
15
  "RunStore",
16
+ "StateStore",
17
17
  ]
@@ -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)
@@ -29,3 +29,8 @@ class DatabaseField(str, Enum):
29
29
  ATTEMPT = "attempt"
30
30
  LOCATION = "location"
31
31
  HW_ID = "hw_id"
32
+ TITLE = "title"
33
+ VISIBLE = "visible"
34
+ IMAGE = "image"
35
+ ID = "id"
36
+ FONT_SIZE = "font_size"
@@ -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: TCH001
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": "CET",
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": "CET",
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": "True"
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": "CET",
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()
@@ -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="store",
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(is_clear_statestore)
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
- self._handle_dependency(node_info)
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, TestStatus.RUN)
221
- self._reporter.set_module_start_time(node_info.module_id)
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
- if call.excinfo:
254
- # first attempt was in pytest_runtest_call
255
- for current_attempt in range(2, attempt + 1):
256
- self._reporter.set_module_status(module_id, TestStatus.RUN)
257
- self._reporter.set_case_status(module_id, case_id, TestStatus.RUN)
258
- self._reporter.set_case_attempt(module_id, case_id, current_attempt)
259
- self._reporter.update_db_by_doc()
260
-
261
- # fmt: off
262
- try:
263
- item.runtest()
264
- call.excinfo = None
265
- self._reporter.set_case_status(module_id, case_id, TestStatus.PASSED) # noqa: E501
266
- break
267
- except AssertionError:
268
- self._reporter.set_case_status(module_id, case_id, TestStatus.FAILED) # noqa: E501
269
- if current_attempt == attempt:
270
- return
271
- # fmt: on
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 _handle_dependency(self, node_info: NodeInfo) -> None:
426
- dependency = self._dependencies.get(
427
- TestDependencyInfo(
428
- node_info.module_id,
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
- if dependency and self._is_dependency_failed(dependency):
433
- self._log.debug(f"Skipping test due to dependency: {dependency}")
434
- self._results[node_info.module_id][node_info.case_id] = TestStatus.SKIPPED
435
- self._reporter.set_progress(
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
- return self._results[module_id][case_id] in incorrect_status
450
- return any(
451
- status in incorrect_status
452
- for status in set(self._results[module_id].values())
453
- )
454
- return False
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 time import sleep
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(key, serial_number)
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
- # cleanup widget
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(msg: str, title: str | None = None) -> None:
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): Message
331
- title (str | None): Title
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
- reporter.set_doc_value(key, {}, statestore_only=True)
337
- reporter.update_db_by_doc()
338
- debounce_time = 0.2
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 = {"msg": msg, "title": title, "visible": True}
342
- reporter.set_doc_value(key, msg_data, statestore_only=True)
343
- reporter.update_db_by_doc()
344
- is_msg_visible = _get_socket_raw_data()
345
- msg_data["visible"] = is_msg_visible
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
- # cleanup widget
350
- reporter.set_doc_value(key, {}, statestore_only=True)
351
- reporter.update_db_by_doc()
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
- server = socket.socket()
392
- server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
393
- con_data = ConnectionData()
394
-
395
- try:
396
- server.bind((con_data.socket_host, con_data.socket_port))
397
- except OSError as exc:
398
- msg = "Socket creating error"
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
- # receive data
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
- return socket_data
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
- client = socket()
139
- client.connect((self.config.socket.host, self.config.socket.port))
140
- client.sendall(data.encode("utf-8"))
141
- client.close()
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
@@ -5,6 +5,6 @@ from hardpy.pytest_hardpy.reporter.hook_reporter import HookReporter
5
5
  from hardpy.pytest_hardpy.reporter.runner_reporter import RunnerReporter
6
6
 
7
7
  __all__ = [
8
- "RunnerReporter",
9
8
  "HookReporter",
9
+ "RunnerReporter",
10
10
  ]
@@ -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, datetime.now().astimezone().tzname())
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())
@@ -5,6 +5,6 @@ from hardpy.pytest_hardpy.result.report_loader.couchdb_loader import CouchdbLoad
5
5
  from hardpy.pytest_hardpy.result.report_reader.couchdb_reader import CouchdbReader
6
6
 
7
7
  __all__ = [
8
- "CouchdbReader",
9
8
  "CouchdbLoader",
9
+ "CouchdbReader",
10
10
  ]
@@ -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
- ImageWidget,
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
- "NodeInfo",
30
- "ProgressCalculator",
31
- "TestStatus",
32
- "SingletonMeta",
31
+ "BaseWidget",
32
+ "CheckboxWidget",
33
33
  "ConnectionData",
34
- "DuplicateSerialNumberError",
34
+ "DialogBox",
35
35
  "DuplicatePartNumberError",
36
+ "DuplicateSerialNumberError",
36
37
  "DuplicateTestStandLocationError",
37
38
  "DuplicateTestStandNameError",
38
- "WidgetInfoError",
39
- "DialogBox",
40
- "TextInputWidget",
39
+ "ImageComponent",
40
+ "ImageError",
41
+ "MultistepWidget",
42
+ "NodeInfo",
41
43
  "NumericInputWidget",
42
- "CheckboxWidget",
44
+ "ProgressCalculator",
43
45
  "RadiobuttonWidget",
44
- "ImageWidget",
45
- "MultistepWidget",
46
+ "SingletonMeta",
46
47
  "StepWidget",
48
+ "TestStatus",
49
+ "TextInputWidget",
50
+ "WidgetInfoError",
47
51
  "machine_id",
48
52
  ]