hardpy 0.14.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.
@@ -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
- DuplicatePartNumberError,
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 set_dut_info(info: dict) -> None:
60
- """Add DUT info to document.
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 (dict): DUT 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
- DuplicateSerialNumberError: if serial number is already set
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
- raise DuplicateSerialNumberError
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
- DuplicatePartNumberError: if part number is already set
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
- raise DuplicatePartNumberError
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
- DuplicateTestStandNameError: if test stand name is already set
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
- raise DuplicateTestStandNameError
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: dict) -> None:
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 (dict): test stand 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
- DuplicateTestStandLocationError: if test stand location is already set
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
- raise DuplicateTestStandLocationError
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
- DuplicateTestStandNumberError: if stand number is already set
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
- raise DuplicateTestStandNumberError
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
- if system() == "Linux":
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
- elif system() == "Windows":
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, statestore_only=True)
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
 
@@ -88,11 +88,7 @@ class CouchdbReader:
88
88
  str: report status
89
89
  """
90
90
  doc = self._db.get(report_name)
91
- status = doc[DF.STATUS]
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.