v2: Python's nitpick (Andres)
In case of an empty list of traces, the results.yml will contain an empty
curly braces. In YAML, an associative array can also be specified by text
enclosed in curly braces ({}),
This commit also adds the corresponding test to check the behavior of
tracie when no traces are added in the traces.yml file.
Signed-off-by: Pablo Saavedra <psaavedra@igalia.com>
Reviewed-by: Andres Gomez <agomez@igalia.com>
(cherry picked from commit 4504d6374d)
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5548>
183 lines
6.5 KiB
Python
183 lines
6.5 KiB
Python
import argparse
|
|
import enum
|
|
import glob
|
|
import hashlib
|
|
import os
|
|
import requests
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import yaml
|
|
import shutil
|
|
|
|
from pathlib import Path
|
|
from PIL import Image
|
|
from urllib import parse
|
|
|
|
import dump_trace_images
|
|
|
|
TRACES_DB_PATH = "./traces-db/"
|
|
RESULTS_PATH = "./results/"
|
|
|
|
def replay(trace_path, device_name):
|
|
success = dump_trace_images.dump_from_trace(trace_path, [], device_name)
|
|
|
|
if not success:
|
|
print("[check_image] Trace %s couldn't be replayed. See above logs for more information." % (str(trace_path)))
|
|
return None, None, None
|
|
else:
|
|
base_path = trace_path.parent
|
|
file_name = trace_path.name
|
|
files = glob.glob(str(base_path / "test" / device_name / (file_name + "-*" + ".png")))
|
|
assert(files)
|
|
image_file = files[0]
|
|
files = glob.glob(str(base_path / "test" / device_name / (file_name + ".log")))
|
|
assert(files)
|
|
log_file = files[0]
|
|
return hashlib.md5(Image.open(image_file).tobytes()).hexdigest(), image_file, log_file
|
|
|
|
def gitlab_download_metadata(project_url, repo_commit, trace_path):
|
|
url = parse.urlparse(project_url)
|
|
|
|
url_path = url.path
|
|
if url_path.startswith("/"):
|
|
url_path = url_path[1:]
|
|
|
|
gitlab_api_url = url.scheme + "://" + url.netloc + "/api/v4/projects/" + parse.quote_plus(url_path)
|
|
|
|
r = requests.get(gitlab_api_url + "/repository/files/%s/raw?ref=%s" % (parse.quote_plus(trace_path), repo_commit))
|
|
metadata_raw = r.text.strip().split('\n')
|
|
metadata = dict(line.split(' ', 1) for line in metadata_raw[1:])
|
|
oid = metadata["oid"][7:] if metadata["oid"].startswith('sha256:') else metadata["oid"]
|
|
size = int(metadata['size'])
|
|
|
|
return oid, size
|
|
|
|
def gitlfs_download_trace(repo_url, repo_commit, trace_path, oid, size):
|
|
headers = {
|
|
"Accept": "application/vnd.git-lfs+json",
|
|
"Content-Type": "application/vnd.git-lfs+json"
|
|
}
|
|
json = {
|
|
"operation": "download",
|
|
"transfers": [ "basic" ],
|
|
"ref": { "name": "refs/heads/%s" % repo_commit },
|
|
"objects": [
|
|
{
|
|
"oid": oid,
|
|
"size": size
|
|
}
|
|
]
|
|
}
|
|
|
|
r = requests.post(repo_url + "/info/lfs/objects/batch", headers=headers, json=json)
|
|
url = r.json()["objects"][0]["actions"]["download"]["href"]
|
|
open(TRACES_DB_PATH + trace_path, "wb").write(requests.get(url).content)
|
|
|
|
def checksum(filename, hash_factory=hashlib.sha256, chunk_num_blocks=128):
|
|
h = hash_factory()
|
|
with open(filename,'rb') as f:
|
|
for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''):
|
|
h.update(chunk)
|
|
return h.hexdigest()
|
|
|
|
def gitlab_ensure_trace(project_url, repo_commit, trace):
|
|
trace_path = TRACES_DB_PATH + trace['path']
|
|
if project_url is None:
|
|
assert(repo_commit is None)
|
|
assert(os.path.exists(trace_path))
|
|
return
|
|
|
|
os.makedirs(os.path.dirname(trace_path), exist_ok=True)
|
|
|
|
if os.path.exists(trace_path):
|
|
local_oid = checksum(trace_path)
|
|
|
|
remote_oid, size = gitlab_download_metadata(project_url, repo_commit, trace['path'])
|
|
|
|
if not os.path.exists(trace_path) or local_oid != remote_oid:
|
|
print("[check_image] Downloading trace %s" % (trace['path']), end=" ", flush=True)
|
|
download_time = time.time()
|
|
gitlfs_download_trace(project_url + ".git", repo_commit, trace['path'], remote_oid, size)
|
|
print("took %ds." % (time.time() - download_time), flush=True)
|
|
|
|
def gitlab_check_trace(project_url, repo_commit, device_name, trace, expectation):
|
|
gitlab_ensure_trace(project_url, repo_commit, trace)
|
|
|
|
result = {}
|
|
result[trace['path']] = {}
|
|
result[trace['path']]['expected'] = expectation['checksum']
|
|
|
|
trace_path = Path(TRACES_DB_PATH + trace['path'])
|
|
checksum, image_file, log_file = replay(trace_path, device_name)
|
|
if checksum is None:
|
|
result[trace['path']]['actual'] = 'error'
|
|
return False, result
|
|
elif checksum == expectation['checksum']:
|
|
print("[check_image] Images match for %s" % (trace['path']))
|
|
ok = True
|
|
else:
|
|
print("[check_image] Images differ for %s (expected: %s, actual: %s)" %
|
|
(trace['path'], expectation['checksum'], checksum))
|
|
print("[check_image] For more information see "
|
|
"https://gitlab.freedesktop.org/mesa/mesa/blob/master/.gitlab-ci/tracie/README.md")
|
|
ok = False
|
|
|
|
trace_dir = os.path.split(trace['path'])[0]
|
|
dir_in_results = os.path.join(trace_dir, "test", device_name)
|
|
results_path = os.path.join(RESULTS_PATH, dir_in_results)
|
|
os.makedirs(results_path, exist_ok=True)
|
|
shutil.move(log_file, os.path.join(results_path, os.path.split(log_file)[1]))
|
|
if not ok or os.environ.get('TRACIE_STORE_IMAGES', '0') == '1':
|
|
image_name = os.path.split(image_file)[1]
|
|
shutil.move(image_file, os.path.join(results_path, image_name))
|
|
result[trace['path']]['image'] = os.path.join(dir_in_results, image_name)
|
|
|
|
result[trace['path']]['actual'] = checksum
|
|
|
|
return ok, result
|
|
|
|
def run(filename, device_name):
|
|
|
|
with open(filename, 'r') as f:
|
|
y = yaml.safe_load(f)
|
|
|
|
if "traces-db" in y:
|
|
project_url = y["traces-db"]["gitlab-project-url"]
|
|
commit_id = y["traces-db"]["commit"]
|
|
else:
|
|
project_url = None
|
|
commit_id = None
|
|
|
|
traces = y['traces'] or []
|
|
all_ok = True
|
|
results = {}
|
|
for trace in traces:
|
|
for expectation in trace['expectations']:
|
|
if expectation['device'] == device_name:
|
|
ok, result = gitlab_check_trace(project_url, commit_id,
|
|
device_name, trace,
|
|
expectation)
|
|
all_ok = all_ok and ok
|
|
results.update(result)
|
|
|
|
os.makedirs(RESULTS_PATH, exist_ok=True)
|
|
with open(os.path.join(RESULTS_PATH, 'results.yml'), 'w') as f:
|
|
yaml.safe_dump(results, f, default_flow_style=False)
|
|
|
|
return all_ok
|
|
|
|
def main(args):
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--file', required=True,
|
|
help='the name of the traces.yml file listing traces and their checksums for each device')
|
|
parser.add_argument('--device-name', required=True,
|
|
help="the name of the graphics device used to replay traces")
|
|
|
|
args = parser.parse_args(args)
|
|
return run(args.file, args.device_name)
|
|
|
|
if __name__ == "__main__":
|
|
all_ok = main(sys.argv[1:])
|
|
sys.exit(0 if all_ok else 1)
|