hardpy 0.13.0__py3-none-any.whl → 0.15.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 +45 -10
- hardpy/cli/cli.py +32 -28
- hardpy/common/config.py +5 -4
- hardpy/common/stand_cloud/connector.py +2 -2
- hardpy/hardpy_panel/api.py +13 -3
- hardpy/hardpy_panel/frontend/dist/assets/{allPaths-Cg7WZDXy.js → allPaths-CV5wjLMB.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-C79wUwqR.js → allPathsLoader-JIzW_pSb.js} +2 -2
- hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +2 -0
- hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +790 -0
- hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-hWuLTMwD.js → splitPathsBySizeLoader-DkZadBcn.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +60 -0
- hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +60 -0
- hardpy/pytest_hardpy/db/base_store.py +23 -0
- hardpy/pytest_hardpy/db/const.py +40 -19
- hardpy/pytest_hardpy/db/schema/v1.py +140 -326
- hardpy/pytest_hardpy/plugin.py +32 -1
- hardpy/pytest_hardpy/pytest_call.py +331 -22
- hardpy/pytest_hardpy/pytest_wrapper.py +25 -34
- hardpy/pytest_hardpy/reporter/hook_reporter.py +53 -2
- hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +8 -2
- hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +1 -5
- hardpy/pytest_hardpy/utils/__init__.py +25 -11
- hardpy/pytest_hardpy/utils/const.py +72 -0
- hardpy/pytest_hardpy/utils/exception.py +7 -32
- hardpy/pytest_hardpy/utils/node_info.py +55 -1
- hardpy/pytest_hardpy/utils/stand_type.py +198 -0
- {hardpy-0.13.0.dist-info → hardpy-0.15.0.dist-info}/METADATA +2 -1
- {hardpy-0.13.0.dist-info → hardpy-0.15.0.dist-info}/RECORD +37 -28
- hardpy/hardpy_panel/frontend/dist/assets/index-De5CJ3kt.js +0 -790
- {hardpy-0.13.0.dist-info → hardpy-0.15.0.dist-info}/WHEEL +0 -0
- {hardpy-0.13.0.dist-info → hardpy-0.15.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.13.0.dist-info → hardpy-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,7 +6,7 @@ from dataclasses import dataclass
|
|
|
6
6
|
from inspect import stack
|
|
7
7
|
from os import environ
|
|
8
8
|
from time import sleep
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
10
|
from uuid import uuid4
|
|
11
11
|
|
|
12
12
|
from pycouchdb.exceptions import NotFound
|
|
@@ -19,17 +19,22 @@ from hardpy.pytest_hardpy.db import (
|
|
|
19
19
|
)
|
|
20
20
|
from hardpy.pytest_hardpy.reporter import RunnerReporter
|
|
21
21
|
from hardpy.pytest_hardpy.utils import (
|
|
22
|
+
Chart,
|
|
22
23
|
DialogBox,
|
|
23
|
-
|
|
24
|
-
DuplicateSerialNumberError,
|
|
25
|
-
DuplicateTestStandLocationError,
|
|
26
|
-
DuplicateTestStandNameError,
|
|
27
|
-
DuplicateTestStandNumberError,
|
|
24
|
+
DuplicateParameterError,
|
|
28
25
|
HTMLComponent,
|
|
29
26
|
ImageComponent,
|
|
27
|
+
Instrument,
|
|
28
|
+
NumericMeasurement,
|
|
29
|
+
StringMeasurement,
|
|
30
|
+
SubUnit,
|
|
30
31
|
TestStandNumberError,
|
|
31
32
|
)
|
|
32
33
|
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from collections.abc import Mapping
|
|
36
|
+
from datetime import datetime
|
|
37
|
+
|
|
33
38
|
|
|
34
39
|
@dataclass
|
|
35
40
|
class CurrentTestInfo:
|
|
@@ -39,6 +44,42 @@ class CurrentTestInfo:
|
|
|
39
44
|
case_id: str
|
|
40
45
|
|
|
41
46
|
|
|
47
|
+
class ErrorCodeMeta(type):
|
|
48
|
+
"""Error code metaclass."""
|
|
49
|
+
|
|
50
|
+
def __call__(cls, code: int, message: str | None = None) -> str | None:
|
|
51
|
+
"""Add error code to document.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
code (int): non-negative error code.
|
|
55
|
+
message (str | None): error message.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str | None: error message
|
|
59
|
+
"""
|
|
60
|
+
if code < 0:
|
|
61
|
+
msg = "error code must be greater than 0"
|
|
62
|
+
raise ValueError(msg)
|
|
63
|
+
reporter = RunnerReporter()
|
|
64
|
+
key = reporter.generate_key(DF.ERROR_CODE)
|
|
65
|
+
if reporter.get_field(key) is None:
|
|
66
|
+
reporter.set_doc_value(key, code)
|
|
67
|
+
reporter.update_db_by_doc()
|
|
68
|
+
if message: # noqa: RET503
|
|
69
|
+
return message
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ErrorCode(metaclass=ErrorCodeMeta):
|
|
73
|
+
"""Save error code and return error message.
|
|
74
|
+
|
|
75
|
+
It must be called from an assert.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
code (int): error code.
|
|
79
|
+
message (str): error message.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
|
|
42
83
|
def get_current_report() -> ResultRunStore | None:
|
|
43
84
|
"""Get current report from runstore database.
|
|
44
85
|
|
|
@@ -56,11 +97,68 @@ def get_current_report() -> ResultRunStore | None:
|
|
|
56
97
|
return None
|
|
57
98
|
|
|
58
99
|
|
|
59
|
-
def
|
|
60
|
-
"""
|
|
100
|
+
def set_user_name(name: str) -> None:
|
|
101
|
+
"""Set operator panel user name.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
name (str): user name
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
DuplicateParameterError: if user name is already set
|
|
108
|
+
"""
|
|
109
|
+
reporter = RunnerReporter()
|
|
110
|
+
key = reporter.generate_key(DF.USER)
|
|
111
|
+
if reporter.get_field(key):
|
|
112
|
+
msg = "user_name"
|
|
113
|
+
raise DuplicateParameterError(msg)
|
|
114
|
+
reporter.set_doc_value(key, name)
|
|
115
|
+
reporter.update_db_by_doc()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def set_batch_serial_number(serial_number: str) -> None:
|
|
119
|
+
"""Add batch serial number to document.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
serial_number (str): batch serial number
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
DuplicateParameterError: if batch serial number is already set
|
|
126
|
+
"""
|
|
127
|
+
reporter = RunnerReporter()
|
|
128
|
+
key = reporter.generate_key(DF.BATCH_SN)
|
|
129
|
+
if reporter.get_field(key):
|
|
130
|
+
msg = "batch_serial_number"
|
|
131
|
+
raise DuplicateParameterError(msg)
|
|
132
|
+
reporter.set_doc_value(key, serial_number)
|
|
133
|
+
reporter.update_db_by_doc()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def set_dut_sub_unit(sub_unit: SubUnit) -> int:
|
|
137
|
+
"""Add sub unit to DUT sub units list.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
sub_unit (SubUnit): Sub unit object of DUT.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
int: sub unit index
|
|
144
|
+
"""
|
|
145
|
+
reporter = RunnerReporter()
|
|
146
|
+
key = reporter.generate_key(DF.DUT, DF.SUB_UNITS)
|
|
147
|
+
|
|
148
|
+
sub_units = reporter.get_field(key) or []
|
|
149
|
+
sub_units.append({k: v for k, v in vars(sub_unit).items() if v is not None})
|
|
150
|
+
|
|
151
|
+
reporter.set_doc_value(key, sub_units)
|
|
152
|
+
reporter.update_db_by_doc()
|
|
153
|
+
|
|
154
|
+
return len(sub_units) - 1
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def set_dut_info(info: Mapping[str, str | int | float | datetime]) -> None:
|
|
158
|
+
"""Set DUT info to document.
|
|
61
159
|
|
|
62
160
|
Args:
|
|
63
|
-
info (
|
|
161
|
+
info (Mapping): DUT info
|
|
64
162
|
"""
|
|
65
163
|
reporter = RunnerReporter()
|
|
66
164
|
for dut_key, dut_value in info.items():
|
|
@@ -76,12 +174,13 @@ def set_dut_serial_number(serial_number: str) -> None:
|
|
|
76
174
|
serial_number (str): DUT serial number
|
|
77
175
|
|
|
78
176
|
Raises:
|
|
79
|
-
|
|
177
|
+
DuplicateParameterError: if serial number is already set
|
|
80
178
|
"""
|
|
81
179
|
reporter = RunnerReporter()
|
|
82
180
|
key = reporter.generate_key(DF.DUT, DF.SERIAL_NUMBER)
|
|
83
181
|
if reporter.get_field(key):
|
|
84
|
-
|
|
182
|
+
msg = "dut_serial_number"
|
|
183
|
+
raise DuplicateParameterError(msg)
|
|
85
184
|
reporter.set_doc_value(
|
|
86
185
|
key,
|
|
87
186
|
serial_number if isinstance(serial_number, str) else str(serial_number),
|
|
@@ -96,16 +195,71 @@ def set_dut_part_number(part_number: str) -> None:
|
|
|
96
195
|
part_number (str): DUT part number
|
|
97
196
|
|
|
98
197
|
Raises:
|
|
99
|
-
|
|
198
|
+
DuplicateParameterError: if part number is already set
|
|
100
199
|
"""
|
|
101
200
|
reporter = RunnerReporter()
|
|
102
201
|
key = reporter.generate_key(DF.DUT, DF.PART_NUMBER)
|
|
103
202
|
if reporter.get_field(key):
|
|
104
|
-
|
|
203
|
+
msg = "dut_part_number"
|
|
204
|
+
raise DuplicateParameterError(msg)
|
|
105
205
|
reporter.set_doc_value(key, part_number)
|
|
106
206
|
reporter.update_db_by_doc()
|
|
107
207
|
|
|
108
208
|
|
|
209
|
+
def set_dut_name(name: str) -> None:
|
|
210
|
+
"""Set DUT name to document.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
name (str): DUT name
|
|
214
|
+
|
|
215
|
+
Raises:
|
|
216
|
+
DuplicateParameterError: if DUT name is already set
|
|
217
|
+
"""
|
|
218
|
+
reporter = RunnerReporter()
|
|
219
|
+
key = reporter.generate_key(DF.DUT, DF.NAME)
|
|
220
|
+
if reporter.get_field(key):
|
|
221
|
+
msg = "dut_name"
|
|
222
|
+
raise DuplicateParameterError(msg)
|
|
223
|
+
reporter.set_doc_value(key, name)
|
|
224
|
+
reporter.update_db_by_doc()
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def set_dut_type(dut_type: str) -> None:
|
|
228
|
+
"""Set DUT type to document.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
dut_type (str): DUT type
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
DuplicateParameterError: if DUT type is already set
|
|
235
|
+
"""
|
|
236
|
+
reporter = RunnerReporter()
|
|
237
|
+
key = reporter.generate_key(DF.DUT, DF.TYPE)
|
|
238
|
+
if reporter.get_field(key):
|
|
239
|
+
msg = "dut_type"
|
|
240
|
+
raise DuplicateParameterError(msg)
|
|
241
|
+
reporter.set_doc_value(key, dut_type)
|
|
242
|
+
reporter.update_db_by_doc()
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def set_dut_revision(revision: str) -> None:
|
|
246
|
+
"""Set DUT revision to document.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
revision (str): DUT revision
|
|
250
|
+
|
|
251
|
+
Raises:
|
|
252
|
+
DuplicateParameterError: if DUT revision is already set
|
|
253
|
+
"""
|
|
254
|
+
reporter = RunnerReporter()
|
|
255
|
+
key = reporter.generate_key(DF.DUT, DF.REVISION)
|
|
256
|
+
if reporter.get_field(key):
|
|
257
|
+
msg = "dut_revision"
|
|
258
|
+
raise DuplicateParameterError(msg)
|
|
259
|
+
reporter.set_doc_value(key, revision)
|
|
260
|
+
reporter.update_db_by_doc()
|
|
261
|
+
|
|
262
|
+
|
|
109
263
|
def set_stand_name(name: str) -> None:
|
|
110
264
|
"""Add test stand name to document.
|
|
111
265
|
|
|
@@ -113,22 +267,24 @@ def set_stand_name(name: str) -> None:
|
|
|
113
267
|
name (str): test stand name
|
|
114
268
|
|
|
115
269
|
Raises:
|
|
116
|
-
|
|
270
|
+
DuplicateParameterError: if test stand name is already set
|
|
117
271
|
"""
|
|
118
272
|
reporter = RunnerReporter()
|
|
119
273
|
key = reporter.generate_key(DF.TEST_STAND, DF.NAME)
|
|
120
274
|
if reporter.get_field(key):
|
|
121
|
-
|
|
275
|
+
msg = "stand_name"
|
|
276
|
+
raise DuplicateParameterError(msg)
|
|
122
277
|
reporter.set_doc_value(key, name)
|
|
123
278
|
reporter.update_db_by_doc()
|
|
124
279
|
|
|
125
280
|
|
|
126
|
-
def set_stand_info(info:
|
|
281
|
+
def set_stand_info(info: Mapping[str, str | int | float | datetime]) -> None:
|
|
127
282
|
"""Add test stand info to document.
|
|
128
283
|
|
|
129
284
|
Args:
|
|
130
|
-
info (
|
|
131
|
-
|
|
285
|
+
info (Mapping[str, str | int | float | datetime]): test stand info as a mapping
|
|
286
|
+
where keys are strings and values can be strings, integers, floats or datetime objects
|
|
287
|
+
""" # noqa: E501
|
|
132
288
|
reporter = RunnerReporter()
|
|
133
289
|
for stand_key, stand_value in info.items():
|
|
134
290
|
key = reporter.generate_key(DF.TEST_STAND, DF.INFO, stand_key)
|
|
@@ -143,12 +299,13 @@ def set_stand_location(location: str) -> None:
|
|
|
143
299
|
location (str): test stand location
|
|
144
300
|
|
|
145
301
|
Raises:
|
|
146
|
-
|
|
302
|
+
DuplicateParameterError: if test stand location is already set
|
|
147
303
|
"""
|
|
148
304
|
reporter = RunnerReporter()
|
|
149
305
|
key = reporter.generate_key(DF.TEST_STAND, DF.LOCATION)
|
|
150
306
|
if reporter.get_field(key):
|
|
151
|
-
|
|
307
|
+
msg = "stand_location"
|
|
308
|
+
raise DuplicateParameterError(msg)
|
|
152
309
|
reporter.set_doc_value(key, location)
|
|
153
310
|
reporter.update_db_by_doc()
|
|
154
311
|
|
|
@@ -160,7 +317,7 @@ def set_stand_number(number: int) -> None:
|
|
|
160
317
|
number (int): test stand number (non negative integer)
|
|
161
318
|
|
|
162
319
|
Raises:
|
|
163
|
-
|
|
320
|
+
DuplicateParameterError: if stand number is already set
|
|
164
321
|
TestStandNumberError: if stand number is incorrect (negative or non integer)
|
|
165
322
|
"""
|
|
166
323
|
reporter = RunnerReporter()
|
|
@@ -168,11 +325,30 @@ def set_stand_number(number: int) -> None:
|
|
|
168
325
|
if not isinstance(number, int) or number < 0:
|
|
169
326
|
raise TestStandNumberError
|
|
170
327
|
if reporter.get_field(key):
|
|
171
|
-
|
|
328
|
+
msg = "stand_number"
|
|
329
|
+
raise DuplicateParameterError(msg)
|
|
172
330
|
reporter.set_doc_value(key, number)
|
|
173
331
|
reporter.update_db_by_doc()
|
|
174
332
|
|
|
175
333
|
|
|
334
|
+
def set_stand_revision(revision: str) -> None:
|
|
335
|
+
"""Add test stand revision to document.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
revision (str): test stand revision
|
|
339
|
+
|
|
340
|
+
Raises:
|
|
341
|
+
DuplicateParameterError: if test stand revision is already set
|
|
342
|
+
"""
|
|
343
|
+
reporter = RunnerReporter()
|
|
344
|
+
key = reporter.generate_key(DF.TEST_STAND, DF.REVISION)
|
|
345
|
+
if reporter.get_field(key):
|
|
346
|
+
msg = "stand_revision"
|
|
347
|
+
raise DuplicateParameterError(msg)
|
|
348
|
+
reporter.set_doc_value(key, revision)
|
|
349
|
+
reporter.update_db_by_doc()
|
|
350
|
+
|
|
351
|
+
|
|
176
352
|
def set_message(msg: str, msg_key: str | None = None) -> None:
|
|
177
353
|
"""Add or update message in current test.
|
|
178
354
|
|
|
@@ -291,6 +467,135 @@ def set_driver_info(drivers: dict) -> None:
|
|
|
291
467
|
reporter.update_db_by_doc()
|
|
292
468
|
|
|
293
469
|
|
|
470
|
+
def set_instrument(instrument: Instrument) -> int:
|
|
471
|
+
"""Add instrument to test stand instruments list.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
instrument (Instrument): Instrument object containing all instrument data
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
int: instrument index
|
|
478
|
+
"""
|
|
479
|
+
reporter = RunnerReporter()
|
|
480
|
+
key = reporter.generate_key(DF.TEST_STAND, DF.INSTRUMENTS)
|
|
481
|
+
|
|
482
|
+
instruments = reporter.get_field(key) or []
|
|
483
|
+
instruments.append({k: v for k, v in vars(instrument).items() if v is not None})
|
|
484
|
+
|
|
485
|
+
reporter.set_doc_value(key, instruments)
|
|
486
|
+
reporter.update_db_by_doc()
|
|
487
|
+
|
|
488
|
+
return len(instruments) - 1
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def set_process_name(name: str) -> None:
|
|
492
|
+
"""Set process name to document.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
name (str): process name
|
|
496
|
+
|
|
497
|
+
Raises:
|
|
498
|
+
DuplicateParameterError: if process name is already set
|
|
499
|
+
"""
|
|
500
|
+
reporter = RunnerReporter()
|
|
501
|
+
key = reporter.generate_key(DF.PROCESS, DF.NAME)
|
|
502
|
+
if reporter.get_field(key):
|
|
503
|
+
msg = "process_name"
|
|
504
|
+
raise DuplicateParameterError(msg)
|
|
505
|
+
reporter.set_doc_value(key, name)
|
|
506
|
+
reporter.update_db_by_doc()
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def set_process_number(number: int) -> None:
|
|
510
|
+
"""Set process number to document.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
number (int): process number
|
|
514
|
+
|
|
515
|
+
Raises:
|
|
516
|
+
DuplicateParameterError: if process number is already set
|
|
517
|
+
"""
|
|
518
|
+
reporter = RunnerReporter()
|
|
519
|
+
key = reporter.generate_key(DF.PROCESS, DF.NUMBER)
|
|
520
|
+
if reporter.get_field(key):
|
|
521
|
+
msg = "process_number"
|
|
522
|
+
raise DuplicateParameterError(msg)
|
|
523
|
+
reporter.set_doc_value(key, number)
|
|
524
|
+
reporter.update_db_by_doc()
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def set_process_info(info: Mapping[str, str | int | float | datetime]) -> None:
|
|
528
|
+
"""Set process info to document.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
info (Mapping): process info
|
|
532
|
+
"""
|
|
533
|
+
reporter = RunnerReporter()
|
|
534
|
+
for key, value in info.items():
|
|
535
|
+
full_key = reporter.generate_key(DF.PROCESS, DF.INFO, key)
|
|
536
|
+
reporter.set_doc_value(full_key, value)
|
|
537
|
+
reporter.update_db_by_doc()
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def set_case_measurement(measurement: NumericMeasurement | StringMeasurement) -> int:
|
|
541
|
+
"""Add measurement to document.
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
measurement (NumericMeasurement | StringMeasurement): measurement object
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
int: measurement index from measurements list
|
|
548
|
+
"""
|
|
549
|
+
current_test = _get_current_test()
|
|
550
|
+
reporter = RunnerReporter()
|
|
551
|
+
|
|
552
|
+
key = reporter.generate_key(
|
|
553
|
+
DF.MODULES,
|
|
554
|
+
current_test.module_id,
|
|
555
|
+
DF.CASES,
|
|
556
|
+
current_test.case_id,
|
|
557
|
+
DF.MEASUREMENTS,
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
measurements = reporter.get_field(key) or []
|
|
561
|
+
measurements.append({k: v for k, v in vars(measurement).items() if v is not None})
|
|
562
|
+
|
|
563
|
+
reporter.set_doc_value(key, measurements)
|
|
564
|
+
reporter.update_db_by_doc()
|
|
565
|
+
|
|
566
|
+
return len(measurements) - 1
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def set_case_chart(chart: Chart) -> None:
|
|
570
|
+
"""Add chart to document.
|
|
571
|
+
|
|
572
|
+
Args:
|
|
573
|
+
chart (Chart): chart object
|
|
574
|
+
"""
|
|
575
|
+
if not chart.x_data or not chart.y_data:
|
|
576
|
+
msg = "x_data and y_data must be set"
|
|
577
|
+
raise ValueError(msg)
|
|
578
|
+
current_test = _get_current_test()
|
|
579
|
+
reporter = RunnerReporter()
|
|
580
|
+
|
|
581
|
+
key = reporter.generate_key(
|
|
582
|
+
DF.MODULES,
|
|
583
|
+
current_test.module_id,
|
|
584
|
+
DF.CASES,
|
|
585
|
+
current_test.case_id,
|
|
586
|
+
DF.CHART,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
if reporter.get_field(key):
|
|
590
|
+
msg = "chart"
|
|
591
|
+
raise DuplicateParameterError(msg)
|
|
592
|
+
|
|
593
|
+
chart_dict = {k: v for k, v in vars(chart).items() if v is not None}
|
|
594
|
+
|
|
595
|
+
reporter.set_doc_value(key, chart_dict)
|
|
596
|
+
reporter.update_db_by_doc()
|
|
597
|
+
|
|
598
|
+
|
|
294
599
|
def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
295
600
|
"""Display a dialog box.
|
|
296
601
|
|
|
@@ -373,6 +678,10 @@ def set_operator_message( # noqa: PLR0913
|
|
|
373
678
|
msg = "The 'font_size' argument cannot be less than 1"
|
|
374
679
|
raise ValueError(msg)
|
|
375
680
|
|
|
681
|
+
if not msg:
|
|
682
|
+
msg = "The 'msg' argument cannot be empty"
|
|
683
|
+
raise ValueError(msg)
|
|
684
|
+
|
|
376
685
|
msg_data = {
|
|
377
686
|
DF.MSG: msg,
|
|
378
687
|
DF.TITLE: title,
|
|
@@ -25,7 +25,7 @@ class PyTestWrapper:
|
|
|
25
25
|
self.config = ConfigManager().get_config()
|
|
26
26
|
self.collect(is_clear_database=True)
|
|
27
27
|
|
|
28
|
-
def start(self) -> bool:
|
|
28
|
+
def start(self, start_args: dict | None = None) -> bool:
|
|
29
29
|
"""Start pytest subprocess.
|
|
30
30
|
|
|
31
31
|
Returns:
|
|
@@ -37,44 +37,35 @@ class PyTestWrapper:
|
|
|
37
37
|
if self.is_running():
|
|
38
38
|
return False
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
cmd = [
|
|
41
|
+
self.python_executable,
|
|
42
|
+
"-m",
|
|
43
|
+
"pytest",
|
|
44
|
+
"--hardpy-db-url",
|
|
45
|
+
self.config.database.connection_url(),
|
|
46
|
+
"--hardpy-tests-name",
|
|
47
|
+
self.config.tests_name,
|
|
48
|
+
"--sc-address",
|
|
49
|
+
self.config.stand_cloud.address,
|
|
50
|
+
]
|
|
51
|
+
if self.config.stand_cloud.connection_only:
|
|
52
|
+
cmd.append("--sc-connection-only")
|
|
53
|
+
cmd.append("--hardpy-pt")
|
|
54
|
+
if start_args:
|
|
55
|
+
for key, value in start_args.items():
|
|
56
|
+
arg_str = f"{key}={value}"
|
|
57
|
+
cmd.extend(["--hardpy-start-arg", arg_str])
|
|
58
|
+
|
|
59
|
+
if system() == "Windows":
|
|
41
60
|
self._proc = subprocess.Popen( # noqa: S603
|
|
42
|
-
|
|
43
|
-
self.python_executable,
|
|
44
|
-
"-m",
|
|
45
|
-
"pytest",
|
|
46
|
-
"--hardpy-db-url",
|
|
47
|
-
self.config.database.connection_url(),
|
|
48
|
-
"--hardpy-tests-name",
|
|
49
|
-
self.config.tests_name,
|
|
50
|
-
"--sc-address",
|
|
51
|
-
self.config.stand_cloud.address,
|
|
52
|
-
"--sc-connection-only"
|
|
53
|
-
if self.config.stand_cloud.connection_only
|
|
54
|
-
else "",
|
|
55
|
-
"--hardpy-pt",
|
|
56
|
-
],
|
|
61
|
+
cmd,
|
|
57
62
|
cwd=ConfigManager().get_tests_path(),
|
|
63
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
|
|
58
64
|
)
|
|
59
|
-
|
|
65
|
+
if system() == "Linux":
|
|
60
66
|
self._proc = subprocess.Popen( # noqa: S603
|
|
61
|
-
|
|
62
|
-
self.python_executable,
|
|
63
|
-
"-m",
|
|
64
|
-
"pytest",
|
|
65
|
-
"--hardpy-db-url",
|
|
66
|
-
self.config.database.connection_url(),
|
|
67
|
-
"--hardpy-tests-name",
|
|
68
|
-
self.config.tests_name,
|
|
69
|
-
"--sc-address",
|
|
70
|
-
self.config.stand_cloud.address,
|
|
71
|
-
"--sc-connection-only"
|
|
72
|
-
if self.config.stand_cloud.connection_only
|
|
73
|
-
else "",
|
|
74
|
-
"--hardpy-pt",
|
|
75
|
-
],
|
|
67
|
+
cmd,
|
|
76
68
|
cwd=ConfigManager().get_tests_path(),
|
|
77
|
-
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, # type: ignore
|
|
78
69
|
)
|
|
79
70
|
|
|
80
71
|
return True
|
|
@@ -31,6 +31,10 @@ class HookReporter(BaseReporter):
|
|
|
31
31
|
doc_name (str): test run name
|
|
32
32
|
"""
|
|
33
33
|
self.set_doc_value(DF.NAME, doc_name)
|
|
34
|
+
self.set_doc_value(DF.USER, None)
|
|
35
|
+
self.set_doc_value(DF.BATCH_SN, None)
|
|
36
|
+
self.set_doc_value(DF.CAUSED_DUT_FAILURE_ID, None)
|
|
37
|
+
self.set_doc_value(DF.ERROR_CODE, None)
|
|
34
38
|
self.set_doc_value(DF.STATUS, TestStatus.READY)
|
|
35
39
|
self.set_doc_value(DF.START_TIME, None)
|
|
36
40
|
self.set_doc_value(DF.STOP_TIME, None)
|
|
@@ -217,7 +221,7 @@ class HookReporter(BaseReporter):
|
|
|
217
221
|
case_id,
|
|
218
222
|
DF.ATTEMPT,
|
|
219
223
|
)
|
|
220
|
-
self.set_doc_value(key, attempt
|
|
224
|
+
self.set_doc_value(key, attempt)
|
|
221
225
|
|
|
222
226
|
def get_module_start_time(self, module_id: str) -> int:
|
|
223
227
|
"""Get module start time.
|
|
@@ -237,6 +241,26 @@ class HookReporter(BaseReporter):
|
|
|
237
241
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.START_TIME)
|
|
238
242
|
return self._statestore.get_field(key)
|
|
239
243
|
|
|
244
|
+
def set_caused_dut_failure_id(self, module_id: str, case_id: str) -> None:
|
|
245
|
+
"""Set caused DUT failure id.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
module_id (str): module id
|
|
249
|
+
case_id (str): case id
|
|
250
|
+
"""
|
|
251
|
+
key = self.generate_key(DF.CAUSED_DUT_FAILURE_ID)
|
|
252
|
+
failure_id = f"{module_id}::{case_id}"
|
|
253
|
+
self.set_doc_value(key, failure_id)
|
|
254
|
+
|
|
255
|
+
def get_caused_dut_failure_id(self) -> str | None:
|
|
256
|
+
"""Get caused DUT failure id.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
str | None: failure id
|
|
260
|
+
"""
|
|
261
|
+
key = self.generate_key(DF.CAUSED_DUT_FAILURE_ID)
|
|
262
|
+
return self._statestore.get_field(key)
|
|
263
|
+
|
|
240
264
|
def update_node_order(self, nodes: dict) -> None:
|
|
241
265
|
"""Update node order.
|
|
242
266
|
|
|
@@ -267,6 +291,7 @@ class HookReporter(BaseReporter):
|
|
|
267
291
|
module_default = {
|
|
268
292
|
DF.STATUS: TestStatus.READY,
|
|
269
293
|
DF.NAME: self._get_module_name(node_info),
|
|
294
|
+
DF.GROUP: self._get_module_group(node_info),
|
|
270
295
|
DF.START_TIME: None,
|
|
271
296
|
DF.STOP_TIME: None,
|
|
272
297
|
DF.CASES: {},
|
|
@@ -274,10 +299,14 @@ class HookReporter(BaseReporter):
|
|
|
274
299
|
case_default = {
|
|
275
300
|
DF.STATUS: TestStatus.READY,
|
|
276
301
|
DF.NAME: self._get_case_name(node_info),
|
|
302
|
+
DF.GROUP: self._get_case_group(node_info),
|
|
277
303
|
DF.START_TIME: None,
|
|
278
304
|
DF.STOP_TIME: None,
|
|
279
305
|
DF.ASSERTION_MSG: None,
|
|
280
306
|
DF.MSG: None,
|
|
307
|
+
DF.ATTEMPT: 0,
|
|
308
|
+
DF.MEASUREMENTS: [],
|
|
309
|
+
DF.CHART: None,
|
|
281
310
|
}
|
|
282
311
|
|
|
283
312
|
if item.get(node_info.module_id) is None:
|
|
@@ -287,6 +316,7 @@ class HookReporter(BaseReporter):
|
|
|
287
316
|
else:
|
|
288
317
|
item[node_info.module_id][DF.STATUS] = TestStatus.READY
|
|
289
318
|
item[node_info.module_id][DF.NAME] = self._get_module_name(node_info)
|
|
319
|
+
item[node_info.module_id][DF.GROUP] = self._get_module_group(node_info)
|
|
290
320
|
item[node_info.module_id][DF.START_TIME] = None
|
|
291
321
|
item[node_info.module_id][DF.STOP_TIME] = None
|
|
292
322
|
item[node_info.module_id][DF.NAME] = self._get_module_name(node_info)
|
|
@@ -296,7 +326,6 @@ class HookReporter(BaseReporter):
|
|
|
296
326
|
|
|
297
327
|
if is_only_statestore:
|
|
298
328
|
case_default[DF.DIALOG_BOX] = {}
|
|
299
|
-
case_default[DF.ATTEMPT] = 0
|
|
300
329
|
item[node_info.module_id][DF.CASES][node_info.case_id] = case_default
|
|
301
330
|
|
|
302
331
|
def _remove_outdate_node(
|
|
@@ -378,6 +407,28 @@ class HookReporter(BaseReporter):
|
|
|
378
407
|
"""
|
|
379
408
|
return node_info.module_name if node_info.module_name else node_info.module_id
|
|
380
409
|
|
|
410
|
+
def _get_module_group(self, node_info: NodeInfo) -> str:
|
|
411
|
+
"""Get module group from markers or use default.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
node_info (NodeInfo): node info
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
str: module group
|
|
418
|
+
"""
|
|
419
|
+
return node_info.module_group
|
|
420
|
+
|
|
421
|
+
def _get_case_group(self, node_info: NodeInfo) -> str:
|
|
422
|
+
"""Get case group from markers or use default.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
node_info (NodeInfo): node info
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
str: case group
|
|
429
|
+
"""
|
|
430
|
+
return node_info.case_group
|
|
431
|
+
|
|
381
432
|
def _get_case_name(self, node_info: NodeInfo) -> str:
|
|
382
433
|
"""Get case name from markers or use default.
|
|
383
434
|
|
|
@@ -32,11 +32,13 @@ class StandCloudLoader:
|
|
|
32
32
|
sc_addr = address if address else connection_data.sc_address
|
|
33
33
|
self._sc_connector = StandCloudConnector(sc_addr)
|
|
34
34
|
|
|
35
|
-
def load(self, report: ResultRunStore) -> Response:
|
|
35
|
+
def load(self, report: ResultRunStore, timeout: int = 20) -> Response:
|
|
36
36
|
"""Load report to the StandCloud.
|
|
37
37
|
|
|
38
38
|
Args:
|
|
39
39
|
report (ResultRunStore): report
|
|
40
|
+
timeout (int, optional): post timeout in seconds. Defaults to 20.
|
|
41
|
+
High timeout value on poor network connections.
|
|
40
42
|
|
|
41
43
|
Returns:
|
|
42
44
|
Response: StandCloud load response, must be 201
|
|
@@ -47,7 +49,11 @@ class StandCloudLoader:
|
|
|
47
49
|
api = self._sc_connector.get_api("test_report")
|
|
48
50
|
|
|
49
51
|
try:
|
|
50
|
-
resp = api.post(
|
|
52
|
+
resp = api.post(
|
|
53
|
+
verify=self._verify_ssl,
|
|
54
|
+
json=report.model_dump(),
|
|
55
|
+
timeout=timeout,
|
|
56
|
+
)
|
|
51
57
|
except RuntimeError as exc:
|
|
52
58
|
raise StandCloudError(str(exc)) from exc
|
|
53
59
|
except OAuth2Error as exc:
|
|
@@ -88,11 +88,7 @@ class CouchdbReader:
|
|
|
88
88
|
str: report status
|
|
89
89
|
"""
|
|
90
90
|
doc = self._db.get(report_name)
|
|
91
|
-
|
|
92
|
-
if status not in {TestStatus.PASSED, TestStatus.FAILED, TestStatus.SKIPPED}:
|
|
93
|
-
msg = "Invalid report status"
|
|
94
|
-
raise ValueError(msg)
|
|
95
|
-
return status
|
|
91
|
+
return doc[DF.STATUS]
|
|
96
92
|
|
|
97
93
|
def get_report_infos(self) -> list[ReportInfo]:
|
|
98
94
|
"""Get a list of information about all reports in the database.
|