1# coding=utf-8 2 3 4class SerialTreeManager(object): 5 """ 6 Manages output tree by building it from flat test names. 7 """ 8 9 def __init__(self, offset): 10 super(SerialTreeManager, self).__init__() 11 # Currently active branch as list. New nodes go to this branch 12 self.current_branch = [] 13 # node unique name to its nodeId 14 self._node_ids_dict = {} 15 # Node id mast be incremented for each new branch 16 self._max_node_id = offset 17 self.offset = offset 18 19 def _calculate_relation(self, branch_as_list): 20 """ 21 Get relation of branch_as_list to current branch. 22 :return: tuple. First argument could be: "same", "child", "parent" or "sibling"(need to start new tree) 23 Second argument is relative path from current branch to child if argument is child 24 """ 25 if branch_as_list == self.current_branch: 26 return "same", None 27 28 hierarchy_name_len = len(branch_as_list) 29 current_branch_len = len(self.current_branch) 30 31 if hierarchy_name_len > current_branch_len and branch_as_list[0:current_branch_len] == self.current_branch: 32 return "child", branch_as_list[current_branch_len:] 33 34 if hierarchy_name_len < current_branch_len and self.current_branch[0:hierarchy_name_len] == branch_as_list: 35 return "parent", None 36 37 return "sibling", None 38 39 def _add_new_node(self, new_node_name): 40 """ 41 Adds new node to branch 42 """ 43 self.current_branch.append(new_node_name) 44 self._max_node_id += 1 45 self._node_ids_dict[".".join(self.current_branch)] = self._max_node_id 46 47 def level_opened(self, test_as_list, func_to_open): 48 """ 49 To be called on test start. 50 51 :param test_as_list: test name splitted as list 52 :param func_to_open: func to be called if test can open new level 53 :return: None if new level opened, or tuple of command client should execute and try opening level again 54 Command is "open" (open provided level) or "close" (close it). Second item is test name as list 55 """ 56 relation, relative_path = self._calculate_relation(test_as_list) 57 if relation == 'same': 58 return # Opening same level? 59 if relation == 'child': 60 # If one level -- open new level gracefully 61 if len(relative_path) == 1: 62 self._add_new_node(relative_path[0]) 63 func_to_open() 64 return None 65 else: 66 # Open previous level 67 return [("open", self.current_branch + relative_path[0:1])] 68 if relation == "sibling": 69 if self.current_branch: 70 # Different tree, close whole branch 71 return [("close", self.current_branch)] 72 else: 73 return None 74 if relation == 'parent': 75 # Opening parent? Insane 76 pass 77 78 def level_closed(self, test_as_list, func_to_close): 79 """ 80 To be called on test end or failure. 81 82 See level_opened doc. 83 """ 84 relation, relative_path = self._calculate_relation(test_as_list) 85 if relation == 'same': 86 # Closing current level 87 func_to_close() 88 self.current_branch.pop() 89 if relation == 'child': 90 return None 91 92 if relation == 'sibling': 93 pass 94 if relation == 'parent': 95 return [("close", self.current_branch)] 96 97 @property 98 def parent_branch(self): 99 return self.current_branch[:-1] if self.current_branch else None 100 101 def _get_node_id(self, branch): 102 return self._node_ids_dict[".".join(branch)] 103 104 # Part of contract 105 # noinspection PyUnusedLocal 106 def get_node_ids(self, test_name): 107 """ 108 109 :return: (current_node_id, parent_node_id) 110 """ 111 current = self._get_node_id(self.current_branch) 112 parent = self._get_node_id(self.parent_branch) if self.parent_branch else str(self.offset) 113 return str(current), str(parent) 114