blob: d186eb6de05a509648f1396ba6486727bcdc7d88 [file] [log] [blame]
Christophe Favergeon3b2a0ee2019-06-12 13:29:14 +02001# Process the test results
2# Test status (like passed, or failed with error code)
3
4import argparse
5import re
6import TestScripts.NewParser as parse
7import TestScripts.CodeGen
8from collections import deque
9import os.path
10import csv
11
12def findItem(root,path):
13 """ Find a node in a tree
14
15 Args:
16 path (list) : A list of node ID
17 This list is describing a path in the tree.
18 By starting from the root and following this path,
19 we can find the node in the tree.
20 Raises:
21 Nothing
22 Returns:
23 TreeItem : A node
24 """
25 # The list is converted into a queue.
26 q = deque(path)
27 q.popleft()
28 c = root
29 while q:
30 n = q.popleft()
31 # We get the children based on its ID and continue
32 c = c[n-1]
33 return(c)
34
35def joinit(iterable, delimiter):
36 # Intersperse a delimiter between element of a list
37 it = iter(iterable)
38 yield next(it)
39 for x in it:
40 yield delimiter
41 yield x
42
43# Return test result as a text tree
44class TextFormatter:
45 def start(self):
46 None
47
48 def printGroup(self,elem,theId):
49 if elem is None:
50 elem = root
51 message=elem.data["message"]
52 if not elem.data["deprecated"]:
53 kind = "Suite"
54 ident = " " * elem.ident
55 if elem.kind == TestScripts.Parser.TreeElem.GROUP:
56 kind = "Group"
57 #print(elem.path)
58 print("%s%s : %s (%d)" % (ident,kind,message,theId))
59
60 def printTest(self,elem, theId, theError,theLine,passed,cycles,params):
61 message=elem.data["message"]
62 if not elem.data["deprecated"]:
63 kind = "Test"
64 ident = " " * elem.ident
65 p="FAILED"
66 if passed == 1:
67 p="PASSED"
68 print("%s%s (%d) : %s (cycles = %d)" % (ident,message,theId,p,cycles))
69 if params:
70 print("%s %s" % (ident,params))
71 if passed != 1:
72 print("%s Error = %d at line %d" % (ident, theError, theLine))
73
74 def pop(self):
75 None
76
77 def end(self):
78 None
79
80# Return test result as a CSV
81class CSVFormatter:
82
83 def __init__(self):
84 self.name=[]
85 self._start=True
86
87 def start(self):
88 print("CATEGORY,NAME,ID,STATUS,CYCLES,PARAMS")
89
90 def printGroup(self,elem,theId):
91 if elem is None:
92 elem = root
93 # Remove Root from category name in CSV file.
94 if not self._start:
95 self.name.append(elem.data["class"])
96 else:
97 self._start=False
98 message=elem.data["message"]
99 if not elem.data["deprecated"]:
100 kind = "Suite"
101 ident = " " * elem.ident
102 if elem.kind == TestScripts.Parser.TreeElem.GROUP:
103 kind = "Group"
104
105 def printTest(self,elem, theId, theError, theLine,passed,cycles,params):
106 message=elem.data["message"]
107 if not elem.data["deprecated"]:
108 kind = "Test"
109 name=elem.data["class"]
110 category= "".join(list(joinit(self.name,":")))
111 print("%s,%s,%d,%d,%d,\"%s\"" % (category,name,theId,passed,cycles,params))
112
113 def pop(self):
114 if self.name:
115 self.name.pop()
116
117 def end(self):
118 None
119
120class MathematicaFormatter:
121
122 def __init__(self):
123 self._hasContent=[False]
124 self._toPop=[]
125
126 def start(self):
127 None
128
129 def printGroup(self,elem,theId):
130 if self._hasContent[len(self._hasContent)-1]:
131 print(",",end="")
132
133 print("<|")
134 self._hasContent[len(self._hasContent)-1] = True
135 self._hasContent.append(False)
136 if elem is None:
137 elem = root
138 message=elem.data["message"]
139 if not elem.data["deprecated"]:
140
141 kind = "Suite"
142 ident = " " * elem.ident
143 if elem.kind == TestScripts.Parser.TreeElem.GROUP:
144 kind = "Group"
145 print("\"%s\" ->" % (message))
146 #if kind == "Suite":
147 print("{",end="")
148 self._toPop.append("}")
149 #else:
150 # self._toPop.append("")
151
152 def printTest(self,elem, theId, theError,theLine,passed,cycles,params):
153 message=elem.data["message"]
154 if not elem.data["deprecated"]:
155 kind = "Test"
156 ident = " " * elem.ident
157 p="FAILED"
158 if passed == 1:
159 p="PASSED"
160 parameters=""
161 if params:
162 parameters = "%s" % params
163 if self._hasContent[len(self._hasContent)-1]:
164 print(",",end="")
165 print("<|\"NAME\" -> \"%s\",\"ID\" -> %d,\"STATUS\" -> \"%s\",\"CYCLES\" -> %d,\"PARAMS\" -> \"%s\"|>" % (message,theId,p,cycles,parameters))
166 self._hasContent[len(self._hasContent)-1] = True
167 #if passed != 1:
168 # print("%s Error = %d at line %d" % (ident, theError, theLine))
169
170 def pop(self):
171 print(self._toPop.pop(),end="")
172 print("|>")
173 self._hasContent.pop()
174
175 def end(self):
176 None
177
178NORMAL = 1
179INTEST = 2
180TESTPARAM = 3
181
182def createMissingDir(destPath):
183 theDir=os.path.normpath(os.path.dirname(destPath))
184 if not os.path.exists(theDir):
185 os.makedirs(theDir)
186
187def correctPath(path):
188 while (path[0]=="/") or (path[0] == "\\"):
189 path = path[1:]
190 return(path)
191
192def extractDataFiles(results,outputDir):
193 infile = False
194 f = None
195 for l in results:
196 if re.match(r'^.*D:[ ].*$',l):
197 if infile:
198 if re.match(r'^.*D:[ ]END$',l):
199 infile = False
200 if f:
201 f.close()
202 else:
203 if f:
204 m = re.match(r'^.*D:[ ](.*)$',l)
205 data = m.group(1)
206 f.write(data)
207 f.write("\n")
208
209 else:
210 m = re.match(r'^.*D:[ ](.*)$',l)
211 path = str(m.group(1))
212 infile = True
213 destPath = os.path.join(outputDir,correctPath(path))
214 createMissingDir(destPath)
215 f = open(destPath,"w")
216
217
218
219def writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config):
220 if benchFile:
221 name=elem.data["class"]
222 category= elem.categoryDesc()
223 benchFile.write("\"%s\",\"%s\",%d,%s,%d,%s\n" % (category,name,theId,params,cycles,config))
224
225def analyseResult(root,results,embedded,benchmark,formatter):
226 formatter.start()
227 path = []
228 state = NORMAL
229 prefix=""
230 elem=None
231 theId=None
232 theError=None
233 theLine=None
234 passed=0
235 cycles=None
236 benchFile = None
237 config=""
238 if embedded:
239 prefix = ".*S:[ ]"
240
241 # Parse the result file.
242 # NORMAL mode is when we are parsing suite or group.
243 # Otherwise we are parsing a test and we need to analyse the
244 # test result.
245 # TESTPARAM is used to read parameters of the test.
246 # Format of output is:
247 #node ident : s id or g id or t or u
248 #test status : id error linenb status Y or N (Y when passing)
249 #param for this test b x,x,x,x or b alone if not param
250 #node end : p
251 # In FPGA mode:
252 #Prefix S:[ ] before driver dump
253 # D:[ ] before data dump (output patterns)
254
255 for l in results:
256 l = l.strip()
257 if not re.match(r'^.*D:[ ].*$',l):
258 if state == NORMAL:
259 if len(l) > 0:
260 # Line starting with g or s is a suite or group.
261 # In FPGA mode, those line are prefixed with 'S: '
262 # and data file with 'D: '
263 if re.match(r'^%s[gs][ ]+[0-9]+.*$' % prefix,l):
264 # Extract the test id
265 theId=re.sub(r'^%s[gs][ ]+([0-9]+).*$' % prefix,r'\1',l)
266 theId=int(theId)
267 path.append(theId)
268 # From a list of id, find the TreeElem in the Parsed tree
269 # to know what is the node.
270 elem = findItem(root,path)
271 # Display formatted output for this node
272 if elem.params:
273 #print(elem.params.full)
274 benchPath = os.path.join(benchmark,elem.fullPath(),"fullBenchmark.csv")
275 createMissingDir(benchPath)
276 if benchFile:
277 printf("ERROR BENCH FILE %s ALREADY OPEN" % benchPath)
278 benchFile.close()
279 benchFile=None
280 benchFile=open(benchPath,"w")
281 header = "".join(list(joinit(elem.params.full,",")))
282 # A test and a benchmark are different
283 # so we don't dump a status and error
284 # A status and error in a benchmark would
285 # impact the cycles since the test
286 # would be taken into account in the measurement
287 # So benchmark are always passing and contain no test
288 #benchFile.write("ID,%s,PASSED,ERROR,CYCLES\n" % header)
289 csvheaders = ""
290
291 with open('currentConfig.csv', 'r') as f:
292 reader = csv.reader(f)
293 csvheaders = next(reader, None)
294 configList = list(reader)
295 #print(configList)
296 config = "".join(list(joinit(configList[0],",")))
297 configHeaders = "".join(list(joinit(csvheaders,",")))
298 benchFile.write("CATEGORY,NAME,ID,%s,CYCLES,%s\n" % (header,configHeaders))
299
300 formatter.printGroup(elem,theId)
301
302 # If we have detected a test, we switch to test mode
303 if re.match(r'^%s[t][ ]*$' % prefix,l):
304 state = INTEST
305
306
307 # Pop
308 # End of suite or group
309 if re.match(r'^%sp.*$' % prefix,l):
310 if benchFile:
311 benchFile.close()
312 benchFile=None
313 path.pop()
314 formatter.pop()
315 elif state == INTEST:
316 if len(l) > 0:
317 # In test mode, we are looking for test status.
318 # A line starting with S
319 # (There may be empty lines or line for data files)
320 passRe = r'^%s([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([YN]).*$' % prefix
321 if re.match(passRe,l):
322 # If we have found a test status then we will start again
323 # in normal mode after this.
324
325 m = re.match(passRe,l)
326
327 # Extract test ID, test error code, line number and status
328 theId=m.group(1)
329 theId=int(theId)
330
331 theError=m.group(2)
332 theError=int(theError)
333
334 theLine=m.group(3)
335 theLine=int(theLine)
336
337 cycles = int(m.group(4))
338
339 status=m.group(5)
340 passed=0
341
342 # Convert status to number as used by formatter.
343 if status=="Y":
344 passed = 1
345 if status=="N":
346 passed = 0
347 # Compute path to this node
348 newPath=path.copy()
349 newPath.append(theId)
350 # Find the node in the Tree
351 elem = findItem(root,newPath)
352
353
354 state = TESTPARAM
355 else:
356 if re.match(r'^%sp.*$' % prefix,l):
357 if benchFile:
358 benchFile.close()
359 benchFile=None
360 path.pop()
361 formatter.pop()
362 if re.match(r'^%s[t][ ]*$' % prefix,l):
363 state = INTEST
364 else:
365 state = NORMAL
366 else:
367 if len(l) > 0:
368 state = INTEST
369 params=""
370 if re.match(r'^.*b[ ]+([0-9,]+)$',l):
371 m=re.match(r'^.*b[ ]+([0-9,]+)$',l)
372 params=m.group(1).strip()
373 # Format the node
374 #print(elem.fullPath())
375 #createMissingDir(destPath)
376 writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
377 else:
378 params=""
379 writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
380 # Format the node
381 formatter.printTest(elem,theId,theError,theLine,passed,cycles,params)
382
383
384 formatter.end()
385
386
387parser = argparse.ArgumentParser(description='Parse test description')
388
389parser.add_argument('-f', nargs='?',type = str, default=None, help="Test description file path")
390# Where the result file can be found
391parser.add_argument('-r', nargs='?',type = str, default=None, help="Result file path")
392parser.add_argument('-c', action='store_true', help="CSV output")
393parser.add_argument('-e', action='store_true', help="Embedded test")
394# -o needed when -e is true to know where to extract the output files
395parser.add_argument('-o', nargs='?',type = str, default="Output", help="Output dir path")
396
397parser.add_argument('-b', nargs='?',type = str, default="FullBenchmark", help="Full Benchmark dir path")
398parser.add_argument('-m', action='store_true', help="Mathematica output")
399
400args = parser.parse_args()
401
402if args.f is not None:
403 p = parse.Parser()
404 # Parse the test description file
405 root = p.parse(args.f)
406 with open(args.r,"r") as results:
407 if args.c:
408 analyseResult(root,results,args.e,args.b,CSVFormatter())
409 elif args.m:
410 analyseResult(root,results,args.e,args.b,MathematicaFormatter())
411 else:
412 analyseResult(root,results,args.e,args.b,TextFormatter())
413 if args.e:
414 # In FPGA mode, extract output files from stdout (result file)
415 with open(args.r,"r") as results:
416 extractDataFiles(results,args.o)
417
418else:
419 parser.print_help()