Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 1 | # resulttool - common library/utility functions |
| 2 | # |
| 3 | # Copyright (c) 2019, Intel Corporation. |
| 4 | # Copyright (c) 2019, Linux Foundation |
| 5 | # |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 6 | # SPDX-License-Identifier: GPL-2.0-only |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 7 | # |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 8 | |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 9 | import os |
| 10 | import json |
| 11 | import scriptpath |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 12 | import copy |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 13 | import urllib.request |
| 14 | import posixpath |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 15 | scriptpath.add_oe_lib_path() |
| 16 | |
| 17 | flatten_map = { |
| 18 | "oeselftest": [], |
| 19 | "runtime": [], |
| 20 | "sdk": [], |
| 21 | "sdkext": [], |
| 22 | "manual": [] |
| 23 | } |
| 24 | regression_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 | } |
| 31 | store_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 Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 39 | def 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 Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 45 | # |
| 46 | # Load the json file and append the results data into the provided results dict |
| 47 | # |
| 48 | def append_resultsdata(results, f, configmap=store_map): |
| 49 | if type(f) is str: |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 50 | 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 Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 59 | 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 Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 65 | data[res]["configuration"]["TESTSERIES"] = testseries |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 66 | 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 Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 73 | results[testpath][res] = data[res] |
| 74 | |
| 75 | # |
| 76 | # Walk a directory and find/load results data |
| 77 | # or load directly from a file |
| 78 | # |
| 79 | def load_resultsdata(source, configmap=store_map): |
| 80 | results = {} |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 81 | if is_url(source) or os.path.isfile(source): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 82 | 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 | |
| 91 | def 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 Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 100 | def 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 | |
| 115 | def save_resultsdata(results, destdir, fn="testresults.json", ptestjson=False, ptestlogs=False): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 116 | 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 Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 122 | resultsout = results[res] |
| 123 | if not ptestjson: |
| 124 | resultsout = strip_ptestresults(results[res]) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 125 | with open(dst, 'w') as f: |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 126 | 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 Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 137 | |
| 138 | def 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 Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 164 | |
| 165 | def 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 | |