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

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

Powered by Google App Engine
This is Rietveld 408576698