baldertest 0.1.0b6__py3-none-any.whl → 0.1.0b7__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.
_balder/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.1.0b6'
3
+ __version__ = version = '0.1.0b7'
4
4
  __version_tuple__ = version_tuple = (0, 1, 0)
_balder/balder_session.py CHANGED
@@ -60,6 +60,8 @@ class BalderSession:
60
60
  self.collect_only: Union[bool, None] = None
61
61
  #: specifies that the tests should only be collected and resolved but not executed
62
62
  self.resolve_only: Union[bool, None] = None
63
+ #: specifies that all discarded variations should be printed (with information why they were discarded)
64
+ self.show_discarded: Union[bool, None] = None
63
65
  #: contains a number of :class:`Setup` class strings that should only be considered for the execution
64
66
  self.only_with_setup: Union[List[str], None] = None
65
67
  #: contains a number of :class:`Scenario` class strings that should only be considered for the execution
@@ -246,6 +248,10 @@ class BalderSession:
246
248
  '--resolve-only', action='store_true',
247
249
  help="specifies that the tests are only collected and resolved but not executed")
248
250
 
251
+ self.cmd_arg_parser.add_argument(
252
+ '--show-discarded', action='store_true',
253
+ help="specifies that all discarded variations should be printed (with information why they were discarded)")
254
+
249
255
  self.cmd_arg_parser.add_argument(
250
256
  '--only-with-setup', nargs="*",
251
257
  help="defines a number of Setup classes which should only be considered for the execution")
@@ -270,6 +276,7 @@ class BalderSession:
270
276
  self.working_dir = self.parsed_args.working_dir
271
277
  self.collect_only = self.parsed_args.collect_only
272
278
  self.resolve_only = self.parsed_args.resolve_only
279
+ self.show_discarded = self.parsed_args.show_discarded
273
280
  self.only_with_setup = self.parsed_args.only_with_setup
274
281
  self.only_with_scenario = self.parsed_args.only_with_scenario
275
282
  self.force_covered_by_duplicates = self.parsed_args.force_covered_by_duplicates
@@ -298,12 +305,6 @@ class BalderSession:
298
305
  self.executor_tree = self.solver.get_executor_tree(plugin_manager=self.plugin_manager)
299
306
  self.plugin_manager.execute_filter_executor_tree(executor_tree=self.executor_tree)
300
307
 
301
- def execute_executor_tree(self):
302
- """
303
- This method executes the :class:`ExecutorTree`.
304
- """
305
- self.executor_tree.execute()
306
-
307
308
  def run(self):
308
309
  """
309
310
  This method executes the whole session
@@ -326,11 +327,14 @@ class BalderSession:
326
327
  if not self.collect_only:
327
328
  self.solve()
328
329
  self.create_executor_tree()
329
- print(f" resolve them to {len(self.executor_tree.get_all_variation_executors())} mapping candidates")
330
+ count_valid = len(self.executor_tree.get_all_variation_executors())
331
+ count_discarded = len(self.executor_tree.get_all_variation_executors(return_discarded=True)) - count_valid
332
+ addon_text = f" ({count_discarded} discarded)" if self.show_discarded else ""
333
+ print(f" resolve them to {count_valid} valid variations{addon_text}")
330
334
  print("")
331
335
  if not self.resolve_only:
332
- self.execute_executor_tree()
336
+ self.executor_tree.execute(show_discarded=self.show_discarded)
333
337
  else:
334
- self.executor_tree.print_tree()
338
+ self.executor_tree.print_tree(show_discarded=self.show_discarded)
335
339
 
336
340
  self.plugin_manager.execute_session_finished(self.executor_tree)
_balder/console/balder.py CHANGED
@@ -15,6 +15,7 @@ def console_balder(cmd_args: Optional[List[str]] = None, working_dir: Union[str,
15
15
  _console_balder_debug(cmd_args=cmd_args, working_dir=working_dir)
16
16
 
17
17
 
18
+ # pylint: disable-next=too-many-arguments
18
19
  def _console_balder_debug(cmd_args: Optional[List[str]] = None, working_dir: Union[str, pathlib.Path, None] = None,
19
20
  cb_session_created: Optional[Callable] = None, cb_run_finished: Optional[Callable] = None,
20
21
  cb_balder_exc: Optional[Callable] = None, cb_unexpected_exc: Optional[Callable] = None):
@@ -58,9 +58,8 @@ class BaseDeviceController(Controller, ABC):
58
58
  This method returns the original instanced feature objects of the related device
59
59
  """
60
60
  if self._original_instanced_features is None:
61
- # todo we should use a balder exception here!!
62
- raise EnvironmentError('can not access the original instantiated features before they were set with '
63
- '`save_all_original_instanced_features`')
61
+ raise RuntimeError('can not access the original instantiated features before they were set with '
62
+ '`save_all_original_instanced_features`')
64
63
  return self._original_instanced_features
65
64
 
66
65
  def save_all_original_instanced_features(self):
@@ -117,13 +117,14 @@ class FeatureController(Controller):
117
117
  if self.get_method_based_for_vdevice() is not None:
118
118
  for _, method_dict in self.get_method_based_for_vdevice().items():
119
119
  for _, vdevice_dict in method_dict.items():
120
- if for_vdevice in vdevice_dict.keys():
121
- for cur_cnn in vdevice_dict[for_vdevice]:
122
- if isinstance(cur_cnn, type):
123
- cur_cnn = cur_cnn()
124
- # clean metadata here because this is no connection between real devices
125
- cur_cnn.set_metadata_for_all_subitems(None)
126
- intersection.append(cur_cnn)
120
+ if for_vdevice not in vdevice_dict.keys():
121
+ continue
122
+ for cur_cnn in vdevice_dict[for_vdevice]:
123
+ if isinstance(cur_cnn, type):
124
+ cur_cnn = cur_cnn()
125
+ # clean metadata here because this is no connection between real devices
126
+ cur_cnn.set_metadata_for_all_subitems(None)
127
+ intersection.append(cur_cnn)
127
128
  if len(intersection) == 0:
128
129
  return [Connection()]
129
130
  return intersection
@@ -151,8 +152,7 @@ class FeatureController(Controller):
151
152
  This method returns the absolute calculated class-based-for-vdevice data for this feature.
152
153
  """
153
154
  if self._abs_cls_for_vdevice is None:
154
- # todo we should use a balder exception here!!
155
- raise EnvironmentError('can not access the absolute class based for-vdevices because they are not set')
155
+ raise RuntimeError('can not access the absolute class based for-vdevices because they are not set yet')
156
156
  return self._abs_cls_for_vdevice
157
157
 
158
158
  def set_class_based_for_vdevice(
@@ -718,9 +718,8 @@ class FeatureController(Controller):
718
718
  This method returns the :class:`VDevice` definitions that are the original definitions for this feature.
719
719
  """
720
720
  if self._original_vdevice_definitions is None:
721
- # todo we should use a balder exception here!!
722
- raise EnvironmentError('can not access the original VDevice definitions before they were set with '
723
- '`save_all_current_vdevice_references_as_originals`')
721
+ raise RuntimeError('can not access the original VDevice definitions before they were set with '
722
+ '`save_all_current_vdevice_references_as_originals`')
724
723
  return self._original_vdevice_definitions
725
724
 
726
725
  def save_all_current_vdevice_references_as_originals(self):
_balder/exceptions.py CHANGED
@@ -158,7 +158,7 @@ class IllegalVDeviceMappingError(BalderException):
158
158
  """
159
159
 
160
160
 
161
- class NotApplicableVariationError(BalderException):
161
+ class NotApplicableVariationException(BalderException):
162
162
  """
163
163
  is thrown internally after the current variation is not applicable
164
164
  """
@@ -87,21 +87,21 @@ class BasicExecutor(ABC):
87
87
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
88
88
 
89
89
  @abstractmethod
90
- def _prepare_execution(self):
90
+ def _prepare_execution(self, show_discarded):
91
91
  """
92
92
  This method runs before the branch will be executed and before the fixture construction code of this branch
93
93
  runs.
94
94
  """
95
95
 
96
96
  @abstractmethod
97
- def _body_execution(self):
97
+ def _body_execution(self, show_discarded):
98
98
  """
99
99
  This method runs between the fixture construction and teardown code. It should trigger the execution of the
100
100
  child branches.
101
101
  """
102
102
 
103
103
  @abstractmethod
104
- def _cleanup_execution(self):
104
+ def _cleanup_execution(self, show_discarded):
105
105
  """
106
106
  This method runs after the branch was executed (also after the fixture teardown code ran)
107
107
  """
@@ -137,17 +137,29 @@ class BasicExecutor(ABC):
137
137
  if isinstance(cur_child_executor.body_result, TestcaseResult):
138
138
  cur_child_executor.body_result.set_result(result=value, exception=None)
139
139
 
140
- def has_runnable_elements(self) -> bool:
140
+ def has_runnable_tests(self, consider_discarded_too=False) -> bool:
141
141
  """
142
142
  This method returns true if this executor element is runnable. The method returns true if this element has
143
143
  `prev_mark=RUNNABLE` and minimum one of its children has `prev_mark=RUNNABLE` too.
144
+
145
+ :param consider_discarded_too: True if the method allows DISCARDED elements too
144
146
  """
145
- if self.prev_mark != PreviousExecutorMark.RUNNABLE:
147
+ allowed_prev_marks = [PreviousExecutorMark.RUNNABLE]
148
+
149
+ if consider_discarded_too:
150
+ allowed_prev_marks.append(PreviousExecutorMark.DISCARDED)
151
+
152
+ if self.prev_mark not in allowed_prev_marks:
146
153
  return False
147
- for cur_child in self.all_child_executors:
148
- if cur_child.prev_mark == PreviousExecutorMark.RUNNABLE:
149
- return True
150
- return False
154
+
155
+ if self.all_child_executors is not None:
156
+ # the executor has child executors -> check them
157
+ for cur_child in self.all_child_executors:
158
+ if cur_child.has_runnable_tests(consider_discarded_too):
159
+ return True
160
+ return False
161
+ else:
162
+ return True
151
163
 
152
164
  def get_all_base_instances_of_this_branch(
153
165
  self, with_type: Union[Type[Setup], Type[Scenario], Type[types.FunctionType]],
@@ -165,7 +177,7 @@ class BasicExecutor(ABC):
165
177
  # only go through cur_executor == ExecutorTree (do not iterate with this object)
166
178
  while cur_executor.parent_executor is not None:
167
179
  if isinstance(cur_executor.base_instance, with_type):
168
- if not only_runnable_elements or cur_executor.has_runnable_elements():
180
+ if not only_runnable_elements or cur_executor.has_runnable_tests():
169
181
  return [cur_executor.base_instance]
170
182
  cur_executor = cur_executor.parent_executor
171
183
 
@@ -221,32 +233,34 @@ class BasicExecutor(ABC):
221
233
  summary[cur_key] = cur_child_dict[cur_key]
222
234
  return summary
223
235
 
224
- def execute(self):
236
+ def execute(self, show_discarded=False):
225
237
  """
226
238
  Executes the whole branch
227
239
  """
228
240
  start_time = time.perf_counter()
229
- self._prepare_execution()
241
+ self._prepare_execution(show_discarded=show_discarded)
230
242
 
231
243
  try:
232
244
  try:
233
- self.fixture_manager.enter(self)
234
- self.construct_result.set_result(ResultState.SUCCESS)
245
+ if self.has_runnable_tests():
246
+ self.fixture_manager.enter(self)
247
+ self.construct_result.set_result(ResultState.SUCCESS)
235
248
 
236
- self._body_execution()
249
+ self._body_execution(show_discarded=show_discarded)
237
250
  except Exception as exc:
238
251
  # this has to be a construction fixture error
239
252
  traceback.print_exception(*sys.exc_info())
240
253
  self.construct_result.set_result(ResultState.ERROR, exc)
241
254
  finally:
242
- if self.fixture_manager.is_allowed_to_leave(self):
243
- self.fixture_manager.leave(self)
244
- self.teardown_result.set_result(ResultState.SUCCESS)
255
+ if self.has_runnable_tests():
256
+ if self.fixture_manager.is_allowed_to_leave(self):
257
+ self.fixture_manager.leave(self)
258
+ self.teardown_result.set_result(ResultState.SUCCESS)
245
259
  except Exception as exc:
246
260
  # this has to be a teardown fixture error
247
261
  traceback.print_exception(*sys.exc_info())
248
262
  self.teardown_result.set_result(ResultState.ERROR, exc)
249
263
 
250
- self._cleanup_execution()
264
+ self._cleanup_execution(show_discarded=show_discarded)
251
265
 
252
266
  self.execution_time_sec = time.perf_counter() - start_time
@@ -18,6 +18,7 @@ class ExecutorTree(BasicExecutor):
18
18
  """
19
19
  This class is the root object of the executor tree structure
20
20
  """
21
+ LINE_LENGTH = 120
21
22
 
22
23
  def __init__(self, fixture_manager: FixtureManager):
23
24
  super().__init__()
@@ -35,18 +36,13 @@ class ExecutorTree(BasicExecutor):
35
36
 
36
37
  @property
37
38
  def all_child_executors(self) -> List[BasicExecutor]:
38
- return self.setup_executors
39
+ return self._setup_executors
39
40
 
40
41
  @property
41
42
  def base_instance(self) -> object:
42
43
  """returns None because this element is a ExecutorTree"""
43
44
  return None
44
45
 
45
- @property
46
- def setup_executors(self) -> List[SetupExecutor]:
47
- """returns all setup executors of this tree"""
48
- return self._setup_executors
49
-
50
46
  @property
51
47
  def fixture_manager(self) -> FixtureManager:
52
48
  """returns the fixture manager of this tree"""
@@ -58,13 +54,13 @@ class ExecutorTree(BasicExecutor):
58
54
 
59
55
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
60
56
 
61
- def _prepare_execution(self):
57
+ def _prepare_execution(self, show_discarded):
62
58
  pass
63
59
 
64
- def _body_execution(self):
65
- for cur_setup_executor in self.setup_executors:
66
- if cur_setup_executor.has_runnable_elements():
67
- cur_setup_executor.execute()
60
+ def _body_execution(self, show_discarded):
61
+ for cur_setup_executor in self.get_setup_executors():
62
+ if cur_setup_executor.has_runnable_tests(consider_discarded_too=show_discarded):
63
+ cur_setup_executor.execute(show_discarded=show_discarded)
68
64
  elif cur_setup_executor.prev_mark == PreviousExecutorMark.SKIP:
69
65
  cur_setup_executor.set_result_for_whole_branch(ResultState.SKIP)
70
66
  elif cur_setup_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
@@ -72,27 +68,31 @@ class ExecutorTree(BasicExecutor):
72
68
  else:
73
69
  cur_setup_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
74
70
 
75
- def _cleanup_execution(self):
71
+ def _cleanup_execution(self, show_discarded):
76
72
  pass
77
73
 
78
74
  # ---------------------------------- METHODS -----------------------------------------------------------------------
79
75
 
76
+ def get_setup_executors(self) -> List[SetupExecutor]:
77
+ """returns all setup executors of this tree"""
78
+ return self._setup_executors
79
+
80
80
  def get_all_scenario_executors(self) -> List[ScenarioExecutor]:
81
81
  """
82
82
  returns a list with all scenario executors
83
83
  """
84
84
  all_scenario_executor = []
85
- for cur_setup_executor in self.setup_executors:
86
- all_scenario_executor += cur_setup_executor.scenario_executors
85
+ for cur_setup_executor in self.get_setup_executors():
86
+ all_scenario_executor += cur_setup_executor.get_scenario_executors()
87
87
  return all_scenario_executor
88
88
 
89
- def get_all_variation_executors(self) -> List[VariationExecutor]:
89
+ def get_all_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
90
90
  """
91
91
  returns a list with all variation executors
92
92
  """
93
93
  all_variation_executor = []
94
94
  for cur_scenario_executor in self.get_all_scenario_executors():
95
- all_variation_executor += cur_scenario_executor.variation_executors
95
+ all_variation_executor += cur_scenario_executor.get_variation_executors(return_discarded=return_discarded)
96
96
  return all_variation_executor
97
97
 
98
98
  def get_all_testcase_executors(self) -> List[TestcaseExecutor]:
@@ -101,8 +101,8 @@ class ExecutorTree(BasicExecutor):
101
101
  """
102
102
  all_testcase_executor = []
103
103
  for cur_scenario_executor in self.get_all_scenario_executors():
104
- for cur_variation_executor in cur_scenario_executor.variation_executors:
105
- all_testcase_executor += cur_variation_executor.testcase_executors
104
+ for cur_variation_executor in cur_scenario_executor.get_variation_executors():
105
+ all_testcase_executor += cur_variation_executor.get_testcase_executors()
106
106
  return all_testcase_executor
107
107
 
108
108
  def add_setup_executor(self, setup_executor: SetupExecutor):
@@ -131,33 +131,33 @@ class ExecutorTree(BasicExecutor):
131
131
 
132
132
  def cleanup_empty_executor_branches(self):
133
133
  to_remove_executor = []
134
- for cur_setup_executor in self.setup_executors:
134
+ for cur_setup_executor in self.get_setup_executors():
135
135
  cur_setup_executor.cleanup_empty_executor_branches()
136
- if len(cur_setup_executor.scenario_executors) == 0:
136
+ if len(cur_setup_executor.get_scenario_executors()) == 0:
137
137
  # remove this whole executor because it has no children anymore
138
138
  to_remove_executor.append(cur_setup_executor)
139
139
  for cur_setup_executor in to_remove_executor:
140
140
  self._setup_executors.remove(cur_setup_executor)
141
141
 
142
- def execute(self) -> None:
142
+ def execute(self, show_discarded=False) -> None:
143
143
  """
144
144
  This method executes this branch of the tree
145
145
  """
146
146
  start_text = "START TESTSESSION"
147
147
  end_text = "FINISH TESTSESSION"
148
- line_length = 120
149
148
 
150
149
  def print_line(text):
151
- full_text = int((line_length - (len(start_text) + 2)) / 2) * "=" + " " + text + " "
152
- full_text += "=" * (line_length - len(full_text))
150
+ full_text = int((self.LINE_LENGTH - (len(start_text) + 2)) / 2) * "=" + " " + text + " "
151
+ full_text += "=" * (self.LINE_LENGTH - len(full_text))
153
152
  print(full_text)
154
153
 
155
154
  print_line(start_text)
156
155
  # check if there exists runnable elements
157
- runnables = [cur_exec.has_runnable_elements() for cur_exec in self.setup_executors]
156
+ runnables = [cur_exec.has_runnable_tests(consider_discarded_too=show_discarded)
157
+ for cur_exec in self.get_setup_executors()]
158
158
  one_or_more_runnable_setups = None if len(runnables) == 0 else max(runnables)
159
159
  if one_or_more_runnable_setups:
160
- super().execute()
160
+ super().execute(show_discarded=show_discarded)
161
161
  else:
162
162
  print("NO EXECUTABLE SETUPS/SCENARIOS FOUND")
163
163
  print_line(end_text)
@@ -171,19 +171,31 @@ class ExecutorTree(BasicExecutor):
171
171
  print(f"TOTAL {cur_key.value}: {cur_val}", end="")
172
172
  print("")
173
173
 
174
- def print_tree(self) -> None:
174
+ def print_tree(self, show_discarded=False) -> None:
175
175
  """this method is an auxiliary method which outputs the entire tree"""
176
176
  print("RESOLVING OVERVIEW", end="\n\n")
177
- for cur_setup_executor in self.setup_executors:
178
- for cur_scenario_executor in cur_setup_executor.scenario_executors:
179
- for cur_variation_executor in cur_scenario_executor.variation_executors:
180
- print(f"Scenario `{cur_scenario_executor.base_scenario_class.__class__.__qualname__}` <-> "
177
+ for cur_setup_executor in self.get_setup_executors():
178
+ for cur_scenario_executor in cur_setup_executor.get_scenario_executors():
179
+ for cur_variation_executor in cur_scenario_executor.get_variation_executors(
180
+ return_discarded=show_discarded):
181
+ applicable = cur_variation_executor.prev_mark != PreviousExecutorMark.DISCARDED
182
+ start_char = '+' if applicable else 'X'
183
+ print(start_char * self.LINE_LENGTH)
184
+ applicability_str = "[APPLICABLE]" if applicable else "[DISCARDED] "
185
+ print(f"{start_char} {applicability_str} Scenario "
186
+ f"`{cur_scenario_executor.base_scenario_class.__class__.__qualname__}` <-> "
181
187
  f"Setup `{cur_setup_executor.base_setup_class.__class__.__qualname__}`")
182
188
  mapping_printings = {}
183
189
  for cur_scenario_device, cur_setup_device in cur_variation_executor.base_device_mapping.items():
184
190
  mapping_printings[f" {cur_scenario_device.__qualname__}"] = str(cur_setup_device.__qualname__)
185
191
  max_len = max(len(cur_elem) for cur_elem in mapping_printings.keys())
186
192
  for cur_key, cur_val in mapping_printings.items():
187
- print(("{:<" + str(max_len) + "} = {}").format(cur_key, cur_val))
188
- for cur_testcase_excutor in cur_variation_executor.testcase_executors:
189
- print(f" -> Testcase<{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
193
+ print(("{} {:<" + str(max_len) + "} = {}").format(start_char, cur_key, cur_val))
194
+ for cur_testcase_excutor in cur_variation_executor.get_testcase_executors():
195
+ print(f"{start_char} -> Testcase<{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
196
+ if cur_variation_executor.prev_mark == PreviousExecutorMark.DISCARDED:
197
+ print(f"{start_char}")
198
+ print(f"{start_char} DISCARDED BECAUSE "
199
+ f"`{cur_variation_executor.not_applicable_variation_exc.args[0]}`")
200
+ print(('+' if applicable else 'X') * self.LINE_LENGTH)
201
+ print('')
@@ -46,7 +46,7 @@ class ScenarioExecutor(BasicExecutor):
46
46
 
47
47
  @property
48
48
  def all_child_executors(self) -> List[VariationExecutor]:
49
- return self.variation_executors
49
+ return self._variation_executors
50
50
 
51
51
  @property
52
52
  def parent_executor(self) -> SetupExecutor:
@@ -64,11 +64,6 @@ class ScenarioExecutor(BasicExecutor):
64
64
  """returns the :class:`Scenario` class that belongs to this executor"""
65
65
  return self._base_scenario_class
66
66
 
67
- @property
68
- def variation_executors(self) -> List[VariationExecutor]:
69
- """returns all variation executors that are child executor of this scenario executor"""
70
- return self._variation_executors
71
-
72
67
  @property
73
68
  def fixture_manager(self) -> FixtureManager:
74
69
  """returns the current active fixture manager that belongs to this scenario executor"""
@@ -91,13 +86,13 @@ class ScenarioExecutor(BasicExecutor):
91
86
 
92
87
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
93
88
 
94
- def _prepare_execution(self):
89
+ def _prepare_execution(self, show_discarded):
95
90
  print(f" SCENARIO {self.base_scenario_class.__class__.__name__}")
96
91
 
97
- def _body_execution(self):
98
- for cur_variation_executor in self.variation_executors:
99
- if cur_variation_executor.has_runnable_elements():
100
- cur_variation_executor.execute()
92
+ def _body_execution(self, show_discarded):
93
+ for cur_variation_executor in self.get_variation_executors(return_discarded=show_discarded):
94
+ if cur_variation_executor.has_runnable_tests(show_discarded):
95
+ cur_variation_executor.execute(show_discarded=show_discarded)
101
96
  elif cur_variation_executor.prev_mark == PreviousExecutorMark.SKIP:
102
97
  cur_variation_executor.set_result_for_whole_branch(ResultState.SKIP)
103
98
  elif cur_variation_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
@@ -105,18 +100,29 @@ class ScenarioExecutor(BasicExecutor):
105
100
  else:
106
101
  cur_variation_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
107
102
 
108
- def _cleanup_execution(self):
103
+ def _cleanup_execution(self, show_discarded):
109
104
  pass
110
105
 
111
106
  # ---------------------------------- METHODS -----------------------------------------------------------------------
112
107
 
108
+ def get_variation_executors(self, return_discarded=False) -> List[VariationExecutor]:
109
+ """
110
+ :param return_discarded: True if the method should return discarded variations too
111
+
112
+ :return: returns all variation executors that are child executor of this scenario executor
113
+ """
114
+ if not return_discarded:
115
+ return [cur_executor for cur_executor in self._variation_executors
116
+ if cur_executor.prev_mark != PreviousExecutorMark.DISCARDED]
117
+ return self._variation_executors
118
+
113
119
  def cleanup_empty_executor_branches(self):
114
120
  """
115
121
  This method removes all sub executors that are empty and not relevant anymore.
116
122
  """
117
123
  to_remove_executor = []
118
- for cur_variation_executor in self.variation_executors:
119
- if len(cur_variation_executor.testcase_executors) == 0:
124
+ for cur_variation_executor in self.get_variation_executors():
125
+ if len(cur_variation_executor.get_testcase_executors()) == 0:
120
126
  # remove this whole executor because it has no children anymore
121
127
  to_remove_executor.append(cur_variation_executor)
122
128
  for cur_variation_executor in to_remove_executor:
@@ -42,7 +42,7 @@ class SetupExecutor(BasicExecutor):
42
42
 
43
43
  @property
44
44
  def all_child_executors(self) -> List[ScenarioExecutor]:
45
- return self.scenario_executors
45
+ return self._scenario_executors
46
46
 
47
47
  @property
48
48
  def parent_executor(self) -> ExecutorTree:
@@ -58,11 +58,6 @@ class SetupExecutor(BasicExecutor):
58
58
  """returns the base :class:`Setup` that belongs to this executor"""
59
59
  return self._base_setup_class
60
60
 
61
- @property
62
- def scenario_executors(self) -> List[ScenarioExecutor]:
63
- """returns a list with all scenario executors that belongs to this setup executor"""
64
- return self._scenario_executors
65
-
66
61
  @property
67
62
  def fixture_manager(self) -> FixtureManager:
68
63
  """returns the current active fixture manager for this executor"""
@@ -70,13 +65,13 @@ class SetupExecutor(BasicExecutor):
70
65
 
71
66
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
72
67
 
73
- def _prepare_execution(self):
68
+ def _prepare_execution(self, show_discarded):
74
69
  print(f"SETUP {self.base_setup_class.__class__.__name__}")
75
70
 
76
- def _body_execution(self):
77
- for cur_scenario_executor in self.scenario_executors:
78
- if cur_scenario_executor.has_runnable_elements():
79
- cur_scenario_executor.execute()
71
+ def _body_execution(self, show_discarded):
72
+ for cur_scenario_executor in self.get_scenario_executors():
73
+ if cur_scenario_executor.has_runnable_tests(consider_discarded_too=show_discarded):
74
+ cur_scenario_executor.execute(show_discarded=show_discarded)
80
75
  elif cur_scenario_executor.prev_mark == PreviousExecutorMark.SKIP:
81
76
  cur_scenario_executor.set_result_for_whole_branch(ResultState.SKIP)
82
77
  elif cur_scenario_executor.prev_mark == PreviousExecutorMark.COVERED_BY:
@@ -84,16 +79,20 @@ class SetupExecutor(BasicExecutor):
84
79
  else:
85
80
  cur_scenario_executor.set_result_for_whole_branch(ResultState.NOT_RUN)
86
81
 
87
- def _cleanup_execution(self):
82
+ def _cleanup_execution(self, show_discarded):
88
83
  pass
89
84
 
90
85
  # ---------------------------------- METHODS -----------------------------------------------------------------------
91
86
 
87
+ def get_scenario_executors(self) -> List[ScenarioExecutor]:
88
+ """returns a list with all scenario executors that belongs to this setup executor"""
89
+ return self._scenario_executors
90
+
92
91
  def cleanup_empty_executor_branches(self):
93
92
  to_remove_executor = []
94
- for cur_scenario_executor in self.scenario_executors:
93
+ for cur_scenario_executor in self.get_scenario_executors():
95
94
  cur_scenario_executor.cleanup_empty_executor_branches()
96
- if len(cur_scenario_executor.variation_executors) == 0:
95
+ if len(cur_scenario_executor.get_variation_executors()) == 0:
97
96
  # remove this whole executor because it has no children anymore
98
97
  to_remove_executor.append(cur_scenario_executor)
99
98
  for cur_scenario_executor in to_remove_executor:
@@ -76,7 +76,7 @@ class TestcaseExecutor(BasicExecutor):
76
76
 
77
77
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
78
78
 
79
- def _prepare_execution(self):
79
+ def _prepare_execution(self, show_discarded):
80
80
  print(f" TEST {self.base_testcase_callable.__qualname__} ", end='')
81
81
  if self.should_be_skipped():
82
82
  self.body_result.set_result(ResultState.SKIP)
@@ -84,7 +84,7 @@ class TestcaseExecutor(BasicExecutor):
84
84
  print("[S]")
85
85
  return
86
86
 
87
- def _body_execution(self):
87
+ def _body_execution(self, show_discarded):
88
88
  start_time = time.perf_counter()
89
89
  try:
90
90
  _, func_type = inspect_method(self.base_testcase_callable)
@@ -109,14 +109,11 @@ class TestcaseExecutor(BasicExecutor):
109
109
  self.body_result.set_result(ResultState.FAILURE, exc)
110
110
  self.test_execution_time_sec = time.perf_counter() - start_time
111
111
 
112
- def _cleanup_execution(self):
112
+ def _cleanup_execution(self, show_discarded):
113
113
  print(f"[{self.body_result.get_result_as_char()}]")
114
114
 
115
115
  # ---------------------------------- METHODS -----------------------------------------------------------------------
116
116
 
117
- def has_runnable_elements(self) -> bool:
118
- return self.prev_mark == PreviousExecutorMark.RUNNABLE
119
-
120
117
  def should_run(self):
121
118
  """returns true if the testcase should be executed (defined in scenario)"""
122
119
  if self.base_testcase_callable in self.parent_executor.parent_executor.all_run_tests: