Parser.py 11 KB

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