blob: 6edf80cb1289195bdea34e54ada5a8b99a6da7cc [file] [log] [blame]
Tao Bao481bab82017-12-21 11:23:09 -08001#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import copy
Tao Baoc7b403a2018-01-30 18:19:04 -080018import os
Tao Baofabe0832018-01-17 15:52:28 -080019import os.path
Tao Bao481bab82017-12-21 11:23:09 -080020import unittest
Tao Baoc7b403a2018-01-30 18:19:04 -080021import zipfile
Tao Bao481bab82017-12-21 11:23:09 -080022
23import common
Tao Bao04e1f012018-02-04 12:13:35 -080024import test_utils
Tao Bao481bab82017-12-21 11:23:09 -080025from ota_from_target_files import (
Tao Baof7140c02018-01-30 17:09:24 -080026 _LoadOemDicts, BuildInfo, GetPackageMetadata,
27 GetTargetFilesZipForSecondaryImages, Payload, PayloadSigner,
Tao Baofabe0832018-01-17 15:52:28 -080028 WriteFingerprintAssertion)
29
30
Tao Baof7140c02018-01-30 17:09:24 -080031def construct_target_files(secondary=False):
32 """Returns a target-files.zip file for generating OTA packages."""
33 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
34 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
35 # META/update_engine_config.txt
36 target_files_zip.writestr(
37 'META/update_engine_config.txt',
38 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
39
40 # META/ab_partitions.txt
41 ab_partitions = ['boot', 'system', 'vendor']
42 target_files_zip.writestr(
43 'META/ab_partitions.txt',
44 '\n'.join(ab_partitions))
45
46 # Create dummy images for each of them.
47 for partition in ab_partitions:
48 target_files_zip.writestr('IMAGES/' + partition + '.img',
49 os.urandom(len(partition)))
50
51 if secondary:
52 target_files_zip.writestr('IMAGES/system_other.img',
53 os.urandom(len("system_other")))
54
55 return target_files
56
57
Tao Bao481bab82017-12-21 11:23:09 -080058class MockScriptWriter(object):
59 """A class that mocks edify_generator.EdifyGenerator.
60
61 It simply pushes the incoming arguments onto script stack, which is to assert
62 the calls to EdifyGenerator functions.
63 """
64
65 def __init__(self):
66 self.script = []
67
68 def Mount(self, *args):
69 self.script.append(('Mount',) + args)
70
71 def AssertDevice(self, *args):
72 self.script.append(('AssertDevice',) + args)
73
74 def AssertOemProperty(self, *args):
75 self.script.append(('AssertOemProperty',) + args)
76
77 def AssertFingerprintOrThumbprint(self, *args):
78 self.script.append(('AssertFingerprintOrThumbprint',) + args)
79
80 def AssertSomeFingerprint(self, *args):
81 self.script.append(('AssertSomeFingerprint',) + args)
82
83 def AssertSomeThumbprint(self, *args):
84 self.script.append(('AssertSomeThumbprint',) + args)
85
86
87class BuildInfoTest(unittest.TestCase):
88
89 TEST_INFO_DICT = {
90 'build.prop' : {
91 'ro.product.device' : 'product-device',
92 'ro.product.name' : 'product-name',
93 'ro.build.fingerprint' : 'build-fingerprint',
94 'ro.build.foo' : 'build-foo',
95 },
96 'vendor.build.prop' : {
97 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
98 },
99 'property1' : 'value1',
100 'property2' : 4096,
101 }
102
103 TEST_INFO_DICT_USES_OEM_PROPS = {
104 'build.prop' : {
105 'ro.product.name' : 'product-name',
106 'ro.build.thumbprint' : 'build-thumbprint',
107 'ro.build.bar' : 'build-bar',
108 },
109 'vendor.build.prop' : {
110 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
111 },
112 'property1' : 'value1',
113 'property2' : 4096,
114 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
115 }
116
117 TEST_OEM_DICTS = [
118 {
119 'ro.product.brand' : 'brand1',
120 'ro.product.device' : 'device1',
121 },
122 {
123 'ro.product.brand' : 'brand2',
124 'ro.product.device' : 'device2',
125 },
126 {
127 'ro.product.brand' : 'brand3',
128 'ro.product.device' : 'device3',
129 },
130 ]
131
132 def test_init(self):
133 target_info = BuildInfo(self.TEST_INFO_DICT, None)
134 self.assertEqual('product-device', target_info.device)
135 self.assertEqual('build-fingerprint', target_info.fingerprint)
136 self.assertFalse(target_info.is_ab)
137 self.assertIsNone(target_info.oem_props)
138
139 def test_init_with_oem_props(self):
140 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
141 self.TEST_OEM_DICTS)
142 self.assertEqual('device1', target_info.device)
143 self.assertEqual('brand1/product-name/device1:build-thumbprint',
144 target_info.fingerprint)
145
146 # Swap the order in oem_dicts, which would lead to different BuildInfo.
147 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
148 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
149 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
150 self.assertEqual('device3', target_info.device)
151 self.assertEqual('brand3/product-name/device3:build-thumbprint',
152 target_info.fingerprint)
153
154 # Missing oem_dict should be rejected.
155 self.assertRaises(AssertionError, BuildInfo,
156 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
157
158 def test___getitem__(self):
159 target_info = BuildInfo(self.TEST_INFO_DICT, None)
160 self.assertEqual('value1', target_info['property1'])
161 self.assertEqual(4096, target_info['property2'])
162 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
163
164 def test___getitem__with_oem_props(self):
165 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
166 self.TEST_OEM_DICTS)
167 self.assertEqual('value1', target_info['property1'])
168 self.assertEqual(4096, target_info['property2'])
169 self.assertRaises(KeyError,
170 lambda: target_info['build.prop']['ro.build.foo'])
171
172 def test_get(self):
173 target_info = BuildInfo(self.TEST_INFO_DICT, None)
174 self.assertEqual('value1', target_info.get('property1'))
175 self.assertEqual(4096, target_info.get('property2'))
176 self.assertEqual(4096, target_info.get('property2', 1024))
177 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
178 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
179
180 def test_get_with_oem_props(self):
181 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
182 self.TEST_OEM_DICTS)
183 self.assertEqual('value1', target_info.get('property1'))
184 self.assertEqual(4096, target_info.get('property2'))
185 self.assertEqual(4096, target_info.get('property2', 1024))
186 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
187 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
188 self.assertRaises(KeyError,
189 lambda: target_info.get('build.prop')['ro.build.foo'])
190
191 def test_GetBuildProp(self):
192 target_info = BuildInfo(self.TEST_INFO_DICT, None)
193 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
194 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
195 'ro.build.nonexistent')
196
197 def test_GetBuildProp_with_oem_props(self):
198 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
199 self.TEST_OEM_DICTS)
200 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
201 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
202 'ro.build.nonexistent')
203
204 def test_GetVendorBuildProp(self):
205 target_info = BuildInfo(self.TEST_INFO_DICT, None)
206 self.assertEqual('vendor-build-fingerprint',
207 target_info.GetVendorBuildProp(
208 'ro.vendor.build.fingerprint'))
209 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
210 'ro.build.nonexistent')
211
212 def test_GetVendorBuildProp_with_oem_props(self):
213 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
214 self.TEST_OEM_DICTS)
215 self.assertEqual('vendor-build-fingerprint',
216 target_info.GetVendorBuildProp(
217 'ro.vendor.build.fingerprint'))
218 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
219 'ro.build.nonexistent')
220
221 def test_WriteMountOemScript(self):
222 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
223 self.TEST_OEM_DICTS)
224 script_writer = MockScriptWriter()
225 target_info.WriteMountOemScript(script_writer)
226 self.assertEqual([('Mount', '/oem', None)], script_writer.script)
227
228 def test_WriteDeviceAssertions(self):
229 target_info = BuildInfo(self.TEST_INFO_DICT, None)
230 script_writer = MockScriptWriter()
231 target_info.WriteDeviceAssertions(script_writer, False)
232 self.assertEqual([('AssertDevice', 'product-device')], script_writer.script)
233
234 def test_WriteDeviceAssertions_with_oem_props(self):
235 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
236 self.TEST_OEM_DICTS)
237 script_writer = MockScriptWriter()
238 target_info.WriteDeviceAssertions(script_writer, False)
239 self.assertEqual(
240 [
241 ('AssertOemProperty', 'ro.product.device',
242 ['device1', 'device2', 'device3'], False),
243 ('AssertOemProperty', 'ro.product.brand',
244 ['brand1', 'brand2', 'brand3'], False),
245 ],
246 script_writer.script)
247
248 def test_WriteFingerprintAssertion_without_oem_props(self):
249 target_info = BuildInfo(self.TEST_INFO_DICT, None)
250 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
251 source_info_dict['build.prop']['ro.build.fingerprint'] = (
252 'source-build-fingerprint')
253 source_info = BuildInfo(source_info_dict, None)
254
255 script_writer = MockScriptWriter()
256 WriteFingerprintAssertion(script_writer, target_info, source_info)
257 self.assertEqual(
258 [('AssertSomeFingerprint', 'source-build-fingerprint',
259 'build-fingerprint')],
260 script_writer.script)
261
262 def test_WriteFingerprintAssertion_with_source_oem_props(self):
263 target_info = BuildInfo(self.TEST_INFO_DICT, None)
264 source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
265 self.TEST_OEM_DICTS)
266
267 script_writer = MockScriptWriter()
268 WriteFingerprintAssertion(script_writer, target_info, source_info)
269 self.assertEqual(
270 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
271 'build-thumbprint')],
272 script_writer.script)
273
274 def test_WriteFingerprintAssertion_with_target_oem_props(self):
275 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
276 self.TEST_OEM_DICTS)
277 source_info = BuildInfo(self.TEST_INFO_DICT, None)
278
279 script_writer = MockScriptWriter()
280 WriteFingerprintAssertion(script_writer, target_info, source_info)
281 self.assertEqual(
282 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
283 'build-thumbprint')],
284 script_writer.script)
285
286 def test_WriteFingerprintAssertion_with_both_oem_props(self):
287 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
288 self.TEST_OEM_DICTS)
289 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
290 source_info_dict['build.prop']['ro.build.thumbprint'] = (
291 'source-build-thumbprint')
292 source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
293
294 script_writer = MockScriptWriter()
295 WriteFingerprintAssertion(script_writer, target_info, source_info)
296 self.assertEqual(
297 [('AssertSomeThumbprint', 'build-thumbprint',
298 'source-build-thumbprint')],
299 script_writer.script)
300
301
302class LoadOemDictsTest(unittest.TestCase):
303
304 def tearDown(self):
305 common.Cleanup()
306
307 def test_NoneDict(self):
308 self.assertIsNone(_LoadOemDicts(None))
309
310 def test_SingleDict(self):
311 dict_file = common.MakeTempFile()
312 with open(dict_file, 'w') as dict_fp:
313 dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
314
315 oem_dicts = _LoadOemDicts([dict_file])
316 self.assertEqual(1, len(oem_dicts))
317 self.assertEqual('foo', oem_dicts[0]['xyz'])
318 self.assertEqual('bar', oem_dicts[0]['a.b.c'])
319
320 def test_MultipleDicts(self):
321 oem_source = []
322 for i in range(3):
323 dict_file = common.MakeTempFile()
324 with open(dict_file, 'w') as dict_fp:
325 dict_fp.write(
326 'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
327 oem_source.append(dict_file)
328
329 oem_dicts = _LoadOemDicts(oem_source)
330 self.assertEqual(3, len(oem_dicts))
331 for i, oem_dict in enumerate(oem_dicts):
332 self.assertEqual('2', oem_dict['def'])
333 self.assertEqual('foo', oem_dict['xyz'])
334 self.assertEqual('bar', oem_dict['a.b.c'])
335 self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800336
337
338class OtaFromTargetFilesTest(unittest.TestCase):
339
340 TEST_TARGET_INFO_DICT = {
341 'build.prop' : {
342 'ro.product.device' : 'product-device',
343 'ro.build.fingerprint' : 'build-fingerprint-target',
344 'ro.build.version.incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800345 'ro.build.version.sdk' : '27',
346 'ro.build.version.security_patch' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800347 'ro.build.date.utc' : '1500000000',
348 },
349 }
350
351 TEST_SOURCE_INFO_DICT = {
352 'build.prop' : {
353 'ro.product.device' : 'product-device',
354 'ro.build.fingerprint' : 'build-fingerprint-source',
355 'ro.build.version.incremental' : 'build-version-incremental-source',
Tao Bao35dc2552018-02-01 13:18:00 -0800356 'ro.build.version.sdk' : '25',
357 'ro.build.version.security_patch' : '2016-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800358 'ro.build.date.utc' : '1400000000',
359 },
360 }
361
362 def setUp(self):
363 # Reset the global options as in ota_from_target_files.py.
364 common.OPTIONS.incremental_source = None
365 common.OPTIONS.downgrade = False
366 common.OPTIONS.timestamp = False
367 common.OPTIONS.wipe_user_data = False
368
369 def test_GetPackageMetadata_abOta_full(self):
370 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
371 target_info_dict['ab_update'] = 'true'
372 target_info = BuildInfo(target_info_dict, None)
373 metadata = GetPackageMetadata(target_info)
374 self.assertDictEqual(
375 {
376 'ota-type' : 'AB',
377 'ota-required-cache' : '0',
378 'post-build' : 'build-fingerprint-target',
379 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800380 'post-sdk-level' : '27',
381 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800382 'post-timestamp' : '1500000000',
383 'pre-device' : 'product-device',
384 },
385 metadata)
386
387 def test_GetPackageMetadata_abOta_incremental(self):
388 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
389 target_info_dict['ab_update'] = 'true'
390 target_info = BuildInfo(target_info_dict, None)
391 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
392 common.OPTIONS.incremental_source = ''
393 metadata = GetPackageMetadata(target_info, source_info)
394 self.assertDictEqual(
395 {
396 'ota-type' : 'AB',
397 'ota-required-cache' : '0',
398 'post-build' : 'build-fingerprint-target',
399 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800400 'post-sdk-level' : '27',
401 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800402 'post-timestamp' : '1500000000',
403 'pre-device' : 'product-device',
404 'pre-build' : 'build-fingerprint-source',
405 'pre-build-incremental' : 'build-version-incremental-source',
406 },
407 metadata)
408
409 def test_GetPackageMetadata_nonAbOta_full(self):
410 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
411 metadata = GetPackageMetadata(target_info)
412 self.assertDictEqual(
413 {
414 'ota-type' : 'BLOCK',
415 'post-build' : 'build-fingerprint-target',
416 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800417 'post-sdk-level' : '27',
418 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800419 'post-timestamp' : '1500000000',
420 'pre-device' : 'product-device',
421 },
422 metadata)
423
424 def test_GetPackageMetadata_nonAbOta_incremental(self):
425 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
426 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
427 common.OPTIONS.incremental_source = ''
428 metadata = GetPackageMetadata(target_info, source_info)
429 self.assertDictEqual(
430 {
431 'ota-type' : 'BLOCK',
432 'post-build' : 'build-fingerprint-target',
433 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800434 'post-sdk-level' : '27',
435 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800436 'post-timestamp' : '1500000000',
437 'pre-device' : 'product-device',
438 'pre-build' : 'build-fingerprint-source',
439 'pre-build-incremental' : 'build-version-incremental-source',
440 },
441 metadata)
442
443 def test_GetPackageMetadata_wipe(self):
444 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
445 common.OPTIONS.wipe_user_data = True
446 metadata = GetPackageMetadata(target_info)
447 self.assertDictEqual(
448 {
449 'ota-type' : 'BLOCK',
450 'ota-wipe' : 'yes',
451 'post-build' : 'build-fingerprint-target',
452 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800453 'post-sdk-level' : '27',
454 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800455 'post-timestamp' : '1500000000',
456 'pre-device' : 'product-device',
457 },
458 metadata)
459
460 @staticmethod
461 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
462 (target_info['build.prop']['ro.build.date.utc'],
463 source_info['build.prop']['ro.build.date.utc']) = (
464 source_info['build.prop']['ro.build.date.utc'],
465 target_info['build.prop']['ro.build.date.utc'])
466
467 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
468 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
469 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
470 self._test_GetPackageMetadata_swapBuildTimestamps(
471 target_info_dict, source_info_dict)
472
473 target_info = BuildInfo(target_info_dict, None)
474 source_info = BuildInfo(source_info_dict, None)
475 common.OPTIONS.incremental_source = ''
476 self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
477 source_info)
478
479 def test_GetPackageMetadata_downgrade(self):
480 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
481 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
482 self._test_GetPackageMetadata_swapBuildTimestamps(
483 target_info_dict, source_info_dict)
484
485 target_info = BuildInfo(target_info_dict, None)
486 source_info = BuildInfo(source_info_dict, None)
487 common.OPTIONS.incremental_source = ''
488 common.OPTIONS.downgrade = True
489 common.OPTIONS.wipe_user_data = True
490 metadata = GetPackageMetadata(target_info, source_info)
491 self.assertDictEqual(
492 {
493 'ota-downgrade' : 'yes',
494 'ota-type' : 'BLOCK',
495 'ota-wipe' : 'yes',
496 'post-build' : 'build-fingerprint-target',
497 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800498 'post-sdk-level' : '27',
499 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800500 'pre-device' : 'product-device',
501 'pre-build' : 'build-fingerprint-source',
502 'pre-build-incremental' : 'build-version-incremental-source',
503 },
504 metadata)
505
506 def test_GetPackageMetadata_overrideTimestamp(self):
507 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
508 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
509 self._test_GetPackageMetadata_swapBuildTimestamps(
510 target_info_dict, source_info_dict)
511
512 target_info = BuildInfo(target_info_dict, None)
513 source_info = BuildInfo(source_info_dict, None)
514 common.OPTIONS.incremental_source = ''
515 common.OPTIONS.timestamp = True
516 metadata = GetPackageMetadata(target_info, source_info)
517 self.assertDictEqual(
518 {
519 'ota-type' : 'BLOCK',
520 'post-build' : 'build-fingerprint-target',
521 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800522 'post-sdk-level' : '27',
523 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800524 'post-timestamp' : '1500000001',
525 'pre-device' : 'product-device',
526 'pre-build' : 'build-fingerprint-source',
527 'pre-build-incremental' : 'build-version-incremental-source',
528 },
529 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800530
Tao Baof7140c02018-01-30 17:09:24 -0800531 def test_GetTargetFilesZipForSecondaryImages(self):
532 input_file = construct_target_files(secondary=True)
533 target_file = GetTargetFilesZipForSecondaryImages(input_file)
534
535 with zipfile.ZipFile(target_file) as verify_zip:
536 namelist = verify_zip.namelist()
537
538 self.assertIn('META/ab_partitions.txt', namelist)
539 self.assertIn('IMAGES/boot.img', namelist)
540 self.assertIn('IMAGES/system.img', namelist)
541 self.assertIn('IMAGES/vendor.img', namelist)
542
543 self.assertNotIn('IMAGES/system_other.img', namelist)
544 self.assertNotIn('IMAGES/system.map', namelist)
545
Tao Baofabe0832018-01-17 15:52:28 -0800546
547class PayloadSignerTest(unittest.TestCase):
548
549 SIGFILE = 'sigfile.bin'
550 SIGNED_SIGFILE = 'signed-sigfile.bin'
551
552 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800553 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baofabe0832018-01-17 15:52:28 -0800554 self.assertTrue(os.path.exists(self.testdata_dir))
555
556 common.OPTIONS.payload_signer = None
557 common.OPTIONS.payload_signer_args = []
558 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
559 common.OPTIONS.key_passwords = {
560 common.OPTIONS.package_key : None,
561 }
562
563 def tearDown(self):
564 common.Cleanup()
565
566 def _assertFilesEqual(self, file1, file2):
567 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
568 self.assertEqual(fp1.read(), fp2.read())
569
570 def test_init(self):
571 payload_signer = PayloadSigner()
572 self.assertEqual('openssl', payload_signer.signer)
573
574 def test_init_withPassword(self):
575 common.OPTIONS.package_key = os.path.join(
576 self.testdata_dir, 'testkey_with_passwd')
577 common.OPTIONS.key_passwords = {
578 common.OPTIONS.package_key : 'foo',
579 }
580 payload_signer = PayloadSigner()
581 self.assertEqual('openssl', payload_signer.signer)
582
583 def test_init_withExternalSigner(self):
584 common.OPTIONS.payload_signer = 'abc'
585 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
586 payload_signer = PayloadSigner()
587 self.assertEqual('abc', payload_signer.signer)
588 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
589
590 def test_Sign(self):
591 payload_signer = PayloadSigner()
592 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
593 signed_file = payload_signer.Sign(input_file)
594
595 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
596 self._assertFilesEqual(verify_file, signed_file)
597
598 def test_Sign_withExternalSigner_openssl(self):
599 """Uses openssl as the external payload signer."""
600 common.OPTIONS.payload_signer = 'openssl'
601 common.OPTIONS.payload_signer_args = [
602 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
603 os.path.join(self.testdata_dir, 'testkey.pk8'),
604 '-pkeyopt', 'digest:sha256']
605 payload_signer = PayloadSigner()
606 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
607 signed_file = payload_signer.Sign(input_file)
608
609 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
610 self._assertFilesEqual(verify_file, signed_file)
611
612 def test_Sign_withExternalSigner_script(self):
613 """Uses testdata/payload_signer.sh as the external payload signer."""
614 common.OPTIONS.payload_signer = os.path.join(
615 self.testdata_dir, 'payload_signer.sh')
616 common.OPTIONS.payload_signer_args = [
617 os.path.join(self.testdata_dir, 'testkey.pk8')]
618 payload_signer = PayloadSigner()
619 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
620 signed_file = payload_signer.Sign(input_file)
621
622 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
623 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -0800624
625
626class PayloadTest(unittest.TestCase):
627
628 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800629 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baoc7b403a2018-01-30 18:19:04 -0800630 self.assertTrue(os.path.exists(self.testdata_dir))
631
632 common.OPTIONS.wipe_user_data = False
633 common.OPTIONS.payload_signer = None
634 common.OPTIONS.payload_signer_args = None
635 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
636 common.OPTIONS.key_passwords = {
637 common.OPTIONS.package_key : None,
638 }
639
640 def tearDown(self):
641 common.Cleanup()
642
643 @staticmethod
Tao Baof7140c02018-01-30 17:09:24 -0800644 def _create_payload_full(secondary=False):
645 target_file = construct_target_files(secondary)
Tao Baoc7b403a2018-01-30 18:19:04 -0800646 payload = Payload()
647 payload.Generate(target_file)
648 return payload
649
Tao Baof7140c02018-01-30 17:09:24 -0800650 @staticmethod
651 def _create_payload_incremental():
652 target_file = construct_target_files()
653 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800654 payload = Payload()
655 payload.Generate(target_file, source_file)
656 return payload
657
658 def test_Generate_full(self):
659 payload = self._create_payload_full()
660 self.assertTrue(os.path.exists(payload.payload_file))
661
662 def test_Generate_incremental(self):
663 payload = self._create_payload_incremental()
664 self.assertTrue(os.path.exists(payload.payload_file))
665
666 def test_Generate_additionalArgs(self):
Tao Baof7140c02018-01-30 17:09:24 -0800667 target_file = construct_target_files()
668 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800669 payload = Payload()
670 # This should work the same as calling payload.Generate(target_file,
671 # source_file).
672 payload.Generate(
673 target_file, additional_args=["--source_image", source_file])
674 self.assertTrue(os.path.exists(payload.payload_file))
675
676 def test_Generate_invalidInput(self):
Tao Baof7140c02018-01-30 17:09:24 -0800677 target_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800678 common.ZipDelete(target_file, 'IMAGES/vendor.img')
679 payload = Payload()
680 self.assertRaises(AssertionError, payload.Generate, target_file)
681
682 def test_Sign_full(self):
683 payload = self._create_payload_full()
684 payload.Sign(PayloadSigner())
685
686 output_file = common.MakeTempFile(suffix='.zip')
687 with zipfile.ZipFile(output_file, 'w') as output_zip:
688 payload.WriteToZip(output_zip)
689
690 import check_ota_package_signature
691 check_ota_package_signature.VerifyAbOtaPayload(
692 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
693 output_file)
694
695 def test_Sign_incremental(self):
696 payload = self._create_payload_incremental()
697 payload.Sign(PayloadSigner())
698
699 output_file = common.MakeTempFile(suffix='.zip')
700 with zipfile.ZipFile(output_file, 'w') as output_zip:
701 payload.WriteToZip(output_zip)
702
703 import check_ota_package_signature
704 check_ota_package_signature.VerifyAbOtaPayload(
705 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
706 output_file)
707
708 def test_Sign_withDataWipe(self):
709 common.OPTIONS.wipe_user_data = True
710 payload = self._create_payload_full()
711 payload.Sign(PayloadSigner())
712
713 with open(payload.payload_properties) as properties_fp:
714 self.assertIn("POWERWASH=1", properties_fp.read())
715
716 def test_Sign_badSigner(self):
717 """Tests that signing failure can be captured."""
718 payload = self._create_payload_full()
719 payload_signer = PayloadSigner()
720 payload_signer.signer_args.append('bad-option')
721 self.assertRaises(AssertionError, payload.Sign, payload_signer)
722
723 def test_WriteToZip(self):
724 payload = self._create_payload_full()
725 payload.Sign(PayloadSigner())
726
727 output_file = common.MakeTempFile(suffix='.zip')
728 with zipfile.ZipFile(output_file, 'w') as output_zip:
729 payload.WriteToZip(output_zip)
730
731 with zipfile.ZipFile(output_file) as verify_zip:
732 # First make sure we have the essential entries.
733 namelist = verify_zip.namelist()
734 self.assertIn(Payload.PAYLOAD_BIN, namelist)
735 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
736
737 # Then assert these entries are stored.
738 for entry_info in verify_zip.infolist():
739 if entry_info.filename not in (Payload.PAYLOAD_BIN,
740 Payload.PAYLOAD_PROPERTIES_TXT):
741 continue
742 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
743
744 def test_WriteToZip_unsignedPayload(self):
745 """Unsigned payloads should not be allowed to be written to zip."""
746 payload = self._create_payload_full()
747
748 output_file = common.MakeTempFile(suffix='.zip')
749 with zipfile.ZipFile(output_file, 'w') as output_zip:
750 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
751
752 # Also test with incremental payload.
753 payload = self._create_payload_incremental()
754
755 output_file = common.MakeTempFile(suffix='.zip')
756 with zipfile.ZipFile(output_file, 'w') as output_zip:
757 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
Tao Baof7140c02018-01-30 17:09:24 -0800758
759 def test_WriteToZip_secondary(self):
760 payload = self._create_payload_full(secondary=True)
761 payload.Sign(PayloadSigner())
762
763 output_file = common.MakeTempFile(suffix='.zip')
764 with zipfile.ZipFile(output_file, 'w') as output_zip:
765 payload.WriteToZip(output_zip, secondary=True)
766
767 with zipfile.ZipFile(output_file) as verify_zip:
768 # First make sure we have the essential entries.
769 namelist = verify_zip.namelist()
770 self.assertIn(Payload.SECONDARY_PAYLOAD_BIN, namelist)
771 self.assertIn(Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)
772
773 # Then assert these entries are stored.
774 for entry_info in verify_zip.infolist():
775 if entry_info.filename not in (
776 Payload.SECONDARY_PAYLOAD_BIN,
777 Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
778 continue
779 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)