blob: 8a8cfe72ff50e83391f5e62f19e813800d514935 [file] [log] [blame]
Mike Frysingerd13faeb2013-09-05 16:00:46 -04001#!/usr/bin/python
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.
5
6"""Unittests for pushimage.py"""
7
Mike Frysinger6791b1d2014-03-04 20:52:22 -05008from __future__ import print_function
9
Mike Frysingerd13faeb2013-09-05 16:00:46 -040010import logging
Don Garrett9fd20a82014-09-04 11:37:22 -070011import mock
Mike Frysingerd13faeb2013-09-05 16:00:46 -040012import os
13import sys
14
15sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
16 '..', '..'))
Mike Frysinger6791b1d2014-03-04 20:52:22 -050017from chromite.lib import cros_build_lib
Mike Frysingerd13faeb2013-09-05 16:00:46 -040018from chromite.lib import cros_test_lib
19from chromite.lib import git
Don Garrett9fd20a82014-09-04 11:37:22 -070020from chromite.lib import gs
Mike Frysingerd13faeb2013-09-05 16:00:46 -040021from chromite.lib import gs_unittest
22from chromite.lib import osutils
Mike Frysinger4495b032014-03-05 17:24:03 -050023from chromite.lib import partial_mock
Mike Frysingerd13faeb2013-09-05 16:00:46 -040024from chromite.lib import signing
25from chromite.scripts import pushimage
26
27
28class InputInsnsTest(cros_test_lib.MockTestCase):
29 """Tests for InputInsns"""
30
31 def testBasic(self):
32 """Simple smoke test"""
Don Garrett9fd20a82014-09-04 11:37:22 -070033 with mock.patch.object(gs.GSContext, 'Exists', return_value=False):
34 insns = pushimage.InputInsns('test.board')
35 insns.GetInsnFile('recovery')
36 self.assertEqual(insns.GetChannels(), ['dev', 'canary'])
37 self.assertEqual(insns.GetKeysets(), ['stumpy-mp-v3'])
Mike Frysingerd13faeb2013-09-05 16:00:46 -040038
39 def testGetInsnFile(self):
40 """Verify various inputs result in right insns path"""
41 testdata = (
42 ('UPPER_CAPS', 'UPPER_CAPS'),
43 ('recovery', 'test.board'),
44 ('firmware', 'test.board.firmware'),
45 ('factory', 'test.board.factory'),
46 )
47 insns = pushimage.InputInsns('test.board')
48 for image_type, filename in testdata:
49 ret = insns.GetInsnFile(image_type)
50 self.assertEqual(os.path.basename(ret), '%s.instructions' % (filename))
51
52 def testSplitCfgField(self):
53 """Verify splitting behavior behaves"""
54 testdata = (
55 ('', []),
56 ('a b c', ['a', 'b', 'c']),
57 ('a, b', ['a', 'b']),
58 ('a,b', ['a', 'b']),
59 ('a,\tb', ['a', 'b']),
60 ('a\tb', ['a', 'b']),
61 )
62 for val, exp in testdata:
63 ret = pushimage.InputInsns.SplitCfgField(val)
64 self.assertEqual(ret, exp)
65
66 def testOutputInsnsBasic(self):
67 """Verify output instructions are sane"""
68 exp_content = """[insns]
69keyset = stumpy-mp-v3
70channel = dev canary
71chromeos_shell = false
72ensure_no_password = true
73firmware_update = true
74security_checks = true
75create_nplusone = true
76
77[general]
78"""
79
80 insns = pushimage.InputInsns('test.board')
81 m = self.PatchObject(osutils, 'WriteFile')
82 insns.OutputInsns('recovery', '/bogus', {}, {})
83 self.assertTrue(m.called)
84 content = m.call_args_list[0][0][1]
85 self.assertEqual(content.rstrip(), exp_content.rstrip())
86
87 def testOutputInsnsReplacements(self):
88 """Verify output instructions can be updated"""
89 exp_content = """[insns]
90keyset = batman
91channel = dev
92chromeos_shell = false
93ensure_no_password = true
94firmware_update = true
95security_checks = true
96create_nplusone = true
97
98[general]
99board = board
100config_board = test.board
101"""
102 sect_insns = {
103 'channel': 'dev',
104 'keyset': 'batman',
105 }
106 sect_general = {
107 'config_board': 'test.board',
108 'board': 'board',
109 }
110
111 insns = pushimage.InputInsns('test.board')
112 m = self.PatchObject(osutils, 'WriteFile')
113 insns.OutputInsns('recovery', '/a/file', sect_insns, sect_general)
114 self.assertTrue(m.called)
115 content = m.call_args_list[0][0][1]
116 self.assertEqual(content.rstrip(), exp_content.rstrip())
117
118
Mike Frysinger4495b032014-03-05 17:24:03 -0500119class MarkImageToBeSignedTest(gs_unittest.AbstractGSContextTest):
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400120 """Tests for MarkImageToBeSigned()"""
121
122 def setUp(self):
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400123 # Minor optimization -- we call this for logging purposes in the main
124 # code, but don't really care about it for testing. It just slows us.
Mike Frysinger6791b1d2014-03-04 20:52:22 -0500125 self.PatchObject(git, 'RunGit',
126 return_value=cros_build_lib.CommandResult(output='1234\n'))
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400127
128 def testBasic(self):
129 """Simple smoke test"""
130 tbs_base = 'gs://some-bucket'
131 insns_path = 'chan/board/ver/file.instructions'
132 tbs_file = '%s/tobesigned/90,chan,board,ver,file.instructions' % tbs_base
133 ret = pushimage.MarkImageToBeSigned(self.ctx, tbs_base, insns_path, 90)
134 self.assertEqual(ret, tbs_file)
135
136 def testPriority(self):
137 """Verify diff priority values get used correctly"""
138 for prio, sprio in ((0, '00'), (9, '09'), (35, '35'), (99, '99')):
139 ret = pushimage.MarkImageToBeSigned(self.ctx, '', '', prio)
140 self.assertEquals(ret, '/tobesigned/%s,' % sprio)
141
142 def testBadPriority(self):
143 """Verify we reject bad priority values"""
144 for prio in (-10, -1, 100, 91239):
145 self.assertRaises(ValueError, pushimage.MarkImageToBeSigned, self.ctx,
146 '', '', prio)
147
148 def testTbsFile(self):
149 """Make sure the tbs file we write has useful data"""
150 WriteFile = osutils.WriteFile
151 def _write_check(*args, **kwargs):
152 # We can't mock every call, so do the actual write for most.
153 WriteFile(*args, **kwargs)
154
155 m = self.PatchObject(osutils, 'WriteFile')
156 m.side_effect = _write_check
157 pushimage.MarkImageToBeSigned(self.ctx, '', '', 50)
158 # We assume the first call is the one we care about.
159 self.assertTrue(m.called)
Mike Frysingere68da372014-02-04 03:10:45 -0500160 content = m.call_args_list[0][0][1]
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400161 self.assertIn('USER=', content)
162 self.assertIn('HOSTNAME=', content)
Mike Frysinger6791b1d2014-03-04 20:52:22 -0500163 self.assertIn('GIT_REV=1234', content)
Mike Frysingere68da372014-02-04 03:10:45 -0500164 self.assertIn('\n', content)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400165
166 def testTbsUpload(self):
167 """Make sure we actually try to upload the file"""
168 pushimage.MarkImageToBeSigned(self.ctx, '', '', 50)
169 self.gs_mock.assertCommandContains(['cp', '--'])
170
171
Mike Frysinger4495b032014-03-05 17:24:03 -0500172class PushImageTests(gs_unittest.AbstractGSContextTest):
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400173 """Tests for PushImage()"""
174
175 def setUp(self):
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400176 self.mark_mock = self.PatchObject(pushimage, 'MarkImageToBeSigned')
177
178 def testBasic(self):
179 """Simple smoke test"""
Don Garrett9459c2f2014-01-22 18:20:24 -0800180 EXPECTED = {
181 'canary': [
182 'gs://chromeos-releases/canary-channel/test.board-hi/5126.0.0/'
183 'ChromeOS-recovery-R34-5126.0.0-test.board-hi.instructions'],
184 'dev': [
185 'gs://chromeos-releases/dev-channel/test.board-hi/5126.0.0/'
186 'ChromeOS-recovery-R34-5126.0.0-test.board-hi.instructions'],
187 }
Don Garrett9fd20a82014-09-04 11:37:22 -0700188 with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
189 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
190 profile='hi')
Don Garrett9459c2f2014-01-22 18:20:24 -0800191
192 self.assertEqual(urls, EXPECTED)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400193
194 def testBasicMock(self):
195 """Simple smoke test in mock mode"""
Don Garrett9fd20a82014-09-04 11:37:22 -0700196 with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
197 pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
198 dry_run=True, mock=True)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400199
200 def testBadVersion(self):
201 """Make sure we barf on bad version strings"""
202 self.assertRaises(ValueError, pushimage.PushImage, '', '', 'asdf')
203
204 def testNoInsns(self):
205 """Boards w/out insn files should get skipped"""
Don Garrett9459c2f2014-01-22 18:20:24 -0800206 urls = pushimage.PushImage('/src', 'a bad bad board', 'R34-5126.0.0')
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400207 self.assertEqual(self.gs_mock.call_count, 0)
Don Garrett9459c2f2014-01-22 18:20:24 -0800208 self.assertEqual(urls, None)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400209
210 def testSignTypesRecovery(self):
211 """Only sign the requested recovery type"""
Don Garrett9459c2f2014-01-22 18:20:24 -0800212 EXPECTED = {
213 'canary': [
214 'gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
215 'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'],
216 'dev': [
217 'gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
218 'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'],
219 }
220
221 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
222 sign_types=['recovery'])
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400223 self.assertEqual(self.gs_mock.call_count, 18)
224 self.assertTrue(self.mark_mock.called)
Don Garrett9459c2f2014-01-22 18:20:24 -0800225 self.assertEqual(urls, EXPECTED)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400226
227 def testSignTypesNone(self):
228 """Verify nothing is signed when we request an unavailable type"""
Don Garrett9459c2f2014-01-22 18:20:24 -0800229 urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
230 sign_types=['nononononono'])
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400231 self.assertEqual(self.gs_mock.call_count, 16)
232 self.assertFalse(self.mark_mock.called)
Don Garrett9459c2f2014-01-22 18:20:24 -0800233 self.assertEqual(urls, {})
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400234
Mike Frysinger4495b032014-03-05 17:24:03 -0500235 def testGsError(self):
236 """Verify random GS errors don't make us blow up entirely"""
237 self.gs_mock.AddCmdResult(partial_mock.In('stat'), returncode=1,
238 output='gobblety gook\n')
239 with cros_test_lib.LoggingCapturer('chromite'):
240 self.assertRaises(pushimage.PushError, pushimage.PushImage, '/src',
241 'test.board', 'R34-5126.0.0')
242
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400243
244class MainTests(cros_test_lib.MockTestCase):
245 """Tests for main()"""
246
247 def setUp(self):
248 self.PatchObject(pushimage, 'PushImage')
249
250 def testBasic(self):
251 """Simple smoke test"""
Mike Frysinger09fe0122014-02-09 02:44:05 -0500252 pushimage.main(['--board', 'test.board', '/src', '--yes'])
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400253
254
255if __name__ == '__main__':
256 # Use our local copy of insns for testing as the main one is not
257 # available in the public manifest.
258 signing.INPUT_INSN_DIR = signing.TEST_INPUT_INSN_DIR
259
260 # Run the tests.
261 cros_test_lib.main(level=logging.INFO)