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

Side by Side Diff: scripts/slave/recipe_modules/chromium_tests/api.py

Issue 1574433004: Allow a single trybot to mirror multiple waterfall bots. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@bot-config-and-test-db
Patch Set: Created 4 years, 11 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
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import ast 5 import ast
6 import collections 6 import collections
7 import contextlib 7 import contextlib
8 import copy 8 import copy
9 import itertools 9 import itertools
10 import json 10 import json
11 11
12 from recipe_engine.types import freeze 12 from recipe_engine.types import freeze
13 from recipe_engine import recipe_api 13 from recipe_engine import recipe_api
14 from recipe_engine import util as recipe_util 14 from recipe_engine import util as recipe_util
15 15
16 from . import bot_config_and_test_db as bdb_module 16 from . import bot_config_and_test_db as bdb_module
17 from . import builders 17 from . import builders
18 from . import steps 18 from . import steps
19 from . import trybots 19 from . import trybots
20 20
21
Sergey Berezin 2016/01/12 01:44:23 nit: please keep 2 blank lines: we separate most t
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 Sorry, that was an accident.
22 MB_CONFIG_FILENAME = ['tools', 'mb', 'mb_config.pyl'] 21 MB_CONFIG_FILENAME = ['tools', 'mb', 'mb_config.pyl']
23 22
24 23
25 # Paths which affect recipe config and behavior in a way that survives 24 # Paths which affect recipe config and behavior in a way that survives
26 # deapplying user's patch. 25 # deapplying user's patch.
27 RECIPE_CONFIG_PATHS = [ 26 RECIPE_CONFIG_PATHS = [
28 'testing/buildbot', 27 'testing/buildbot',
29 ] 28 ]
30 29
31 30
(...skipping 30 matching lines...) Expand all
62 return steps 61 return steps
63 62
64 @property 63 @property
65 def trybots(self): 64 def trybots(self):
66 return trybots.TRYBOTS 65 return trybots.TRYBOTS
67 66
68 def add_builders(self, builders): 67 def add_builders(self, builders):
69 """Adds builders to our builder map""" 68 """Adds builders to our builder map"""
70 self._builders.update(builders) 69 self._builders.update(builders)
71 70
71 def _get_mutable_bot_config(self, mastername, buildername):
72 master_dict = self.builders.get(mastername, {})
73 return master_dict.get('builders', {}).get(buildername)
74
72 def _get_bot_config(self, mastername, buildername): 75 def _get_bot_config(self, mastername, buildername):
73 master_dict = self.builders.get(mastername, {}) 76 return freeze(self._get_mutable_bot_config(mastername, buildername))
74 return freeze(master_dict.get('builders', {}).get(buildername))
75 77
76 def configure_build(self, mastername, buildername, override_bot_type=None): 78 def _get_bot_configs(self, bot_descs):
77 master_dict = self.builders.get(mastername, {}) 79 bot_configs = []
78 bot_config = master_dict.get('builders', {}).get(buildername) 80 for bot_desc in bot_descs:
81 bot_configs.append(self._get_mutable_bot_config(bot_desc['mastername'],
82 bot_desc['buildername']))
83 return bot_configs
84
85 def normalize_bot_descs(self, bot_descs):
Paweł Hajdan Jr. 2016/01/11 18:41:11 Why do we need this? It's not a very strong prefer
Ken Russell (switch to Gerrit) 2016/01/11 22:54:01 chromium_trybot.py fetches the entry for a tryserv
Sergey Berezin 2016/01/12 01:44:23 This is per my request in the original CL to keep
86 """Gets the bot_descs into a uniform format. It should be an array of
87 dictionary-like objects containing the properties 'mastername' and
88 'buildername'.
89 """
90 if not isinstance(bot_descs, (list, tuple)):
91 return [bot_descs]
92 return bot_descs
93
94 def _verify_property_is_same_across_bot_configs(self, bot_configs, property):
95 if len(bot_configs) < 2:
96 return
97 # This code path is only taken for trybots that mirror more than
98 # one waterfall bot. Verify that all of the other builders this
99 # trybot is supposed to mirror all use the same key properties
100 # like chromium_config and gclient_config. Otherwise, unexpected
101 # results are likely.
102 value = bot_configs[0].get(property)
103 for bot_config in bot_configs[1:]:
104 if value != bot_config.get(property):
105 # TODO(kbr): add a test configuration which triggers this error.
106 # Unclear how difficult it will be.
107 raise ValueError(
108 '%s was different between two bots a trybot is mirroring (%s vs %s)' %
109 (property, value, bot_config.get(value))) # pragma: no cover
110
111 def create_bot_desc(self, mastername, buildername):
112 """Creates a valid "bot_desc" argument for the various APIs below from
113 the given mastername and buildername. A dictionary containing the
114 properties "mastername" and "buildername" is a suitable argument.
115 An array of such dictionaries is also suitable, in which case tests
116 will be pulled from multiple testers.
Sergey Berezin 2016/01/12 01:44:23 The docstring doesnt' make sense to me. It appears
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 That's true. I should have documented the "bot_des
117 """
118 return {'mastername': mastername, 'buildername': buildername}
119
120 def configure_build(self, bot_desc, override_bot_type=None):
121 bot_desc = self.normalize_bot_descs(bot_desc)
122 bot_configs = self._get_bot_configs(bot_desc)
79 123
80 # Get the buildspec version. It can be supplied as a build property or as 124 # Get the buildspec version. It can be supplied as a build property or as
81 # a recipe config value. 125 # a recipe config value.
82 buildspec_version = (self.m.properties.get('buildspec_version') or 126 buildspec_version = (self.m.properties.get('buildspec_version') or
83 bot_config.get('buildspec_version')) 127 bot_configs[0].get('buildspec_version'))
Paweł Hajdan Jr. 2016/01/11 18:41:11 Shouldn't we check that 'buildspec_version' is con
Ken Russell (switch to Gerrit) 2016/01/11 22:54:01 Probably, or at least possibly. I didn't know what
128
129 self._verify_property_is_same_across_bot_configs(bot_configs,
130 'chromium_config')
84 131
85 self.m.chromium.set_config( 132 self.m.chromium.set_config(
86 bot_config.get('chromium_config'), 133 bot_configs[0].get('chromium_config'),
87 **bot_config.get('chromium_config_kwargs', {})) 134 **bot_configs[0].get('chromium_config_kwargs', {}))
88 135
89 # Set GYP_DEFINES explicitly because chromium config constructor does 136 # Set GYP_DEFINES explicitly because chromium config constructor does
90 # not support that. 137 # not support that.
91 self.m.chromium.c.gyp_env.GYP_DEFINES.update( 138 for bot_config in bot_configs:
92 bot_config.get('GYP_DEFINES', {})) 139 self.m.chromium.c.gyp_env.GYP_DEFINES.update(
93 if bot_config.get('use_isolate'): 140 bot_config.get('GYP_DEFINES', {}))
94 self.m.isolate.set_isolate_environment(self.m.chromium.c) 141 if bot_config.get('use_isolate'):
142 self.m.isolate.set_isolate_environment(self.m.chromium.c)
143
144 self._verify_property_is_same_across_bot_configs(bot_configs,
145 'gclient_config')
95 146
96 self.m.gclient.set_config( 147 self.m.gclient.set_config(
97 bot_config.get('gclient_config'), 148 bot_configs[0].get('gclient_config'),
98 PATCH_PROJECT=self.m.properties.get('patch_project'), 149 PATCH_PROJECT=self.m.properties.get('patch_project'),
99 BUILDSPEC_VERSION=buildspec_version, 150 BUILDSPEC_VERSION=buildspec_version,
100 **bot_config.get('gclient_config_kwargs', {})) 151 **bot_configs[0].get('gclient_config_kwargs', {}))
Sergey Berezin 2016/01/12 01:44:23 Should *_kwargs properties also be checked for bei
Ken Russell (switch to Gerrit) 2016/01/12 05:35:50 Good point. Yes, I think so. (Is this property eve
101 152
102 if 'android_config' in bot_config: 153 if 'android_config' in bot_configs[0]:
Sergey Berezin 2016/01/12 01:44:23 nit: perhaps, more generally: if any('android_c
Ken Russell (switch to Gerrit) 2016/01/12 05:35:50 Good point.
103 self.m.chromium_android.configure_from_properties( 154 self.m.chromium_android.configure_from_properties(
104 bot_config['android_config'], 155 bot_configs[0]['android_config'],
105 **bot_config.get('chromium_config_kwargs', {})) 156 **bot_configs[0].get('chromium_config_kwargs', {}))
157 # This code path hasn't been generalized yet for a trybot that
Paweł Hajdan Jr. 2016/01/11 18:41:11 Why not a TODO? Also applies to other places which
Ken Russell (switch to Gerrit) 2016/01/11 22:54:01 Yes, these should have TODOs.
158 # mirrors multiple bots.
159 assert len(bot_configs) == 1
106 160
107 if 'amp_config' in bot_config: 161 if 'amp_config' in bot_configs[0]:
Sergey Berezin 2016/01/12 01:44:23 nit: same generalization as above.
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 Agree.
108 self.m.amp.set_config(bot_config['amp_config']) 162 self.m.amp.set_config(bot_configs[0]['amp_config'])
163 # This code path hasn't been generalized yet for a trybot that
164 # mirrors multiple bots.
165 assert len(bot_configs) == 1
109 166
110 for c in bot_config.get('chromium_apply_config', []): 167 chromium_applied_configs = set()
111 self.m.chromium.apply_config(c) 168 gclient_applied_configs = set()
112 169 for bot_config in bot_configs:
113 for c in bot_config.get('gclient_apply_config', []): 170 for c in bot_config.get('chromium_apply_config', []):
114 self.m.gclient.apply_config(c) 171 if not c in chromium_applied_configs:
172 self.m.chromium.apply_config(c)
173 chromium_applied_configs.add(c)
174 for c in bot_config.get('gclient_apply_config', []):
175 if not c in gclient_applied_configs:
176 self.m.gclient.apply_config(c)
177 gclient_applied_configs.add(c)
115 178
116 # WARNING: src-side runtest.py is only tested with chromium CQ builders. 179 # WARNING: src-side runtest.py is only tested with chromium CQ builders.
117 # Usage not covered by chromium CQ is not supported and can break 180 # Usage not covered by chromium CQ is not supported and can break
118 # without notice. 181 # without notice.
182 master_dict = self.builders.get(bot_desc[0]['mastername'], {})
Paweł Hajdan Jr. 2016/01/11 18:41:11 Why don't we also ensure consistency here?
Ken Russell (switch to Gerrit) 2016/01/11 22:54:01 No good reason not to. This query would be a littl
119 if master_dict.get('settings', {}).get('src_side_runtest_py'): 183 if master_dict.get('settings', {}).get('src_side_runtest_py'):
120 self.m.chromium.c.runtest_py.src_side = True 184 self.m.chromium.c.runtest_py.src_side = True
121 185
122 if bot_config.get('goma_canary'): 186 if bot_configs[0].get('goma_canary'):
Sergey Berezin 2016/01/12 01:44:23 nit: same generalization as above.
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 Acknowledged.
123 self.m.goma.update_goma_canary(buildername) 187 self.m.goma.update_goma_canary(bot_desc[0]['buildername'])
188 # This code path hasn't been generalized yet for a trybot that
189 # mirrors multiple bots.
190 assert len(bot_configs) == 1
124 191
125 bot_type = override_bot_type or bot_config.get('bot_type', 'builder_tester') 192 bot_type = override_bot_type or bot_configs[0].get('bot_type',
Sergey Berezin 2016/01/12 01:44:23 nit: move inside the 'if'. This var is only used i
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 Good point.
193 'builder_tester')
126 194
127 if bot_config.get('set_component_rev'): 195 if bot_configs[0].get('set_component_rev'):
Sergey Berezin 2016/01/12 01:44:23 nit: same generalization as above.
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 Acknowledged.
128 # If this is a component build and the main revision is e.g. blink, 196 # If this is a component build and the main revision is e.g. blink,
129 # webrtc, or v8, the custom deps revision of this component must be 197 # webrtc, or v8, the custom deps revision of this component must be
130 # dynamically set to either: 198 # dynamically set to either:
131 # (1) the revision of the builder if this is a tester, 199 # (1) the revision of the builder if this is a tester,
132 # (2) 'revision' from the waterfall, or 200 # (2) 'revision' from the waterfall, or
133 # (3) 'HEAD' for forced builds with unspecified 'revision'. 201 # (3) 'HEAD' for forced builds with unspecified 'revision'.
134 # TODO(machenbach): Use parent_got_cr_revision on testers with component 202 # TODO(machenbach): Use parent_got_cr_revision on testers with component
135 # builds to match also the chromium revision from the builder. 203 # builds to match also the chromium revision from the builder.
136 component_rev = self.m.properties.get('revision') or 'HEAD' 204 component_rev = self.m.properties.get('revision') or 'HEAD'
137 if bot_type == 'tester': 205 if bot_type == 'tester':
138 component_rev = self.m.properties.get( 206 component_rev = self.m.properties.get(
139 'parent_got_revision', component_rev) 207 'parent_got_revision', component_rev)
140 dep = bot_config.get('set_component_rev') 208 dep = bot_configs[0].get('set_component_rev')
141 self.m.gclient.c.revisions[dep['name']] = dep['rev_str'] % component_rev 209 self.m.gclient.c.revisions[dep['name']] = dep['rev_str'] % component_rev
210 # This code path hasn't been generalized yet for a trybot that
211 # mirrors multiple bots.
212 assert len(bot_configs) == 1
142 213
143 def ensure_checkout(self, mastername, buildername, 214 def ensure_checkout(self, mastername, buildername,
144 root_solution_revision=None): 215 root_solution_revision=None):
145 bot_config = self._get_bot_config(mastername, buildername) 216 bot_config = self._get_bot_config(mastername, buildername)
146 217
147 if self.m.platform.is_win: 218 if self.m.platform.is_win:
148 self.m.chromium.taskkill() 219 self.m.chromium.taskkill()
149 220
150 # Bot Update re-uses the gclient configs. 221 # Bot Update re-uses the gclient configs.
151 update_step = self.m.bot_update.ensure_checkout( 222 update_step = self.m.bot_update.ensure_checkout(
(...skipping 20 matching lines...) Expand all
172 if self.m.tryserver.is_tryserver: 243 if self.m.tryserver.is_tryserver:
173 try: 244 try:
174 self.m.chromium.runhooks(name='runhooks (with patch)') 245 self.m.chromium.runhooks(name='runhooks (with patch)')
175 except self.m.step.StepFailure: 246 except self.m.step.StepFailure:
176 # As part of deapplying patch we call runhooks without the patch. 247 # As part of deapplying patch we call runhooks without the patch.
177 self.deapply_patch(update_step) 248 self.deapply_patch(update_step)
178 raise 249 raise
179 else: 250 else:
180 self.m.chromium.runhooks() 251 self.m.chromium.runhooks()
181 252
182 def get_test_spec(self, mastername, buildername): 253 def get_test_spec(self, mastername, buildername,
254 annotate_master_name=False):
183 bot_config = self._get_bot_config(mastername, buildername) 255 bot_config = self._get_bot_config(mastername, buildername)
184 256
185 test_spec_file = bot_config.get('testing', {}).get( 257 test_spec_file = bot_config.get('testing', {}).get(
186 'test_spec_file', '%s.json' % mastername) 258 'test_spec_file', '%s.json' % mastername)
187 259
188 # TODO(phajdan.jr): Bots should have no generators instead. 260 # TODO(phajdan.jr): Bots should have no generators instead.
189 if bot_config.get('disable_tests'): 261 if bot_config.get('disable_tests'):
190 return {} 262 return {}
191 return self.read_test_spec(self.m, test_spec_file) 263 return self.read_test_spec(self.m, test_spec_file,
264 annotate_master_name=annotate_master_name,
265 master_name_for_annotation=mastername)
192 266
193 def _get_master_dict_with_dynamic_tests( 267 def _get_master_dict_with_dynamic_tests(
194 self, mastername, buildername, test_spec, scripts_compile_targets): 268 self, mastername, buildername, test_spec, scripts_compile_targets):
195 # We manually thaw the path to the elements we are modifying, since the 269 # We manually thaw the path to the elements we are modifying, since the
196 # builders are frozen. 270 # builders are frozen.
197 master_dict = dict(self.builders[mastername]) 271 master_dict = dict(self.builders[mastername])
198 builders = master_dict['builders'] = dict(master_dict['builders']) 272 builders = master_dict['builders'] = dict(master_dict['builders'])
199 bot_config = builders[buildername] 273 bot_config = builders[buildername]
200 for loop_buildername in builders: 274 for loop_buildername in builders:
201 builder_dict = builders[loop_buildername] = ( 275 builder_dict = builders[loop_buildername] = (
202 dict(builders[loop_buildername])) 276 dict(builders[loop_buildername]))
203 builders[loop_buildername]['tests'] = ( 277 builders[loop_buildername]['tests'] = (
204 self.generate_tests_from_test_spec( 278 self.generate_tests_from_test_spec(
205 self.m, test_spec, builder_dict, loop_buildername, mastername, 279 self.m, test_spec, builder_dict, loop_buildername, mastername,
206 # TODO(phajdan.jr): Get enable_swarming value from builder_dict. 280 # TODO(phajdan.jr): Get enable_swarming value from builder_dict.
207 # Above should remove the need to get bot_config and buildername 281 # Above should remove the need to get bot_config and buildername
208 # in this method. 282 # in this method.
209 bot_config.get('enable_swarming', False), 283 bot_config.get('enable_swarming', False),
210 scripts_compile_targets, builder_dict.get('test_generators', []) 284 scripts_compile_targets, builder_dict.get('test_generators', [])
211 )) 285 ))
212 286
213 return freeze(master_dict) 287 return freeze(master_dict)
214 288
215 def create_bot_db_from_master_dict(self, mastername, master_dict): 289 def create_bot_db_from_master_dict(self, mastername, master_dict):
216 bot_db = bdb_module.BotConfigAndTestDB() 290 bot_db = bdb_module.BotConfigAndTestDB()
217 bot_db._add_master_dict_and_test_spec(mastername, master_dict, {}) 291 bot_db._add_master_dict_and_test_spec(mastername, master_dict, {})
218 return bot_db 292 return bot_db
219 293
220 def prepare_checkout(self, mastername, buildername, 294 def prepare_checkout(self, bot_desc, root_solution_revision=None):
221 root_solution_revision=None): 295 bot_desc = self.normalize_bot_descs(bot_desc)
222 bot_config = self._get_bot_config(mastername, buildername) 296 bot_configs = self._get_bot_configs(bot_desc)
223 297
224 update_step = self.ensure_checkout(mastername, buildername, 298 # Use the first bot's mastername and buildername for several of
299 # the setup steps in this routine.
300 mastername = bot_desc[0]['mastername']
301 buildername = bot_desc[0]['buildername']
302
303 update_step = self.ensure_checkout(mastername,
304 buildername,
225 root_solution_revision) 305 root_solution_revision)
226 # TODO(robertocn): Remove this hack by the end of Q1/2016. 306 # TODO(robertocn): Remove this hack by the end of Q1/2016.
227 if (mastername == 'tryserver.chromium.perf' 307 if (mastername == 'tryserver.chromium.perf'
228 and bot_config.get('bot_type') == 'builder' 308 and bot_configs[0].get('bot_type') == 'builder'
Sergey Berezin 2016/01/12 01:44:23 nit: perhaps, also generalize as above?
Ken Russell (switch to Gerrit) 2016/01/12 05:35:50 Maybe. Since this is a hack it might be best to to
229 and buildername.endswith('builder')): 309 and buildername.endswith('builder')):
230 force_legacy_compile = self.should_force_legacy_compiling( 310 force_legacy_compile = self.should_force_legacy_compiling(
231 mastername, buildername) 311 mastername, buildername)
232 if force_legacy_compile: 312 if force_legacy_compile:
233 self.m.chromium.c.project_generator.tool = 'gyp' 313 self.m.chromium.c.project_generator.tool = 'gyp'
314 # This code path hasn't been generalized yet for a trybot that
315 # mirrors multiple bots.
316 assert len(bot_configs) == 1
234 317
235 self.set_up_swarming(mastername, buildername) 318 self.set_up_swarming(mastername, buildername)
236 self.runhooks(update_step) 319 self.runhooks(update_step)
237 320
238 test_spec = self.get_test_spec(mastername, buildername) 321 bot_db = bdb_module.BotConfigAndTestDB()
239 322
240 # TODO(phajdan.jr): Bots should have no generators instead. 323 scripts_compile_targets = {}
241 if bot_config.get('disable_tests'): 324 first_iteration = True
242 scripts_compile_targets = {}
243 else:
244 scripts_compile_targets = \
245 self.get_compile_targets_for_scripts().json.output
246 325
247 master_dict = self._get_master_dict_with_dynamic_tests( 326 for bot in bot_desc:
248 mastername, buildername, test_spec, scripts_compile_targets) 327 test_spec = self.get_test_spec(
328 bot['mastername'], bot['buildername'],
329 annotate_master_name=not first_iteration)
330
331 # This is done this way to reduce test expectation changes.
332 if first_iteration:
333 # TODO(phajdan.jr): Bots should have no generators instead.
334 if bot_configs[0].get('disable_tests'):
335 scripts_compile_targets = {}
336 else:
337 scripts_compile_targets = \
338 self.get_compile_targets_for_scripts().json.output
339 first_iteration = False
340
341 master_dict = self._get_master_dict_with_dynamic_tests(
342 bot['mastername'], bot['buildername'], test_spec,
343 scripts_compile_targets)
344 bot_db._add_master_dict_and_test_spec(
345 bot['mastername'], master_dict, test_spec)
249 346
250 if self.m.chromium.c.lto and \ 347 if self.m.chromium.c.lto and \
251 not self.m.chromium.c.env.LLVM_FORCE_HEAD_REVISION: 348 not self.m.chromium.c.env.LLVM_FORCE_HEAD_REVISION:
252 self.m.chromium.download_lto_plugin() 349 self.m.chromium.download_lto_plugin()
253 350
254 bot_db = bdb_module.BotConfigAndTestDB()
255 bot_db._add_master_dict_and_test_spec(mastername, master_dict, test_spec)
256
257 return update_step, bot_db 351 return update_step, bot_db
258 352
259 def generate_tests_from_test_spec(self, api, test_spec, builder_dict, 353 def generate_tests_from_test_spec(self, api, test_spec, builder_dict,
260 buildername, mastername, enable_swarming, scripts_compile_targets, 354 buildername, mastername, enable_swarming, scripts_compile_targets,
261 generators): 355 generators):
262 tests = builder_dict.get('tests', ()) 356 tests = builder_dict.get('tests', ())
263 # TODO(phajdan.jr): Switch everything to scripts generators and simplify. 357 # TODO(phajdan.jr): Switch everything to scripts generators and simplify.
264 for generator in generators: 358 for generator in generators:
265 tests = ( 359 tests = (
266 tuple(generator(api, mastername, buildername, test_spec, 360 tuple(generator(api, mastername, buildername, test_spec,
267 enable_swarming=enable_swarming, 361 enable_swarming=enable_swarming,
268 scripts_compile_targets=scripts_compile_targets)) + 362 scripts_compile_targets=scripts_compile_targets)) +
269 tests) 363 tests)
270 return tests 364 return tests
271 365
272 def read_test_spec(self, api, test_spec_file): 366 def read_test_spec(self, api, test_spec_file, annotate_master_name=False,
367 master_name_for_annotation=None):
273 test_spec_path = api.path['checkout'].join('testing', 'buildbot', 368 test_spec_path = api.path['checkout'].join('testing', 'buildbot',
274 test_spec_file) 369 test_spec_file)
370 step_name = 'read test spec'
371 if annotate_master_name:
372 step_name += ' (%s)' % master_name_for_annotation
275 test_spec_result = api.json.read( 373 test_spec_result = api.json.read(
276 'read test spec', 374 step_name,
277 test_spec_path, 375 test_spec_path,
278 step_test_data=lambda: api.json.test_api.output({})) 376 step_test_data=lambda: api.json.test_api.output({}))
279 test_spec_result.presentation.step_text = 'path: %s' % test_spec_path 377 test_spec_result.presentation.step_text = 'path: %s' % test_spec_path
280 test_spec = test_spec_result.json.output 378 test_spec = test_spec_result.json.output
281 379
282 return test_spec 380 return test_spec
283 381
284 def create_test_runner(self, api, tests, suffix=''): 382 def create_test_runner(self, api, tests, suffix=''):
285 """Creates a test runner to run a set of tests. 383 """Creates a test runner to run a set of tests.
286 384
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 raise 423 raise
326 424
327 if failed_tests: 425 if failed_tests:
328 failed_tests_names = [t.name for t in failed_tests] 426 failed_tests_names = [t.name for t in failed_tests]
329 raise self.m.step.StepFailure( 427 raise self.m.step.StepFailure(
330 '%d tests failed: %r' % (len(failed_tests), failed_tests_names)) 428 '%d tests failed: %r' % (len(failed_tests), failed_tests_names))
331 429
332 return test_runner 430 return test_runner
333 431
334 def get_compile_targets_and_tests( 432 def get_compile_targets_and_tests(
433 self, bot_desc, bot_db, override_bot_type=None, override_tests=None):
434 bot_desc = self.normalize_bot_descs(bot_desc)
435 """Returns a tuple: list of compile targets and list of tests.
436
437 The list of tests includes ones on the triggered testers."""
438 all_compile_targets = set()
439 all_tests = []
440 for bot in bot_desc:
441 compile_targets, tests = self._get_single_bot_compile_targets_and_tests(
442 bot['mastername'], bot['buildername'], bot_db,
443 override_bot_type=override_bot_type, override_tests=override_tests)
444 all_compile_targets.update(compile_targets)
445 # It doesn't seem feasible to de-duplicate tests in the case where
446 # a tryserver mirrors multiple waterfall bots. (This is the only
447 # case where there's more than one entry in bot_configs.) Just add
448 # them all in the order returned.
449 all_tests.extend(tests)
450 # The incoming 'override_tests' must be reset during the second
Paweł Hajdan Jr. 2016/01/11 18:41:11 Why don't we override tests at the end? The curre
Ken Russell (switch to Gerrit) 2016/01/11 22:54:01 Good point; that's probably the correct thing to d
451 # and subsequent loop iterations, or trybots which mirror multiple
452 # bots will add the same set of tests multiple times.
453 # TODO(kbr): it isn't clear to me what the correct behavior is here.
454 # What should happen with the second bot's 'tests' property?
455 override_tests = None
456
457 return sorted(all_compile_targets), all_tests
458
459 def _get_single_bot_compile_targets_and_tests(
335 self, mastername, buildername, bot_db, 460 self, mastername, buildername, bot_db,
336 override_bot_type=None, override_tests=None): 461 override_bot_type=None, override_tests=None):
337 """Returns a tuple: list of compile targets and list of tests. 462 """Returns a tuple: list of compile targets and list of tests.
338 463
339 The list of tests includes ones on the triggered testers.""" 464 The list of tests includes ones on the triggered testers."""
340 465
341 assert isinstance(bot_db, bdb_module.BotConfigAndTestDB), \ 466 assert isinstance(bot_db, bdb_module.BotConfigAndTestDB), \
342 "bot_db argument %r was not a BotConfigAndTestDB" % (bot_db) 467 "bot_db argument %r was not a BotConfigAndTestDB" % (bot_db)
343 bot_config = bot_db.get_bot_config(mastername, buildername) 468 bot_config = bot_db.get_bot_config(mastername, buildername)
344 bot_type = override_bot_type or bot_config.get('bot_type', 'builder_tester') 469 bot_type = override_bot_type or bot_config.get('bot_type', 'builder_tester')
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 try: 509 try:
385 command(lambda name: '%s (with patch)' % name) 510 command(lambda name: '%s (with patch)' % name)
386 except self.m.step.StepFailure: 511 except self.m.step.StepFailure:
387 self.deapply_patch(update_step) 512 self.deapply_patch(update_step)
388 command(lambda name: '%s (without patch)' % name) 513 command(lambda name: '%s (without patch)' % name)
389 raise 514 raise
390 else: 515 else:
391 command(lambda name: name) 516 command(lambda name: name)
392 517
393 518
394 def compile(self, mastername, buildername, update_step, bot_db, 519 def compile(self, bot_desc, update_step, bot_db,
395 mb_mastername=None, mb_buildername=None): 520 mb_mastername=None, mb_buildername=None):
396 """Runs compile and related steps for given builder.""" 521 """Runs compile and related steps for builder(s) described in bot_desc."""
522 bot_desc = self.normalize_bot_descs(bot_desc)
397 assert isinstance(bot_db, bdb_module.BotConfigAndTestDB), \ 523 assert isinstance(bot_db, bdb_module.BotConfigAndTestDB), \
398 "bot_db argument %r was not a BotConfigAndTestDB" % (bot_db) 524 "bot_db argument %r was not a BotConfigAndTestDB" % (bot_db)
399 compile_targets, tests_including_triggered = \ 525 compile_targets, tests_including_triggered = \
400 self.get_compile_targets_and_tests( 526 self.get_compile_targets_and_tests(
401 mastername, 527 bot_desc,
402 buildername,
403 bot_db) 528 bot_db)
404 self.compile_specific_targets( 529 self.compile_specific_targets(
405 mastername, buildername, update_step, bot_db, 530 bot_desc[0]['mastername'], bot_desc[0]['buildername'],
Sergey Berezin 2016/01/12 01:44:23 Shouldn't we compile for all bot_descs? E.g. this
Ken Russell (switch to Gerrit) 2016/01/12 05:35:51 At first glance it didn't seem necessary to genera
406 compile_targets, tests_including_triggered, 531 update_step, bot_db, compile_targets, tests_including_triggered,
407 mb_mastername=mb_mastername, mb_buildername=mb_buildername) 532 mb_mastername=mb_mastername, mb_buildername=mb_buildername)
408 533
409 def compile_specific_targets( 534 def compile_specific_targets(
410 self, mastername, buildername, update_step, bot_db, 535 self, mastername, buildername, update_step, bot_db,
411 compile_targets, tests_including_triggered, 536 compile_targets, tests_including_triggered,
412 mb_mastername=None, mb_buildername=None, override_bot_type=None): 537 mb_mastername=None, mb_buildername=None, override_bot_type=None):
413 """Runs compile and related steps for given builder. 538 """Runs compile and related steps for given builder.
414 539
415 Allows finer-grained control about exact compile targets used. 540 Allows finer-grained control about exact compile targets used.
416 541
(...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 result_text = 'MB is enabled for this builder at this revision.' 1018 result_text = 'MB is enabled for this builder at this revision.'
894 log_name = 'Builder MB-ready' 1019 log_name = 'Builder MB-ready'
895 self.m.step.active_result.presentation.logs[log_name] = [result_text] 1020 self.m.step.active_result.presentation.logs[log_name] = [result_text]
896 return False 1021 return False
897 except (self.m.step.StepFailure, KeyError): 1022 except (self.m.step.StepFailure, KeyError):
898 result_text = 'MB is not enabled for this builder at this revision.' 1023 result_text = 'MB is not enabled for this builder at this revision.'
899 log_name = 'Builder NOT MB-ready' 1024 log_name = 'Builder NOT MB-ready'
900 self.m.step.active_result.presentation.logs[log_name] = [result_text] 1025 self.m.step.active_result.presentation.logs[log_name] = [result_text]
901 self.m.step.active_result.presentation.status = self.m.step.WARNING 1026 self.m.step.active_result.presentation.status = self.m.step.WARNING
902 return True 1027 return True
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698