radeonsi: stop using TGSI_PROPERTY_FS_COORD_PIXEL_CENTER
[mesa.git] / .gitlab-ci / tracie / tracie.py
1 import argparse
2 import base64
3 import datetime
4 import enum
5 import glob
6 import hashlib
7 import hmac
8 import json
9 import os
10 import requests
11 import sys
12 import tempfile
13 import time
14 import yaml
15 import shutil
16 import xml.etree.ElementTree as ET
17
18 from email.utils import formatdate
19 from pathlib import Path
20 from PIL import Image
21 from urllib import parse
22
23 import dump_trace_images
24
25 TRACES_DB_PATH = "./traces-db/"
26 RESULTS_PATH = "./results/"
27 MINIO_HOST = "minio-packet.freedesktop.org"
28 DASHBOARD_URL = "https://tracie.freedesktop.org/dashboard"
29
30 minio_credentials = None
31
32 def replay(trace_path, device_name):
33 success = dump_trace_images.dump_from_trace(trace_path, [], device_name)
34
35 if not success:
36 print("[check_image] Trace %s couldn't be replayed. See above logs for more information." % (str(trace_path)))
37 return None, None, None
38 else:
39 base_path = trace_path.parent
40 file_name = trace_path.name
41 files = glob.glob(str(base_path / "test" / device_name / (file_name + "-*" + ".png")))
42 assert(files)
43 image_file = files[0]
44 files = glob.glob(str(base_path / "test" / device_name / (file_name + ".log")))
45 assert(files)
46 log_file = files[0]
47 return hashlib.md5(Image.open(image_file).tobytes()).hexdigest(), image_file, log_file
48
49 def gitlab_ensure_trace(project_url, trace):
50 trace_path = TRACES_DB_PATH + trace['path']
51 if project_url is None:
52 if not os.path.exists(trace_path):
53 print("{} missing".format(trace_path))
54 sys.exit(1)
55 return
56
57 os.makedirs(os.path.dirname(trace_path), exist_ok=True)
58
59 if os.path.exists(trace_path):
60 return
61
62 print("[check_image] Downloading trace %s" % (trace['path']), end=" ", flush=True)
63 download_time = time.time()
64 r = requests.get(project_url + trace['path'])
65 open(trace_path, "wb").write(r.content)
66 print("took %ds." % (time.time() - download_time), flush=True)
67
68 def sign_with_hmac(key, message):
69 key = key.encode("UTF-8")
70 message = message.encode("UTF-8")
71
72 signature = hmac.new(key, message, hashlib.sha1).digest()
73
74 return base64.encodebytes(signature).strip().decode()
75
76 def ensure_minio_credentials():
77 global minio_credentials
78
79 if minio_credentials is None:
80 minio_credentials = {}
81
82 params = {'Action': 'AssumeRoleWithWebIdentity',
83 'Version': '2011-06-15',
84 'RoleArn': 'arn:aws:iam::123456789012:role/FederatedWebIdentityRole',
85 'RoleSessionName': '%s:%s' % (os.environ['CI_PROJECT_PATH'], os.environ['CI_JOB_ID']),
86 'DurationSeconds': 900,
87 'WebIdentityToken': os.environ['CI_JOB_JWT']}
88 r = requests.post('https://%s' % (MINIO_HOST), params=params)
89 if r.status_code >= 400:
90 print(r.text)
91 r.raise_for_status()
92
93 root = ET.fromstring(r.text)
94 for attr in root.iter():
95 if attr.tag == '{https://sts.amazonaws.com/doc/2011-06-15/}AccessKeyId':
96 minio_credentials['AccessKeyId'] = attr.text
97 elif attr.tag == '{https://sts.amazonaws.com/doc/2011-06-15/}SecretAccessKey':
98 minio_credentials['SecretAccessKey'] = attr.text
99 elif attr.tag == '{https://sts.amazonaws.com/doc/2011-06-15/}SessionToken':
100 minio_credentials['SessionToken'] = attr.text
101
102 def upload_to_minio(file_name, resource, content_type):
103 ensure_minio_credentials()
104
105 minio_key = minio_credentials['AccessKeyId']
106 minio_secret = minio_credentials['SecretAccessKey']
107 minio_token = minio_credentials['SessionToken']
108
109 date = formatdate(timeval=None, localtime=False, usegmt=True)
110 url = 'https://%s%s' % (MINIO_HOST, resource)
111 to_sign = "PUT\n\n%s\n%s\nx-amz-security-token:%s\n%s" % (content_type, date, minio_token, resource)
112 signature = sign_with_hmac(minio_secret, to_sign)
113
114 with open(file_name, 'rb') as data:
115 headers = {'Host': MINIO_HOST,
116 'Date': date,
117 'Content-Type': content_type,
118 'Authorization': 'AWS %s:%s' % (minio_key, signature),
119 'x-amz-security-token': minio_token}
120 print("Uploading artifact to %s" % url);
121 r = requests.put(url, headers=headers, data=data)
122 if r.status_code >= 400:
123 print(r.text)
124 r.raise_for_status()
125
126 def upload_artifact(file_name, key, content_type):
127 resource = '/artifacts/%s/%s/%s/%s' % (os.environ['CI_PROJECT_PATH'],
128 os.environ['CI_PIPELINE_ID'],
129 os.environ['CI_JOB_ID'],
130 key)
131 upload_to_minio(file_name, resource, content_type)
132
133 def ensure_reference_image(file_name, checksum):
134 resource = '/mesa-tracie-results/%s/%s.png' % (os.environ['CI_PROJECT_PATH'], checksum)
135 url = 'https://%s%s' % (MINIO_HOST, resource)
136 r = requests.head(url, allow_redirects=True)
137 if r.status_code == 200:
138 return
139 upload_to_minio(file_name, resource, 'image/png')
140
141 def gitlab_check_trace(project_url, device_name, trace, expectation):
142 gitlab_ensure_trace(project_url, trace)
143
144 result = {}
145 result[trace['path']] = {}
146 result[trace['path']]['expected'] = expectation['checksum']
147
148 trace_path = Path(TRACES_DB_PATH + trace['path'])
149 checksum, image_file, log_file = replay(trace_path, device_name)
150 if checksum is None:
151 result[trace['path']]['actual'] = 'error'
152 return False, result
153 elif checksum == expectation['checksum']:
154 print("[check_image] Images match for %s" % (trace['path']))
155 ok = True
156 else:
157 print("[check_image] Images differ for %s (expected: %s, actual: %s)" %
158 (trace['path'], expectation['checksum'], checksum))
159 print("[check_image] For more information see "
160 "https://gitlab.freedesktop.org/mesa/mesa/blob/master/.gitlab-ci/tracie/README.md")
161 image_diff_url = "%s/imagediff/%s/%s/%s" % (DASHBOARD_URL,
162 os.environ['CI_PROJECT_PATH'],
163 os.environ['CI_JOB_ID'],
164 trace['path'])
165 print("[check_image] %s" % image_diff_url)
166 ok = False
167
168 trace_dir = os.path.split(trace['path'])[0]
169 dir_in_results = os.path.join(trace_dir, "test", device_name)
170 results_path = os.path.join(RESULTS_PATH, dir_in_results)
171 os.makedirs(results_path, exist_ok=True)
172 shutil.move(log_file, os.path.join(results_path, os.path.split(log_file)[1]))
173 if os.environ.get('TRACIE_UPLOAD_TO_MINIO', '0') == '1':
174 if ok:
175 if os.environ['CI_PROJECT_PATH'] == 'mesa/mesa':
176 ensure_reference_image(image_file, checksum)
177 else:
178 upload_artifact(image_file, 'traces/%s.png' % checksum, 'image/png')
179 if not ok or os.environ.get('TRACIE_STORE_IMAGES', '0') == '1':
180 image_name = os.path.split(image_file)[1]
181 shutil.move(image_file, os.path.join(results_path, image_name))
182 result[trace['path']]['image'] = os.path.join(dir_in_results, image_name)
183
184 result[trace['path']]['actual'] = checksum
185
186 return ok, result
187
188 def run(filename, device_name):
189
190 with open(filename, 'r') as f:
191 y = yaml.safe_load(f)
192
193 if "traces-db" in y:
194 project_url = y["traces-db"]["download-url"]
195 else:
196 project_url = None
197
198 traces = y['traces'] or []
199 all_ok = True
200 results = {}
201 for trace in traces:
202 for expectation in trace['expectations']:
203 if expectation['device'] == device_name:
204 ok, result = gitlab_check_trace(project_url,
205 device_name, trace,
206 expectation)
207 all_ok = all_ok and ok
208 results.update(result)
209
210 os.makedirs(RESULTS_PATH, exist_ok=True)
211 with open(os.path.join(RESULTS_PATH, 'results.yml'), 'w') as f:
212 yaml.safe_dump(results, f, default_flow_style=False)
213 if os.environ.get('TRACIE_UPLOAD_TO_MINIO', '0') == '1':
214 upload_artifact(os.path.join(RESULTS_PATH, 'results.yml'), 'traces/results.yml', 'text/yaml')
215
216 return all_ok
217
218 def main(args):
219 parser = argparse.ArgumentParser()
220 parser.add_argument('--file', required=True,
221 help='the name of the traces.yml file listing traces and their checksums for each device')
222 parser.add_argument('--device-name', required=True,
223 help="the name of the graphics device used to replay traces")
224
225 args = parser.parse_args(args)
226 return run(args.file, args.device_name)
227
228 if __name__ == "__main__":
229 all_ok = main(sys.argv[1:])
230 sys.exit(0 if all_ok else 1)