Parser.py 7.24 KB
Newer Older
1
2
#SCT log parser

3
#BIGFIXME: do a deeper dive into how logs are represented.
4

5
import sys
6
import json
7

8
#based loosley on https://stackoverflow.com/a/4391978
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
9
# returns a filtered dict of dicts that meet some Key-value pair.
10
# I.E. key="result" value="FAILURE"
11
def key_value_find(dict1, key, value):
12
13
    found = list()
    for test in dict1:
14
        if test[key] == value:
15
            found.append(test)
16
    return found
17

18
19

#Were we intrept test logs into test dicts
20
def test_parser(string,current_group,current_test_set,current_set_guid,current_sub_set):
21
    test_dict = {
22
      "name": string[2], #FIXME:Sometimes, SCT has name and Description, 
23
24
      "result": string[1],
      "group": current_group,
25
26
27
      "test set": current_test_set,  
      "sub set": current_sub_set,
      "set guid": current_set_guid,
28
      "guid": string[0], #FIXME:GUID's overlap... need fix... 
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
29
      #"comment": string[-1], #FIXME:need to hash this out, sometime there is no comments
30
      "log": string
31
    }
32
    return (test_dict["guid"]+test_dict["set guid"]), test_dict
33
    
34
#Parse the ekl file, and create a map of the tests
35
36
def ekl_parser (file):
    #create our "database" dict
37
    temp_list = list()
38
    #All tests are grouped by the "HEAD" line the procedes them.
39
40
    current_group = "N/A"
    current_set = "N/A"
41
42
    current_set_guid = "N/A"
    current_sub_set = "N/A"
43
44
45
46
47
48
49
50
51
52
53
54

    for line in file:
        #strip the line of | & || used for sepration
        split_line = [string for string in line.split('|') if string != ""]

        #TODO:I can skip TERM, but I feel like "\n" should be able to be handled in the above list comprehension 
        if split_line[0]=="TERM" or split_line[0]=="\n":
            continue

        #The "HEAD" tag is the only indcation we are on a new test set
        if split_line[0]=="HEAD":
            #split the header into test group and test set.
55
56
57
58
            try:
                current_group, current_set = split_line[8].split('\\')
            except:
                current_group, current_set =split_line[8],split_line
59
60
            current_set_guid = split_line[4]
            current_sub_set = split_line[6]
61

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
62
        #FIXME:? EKL file has an inconsistent line structure,
63
64
65
        # sometime we see a line that consits ' dump of GOP->I\n'
        #easiest way to skip is check for blank space in the first char
        elif split_line[0][0] != " ":
66
67
68
69
70
71
72
73
74
75
76
            try:
                #deliminiate on ':' for tests
                split_test = [new_string for old_string in split_line for new_string in old_string.split(':')]
                #put the test into a dict, and then place that dict in another dict with GUID as key
                guid,tmp_dict = test_parser(split_test,current_group,current_set,current_set_guid,current_sub_set)
                #print(guid)
                temp_list.append(tmp_dict)
            except:
                print("Line:",split_line)
                sys.exit("your log may be corrupted")
    return temp_list
77

78
def seq_parser(file):
79
    temp_dict = dict()
80
    lines=file.readlines()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
81
    magic=7 #a test in a seq file is 7 lines, if not mod7, something wrong..
82
83
    if len(lines)%magic != 0:
        sys.exit("seqfile cut short, should be mod7")
84
85
86
87
88
89
90
91
92
93
    #the utf-16 char makes this looping a bit harder, so we use x+(i) where i is next 0-6th
    for x in range(0,len(lines),magic): #loop ever "7 lines"
        #(x+0)[Test Case]
        #(x+1)Revision=0x10000
        #(x+2)Guid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
        #(x+3)Name=InstallAcpiTableFunction
        #(x+4)Order=0xFFFFFFFF
        #(x+5)Iterations=0xFFFFFFFF
        #(x+6)(utf-16 char) 
        #currently only add tests that are supposed to run, should add all?
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
94
        #0xFFFFFFFF in "Iterations" means the test is NOT supposed to run
95
96
97
98
99
100
101
102
        if not "0xFFFFFFFF" in lines[x+5]:
            seq_dict = {
                "name": lines[x+3][5:-1],#from after "Name=" to end (5char long)
                "guid": lines[x+2][5:-1],#from after"Guid=" to the end, (5char long)
                "Iteration": lines[x+5][11:-1],#from after "Iterations=" (11char long)
                "rev": lines[x+1][9:-1],#from after "Revision=" (9char long)
                "Order": lines[x+4][6:-1]#from after "Order=" (6char long)
            }
103
            temp_dict[seq_dict["guid"]]=seq_dict #put in a dict based on guid
104

105
    return temp_dict
106

107

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
108

109
def main():
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
110
111
    #Command line argument 1, ekl file to open, else open sample
    log_file = sys.argv[1] if len(sys.argv) >= 2 else "sample.ekl"
112
    db1 = list() #"database 1" all tests.
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
113
114
115
116
117
118
119
    with open(log_file,"r",encoding="utf-16") as f: #files are encoded in utf-16
        db1 = ekl_parser(f.readlines())

    #Command line argument 2, seq file to open, else open sample
    seq_file = sys.argv[2] if len(sys.argv) >= 3 else "sample.seq"
    db2 = {} #"database 2" all test sets that should run
    with open(seq_file,"r",encoding="utf-16") as f: #files are encoded in utf-16
120
        db2 = seq_parser(f)
121
    
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
122
    #cross check is filled only with tests labled as "run" int the seq file
123
    cross_check = list()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
124
    #combine a list of test sets that did not run for whatever reason.
125
    would_not_run = list()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
126
127
128
    for x in db2: #for each "set guid" in db2
        temp_dict = key_value_find(db1,"set guid",x)#find tests in db1 with given set guid
        if bool(temp_dict): #if its not empty, apprend it to our dict
129
            cross_check = (cross_check +temp_dict)
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
130
131
        else: #if it is empty, this test set was not run.
            would_not_run.append(db2[x]) 
132
133

    
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
134
    #search for failures and warnings & passes,
135
    
136
137
138
    failures = key_value_find(cross_check,"result","FAILURE")
    warnings = key_value_find(cross_check,"result","WARNING")
    passes = key_value_find(cross_check,"result","PASS")
139
140
141
142
143
144
145
146
147
148
149
    fail_and_warn = (failures + warnings)#dict of failures and warnings


    # generate MD summary
    #TODO: this should be split out into functions, and also "Beautified"
    with open('result.md', 'w') as resultfile:
        resultfile.write("# SCT Summary \n")
        resultfile.write("### Failures:"+str(len(failures))+"\n")
        resultfile.write("### Warnings:"+str(len(warnings))+"\n")
        resultfile.write("### Passes:"+str(len(passes))+"\n")
        resultfile.write("### Dropped:"+str(len(would_not_run))+"\n")
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
150
        if len(would_not_run) > 0:
151
152
153
            resultfile.write("\n\n# Silently dropped or missing \n")
            resultfile.write("|dict| \n")
            resultfile.write("|---| \n")
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
154
            for x in would_not_run:
155
156
157
158
159
160
161
162
163
164
165
                resultfile.write("| ")
                json.dump(x,resultfile)
                resultfile.write(" |\n")
        if len(fail_and_warn) > 0:
            resultfile.write("\n# Failures & warnings\n")
            resultfile.write("|dict| \n")
            resultfile.write("|---| \n")
            for x in fail_and_warn:
                json.dump(x,resultfile)
                resultfile.write(" |\n")
    
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
166
167
168
169
170
171
172
173
174
175
    #command line argument 3&4, key are to support a key & value search.
    #these will be displayed in CLI
    if len(sys.argv) >= 5:
        find_key = sys.argv[3]
        find_value = sys.argv[4]
        found = key_value_find(db1,find_key,find_value)
        #print the dict
        print("found:",len(found),"items with search constraints")
        for x in found:
            print(found[x]["guid"],":",found[x]["name"],"with",found[x][find_key],":",found[x][find_value])
176

177
main()