| 172 | |
| 173 | |
| 174 | class Probe(object): |
| 175 | def __init__(self, tree, f, eval_on_call=False): |
| 176 | self.tree = tree |
| 177 | self.f = f |
| 178 | self.test_name = f.__name__ |
| 179 | self.eval_on_call = eval_on_call |
| 180 | # TODO: auto sub_test detection |
| 181 | self.sub_tests = SUB_TESTS.get(self.test_name) or [] |
| 182 | |
| 183 | def __call__(self, *args, **kwargs): |
| 184 | """Bind arguments to original function signature, and store in a Node |
| 185 | |
| 186 | This is used to discover what tests and sub-tests the SCT would like to |
| 187 | call, and defer them for later execution via their node instance. Node |
| 188 | instances are assembled into a tree. |
| 189 | |
| 190 | """ |
| 191 | if (len(args) > 0 and not isinstance(args[0], State)) or len(args) == 0: |
| 192 | # no state placeholder if a state is passed |
| 193 | args = ["state_placeholder"] + list(args) |
| 194 | |
| 195 | bound_args = inspect.signature(self.f).bind(*args, **kwargs) |
| 196 | |
| 197 | data = dict(bound_args=bound_args, func=self.f) |
| 198 | this_node = Node(data=data, name=self.test_name) |
| 199 | if self.tree is not None: |
| 200 | self.tree.crnt_node.add_child(this_node) |
| 201 | |
| 202 | # First pass to set up branches off node |
| 203 | arguments = bound_args.arguments |
| 204 | for subtest in self.sub_tests: # TODO: auto sub test detection |
| 205 | if subtest in arguments and arguments[subtest]: |
| 206 | self.build_sub_test_nodes( |
| 207 | arguments[subtest], self.tree, this_node, subtest |
| 208 | ) |
| 209 | |
| 210 | # Second pass to build node and all its children into a subtest |
| 211 | for node in this_node.descend(include_me=True): |
| 212 | if node.updated: # already built, e.g. node used multiple times |
| 213 | continue |
| 214 | else: |
| 215 | node.update_child_calls() |
| 216 | |
| 217 | if self.eval_on_call: |
| 218 | return this_node() |
| 219 | else: |
| 220 | return this_node |
| 221 | |
| 222 | @staticmethod |
| 223 | def build_sub_test_nodes(test, tree, node, arg_name): |
| 224 | # note that I've made the strong assumption that |
| 225 | # if not a function, then test is a dict, list or tuple of them |
| 226 | if isinstance(test, (list, tuple)): |
| 227 | nl = NodeList(name="List", arg_name=arg_name) |
| 228 | node.add_child(nl) |
| 229 | for ii, f in enumerate(test): |
| 230 | Probe.build_sub_test_nodes(f, tree, nl, str(ii)) |
| 231 | elif isinstance(test, Node): |
no outgoing calls
no test coverage detected