Parser.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 addGroup(self,g):
  117. """ Add a group to this node
  118. Args:
  119. g (TreeElem) : group to add
  120. Raises:
  121. Nothing
  122. Returns:
  123. Nothing
  124. """
  125. g.parent = self
  126. self._children.append(g)
  127. def classify(self):
  128. """ Compute the node kind recursively
  129. Node kind is infered from the tree structure and not present
  130. in the test description.
  131. A suite is basically a leaf of the tree and only contain tests.
  132. A group is containing a suite or another group.
  133. """
  134. r = TreeElem.TEST
  135. for c in self._children:
  136. c.classify()
  137. for c in self._children:
  138. if c.kind == TreeElem.TEST and r != TreeElem.GROUP:
  139. r = TreeElem.SUITE
  140. if c.kind == TreeElem.SUITE:
  141. r = TreeElem.GROUP
  142. if c.kind == TreeElem.GROUP:
  143. r = TreeElem.GROUP
  144. self.kind = r
  145. def computeId(self):
  146. """ Compute the node ID and the node param ID
  147. """
  148. i = 1
  149. for c in self._children:
  150. c.id = i
  151. if not "PARAMID" in c.data and "PARAMID" in self.data:
  152. c.data["PARAMID"] = self.data["PARAMID"]
  153. c.computeId()
  154. i = i + 1
  155. self.parameterToID={}
  156. # PARAM ID is starting at 0
  157. paramId=0
  158. if self.parameters:
  159. for (paramKind,pID,pPath) in self.parameters:
  160. self.parameterToID[pID]=paramId
  161. paramId = paramId + 1
  162. def reident(self,current,d=2):
  163. """ Recompute identation lebel
  164. """
  165. self.ident=current
  166. for c in self._children:
  167. c.reident(current+d)
  168. def findIdentParent(self,newIdent):
  169. """ Find parent of this node based on the new identation level
  170. Find the node which is the parent of this node with indentation level
  171. newIdent.
  172. Args:
  173. newIdent (int) : identation of a new node read in the descriptino file
  174. """
  175. if self.ident < newIdent:
  176. return(self)
  177. else:
  178. return(self.parent.findIdentParent(newIdent))
  179. def __getitem__(self, i):
  180. return(self._children[i])
  181. def __iter__(self):
  182. self._currentPos = 0
  183. return(self)
  184. def __next__(self):
  185. oldPos = self._currentPos
  186. self._currentPos = self._currentPos + 1
  187. if (oldPos >= len(self._children)):
  188. raise StopIteration
  189. return(self._children[oldPos])
  190. def addPattern(self,theId,thePath):
  191. """ Add a new pattern
  192. Args:
  193. theId (int) : pattern ID
  194. thePath (str) : pattern path
  195. """
  196. self.patterns.append((theId,thePath))
  197. #print(thePath)
  198. #print(self.patterns)
  199. def addParam(self,paramKind,theId,theData):
  200. """ Add a new parameter file
  201. Args:
  202. paramKind (int) : parameter kind (path or generator)
  203. theId (int) : parameter ID
  204. thePath (str or list) : parameter path or generator data
  205. """
  206. self.parameters.append((paramKind,theId,theData))
  207. #print(thePath)
  208. #print(self.patterns)
  209. def addOutput(self,theId,thePath):
  210. """ Add a new output
  211. Args:
  212. theId (int) : output ID
  213. thePath (str) : output path
  214. """
  215. self.outputs.append((theId,thePath))
  216. def parse(self,filePath):
  217. """ Parser the test description file
  218. Args:
  219. filePath (str) : Path to the description file
  220. """
  221. root = None
  222. current = None
  223. with open(filePath,"r") as ins:
  224. for line in ins:
  225. # Compute identation level
  226. identLevel = 0
  227. if re.match(r'^([ \t]+)[^ \t].*$',line):
  228. leftSpaces=re.sub(r'^([ \t]+)[^ \t].*$',r'\1',line.rstrip())
  229. #print("-%s-" % leftSpaces)
  230. identLevel = len(leftSpaces)
  231. # Remove comments
  232. line = re.sub(r'^(.*)//.*$',r'\1',line).rstrip()
  233. # If line is not just a comment
  234. if line:
  235. regPat = r'^[ \t]+Pattern[ \t]+([a-zA-Z0-9_]+)[ \t]*:[ \t]*(.+)$'
  236. regOutput = r'^[ \t]+Output[ \t]+([a-zA-Z0-9_]+)[ \t]*:[ \t]*(.+)$'
  237. # If a pattern line is detected, we record it
  238. if re.match(regPat,line):
  239. m = re.match(regPat,line)
  240. patternID = m.group(1).strip()
  241. patternPath = m.group(2).strip()
  242. #print(patternID)
  243. #print(patternPath)
  244. if identLevel > current.ident:
  245. current.addPattern(patternID,patternPath)
  246. # If an output line is detected, we record it
  247. elif re.match(regOutput,line):
  248. m = re.match(regOutput,line)
  249. outputID = m.group(1).strip()
  250. outputPath = m.group(2).strip()
  251. #print(patternID)
  252. #print(patternPath)
  253. if identLevel > current.ident:
  254. current.addOutput(outputID,outputPath)
  255. else:
  256. #if current is None:
  257. # print(" -> %d" % (identLevel))
  258. #else:
  259. # print("%d -> %d" % (current.ident,identLevel))
  260. # Separate line into components
  261. data = line.split(':')
  262. # Remove empty strings
  263. data = [item for item in data if item]
  264. # If it is the first node we detect, it is the root node
  265. if root is None:
  266. root = TreeElem(identLevel)
  267. root.setData(data)
  268. current = root
  269. else:
  270. # We analyse and set the data
  271. newItem = TreeElem(identLevel)
  272. newItem.setData(data)
  273. # New identation then it is a group (or suite)
  274. if identLevel > current.ident:
  275. #print( ">")
  276. current.addGroup(newItem)
  277. current = newItem
  278. # Same identation, we add to parent
  279. elif identLevel == current.ident:
  280. #print( "==")
  281. current.parent.addGroup(newItem)
  282. else:
  283. #print("<")
  284. #print("--")
  285. #print(identLevel)
  286. # Smaller identation we need to find the parent where to
  287. # attach this node.
  288. current = current.findIdentParent(identLevel)
  289. current.addGroup(newItem)
  290. current = newItem
  291. #print(identLevel)
  292. #print(data)
  293. # Identify suites, groups and tests
  294. # Above we are just adding TreeElement but we don't yet know their
  295. # kind. So we classify them to now if we have group, suite or test
  296. root.classify()
  297. # We compute ID of all nodes.
  298. root.computeId()
  299. return(root)