blob: e595c185dffa4ed5298ff3935cd14faa21ede658 [file] [log] [blame]
Brad Bishop40320b12019-03-26 16:08:25 -04001# resulttool - common library/utility functions
2#
3# Copyright (c) 2019, Intel Corporation.
4# Copyright (c) 2019, Linux Foundation
5#
Brad Bishopc342db32019-05-15 21:57:59 -04006# SPDX-License-Identifier: GPL-2.0-only
Brad Bishop40320b12019-03-26 16:08:25 -04007#
Brad Bishopc342db32019-05-15 21:57:59 -04008
Brad Bishop40320b12019-03-26 16:08:25 -04009import os
10import json
11import scriptpath
Brad Bishop19323692019-04-05 15:28:33 -040012import copy
Brad Bishopc342db32019-05-15 21:57:59 -040013import urllib.request
14import posixpath
Brad Bishop40320b12019-03-26 16:08:25 -040015scriptpath.add_oe_lib_path()
16
17flatten_map = {
18 "oeselftest": [],
19 "runtime": [],
20 "sdk": [],
21 "sdkext": [],
22 "manual": []
23}
24regression_map = {
25 "oeselftest": ['TEST_TYPE', 'MACHINE'],
26 "runtime": ['TESTSERIES', 'TEST_TYPE', 'IMAGE_BASENAME', 'MACHINE', 'IMAGE_PKGTYPE', 'DISTRO'],
27 "sdk": ['TESTSERIES', 'TEST_TYPE', 'IMAGE_BASENAME', 'MACHINE', 'SDKMACHINE'],
28 "sdkext": ['TESTSERIES', 'TEST_TYPE', 'IMAGE_BASENAME', 'MACHINE', 'SDKMACHINE'],
29 "manual": ['TEST_TYPE', 'TEST_MODULE', 'IMAGE_BASENAME', 'MACHINE']
30}
31store_map = {
32 "oeselftest": ['TEST_TYPE'],
33 "runtime": ['TEST_TYPE', 'DISTRO', 'MACHINE', 'IMAGE_BASENAME'],
34 "sdk": ['TEST_TYPE', 'MACHINE', 'SDKMACHINE', 'IMAGE_BASENAME'],
35 "sdkext": ['TEST_TYPE', 'MACHINE', 'SDKMACHINE', 'IMAGE_BASENAME'],
36 "manual": ['TEST_TYPE', 'TEST_MODULE', 'MACHINE', 'IMAGE_BASENAME']
37}
38
Brad Bishopc342db32019-05-15 21:57:59 -040039def is_url(p):
40 """
41 Helper for determining if the given path is a URL
42 """
43 return p.startswith('http://') or p.startswith('https://')
44
Brad Bishop15ae2502019-06-18 21:44:24 -040045extra_configvars = {'TESTSERIES': ''}
46
Brad Bishop40320b12019-03-26 16:08:25 -040047#
48# Load the json file and append the results data into the provided results dict
49#
Brad Bishop15ae2502019-06-18 21:44:24 -040050def append_resultsdata(results, f, configmap=store_map, configvars=extra_configvars):
Brad Bishop40320b12019-03-26 16:08:25 -040051 if type(f) is str:
Brad Bishopc342db32019-05-15 21:57:59 -040052 if is_url(f):
53 with urllib.request.urlopen(f) as response:
54 data = json.loads(response.read().decode('utf-8'))
55 url = urllib.parse.urlparse(f)
56 testseries = posixpath.basename(posixpath.dirname(url.path))
57 else:
58 with open(f, "r") as filedata:
59 data = json.load(filedata)
60 testseries = os.path.basename(os.path.dirname(f))
Brad Bishop40320b12019-03-26 16:08:25 -040061 else:
62 data = f
63 for res in data:
64 if "configuration" not in data[res] or "result" not in data[res]:
65 raise ValueError("Test results data without configuration or result section?")
Brad Bishop15ae2502019-06-18 21:44:24 -040066 for config in configvars:
67 if config == "TESTSERIES" and "TESTSERIES" not in data[res]["configuration"]:
68 data[res]["configuration"]["TESTSERIES"] = testseries
69 continue
70 if config not in data[res]["configuration"]:
71 data[res]["configuration"][config] = configvars[config]
Brad Bishop40320b12019-03-26 16:08:25 -040072 testtype = data[res]["configuration"].get("TEST_TYPE")
73 if testtype not in configmap:
74 raise ValueError("Unknown test type %s" % testtype)
Brad Bishop40320b12019-03-26 16:08:25 -040075 testpath = "/".join(data[res]["configuration"].get(i) for i in configmap[testtype])
76 if testpath not in results:
77 results[testpath] = {}
Brad Bishop40320b12019-03-26 16:08:25 -040078 results[testpath][res] = data[res]
79
80#
81# Walk a directory and find/load results data
82# or load directly from a file
83#
Brad Bishop15ae2502019-06-18 21:44:24 -040084def load_resultsdata(source, configmap=store_map, configvars=extra_configvars):
Brad Bishop40320b12019-03-26 16:08:25 -040085 results = {}
Brad Bishopc342db32019-05-15 21:57:59 -040086 if is_url(source) or os.path.isfile(source):
Brad Bishop15ae2502019-06-18 21:44:24 -040087 append_resultsdata(results, source, configmap, configvars)
Brad Bishop40320b12019-03-26 16:08:25 -040088 return results
89 for root, dirs, files in os.walk(source):
90 for name in files:
91 f = os.path.join(root, name)
92 if name == "testresults.json":
Brad Bishop15ae2502019-06-18 21:44:24 -040093 append_resultsdata(results, f, configmap, configvars)
Brad Bishop40320b12019-03-26 16:08:25 -040094 return results
95
96def filter_resultsdata(results, resultid):
97 newresults = {}
98 for r in results:
99 for i in results[r]:
100 if i == resultsid:
101 newresults[r] = {}
102 newresults[r][i] = results[r][i]
103 return newresults
104
Brad Bishop19323692019-04-05 15:28:33 -0400105def strip_ptestresults(results):
106 newresults = copy.deepcopy(results)
107 #for a in newresults2:
108 # newresults = newresults2[a]
109 for res in newresults:
110 if 'result' not in newresults[res]:
111 continue
112 if 'ptestresult.rawlogs' in newresults[res]['result']:
113 del newresults[res]['result']['ptestresult.rawlogs']
114 if 'ptestresult.sections' in newresults[res]['result']:
115 for i in newresults[res]['result']['ptestresult.sections']:
116 if 'log' in newresults[res]['result']['ptestresult.sections'][i]:
117 del newresults[res]['result']['ptestresult.sections'][i]['log']
118 return newresults
119
120def save_resultsdata(results, destdir, fn="testresults.json", ptestjson=False, ptestlogs=False):
Brad Bishop40320b12019-03-26 16:08:25 -0400121 for res in results:
122 if res:
123 dst = destdir + "/" + res + "/" + fn
124 else:
125 dst = destdir + "/" + fn
126 os.makedirs(os.path.dirname(dst), exist_ok=True)
Brad Bishop19323692019-04-05 15:28:33 -0400127 resultsout = results[res]
128 if not ptestjson:
129 resultsout = strip_ptestresults(results[res])
Brad Bishop40320b12019-03-26 16:08:25 -0400130 with open(dst, 'w') as f:
Brad Bishop19323692019-04-05 15:28:33 -0400131 f.write(json.dumps(resultsout, sort_keys=True, indent=4))
132 for res2 in results[res]:
133 if ptestlogs and 'result' in results[res][res2]:
134 if 'ptestresult.rawlogs' in results[res][res2]['result']:
135 with open(dst.replace(fn, "ptest-raw.log"), "w+") as f:
136 f.write(results[res][res2]['result']['ptestresult.rawlogs']['log'])
137 if 'ptestresult.sections' in results[res][res2]['result']:
138 for i in results[res][res2]['result']['ptestresult.sections']:
139 if 'log' in results[res][res2]['result']['ptestresult.sections'][i]:
140 with open(dst.replace(fn, "ptest-%s.log" % i), "w+") as f:
141 f.write(results[res][res2]['result']['ptestresult.sections'][i]['log'])
Brad Bishop40320b12019-03-26 16:08:25 -0400142
143def git_get_result(repo, tags):
144 git_objs = []
145 for tag in tags:
146 files = repo.run_cmd(['ls-tree', "--name-only", "-r", tag]).splitlines()
147 git_objs.extend([tag + ':' + f for f in files if f.endswith("testresults.json")])
148
149 def parse_json_stream(data):
150 """Parse multiple concatenated JSON objects"""
151 objs = []
152 json_d = ""
153 for line in data.splitlines():
154 if line == '}{':
155 json_d += '}'
156 objs.append(json.loads(json_d))
157 json_d = '{'
158 else:
159 json_d += line
160 objs.append(json.loads(json_d))
161 return objs
162
163 # Optimize by reading all data with one git command
164 results = {}
165 for obj in parse_json_stream(repo.run_cmd(['show'] + git_objs + ['--'])):
166 append_resultsdata(results, obj)
167
168 return results
Brad Bishopc342db32019-05-15 21:57:59 -0400169
170def test_run_results(results):
171 """
172 Convenient generator function that iterates over all test runs that have a
173 result section.
174
175 Generates a tuple of:
176 (result json file path, test run name, test run (dict), test run "results" (dict))
177 for each test run that has a "result" section
178 """
179 for path in results:
180 for run_name, test_run in results[path].items():
181 if not 'result' in test_run:
182 continue
183 yield path, run_name, test_run, test_run['result']
184