# Process the test results # Test status (like passed, or failed with error code) import argparse import re import TestScripts.NewParser as parse import TestScripts.CodeGen from collections import deque import os.path import csv def findItem(root,path): """ Find a node in a tree Args: path (list) : A list of node ID This list is describing a path in the tree. By starting from the root and following this path, we can find the node in the tree. Raises: Nothing Returns: TreeItem : A node """ # The list is converted into a queue. q = deque(path) q.popleft() c = root while q: n = q.popleft() # We get the children based on its ID and continue c = c[n-1] return(c) def joinit(iterable, delimiter): # Intersperse a delimiter between element of a list it = iter(iterable) yield next(it) for x in it: yield delimiter yield x # Return test result as a text tree class TextFormatter: def start(self): None def printGroup(self,elem,theId): if elem is None: elem = root message=elem.data["message"] if not elem.data["deprecated"]: kind = "Suite" ident = " " * elem.ident if elem.kind == TestScripts.Parser.TreeElem.GROUP: kind = "Group" #print(elem.path) print("%s%s : %s (%d)" % (ident,kind,message,theId)) def printTest(self,elem, theId, theError,theLine,passed,cycles,params): message=elem.data["message"] if not elem.data["deprecated"]: kind = "Test" ident = " " * elem.ident p="FAILED" if passed == 1: p="PASSED" print("%s%s (%d) : %s (cycles = %d)" % (ident,message,theId,p,cycles)) if params: print("%s %s" % (ident,params)) if passed != 1: print("%s Error = %d at line %d" % (ident, theError, theLine)) def pop(self): None def end(self): None # Return test result as a CSV class CSVFormatter: def __init__(self): self.name=[] self._start=True def start(self): print("CATEGORY,NAME,ID,STATUS,CYCLES,PARAMS") def printGroup(self,elem,theId): if elem is None: elem = root # Remove Root from category name in CSV file. if not self._start: self.name.append(elem.data["class"]) else: self._start=False message=elem.data["message"] if not elem.data["deprecated"]: kind = "Suite" ident = " " * elem.ident if elem.kind == TestScripts.Parser.TreeElem.GROUP: kind = "Group" def printTest(self,elem, theId, theError, theLine,passed,cycles,params): message=elem.data["message"] if not elem.data["deprecated"]: kind = "Test" name=elem.data["class"] category= "".join(list(joinit(self.name,":"))) print("%s,%s,%d,%d,%d,\"%s\"" % (category,name,theId,passed,cycles,params)) def pop(self): if self.name: self.name.pop() def end(self): None class MathematicaFormatter: def __init__(self): self._hasContent=[False] self._toPop=[] def start(self): None def printGroup(self,elem,theId): if self._hasContent[len(self._hasContent)-1]: print(",",end="") print("<|") self._hasContent[len(self._hasContent)-1] = True self._hasContent.append(False) if elem is None: elem = root message=elem.data["message"] if not elem.data["deprecated"]: kind = "Suite" ident = " " * elem.ident if elem.kind == TestScripts.Parser.TreeElem.GROUP: kind = "Group" print("\"%s\" ->" % (message)) #if kind == "Suite": print("{",end="") self._toPop.append("}") #else: # self._toPop.append("") def printTest(self,elem, theId, theError,theLine,passed,cycles,params): message=elem.data["message"] if not elem.data["deprecated"]: kind = "Test" ident = " " * elem.ident p="FAILED" if passed == 1: p="PASSED" parameters="" if params: parameters = "%s" % params if self._hasContent[len(self._hasContent)-1]: print(",",end="") print("<|\"NAME\" -> \"%s\",\"ID\" -> %d,\"STATUS\" -> \"%s\",\"CYCLES\" -> %d,\"PARAMS\" -> \"%s\"|>" % (message,theId,p,cycles,parameters)) self._hasContent[len(self._hasContent)-1] = True #if passed != 1: # print("%s Error = %d at line %d" % (ident, theError, theLine)) def pop(self): print(self._toPop.pop(),end="") print("|>") self._hasContent.pop() def end(self): None NORMAL = 1 INTEST = 2 TESTPARAM = 3 def createMissingDir(destPath): theDir=os.path.normpath(os.path.dirname(destPath)) if not os.path.exists(theDir): os.makedirs(theDir) def correctPath(path): while (path[0]=="/") or (path[0] == "\\"): path = path[1:] return(path) def extractDataFiles(results,outputDir): infile = False f = None for l in results: if re.match(r'^.*D:[ ].*$',l): if infile: if re.match(r'^.*D:[ ]END$',l): infile = False if f: f.close() else: if f: m = re.match(r'^.*D:[ ](.*)$',l) data = m.group(1) f.write(data) f.write("\n") else: m = re.match(r'^.*D:[ ](.*)$',l) path = str(m.group(1)) infile = True destPath = os.path.join(outputDir,correctPath(path)) createMissingDir(destPath) f = open(destPath,"w") def writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config): if benchFile: name=elem.data["class"] category= elem.categoryDesc() old="" if "testData" in elem.data: if "oldID" in elem.data["testData"]: old=elem.data["testData"]["oldID"] benchFile.write("\"%s\",\"%s\",%d,\"%s\",%s,%d,%s\n" % (category,name,theId,old,params,cycles,config)) def analyseResult(root,results,embedded,benchmark,formatter): formatter.start() path = [] state = NORMAL prefix="" elem=None theId=None theError=None theLine=None passed=0 cycles=None benchFile = None config="" if embedded: prefix = ".*S:[ ]" # Parse the result file. # NORMAL mode is when we are parsing suite or group. # Otherwise we are parsing a test and we need to analyse the # test result. # TESTPARAM is used to read parameters of the test. # Format of output is: #node ident : s id or g id or t or u #test status : id error linenb status Y or N (Y when passing) #param for this test b x,x,x,x or b alone if not param #node end : p # In FPGA mode: #Prefix S:[ ] before driver dump # D:[ ] before data dump (output patterns) for l in results: l = l.strip() if not re.match(r'^.*D:[ ].*$',l): if state == NORMAL: if len(l) > 0: # Line starting with g or s is a suite or group. # In FPGA mode, those line are prefixed with 'S: ' # and data file with 'D: ' if re.match(r'^%s[gs][ ]+[0-9]+.*$' % prefix,l): # Extract the test id theId=re.sub(r'^%s[gs][ ]+([0-9]+).*$' % prefix,r'\1',l) theId=int(theId) path.append(theId) # From a list of id, find the TreeElem in the Parsed tree # to know what is the node. elem = findItem(root,path) # Display formatted output for this node if elem.params: #print(elem.params.full) benchPath = os.path.join(benchmark,elem.fullPath(),"fullBenchmark.csv") createMissingDir(benchPath) if benchFile: printf("ERROR BENCH FILE %s ALREADY OPEN" % benchPath) benchFile.close() benchFile=None benchFile=open(benchPath,"w") header = "".join(list(joinit(elem.params.full,","))) # A test and a benchmark are different # so we don't dump a status and error # A status and error in a benchmark would # impact the cycles since the test # would be taken into account in the measurement # So benchmark are always passing and contain no test #benchFile.write("ID,%s,PASSED,ERROR,CYCLES\n" % header) csvheaders = "" with open('currentConfig.csv', 'r') as f: reader = csv.reader(f) csvheaders = next(reader, None) configList = list(reader) #print(configList) config = "".join(list(joinit(configList[0],","))) configHeaders = "".join(list(joinit(csvheaders,","))) benchFile.write("CATEGORY,NAME,ID,OLDID,%s,CYCLES,%s\n" % (header,configHeaders)) formatter.printGroup(elem,theId) # If we have detected a test, we switch to test mode if re.match(r'^%s[t][ ]*$' % prefix,l): state = INTEST # Pop # End of suite or group if re.match(r'^%sp.*$' % prefix,l): if benchFile: benchFile.close() benchFile=None path.pop() formatter.pop() elif state == INTEST: if len(l) > 0: # In test mode, we are looking for test status. # A line starting with S # (There may be empty lines or line for data files) passRe = r'^%s([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([YN]).*$' % prefix if re.match(passRe,l): # If we have found a test status then we will start again # in normal mode after this. m = re.match(passRe,l) # Extract test ID, test error code, line number and status theId=m.group(1) theId=int(theId) theError=m.group(2) theError=int(theError) theLine=m.group(3) theLine=int(theLine) cycles = int(m.group(4)) status=m.group(5) passed=0 # Convert status to number as used by formatter. if status=="Y": passed = 1 if status=="N": passed = 0 # Compute path to this node newPath=path.copy() newPath.append(theId) # Find the node in the Tree elem = findItem(root,newPath) state = TESTPARAM else: if re.match(r'^%sp.*$' % prefix,l): if benchFile: benchFile.close() benchFile=None path.pop() formatter.pop() if re.match(r'^%s[t][ ]*$' % prefix,l): state = INTEST else: state = NORMAL else: if len(l) > 0: state = INTEST params="" if re.match(r'^.*b[ ]+([0-9,]+)$',l): m=re.match(r'^.*b[ ]+([0-9,]+)$',l) params=m.group(1).strip() # Format the node #print(elem.fullPath()) #createMissingDir(destPath) writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config) else: params="" writeBenchmark(elem,benchFile,theId,theError,passed,cycles,params,config) # Format the node formatter.printTest(elem,theId,theError,theLine,passed,cycles,params) formatter.end() parser = argparse.ArgumentParser(description='Parse test description') parser.add_argument('-f', nargs='?',type = str, default=None, help="Test description file path") # Where the result file can be found parser.add_argument('-r', nargs='?',type = str, default=None, help="Result file path") parser.add_argument('-c', action='store_true', help="CSV output") parser.add_argument('-e', action='store_true', help="Embedded test") # -o needed when -e is true to know where to extract the output files parser.add_argument('-o', nargs='?',type = str, default="Output", help="Output dir path") parser.add_argument('-b', nargs='?',type = str, default="FullBenchmark", help="Full Benchmark dir path") parser.add_argument('-m', action='store_true', help="Mathematica output") args = parser.parse_args() if args.f is not None: p = parse.Parser() # Parse the test description file root = p.parse(args.f) with open(args.r,"r") as results: if args.c: analyseResult(root,results,args.e,args.b,CSVFormatter()) elif args.m: analyseResult(root,results,args.e,args.b,MathematicaFormatter()) else: analyseResult(root,results,args.e,args.b,TextFormatter()) if args.e: # In FPGA mode, extract output files from stdout (result file) with open(args.r,"r") as results: extractDataFiles(results,args.o) else: parser.print_help()