1import re 2import os 3 4class TreeElem: 5 """ Result of the parsing of the test description. 6 7 It is a tree of objects describing the groups, suites and tests 8 9 Attributes: 10 kind (int) : Node kind 11 ident (int) : Indentation level in the test description. 12 It is used to format output of test results 13 parent (TreeElem) : parent of this node 14 id (int) : Node id 15 patterns (list) : List of pairs 16 Each pair is a pattern ID and pattern path 17 outputs (list) : List of pairs 18 Each pair is an output ID and an output path 19 20 """ 21 22 TEST = 1 23 SUITE = 2 24 GROUP = 3 25 26 PARAMFILE = 1 27 PARAMGEN = 2 28 29 def __init__(self,ident): 30 self.kind=TreeElem.TEST 31 self.ident = ident 32 self._children = [] 33 self.parent = None 34 self._data = None 35 self.id = 1 36 self._path="" 37 self.patterns=[] 38 self.outputs=[] 39 # List of parameters files 40 self.parameters=[] 41 # List of arguments 42 self.params = None 43 44 def __str__(self): 45 """ Convert the TreeElem into a string for debug purpose 46 """ 47 if self.kind == TreeElem.TEST: 48 g="Test" 49 if self.kind == TreeElem.SUITE: 50 g="Suite" 51 if self.kind == TreeElem.GROUP: 52 g="Group" 53 a = str("%s -> %s%s(%d)\n" % (g,' ' * self.ident, str(self.data),self.id)) 54 if self.params: 55 a = a + str(self.params.full) + "\n" + str(self.params.summary) + "\n" + str(self.params.paramNames) + "\n" 56 for i in self._children: 57 a = a + str(i) 58 return(a) 59 60 def setData(self,data): 61 """ Set the data property of this node 62 63 Args: 64 data (list) : A list of fields for this node 65 The fields are parsed and a data dictionary is created 66 fpga (bool) : false in semihosting mode 67 Raises: 68 Nothing 69 Returns: 70 Nothing 71 """ 72 d = {} 73 74 # A node OFF in the list is deprecated. It won't be included 75 # or executed in the final tests 76 # but it will be taken into account for ID generation 77 d["deprecated"] = False 78 # Text message to display to the user zhen displaying test result 79 # This text message is never used in any .txt,.cpp or .h 80 # generated. It is only for better display of the test 81 # results 82 d["message"] = data[0].strip() 83 # CPP class or function name to use 84 if len(data) > 1: 85 d["class"] = data[1].strip() 86 if len(data) == 3: 87 if data[2].strip() == "OFF": 88 d["deprecated"] = True 89 else: 90 self._path = data[2].strip() 91 # New path for this node (when we want a new subfolder 92 # for the patterns or output of a group of suite) 93 if len(data) == 4: 94 self._path = data[3].strip() 95 96 self._data = d 97 98 @property 99 def data(self): 100 return(self._data) 101 102 def writeData(self,d): 103 self._data=d 104 105 def setPath(self,p): 106 self._path=p 107 108 @property 109 def path(self): 110 return(self._path) 111 112 @property 113 def children(self): 114 return(self._children) 115 116 def _fullPath(self): 117 if self.parent: 118 return(os.path.join(self.parent._fullPath() , self.path)) 119 else: 120 return("") 121 122 def fullPath(self): 123 return(os.path.normpath(self._fullPath())) 124 125 def categoryDesc(self): 126 if self.parent: 127 p = self.parent.categoryDesc() 128 if p and self.path: 129 return(p + ":" + self.path) 130 if p: 131 return(p) 132 if self.path: 133 return(self.path) 134 else: 135 return("") 136 137 def getSuiteMessage(self): 138 suite = self.parent 139 group = suite.parent 140 p = group.data["message"] 141 return(p) 142 143 def addGroup(self,g): 144 """ Add a group to this node 145 146 Args: 147 g (TreeElem) : group to add 148 Raises: 149 Nothing 150 Returns: 151 Nothing 152 """ 153 g.parent = self 154 self._children.append(g) 155 156 def classify(self): 157 """ Compute the node kind recursively 158 159 Node kind is infered from the tree structure and not present 160 in the test description. 161 A suite is basically a leaf of the tree and only contain tests. 162 A group is containing a suite or another group. 163 164 """ 165 r = TreeElem.TEST 166 for c in self._children: 167 c.classify() 168 169 170 for c in self._children: 171 if c.kind == TreeElem.TEST and r != TreeElem.GROUP: 172 r = TreeElem.SUITE 173 if c.kind == TreeElem.SUITE: 174 r = TreeElem.GROUP 175 if c.kind == TreeElem.GROUP: 176 r = TreeElem.GROUP 177 self.kind = r 178 179 def computeId(self): 180 """ Compute the node ID and the node param ID 181 """ 182 i = 1 183 for c in self._children: 184 c.id = i 185 if not "PARAMID" in c.data and "PARAMID" in self.data: 186 c.data["PARAMID"] = self.data["PARAMID"] 187 c.computeId() 188 i = i + 1 189 190 self.parameterToID={} 191 # PARAM ID is starting at 0 192 paramId=0 193 if self.parameters: 194 for (paramKind,pID,pPath) in self.parameters: 195 self.parameterToID[pID]=paramId 196 paramId = paramId + 1 197 198 def reident(self,current,d=2): 199 """ Recompute identation lebel 200 """ 201 self.ident=current 202 for c in self._children: 203 c.reident(current+d) 204 205 def findIdentParent(self,newIdent): 206 """ Find parent of this node based on the new identation level 207 208 Find the node which is the parent of this node with indentation level 209 newIdent. 210 211 Args: 212 newIdent (int) : identation of a new node read in the descriptino file 213 214 """ 215 if self.ident < newIdent: 216 return(self) 217 else: 218 return(self.parent.findIdentParent(newIdent)) 219 220 221 def __getitem__(self, i): 222 return(self._children[i]) 223 224 def __iter__(self): 225 self._currentPos = 0 226 return(self) 227 228 def __next__(self): 229 oldPos = self._currentPos 230 self._currentPos = self._currentPos + 1 231 if (oldPos >= len(self._children)): 232 raise StopIteration 233 return(self._children[oldPos]) 234 235 def addPattern(self,theId,thePath): 236 """ Add a new pattern 237 238 Args: 239 theId (int) : pattern ID 240 thePath (str) : pattern path 241 242 """ 243 self.patterns.append((theId,thePath)) 244 #print(thePath) 245 #print(self.patterns) 246 247 def addParam(self,paramKind,theId,theData): 248 """ Add a new parameter file 249 250 Args: 251 paramKind (int) : parameter kind (path or generator) 252 theId (int) : parameter ID 253 thePath (str or list) : parameter path or generator data 254 255 """ 256 self.parameters.append((paramKind,theId,theData)) 257 #print(thePath) 258 #print(self.patterns) 259 260 def addOutput(self,theId,thePath): 261 """ Add a new output 262 263 Args: 264 theId (int) : output ID 265 thePath (str) : output path 266 267 """ 268 self.outputs.append((theId,thePath)) 269 270 def parse(self,filePath): 271 """ Parser the test description file 272 273 Args: 274 filePath (str) : Path to the description file 275 """ 276 root = None 277 current = None 278 with open(filePath,"r") as ins: 279 for line in ins: 280 # Compute identation level 281 identLevel = 0 282 if re.match(r'^([ \t]+)[^ \t].*$',line): 283 leftSpaces=re.sub(r'^([ \t]+)[^ \t].*$',r'\1',line.rstrip()) 284 #print("-%s-" % leftSpaces) 285 identLevel = len(leftSpaces) 286 # Remove comments 287 line = re.sub(r'^(.*)//.*$',r'\1',line).rstrip() 288 # If line is not just a comment 289 if line: 290 regPat = r'^[ \t]+Pattern[ \t]+([a-zA-Z0-9_]+)[ \t]*:[ \t]*(.+)$' 291 regOutput = r'^[ \t]+Output[ \t]+([a-zA-Z0-9_]+)[ \t]*:[ \t]*(.+)$' 292 # If a pattern line is detected, we record it 293 if re.match(regPat,line): 294 m = re.match(regPat,line) 295 patternID = m.group(1).strip() 296 patternPath = m.group(2).strip() 297 #print(patternID) 298 #print(patternPath) 299 if identLevel > current.ident: 300 current.addPattern(patternID,patternPath) 301 # If an output line is detected, we record it 302 elif re.match(regOutput,line): 303 m = re.match(regOutput,line) 304 outputID = m.group(1).strip() 305 outputPath = m.group(2).strip() 306 #print(patternID) 307 #print(patternPath) 308 if identLevel > current.ident: 309 current.addOutput(outputID,outputPath) 310 else: 311 #if current is None: 312 # print(" -> %d" % (identLevel)) 313 #else: 314 # print("%d -> %d" % (current.ident,identLevel)) 315 # Separate line into components 316 data = line.split(':') 317 # Remove empty strings 318 data = [item for item in data if item] 319 # If it is the first node we detect, it is the root node 320 if root is None: 321 root = TreeElem(identLevel) 322 root.setData(data) 323 current = root 324 else: 325 # We analyse and set the data 326 newItem = TreeElem(identLevel) 327 newItem.setData(data) 328 # New identation then it is a group (or suite) 329 if identLevel > current.ident: 330 #print( ">") 331 current.addGroup(newItem) 332 current = newItem 333 # Same identation, we add to parent 334 elif identLevel == current.ident: 335 #print( "==") 336 current.parent.addGroup(newItem) 337 else: 338 #print("<") 339 #print("--") 340 #print(identLevel) 341 # Smaller identation we need to find the parent where to 342 # attach this node. 343 current = current.findIdentParent(identLevel) 344 current.addGroup(newItem) 345 current = newItem 346 347 #print(identLevel) 348 #print(data) 349 350 # Identify suites, groups and tests 351 # Above we are just adding TreeElement but we don't yet know their 352 # kind. So we classify them to now if we have group, suite or test 353 root.classify() 354 # We compute ID of all nodes. 355 root.computeId() 356 return(root) 357 358