Upstream version 8.36.161.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / buildbot / cbuildbot_config_unittest.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2012 The Chromium OS 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 """Unittests for config.  Needs to be run inside of chroot for mox."""
8
9 import mock
10 import cPickle
11 import sys
12
13 import constants
14 sys.path.insert(0, constants.SOURCE_ROOT)
15 from chromite.buildbot import cbuildbot_config
16 from chromite.lib import cros_test_lib
17 from chromite.lib import git
18 from chromite.lib import parallel
19
20 CHROMIUM_WATCHING_URL = ('http://src.chromium.org/chrome/trunk/tools/build/'
21     'masters/master.chromium.chromiumos/master_chromiumos_cros_cfg.py')
22
23
24 # pylint: disable=W0212,R0904
25 class ConfigPickleTest(cros_test_lib.TestCase):
26   """Test that a config object is pickleable."""
27
28   def testPickle(self):
29     bc1 = cbuildbot_config.config['x86-mario-paladin']
30     bc2 = cPickle.loads(cPickle.dumps(bc1))
31
32     self.assertEquals(bc1.boards, bc2.boards)
33     self.assertEquals(bc1.name, bc2.name)
34
35
36 class ConfigClassTest(cros_test_lib.TestCase):
37   """Tests of the config class itself."""
38
39   def testValueAccess(self):
40     cfg = cbuildbot_config.config['x86-mario-paladin']
41
42     self.assertTrue(cfg.name)
43     self.assertEqual(cfg.name, cfg['name'])
44
45     self.assertRaises(AttributeError, getattr, cfg, 'foobar')
46
47
48 class CBuildBotTest(cros_test_lib.MoxTestCase):
49   """General tests of cbuildbot_config with respect to cbuildbot."""
50
51   def testConfigsKeysMismatch(self):
52     """Verify that all configs contain exactly the default keys.
53
54     This checks for mispelled keys, or keys that are somehow removed.
55     """
56     expected_keys = set(cbuildbot_config._default.keys())
57     for build_name, config in cbuildbot_config.config.iteritems():
58       config_keys = set(config.keys())
59
60       extra_keys = config_keys.difference(expected_keys)
61       self.assertFalse(extra_keys, ('Config %s has extra values %s' %
62                                     (build_name, list(extra_keys))))
63
64       missing_keys = expected_keys.difference(config_keys)
65       self.assertFalse(missing_keys, ('Config %s is missing values %s' %
66                                       (build_name, list(missing_keys))))
67
68   def testConfigsHaveName(self):
69     """Configs must have names set."""
70     for build_name, config in cbuildbot_config.config.iteritems():
71       self.assertTrue(build_name == config['name'])
72
73   def testConfigUseflags(self):
74     """Useflags must be lists.
75
76     Strings are interpreted as arrays of characters for this, which is not
77     useful.
78     """
79     for build_name, config in cbuildbot_config.config.iteritems():
80       useflags = config.get('useflags')
81       if not useflags is None:
82         self.assertTrue(
83           isinstance(useflags, list),
84           'Config %s: useflags should be a list.' % build_name)
85
86   def testBoards(self):
87     """Verify 'boards' is explicitly set for every config."""
88     for build_name, config in cbuildbot_config.config.iteritems():
89       self.assertTrue(isinstance(config['boards'], (tuple, list)),
90                       "Config %s doesn't have a list of boards." % build_name)
91       self.assertEqual(len(set(config['boards'])), len(config['boards']),
92                        'Config %s has duplicate boards.' % build_name)
93       self.assertTrue(config['boards'] is not None,
94                       'Config %s defines a list of boards.' % build_name)
95
96   def testOverlaySettings(self):
97     """Verify overlays and push_overlays have legal values."""
98     for build_name, config in cbuildbot_config.config.iteritems():
99       overlays = config['overlays']
100       push_overlays = config['push_overlays']
101
102       self.assertTrue(overlays in [None, 'public', 'private', 'both'],
103                       'Config %s: has unexpected overlays value.' % build_name)
104       self.assertTrue(
105           push_overlays in [None, 'public', 'private', 'both'],
106           'Config %s: has unexpected push_overlays value.' % build_name)
107
108       if overlays == None:
109         subset = [None]
110       elif overlays == 'public':
111         subset = [None, 'public']
112       elif overlays == 'private':
113         subset = [None, 'private']
114       elif overlays == 'both':
115         subset = [None, 'public', 'private', 'both']
116
117       self.assertTrue(
118           push_overlays in subset,
119           'Config %s: push_overlays should be a subset of overlays.' %
120               build_name)
121
122   def testOverlayMaster(self):
123     """Verify that only one master is pushing uprevs for each overlay."""
124     masters = {}
125     for build_name, config in cbuildbot_config.config.iteritems():
126       overlays = config['overlays']
127       push_overlays = config['push_overlays']
128       if (overlays and push_overlays and config['uprev'] and config['master']
129           and not config['branch']):
130         other_master = masters.get(push_overlays)
131         err_msg = 'Found two masters for push_overlays=%s: %s and %s'
132         self.assertFalse(other_master,
133             err_msg % (push_overlays, build_name, other_master))
134         masters[push_overlays] = build_name
135
136     if 'both' in masters:
137       self.assertEquals(len(masters), 1, 'Found too many masters.')
138
139   def testChromeRev(self):
140     """Verify chrome_rev has an expected value"""
141     for build_name, config in cbuildbot_config.config.iteritems():
142       self.assertTrue(
143           config['chrome_rev'] in constants.VALID_CHROME_REVISIONS + [None],
144           'Config %s: has unexpected chrome_rev value.' % build_name)
145       self.assertFalse(
146           config['chrome_rev'] == constants.CHROME_REV_LOCAL,
147           'Config %s: has unexpected chrome_rev_local value.' % build_name)
148       if config['chrome_rev']:
149         self.assertTrue(cbuildbot_config.IsPFQType(config['build_type']),
150           'Config %s: has chrome_rev but is not a PFQ.' % build_name)
151
152   def testValidVMTestType(self):
153     """Verify vm_tests has an expected value"""
154     for build_name, config in cbuildbot_config.config.iteritems():
155       for test_type in config['vm_tests']:
156         self.assertTrue(
157           test_type in constants.VALID_VM_TEST_TYPES,
158           'Config %s: has unexpected vm test type value.' % build_name)
159
160   def testBuildType(self):
161     """Verifies that all configs use valid build types."""
162     for build_name, config in cbuildbot_config.config.iteritems():
163       self.assertTrue(
164           config['build_type'] in constants.VALID_BUILD_TYPES,
165           'Config %s: has unexpected build_type value.' % build_name)
166
167   def testGCCGitHash(self):
168     """Verifies that gcc_githash is not set without setting latest_toolchain."""
169     for build_name, config in cbuildbot_config.config.iteritems():
170       if config['gcc_githash']:
171         self.assertTrue(
172             config['latest_toolchain'],
173             'Config %s: has gcc_githash but not latest_toolchain.' % build_name)
174
175   def testBuildToRun(self):
176     """Verify we don't try to run tests without building them."""
177     for build_name, config in cbuildbot_config.config.iteritems():
178       self.assertFalse(
179           isinstance(config['useflags'], list) and
180           '-build_tests' in config['useflags'] and config['vm_tests'],
181           'Config %s: has vm_tests and use -build_tests.' % build_name)
182
183   def testSyncToChromeSdk(self):
184     """Verify none of the configs build chrome sdk but don't sync chrome."""
185     for build_name, config in cbuildbot_config.config.iteritems():
186       if config['sync_chrome'] is not None and not config['sync_chrome']:
187         self.assertFalse(
188             config['chrome_sdk'],
189             'Config %s: has chrome_sdk but not sync_chrome.' % build_name)
190
191   def testARMNoVMTest(self):
192     """Verify ARM builds don't get VMTests turned on by accident."""
193     for build_name, config in cbuildbot_config.config.iteritems():
194       if build_name.startswith('arm-') or config['arm']:
195         self.assertTrue(not config['vm_tests'],
196                         "ARM builder %s can't run vm tests!" % build_name)
197
198   def testHWTestsIFFArchivingHWTestArtifacts(self):
199     """Make sure all configs upload artifacts that need them for hw testing."""
200     for build_name, config in cbuildbot_config.config.iteritems():
201       if config['hw_tests']:
202         self.assertTrue(
203             config['upload_hw_test_artifacts'],
204             "%s is trying to run hw tests without uploading payloads." %
205             build_name)
206
207   def testHWTestTimeout(self):
208     """Verify that hw test timeout is in a reasonable range."""
209     # The parallel library will kill the process if it's silent for longer
210     # than the silent timeout.
211     max_timeout = parallel._BackgroundTask.SILENT_TIMEOUT
212     for build_name, config in cbuildbot_config.config.iteritems():
213       for test_config in config['hw_tests']:
214         self.assertTrue(test_config.timeout < max_timeout,
215             '%s has a hw_tests_timeout of %s that is too large. Expected %s' %
216             (build_name, test_config.timeout, max_timeout))
217
218   def testValidUnifiedMasterConfig(self):
219     """Make sure any unified master configurations are valid."""
220     for build_name, config in cbuildbot_config.config.iteritems():
221       error = 'Unified config for %s has invalid values' % build_name
222       # Unified masters must be internal and must rev both overlays.
223       if config['master']:
224         self.assertTrue(
225             config['internal'] and config['manifest_version'], error)
226       elif not config['master'] and config['manifest_version']:
227         # Unified slaves can rev either public or both depending on whether
228         # they are internal or not.
229         if not config['internal']:
230           self.assertEqual(config['overlays'], constants.PUBLIC_OVERLAYS, error)
231         elif cbuildbot_config.IsCQType(config['build_type']):
232           self.assertEqual(config['overlays'], constants.BOTH_OVERLAYS, error)
233
234   def testGetSlaves(self):
235     """Make sure every master has a sane list of slaves"""
236     for build_name, config in cbuildbot_config.config.iteritems():
237       if config['master']:
238         configs = cbuildbot_config.GetSlavesForMaster(config)
239         self.assertEqual(
240             len(map(repr, configs)), len(set(map(repr, configs))),
241             'Duplicate board in slaves of %s will cause upload prebuilts'
242             ' failures' % build_name)
243
244   def testFactoryFirmwareValidity(self):
245     """Ensures that firmware/factory branches have at least 1 valid name."""
246     tracking_branch = git.GetChromiteTrackingBranch()
247     for branch in ['firmware', 'factory']:
248       if tracking_branch.startswith(branch):
249         saw_config_for_branch = False
250         for build_name in cbuildbot_config.config:
251           if build_name.endswith('-%s' % branch):
252             self.assertFalse('release' in build_name,
253                              'Factory|Firmware release builders should not '
254                              'contain release in their name.')
255             saw_config_for_branch = True
256
257         self.assertTrue(
258             saw_config_for_branch, 'No config found for %s branch. '
259             'As this is the %s branch, all release configs that are being used '
260             'must end in %s.' % (branch, tracking_branch, branch))
261
262   def testBuildTests(self):
263     """Verify that we don't try to use tests without building them."""
264
265     for build_name, config in cbuildbot_config.config.iteritems():
266       if not config['build_tests']:
267         for flag in ('factory_toolkit', 'vm_tests', 'hw_tests'):
268           self.assertFalse(config[flag],
269               'Config %s set %s without build_tests.' % (build_name, flag))
270
271   def testPGOInBackground(self):
272     """Verify that we don't try to build or use PGO data in the background."""
273     for build_name, config in cbuildbot_config.config.iteritems():
274       if config.build_packages_in_background:
275         # It is unsupported to use the build_packages_in_background flags with
276         # the pgo_generate or pgo_use config options.
277         msg = 'Config %s uses build_packages_in_background with pgo_%s'
278         self.assertFalse(config.pgo_generate, msg % (build_name, 'generate'))
279         self.assertFalse(config.pgo_use, msg % (build_name, 'use'))
280
281   def testReleaseGroupInBackground(self):
282     """Verify build_packages_in_background settings for release groups.
283
284     For each release group, the first builder should be set to run in the
285     foreground (to build binary packages), and the remainder of the builders
286     should be set to run in parallel (to install the binary packages.)
287     """
288     for build_name, config in cbuildbot_config.config.iteritems():
289       if build_name.endswith('-release-group'):
290         msg = 'Config %s should not build_packages_in_background'
291         self.assertFalse(config.build_packages_in_background, msg % build_name)
292
293         self.assertTrue(config.child_configs,
294             'Config %s should have child configs' % build_name)
295         first_config = config.child_configs[0]
296         msg = 'Primary config for %s should not build_packages_in_background'
297         self.assertFalse(first_config.build_packages_in_background,
298             msg % build_name)
299
300         msg = 'Child config %s for %s should build_packages_in_background'
301         for child_config in config.child_configs[1:]:
302           self.assertTrue(child_config.build_packages_in_background,
303               msg % (child_config.name, build_name))
304
305   def testNoGrandChildConfigs(self):
306     """Verify that no child configs have a child config."""
307     for build_name, config in cbuildbot_config.config.iteritems():
308       for child_config in config.child_configs:
309         for grandchild_config in child_config.child_configs:
310           self.fail('Config %s has grandchild %s' % (build_name,
311                                                      grandchild_config.name))
312
313   def testUseChromeLKGMImpliesInternal(self):
314     """Currently use_chrome_lkgm refers only to internal manifests."""
315     for build_name, config in cbuildbot_config.config.iteritems():
316       if config['use_chrome_lkgm']:
317         self.assertTrue(config['internal'],
318             'Chrome lkgm currently only works with an internal manifest: %s' % (
319                 build_name,))
320
321   def testChromePFQsNeedChromeOSPFQs(self):
322     """Make sure every Chrome PFQ has a matching ChromeOS PFQ for prebuilts.
323
324     See http://crosbug.com/31695 for details on why.
325
326     Note: This check isn't perfect (as we can't check to see if we have
327     any ChromeOS PFQs actually running), but it at least makes sure people
328     realize we need to have the configs in sync.
329     """
330
331     # Figure out all the boards our Chrome and ChromeOS PFQs are using.
332     cr_pfqs = set()
333     cros_pfqs = set()
334     for config in cbuildbot_config.config.itervalues():
335       if config['build_type'] == constants.CHROME_PFQ_TYPE:
336         cr_pfqs.update(config['boards'])
337       elif config['build_type'] == constants.PALADIN_TYPE:
338         cros_pfqs.update(config['boards'])
339
340     # Then make sure the ChromeOS PFQ set is a superset of the Chrome PFQ set.
341     missing_pfqs = cr_pfqs.difference(cros_pfqs)
342     if missing_pfqs:
343       self.fail('Chrome PFQs are using boards that are missing ChromeOS PFQs:'
344                 '\n\t' + ' '.join(missing_pfqs))
345
346   def testNonOverlappingConfigTypes(self):
347     """Test that a config can only match one build suffix."""
348     for config_type in cbuildbot_config.CONFIG_TYPE_DUMP_ORDER:
349       # A longer config_type should never end with a shorter suffix.
350       my_list = list(cbuildbot_config.CONFIG_TYPE_DUMP_ORDER)
351       my_list.remove(config_type)
352       self.assertEquals(
353           cbuildbot_config.GetDisplayPosition(
354               config_type, type_order=my_list),
355           len(my_list))
356
357   def testCorrectConfigTypeIndex(self):
358     """Test that the correct build suffix index is returned."""
359     type_order = (
360         'type1',
361         'donkey-type2',
362         'kong-type3')
363
364     for index, config_type in enumerate(type_order):
365       config = '-'.join(['pre-fix', config_type])
366       self.assertEquals(
367           cbuildbot_config.GetDisplayPosition(
368               config, type_order=type_order),
369           index)
370
371     # Verify suffix needs to match up to a '-'.
372     self.assertEquals(
373         cbuildbot_config.GetDisplayPosition(
374             'pre-fix-sometype1', type_order=type_order),
375         len(type_order))
376
377   def testConfigTypesComplete(self):
378     """Verify CONFIG_TYPE_DUMP_ORDER contains all valid config types."""
379     for config_name in cbuildbot_config.config:
380       self.assertNotEqual(
381           cbuildbot_config.GetDisplayPosition(config_name),
382           len(cbuildbot_config.CONFIG_TYPE_DUMP_ORDER),
383           '%s did not match any types in %s' %
384           (config_name, 'cbuildbot_config.CONFIG_TYPE_DUMP_ORDER'))
385
386   def testCantBeBothTypesOfLKGM(self):
387     """Using lkgm and chrome_lkgm doesn't make sense."""
388     for config in cbuildbot_config.config.values():
389       self.assertFalse(config['use_lkgm'] and config['use_chrome_lkgm'])
390
391   def testNoDuplicateSlavePrebuilts(self):
392     """Test that no two same-board paladin slaves upload prebuilts."""
393     for cfg in cbuildbot_config.config.values():
394       if (cfg['build_type'] == constants.PALADIN_TYPE and cfg['master']):
395         slaves = cbuildbot_config.GetSlavesForMaster(cfg)
396         prebuilt_slaves = [s for s in slaves if s['prebuilts']]
397         # Dictionary from board name to builder name that uploads prebuilt
398         prebuilt_slave_boards = {}
399         for slave in prebuilt_slaves:
400           for board in slave['boards']:
401             self.assertFalse(prebuilt_slave_boards.has_key(board),
402                              'Configs %s and %s both upload prebuilts for '
403                              'board %s.' % (prebuilt_slave_boards.get(board),
404                                             slave['name'],
405                                             board))
406             prebuilt_slave_boards[board] = slave['name']
407
408   def testCantBeBothTypesOfPGO(self):
409     """Using pgo_generate and pgo_use together doesn't work."""
410     for config in cbuildbot_config.config.values():
411       self.assertFalse(config['pgo_use'] and config['pgo_generate'])
412
413   def testValidPrebuilts(self):
414     """Verify all builders have valid prebuilt values."""
415     for build_name, config in cbuildbot_config.config.iteritems():
416       msg = 'Config %s: has unexpected prebuilts value.' % build_name
417       valid_values = (False, constants.PRIVATE, constants.PUBLIC)
418       self.assertTrue(config['prebuilts'] in valid_values, msg)
419
420   def testInternalPrebuilts(self):
421     for build_name, config in cbuildbot_config.config.iteritems():
422       if (config['internal'] and
423           config['build_type'] != constants.CHROME_PFQ_TYPE):
424         msg = 'Config %s is internal but has public prebuilts.' % build_name
425         self.assertNotEqual(config['prebuilts'], constants.PUBLIC, msg)
426
427   def testValidHWTestPriority(self):
428     """Verify that hw test priority is valid."""
429     for build_name, config in cbuildbot_config.config.iteritems():
430       for test_config in config['hw_tests']:
431         self.assertTrue(
432             test_config.priority in constants.HWTEST_VALID_PRIORITIES,
433             '%s has an invalid hwtest priority.' % build_name)
434
435   def testPushImageSignerResultsPaygenDependancies(self):
436     """Paygen requires SignerResults which requires PushImage."""
437     for build_name, config in cbuildbot_config.config.iteritems():
438
439       # signer_results can't complete without push_image.
440       if config['signer_results']:
441         self.assertTrue(config['push_image'],
442                         '%s has signer_results without push_image' % build_name)
443
444       # paygen can't complete without signer_results, except for payloads
445       # where --channel arguments meet the requirements.
446       if config['paygen']:
447         self.assertTrue(config['signer_results'] or
448                         config['build_type'] == constants.PAYLOADS_TYPE,
449                         '%s has paygen without signer_results' % build_name)
450
451
452 class FindFullTest(cros_test_lib.TestCase):
453   """Test locating of official build for a board."""
454
455   def _RunTest(self, board, external_expected=None, internal_expected=None):
456     def check_expected(l, expected):
457       if expected is not None:
458         self.assertTrue(expected in [v['name'] for v in l])
459
460     external, internal = cbuildbot_config.FindFullConfigsForBoard(board)
461     self.assertFalse(
462         all(v is None for v in [external_expected, internal_expected]))
463     check_expected(external, external_expected)
464     check_expected(internal, internal_expected)
465
466   def _CheckCanonicalConfig(self, board, ending):
467     self.assertEquals(
468         '-'.join((board, ending)),
469         cbuildbot_config.FindCanonicalConfigForBoard(board)['name'])
470
471   def testExternal(self):
472     """Test finding of a full builder."""
473     self._RunTest('amd64-generic', external_expected='amd64-generic-full')
474
475   def testInternal(self):
476     """Test finding of a release builder."""
477     self._RunTest('lumpy', internal_expected='lumpy-release')
478
479   def testBoth(self):
480     """Both an external and internal config exist for board."""
481     self._RunTest('daisy', external_expected='daisy-full',
482                   internal_expected='daisy-release')
483
484   def testExternalCanonicalResolution(self):
485     """Test an external canonical config."""
486     self._CheckCanonicalConfig('x86-generic', 'full')
487
488   def testInternalCanonicalResolution(self):
489     """Test prefer internal over external when both exist."""
490     self._CheckCanonicalConfig('daisy', 'release')
491
492   def testPGOCanonicalResolution(self):
493     """Test prefer non-PGO over PGO builder."""
494     self._CheckCanonicalConfig('lumpy', 'release')
495
496   def testOneFullConfigPerBoard(self):
497     """There is at most one 'full' config for a board."""
498     # Verifies that there is one external 'full' and one internal 'release'
499     # build per board.  This is to ensure that we fail any new configs that
500     # wrongly have names like *-bla-release or *-bla-full. This case can also
501     # be caught if the new suffix was added to
502     # cbuildbot_config.CONFIG_TYPE_DUMP_ORDER
503     # (see testNonOverlappingConfigTypes), but that's not guaranteed to happen.
504     def AtMostOneConfig(board, label, configs):
505       if len(configs) > 1:
506         self.fail(
507             'Found more than one %s config for %s: %r'
508             % (label, board, [c['name'] for c in configs]))
509
510     boards = set()
511     for config in cbuildbot_config.config.itervalues():
512       boards.update(config['boards'])
513     # Sanity check of the boards.
514     assert boards
515
516     for b in boards:
517       external, internal = cbuildbot_config.FindFullConfigsForBoard(b)
518       AtMostOneConfig(b, 'external', external)
519       AtMostOneConfig(b, 'internal', internal)
520
521
522 class OverrideForTrybotTest(cros_test_lib.TestCase):
523   """Test config override functionality."""
524
525   def _testWithOptions(self, **kwargs):
526     mock_options = mock.Mock()
527     for k, v in kwargs.iteritems():
528       mock_options.setattr(k, v)
529
530     for config in cbuildbot_config.config.itervalues():
531       cbuildbot_config.OverrideConfigForTrybot(config, mock_options)
532
533   def testLocalTrybot(self):
534     """Override each config for local trybot."""
535     self._testWithOptions(remote_trybot=False, hw_test=False)
536
537   def testRemoteTrybot(self):
538     """Override each config for remote trybot."""
539     self._testWithOptions(remote_trybot=True, hw_test=False)
540
541   def testRemoteHWTest(self):
542     """Override each config for remote trybot + hwtests."""
543     self._testWithOptions(remote_trybot=True, hw_test=True)
544
545   def testChromeInternalOverride(self):
546     """Verify that we are not using official Chrome for local trybots."""
547     mock_options = mock.Mock()
548     mock_options.remote_trybot = False
549     mock_options.hw_test = False
550     old = cbuildbot_config.config['x86-mario-paladin']
551     new = cbuildbot_config.OverrideConfigForTrybot(old, mock_options)
552     self.assertTrue(constants.USE_CHROME_INTERNAL in old['useflags'])
553     self.assertTrue(constants.USE_CHROME_PDF in old['useflags'])
554     self.assertFalse(constants.USE_CHROME_INTERNAL in new['useflags'])
555     self.assertFalse(constants.USE_CHROME_PDF in new['useflags'])
556
557   def testVmTestOverride(self):
558     """Verify that vm_tests override for trybots pay heed to original config."""
559     mock_options = mock.Mock()
560     old = cbuildbot_config.config['x86-mario-paladin']
561     new = cbuildbot_config.OverrideConfigForTrybot(old, mock_options)
562     self.assertEquals(new['vm_tests'], [constants.SIMPLE_AU_TEST_TYPE,
563                                         constants.CROS_VM_TEST_TYPE])
564     old['vm_tests'] = None
565     new = cbuildbot_config.OverrideConfigForTrybot(old, mock_options)
566     self.assertIsNone(new['vm_tests'])
567
568
569 if __name__ == '__main__':
570   cros_test_lib.main()