baldertest 0.1.0b9__py3-none-any.whl → 0.1.0b11__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 (53) hide show
  1. _balder/_version.py +1 -1
  2. _balder/balder_session.py +4 -2
  3. _balder/cnnrelations/__init__.py +7 -0
  4. _balder/cnnrelations/and_connection_relation.py +149 -0
  5. _balder/cnnrelations/base_connection_relation.py +270 -0
  6. _balder/cnnrelations/or_connection_relation.py +65 -0
  7. _balder/collector.py +116 -14
  8. _balder/connection.py +400 -881
  9. _balder/connection_metadata.py +255 -0
  10. _balder/console/balder.py +1 -1
  11. _balder/controllers/device_controller.py +26 -12
  12. _balder/controllers/feature_controller.py +63 -99
  13. _balder/controllers/normal_scenario_setup_controller.py +7 -7
  14. _balder/controllers/scenario_controller.py +97 -7
  15. _balder/controllers/setup_controller.py +2 -3
  16. _balder/decorator_connect.py +12 -10
  17. _balder/decorator_fixture.py +4 -7
  18. _balder/decorator_for_vdevice.py +21 -31
  19. _balder/decorator_gateway.py +3 -3
  20. _balder/decorator_parametrize.py +31 -0
  21. _balder/decorator_parametrize_by_feature.py +36 -0
  22. _balder/exceptions.py +6 -0
  23. _balder/executor/basic_executable_executor.py +126 -0
  24. _balder/executor/basic_executor.py +1 -78
  25. _balder/executor/executor_tree.py +7 -5
  26. _balder/executor/parametrized_testcase_executor.py +52 -0
  27. _balder/executor/scenario_executor.py +5 -2
  28. _balder/executor/setup_executor.py +5 -2
  29. _balder/executor/testcase_executor.py +41 -9
  30. _balder/executor/unresolved_parametrized_testcase_executor.py +209 -0
  31. _balder/executor/variation_executor.py +148 -123
  32. _balder/feature.py +1 -1
  33. _balder/fixture_definition_scope.py +19 -0
  34. _balder/fixture_execution_level.py +22 -0
  35. _balder/fixture_manager.py +170 -182
  36. _balder/fixture_metadata.py +26 -0
  37. _balder/objects/connections/osi_3_network.py +2 -2
  38. _balder/objects/connections/osi_4_transport.py +2 -2
  39. _balder/parametrization.py +75 -0
  40. _balder/routing_path.py +18 -25
  41. _balder/solver.py +52 -32
  42. _balder/testresult.py +1 -1
  43. _balder/utils.py +27 -1
  44. balder/__init__.py +6 -0
  45. balder/exceptions.py +4 -3
  46. balder/parametrization.py +8 -0
  47. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/METADATA +2 -2
  48. baldertest-0.1.0b11.dist-info/RECORD +83 -0
  49. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/WHEEL +1 -1
  50. baldertest-0.1.0b9.dist-info/RECORD +0 -68
  51. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/LICENSE +0 -0
  52. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/entry_points.txt +0 -0
  53. {baldertest-0.1.0b9.dist-info → baldertest-0.1.0b11.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- import time
4
3
  from typing import List, Union, Type, TYPE_CHECKING
5
4
 
6
- import sys
7
5
  import types
8
- import traceback
9
6
  from abc import ABC, abstractmethod
10
- from _balder.testresult import FixturePartResult, ResultState, ResultSummary
11
7
  from _balder.previous_executor_mark import PreviousExecutorMark
12
- from _balder.testresult import TestcaseResult
8
+ from _balder.testresult import FixturePartResult, ResultState, ResultSummary, TestcaseResult
13
9
 
14
10
  if TYPE_CHECKING:
15
11
  from _balder.setup import Setup
16
12
  from _balder.scenario import Scenario
17
- from _balder.fixture_manager import FixtureManager
18
13
 
19
14
 
20
15
  class BasicExecutor(ABC):
@@ -35,9 +30,6 @@ class BasicExecutor(ABC):
35
30
  # contains the result object for the TEARDOWN FIXTURE part of this branch
36
31
  self.teardown_result = FixturePartResult(self)
37
32
 
38
- # holds the execution time of this branch (with branch fixtures)
39
- self.execution_time_sec = None
40
-
41
33
  # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
42
34
 
43
35
  # ---------------------------------- CLASS METHODS ----------------------------------------------------------------
@@ -62,11 +54,6 @@ class BasicExecutor(ABC):
62
54
  """returns the base class instance to which this executor instance belongs or None if this element is a
63
55
  ExecutorTree"""
64
56
 
65
- @property
66
- @abstractmethod
67
- def fixture_manager(self) -> FixtureManager:
68
- """returns the active fixture manager instance"""
69
-
70
57
  @property
71
58
  def executor_result(self) -> ResultState:
72
59
  """
@@ -86,26 +73,6 @@ class BasicExecutor(ABC):
86
73
 
87
74
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
88
75
 
89
- @abstractmethod
90
- def _prepare_execution(self, show_discarded):
91
- """
92
- This method runs before the branch will be executed and before the fixture construction code of this branch
93
- runs.
94
- """
95
-
96
- @abstractmethod
97
- def _body_execution(self, show_discarded):
98
- """
99
- This method runs between the fixture construction and teardown code. It should trigger the execution of the
100
- child branches.
101
- """
102
-
103
- @abstractmethod
104
- def _cleanup_execution(self, show_discarded):
105
- """
106
- This method runs after the branch was executed (also after the fixture teardown code ran)
107
- """
108
-
109
76
  # ---------------------------------- METHODS -----------------------------------------------------------------------
110
77
 
111
78
  def get_all_recognized_exception(self) -> List[Exception]:
@@ -125,18 +92,6 @@ class BasicExecutor(ABC):
125
92
  all_own.remove(None)
126
93
  return all_own
127
94
 
128
- def set_result_for_whole_branch(self, value: ResultState):
129
- """
130
- This method sets the executor result for all sub executors.
131
-
132
- :param value: the new value that should be set for this branch
133
- """
134
- if value not in (ResultState.SKIP, ResultState.COVERED_BY, ResultState.NOT_RUN):
135
- raise ValueError("can not set a state that is not NOT_RUN, SKIP or COVERED_BY for a whole branch")
136
- for cur_child_executor in self.all_child_executors:
137
- if isinstance(cur_child_executor.body_result, TestcaseResult):
138
- cur_child_executor.body_result.set_result(result=value, exception=None)
139
-
140
95
  def has_runnable_tests(self, consider_discarded_too=False) -> bool:
141
96
  """
142
97
  This method returns true if this executor element is runnable. The method returns true if this element has
@@ -221,35 +176,3 @@ class BasicExecutor(ABC):
221
176
  for cur_child_exec in self.all_child_executors:
222
177
  summary += cur_child_exec.testsummary()
223
178
  return summary
224
-
225
- def execute(self, show_discarded=False):
226
- """
227
- Executes the whole branch
228
- """
229
- start_time = time.perf_counter()
230
- self._prepare_execution(show_discarded=show_discarded)
231
-
232
- try:
233
- try:
234
- if self.has_runnable_tests():
235
- self.fixture_manager.enter(self)
236
- self.construct_result.set_result(ResultState.SUCCESS)
237
-
238
- self._body_execution(show_discarded=show_discarded)
239
- except Exception as exc:
240
- # this has to be a construction fixture error
241
- traceback.print_exception(*sys.exc_info())
242
- self.construct_result.set_result(ResultState.ERROR, exc)
243
- finally:
244
- if self.has_runnable_tests():
245
- if self.fixture_manager.is_allowed_to_leave(self):
246
- self.fixture_manager.leave(self)
247
- self.teardown_result.set_result(ResultState.SUCCESS)
248
- except Exception as exc:
249
- # this has to be a teardown fixture error
250
- traceback.print_exception(*sys.exc_info())
251
- self.teardown_result.set_result(ResultState.ERROR, exc)
252
-
253
- self._cleanup_execution(show_discarded=show_discarded)
254
-
255
- self.execution_time_sec = time.perf_counter() - start_time
@@ -3,22 +3,24 @@ from typing import Union, List, Type, TYPE_CHECKING
3
3
 
4
4
  from dataclasses import fields
5
5
  from _balder.executor.setup_executor import SetupExecutor
6
- from _balder.executor.basic_executor import BasicExecutor
7
- from _balder.fixture_manager import FixtureManager
6
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
7
+ from _balder.fixture_execution_level import FixtureExecutionLevel
8
8
  from _balder.testresult import ResultState, BranchBodyResult, ResultSummary
9
9
  from _balder.previous_executor_mark import PreviousExecutorMark
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from _balder.setup import Setup
13
+ from _balder.fixture_manager import FixtureManager
13
14
  from _balder.executor.scenario_executor import ScenarioExecutor
14
15
  from _balder.executor.variation_executor import VariationExecutor
15
16
  from _balder.executor.testcase_executor import TestcaseExecutor
16
17
 
17
18
 
18
- class ExecutorTree(BasicExecutor):
19
+ class ExecutorTree(BasicExecutableExecutor):
19
20
  """
20
21
  This class is the root object of the executor tree structure
21
22
  """
23
+ fixture_execution_level = FixtureExecutionLevel.SESSION
22
24
  LINE_LENGTH = 120
23
25
 
24
26
  def __init__(self, fixture_manager: FixtureManager):
@@ -36,7 +38,7 @@ class ExecutorTree(BasicExecutor):
36
38
  # ---------------------------------- PROPERTIES --------------------------------------------------------------------
37
39
 
38
40
  @property
39
- def all_child_executors(self) -> List[BasicExecutor]:
41
+ def all_child_executors(self) -> List[BasicExecutableExecutor]:
40
42
  return self._setup_executors
41
43
 
42
44
  @property
@@ -180,7 +182,7 @@ class ExecutorTree(BasicExecutor):
180
182
  is_first = False
181
183
  else:
182
184
  print(" | ", end="")
183
- print(f"TOTAL {cur_field.name}: {getattr(summary, cur_field.name)}", end="")
185
+ print(f"TOTAL {cur_field.name.upper()}: {getattr(summary, cur_field.name)}", end="")
184
186
  print("")
185
187
 
186
188
  def print_tree(self, show_discarded=False) -> None:
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+ from typing import Any, TYPE_CHECKING
3
+
4
+ from collections import OrderedDict
5
+ from _balder.utils import inspect_method
6
+ from .testcase_executor import TestcaseExecutor
7
+
8
+ if TYPE_CHECKING:
9
+ from _balder.executor.unresolved_parametrized_testcase_executor import UnresolvedParametrizedTestcaseExecutor
10
+ from _balder.executor.variation_executor import VariationExecutor
11
+
12
+
13
+ class ParametrizedTestcaseExecutor(TestcaseExecutor):
14
+ """
15
+ special testcase executor for parametrized testcases
16
+ """
17
+ def __init__(
18
+ self,
19
+ testcase: callable,
20
+ parent: VariationExecutor,
21
+ parametrization: OrderedDict[str, Any] = None,
22
+ unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor = None
23
+ ) -> None:
24
+ super().__init__(testcase=testcase, parent=parent)
25
+ self._parametrization = parametrization
26
+ # holds a reference to the parent unresolved object (if it has dynamic parametrized components
27
+ self._unresolved_group_obj: UnresolvedParametrizedTestcaseExecutor | None = unresolved_group_obj
28
+
29
+ @property
30
+ def full_test_name_str(self) -> str:
31
+ """
32
+ returns the name of the test method, that should be used in output
33
+ """
34
+ # try to get string name for parametrization
35
+ if self._parametrization:
36
+ # todo what if it is not convertable to string?
37
+ parametrization_name = ';'.join([str(e) for e in self._parametrization.values()])
38
+ return f"{super().full_test_name_str}[{parametrization_name}]"
39
+ return super().full_test_name_str
40
+
41
+ def get_all_test_method_args(self):
42
+ _, func_type = inspect_method(self.base_testcase_callable)
43
+ all_kwargs = self.fixture_manager.get_all_attribute_values(
44
+ self,
45
+ self.base_testcase_obj.__class__,
46
+ self.base_testcase_callable,
47
+ func_type,
48
+ ignore_attributes=self._parametrization.keys()
49
+ )
50
+ if self._parametrization:
51
+ all_kwargs.update(self._parametrization)
52
+ return all_kwargs
@@ -1,9 +1,10 @@
1
1
  from __future__ import annotations
2
2
  from typing import Type, Union, List, Dict, TYPE_CHECKING
3
3
 
4
+ from _balder.fixture_execution_level import FixtureExecutionLevel
4
5
  from _balder.testresult import ResultState, BranchBodyResult
5
6
  from _balder.utils import get_class_that_defines_method
6
- from _balder.executor.basic_executor import BasicExecutor
7
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
7
8
  from _balder.executor.variation_executor import VariationExecutor
8
9
  from _balder.previous_executor_mark import PreviousExecutorMark
9
10
  from _balder.controllers.scenario_controller import ScenarioController
@@ -14,10 +15,11 @@ if TYPE_CHECKING:
14
15
  from _balder.fixture_manager import FixtureManager
15
16
 
16
17
 
17
- class ScenarioExecutor(BasicExecutor):
18
+ class ScenarioExecutor(BasicExecutableExecutor):
18
19
  """
19
20
  A ScenarioExecutor can contain :meth:`VariationExecutor` as children.
20
21
  """
22
+ fixture_execution_level = FixtureExecutionLevel.SCENARIO
21
23
 
22
24
  def __init__(self, scenario: Type[Scenario], parent: SetupExecutor):
23
25
  super().__init__()
@@ -67,6 +69,7 @@ class ScenarioExecutor(BasicExecutor):
67
69
 
68
70
  @property
69
71
  def base_scenario_controller(self) -> ScenarioController:
72
+ """returns the :class:`ScenarioController` for the setup object of this executor"""
70
73
  return ScenarioController.get_for(self.base_scenario_class.__class__)
71
74
 
72
75
  @property
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
  from typing import Type, Union, List, TYPE_CHECKING
3
3
 
4
+ from _balder.fixture_execution_level import FixtureExecutionLevel
4
5
  from _balder.testresult import ResultState, BranchBodyResult
5
- from _balder.executor.basic_executor import BasicExecutor
6
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
6
7
  from _balder.executor.scenario_executor import ScenarioExecutor
7
8
  from _balder.previous_executor_mark import PreviousExecutorMark
8
9
  from _balder.controllers.setup_controller import SetupController
@@ -16,11 +17,12 @@ if TYPE_CHECKING:
16
17
  from _balder.executor.executor_tree import ExecutorTree
17
18
 
18
19
 
19
- class SetupExecutor(BasicExecutor):
20
+ class SetupExecutor(BasicExecutableExecutor):
20
21
  """
21
22
  A SetupExecutor is the highest branch object of an :class:`ExecutorTree`. It contains all scenarios and the
22
23
  underlying device mappings and their test cases that exist with this setup.
23
24
  """
25
+ fixture_execution_level = FixtureExecutionLevel.SETUP
24
26
 
25
27
  def __init__(self, setup: Type[Setup], parent: ExecutorTree):
26
28
  super().__init__()
@@ -63,6 +65,7 @@ class SetupExecutor(BasicExecutor):
63
65
 
64
66
  @property
65
67
  def base_setup_controller(self) -> SetupController:
68
+ """returns the :class:`SetupController` for the setup object of this executor"""
66
69
  return SetupController.get_for(self.base_setup_class.__class__)
67
70
 
68
71
  @property
@@ -4,9 +4,12 @@ from typing import List, Union, TYPE_CHECKING
4
4
  import sys
5
5
  import time
6
6
  import traceback
7
- from _balder.utils import inspect_method
7
+
8
+ from _balder.executor.basic_executable_executor import BasicExecutableExecutor
9
+ from _balder.fixture_execution_level import FixtureExecutionLevel
10
+ from _balder.previous_executor_mark import PreviousExecutorMark
8
11
  from _balder.testresult import ResultState, TestcaseResult
9
- from _balder.executor.basic_executor import BasicExecutor
12
+ from _balder.utils import inspect_method
10
13
 
11
14
  if TYPE_CHECKING:
12
15
  from _balder.executor.variation_executor import VariationExecutor
@@ -14,13 +17,18 @@ if TYPE_CHECKING:
14
17
  from _balder.scenario import Scenario
15
18
 
16
19
 
17
- class TestcaseExecutor(BasicExecutor):
20
+ class TestcaseExecutor(BasicExecutableExecutor):
18
21
  """
19
22
  A TestcaseExecutor class represents an actual single test that can be executed. It therefore references exactly to a
20
23
  test method of a scenario that can be executed on the specific setup this executor belongs to.
21
24
  """
25
+ fixture_execution_level = FixtureExecutionLevel.TESTCASE
22
26
 
23
- def __init__(self, testcase: callable, parent: VariationExecutor):
27
+ def __init__(
28
+ self,
29
+ testcase: callable,
30
+ parent: VariationExecutor,
31
+ ) -> None:
24
32
  super().__init__()
25
33
 
26
34
  self._base_testcase_callable = testcase
@@ -36,6 +44,13 @@ class TestcaseExecutor(BasicExecutor):
36
44
  # holds the raw test execution time in seconds
37
45
  self.test_execution_time_sec = 0
38
46
 
47
+ # determine prev_mark IGNORE/SKIP for the testcase
48
+ if self.should_be_skipped():
49
+ self.prev_mark = PreviousExecutorMark.SKIP
50
+ # always overwrite if it should be ignored
51
+ if self.should_be_ignored():
52
+ self.prev_mark = PreviousExecutorMark.IGNORE
53
+
39
54
  # ---------------------------------- STATIC METHODS ----------------------------------------------------------------
40
55
 
41
56
  # ---------------------------------- CLASS METHODS ----------------------------------------------------------------
@@ -73,22 +88,27 @@ class TestcaseExecutor(BasicExecutor):
73
88
  """returns the current active fixture manager"""
74
89
  return self._fixture_manager
75
90
 
91
+ @property
92
+ def full_test_name_str(self) -> str:
93
+ """
94
+ returns the name of the test method, that should be used in output
95
+ """
96
+ return self._base_testcase_callable.__qualname__
97
+
76
98
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
77
99
 
78
100
  def _prepare_execution(self, show_discarded):
79
- print(f" TEST {self.base_testcase_callable.__qualname__} ", end='')
101
+ print(f" TEST {self.full_test_name_str} ", end='')
80
102
  if self.should_be_skipped():
81
103
  self.body_result.set_result(ResultState.SKIP)
82
104
  self.execution_time_sec = 0
83
105
  print("[S]")
84
- return
85
106
 
86
107
  def _body_execution(self, show_discarded):
87
108
  start_time = time.perf_counter()
88
109
  try:
89
110
  _, func_type = inspect_method(self.base_testcase_callable)
90
- all_args = self.fixture_manager.get_all_attribute_values(
91
- self, self.base_testcase_obj.__class__, self.base_testcase_callable, func_type)
111
+ all_args = self.get_all_test_method_args()
92
112
  if func_type == "staticmethod":
93
113
  # testcase is a staticmethod - no special first attribute
94
114
  self.base_testcase_callable(**all_args)
@@ -102,7 +122,7 @@ class TestcaseExecutor(BasicExecutor):
102
122
  f"`{self.base_testcase_callable.__name__}`")
103
123
 
104
124
  self.body_result.set_result(ResultState.SUCCESS)
105
- except Exception as exc:
125
+ except Exception as exc: # pylint: disable=broad-exception-caught
106
126
  # this has to be a test error
107
127
  traceback.print_exception(*sys.exc_info())
108
128
  self.body_result.set_result(ResultState.FAILURE, exc)
@@ -153,3 +173,15 @@ class TestcaseExecutor(BasicExecutor):
153
173
  if scenario_class in covered_by_dict_resolved.keys():
154
174
  all_covered_by_data += covered_by_dict_resolved[scenario_class]
155
175
  return all_covered_by_data
176
+
177
+ def get_all_test_method_args(self):
178
+ """
179
+ returns all kwargs values for the test method
180
+ """
181
+ _, func_type = inspect_method(self.base_testcase_callable)
182
+ return self.fixture_manager.get_all_attribute_values(
183
+ self,
184
+ self.base_testcase_obj.__class__,
185
+ self.base_testcase_callable,
186
+ func_type
187
+ )
@@ -0,0 +1,209 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import List, Type, Any, Dict, Iterable, TYPE_CHECKING
5
+ import types
6
+ from graphlib import TopologicalSorter
7
+ from collections import OrderedDict
8
+
9
+ from _balder.executor.basic_executor import BasicExecutor
10
+ from _balder.executor.parametrized_testcase_executor import ParametrizedTestcaseExecutor
11
+ from _balder.parametrization import Parameter
12
+ from _balder.previous_executor_mark import PreviousExecutorMark
13
+ from _balder.testresult import BranchBodyResult
14
+
15
+ if TYPE_CHECKING:
16
+ from _balder.executor.variation_executor import VariationExecutor
17
+ from _balder.scenario import Scenario
18
+ from _balder.setup import Setup
19
+
20
+
21
+ class UnresolvedParametrizedTestcaseExecutor(BasicExecutor):
22
+ """
23
+ This executor class represents a group of dynamically parametrized tests.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ testcase: callable,
29
+ parent: VariationExecutor,
30
+ static_parameters: Dict[str, Any] = None,
31
+ ) -> None:
32
+ super().__init__()
33
+
34
+ self._base_testcase_callable = testcase
35
+ self._parent_executor = parent
36
+
37
+ # holds the specific static parameters for this unresolved group
38
+ self._static_parameters = static_parameters if static_parameters is not None else {}
39
+
40
+ # holds the dynamically created testcase executors as soon as this executor is entered
41
+ self._testcase_executors = None
42
+
43
+ # contains the result object for the BODY part of this branch
44
+ self.body_result = BranchBodyResult(self)
45
+
46
+ @property
47
+ def parent_executor(self) -> VariationExecutor:
48
+ return self._parent_executor
49
+
50
+ @property
51
+ def base_testcase_callable(self) -> callable:
52
+ """returns the testcase function"""
53
+ return self._base_testcase_callable
54
+
55
+ @property
56
+ def all_child_executors(self) -> List[ParametrizedTestcaseExecutor]:
57
+ return self.get_testcase_executors()
58
+
59
+ @property
60
+ def base_testcase_obj(self) -> Scenario:
61
+ """
62
+ return the testcase Scenario this testcase is defined in
63
+ """
64
+ return self.parent_executor.cur_scenario_class
65
+
66
+ @property
67
+ def base_instance(self) -> object:
68
+ """
69
+ returns the base class instance to which this executor instance belongs"""
70
+ return self._base_testcase_callable
71
+
72
+ @property
73
+ def parametrization_has_been_resolved(self) -> bool:
74
+ """returns true if the parametrization has been resolved"""
75
+ return self._testcase_executors is not None
76
+
77
+ def has_runnable_tests(self, consider_discarded_too=False) -> bool:
78
+ """
79
+ This method returns true if this executor element is runnable. The method returns true if this element has
80
+ `prev_mark=RUNNABLE` and minimum one of its children has `prev_mark=RUNNABLE` too.
81
+
82
+ :param consider_discarded_too: True if the method allows DISCARDED elements too
83
+ """
84
+ if self.parametrization_has_been_resolved:
85
+ return super().has_runnable_tests(consider_discarded_too=consider_discarded_too)
86
+
87
+ allowed_prev_marks = [PreviousExecutorMark.RUNNABLE]
88
+
89
+ if consider_discarded_too:
90
+ allowed_prev_marks.append(PreviousExecutorMark.DISCARDED)
91
+
92
+ if self.prev_mark not in allowed_prev_marks:
93
+ return False
94
+ return True
95
+
96
+ def get_all_base_instances_of_this_branch(
97
+ self, with_type: Type[Setup] | Type[Scenario] | Type[types.FunctionType],
98
+ only_runnable_elements: bool = True) -> List[Setup | Scenario | object]:
99
+ """
100
+ returns itself if the type matches, otherwise an empty list
101
+ """
102
+ # todo
103
+ if isinstance(self.base_instance, with_type):
104
+ return [self.base_instance]
105
+ return []
106
+
107
+ def get_covered_by_element(self) -> List[Scenario | callable]:
108
+ """
109
+ This method returns a list of elements where the whole scenario is covered from. This means, that the whole
110
+ test methods in this scenario are already be covered from every single element in the list.
111
+ """
112
+ all_covered_by_data = []
113
+ scenario_executor = self.parent_executor.parent_executor
114
+ scenario_class = scenario_executor.base_scenario_class
115
+ covered_by_dict_resolved = scenario_executor.get_covered_by_dict()
116
+ if self.base_testcase_callable in covered_by_dict_resolved.keys():
117
+ all_covered_by_data += covered_by_dict_resolved[self.base_testcase_callable]
118
+ if scenario_class in covered_by_dict_resolved.keys():
119
+ all_covered_by_data += covered_by_dict_resolved[scenario_class]
120
+ return all_covered_by_data
121
+
122
+ def get_testcase_executors(self) -> List[ParametrizedTestcaseExecutor | UnresolvedParametrizedTestcaseExecutor]:
123
+ """returns all sub testcase executors that belongs to this variation-executor"""
124
+ if self._testcase_executors is None:
125
+ return [self]
126
+ return self._testcase_executors
127
+
128
+ def resolve_dynamic_parametrization(self):
129
+ """
130
+ resolves the dynamic parametrization - should be called when setup features are active in the scenario
131
+ """
132
+ self._testcase_executors = []
133
+
134
+ parametrization = self.get_parametrization()
135
+ if parametrization:
136
+ for cur_parametrization in parametrization:
137
+ self._testcase_executors.append(
138
+ ParametrizedTestcaseExecutor(
139
+ self._base_testcase_callable,
140
+ parent=self.parent_executor,
141
+ parametrization=cur_parametrization,
142
+ unresolved_group_obj=self
143
+ )
144
+ )
145
+
146
+ def get_parametrization(self) -> List[OrderedDict[str, Any]] | None:
147
+ """
148
+ returns all parametrization elements that belongs to this group executor
149
+ """
150
+ scenario_controller = self.parent_executor.parent_executor.base_scenario_controller
151
+ dynamic_parametrization = scenario_controller.get_parametrization_for(self._base_testcase_callable,
152
+ static=False)
153
+ if not dynamic_parametrization:
154
+ raise ValueError('can not determine dynamic parametrization, because there are no dynamic parameters')
155
+
156
+ # sort attributes according their Parameter - using TopologicalSorter
157
+ graph = {attr: [param.name for param in config.get_parameters(of_type=Parameter).values()]
158
+ for attr, config in dynamic_parametrization.items()}
159
+ # also add all elements from static parameters
160
+ graph.update({param: [] for param in self._static_parameters})
161
+
162
+ ts = TopologicalSorter(graph)
163
+ resolvable_order_of_attribues = ts.static_order()
164
+ resolvable_dynamic_attribues = [attr for attr in resolvable_order_of_attribues
165
+ if attr in dynamic_parametrization.keys()]
166
+
167
+ def get_variations_for(
168
+ resolved_parameters: Dict[str, Any],
169
+ remaining_attributes: Iterable[str]
170
+ ) -> List[Dict[str, Any]]:
171
+ result = []
172
+ remaining_attributes = list(remaining_attributes).copy()
173
+ attr = remaining_attributes.pop(0)
174
+ feature_access_selector = dynamic_parametrization[attr]
175
+ # get value for this attribute
176
+ attr_value_list = feature_access_selector.get_value(resolved_parameters)
177
+ if not isinstance(attr_value_list, Iterable):
178
+ raise TypeError(
179
+ f'feature parametrizing not possible, because `{feature_access_selector.device.__qualname__}'
180
+ f'.{feature_access_selector.device_property_name}.{feature_access_selector.feature_property_name}` '
181
+ f'does not return Iterable')
182
+
183
+ for cur_value in attr_value_list:
184
+ parameters_with_cur_attr_value = resolved_parameters.copy()
185
+ parameters_with_cur_attr_value[attr] = cur_value
186
+ if len(remaining_attributes) > 0:
187
+ result.extend(get_variations_for(parameters_with_cur_attr_value, remaining_attributes))
188
+ else:
189
+ result.append(parameters_with_cur_attr_value)
190
+
191
+ return result
192
+
193
+ resolved_parameters = self._static_parameters.copy()
194
+
195
+ all_full_parametrization = get_variations_for(resolved_parameters, resolvable_dynamic_attribues)
196
+
197
+ # get combined parametrization
198
+ result = []
199
+ for cur_full_parametrization in all_full_parametrization:
200
+ cur_parameter_set = OrderedDict()
201
+ for cur_arg in inspect.getfullargspec(self._base_testcase_callable).args:
202
+ # only if this is a parametrization value
203
+ if cur_arg in cur_full_parametrization:
204
+ cur_parameter_set[cur_arg] = cur_full_parametrization[cur_arg]
205
+ result.append(cur_parameter_set)
206
+ return result
207
+
208
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
209
+ pass