1#! /usr/bin/env python 2# Copyright 2019 Google LLC. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import json 7import os 8import re 9import subprocess 10import sys 11import threading 12import urllib 13import urllib2 14 15 16assert '/' in [os.sep, os.altsep] 17 18 19skia_directory = os.path.abspath(os.path.dirname(__file__) + '/../..') 20 21 22def get_jobs(): 23 path = skia_directory + '/infra/bots/jobs.json' 24 reg = re.compile('Test-(?P<os>[A-Za-z0-9_]+)-' 25 '(?P<compiler>[A-Za-z0-9_]+)-' 26 '(?P<model>[A-Za-z0-9_]+)-GPU-' 27 '(?P<cpu_or_gpu_value>[A-Za-z0-9_]+)-' 28 '(?P<arch>[A-Za-z0-9_]+)-' 29 '(?P<configuration>[A-Za-z0-9_]+)-' 30 'All(-(?P<extra_config>[A-Za-z0-9_]+)|)') 31 keys = ['os', 'compiler', 'model', 'cpu_or_gpu_value', 'arch', 32 'configuration', 'extra_config'] 33 def fmt(s): 34 return s.encode('utf-8') if s is not None else '' 35 with open(path) as f: 36 jobs = json.load(f) 37 for job in jobs: 38 m = reg.match(job) 39 if m is not None: 40 yield [(k, fmt(m.group(k))) for k in keys] 41 42 43def gold_export_url(job, config, first_commit, last_commit): 44 qq = [('source_type', 'gm'), ('config', config)] + job 45 query = [ 46 ('fbegin', first_commit), 47 ('fend', last_commit), 48 ('query', urllib.urlencode(qq)), 49 ('pos', 'true'), 50 ('neg', 'false'), 51 ('unt', 'false'), 52 ('head', 'true') 53 ] 54 return 'https://public-gold.skia.org/json/export?' + urllib.urlencode(query) 55 56 57def urlopen(url): 58 cookie = os.environ.get('SKIA_GOLD_COOKIE', '') 59 return urllib2.urlopen(urllib2.Request(url, headers={'Cookie': cookie})) 60 61 62def get_results_for_commit(commit, jobs): 63 sys.stderr.write('%s\n' % commit) 64 sys.stderr.flush() 65 CONFIGS = ['gles', 'vk'] 66 passing_tests_for_all_jobs = [] 67 def process(url): 68 try: 69 testResults = json.load(urlopen(url)) 70 except urllib2.URLError: 71 sys.stderr.write('\nerror "%s":\n' % url) 72 return 73 sys.stderr.write('.') 74 sys.stderr.flush() 75 passing_tests = 0 76 for t in testResults: 77 assert t['digests'] 78 passing_tests += 1 79 passing_tests_for_all_jobs.append(passing_tests) 80 all_urls = [gold_export_url(job, config, commit, commit) 81 for job in jobs for config in CONFIGS] 82 threads = [threading.Thread(target=process, args=(url,)) for url in all_urls] 83 for t in threads: 84 t.start() 85 for t in threads: 86 t.join() 87 result = sum(passing_tests_for_all_jobs) 88 sys.stderr.write('\n%d\n' % result) 89 sys.stderr.flush() 90 return result 91 92 93def find_best_commit(commits): 94 jobs = [j for j in get_jobs()] 95 results = [] 96 for commit_name in commits: 97 commit_hash = subprocess.check_output(['git', 'rev-parse', commit_name]).strip() 98 results.append((commit_hash, get_results_for_commit(commit_hash, jobs))) 99 100 best_result = max(r for h, r in results) 101 for h, r in results: 102 if r == best_result: 103 return h 104 return None 105 106 107def generate_commit_list(args): 108 return subprocess.check_output(['git', 'log', '--format=%H'] + args).splitlines() 109 110 111def main(args): 112 os.chdir(skia_directory) 113 subprocess.check_call(['git', 'fetch', 'origin']) 114 sys.stderr.write('%s\n' % ' '.join(args)) 115 commits = generate_commit_list(args) 116 sys.stderr.write('%d\n' % len(commits)) 117 best = find_best_commit(commits) 118 sys.stderr.write('DONE:\n') 119 sys.stderr.flush() 120 sys.stdout.write('%s\n' % best) 121 122 123usage = '''Example usage: 124 python %s origin/master ^origin/skqp/dev < /dev/null > LOG 2>&1 & disown 125''' 126 127if __name__ == '__main__': 128 if len(sys.argv) < 2: 129 sys.stderr.write(usage % sys.argv[0]) 130 sys.exit(1) 131 main(sys.argv[1:]) 132