blob: ea4ab42d9a042003f8c8df178712fc2c35d2a5de [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 Bishop40320b12019-03-26 16:08:25 -040045#
46# Load the json file and append the results data into the provided results dict
47#
48def append_resultsdata(results, f, configmap=store_map):
49 if type(f) is str:
Brad Bishopc342db32019-05-15 21:57:59 -040050 if is_url(f):
51 with urllib.request.urlopen(f) as response:
52 data = json.loads(response.read().decode('utf-8'))
53 url = urllib.parse.urlparse(f)
54 testseries = posixpath.basename(posixpath.dirname(url.path))
55 else:
56 with open(f, "r") as filedata:
57 data = json.load(filedata)
58 testseries = os.path.basename(os.path.dirname(f))
Brad Bishop40320b12019-03-26 16:08:25 -040059 else:
60 data = f
61 for res in data:
62 if "configuration" not in data[res] or "result" not in data[res]:
63 raise ValueError("Test results data without configuration or result section?")
64 if "TESTSERIES" not in data[res]["configuration"]:
Brad Bishopc342db32019-05-15 21:57:59 -040065 data[res]["configuration"]["TESTSERIES"] = testseries
Brad Bishop40320b12019-03-26 16:08:25 -040066 testtype = data[res]["configuration"].get("TEST_TYPE")
67 if testtype not in configmap:
68 raise ValueError("Unknown test type %s" % testtype)
69 configvars = configmap[testtype]
70 testpath = "/".join(data[res]["configuration"].get(i) for i in configmap[testtype])
71 if testpath not in results:
72 results[testpath] = {}
Brad Bishop40320b12019-03-26 16:08:25 -040073 results[testpath][res] = data[res]
74
75#
76# Walk a directory and find/load results data
77# or load directly from a file
78#
79def load_resultsdata(source, configmap=store_map):
80 results = {}
Brad Bishopc342db32019-05-15 21:57:59 -040081 if is_url(source) or os.path.isfile(source):
Brad Bishop40320b12019-03-26 16:08:25 -040082 append_resultsdata(results, source, configmap)
83 return results
84 for root, dirs, files in os.walk(source):
85 for name in files:
86 f = os.path.join(root, name)
87 if name == "testresults.json":
88 append_resultsdata(results, f, configmap)
89 return results
90
91def filter_resultsdata(results, resultid):
92 newresults = {}
93 for r in results:
94 for i in results[r]:
95 if i == resultsid:
96 newresults[r] = {}
97 newresults[r][i] = results[r][i]
98 return newresults
99
Brad Bishop19323692019-04-05 15:28:33 -0400100def strip_ptestresults(results):
101 newresults = copy.deepcopy(results)
102 #for a in newresults2:
103 # newresults = newresults2[a]
104 for res in newresults:
105 if 'result' not in newresults[res]:
106 continue
107 if 'ptestresult.rawlogs' in newresults[res]['result']:
108 del newresults[res]['result']['ptestresult.rawlogs']
109 if 'ptestresult.sections' in newresults[res]['result']:
110 for i in newresults[res]['result']['ptestresult.sections']:
111 if 'log' in newresults[res]['result']['ptestresult.sections'][i]:
112 del newresults[res]['result']['ptestresult.sections'][i]['log']
113 return newresults
114
115def save_resultsdata(results, destdir, fn="testresults.json", ptestjson=False, ptestlogs=False):
Brad Bishop40320b12019-03-26 16:08:25 -0400116 for res in results:
117 if res:
118 dst = destdir + "/" + res + "/" + fn
119 else:
120 dst = destdir + "/" + fn
121 os.makedirs(os.path.dirname(dst), exist_ok=True)
Brad Bishop19323692019-04-05 15:28:33 -0400122 resultsout = results[res]
123 if not ptestjson:
124 resultsout = strip_ptestresults(results[res])
Brad Bishop40320b12019-03-26 16:08:25 -0400125 with open(dst, 'w') as f:
Brad Bishop19323692019-04-05 15:28:33 -0400126 f.write(json.dumps(resultsout, sort_keys=True, indent=4))
127 for res2 in results[res]:
128 if ptestlogs and 'result' in results[res][res2]:
129 if 'ptestresult.rawlogs' in results[res][res2]['result']:
130 with open(dst.replace(fn, "ptest-raw.log"), "w+") as f:
131 f.write(results[res][res2]['result']['ptestresult.rawlogs']['log'])
132 if 'ptestresult.sections' in results[res][res2]['result']:
133 for i in results[res][res2]['result']['ptestresult.sections']:
134 if 'log' in results[res][res2]['result']['ptestresult.sections'][i]:
135 with open(dst.replace(fn, "ptest-%s.log" % i), "w+") as f:
136 f.write(results[res][res2]['result']['ptestresult.sections'][i]['log'])
Brad Bishop40320b12019-03-26 16:08:25 -0400137
138def git_get_result(repo, tags):
139 git_objs = []
140 for tag in tags:
141 files = repo.run_cmd(['ls-tree', "--name-only", "-r", tag]).splitlines()
142 git_objs.extend([tag + ':' + f for f in files if f.endswith("testresults.json")])
143
144 def parse_json_stream(data):
145 """Parse multiple concatenated JSON objects"""
146 objs = []
147 json_d = ""
148 for line in data.splitlines():
149 if line == '}{':
150 json_d += '}'
151 objs.append(json.loads(json_d))
152 json_d = '{'
153 else:
154 json_d += line
155 objs.append(json.loads(json_d))
156 return objs
157
158 # Optimize by reading all data with one git command
159 results = {}
160 for obj in parse_json_stream(repo.run_cmd(['show'] + git_objs + ['--'])):
161 append_resultsdata(results, obj)
162
163 return results
Brad Bishopc342db32019-05-15 21:57:59 -0400164
165def test_run_results(results):
166 """
167 Convenient generator function that iterates over all test runs that have a
168 result section.
169
170 Generates a tuple of:
171 (result json file path, test run name, test run (dict), test run "results" (dict))
172 for each test run that has a "result" section
173 """
174 for path in results:
175 for run_name, test_run in results[path].items():
176 if not 'result' in test_run:
177 continue
178 yield path, run_name, test_run, test_run['result']
179