2 # Copyright (c) 2013 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.
6 """Unittests for pushimage.py"""
8 from __future__ import print_function
14 sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
16 from chromite.lib import cros_build_lib
17 from chromite.lib import cros_test_lib
18 from chromite.lib import git
19 from chromite.lib import gs_unittest
20 from chromite.lib import osutils
21 from chromite.lib import partial_mock
22 from chromite.lib import signing
23 from chromite.scripts import pushimage
26 class InputInsnsTest(cros_test_lib.MockTestCase):
27 """Tests for InputInsns"""
30 """Simple smoke test"""
31 insns = pushimage.InputInsns('test.board')
32 insns.GetInsnFile('recovery')
33 self.assertEqual(insns.GetChannels(), ['dev', 'canary'])
34 self.assertEqual(insns.GetKeysets(), ['stumpy-mp-v3'])
36 def testGetInsnFile(self):
37 """Verify various inputs result in right insns path"""
39 ('UPPER_CAPS', 'UPPER_CAPS'),
40 ('recovery', 'test.board'),
41 ('firmware', 'test.board.firmware'),
42 ('factory', 'test.board.factory'),
44 insns = pushimage.InputInsns('test.board')
45 for image_type, filename in testdata:
46 ret = insns.GetInsnFile(image_type)
47 self.assertEqual(os.path.basename(ret), '%s.instructions' % (filename))
49 def testSplitCfgField(self):
50 """Verify splitting behavior behaves"""
53 ('a b c', ['a', 'b', 'c']),
56 ('a,\tb', ['a', 'b']),
59 for val, exp in testdata:
60 ret = pushimage.InputInsns.SplitCfgField(val)
61 self.assertEqual(ret, exp)
63 def testOutputInsnsBasic(self):
64 """Verify output instructions are sane"""
65 exp_content = """[insns]
68 chromeos_shell = false
69 ensure_no_password = true
70 firmware_update = true
71 security_checks = true
72 create_nplusone = true
77 insns = pushimage.InputInsns('test.board')
78 m = self.PatchObject(osutils, 'WriteFile')
79 insns.OutputInsns('recovery', '/bogus', {}, {})
80 self.assertTrue(m.called)
81 content = m.call_args_list[0][0][1]
82 self.assertEqual(content.rstrip(), exp_content.rstrip())
84 def testOutputInsnsReplacements(self):
85 """Verify output instructions can be updated"""
86 exp_content = """[insns]
89 chromeos_shell = false
90 ensure_no_password = true
91 firmware_update = true
92 security_checks = true
93 create_nplusone = true
97 config_board = test.board
104 'config_board': 'test.board',
108 insns = pushimage.InputInsns('test.board')
109 m = self.PatchObject(osutils, 'WriteFile')
110 insns.OutputInsns('recovery', '/a/file', sect_insns, sect_general)
111 self.assertTrue(m.called)
112 content = m.call_args_list[0][0][1]
113 self.assertEqual(content.rstrip(), exp_content.rstrip())
116 class MarkImageToBeSignedTest(gs_unittest.AbstractGSContextTest):
117 """Tests for MarkImageToBeSigned()"""
120 # Minor optimization -- we call this for logging purposes in the main
121 # code, but don't really care about it for testing. It just slows us.
122 self.PatchObject(git, 'RunGit',
123 return_value=cros_build_lib.CommandResult(output='1234\n'))
126 """Simple smoke test"""
127 tbs_base = 'gs://some-bucket'
128 insns_path = 'chan/board/ver/file.instructions'
129 tbs_file = '%s/tobesigned/90,chan,board,ver,file.instructions' % tbs_base
130 ret = pushimage.MarkImageToBeSigned(self.ctx, tbs_base, insns_path, 90)
131 self.assertEqual(ret, tbs_file)
133 def testPriority(self):
134 """Verify diff priority values get used correctly"""
135 for prio, sprio in ((0, '00'), (9, '09'), (35, '35'), (99, '99')):
136 ret = pushimage.MarkImageToBeSigned(self.ctx, '', '', prio)
137 self.assertEquals(ret, '/tobesigned/%s,' % sprio)
139 def testBadPriority(self):
140 """Verify we reject bad priority values"""
141 for prio in (-10, -1, 100, 91239):
142 self.assertRaises(ValueError, pushimage.MarkImageToBeSigned, self.ctx,
145 def testTbsFile(self):
146 """Make sure the tbs file we write has useful data"""
147 WriteFile = osutils.WriteFile
148 def _write_check(*args, **kwargs):
149 # We can't mock every call, so do the actual write for most.
150 WriteFile(*args, **kwargs)
152 m = self.PatchObject(osutils, 'WriteFile')
153 m.side_effect = _write_check
154 pushimage.MarkImageToBeSigned(self.ctx, '', '', 50)
155 # We assume the first call is the one we care about.
156 self.assertTrue(m.called)
157 content = m.call_args_list[0][0][1]
158 self.assertIn('USER=', content)
159 self.assertIn('HOSTNAME=', content)
160 self.assertIn('GIT_REV=1234', content)
161 self.assertIn('\n', content)
163 def testTbsUpload(self):
164 """Make sure we actually try to upload the file"""
165 pushimage.MarkImageToBeSigned(self.ctx, '', '', 50)
166 self.gs_mock.assertCommandContains(['cp', '--'])
169 class PushImageTests(gs_unittest.AbstractGSContextTest):
170 """Tests for PushImage()"""
173 self.mark_mock = self.PatchObject(pushimage, 'MarkImageToBeSigned')
176 """Simple smoke test"""
179 'gs://chromeos-releases/canary-channel/test.board-hi/5126.0.0/'
180 'ChromeOS-recovery-R34-5126.0.0-test.board-hi.instructions'],
182 'gs://chromeos-releases/dev-channel/test.board-hi/5126.0.0/'
183 'ChromeOS-recovery-R34-5126.0.0-test.board-hi.instructions'],
185 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
188 self.assertEqual(urls, EXPECTED)
190 def testBasicMock(self):
191 """Simple smoke test in mock mode"""
192 pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
193 dry_run=True, mock=True)
195 def testBadVersion(self):
196 """Make sure we barf on bad version strings"""
197 self.assertRaises(ValueError, pushimage.PushImage, '', '', 'asdf')
199 def testNoInsns(self):
200 """Boards w/out insn files should get skipped"""
201 urls = pushimage.PushImage('/src', 'a bad bad board', 'R34-5126.0.0')
202 self.assertEqual(self.gs_mock.call_count, 0)
203 self.assertEqual(urls, None)
205 def testSignTypesRecovery(self):
206 """Only sign the requested recovery type"""
209 'gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
210 'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'],
212 'gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
213 'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'],
216 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
217 sign_types=['recovery'])
218 self.assertEqual(self.gs_mock.call_count, 18)
219 self.assertTrue(self.mark_mock.called)
220 self.assertEqual(urls, EXPECTED)
222 def testSignTypesNone(self):
223 """Verify nothing is signed when we request an unavailable type"""
224 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
225 sign_types=['nononononono'])
226 self.assertEqual(self.gs_mock.call_count, 16)
227 self.assertFalse(self.mark_mock.called)
228 self.assertEqual(urls, {})
230 def testGsError(self):
231 """Verify random GS errors don't make us blow up entirely"""
232 self.gs_mock.AddCmdResult(partial_mock.In('stat'), returncode=1,
233 output='gobblety gook\n')
234 with cros_test_lib.LoggingCapturer('chromite'):
235 self.assertRaises(pushimage.PushError, pushimage.PushImage, '/src',
236 'test.board', 'R34-5126.0.0')
239 class MainTests(cros_test_lib.MockTestCase):
240 """Tests for main()"""
243 self.PatchObject(pushimage, 'PushImage')
246 """Simple smoke test"""
247 pushimage.main(['--board', 'test.board', '/src', '--yes'])
250 if __name__ == '__main__':
251 # Use our local copy of insns for testing as the main one is not
252 # available in the public manifest.
253 signing.INPUT_INSN_DIR = signing.TEST_INPUT_INSN_DIR
256 cros_test_lib.main(level=logging.INFO)