OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 | |
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """Dart2js buildbot steps | |
8 | |
9 Runs tests for the dart2js compiler. | |
10 """ | |
11 | |
12 import platform | |
13 import optparse | |
14 import os | |
15 import re | |
16 import shutil | |
17 import subprocess | |
18 import sys | |
19 | |
20 BUILDER_NAME = 'BUILDBOT_BUILDERNAME' | |
21 BUILDER_CLOBBER = 'BUILDBOT_CLOBBER' | |
22 | |
23 | |
24 DART_PATH = os.path.dirname( | |
25 os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
26 | |
27 DART2JS_BUILDER = ( | |
28 r'dart2js-(linux|mac|windows)(-(jsshell))?-(debug|release)(-(checked|host-ch
ecked))?(-(host-checked))?-?(\d*)-?(\d*)') | |
29 WEB_BUILDER = ( | |
30 r'dart2js-(ie9|ie10|ff|safari|chrome|opera)-(win7|win8|mac|linux)(-(all|html
))?') | |
31 | |
32 NO_COLOR_ENV = dict(os.environ) | |
33 NO_COLOR_ENV['TERM'] = 'nocolor' | |
34 | |
35 class BuildInfo(object): | |
36 """ Encapsulation of build information. | |
37 - compiler: 'dart2js' or None when the builder has an incorrect name | |
38 - runtime: 'd8', 'ie', 'ff', 'safari', 'chrome', 'opera' | |
39 - mode: 'debug' or 'release' | |
40 - system: 'linux', 'mac', or 'win7' | |
41 - checked: True if we should run in checked mode, otherwise False | |
42 - host_checked: True if we should run in host checked mode, otherwise False | |
43 - shard_index: The shard we are running, None when not specified. | |
44 - total_shards: The total number of shards, None when not specified. | |
45 - is_buildbot: True if we are on a buildbot (or emulating it). | |
46 - test_set: Specification of a non standard test set, default None | |
47 """ | |
48 def __init__(self, compiler, runtime, mode, system, checked=False, | |
49 host_checked=False, shard_index=None, total_shards=None, | |
50 is_buildbot=False, test_set=None): | |
51 self.compiler = compiler | |
52 self.runtime = runtime | |
53 self.mode = mode | |
54 self.system = system | |
55 self.checked = checked | |
56 self.host_checked = host_checked | |
57 self.shard_index = shard_index | |
58 self.total_shards = total_shards | |
59 self.is_buildbot = is_buildbot | |
60 self.test_set = test_set | |
61 | |
62 def PrintBuildInfo(self): | |
63 shard_description = "" | |
64 if self.shard_index: | |
65 shard_description = " shard %s of %s" % (self.shard_index, | |
66 self.total_shards) | |
67 print ("compiler: %s, runtime: %s mode: %s, system: %s," | |
68 " checked: %s, host-checked: %s, test-set: %s%s" | |
69 ) % (self.compiler, self.runtime, self.mode, self.system, | |
70 self.checked, self.host_checked, self.test_set, | |
71 shard_description) | |
72 | |
73 | |
74 def GetBuildInfo(): | |
75 """Returns a BuildInfo object for the current buildbot based on the | |
76 name of the builder. | |
77 """ | |
78 parser = optparse.OptionParser() | |
79 parser.add_option('-n', '--name', dest='name', help='The name of the build' | |
80 'bot you would like to emulate (ex: web-chrome-win7)', default=None) | |
81 args, _ = parser.parse_args() | |
82 | |
83 compiler = None | |
84 runtime = None | |
85 mode = None | |
86 system = None | |
87 builder_name = os.environ.get(BUILDER_NAME) | |
88 checked = False | |
89 host_checked = False | |
90 shard_index = None | |
91 total_shards = None | |
92 is_buildbot = True | |
93 test_set = None | |
94 | |
95 if not builder_name: | |
96 # We are not running on a buildbot. | |
97 is_buildbot = False | |
98 if args.name: | |
99 builder_name = args.name | |
100 else: | |
101 print 'Use -n $BUILDBOT_NAME for the bot you would like to emulate.' | |
102 sys.exit(1) | |
103 | |
104 if builder_name: | |
105 dart2js_pattern = re.match(DART2JS_BUILDER, builder_name) | |
106 web_pattern = re.match(WEB_BUILDER, builder_name) | |
107 | |
108 if web_pattern: | |
109 compiler = 'dart2js' | |
110 runtime = web_pattern.group(1) | |
111 system = web_pattern.group(2) | |
112 mode = 'release' | |
113 test_set = web_pattern.group(4) | |
114 elif dart2js_pattern: | |
115 compiler = 'dart2js' | |
116 system = dart2js_pattern.group(1) | |
117 runtime = 'd8' | |
118 if dart2js_pattern.group(3) == 'jsshell': | |
119 runtime = 'jsshell' | |
120 mode = dart2js_pattern.group(4) | |
121 # The valid naming parts for checked and host-checked are: | |
122 # Empty: checked=False, host_checked=False | |
123 # -checked: checked=True, host_checked=False | |
124 # -host-checked: checked=False, host_checked=True | |
125 # -checked-host-checked: checked=True, host_checked=True | |
126 if dart2js_pattern.group(6) == 'checked': | |
127 checked = True | |
128 if dart2js_pattern.group(6) == 'host-checked': | |
129 host_checked = True | |
130 if dart2js_pattern.group(8) == 'host-checked': | |
131 host_checked = True | |
132 shard_index = dart2js_pattern.group(9) | |
133 total_shards = dart2js_pattern.group(10) | |
134 | |
135 if system == 'windows': | |
136 system = 'win7' | |
137 | |
138 if (system == 'win7' and platform.system() != 'Windows') or ( | |
139 system == 'mac' and platform.system() != 'Darwin') or ( | |
140 system == 'linux' and platform.system() != 'Linux'): | |
141 print ('Error: You cannot emulate a buildbot with a platform different ' | |
142 'from your own.') | |
143 sys.exit(1) | |
144 return BuildInfo(compiler, runtime, mode, system, checked, host_checked, | |
145 shard_index, total_shards, is_buildbot, test_set) | |
146 | |
147 | |
148 def NeedsXterm(compiler, runtime): | |
149 return runtime in ['ie', 'chrome', 'safari', 'opera', 'ff', 'drt'] | |
150 | |
151 | |
152 def TestStepName(name, flags): | |
153 # Filter out flags with '=' as this breaks the /stats feature of the | |
154 # build bot. | |
155 flags = [x for x in flags if not '=' in x] | |
156 return ('%s tests %s' % (name, ' '.join(flags))).strip() | |
157 | |
158 | |
159 def TestStep(name, mode, system, compiler, runtime, targets, flags): | |
160 step_name = TestStepName(name, flags) | |
161 print '@@@BUILD_STEP %s@@@' % step_name | |
162 sys.stdout.flush() | |
163 if NeedsXterm(compiler, runtime) and system == 'linux': | |
164 cmd = ['xvfb-run', '-a'] | |
165 else: | |
166 cmd = [] | |
167 | |
168 user_test = os.environ.get('USER_TEST', 'no') | |
169 | |
170 cmd.extend([sys.executable, | |
171 os.path.join(os.curdir, 'tools', 'test.py'), | |
172 '--step_name=' + step_name, | |
173 '--mode=' + mode, | |
174 '--compiler=' + compiler, | |
175 '--runtime=' + runtime, | |
176 '--time', | |
177 '--use-sdk', | |
178 '--report']) | |
179 | |
180 if user_test == 'yes': | |
181 cmd.append('--progress=color') | |
182 else: | |
183 cmd.extend(['--progress=buildbot', '-v']) | |
184 | |
185 if flags: | |
186 cmd.extend(flags) | |
187 cmd.extend(targets) | |
188 | |
189 print 'running %s' % (' '.join(cmd)) | |
190 exit_code = subprocess.call(cmd, env=NO_COLOR_ENV) | |
191 if exit_code != 0: | |
192 print '@@@STEP_FAILURE@@@' | |
193 return exit_code | |
194 | |
195 | |
196 def BuildSDK(mode, system): | |
197 """ build the SDK. | |
198 Args: | |
199 - mode: either 'debug' or 'release' | |
200 - system: either 'linux', 'mac', or 'win7' | |
201 """ | |
202 os.chdir(DART_PATH) | |
203 | |
204 args = [sys.executable, './tools/build.py', '--mode=' + mode, 'create_sdk'] | |
205 print 'running %s' % (' '.join(args)) | |
206 return subprocess.call(args, env=NO_COLOR_ENV) | |
207 | |
208 | |
209 def TestCompiler(runtime, mode, system, flags, is_buildbot, test_set): | |
210 """ test the compiler. | |
211 Args: | |
212 - runtime: either 'd8', 'jsshell', or one of the browsers, see GetBuildInfo | |
213 - mode: either 'debug' or 'release' | |
214 - system: either 'linux', 'mac', or 'win7' | |
215 - flags: extra flags to pass to test.dart | |
216 - is_buildbot: true if we are running on a real buildbot instead of | |
217 emulating one. | |
218 - test_set: Specification of a non standard test set, default None | |
219 """ | |
220 | |
221 # Make sure we are in the dart directory | |
222 os.chdir(DART_PATH) | |
223 | |
224 if system.startswith('win') and runtime == 'ie': | |
225 # There should not be more than one InternetExplorerDriver instance | |
226 # running at a time. For details, see | |
227 # http://code.google.com/p/selenium/wiki/InternetExplorerDriver. | |
228 flags += ['-j1'] | |
229 | |
230 def GetPath(runtime): | |
231 """ Helper to get the path to the Chrome or Firefox executable for a | |
232 particular platform on the buildbot. Throws a KeyError if runtime is not | |
233 either 'chrome' or 'ff'.""" | |
234 if system == 'mac': | |
235 partDict = {'chrome': 'Google\\ Chrome', 'ff': 'Firefox'} | |
236 mac_path = '/Applications/%s.app/Contents/MacOS/%s' | |
237 path_dict = {'chrome': mac_path % (partDict[runtime], partDict[runtime]), | |
238 'ff': mac_path % (partDict[runtime], partDict[runtime].lower())} | |
239 elif system == 'linux': | |
240 path_dict = {'ff': 'firefox', 'chrome': 'google-chrome'} | |
241 else: | |
242 # Windows. | |
243 path_dict = {'ff': os.path.join('C:/', 'Program Files (x86)', | |
244 'Mozilla Firefox', 'firefox.exe'), | |
245 'chrome': os.path.join('C:/', 'Users', 'chrome-bot', 'AppData', | |
246 'Local', 'Google', 'Chrome', 'Application', 'chrome.exe')} | |
247 return path_dict[runtime] | |
248 | |
249 if system == 'linux' and runtime == 'chrome': | |
250 # TODO(ngeoffray): We should install selenium on the buildbot. | |
251 runtime = 'drt' | |
252 elif (runtime == 'ff' or runtime == 'chrome') and is_buildbot: | |
253 # Print out browser version numbers if we're running on the buildbot (where | |
254 # we know the paths to these browser installations). | |
255 version_query_string = '"%s" --version' % GetPath(runtime) | |
256 if runtime == 'ff' and system == 'win7': | |
257 version_query_string += '| more' | |
258 elif runtime == 'chrome' and system == 'win7': | |
259 version_query_string = ('''reg query "HKCU\\Software\\Microsoft\\''' + | |
260 '''Windows\\CurrentVersion\\Uninstall\\Google Chrome" /v Version''') | |
261 p = subprocess.Popen(version_query_string, | |
262 stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) | |
263 output, stderr = p.communicate() | |
264 output = output.split() | |
265 try: | |
266 print 'Version of %s: %s' % (runtime, output[-1]) | |
267 except IndexError: | |
268 # Failed to obtain version information. Continue running tests. | |
269 pass | |
270 | |
271 if runtime == 'd8': | |
272 # The dart2js compiler isn't self-hosted (yet) so we run its | |
273 # unit tests on the VM. We avoid doing this on the builders | |
274 # that run the browser tests to cut down on the cycle time. | |
275 TestStep("dart2js_unit", mode, system, 'none', 'vm', ['dart2js'], flags) | |
276 | |
277 if not (system.startswith('win') and runtime == 'ie'): | |
278 # Run the default set of test suites. | |
279 TestStep("dart2js", mode, system, 'dart2js', runtime, [], flags) | |
280 | |
281 # TODO(kasperl): Consider running peg and css tests too. | |
282 extras = ['dart2js_extra', 'dart2js_native', 'dart2js_foreign'] | |
283 TestStep("dart2js_extra", mode, system, 'dart2js', runtime, extras, flags) | |
284 else: | |
285 # TODO(ricow): Enable standard sharding for IE bots when we have more vms. | |
286 if test_set == 'html': | |
287 TestStep("dart2js", mode, system, 'dart2js', runtime, ['html'], flags) | |
288 elif test_set == 'all': | |
289 TestStep("dart2js", mode, system, 'dart2js', runtime, ['dartc', | |
290 'samples', 'standalone', 'corelib', 'co19', 'language', 'isolate', | |
291 'vm', 'json', 'benchmark_smoke', 'dartdoc', 'utils', 'pub', 'lib'], | |
292 flags) | |
293 extras = ['dart2js_extra', 'dart2js_native', 'dart2js_foreign'] | |
294 TestStep("dart2js_extra", mode, system, 'dart2js', runtime, extras, | |
295 flags) | |
296 | |
297 return 0 | |
298 | |
299 def _DeleteTempWebdriverProfiles(directory): | |
300 """Find all the firefox profiles in a particular directory and delete them.""" | |
301 for f in os.listdir(directory): | |
302 item = os.path.join(directory, f) | |
303 if os.path.isdir(item) and (f.startswith('tmp') or f.startswith('opera')): | |
304 subprocess.Popen('rm -rf %s' % item, shell=True) | |
305 | |
306 def CleanUpTemporaryFiles(system, browser): | |
307 """For some browser (selenium) tests, the browser creates a temporary profile | |
308 on each browser session start. On Windows, generally these files are | |
309 automatically deleted when all python processes complete. However, since our | |
310 buildbot slave script also runs on python, we never get the opportunity to | |
311 clear out the temp files, so we do so explicitly here. Our batch browser | |
312 testing will make this problem occur much less frequently, but will still | |
313 happen eventually unless we do this. | |
314 | |
315 This problem also occurs with batch tests in Firefox. For some reason selenium | |
316 automatically deletes the temporary profiles for Firefox for one browser, | |
317 but not multiple ones when we have many open batch tasks running. This | |
318 behavior has not been reproduced outside of the buildbots. | |
319 | |
320 Args: | |
321 - system: either 'linux', 'mac', or 'win7' | |
322 - browser: one of the browsers, see GetBuildInfo | |
323 """ | |
324 if system == 'win7': | |
325 shutil.rmtree('C:\\Users\\chrome-bot\\AppData\\Local\\Temp', | |
326 ignore_errors=True) | |
327 elif browser == 'ff' or 'opera': | |
328 # Note: the buildbots run as root, so we can do this without requiring a | |
329 # password. The command won't actually work on regular machines without | |
330 # root permissions. | |
331 _DeleteTempWebdriverProfiles('/tmp') | |
332 _DeleteTempWebdriverProfiles('/var/tmp') | |
333 | |
334 def ClobberBuilder(mode): | |
335 """ Clobber the builder before we do the build. | |
336 Args: | |
337 - mode: either 'debug' or 'release' | |
338 """ | |
339 cmd = [sys.executable, | |
340 './tools/clean_output_directory.py', | |
341 '--mode=' + mode] | |
342 print 'Clobbering %s' % (' '.join(cmd)) | |
343 return subprocess.call(cmd, env=NO_COLOR_ENV) | |
344 | |
345 def GetShouldClobber(): | |
346 return os.environ.get(BUILDER_CLOBBER) == "1" | |
347 | |
348 def GetHasHardCodedCheckedMode(build_info): | |
349 # TODO(ricow): We currently run checked mode tests on chrome on linux and | |
350 # on the slow (all) IE windows bots. This is a hack and we should use the | |
351 # normal sharding and checked splitting functionality when we get more | |
352 # vms for testing this. | |
353 if (build_info.system == 'linux' and build_info.runtime == 'chrome'): | |
354 return True | |
355 if (build_info.system == 'win7' and build_info.runtime == 'ie' and | |
356 build_info.test_set == 'all'): | |
357 return True | |
358 return False | |
359 | |
360 def main(): | |
361 if len(sys.argv) == 0: | |
362 print 'Script pathname not known, giving up.' | |
363 return 1 | |
364 | |
365 build_info = GetBuildInfo() | |
366 | |
367 # Print out the buildinfo for easy debugging. | |
368 build_info.PrintBuildInfo() | |
369 | |
370 if build_info.compiler is None: | |
371 return 1 | |
372 | |
373 if GetShouldClobber(): | |
374 print '@@@BUILD_STEP Clobber@@@' | |
375 status = ClobberBuilder(build_info.mode) | |
376 if status != 0: | |
377 print '@@@STEP_FAILURE@@@' | |
378 return status | |
379 | |
380 print '@@@BUILD_STEP build sdk@@@' | |
381 status = BuildSDK(build_info.mode, build_info.system) | |
382 if status != 0: | |
383 print '@@@STEP_FAILURE@@@' | |
384 return status | |
385 | |
386 test_flags = [] | |
387 if build_info.shard_index: | |
388 test_flags = ['--shards=%s' % build_info.total_shards, | |
389 '--shard=%s' % build_info.shard_index] | |
390 | |
391 if build_info.checked: test_flags += ['--checked'] | |
392 | |
393 if build_info.host_checked: test_flags += ['--host-checked'] | |
394 | |
395 status = TestCompiler(build_info.runtime, build_info.mode, | |
396 build_info.system, list(test_flags), | |
397 build_info.is_buildbot, build_info.test_set) | |
398 | |
399 # See comment in GetHasHardCodedCheckedMode, this is a hack. | |
400 if (status == 0 and GetHasHardCodedCheckedMode(build_info)): | |
401 status = TestCompiler(build_info.runtime, build_info.mode, | |
402 build_info.system, | |
403 test_flags + ['--checked'], | |
404 build_info.is_buildbot, | |
405 build_info.test_set) | |
406 | |
407 if build_info.runtime != 'd8': CleanUpTemporaryFiles(build_info.system, | |
408 build_info.runtime) | |
409 if status != 0: print '@@@STEP_FAILURE@@@' | |
410 return status | |
411 | |
412 if __name__ == '__main__': | |
413 sys.exit(main()) | |
OLD | NEW |