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 6 import json 7 import os 8 import re 9 import subprocess 10 import sys 11 import threading 12 import urllib 13 import urllib2 14 15 16 assert '/' in [os.sep, os.altsep] 17 18 19 skia_directory = os.path.abspath(os.path.dirname(__file__) + '/../..') 20 21 22 def 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 43 def 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 57 def get_results_for_commit(commit, jobs): 58 sys.stderr.write('%s\n' % commit) 59 sys.stderr.flush() 60 CONFIGS = ['gles', 'vk'] 61 passing_tests_for_all_jobs = [] 62 def process(url): 63 testResults = json.load(urllib2.urlopen(url)) 64 sys.stderr.write('.') 65 sys.stderr.flush() 66 passing_tests = 0 67 for t in testResults: 68 assert t['digests'] 69 passing_tests += 1 70 passing_tests_for_all_jobs.append(passing_tests) 71 all_urls = [gold_export_url(job, config, commit, commit) 72 for job in jobs for config in CONFIGS] 73 threads = [threading.Thread(target=process, args=(url,)) for url in all_urls] 74 for t in threads: 75 t.start() 76 for t in threads: 77 t.join() 78 result = sum(passing_tests_for_all_jobs) 79 sys.stderr.write('\n%d\n' % result) 80 sys.stderr.flush() 81 return result 82 83 84 def find_best_commit(commits): 85 jobs = [j for j in get_jobs()] 86 results = [] 87 for commit_name in commits: 88 commit_hash = subprocess.check_output(['git', 'rev-parse', commit_name]).strip() 89 results.append((commit_hash, get_results_for_commit(commit_hash, jobs))) 90 91 best_result = max(r for h, r in results) 92 for h, r in results: 93 if r == best_result: 94 return h 95 return None 96 97 98 def generate_commit_list(args): 99 return subprocess.check_output(['git', 'log', '--format=%H'] + args).splitlines() 100 101 102 def main(args): 103 os.chdir(skia_directory) 104 subprocess.check_call(['git', 'fetch', 'origin']) 105 sys.stderr.write('%s\n' % ' '.join(args)) 106 commits = generate_commit_list(args) 107 sys.stderr.write('%d\n' % len(commits)) 108 best = find_best_commit(commits) 109 sys.stderr.write('DONE:\n') 110 sys.stderr.flush() 111 sys.stdout.write('%s\n' % best) 112 113 114 usage = '''Example usage: 115 python %s origin/master ^origin/skqp/dev < /dev/null > LOG 2>&1 & disown 116 ''' 117 118 if __name__ == '__main__': 119 if len(sys.argv) < 2: 120 sys.stderr.write(usage % sys.argv[0]) 121 sys.exit(1) 122 main(sys.argv[1:]) 123