blob: 543c36bb281669325de42a657c9e94f1919520dd [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()
Christophe Favergeon37b86222019-07-17 11:49:00 +0200223 old=""
224 if "testData" in elem.data:
225 if "oldID" in elem.data["testData"]:
226 old=elem.data["testData"]["oldID"]
227 benchFile.write("\"%s\",\"%s\",%d,\"%s\",%s,%d,%s\n" % (category,name,theId,old,params,cycles,config))
Christophe Favergeon3b2a0ee2019-06-12 13:29:14 +0200228
229def analyseResult(root,results,embedded,benchmark,formatter):
230 formatter.start()
231 path = []
232 state = NORMAL
233 prefix=""
234 elem=None
235 theId=None
236 theError=None
237 theLine=None
238 passed=0
239 cycles=None
240 benchFile = None
241 config=""
242 if embedded:
243 prefix = ".*S:[ ]"
244
245 # Parse the result file.
246 # NORMAL mode is when we are parsing suite or group.
247 # Otherwise we are parsing a test and we need to analyse the
248 # test result.
249 # TESTPARAM is used to read parameters of the test.
250 # Format of output is:
251 #node ident : s id or g id or t or u
252 #test status : id error linenb status Y or N (Y when passing)
253 #param for this test b x,x,x,x or b alone if not param
254 #node end : p
255 # In FPGA mode:
256 #Prefix S:[ ] before driver dump
257 # D:[ ] before data dump (output patterns)
258
259 for l in results:
260 l = l.strip()
261 if not re.match(r'^.*D:[ ].*$',l):
262 if state == NORMAL:
263 if len(l) > 0:
264 # Line starting with g or s is a suite or group.
265 # In FPGA mode, those line are prefixed with 'S: '
266 # and data file with 'D: '
267 if re.match(r'^%s[gs][ ]+[0-9]+.*$' % prefix,l):
268 # Extract the test id
269 theId=re.sub(r'^%s[gs][ ]+([0-9]+).*$' % prefix,r'\1',l)
270 theId=int(theId)
271 path.append(theId)
272 # From a list of id, find the TreeElem in the Parsed tree
273 # to know what is the node.
274 elem = findItem(root,path)
275 # Display formatted output for this node
276 if elem.params:
277 #print(elem.params.full)
278 benchPath = os.path.join(benchmark,elem.fullPath(),"fullBenchmark.csv")
279 createMissingDir(benchPath)
280 if benchFile:
281 printf("ERROR BENCH FILE %s ALREADY OPEN" % benchPath)
282 benchFile.close()
283 benchFile=None
284 benchFile=open(benchPath,"w")
285 header = "".join(list(joinit(elem.params.full,",")))
286 # A test and a benchmark are different
287 # so we don't dump a status and error
288 # A status and error in a benchmark would
289 # impact the cycles since the test
290 # would be taken into account in the measurement
291 # So benchmark are always passing and contain no test
292 #benchFile.write("ID,%s,PASSED,ERROR,CYCLES\n" % header)
293 csvheaders = ""
294
295 with open('currentConfig.csv', 'r') as f:
296 reader = csv.reader(f)
297 csvheaders = next(reader, None)
298 configList = list(reader)
299 #print(configList)
300 config = "".join(list(joinit(configList[0],",")))
301 configHeaders = "".join(list(joinit(csvheaders,",")))
Christophe Favergeon37b86222019-07-17 11:49:00 +0200302 benchFile.write("CATEGORY,NAME,ID,OLDID,%s,CYCLES,%s\n" % (header,configHeaders))
Christophe Favergeon3b2a0ee2019-06-12 13:29:14 +0200303
304 formatter.printGroup(elem,theId)
305
306 # If we have detected a test, we switch to test mode
307 if re.match(r'^%s[t][ ]*$' % prefix,l):
308 state = INTEST
309
310
311 # Pop
312 # End of suite or group
313 if re.match(r'^%sp.*$' % prefix,l):
314 if benchFile:
315 benchFile.close()
316 benchFile=None
317 path.pop()
318 formatter.pop()
319 elif state == INTEST:
320 if len(l) > 0:
321 # In test mode, we are looking for test status.
322 # A line starting with S
323 # (There may be empty lines or line for data files)
324 passRe = r'^%s([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([YN]).*$' % prefix
325 if re.match(passRe,l):
326 # If we have found a test status then we will start again
327 # in normal mode after this.
328
329 m = re.match(passRe,l)
330
331 # Extract test ID, test error code, line number and status
332 theId=m.group(1)
333 theId=int(theId)
334
335 theError=m.group(2)
336 theError=int(theError)
337
338 theLine=m.group(3)
339 theLine=int(theLine)
340
341 cycles = int(m.group(4))
342
343 status=m.group(5)
344 passed=0
345
346 # Convert status to number as used by formatter.
347 if status=="Y":
348 passed = 1
349 if status=="N":
350 passed = 0
351 # Compute path to this node
352 newPath=path.copy()
353 newPath.append(theId)
354 # Find the node in the Tree
355 elem = findItem(root,newPath)
356
357
358 state = TESTPARAM
359 else:
360 if re.match(r'^%sp.*$' % prefix,l):
361 if benchFile:
362 benchFile.close()
363 benchFile=None
364 path.pop()
365 formatter.pop()
366 if re.match(r'^%s[t][ ]*$' % prefix,l):
367 state = INTEST
368 else:
369 state = NORMAL
370 else:
371 if len(l) > 0:
372 state = INTEST
373 params=""
374 if re.match(r'^.*b[ ]+([0-9,]+)$',l):
375 m=re.match(r'^.*b[ ]+([0-9,]+)$',l)
376 params=m.group(1).strip()
377 # Format the node
378 #print(elem.fullPath())
379 #createMissingDir(destPath)
380 writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
381 else:
382 params=""
383 writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config)
384 # Format the node
385 formatter.printTest(elem,theId,theError,theLine,passed,cycles,params)
386
387
388 formatter.end()
389
390
391parser = argparse.ArgumentParser(description='Parse test description')
392
393parser.add_argument('-f', nargs='?',type = str, default=None, help="Test description file path")
394# Where the result file can be found
395parser.add_argument('-r', nargs='?',type = str, default=None, help="Result file path")
396parser.add_argument('-c', action='store_true', help="CSV output")
397parser.add_argument('-e', action='store_true', help="Embedded test")
398# -o needed when -e is true to know where to extract the output files
399parser.add_argument('-o', nargs='?',type = str, default="Output", help="Output dir path")
400
401parser.add_argument('-b', nargs='?',type = str, default="FullBenchmark", help="Full Benchmark dir path")
402parser.add_argument('-m', action='store_true', help="Mathematica output")
403
404args = parser.parse_args()
405
406if args.f is not None:
407 p = parse.Parser()
408 # Parse the test description file
409 root = p.parse(args.f)
410 with open(args.r,"r") as results:
411 if args.c:
412 analyseResult(root,results,args.e,args.b,CSVFormatter())
413 elif args.m:
414 analyseResult(root,results,args.e,args.b,MathematicaFormatter())
415 else:
416 analyseResult(root,results,args.e,args.b,TextFormatter())
417 if args.e:
418 # In FPGA mode, extract output files from stdout (result file)
419 with open(args.r,"r") as results:
420 extractDataFiles(results,args.o)
421
422else:
423 parser.print_help()