Parser.py 8.47 KB
Newer Older
1
#!/usr/bin/env python3
2
3
#SCT log parser

4

5
6
import sys

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

17
18

#Were we intrept test logs into test dicts
19
def test_parser(string, current):
20
    test_list = {
Vincent Stehlé's avatar
Vincent Stehlé committed
21
      "name": string[2], #FIXME:Sometimes, SCT has name and Description,
22
      "result": string[1],
23
      **current,
24
      "guid": string[0], #FIXME:GUID's overlap
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
25
      #"comment": string[-1], #FIXME:need to hash this out, sometime there is no comments
26
      "log": ' '.join(string[3:])
27
    }
28
    return test_list
Vincent Stehlé's avatar
Vincent Stehlé committed
29

30
#Parse the ekl file, and create a map of the tests
31
32
def ekl_parser (file):
    #create our "database" dict
33
    temp_list = list()
34
    #All tests are grouped by the "HEAD" line the procedes them.
35
36
37
38
39
40
    current = {
        'group': "N/A",
        'test set': "N/A",
        'sub set': "N/A",
        'set guid': "N/A",
    }
41
42

    for line in file:
Vincent Stehlé's avatar
Vincent Stehlé committed
43
44
45
46
47
48
49
        # Strip the line from trailing whitespaces
        line = line.rstrip()

        # Skip empty line
        if line == '':
            continue

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

Vincent Stehlé's avatar
Vincent Stehlé committed
53
54
        # Skip TERM
        if split_line[0] == "TERM":
55
56
57
58
59
            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.
60
            try:
61
                group, Set = split_line[8].split('\\')
62
            except:
63
64
65
66
67
68
                group, Set = '', split_line[8]
            current = {
                'group': group,
                'test set': Set,
                'sub set': split_line[6],
                'set guid': split_line[4],
69
70
71
72
73
74
                'iteration': split_line[1],
                'start date': split_line[2],
                'start time': split_line[3],
                'revision': split_line[5],
                'descr': split_line[7],
                'device path': split_line[9],
75
            }
76

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
77
        #FIXME:? EKL file has an inconsistent line structure,
78
79
80
        # 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] != " ":
81
82
83
84
            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
85
                tmp_dict = test_parser(split_test, current)
86
87
88
89
90
                temp_list.append(tmp_dict)
            except:
                print("Line:",split_line)
                sys.exit("your log may be corrupted")
    return temp_list
91

Jeff Booher-Kaeding's avatar
Jeff Booher-Kaeding committed
92
#Parse Seq file, used to tell which tests should run.
93
def seq_parser(file):
94
    temp_dict = list()
95
    lines=file.readlines()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
96
    magic=7 #a test in a seq file is 7 lines, if not mod7, something wrong..
97
98
    if len(lines)%magic != 0:
        sys.exit("seqfile cut short, should be mod7")
99
100
101
102
103
104
105
106
    #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
Vincent Stehlé's avatar
Vincent Stehlé committed
107
        #(x+6)(utf-16 char)
108
        #currently only add tests that are supposed to run, should add all?
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
109
        #0xFFFFFFFF in "Iterations" means the test is NOT supposed to run
110
111
112
113
114
115
116
117
        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)
            }
118
            temp_dict.append(seq_dict) #put in a dict based on guid
119

120
    return temp_dict
121

122
123
124
125
#group items by key, and print by key
#we slowly iterate through the list, group and print groups
def key_tree_2_md(input_list,file,key):
    #make a copy so we don't destroy the first list.
Vincent Stehlé's avatar
Vincent Stehlé committed
126
    temp_list = input_list.copy()
127
128
129
130
131
132
133
134
135
136
137
138
    while temp_list:
        test_dict = temp_list.pop()
        found, not_found = [test_dict],[]
        #go through whole list looking for key match
        while temp_list:
            next_dict = temp_list.pop()
            if next_dict[key] == test_dict[key]: #if match add to found
                found.append(next_dict)
            else: #else not found
                not_found.append(next_dict)
        temp_list = not_found #start over with found items removed
        file.write("### " + test_dict[key])
139
        dict_2_md(found,file)
Vincent Stehlé's avatar
Vincent Stehlé committed
140

141
142


143
144
145
#generic writer, takes a list of dicts and turns the dicts into an MD table.
def dict_2_md(input_list,file):
    if len(input_list) > 0:
Jeff Booher-Kaeding's avatar
Jeff Booher-Kaeding committed
146
        file.write("\n\n")
147
148
149
150
        #create header for MD table using dict keys
        temp_string1, temp_string2 = "|", "|"
        for x in (input_list[0].keys()):
            temp_string1 += (x + "|")
Jeff Booher-Kaeding's avatar
Jeff Booher-Kaeding committed
151
            temp_string2 += ("---|")
152
153
154
155
156
157
158
        file.write(temp_string1+"\n"+temp_string2+"\n")
        #print each item from the dict into the table
        for x in input_list:
            test_string = "|"
            for y in x.keys():
                test_string += (x[y] + "|")
            file.write(test_string+'\n')
Vincent Stehlé's avatar
Vincent Stehlé committed
159
    #seprate table from other items in MD
160
    file.write("\n\n")
161

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

163
def main():
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
164
    #Command line argument 1, ekl file to open, else open sample
165
    log_file = sys.argv[1] if len(sys.argv) >= 2 else "sample.ekl"
166
    db1 = list() #"database 1" all tests.
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
167
168
169
170
    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
171
    seq_file = sys.argv[2] if len(sys.argv) >= 3 else "sample.seq"
172
    db2 = dict() #"database 2" all test sets that should run
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
173
    with open(seq_file,"r",encoding="utf-16") as f: #files are encoded in utf-16
174
        db2 = seq_parser(f)
Vincent Stehlé's avatar
Vincent Stehlé committed
175

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
176
    #cross check is filled only with tests labled as "run" int the seq file
177
    cross_check = list()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
178
    #combine a list of test sets that did not run for whatever reason.
179
    would_not_run = list()
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
180
    for x in db2: #for each "set guid" in db2
181
        temp_dict = key_value_find(db1,"set guid",x["guid"])#find tests in db1 with given set guid
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
182
        if bool(temp_dict): #if its not empty, apprend it to our dict
183
            cross_check = (cross_check +temp_dict)
Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
184
        else: #if it is empty, this test set was not run.
185
            would_not_run.append(x)
186

Vincent Stehlé's avatar
Vincent Stehlé committed
187

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
188
    #search for failures and warnings & passes,
Vincent Stehlé's avatar
Vincent Stehlé committed
189

190
191
192
    failures = key_value_find(cross_check,"result","FAILURE")
    warnings = key_value_find(cross_check,"result","WARNING")
    passes = key_value_find(cross_check,"result","PASS")
193
194
195
196


    # generate MD summary
    with open('result.md', 'w') as resultfile:
197
198
199
200
201
202
203
        resultfile.write("# SCT Summary \n\n")
        resultfile.write("|  |  |\n")
        resultfile.write("|--|--|\n")
        resultfile.write("|Dropped:|" + str(len(would_not_run)) + "|\n")
        resultfile.write("|Failures:|" + str(len(failures)) + "|\n")
        resultfile.write("|Warnings:|" + str(len(warnings)) + "|\n")
        resultfile.write("|Passes:|" + str(len(passes)) + "|\n")
204
205
        resultfile.write("\n\n")

Jeff Booher-Kaeding's avatar
Jeff Booher-Kaeding committed
206
        resultfile.write("## 1. Silently dropped or missing")
207
208
        dict_2_md(would_not_run,resultfile)

209
        resultfile.write("## 4. Failure by group")
Vincent Stehlé's avatar
Vincent Stehlé committed
210
        resultfile.write("\n\n")
211
        key_tree_2_md(failures,resultfile,"group")
Vincent Stehlé's avatar
Vincent Stehlé committed
212

213

214
215
        resultfile.write("## 3. Warnings by group")
        resultfile.write("\n\n")
216
        key_tree_2_md(warnings,resultfile,"group")
217

Vincent Stehlé's avatar
Vincent Stehlé committed
218

Jeff Booher-Kaeding's avatar
V1.0?    
Jeff Booher-Kaeding committed
219
220
221
222
223
224
225
226
227
    #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:
228
            print(x["guid"],":",x["name"],"with",find_key,":",x[find_key])
229

230
main()