Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / cbuildbot / stages / artifact_stages_unittest.py
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Unittests for the artifact stages."""
7
8 from __future__ import print_function
9
10 import argparse
11 import mox
12 import os
13 import sys
14
15 sys.path.insert(0, os.path.abspath('%s/../../..' % os.path.dirname(__file__)))
16 from chromite.cbuildbot import commands
17 from chromite.cbuildbot import constants
18 from chromite.cbuildbot import failures_lib
19 from chromite.cbuildbot import prebuilts
20 from chromite.cbuildbot.cbuildbot_unittest import BuilderRunMock
21 from chromite.cbuildbot.stages import artifact_stages
22 from chromite.cbuildbot.stages import build_stages_unittest
23 from chromite.cbuildbot.stages import generic_stages_unittest
24 from chromite.lib import cros_build_lib
25 from chromite.lib import cros_build_lib_unittest
26 from chromite.lib import cros_test_lib
27 from chromite.lib import git
28 from chromite.lib import osutils
29 from chromite.lib import parallel
30 from chromite.lib import parallel_unittest
31 from chromite.lib import partial_mock
32
33 from chromite.cbuildbot.stages.generic_stages_unittest import patch
34 from chromite.cbuildbot.stages.generic_stages_unittest import patches
35
36
37 DEFAULT_CHROME_BRANCH = '27'
38
39
40 # pylint: disable=R0901,W0212
41 class ArchiveStageTest(generic_stages_unittest.AbstractStageTest):
42   """Exercise ArchiveStage functionality."""
43   RELEASE_TAG = ''
44   VERSION = '3333.1.0'
45
46   def _PatchDependencies(self):
47     """Patch dependencies of ArchiveStage.PerformStage()."""
48     to_patch = [
49         (parallel, 'RunParallelSteps'), (commands, 'PushImages'),
50         (commands, 'UploadArchivedFile')]
51     self.AutoPatch(to_patch)
52
53   def setUp(self):
54     self.StartPatcher(BuilderRunMock())
55     self._PatchDependencies()
56
57     self._Prepare()
58
59   def _Prepare(self, bot_id=None, **kwargs):
60     extra_config = {'upload_symbols': True, 'push_image': True}
61     super(ArchiveStageTest, self)._Prepare(bot_id, extra_config=extra_config,
62                                            **kwargs)
63
64   def ConstructStage(self):
65     self._run.GetArchive().SetupArchivePath()
66     return artifact_stages.ArchiveStage(self._run, self._current_board)
67
68   def testArchive(self):
69     """Simple did-it-run test."""
70     # TODO(davidjames): Test the individual archive steps as well.
71     self.RunStage()
72
73   # TODO(build): This test is not actually testing anything real.  It confirms
74   # that PushImages is not called, but the mock for RunParallelSteps already
75   # prevents PushImages from being called, regardless of whether this is a
76   # trybot flow.
77   def testNoPushImagesForRemoteTrybot(self):
78     """Test that remote trybot overrides work to disable push images."""
79     self._Prepare('x86-mario-release',
80                   cmd_args=['--remote-trybot', '-r', self.build_root,
81                             '--buildnumber=1234'])
82     self.RunStage()
83     # pylint: disable=E1101
84     self.assertEquals(commands.PushImages.call_count, 0)
85
86   def ConstructStageForArchiveStep(self):
87     """Stage construction for archive steps."""
88     stage = self.ConstructStage()
89     self.PatchObject(stage._upload_queue, 'put', autospec=True)
90     self.PatchObject(git, 'ReinterpretPathForChroot', return_value='',
91                      autospec=True)
92     return stage
93
94   def testBuildAndArchiveDeltaSysroot(self):
95     """Test tarball is added to upload queue."""
96     stage = self.ConstructStageForArchiveStep()
97     with cros_build_lib_unittest.RunCommandMock() as rc:
98       rc.SetDefaultCmdResult()
99       stage.BuildAndArchiveDeltaSysroot()
100     stage._upload_queue.put.assert_called_with([constants.DELTA_SYSROOT_TAR])
101
102   def testBuildAndArchiveDeltaSysrootFailure(self):
103     """Test tarball not added to upload queue on command exception."""
104     stage = self.ConstructStageForArchiveStep()
105     with cros_build_lib_unittest.RunCommandMock() as rc:
106       rc.AddCmdResult(partial_mock.In('generate_delta_sysroot'), returncode=1,
107                       error='generate_delta_sysroot: error')
108       self.assertRaises2(cros_build_lib.RunCommandError,
109                         stage.BuildAndArchiveDeltaSysroot)
110     self.assertFalse(stage._upload_queue.put.called)
111
112
113 class UploadPrebuiltsStageTest(
114     generic_stages_unittest.RunCommandAbstractStageTest):
115   """Tests for the UploadPrebuilts stage."""
116
117   CMD = './upload_prebuilts'
118   RELEASE_TAG = ''
119
120   def setUp(self):
121     self.StartPatcher(BuilderRunMock())
122
123   def _Prepare(self, bot_id=None, **kwargs):
124     super(UploadPrebuiltsStageTest, self)._Prepare(bot_id, **kwargs)
125
126     self._run.options.prebuilts = True
127
128   def ConstructStage(self):
129     return artifact_stages.UploadPrebuiltsStage(self._run,
130                                                 self._run.config.boards[-1])
131
132   def _VerifyBoardMap(self, bot_id, count, board_map, public_args=None,
133                       private_args=None):
134     """Verify that the prebuilts are uploaded for the specified bot.
135
136     Args:
137       bot_id: Bot to upload prebuilts for.
138       count: Number of assert checks that should be performed.
139       board_map: Map from slave boards to whether the bot is public.
140       public_args: List of extra arguments for public boards.
141       private_args: List of extra arguments for private boards.
142     """
143     self._Prepare(bot_id)
144     self.RunStage()
145     public_prefix = [self.CMD] + (public_args or [])
146     private_prefix = [self.CMD] + (private_args or [])
147     for board, public in board_map.iteritems():
148       if public or public_args:
149         public_cmd = public_prefix + ['--slave-board', board]
150         self.assertCommandContains(public_cmd, expected=public)
151         count -= 1
152       private_cmd = private_prefix + ['--slave-board', board, '--private']
153       self.assertCommandContains(private_cmd, expected=not public)
154       count -= 1
155     if board_map:
156       self.assertCommandContains([self.CMD, '--set-version',
157                                   self._run.GetVersion()])
158       count -= 1
159     self.assertEqual(count, 0,
160         'Number of asserts performed does not match (%d remaining)' % count)
161
162   def testFullPrebuiltsUpload(self):
163     """Test uploading of full builder prebuilts."""
164     self._VerifyBoardMap('x86-generic-full', 0, {})
165     self.assertCommandContains([self.CMD, '--git-sync'])
166
167   def testIncorrectCount(self):
168     """Test that _VerifyBoardMap asserts when the count is wrong."""
169     self.assertRaises(AssertionError, self._VerifyBoardMap, 'x86-generic-full',
170                       1, {})
171
172
173 class MasterUploadPrebuiltsStageTest(
174     generic_stages_unittest.RunCommandAbstractStageTest):
175   """Tests for the MasterUploadPrebuilts stage."""
176
177   CMD = './upload_prebuilts'
178   RELEASE_TAG = '1234.5.6'
179   VERSION = 'R%s-%s' % (DEFAULT_CHROME_BRANCH, RELEASE_TAG)
180
181   def setUp(self):
182     self.StartPatcher(BuilderRunMock())
183
184   def _Prepare(self, bot_id=None, **kwargs):
185     super(MasterUploadPrebuiltsStageTest, self)._Prepare(bot_id, **kwargs)
186
187     self._run.options.prebuilts = True
188
189   def ConstructStage(self):
190     return artifact_stages.MasterUploadPrebuiltsStage(self._run)
191
192   def _RunStage(self, bot_id):
193     """Run the stage under test with the given |bot_id| config.
194
195     Args:
196       bot_id: Builder config target name.
197     """
198     self._Prepare(bot_id)
199     self.RunStage()
200
201   def _VerifyResults(self, public_slave_boards=(), private_slave_boards=()):
202     """Verify that the expected prebuilt commands were run.
203
204     Do various assertions on the two RunCommands that were run by stage.
205     There should be one private (--private) and one public (default) run.
206
207     Args:
208       public_slave_boards: List of public slave boards.
209       private_slave_boards: List of private slave boards.
210     """
211     # TODO(mtennant): Add functionality in partial_mock to support more flexible
212     # asserting.  For example here, asserting that '--sync-host' appears in
213     # the command that did not include '--public'.
214
215     # Some args are expected for any public run.
216     if public_slave_boards:
217       # It would be nice to confirm that --private is not in command, but note
218       # that --sync-host should not appear in the --private command.
219       cmd = [self.CMD, '--sync-binhost-conf', '--sync-host']
220       self.assertCommandContains(cmd, expected=True)
221
222     # Some args are expected for any private run.
223     if private_slave_boards:
224       cmd = [self.CMD, '--sync-binhost-conf', '--private']
225       self.assertCommandContains(cmd, expected=True)
226
227     # Assert public slave boards are mentioned in public run.
228     for board in public_slave_boards:
229       # This check does not actually confirm that this board was in the public
230       # run rather than the private run, unfortunately.
231       cmd = [self.CMD, '--slave-board', board]
232       self.assertCommandContains(cmd, expected=True)
233
234     # Assert private slave boards are mentioned in private run.
235     for board in private_slave_boards:
236       cmd = [self.CMD, '--slave-board', board, '--private']
237       self.assertCommandContains(cmd, expected=True)
238
239     # We expect --set-version so long as build config has manifest_version=True.
240     self.assertCommandContains([self.CMD, '--set-version', self.VERSION],
241                                expected=self._run.config.manifest_version)
242
243   def testMasterPaladinUpload(self):
244     self._RunStage('master-paladin')
245
246     # Provide a sample of private/public slave boards that are expected.
247     public_slave_boards = ('amd64-generic', 'x86-generic')
248     private_slave_boards = ('x86-mario', 'x86-alex', 'lumpy', 'daisy_spring')
249
250     self._VerifyResults(public_slave_boards=public_slave_boards,
251                         private_slave_boards=private_slave_boards)
252
253   def testMasterChromiumPFQUpload(self):
254     self._RunStage('master-chromium-pfq')
255
256     # Provide a sample of private/public slave boards that are expected.
257     public_slave_boards = ('amd64-generic', 'x86-generic', 'daisy')
258     private_slave_boards = ('x86-alex', 'lumpy', 'daisy_spring', 'falco')
259
260     self._VerifyResults(public_slave_boards=public_slave_boards,
261                         private_slave_boards=private_slave_boards)
262
263
264 class UploadDevInstallerPrebuiltsStageTest(
265     generic_stages_unittest.AbstractStageTest):
266   """Tests for the UploadDevInstallerPrebuilts stage."""
267
268   RELEASE_TAG = 'RT'
269
270   def setUp(self):
271     self.mox.StubOutWithMock(prebuilts, 'UploadDevInstallerPrebuilts')
272
273     self.StartPatcher(BuilderRunMock())
274
275     self._Prepare()
276
277   def _Prepare(self, bot_id=None, **kwargs):
278     super(UploadDevInstallerPrebuiltsStageTest, self)._Prepare(bot_id, **kwargs)
279
280     self._run.options.chrome_rev = None
281     self._run.options.prebuilts = True
282     self._run.config['dev_installer_prebuilts'] = True
283     self._run.config['binhost_bucket'] = 'gs://testbucket'
284     self._run.config['binhost_key'] = 'dontcare'
285     self._run.config['binhost_base_url'] = 'https://dontcare/here'
286
287   def ConstructStage(self):
288     return artifact_stages.DevInstallerPrebuiltsStage(self._run,
289                                                       self._current_board)
290
291   def testDevInstallerUpload(self):
292     """Basic sanity test testing uploads of dev installer prebuilts."""
293     version = 'R%s-%s' % (DEFAULT_CHROME_BRANCH, self.RELEASE_TAG)
294
295     prebuilts.UploadDevInstallerPrebuilts(
296         binhost_bucket=self._run.config.binhost_bucket,
297         binhost_key=self._run.config.binhost_key,
298         binhost_base_url=self._run.config.binhost_base_url,
299         buildroot=self.build_root,
300         board=self._current_board,
301         extra_args=mox.And(mox.IsA(list),
302                            mox.In(version)))
303
304     self.mox.ReplayAll()
305     self.RunStage()
306     self.mox.VerifyAll()
307
308
309 class CPEExportStageTest(generic_stages_unittest.AbstractStageTest):
310   """Test CPEExportStage"""
311
312   def setUp(self):
313     self.StartPatcher(BuilderRunMock())
314     self.StartPatcher(generic_stages_unittest.ArchivingStageMixinMock())
315     self.StartPatcher(parallel_unittest.ParallelMock())
316
317     self.rc_mock = self.StartPatcher(cros_build_lib_unittest.RunCommandMock())
318     self.rc_mock.SetDefaultCmdResult(output='')
319
320     self.stage = None
321
322   def ConstructStage(self):
323     """Create a CPEExportStage instance for testing"""
324     self._run.GetArchive().SetupArchivePath()
325     return artifact_stages.CPEExportStage(self._run, self._current_board)
326
327   def assertBoardAttrEqual(self, attr, expected_value):
328     """Assert the value of a board run |attr| against |expected_value|."""
329     value = self.stage.board_runattrs.GetParallel(attr)
330     self.assertEqual(expected_value, value)
331
332   def _TestPerformStage(self):
333     """Run PerformStage for the stage."""
334     self._Prepare()
335     self._run.attrs.release_tag = BuilderRunMock.VERSION
336
337     self.stage = self.ConstructStage()
338     self.stage.PerformStage()
339
340   def testCPEExport(self):
341     """Test that CPEExport stage runs without syntax errors."""
342     self._TestPerformStage()
343
344
345 class DebugSymbolsStageTest(generic_stages_unittest.AbstractStageTest):
346   """Test DebugSymbolsStage"""
347
348   def setUp(self):
349     self.StartPatcher(BuilderRunMock())
350     self.StartPatcher(generic_stages_unittest.ArchivingStageMixinMock())
351     self.StartPatcher(parallel_unittest.ParallelMock())
352
353     self.gen_mock = self.PatchObject(commands, 'GenerateBreakpadSymbols')
354     self.upload_mock = self.PatchObject(commands, 'UploadSymbols')
355     self.tar_mock = self.PatchObject(commands, 'GenerateDebugTarball')
356
357     self.rc_mock = self.StartPatcher(cros_build_lib_unittest.RunCommandMock())
358     self.rc_mock.SetDefaultCmdResult(output='')
359
360     self.stage = None
361
362   def ConstructStage(self):
363     """Create a DebugSymbolsStage instance for testing"""
364     self._run.GetArchive().SetupArchivePath()
365     return artifact_stages.DebugSymbolsStage(self._run, self._current_board)
366
367   def assertBoardAttrEqual(self, attr, expected_value):
368     """Assert the value of a board run |attr| against |expected_value|."""
369     value = self.stage.board_runattrs.GetParallel(attr)
370     self.assertEqual(expected_value, value)
371
372   def _TestPerformStage(self, extra_config=None):
373     """Run PerformStage for the stage with the given extra config."""
374     if not extra_config:
375       extra_config = {
376           'archive_build_debug': True,
377           'vm_tests': True,
378           'upload_symbols': True,
379       }
380
381     self._Prepare(extra_config=extra_config)
382     self._run.attrs.release_tag = BuilderRunMock.VERSION
383
384     self.tar_mock.side_effect = '/my/tar/ball'
385     self.stage = self.ConstructStage()
386     try:
387       self.stage.PerformStage()
388     except Exception:
389       self.stage._HandleStageException(sys.exc_info())
390       raise
391
392   def testPerformStageWithSymbols(self):
393     """Smoke test for an PerformStage when debugging is enabled"""
394     self._TestPerformStage()
395
396     self.assertEqual(self.gen_mock.call_count, 1)
397     self.assertEqual(self.upload_mock.call_count, 1)
398     self.assertEqual(self.tar_mock.call_count, 1)
399
400     self.assertBoardAttrEqual('breakpad_symbols_generated', True)
401     self.assertBoardAttrEqual('debug_tarball_generated', True)
402
403   def testPerformStageNoSymbols(self):
404     """Smoke test for an PerformStage when debugging is disabled"""
405     extra_config = {
406         'archive_build_debug': False,
407         'vm_tests': False,
408         'upload_symbols': False,
409     }
410     self._TestPerformStage(extra_config)
411
412     self.assertEqual(self.gen_mock.call_count, 1)
413     self.assertEqual(self.upload_mock.call_count, 0)
414     self.assertEqual(self.tar_mock.call_count, 1)
415
416     self.assertBoardAttrEqual('breakpad_symbols_generated', True)
417     self.assertBoardAttrEqual('debug_tarball_generated', True)
418
419   def testGenerateCrashStillNotifies(self):
420     """Crashes in symbol generation should still notify external events."""
421     self.skipTest('Test skipped due to crbug.com/363339')
422     class TestError(Exception):
423       """Unique test exception"""
424
425     self.gen_mock.side_effect = TestError('mew')
426     self.assertRaises(TestError, self._TestPerformStage)
427
428     self.assertEqual(self.gen_mock.call_count, 1)
429     self.assertEqual(self.upload_mock.call_count, 0)
430     self.assertEqual(self.tar_mock.call_count, 0)
431
432     self.assertBoardAttrEqual('breakpad_symbols_generated', False)
433     self.assertBoardAttrEqual('debug_tarball_generated', False)
434
435   def testUploadCrashStillNotifies(self):
436     """Crashes in symbol upload should still notify external events."""
437     class TestError(failures_lib.CrashCollectionFailure):
438       """Unique test exception"""
439
440     self.upload_mock.side_effect = TestError('mew')
441     self.assertRaises(TestError, self._TestPerformStage)
442
443     self.assertEqual(self.gen_mock.call_count, 1)
444     self.assertEqual(self.upload_mock.call_count, 1)
445     self.assertEqual(self.tar_mock.call_count, 1)
446
447     self.assertBoardAttrEqual('breakpad_symbols_generated', True)
448     self.assertBoardAttrEqual('debug_tarball_generated', True)
449
450
451 class UploadTestArtifactsStageMock(
452     generic_stages_unittest.ArchivingStageMixinMock):
453   """Partial mock for BuildImageStage."""
454
455   TARGET = 'chromite.cbuildbot.stages.artifact_stages.UploadTestArtifactsStage'
456   ATTRS = (generic_stages_unittest.ArchivingStageMixinMock.ATTRS +
457            ('BuildAutotestTarballs',))
458
459   def BuildAutotestTarballs(self, *args, **kwargs):
460     with patches(
461         patch(commands, 'BuildTarball'),
462         patch(commands, 'FindFilesWithPattern', return_value=['foo.txt'])):
463       self.backup['BuildAutotestTarballs'](*args, **kwargs)
464
465
466 class UploadTestArtifactsStageTest(
467     build_stages_unittest.BuildPackagesStageTest):
468   """Tests UploadTestArtifactsStage."""
469
470   def setUp(self):
471     osutils.SafeMakedirs(os.path.join(self.build_root, 'chroot', 'tmp'))
472     self.StartPatcher(UploadTestArtifactsStageMock())
473
474   def ConstructStage(self):
475     return artifact_stages.UploadTestArtifactsStage(self._run,
476                                                     self._current_board)
477
478   def RunTestsWithBotId(self, bot_id, options_tests=True):
479     """Test with the config for the specified bot_id."""
480     self._Prepare(bot_id)
481     self._run.options.tests = options_tests
482     self._run.attrs.release_tag = '0.0.1'
483
484     # Simulate images being ready.
485     board_runattrs = self._run.GetBoardRunAttrs(self._current_board)
486     board_runattrs.SetParallel('images_generated', True)
487
488     chroot_base = os.path.join(self.build_root, 'chroot')
489
490     def _ExtractOutputParam(cmd):
491       """Extract the --output option from a list of arguments."""
492       argparser = argparse.ArgumentParser()
493       argparser.add_argument('--output', action='store')
494       options, _ = argparser.parse_known_args(cmd)
495       return options.output
496
497     def _SimUpdatePayload(cmd, *_args, **kwargs):
498       """Simulate cros_generate_update_payload by creating its output file."""
499       self.assertTrue(kwargs.get('enter_chroot'))
500
501       output = _ExtractOutputParam(cmd)
502       self.assertTrue(output)
503       self.assertTrue(os.path.dirname(output))
504
505       # Join these paths manually since output is absolute and os.path.join
506       # will throw away chroot_base.
507       output = os.sep.join([chroot_base, output])
508
509       if not os.path.isdir(os.path.dirname(output)):
510         os.makedirs(os.path.dirname(output))
511       self.assertFalse(os.path.exists(output))
512
513       osutils.Touch(output)
514
515     def _SimUpdateStatefulPayload(cmd, *_args, **kwargs):
516       """Simulate cros_generate_stateful_update_payload like above."""
517       self.assertTrue(kwargs.get('enter_chroot'))
518
519       output = _ExtractOutputParam(cmd)
520       self.assertTrue(output)
521
522       # Join these paths manually since output is absolute and os.path.join
523       # will throw away chroot_base.
524       output = os.sep.join([chroot_base, output])
525
526       if not os.path.isdir(output):
527         os.makedirs(output)
528
529       output = os.path.join(output, commands.STATEFUL_FILE)
530
531       self.assertFalse(os.path.exists(output))
532
533       osutils.Touch(output)
534
535     def _HookRunCommand(rc):
536       rc.AddCmdResult(
537         partial_mock.ListRegex('cros_generate_update_payload'),
538         side_effect=_SimUpdatePayload)
539       rc.AddCmdResult(
540         partial_mock.ListRegex('cros_generate_stateful_update_payload'),
541         side_effect=_SimUpdateStatefulPayload)
542
543     with parallel_unittest.ParallelMock():
544       with self.RunStageWithConfig(mock_configurator=_HookRunCommand) as rc:
545         if self._run.config.upload_hw_test_artifacts:
546           self.assertNotEqual(rc.call_count, 0)
547         else:
548           self.assertEqual(rc.call_count, 0)
549
550
551 # TODO: Delete ArchivingMock once ArchivingStage is deprecated.
552 class ArchivingMock(partial_mock.PartialMock):
553   """Partial mock for ArchivingStage."""
554
555   TARGET = 'chromite.cbuildbot.stages.artifact_stages.ArchivingStage'
556   ATTRS = ('UploadArtifact',)
557
558   def UploadArtifact(self, *args, **kwargs):
559     with patch(commands, 'ArchiveFile', return_value='foo.txt'):
560       with patch(commands, 'UploadArchivedFile'):
561         self.backup['UploadArtifact'](*args, **kwargs)
562
563
564 # TODO: Delete ArchivingStageTest once ArchivingStage is deprecated.
565 class ArchivingStageTest(generic_stages_unittest.AbstractStageTest):
566   """Excerise ArchivingStage functionality."""
567   RELEASE_TAG = ''
568
569   def setUp(self):
570     self.StartPatcher(BuilderRunMock())
571     self.StartPatcher(ArchivingMock())
572
573     self._Prepare()
574
575   def ConstructStage(self):
576     self._run.GetArchive().SetupArchivePath()
577     archive_stage = artifact_stages.ArchiveStage(
578         self._run, self._current_board)
579     return artifact_stages.ArchivingStage(
580         self._run, self._current_board, archive_stage)
581
582
583 if __name__ == '__main__':
584   cros_test_lib.main()