Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Side by Side Diff: infra/bots/common.py

Issue 1743113003: Add test_skia.py, isolates for test_skia, images, skps (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Address comments Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | infra/bots/compile_skia.isolate » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2016 Google Inc. 3 # Copyright 2016 Google Inc.
4 # 4 #
5 # Use of this source code is governed by a BSD-style license that can be 5 # Use of this source code is governed by a BSD-style license that can be
6 # found in the LICENSE file. 6 # found in the LICENSE file.
7 7
8 8
9 import contextlib
10 import math
9 import os 11 import os
12 import shutil
13 import socket
10 import subprocess 14 import subprocess
11 import sys 15 import sys
16 import time
17 import urllib2
12 18
13 from flavor import android_flavor 19 from flavor import android_flavor
14 from flavor import chromeos_flavor 20 from flavor import chromeos_flavor
15 from flavor import cmake_flavor 21 from flavor import cmake_flavor
16 from flavor import coverage_flavor 22 from flavor import coverage_flavor
17 from flavor import default_flavor 23 from flavor import default_flavor
18 from flavor import ios_flavor 24 from flavor import ios_flavor
19 from flavor import valgrind_flavor 25 from flavor import valgrind_flavor
20 from flavor import xsan_flavor 26 from flavor import xsan_flavor
21 27
22 28
23 CONFIG_COVERAGE = 'Coverage' 29 CONFIG_COVERAGE = 'Coverage'
24 CONFIG_DEBUG = 'Debug' 30 CONFIG_DEBUG = 'Debug'
25 CONFIG_RELEASE = 'Release' 31 CONFIG_RELEASE = 'Release'
26 VALID_CONFIGS = (CONFIG_COVERAGE, CONFIG_DEBUG, CONFIG_RELEASE) 32 VALID_CONFIGS = (CONFIG_COVERAGE, CONFIG_DEBUG, CONFIG_RELEASE)
27 33
28 GM_ACTUAL_FILENAME = 'actual-results.json' 34 GM_ACTUAL_FILENAME = 'actual-results.json'
29 GM_EXPECTATIONS_FILENAME = 'expected-results.json' 35 GM_EXPECTATIONS_FILENAME = 'expected-results.json'
30 GM_IGNORE_TESTS_FILENAME = 'ignored-tests.txt' 36 GM_IGNORE_TESTS_FILENAME = 'ignored-tests.txt'
31 37
38 GOLD_UNINTERESTING_HASHES_URL = 'https://gold.skia.org/_/hashes'
39
32 GS_GM_BUCKET = 'chromium-skia-gm' 40 GS_GM_BUCKET = 'chromium-skia-gm'
33 GS_SUMMARIES_BUCKET = 'chromium-skia-gm-summaries' 41 GS_SUMMARIES_BUCKET = 'chromium-skia-gm-summaries'
34 42
43 GS_SUBDIR_TMPL_SK_IMAGE = 'skimage/v%s'
44 GS_SUBDIR_TMPL_SKP = 'playback_%s/skps'
45
35 SKIA_REPO = 'https://skia.googlesource.com/skia.git' 46 SKIA_REPO = 'https://skia.googlesource.com/skia.git'
36 INFRA_REPO = 'https://skia.googlesource.com/buildbot.git' 47 INFRA_REPO = 'https://skia.googlesource.com/buildbot.git'
37 48
38 SERVICE_ACCOUNT_FILE = 'service-account-skia.json' 49 SERVICE_ACCOUNT_FILE = 'service-account-skia.json'
39 SERVICE_ACCOUNT_INTERNAL_FILE = 'service-account-skia-internal.json' 50 SERVICE_ACCOUNT_INTERNAL_FILE = 'service-account-skia-internal.json'
40 51
52 VERSION_FILE_SK_IMAGE = 'SK_IMAGE_VERSION'
53 VERSION_FILE_SKP = 'SKP_VERSION'
54
41 55
42 def is_android(bot_cfg): 56 def is_android(bot_cfg):
43 """Determine whether the given bot is an Android bot.""" 57 """Determine whether the given bot is an Android bot."""
44 return ('Android' in bot_cfg.get('extra_config', '') or 58 return ('Android' in bot_cfg.get('extra_config', '') or
45 bot_cfg.get('os') == 'Android') 59 bot_cfg.get('os') == 'Android')
46 60
47 def is_chromeos(bot_cfg): 61 def is_chromeos(bot_cfg):
48 return ('CrOS' in bot_cfg.get('extra_config', '') or 62 return ('CrOS' in bot_cfg.get('extra_config', '') or
49 bot_cfg.get('os') == 'ChromeOS') 63 bot_cfg.get('os') == 'ChromeOS')
50 64
51 def is_cmake(bot_cfg): 65 def is_cmake(bot_cfg):
52 return 'CMake' in bot_cfg.get('extra_config', '') 66 return 'CMake' in bot_cfg.get('extra_config', '')
53 67
54 def is_ios(bot_cfg): 68 def is_ios(bot_cfg):
55 return ('iOS' in bot_cfg.get('extra_config', '') or 69 return ('iOS' in bot_cfg.get('extra_config', '') or
56 bot_cfg.get('os') == 'iOS') 70 bot_cfg.get('os') == 'iOS')
57 71
58 72
59 def is_valgrind(bot_cfg): 73 def is_valgrind(bot_cfg):
60 return 'Valgrind' in bot_cfg.get('extra_config', '') 74 return 'Valgrind' in bot_cfg.get('extra_config', '')
61 75
62 76
63 def is_xsan(bot_cfg): 77 def is_xsan(bot_cfg):
64 return (bot_cfg.get('extra_config') == 'ASAN' or 78 return (bot_cfg.get('extra_config') == 'ASAN' or
65 bot_cfg.get('extra_config') == 'MSAN' or 79 bot_cfg.get('extra_config') == 'MSAN' or
66 bot_cfg.get('extra_config') == 'TSAN') 80 bot_cfg.get('extra_config') == 'TSAN')
67 81
68 82
83 def download_dir(skia_dir, tmp_dir, version_file, gs_path_tmpl, dst_dir):
84 # Ensure that the tmp_dir exists.
85 if not os.path.isdir(tmp_dir):
86 os.makedirs(tmp_dir)
87
88 # Get the expected version.
89 with open(os.path.join(skia_dir, version_file)) as f:
90 expected_version = f.read().rstrip()
91
92 print 'Expected %s = %s' % (version_file, expected_version)
93
94 # Get the actually-downloaded version, if we have one.
95 actual_version_file = os.path.join(tmp_dir, version_file)
96 try:
97 with open(actual_version_file) as f:
98 actual_version = f.read().rstrip()
99 except IOError:
100 actual_version = -1
101
102 print 'Actual %s = %s' % (version_file, actual_version)
103
104 # If we don't have the desired version, download it.
105 if actual_version != expected_version:
106 if actual_version != -1:
107 os.remove(actual_version_file)
108 if os.path.isdir(dst_dir):
109 shutil.rmtree(dst_dir)
110 os.makedirs(dst_dir)
111 gs_path = 'gs://%s/%s/*' % (GS_GM_BUCKET, gs_path_tmpl % expected_version)
112 print 'Downloading from %s' % gs_path
113 subprocess.check_call(['gsutil', 'cp', '-R', gs_path, dst_dir])
114 with open(actual_version_file, 'w') as f:
115 f.write(expected_version)
116
117
118 def get_uninteresting_hashes(hashes_file):
119 retries = 5
120 timeout = 60
121 wait_base = 15
122
123 socket.setdefaulttimeout(timeout)
124 for retry in range(retries):
125 try:
126 with contextlib.closing(
127 urllib2.urlopen(GOLD_UNINTERESTING_HASHES_URL, timeout=timeout)) as w:
128 hashes = w.read()
129 with open(hashes_file, 'w') as f:
130 f.write(hashes)
131 break
132 except Exception as e:
133 print >> sys.stderr, 'Failed to get uninteresting hashes from %s:\n%s' % (
134 GOLD_UNINTERESTING_HASHES_URL, e)
135 if retry == retries:
136 raise
137 waittime = wait_base * math.pow(2, retry)
138 print 'Retry in %d seconds.' % waittime
139 time.sleep(waittime)
140
141
69 class BotInfo(object): 142 class BotInfo(object):
70 def __init__(self, bot_name, slave_name, out_dir): 143 def __init__(self, bot_name, swarm_out_dir):
71 """Initialize the bot, given its name. 144 """Initialize the bot, given its name.
72 145
73 Assumes that CWD is the directory containing this file. 146 Assumes that CWD is the directory containing this file.
74 """ 147 """
75 self.name = bot_name 148 self.name = bot_name
76 self.slave_name = slave_name
77 self.skia_dir = os.path.abspath(os.path.join( 149 self.skia_dir = os.path.abspath(os.path.join(
78 os.path.dirname(os.path.realpath(__file__)), 150 os.path.dirname(os.path.realpath(__file__)),
79 os.pardir, os.pardir)) 151 os.pardir, os.pardir))
152 self.swarm_out_dir = swarm_out_dir
80 os.chdir(self.skia_dir) 153 os.chdir(self.skia_dir)
81 self.build_dir = os.path.abspath(os.path.join(self.skia_dir, os.pardir)) 154 self.build_dir = os.path.abspath(os.path.join(self.skia_dir, os.pardir))
82 self.out_dir = out_dir
83 self.spec = self.get_bot_spec(bot_name) 155 self.spec = self.get_bot_spec(bot_name)
156 self.bot_cfg = self.spec['builder_cfg']
157 if self.bot_cfg['role'] == 'Build':
158 self.out_dir = os.path.join(swarm_out_dir, 'out')
159 else:
160 self.out_dir = 'out'
84 self.configuration = self.spec['configuration'] 161 self.configuration = self.spec['configuration']
85 self.default_env = { 162 self.default_env = {
86 'SKIA_OUT': self.out_dir, 163 'SKIA_OUT': self.out_dir,
87 'BUILDTYPE': self.configuration, 164 'BUILDTYPE': self.configuration,
88 'PATH': os.environ['PATH'], 165 'PATH': os.environ['PATH'],
89 } 166 }
90 self.default_env.update(self.spec['env']) 167 self.default_env.update(self.spec['env'])
91 self.build_targets = [str(t) for t in self.spec['build_targets']] 168 self.build_targets = [str(t) for t in self.spec['build_targets']]
92 self.bot_cfg = self.spec['builder_cfg']
93 self.is_trybot = self.bot_cfg['is_trybot'] 169 self.is_trybot = self.bot_cfg['is_trybot']
94 self.upload_dm_results = self.spec['upload_dm_results'] 170 self.upload_dm_results = self.spec['upload_dm_results']
95 self.upload_perf_results = self.spec['upload_perf_results'] 171 self.upload_perf_results = self.spec['upload_perf_results']
172 self.perf_data_dir = os.path.join(self.swarm_out_dir, 'perfdata',
173 self.name, 'data')
174 self.resource_dir = os.path.join(self.build_dir, 'resources')
175 self.images_dir = os.path.join(self.build_dir, 'images')
176 self.local_skp_dir = os.path.join(self.build_dir, 'playback', 'skps')
96 self.dm_flags = self.spec['dm_flags'] 177 self.dm_flags = self.spec['dm_flags']
97 self.nanobench_flags = self.spec['nanobench_flags'] 178 self.nanobench_flags = self.spec['nanobench_flags']
98 self._ccache = None 179 self._ccache = None
99 self._checked_for_ccache = False 180 self._checked_for_ccache = False
181 self._already_ran = {}
182 self.tmp_dir = os.path.join(self.build_dir, 'tmp')
100 self.flavor = self.get_flavor(self.bot_cfg) 183 self.flavor = self.get_flavor(self.bot_cfg)
101 184
185 # These get filled in during subsequent steps.
186 self.device_dirs = None
187 self.build_number = None
188 self.got_revision = None
189 self.master_name = None
190 self.slave_name = None
191
102 @property 192 @property
103 def ccache(self): 193 def ccache(self):
104 if not self._checked_for_ccache: 194 if not self._checked_for_ccache:
105 self._checked_for_ccache = True 195 self._checked_for_ccache = True
106 if sys.platform != 'win32': 196 if sys.platform != 'win32':
107 try: 197 try:
108 result = subprocess.check_output(['which', 'ccache']) 198 result = subprocess.check_output(['which', 'ccache'])
109 self._ccache = result.rstrip() 199 self._ccache = result.rstrip()
110 except subprocess.CalledProcessError: 200 except subprocess.CalledProcessError:
111 pass 201 pass
(...skipping 29 matching lines...) Expand all
141 _env = {} 231 _env = {}
142 _env.update(self.default_env) 232 _env.update(self.default_env)
143 _env.update(env or {}) 233 _env.update(env or {})
144 cwd = cwd or self.skia_dir 234 cwd = cwd or self.skia_dir
145 print '============' 235 print '============'
146 print 'CMD: %s' % cmd 236 print 'CMD: %s' % cmd
147 print 'CWD: %s' % cwd 237 print 'CWD: %s' % cwd
148 print 'ENV: %s' % _env 238 print 'ENV: %s' % _env
149 print '============' 239 print '============'
150 subprocess.check_call(cmd, env=_env, cwd=cwd) 240 subprocess.check_call(cmd, env=_env, cwd=cwd)
241
242 def compile_steps(self):
243 for t in self.build_targets:
244 self.flavor.compile(t)
245
246 def _run_once(self, fn, *args, **kwargs):
247 if not fn.__name__ in self._already_ran:
248 self._already_ran[fn.__name__] = True
249 fn(*args, **kwargs)
250
251 def install(self):
252 """Copy the required executables and files to the device."""
253 self.device_dirs = self.flavor.get_device_dirs()
254
255 # Run any device-specific installation.
256 self.flavor.install()
257
258 # TODO(borenet): Only copy files which have changed.
259 # Resources
260 self.flavor.copy_directory_contents_to_device(self.resource_dir,
261 self.device_dirs.resource_dir)
262
263 def _key_params(self):
264 """Build a unique key from the builder name (as a list).
265
266 E.g. arch x86 gpu GeForce320M mode MacMini4.1 os Mac10.6
267 """
268 # Don't bother to include role, which is always Test.
269 # TryBots are uploaded elsewhere so they can use the same key.
270 blacklist = ['role', 'is_trybot']
271
272 flat = []
273 for k in sorted(self.bot_cfg.keys()):
274 if k not in blacklist:
275 flat.append(k)
276 flat.append(self.bot_cfg[k])
277 return flat
278
279 def test_steps(self, got_revision, master_name, slave_name, build_number):
280 """Run the DM test."""
281 self.build_number = build_number
282 self.got_revision = got_revision
283 self.master_name = master_name
284 self.slave_name = slave_name
285 self._run_once(self.install)
286
287 use_hash_file = False
288 if self.upload_dm_results:
289 # This must run before we write anything into self.device_dirs.dm_dir
290 # or we may end up deleting our output on machines where they're the same.
291 host_dm_dir = os.path.join(self.swarm_out_dir, 'dm')
292 print 'host dm dir: %s' % host_dm_dir
293 self.flavor.create_clean_host_dir(host_dm_dir)
294 if str(host_dm_dir) != str(self.device_dirs.dm_dir):
295 self.flavor.create_clean_device_dir(self.device_dirs.dm_dir)
296
297 # Obtain the list of already-generated hashes.
298 hash_filename = 'uninteresting_hashes.txt'
299 host_hashes_file = self.tmp_dir.join(hash_filename)
300 hashes_file = self.flavor.device_path_join(
301 self.device_dirs.tmp_dir, hash_filename)
302
303 try:
304 get_uninteresting_hashes(host_hashes_file)
305 except Exception:
306 pass
307
308 if os.path.exists(host_hashes_file):
309 self.flavor.copy_file_to_device(host_hashes_file, hashes_file)
310 use_hash_file = True
311
312 # Run DM.
313 properties = [
314 'gitHash', self.got_revision,
315 'master', self.master_name,
316 'builder', self.name,
317 'build_number', self.build_number,
318 ]
319 if self.is_trybot:
320 properties.extend([
321 'issue', self.m.properties['issue'],
322 'patchset', self.m.properties['patchset'],
323 ])
324
325 args = [
326 'dm',
327 '--undefok', # This helps branches that may not know new flags.
328 '--verbose',
329 '--resourcePath', self.device_dirs.resource_dir,
330 '--skps', self.device_dirs.skp_dir,
331 '--images', self.flavor.device_path_join(
332 self.device_dirs.images_dir, 'dm'),
333 '--nameByHash',
334 '--properties'
335 ] + properties
336
337 args.append('--key')
338 args.extend(self._key_params())
339 if use_hash_file:
340 args.extend(['--uninterestingHashesFile', hashes_file])
341 if self.upload_dm_results:
342 args.extend(['--writePath', self.device_dirs.dm_dir])
343
344 skip_flag = None
345 if self.bot_cfg.get('cpu_or_gpu') == 'CPU':
346 skip_flag = '--nogpu'
347 elif self.bot_cfg.get('cpu_or_gpu') == 'GPU':
348 skip_flag = '--nocpu'
349 if skip_flag:
350 args.append(skip_flag)
351 args.extend(self.dm_flags)
352
353 self.flavor.run(args, env=self.default_env)
354
355 if self.upload_dm_results:
356 # Copy images and JSON to host machine if needed.
357 self.flavor.copy_directory_contents_to_host(self.device_dirs.dm_dir,
358 host_dm_dir)
359
360 # See skia:2789.
361 if ('Valgrind' in self.name and
362 self.builder_cfg.get('cpu_or_gpu') == 'GPU'):
363 abandonGpuContext = list(args)
364 abandonGpuContext.append('--abandonGpuContext')
365 self.flavor.run(abandonGpuContext)
366 preAbandonGpuContext = list(args)
367 preAbandonGpuContext.append('--preAbandonGpuContext')
368 self.flavor.run(preAbandonGpuContext)
OLDNEW
« no previous file with comments | « no previous file | infra/bots/compile_skia.isolate » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698