baldertest 0.1.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.
- _balder/__init__.py +12 -0
- _balder/_version.py +34 -0
- _balder/balder_plugin.py +73 -0
- _balder/balder_session.py +341 -0
- _balder/balder_settings.py +15 -0
- _balder/cnnrelations/__init__.py +7 -0
- _balder/cnnrelations/and_connection_relation.py +176 -0
- _balder/cnnrelations/base_connection_relation.py +270 -0
- _balder/cnnrelations/or_connection_relation.py +65 -0
- _balder/collector.py +874 -0
- _balder/connection.py +863 -0
- _balder/connection_metadata.py +255 -0
- _balder/console/__init__.py +0 -0
- _balder/console/balder.py +58 -0
- _balder/controllers/__init__.py +12 -0
- _balder/controllers/base_device_controller.py +72 -0
- _balder/controllers/controller.py +29 -0
- _balder/controllers/device_controller.py +446 -0
- _balder/controllers/feature_controller.py +715 -0
- _balder/controllers/normal_scenario_setup_controller.py +402 -0
- _balder/controllers/scenario_controller.py +524 -0
- _balder/controllers/setup_controller.py +134 -0
- _balder/controllers/vdevice_controller.py +95 -0
- _balder/decorator_connect.py +104 -0
- _balder/decorator_covered_by.py +74 -0
- _balder/decorator_fixture.py +29 -0
- _balder/decorator_for_vdevice.py +118 -0
- _balder/decorator_gateway.py +34 -0
- _balder/decorator_insert_into_tree.py +52 -0
- _balder/decorator_parametrize.py +31 -0
- _balder/decorator_parametrize_by_feature.py +36 -0
- _balder/device.py +18 -0
- _balder/exceptions.py +182 -0
- _balder/executor/__init__.py +0 -0
- _balder/executor/basic_executable_executor.py +133 -0
- _balder/executor/basic_executor.py +205 -0
- _balder/executor/executor_tree.py +217 -0
- _balder/executor/parametrized_testcase_executor.py +52 -0
- _balder/executor/scenario_executor.py +169 -0
- _balder/executor/setup_executor.py +163 -0
- _balder/executor/testcase_executor.py +203 -0
- _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
- _balder/executor/variation_executor.py +882 -0
- _balder/exit_code.py +19 -0
- _balder/feature.py +74 -0
- _balder/feature_replacement_mapping.py +107 -0
- _balder/feature_vdevice_mapping.py +88 -0
- _balder/fixture_definition_scope.py +19 -0
- _balder/fixture_execution_level.py +22 -0
- _balder/fixture_manager.py +483 -0
- _balder/fixture_metadata.py +26 -0
- _balder/node_gateway.py +103 -0
- _balder/objects/__init__.py +0 -0
- _balder/objects/connections/__init__.py +0 -0
- _balder/objects/connections/osi_1_physical.py +116 -0
- _balder/objects/connections/osi_2_datalink.py +35 -0
- _balder/objects/connections/osi_3_network.py +47 -0
- _balder/objects/connections/osi_4_transport.py +40 -0
- _balder/objects/connections/osi_5_session.py +13 -0
- _balder/objects/connections/osi_6_presentation.py +13 -0
- _balder/objects/connections/osi_7_application.py +83 -0
- _balder/objects/devices/__init__.py +0 -0
- _balder/objects/devices/this_device.py +12 -0
- _balder/parametrization.py +75 -0
- _balder/plugin_manager.py +138 -0
- _balder/previous_executor_mark.py +23 -0
- _balder/routing_path.py +335 -0
- _balder/scenario.py +20 -0
- _balder/setup.py +18 -0
- _balder/solver.py +246 -0
- _balder/testresult.py +163 -0
- _balder/unmapped_vdevice.py +13 -0
- _balder/utils/__init__.py +0 -0
- _balder/utils/functions.py +103 -0
- _balder/utils/inner_device_managing_metaclass.py +14 -0
- _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
- _balder/utils/typings.py +4 -0
- _balder/vdevice.py +9 -0
- balder/__init__.py +56 -0
- balder/connections.py +43 -0
- balder/devices.py +9 -0
- balder/exceptions.py +44 -0
- balder/parametrization.py +8 -0
- baldertest-0.1.0.dist-info/METADATA +356 -0
- baldertest-0.1.0.dist-info/RECORD +89 -0
- baldertest-0.1.0.dist-info/WHEEL +5 -0
- baldertest-0.1.0.dist-info/entry_points.txt +2 -0
- baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
- baldertest-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import List, Union, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
import traceback
|
|
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
|
|
11
|
+
from _balder.testresult import ResultState, TestcaseResult
|
|
12
|
+
from _balder.utils.functions import get_method_type
|
|
13
|
+
from _balder.utils.mixin_can_be_covered_by_executor import MixinCanBeCoveredByExecutor
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
17
|
+
from _balder.executor.variation_executor import VariationExecutor
|
|
18
|
+
from _balder.fixture_manager import FixtureManager
|
|
19
|
+
from _balder.scenario import Scenario
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestcaseExecutor(BasicExecutableExecutor, MixinCanBeCoveredByExecutor):
|
|
23
|
+
"""
|
|
24
|
+
A TestcaseExecutor class represents an actual single test that can be executed. It therefore references exactly to a
|
|
25
|
+
test method of a scenario that can be executed on the specific setup this executor belongs to.
|
|
26
|
+
"""
|
|
27
|
+
fixture_execution_level = FixtureExecutionLevel.TESTCASE
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
testcase: callable,
|
|
32
|
+
parent: VariationExecutor,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
self._base_testcase_callable = testcase
|
|
37
|
+
self._parent_executor = parent
|
|
38
|
+
self._fixture_manager: FixtureManager = parent.fixture_manager
|
|
39
|
+
|
|
40
|
+
# if there exist executors that cover this item, they are contained as list in this property
|
|
41
|
+
self.covered_by_executors = None
|
|
42
|
+
|
|
43
|
+
# contains the result object for the BODY part of this branch
|
|
44
|
+
self.body_result = TestcaseResult(self)
|
|
45
|
+
|
|
46
|
+
# holds the raw test execution time in seconds
|
|
47
|
+
self.test_execution_time_sec = 0
|
|
48
|
+
|
|
49
|
+
# determine prev_mark IGNORE/SKIP for the testcase
|
|
50
|
+
if self.should_be_skipped():
|
|
51
|
+
self.prev_mark = PreviousExecutorMark.SKIP
|
|
52
|
+
# always overwrite if it should be ignored
|
|
53
|
+
if self.should_be_ignored():
|
|
54
|
+
self.prev_mark = PreviousExecutorMark.IGNORE
|
|
55
|
+
|
|
56
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
# ---------------------------------- CLASS METHODS ----------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def all_child_executors(self) -> None:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def parent_executor(self) -> VariationExecutor:
|
|
68
|
+
return self._parent_executor
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def scenario_executor(self) -> ScenarioExecutor:
|
|
72
|
+
return self.parent_executor.parent_executor
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def base_instance(self) -> object:
|
|
76
|
+
"""
|
|
77
|
+
returns the base class instance to which this executor instance belongs"""
|
|
78
|
+
return self._base_testcase_callable
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def base_testcase_callable(self) -> callable:
|
|
82
|
+
"""returns the testcase function"""
|
|
83
|
+
return self._base_testcase_callable
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def base_testcase_obj(self) -> Scenario:
|
|
87
|
+
"""
|
|
88
|
+
return the testcase Scenario this testcase is defined in
|
|
89
|
+
"""
|
|
90
|
+
return self._parent_executor.cur_scenario_class
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def fixture_manager(self):
|
|
94
|
+
"""returns the current active fixture manager"""
|
|
95
|
+
return self._fixture_manager
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def full_test_name_str(self) -> str:
|
|
99
|
+
"""
|
|
100
|
+
returns the name of the test method, that should be used in output
|
|
101
|
+
"""
|
|
102
|
+
return self._base_testcase_callable.__qualname__
|
|
103
|
+
|
|
104
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
def _prepare_execution(self, show_discarded):
|
|
107
|
+
print(f" TEST {self.full_test_name_str} ", end='')
|
|
108
|
+
|
|
109
|
+
def _body_execution(self, show_discarded):
|
|
110
|
+
self.test_execution_time_sec = 0
|
|
111
|
+
|
|
112
|
+
if self.should_be_skipped():
|
|
113
|
+
self.body_result.set_result(ResultState.SKIP)
|
|
114
|
+
return
|
|
115
|
+
if self.should_be_ignored():
|
|
116
|
+
self.body_result.set_result(ResultState.NOT_RUN)
|
|
117
|
+
return
|
|
118
|
+
if self.is_covered_by():
|
|
119
|
+
self.body_result.set_result(ResultState.COVERED_BY)
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
start_time = time.perf_counter()
|
|
123
|
+
try:
|
|
124
|
+
func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
|
|
125
|
+
all_args = self.get_all_test_method_args()
|
|
126
|
+
if func_type == "staticmethod":
|
|
127
|
+
# testcase is a staticmethod - no special first attribute
|
|
128
|
+
self.base_testcase_callable(**all_args)
|
|
129
|
+
elif func_type == "classmethod":
|
|
130
|
+
self.base_testcase_callable(self=self.base_testcase_obj.__class__, **all_args)
|
|
131
|
+
elif func_type == "instancemethod":
|
|
132
|
+
self.base_testcase_callable(self=self.base_testcase_obj, **all_args)
|
|
133
|
+
else:
|
|
134
|
+
# `function` is not allowed here!
|
|
135
|
+
raise ValueError(f"found illegal value for func_type `{func_type}` for test "
|
|
136
|
+
f"`{self.base_testcase_callable.__name__}`")
|
|
137
|
+
|
|
138
|
+
self.body_result.set_result(ResultState.SUCCESS)
|
|
139
|
+
except Exception as exc: # pylint: disable=broad-exception-caught
|
|
140
|
+
# this has to be a test error
|
|
141
|
+
traceback.print_exception(*sys.exc_info())
|
|
142
|
+
self.body_result.set_result(ResultState.FAILURE, exc)
|
|
143
|
+
self.test_execution_time_sec = time.perf_counter() - start_time
|
|
144
|
+
|
|
145
|
+
def _cleanup_execution(self, show_discarded):
|
|
146
|
+
print(f"[{self.body_result.get_result_as_char()}]")
|
|
147
|
+
|
|
148
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
def should_run(self):
|
|
151
|
+
"""returns true if the testcase should be executed (defined in scenario)"""
|
|
152
|
+
if self.base_testcase_callable in self.parent_executor.parent_executor.all_run_tests:
|
|
153
|
+
return True
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def should_be_skipped(self):
|
|
157
|
+
"""returns true if the testcase should be skipped (defined in scenario)"""
|
|
158
|
+
if self.base_testcase_callable in self.parent_executor.parent_executor.all_skip_tests:
|
|
159
|
+
return True
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
def should_be_ignored(self):
|
|
163
|
+
"""returns true if the testcase should be ignored (defined in scenario)"""
|
|
164
|
+
if self.base_testcase_callable in self.parent_executor.parent_executor.all_ignore_tests:
|
|
165
|
+
return True
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def is_covered_by(self):
|
|
169
|
+
"""returns true if the testcase is covered-by"""
|
|
170
|
+
return self.prev_mark == PreviousExecutorMark.COVERED_BY
|
|
171
|
+
|
|
172
|
+
def has_skipped_tests(self) -> bool:
|
|
173
|
+
return self.prev_mark == PreviousExecutorMark.SKIP
|
|
174
|
+
|
|
175
|
+
def has_covered_by_tests(self) -> bool:
|
|
176
|
+
return self.prev_mark == PreviousExecutorMark.COVERED_BY
|
|
177
|
+
|
|
178
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
179
|
+
"""
|
|
180
|
+
This method searches the whole tree and removes branches where an executor item has no own children. It can
|
|
181
|
+
remove these branches, because they have no valid matchings.
|
|
182
|
+
|
|
183
|
+
This method implementation of the :class:`TestcaseExecutor` does nothing.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def get_covered_by_element(self) -> List[Union[Scenario, callable]]:
|
|
187
|
+
covered_by_dict = self.scenario_executor.base_scenario_controller.get_abs_covered_by_dict()
|
|
188
|
+
all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, [])
|
|
189
|
+
# also add all scenario specified covered-by elements
|
|
190
|
+
all_covered_by_data.extend(covered_by_dict.get(None, []))
|
|
191
|
+
return all_covered_by_data
|
|
192
|
+
|
|
193
|
+
def get_all_test_method_args(self):
|
|
194
|
+
"""
|
|
195
|
+
returns all kwargs values for the test method
|
|
196
|
+
"""
|
|
197
|
+
func_type = get_method_type(self.base_testcase_obj.__class__, self.base_testcase_callable)
|
|
198
|
+
return self.fixture_manager.get_all_attribute_values(
|
|
199
|
+
self,
|
|
200
|
+
self.base_testcase_obj.__class__,
|
|
201
|
+
self.base_testcase_callable,
|
|
202
|
+
func_type
|
|
203
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
from _balder.utils.mixin_can_be_covered_by_executor import MixinCanBeCoveredByExecutor
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
18
|
+
from _balder.executor.variation_executor import VariationExecutor
|
|
19
|
+
from _balder.scenario import Scenario
|
|
20
|
+
from _balder.setup import Setup
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UnresolvedParametrizedTestcaseExecutor(BasicExecutor, MixinCanBeCoveredByExecutor):
|
|
24
|
+
"""
|
|
25
|
+
This executor class represents a group of dynamically parametrized tests.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
testcase: callable,
|
|
31
|
+
parent: VariationExecutor,
|
|
32
|
+
static_parameters: Dict[str, Any] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
self._base_testcase_callable = testcase
|
|
37
|
+
self._parent_executor = parent
|
|
38
|
+
|
|
39
|
+
# holds the specific static parameters for this unresolved group
|
|
40
|
+
self._static_parameters = static_parameters if static_parameters is not None else {}
|
|
41
|
+
|
|
42
|
+
# contains the result object for the BODY part of this branch
|
|
43
|
+
self.body_result = BranchBodyResult(self)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def parent_executor(self) -> VariationExecutor:
|
|
47
|
+
return self._parent_executor
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def scenario_executor(self) -> ScenarioExecutor:
|
|
51
|
+
return self.parent_executor.parent_executor
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def base_testcase_callable(self) -> callable:
|
|
55
|
+
"""returns the testcase function"""
|
|
56
|
+
return self._base_testcase_callable
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def all_child_executors(self) -> None:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def base_testcase_obj(self) -> Scenario:
|
|
64
|
+
"""
|
|
65
|
+
return the testcase Scenario this testcase is defined in
|
|
66
|
+
"""
|
|
67
|
+
return self.parent_executor.cur_scenario_class
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def base_instance(self) -> object:
|
|
71
|
+
"""
|
|
72
|
+
returns the base class instance to which this executor instance belongs"""
|
|
73
|
+
return self._base_testcase_callable
|
|
74
|
+
|
|
75
|
+
def has_skipped_tests(self) -> bool:
|
|
76
|
+
return self.prev_mark == PreviousExecutorMark.SKIP
|
|
77
|
+
|
|
78
|
+
def has_covered_by_tests(self) -> bool:
|
|
79
|
+
return self.prev_mark == PreviousExecutorMark.COVERED_BY
|
|
80
|
+
|
|
81
|
+
def get_all_base_instances_of_this_branch(
|
|
82
|
+
self, with_type: Type[Setup] | Type[Scenario] | Type[types.FunctionType],
|
|
83
|
+
only_runnable_elements: bool = True) -> List[Setup | Scenario | object]:
|
|
84
|
+
"""
|
|
85
|
+
returns itself if the type matches, otherwise an empty list
|
|
86
|
+
"""
|
|
87
|
+
# todo
|
|
88
|
+
if isinstance(self.base_instance, with_type):
|
|
89
|
+
return [self.base_instance]
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
def get_covered_by_element(self) -> List[Scenario | callable]:
|
|
93
|
+
covered_by_dict = self.scenario_executor.base_scenario_controller.get_abs_covered_by_dict()
|
|
94
|
+
all_covered_by_data = covered_by_dict.get(self.base_testcase_callable.__name__, [])
|
|
95
|
+
# also add all scenario specified covered-by elements
|
|
96
|
+
all_covered_by_data.extend(covered_by_dict.get(None, []))
|
|
97
|
+
return all_covered_by_data
|
|
98
|
+
|
|
99
|
+
def get_resolved_parametrized_testcase_executors(self):
|
|
100
|
+
"""
|
|
101
|
+
resolves the dynamic parametrization - should be called when setup features are active in the scenario
|
|
102
|
+
"""
|
|
103
|
+
executors = []
|
|
104
|
+
|
|
105
|
+
parametrization = self.get_parametrization()
|
|
106
|
+
|
|
107
|
+
if not parametrization:
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
for cur_parametrization in parametrization:
|
|
111
|
+
executors.append(
|
|
112
|
+
ParametrizedTestcaseExecutor(
|
|
113
|
+
self._base_testcase_callable,
|
|
114
|
+
parent=self.parent_executor,
|
|
115
|
+
parametrization=cur_parametrization,
|
|
116
|
+
unresolved_group_obj=self
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
return executors
|
|
120
|
+
|
|
121
|
+
def get_parametrization(self) -> List[OrderedDict[str, Any]] | None:
|
|
122
|
+
"""
|
|
123
|
+
returns all parametrization elements that belongs to this group executor
|
|
124
|
+
"""
|
|
125
|
+
scenario_controller = self.parent_executor.parent_executor.base_scenario_controller
|
|
126
|
+
dynamic_parametrization = scenario_controller.get_parametrization_for(self._base_testcase_callable,
|
|
127
|
+
static=False)
|
|
128
|
+
if not dynamic_parametrization:
|
|
129
|
+
raise ValueError('can not determine dynamic parametrization, because there are no dynamic parameters')
|
|
130
|
+
|
|
131
|
+
# sort attributes according their Parameter - using TopologicalSorter
|
|
132
|
+
graph = {attr: [param.name for param in config.get_parameters(of_type=Parameter).values()]
|
|
133
|
+
for attr, config in dynamic_parametrization.items()}
|
|
134
|
+
# also add all elements from static parameters
|
|
135
|
+
graph.update({param: [] for param in self._static_parameters})
|
|
136
|
+
|
|
137
|
+
ts = TopologicalSorter(graph)
|
|
138
|
+
resolvable_order_of_attribues = ts.static_order()
|
|
139
|
+
resolvable_dynamic_attribues = [attr for attr in resolvable_order_of_attribues
|
|
140
|
+
if attr in dynamic_parametrization.keys()]
|
|
141
|
+
|
|
142
|
+
def get_variations_for(
|
|
143
|
+
resolved_parameters: Dict[str, Any],
|
|
144
|
+
remaining_attributes: Iterable[str]
|
|
145
|
+
) -> List[Dict[str, Any]]:
|
|
146
|
+
result = []
|
|
147
|
+
remaining_attributes = list(remaining_attributes).copy()
|
|
148
|
+
attr = remaining_attributes.pop(0)
|
|
149
|
+
feature_access_selector = dynamic_parametrization[attr]
|
|
150
|
+
# get value for this attribute
|
|
151
|
+
attr_value_list = feature_access_selector.get_value(resolved_parameters)
|
|
152
|
+
if not isinstance(attr_value_list, Iterable):
|
|
153
|
+
raise TypeError(
|
|
154
|
+
f'feature parametrizing not possible, because `{feature_access_selector.device.__qualname__}'
|
|
155
|
+
f'.{feature_access_selector.device_property_name}.{feature_access_selector.feature_property_name}` '
|
|
156
|
+
f'does not return Iterable')
|
|
157
|
+
|
|
158
|
+
for cur_value in attr_value_list:
|
|
159
|
+
parameters_with_cur_attr_value = resolved_parameters.copy()
|
|
160
|
+
parameters_with_cur_attr_value[attr] = cur_value
|
|
161
|
+
if len(remaining_attributes) > 0:
|
|
162
|
+
result.extend(get_variations_for(parameters_with_cur_attr_value, remaining_attributes))
|
|
163
|
+
else:
|
|
164
|
+
result.append(parameters_with_cur_attr_value)
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
resolved_parameters = self._static_parameters.copy()
|
|
169
|
+
|
|
170
|
+
all_full_parametrization = get_variations_for(resolved_parameters, resolvable_dynamic_attribues)
|
|
171
|
+
|
|
172
|
+
# get combined parametrization
|
|
173
|
+
result = []
|
|
174
|
+
for cur_full_parametrization in all_full_parametrization:
|
|
175
|
+
cur_parameter_set = OrderedDict()
|
|
176
|
+
for cur_arg in inspect.getfullargspec(self._base_testcase_callable).args:
|
|
177
|
+
# only if this is a parametrization value
|
|
178
|
+
if cur_arg in cur_full_parametrization:
|
|
179
|
+
cur_parameter_set[cur_arg] = cur_full_parametrization[cur_arg]
|
|
180
|
+
result.append(cur_parameter_set)
|
|
181
|
+
return result
|
|
182
|
+
|
|
183
|
+
def cleanup_empty_executor_branches(self, consider_discarded=False):
|
|
184
|
+
pass
|