blob: f1971d3b449ff81dddb6fe0fb601d7abf3772d3f [file] [log] [blame]
Dan Albert8e0178d2015-01-27 15:53:15 -08001#
2# Copyright (C) 2015 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#
Tao Baofc7e0e02018-02-13 13:54:02 -080016
Tao Baoa57ab9f2018-08-24 12:08:38 -070017import copy
Dan Albert8e0178d2015-01-27 15:53:15 -080018import os
Tao Bao17e4e612018-02-16 17:12:54 -080019import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080020import tempfile
21import time
Tianjie20dd8f22020-04-19 15:51:16 -070022import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080023import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080024from hashlib import sha1
25
Dan Albert8e0178d2015-01-27 15:53:15 -080026import common
Tao Bao04e1f012018-02-04 12:13:35 -080027import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070028import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070029from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080030from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080031
Tao Bao04e1f012018-02-04 12:13:35 -080032
Tao Bao31b08072017-11-08 15:50:59 -080033KiB = 1024
34MiB = 1024 * KiB
35GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080036
Tao Bao1c830bf2017-12-25 10:43:47 -080037
Tao Baof3282b42015-04-01 11:21:55 -070038def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080039 size = int(2 * GiB + 1)
40 block_size = 4 * KiB
41 step_size = 4 * MiB
42 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
43 for _ in range(0, size, step_size):
44 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070045 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070046
Dan Albert8e0178d2015-01-27 15:53:15 -080047
Tao Bao1c320f82019-10-04 23:25:12 -070048class BuildInfoTest(test_utils.ReleaseToolsTestCase):
49
50 TEST_INFO_DICT = {
51 'build.prop' : {
52 'ro.product.device' : 'product-device',
53 'ro.product.name' : 'product-name',
54 'ro.build.fingerprint' : 'build-fingerprint',
55 'ro.build.foo' : 'build-foo',
56 },
Daniel Normand5fe8622020-01-08 17:01:11 -080057 'system.build.prop' : {
58 'ro.product.system.brand' : 'product-brand',
59 'ro.product.system.name' : 'product-name',
60 'ro.product.system.device' : 'product-device',
61 'ro.system.build.version.release' : 'version-release',
62 'ro.system.build.id' : 'build-id',
63 'ro.system.build.version.incremental' : 'version-incremental',
64 'ro.system.build.type' : 'build-type',
65 'ro.system.build.tags' : 'build-tags',
66 'ro.system.build.foo' : 'build-foo',
67 },
Tao Bao1c320f82019-10-04 23:25:12 -070068 'vendor.build.prop' : {
Daniel Normand5fe8622020-01-08 17:01:11 -080069 'ro.product.vendor.brand' : 'vendor-product-brand',
70 'ro.product.vendor.name' : 'vendor-product-name',
71 'ro.product.vendor.device' : 'vendor-product-device',
72 'ro.vendor.build.version.release' : 'vendor-version-release',
73 'ro.vendor.build.id' : 'vendor-build-id',
74 'ro.vendor.build.version.incremental' : 'vendor-version-incremental',
75 'ro.vendor.build.type' : 'vendor-build-type',
76 'ro.vendor.build.tags' : 'vendor-build-tags',
Tao Bao1c320f82019-10-04 23:25:12 -070077 },
78 'property1' : 'value1',
79 'property2' : 4096,
80 }
81
82 TEST_INFO_DICT_USES_OEM_PROPS = {
83 'build.prop' : {
84 'ro.product.name' : 'product-name',
85 'ro.build.thumbprint' : 'build-thumbprint',
86 'ro.build.bar' : 'build-bar',
87 },
88 'vendor.build.prop' : {
89 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
90 },
91 'property1' : 'value1',
92 'property2' : 4096,
93 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
94 }
95
96 TEST_OEM_DICTS = [
97 {
98 'ro.product.brand' : 'brand1',
99 'ro.product.device' : 'device1',
100 },
101 {
102 'ro.product.brand' : 'brand2',
103 'ro.product.device' : 'device2',
104 },
105 {
106 'ro.product.brand' : 'brand3',
107 'ro.product.device' : 'device3',
108 },
109 ]
110
Steven Laver8e2086e2020-04-27 16:26:31 -0700111 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
112 'build.prop' : {
113 'ro.build.fingerprint' : 'build-fingerprint',
114 'ro.product.property_source_order' :
115 'product,odm,vendor,system_ext,system',
116 },
117 'system.build.prop' : {
118 'ro.product.system.device' : 'system-product-device',
119 },
120 'vendor.build.prop' : {
121 'ro.product.vendor.device' : 'vendor-product-device',
122 },
123 }
124
125 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
126 'build.prop' : {
127 'ro.build.fingerprint' : 'build-fingerprint',
128 'ro.product.property_source_order' :
129 'product,product_services,odm,vendor,system',
130 'ro.build.version.release' : '10',
131 'ro.build.version.codename' : 'REL',
132 },
133 'system.build.prop' : {
134 'ro.product.system.device' : 'system-product-device',
135 },
136 'vendor.build.prop' : {
137 'ro.product.vendor.device' : 'vendor-product-device',
138 },
139 }
140
141 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
142 'build.prop' : {
143 'ro.product.device' : 'product-device',
144 'ro.build.fingerprint' : 'build-fingerprint',
145 'ro.build.version.release' : '9',
146 'ro.build.version.codename' : 'REL',
147 },
148 'system.build.prop' : {
149 'ro.product.system.device' : 'system-product-device',
150 },
151 'vendor.build.prop' : {
152 'ro.product.vendor.device' : 'vendor-product-device',
153 },
154 }
155
Tao Bao1c320f82019-10-04 23:25:12 -0700156 def test_init(self):
157 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
158 self.assertEqual('product-device', target_info.device)
159 self.assertEqual('build-fingerprint', target_info.fingerprint)
160 self.assertFalse(target_info.is_ab)
161 self.assertIsNone(target_info.oem_props)
162
163 def test_init_with_oem_props(self):
164 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
165 self.TEST_OEM_DICTS)
166 self.assertEqual('device1', target_info.device)
167 self.assertEqual('brand1/product-name/device1:build-thumbprint',
168 target_info.fingerprint)
169
170 # Swap the order in oem_dicts, which would lead to different BuildInfo.
171 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
172 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
173 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
174 oem_dicts)
175 self.assertEqual('device3', target_info.device)
176 self.assertEqual('brand3/product-name/device3:build-thumbprint',
177 target_info.fingerprint)
178
179 # Missing oem_dict should be rejected.
180 self.assertRaises(AssertionError, common.BuildInfo,
181 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
182
183 def test_init_badFingerprint(self):
184 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
185 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
186 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
187
188 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
189 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
190
191 def test___getitem__(self):
192 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
193 self.assertEqual('value1', target_info['property1'])
194 self.assertEqual(4096, target_info['property2'])
195 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
196
197 def test___getitem__with_oem_props(self):
198 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
199 self.TEST_OEM_DICTS)
200 self.assertEqual('value1', target_info['property1'])
201 self.assertEqual(4096, target_info['property2'])
202 self.assertRaises(KeyError,
203 lambda: target_info['build.prop']['ro.build.foo'])
204
205 def test___setitem__(self):
206 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
207 self.assertEqual('value1', target_info['property1'])
208 target_info['property1'] = 'value2'
209 self.assertEqual('value2', target_info['property1'])
210
211 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
212 target_info['build.prop']['ro.build.foo'] = 'build-bar'
213 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
214
215 def test_get(self):
216 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
217 self.assertEqual('value1', target_info.get('property1'))
218 self.assertEqual(4096, target_info.get('property2'))
219 self.assertEqual(4096, target_info.get('property2', 1024))
220 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
221 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
222
223 def test_get_with_oem_props(self):
224 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
225 self.TEST_OEM_DICTS)
226 self.assertEqual('value1', target_info.get('property1'))
227 self.assertEqual(4096, target_info.get('property2'))
228 self.assertEqual(4096, target_info.get('property2', 1024))
229 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
230 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
231 self.assertRaises(KeyError,
232 lambda: target_info.get('build.prop')['ro.build.foo'])
233
234 def test_items(self):
235 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
236 items = target_info.items()
237 self.assertIn(('property1', 'value1'), items)
238 self.assertIn(('property2', 4096), items)
239
240 def test_GetBuildProp(self):
241 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
242 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
243 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
244 'ro.build.nonexistent')
245
246 def test_GetBuildProp_with_oem_props(self):
247 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
248 self.TEST_OEM_DICTS)
249 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
250 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
251 'ro.build.nonexistent')
252
Daniel Normand5fe8622020-01-08 17:01:11 -0800253 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700254 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800255 self.assertEqual(
256 target_info.GetPartitionFingerprint('vendor'),
257 'vendor-product-brand/vendor-product-name/vendor-product-device'
258 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
259 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700260
Daniel Normand5fe8622020-01-08 17:01:11 -0800261 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700262 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800263 self.assertEqual(
264 target_info.GetPartitionFingerprint('system_other'),
265 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700266
Daniel Normand5fe8622020-01-08 17:01:11 -0800267 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
268 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
269 info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
270 target_info = common.BuildInfo(info_dict, None)
271 self.assertEqual(
272 target_info.GetPartitionFingerprint('vendor'),
273 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700274
275 def test_WriteMountOemScript(self):
276 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
277 self.TEST_OEM_DICTS)
278 script_writer = test_utils.MockScriptWriter()
279 target_info.WriteMountOemScript(script_writer)
280 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
281
282 def test_WriteDeviceAssertions(self):
283 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
284 script_writer = test_utils.MockScriptWriter()
285 target_info.WriteDeviceAssertions(script_writer, False)
286 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
287
288 def test_WriteDeviceAssertions_with_oem_props(self):
289 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
290 self.TEST_OEM_DICTS)
291 script_writer = test_utils.MockScriptWriter()
292 target_info.WriteDeviceAssertions(script_writer, False)
293 self.assertEqual(
294 [
295 ('AssertOemProperty', 'ro.product.device',
296 ['device1', 'device2', 'device3'], False),
297 ('AssertOemProperty', 'ro.product.brand',
298 ['brand1', 'brand2', 'brand3'], False),
299 ],
300 script_writer.lines)
301
Steven Laver8e2086e2020-04-27 16:26:31 -0700302 def test_ResolveRoProductProperty_FromVendor(self):
303 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
304 info = common.BuildInfo(info_dict, None)
305 self.assertEqual('vendor-product-device',
306 info.GetBuildProp('ro.product.device'))
307
308 def test_ResolveRoProductProperty_FromSystem(self):
309 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
310 del info_dict['vendor.build.prop']['ro.product.vendor.device']
311 info = common.BuildInfo(info_dict, None)
312 self.assertEqual('system-product-device',
313 info.GetBuildProp('ro.product.device'))
314
315 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
316 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
317 info_dict['build.prop']['ro.product.property_source_order'] = 'bad-source'
318 with self.assertRaisesRegexp(common.ExternalError,
319 'Invalid ro.product.property_source_order'):
320 info = common.BuildInfo(info_dict, None)
321 info.GetBuildProp('ro.product.device')
322
323 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
324 info_dict = copy.deepcopy(
325 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
326 info = common.BuildInfo(info_dict, None)
327 self.assertEqual('vendor-product-device',
328 info.GetBuildProp('ro.product.device'))
329
330 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
331 info_dict = copy.deepcopy(
332 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
333 info = common.BuildInfo(info_dict, None)
334 self.assertEqual('product-device',
335 info.GetBuildProp('ro.product.device'))
336
Tao Bao1c320f82019-10-04 23:25:12 -0700337
Tao Bao65b94e92018-10-11 21:57:26 -0700338class CommonZipTest(test_utils.ReleaseToolsTestCase):
339
Tao Bao31b08072017-11-08 15:50:59 -0800340 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700341 test_file_name=None, expected_stat=None, expected_mode=0o644,
342 expected_compress_type=zipfile.ZIP_STORED):
343 # Verify the stat if present.
344 if test_file_name is not None:
345 new_stat = os.stat(test_file_name)
346 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
347 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
348
349 # Reopen the zip file to verify.
350 zip_file = zipfile.ZipFile(zip_file_name, "r")
351
352 # Verify the timestamp.
353 info = zip_file.getinfo(arcname)
354 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
355
356 # Verify the file mode.
357 mode = (info.external_attr >> 16) & 0o777
358 self.assertEqual(mode, expected_mode)
359
360 # Verify the compress type.
361 self.assertEqual(info.compress_type, expected_compress_type)
362
363 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800364 entry = zip_file.open(arcname)
365 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700366 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800367 sha1_hash.update(chunk)
368 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700369 self.assertIsNone(zip_file.testzip())
370
Dan Albert8e0178d2015-01-27 15:53:15 -0800371 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
372 extra_zipwrite_args = dict(extra_zipwrite_args or {})
373
374 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800375 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700376
377 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800378 zip_file_name = zip_file.name
379
380 # File names within an archive strip the leading slash.
381 arcname = extra_zipwrite_args.get("arcname", test_file_name)
382 if arcname[0] == "/":
383 arcname = arcname[1:]
384
385 zip_file.close()
386 zip_file = zipfile.ZipFile(zip_file_name, "w")
387
388 try:
Tao Bao31b08072017-11-08 15:50:59 -0800389 sha1_hash = sha1()
390 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700391 sha1_hash.update(bytes(data))
392 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800393 test_file.close()
394
Tao Baof3282b42015-04-01 11:21:55 -0700395 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800396 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700397 expected_compress_type = extra_zipwrite_args.get("compress_type",
398 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800399 time.sleep(5) # Make sure the atime/mtime will change measurably.
400
401 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700402 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800403
Tao Bao31b08072017-11-08 15:50:59 -0800404 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
405 test_file_name, expected_stat, expected_mode,
406 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800407 finally:
408 os.remove(test_file_name)
409 os.remove(zip_file_name)
410
Tao Baof3282b42015-04-01 11:21:55 -0700411 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
412 extra_args = dict(extra_args or {})
413
414 zip_file = tempfile.NamedTemporaryFile(delete=False)
415 zip_file_name = zip_file.name
416 zip_file.close()
417
418 zip_file = zipfile.ZipFile(zip_file_name, "w")
419
420 try:
421 expected_compress_type = extra_args.get("compress_type",
422 zipfile.ZIP_STORED)
423 time.sleep(5) # Make sure the atime/mtime will change measurably.
424
425 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700426 arcname = zinfo_or_arcname
427 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700428 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700429 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700430 if zinfo_or_arcname.external_attr:
431 zinfo_perms = zinfo_or_arcname.external_attr >> 16
432 else:
433 zinfo_perms = 0o600
434 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700435
Tao Bao58c1b962015-05-20 09:32:18 -0700436 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700437 common.ZipClose(zip_file)
438
Tao Bao31b08072017-11-08 15:50:59 -0800439 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700440 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700441 expected_compress_type=expected_compress_type)
442 finally:
443 os.remove(zip_file_name)
444
445 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
446 extra_args = dict(extra_args or {})
447
448 zip_file = tempfile.NamedTemporaryFile(delete=False)
449 zip_file_name = zip_file.name
450
451 test_file = tempfile.NamedTemporaryFile(delete=False)
452 test_file_name = test_file.name
453
454 arcname_large = test_file_name
455 arcname_small = "bar"
456
457 # File names within an archive strip the leading slash.
458 if arcname_large[0] == "/":
459 arcname_large = arcname_large[1:]
460
461 zip_file.close()
462 zip_file = zipfile.ZipFile(zip_file_name, "w")
463
464 try:
Tao Bao31b08072017-11-08 15:50:59 -0800465 sha1_hash = sha1()
466 for data in large:
467 sha1_hash.update(data)
468 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700469 test_file.close()
470
471 expected_stat = os.stat(test_file_name)
472 expected_mode = 0o644
473 expected_compress_type = extra_args.get("compress_type",
474 zipfile.ZIP_STORED)
475 time.sleep(5) # Make sure the atime/mtime will change measurably.
476
477 common.ZipWrite(zip_file, test_file_name, **extra_args)
478 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
479 common.ZipClose(zip_file)
480
481 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800482 self._verify(zip_file, zip_file_name, arcname_large,
483 sha1_hash.hexdigest(), test_file_name, expected_stat,
484 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700485
486 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800487 self._verify(zip_file, zip_file_name, arcname_small,
488 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700489 expected_compress_type=expected_compress_type)
490 finally:
491 os.remove(zip_file_name)
492 os.remove(test_file_name)
493
494 def _test_reset_ZIP64_LIMIT(self, func, *args):
495 default_limit = (1 << 31) - 1
496 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
497 func(*args)
498 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
499
Dan Albert8e0178d2015-01-27 15:53:15 -0800500 def test_ZipWrite(self):
501 file_contents = os.urandom(1024)
502 self._test_ZipWrite(file_contents)
503
504 def test_ZipWrite_with_opts(self):
505 file_contents = os.urandom(1024)
506 self._test_ZipWrite(file_contents, {
507 "arcname": "foobar",
508 "perms": 0o777,
509 "compress_type": zipfile.ZIP_DEFLATED,
510 })
Tao Baof3282b42015-04-01 11:21:55 -0700511 self._test_ZipWrite(file_contents, {
512 "arcname": "foobar",
513 "perms": 0o700,
514 "compress_type": zipfile.ZIP_STORED,
515 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800516
517 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700518 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800519 self._test_ZipWrite(file_contents, {
520 "compress_type": zipfile.ZIP_DEFLATED,
521 })
522
523 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700524 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
525
526 def test_ZipWriteStr(self):
527 random_string = os.urandom(1024)
528 # Passing arcname
529 self._test_ZipWriteStr("foo", random_string)
530
531 # Passing zinfo
532 zinfo = zipfile.ZipInfo(filename="foo")
533 self._test_ZipWriteStr(zinfo, random_string)
534
535 # Timestamp in the zinfo should be overwritten.
536 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
537 self._test_ZipWriteStr(zinfo, random_string)
538
539 def test_ZipWriteStr_with_opts(self):
540 random_string = os.urandom(1024)
541 # Passing arcname
542 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700543 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700544 "compress_type": zipfile.ZIP_DEFLATED,
545 })
Tao Bao58c1b962015-05-20 09:32:18 -0700546 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700547 "compress_type": zipfile.ZIP_STORED,
548 })
549
550 # Passing zinfo
551 zinfo = zipfile.ZipInfo(filename="foo")
552 self._test_ZipWriteStr(zinfo, random_string, {
553 "compress_type": zipfile.ZIP_DEFLATED,
554 })
555 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700556 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700557 "compress_type": zipfile.ZIP_STORED,
558 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700559 self._test_ZipWriteStr(zinfo, random_string, {
560 "perms": 0o000,
561 "compress_type": zipfile.ZIP_STORED,
562 })
Tao Baof3282b42015-04-01 11:21:55 -0700563
564 def test_ZipWriteStr_large_file(self):
565 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
566 # the workaround. We will only test the case of writing a string into a
567 # large archive.
568 long_string = get_2gb_string()
569 short_string = os.urandom(1024)
570 self._test_ZipWriteStr_large_file(long_string, short_string, {
571 "compress_type": zipfile.ZIP_DEFLATED,
572 })
573
574 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700575 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700576 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700577 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700578
579 def test_bug21309935(self):
580 zip_file = tempfile.NamedTemporaryFile(delete=False)
581 zip_file_name = zip_file.name
582 zip_file.close()
583
584 try:
585 random_string = os.urandom(1024)
586 zip_file = zipfile.ZipFile(zip_file_name, "w")
587 # Default perms should be 0o644 when passing the filename.
588 common.ZipWriteStr(zip_file, "foo", random_string)
589 # Honor the specified perms.
590 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
591 # The perms in zinfo should be untouched.
592 zinfo = zipfile.ZipInfo(filename="baz")
593 zinfo.external_attr = 0o740 << 16
594 common.ZipWriteStr(zip_file, zinfo, random_string)
595 # Explicitly specified perms has the priority.
596 zinfo = zipfile.ZipInfo(filename="qux")
597 zinfo.external_attr = 0o700 << 16
598 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
599 common.ZipClose(zip_file)
600
Tao Bao31b08072017-11-08 15:50:59 -0800601 self._verify(zip_file, zip_file_name, "foo",
602 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700603 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800604 self._verify(zip_file, zip_file_name, "bar",
605 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700606 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800607 self._verify(zip_file, zip_file_name, "baz",
608 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700609 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800610 self._verify(zip_file, zip_file_name, "qux",
611 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700612 expected_mode=0o400)
613 finally:
614 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700615
Tao Bao82490d32019-04-09 00:12:30 -0700616 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800617 def test_ZipDelete(self):
618 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
619 output_zip = zipfile.ZipFile(zip_file.name, 'w',
620 compression=zipfile.ZIP_DEFLATED)
621 with tempfile.NamedTemporaryFile() as entry_file:
622 entry_file.write(os.urandom(1024))
623 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
624 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
625 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
626 common.ZipClose(output_zip)
627 zip_file.close()
628
629 try:
630 common.ZipDelete(zip_file.name, 'Test2')
631 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
632 entries = check_zip.namelist()
633 self.assertTrue('Test1' in entries)
634 self.assertFalse('Test2' in entries)
635 self.assertTrue('Test3' in entries)
636
Tao Bao986ee862018-10-04 15:46:16 -0700637 self.assertRaises(
638 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800639 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
640 entries = check_zip.namelist()
641 self.assertTrue('Test1' in entries)
642 self.assertFalse('Test2' in entries)
643 self.assertTrue('Test3' in entries)
644
645 common.ZipDelete(zip_file.name, ['Test3'])
646 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
647 entries = check_zip.namelist()
648 self.assertTrue('Test1' in entries)
649 self.assertFalse('Test2' in entries)
650 self.assertFalse('Test3' in entries)
651
652 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
653 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
654 entries = check_zip.namelist()
655 self.assertFalse('Test1' in entries)
656 self.assertFalse('Test2' in entries)
657 self.assertFalse('Test3' in entries)
658 finally:
659 os.remove(zip_file.name)
660
Tao Bao0ff15de2019-03-20 11:26:06 -0700661 @staticmethod
662 def _test_UnzipTemp_createZipFile():
663 zip_file = common.MakeTempFile(suffix='.zip')
664 output_zip = zipfile.ZipFile(
665 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
666 contents = os.urandom(1024)
667 with tempfile.NamedTemporaryFile() as entry_file:
668 entry_file.write(contents)
669 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
670 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
671 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
672 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
673 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
674 common.ZipClose(output_zip)
675 common.ZipClose(output_zip)
676 return zip_file
677
Tao Bao82490d32019-04-09 00:12:30 -0700678 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700679 def test_UnzipTemp(self):
680 zip_file = self._test_UnzipTemp_createZipFile()
681 unzipped_dir = common.UnzipTemp(zip_file)
682 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
683 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
684 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
685 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
686 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
687
Tao Bao82490d32019-04-09 00:12:30 -0700688 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700689 def test_UnzipTemp_withPatterns(self):
690 zip_file = self._test_UnzipTemp_createZipFile()
691
692 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
693 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
694 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
695 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
696 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
697 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
698
699 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
700 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
701 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
702 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
703 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
704 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
705
706 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
707 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
708 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
709 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
710 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
711 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
712
713 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
714 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
715 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
716 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
717 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
718 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
719
720 def test_UnzipTemp_withEmptyPatterns(self):
721 zip_file = self._test_UnzipTemp_createZipFile()
722 unzipped_dir = common.UnzipTemp(zip_file, [])
723 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
724 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
725 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
726 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
727 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
728
Tao Bao82490d32019-04-09 00:12:30 -0700729 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700730 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
731 zip_file = self._test_UnzipTemp_createZipFile()
732 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
733 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
734 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
735 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
736 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
737 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
738
739 def test_UnzipTemp_withNoMatchingPatterns(self):
740 zip_file = self._test_UnzipTemp_createZipFile()
741 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
742 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
743 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
744 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
745 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
746 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
747
Tao Bao89d7ab22017-12-14 17:05:33 -0800748
Tao Bao65b94e92018-10-11 21:57:26 -0700749class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800750 """Tests the APK utils related functions."""
751
752 APKCERTS_TXT1 = (
753 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
754 ' private_key="certs/devkey.pk8"\n'
755 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700756 ' certificate="build/make/target/product/security/platform.x509.pem"'
757 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800758 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
759 )
760
761 APKCERTS_CERTMAP1 = {
762 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700763 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800764 'TV.apk' : 'PRESIGNED',
765 }
766
767 APKCERTS_TXT2 = (
768 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
769 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
770 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
771 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
772 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
773 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
774 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
775 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
776 )
777
778 APKCERTS_CERTMAP2 = {
779 'Compressed1.apk' : 'certs/compressed1',
780 'Compressed2a.apk' : 'certs/compressed2',
781 'Compressed2b.apk' : 'certs/compressed2',
782 'Compressed3.apk' : 'certs/compressed3',
783 }
784
785 APKCERTS_TXT3 = (
786 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
787 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
788 )
789
790 APKCERTS_CERTMAP3 = {
791 'Compressed4.apk' : 'certs/compressed4',
792 }
793
Bill Peckham5c7b0342020-04-03 15:36:23 -0700794 # Test parsing with no optional fields, both optional fields, and only the
795 # partition optional field.
796 APKCERTS_TXT4 = (
797 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
798 ' private_key="certs/devkey.pk8"\n'
799 'name="Settings.apk"'
800 ' certificate="build/make/target/product/security/platform.x509.pem"'
801 ' private_key="build/make/target/product/security/platform.pk8"'
802 ' compressed="gz" partition="system"\n'
803 'name="TV.apk" certificate="PRESIGNED" private_key=""'
804 ' partition="product"\n'
805 )
806
807 APKCERTS_CERTMAP4 = {
808 'RecoveryLocalizer.apk' : 'certs/devkey',
809 'Settings.apk' : 'build/make/target/product/security/platform',
810 'TV.apk' : 'PRESIGNED',
811 }
812
Tao Bao17e4e612018-02-16 17:12:54 -0800813 def setUp(self):
814 self.testdata_dir = test_utils.get_testdata_dir()
815
Tao Bao818ddf52018-01-05 11:17:34 -0800816 @staticmethod
817 def _write_apkcerts_txt(apkcerts_txt, additional=None):
818 if additional is None:
819 additional = []
820 target_files = common.MakeTempFile(suffix='.zip')
821 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
822 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
823 for entry in additional:
824 target_files_zip.writestr(entry, '')
825 return target_files
826
827 def test_ReadApkCerts_NoncompressedApks(self):
828 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
829 with zipfile.ZipFile(target_files, 'r') as input_zip:
830 certmap, ext = common.ReadApkCerts(input_zip)
831
832 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
833 self.assertIsNone(ext)
834
835 def test_ReadApkCerts_CompressedApks(self):
836 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
837 # not stored in '.gz' format, so it shouldn't be considered as installed.
838 target_files = self._write_apkcerts_txt(
839 self.APKCERTS_TXT2,
840 ['Compressed1.apk.gz', 'Compressed3.apk'])
841
842 with zipfile.ZipFile(target_files, 'r') as input_zip:
843 certmap, ext = common.ReadApkCerts(input_zip)
844
845 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
846 self.assertEqual('.gz', ext)
847
848 # Alternative case with '.xz'.
849 target_files = self._write_apkcerts_txt(
850 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
851
852 with zipfile.ZipFile(target_files, 'r') as input_zip:
853 certmap, ext = common.ReadApkCerts(input_zip)
854
855 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
856 self.assertEqual('.xz', ext)
857
858 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
859 target_files = self._write_apkcerts_txt(
860 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
861 ['Compressed1.apk.gz', 'Compressed3.apk'])
862
863 with zipfile.ZipFile(target_files, 'r') as input_zip:
864 certmap, ext = common.ReadApkCerts(input_zip)
865
866 certmap_merged = self.APKCERTS_CERTMAP1.copy()
867 certmap_merged.update(self.APKCERTS_CERTMAP2)
868 self.assertDictEqual(certmap_merged, certmap)
869 self.assertEqual('.gz', ext)
870
871 def test_ReadApkCerts_MultipleCompressionMethods(self):
872 target_files = self._write_apkcerts_txt(
873 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
874 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
875
876 with zipfile.ZipFile(target_files, 'r') as input_zip:
877 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
878
879 def test_ReadApkCerts_MismatchingKeys(self):
880 malformed_apkcerts_txt = (
881 'name="App1.apk" certificate="certs/cert1.x509.pem"'
882 ' private_key="certs/cert2.pk8"\n'
883 )
884 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
885
886 with zipfile.ZipFile(target_files, 'r') as input_zip:
887 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
888
Bill Peckham5c7b0342020-04-03 15:36:23 -0700889 def test_ReadApkCerts_WithWithoutOptionalFields(self):
890 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
891 with zipfile.ZipFile(target_files, 'r') as input_zip:
892 certmap, ext = common.ReadApkCerts(input_zip)
893
894 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
895 self.assertIsNone(ext)
896
Tao Bao04e1f012018-02-04 12:13:35 -0800897 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800898 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
899 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800900 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800901 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
902
903 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800904 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800905 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
906
Tao Bao82490d32019-04-09 00:12:30 -0700907 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700908 def test_ExtractAvbPublicKey(self):
909 privkey = os.path.join(self.testdata_dir, 'testkey.key')
910 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700911 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
912 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
913 with open(extracted_from_privkey, 'rb') as privkey_fp, \
914 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700915 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
916
Tao Bao17e4e612018-02-16 17:12:54 -0800917 def test_ParseCertificate(self):
918 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
919
920 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800921 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
922 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800923 expected, _ = proc.communicate()
924 self.assertEqual(0, proc.returncode)
925
926 with open(cert) as cert_fp:
927 actual = common.ParseCertificate(cert_fp.read())
928 self.assertEqual(expected, actual)
929
Tao Bao82490d32019-04-09 00:12:30 -0700930 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700931 def test_GetMinSdkVersion(self):
932 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
933 self.assertEqual('24', common.GetMinSdkVersion(test_app))
934
Tao Bao82490d32019-04-09 00:12:30 -0700935 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700936 def test_GetMinSdkVersion_invalidInput(self):
937 self.assertRaises(
938 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
939
Tao Bao82490d32019-04-09 00:12:30 -0700940 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700941 def test_GetMinSdkVersionInt(self):
942 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
943 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
944
Tao Bao82490d32019-04-09 00:12:30 -0700945 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700946 def test_GetMinSdkVersionInt_invalidInput(self):
947 self.assertRaises(
948 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
949 {})
950
Tao Bao818ddf52018-01-05 11:17:34 -0800951
Tao Bao65b94e92018-10-11 21:57:26 -0700952class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800953
Tao Bao02a08592018-07-22 12:40:45 -0700954 def setUp(self):
955 self.testdata_dir = test_utils.get_testdata_dir()
956
Tao Bao82490d32019-04-09 00:12:30 -0700957 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800958 def test_GetSparseImage_emptyBlockMapFile(self):
959 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
960 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
961 target_files_zip.write(
962 test_utils.construct_sparse_image([
963 (0xCAC1, 6),
964 (0xCAC3, 3),
965 (0xCAC1, 4)]),
966 arcname='IMAGES/system.img')
967 target_files_zip.writestr('IMAGES/system.map', '')
968 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
969 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
970
Tao Baodba59ee2018-01-09 13:21:02 -0800971 tempdir = common.UnzipTemp(target_files)
972 with zipfile.ZipFile(target_files, 'r') as input_zip:
973 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800974
975 self.assertDictEqual(
976 {
977 '__COPY': RangeSet("0"),
978 '__NONZERO-0': RangeSet("1-5 9-12"),
979 },
980 sparse_image.file_map)
981
Tao Baob2de7d92019-04-10 10:01:47 -0700982 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800983 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700984 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
985 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800986 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700987 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
988 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800989
Tao Bao82490d32019-04-09 00:12:30 -0700990 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800991 def test_GetSparseImage_missingBlockMapFile(self):
992 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
993 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
994 target_files_zip.write(
995 test_utils.construct_sparse_image([
996 (0xCAC1, 6),
997 (0xCAC3, 3),
998 (0xCAC1, 4)]),
999 arcname='IMAGES/system.img')
1000 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1001 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1002
Tao Baodba59ee2018-01-09 13:21:02 -08001003 tempdir = common.UnzipTemp(target_files)
1004 with zipfile.ZipFile(target_files, 'r') as input_zip:
1005 self.assertRaises(
1006 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1007 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001008
Tao Bao82490d32019-04-09 00:12:30 -07001009 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001010 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1011 """Tests the case of having overlapping blocks but disallowed."""
1012 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1013 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1014 target_files_zip.write(
1015 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1016 arcname='IMAGES/system.img')
1017 # Block 10 is shared between two files.
1018 target_files_zip.writestr(
1019 'IMAGES/system.map',
1020 '\n'.join([
1021 '/system/file1 1-5 9-10',
1022 '/system/file2 10-12']))
1023 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1024 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1025
Tao Baodba59ee2018-01-09 13:21:02 -08001026 tempdir = common.UnzipTemp(target_files)
1027 with zipfile.ZipFile(target_files, 'r') as input_zip:
1028 self.assertRaises(
1029 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1030 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001031
Tao Bao82490d32019-04-09 00:12:30 -07001032 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001033 def test_GetSparseImage_sharedBlocks_allowed(self):
1034 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1035 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1036 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1037 # Construct an image with a care_map of "0-5 9-12".
1038 target_files_zip.write(
1039 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1040 arcname='IMAGES/system.img')
1041 # Block 10 is shared between two files.
1042 target_files_zip.writestr(
1043 'IMAGES/system.map',
1044 '\n'.join([
1045 '/system/file1 1-5 9-10',
1046 '/system/file2 10-12']))
1047 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1048 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1049
Tao Baodba59ee2018-01-09 13:21:02 -08001050 tempdir = common.UnzipTemp(target_files)
1051 with zipfile.ZipFile(target_files, 'r') as input_zip:
1052 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001053
1054 self.assertDictEqual(
1055 {
1056 '__COPY': RangeSet("0"),
1057 '__NONZERO-0': RangeSet("6-8 13-15"),
1058 '/system/file1': RangeSet("1-5 9-10"),
1059 '/system/file2': RangeSet("11-12"),
1060 },
1061 sparse_image.file_map)
1062
1063 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1064 # 'incomplete'.
1065 self.assertTrue(
1066 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1067 self.assertNotIn(
1068 'incomplete', sparse_image.file_map['/system/file2'].extra)
1069
Tao Baoa264fef2019-10-06 21:55:20 -07001070 # '/system/file1' will only contain one field -- a copy of the input text.
1071 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1072
1073 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001074 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1075 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001076
Tao Bao82490d32019-04-09 00:12:30 -07001077 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001078 def test_GetSparseImage_incompleteRanges(self):
1079 """Tests the case of ext4 images with holes."""
1080 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1081 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1082 target_files_zip.write(
1083 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1084 arcname='IMAGES/system.img')
1085 target_files_zip.writestr(
1086 'IMAGES/system.map',
1087 '\n'.join([
1088 '/system/file1 1-5 9-10',
1089 '/system/file2 11-12']))
1090 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1091 # '/system/file2' has less blocks listed (2) than actual (3).
1092 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1093
Tao Baodba59ee2018-01-09 13:21:02 -08001094 tempdir = common.UnzipTemp(target_files)
1095 with zipfile.ZipFile(target_files, 'r') as input_zip:
1096 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001097
Tao Baoa264fef2019-10-06 21:55:20 -07001098 self.assertEqual(
1099 '1-5 9-10',
1100 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001101 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1102
Tao Bao82490d32019-04-09 00:12:30 -07001103 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001104 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1105 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1106 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1107 target_files_zip.write(
1108 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1109 arcname='IMAGES/system.img')
1110 target_files_zip.writestr(
1111 'IMAGES/system.map',
1112 '\n'.join([
1113 '//system/file1 1-5 9-10',
1114 '//system/file2 11-12',
1115 '/system/app/file3 13-15']))
1116 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1117 # '/system/file2' has less blocks listed (2) than actual (3).
1118 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1119 # '/system/app/file3' has less blocks listed (3) than actual (4).
1120 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1121
1122 tempdir = common.UnzipTemp(target_files)
1123 with zipfile.ZipFile(target_files, 'r') as input_zip:
1124 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1125
Tao Baoa264fef2019-10-06 21:55:20 -07001126 self.assertEqual(
1127 '1-5 9-10',
1128 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001129 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1130 self.assertTrue(
1131 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1132
Tao Bao82490d32019-04-09 00:12:30 -07001133 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001134 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1135 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1136 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1137 target_files_zip.write(
1138 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1139 arcname='IMAGES/system.img')
1140 target_files_zip.writestr(
1141 'IMAGES/system.map',
1142 '\n'.join([
1143 '//system/file1 1-5 9-10',
1144 '//init.rc 13-15']))
1145 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1146 # '/init.rc' has less blocks listed (3) than actual (4).
1147 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1148
1149 tempdir = common.UnzipTemp(target_files)
1150 with zipfile.ZipFile(target_files, 'r') as input_zip:
1151 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1152
Tao Baoa264fef2019-10-06 21:55:20 -07001153 self.assertEqual(
1154 '1-5 9-10',
1155 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001156 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1157
Tao Bao82490d32019-04-09 00:12:30 -07001158 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001159 def test_GetSparseImage_fileNotFound(self):
1160 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1161 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1162 target_files_zip.write(
1163 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1164 arcname='IMAGES/system.img')
1165 target_files_zip.writestr(
1166 'IMAGES/system.map',
1167 '\n'.join([
1168 '//system/file1 1-5 9-10',
1169 '//system/file2 11-12']))
1170 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1171
1172 tempdir = common.UnzipTemp(target_files)
1173 with zipfile.ZipFile(target_files, 'r') as input_zip:
1174 self.assertRaises(
1175 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1176 False)
1177
Tao Bao82490d32019-04-09 00:12:30 -07001178 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001179 def test_GetAvbChainedPartitionArg(self):
1180 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1181 info_dict = {
1182 'avb_avbtool': 'avbtool',
1183 'avb_system_key_path': pubkey,
1184 'avb_system_rollback_index_location': 2,
1185 }
1186 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1187 self.assertEqual(3, len(args))
1188 self.assertEqual('system', args[0])
1189 self.assertEqual('2', args[1])
1190 self.assertTrue(os.path.exists(args[2]))
1191
Tao Bao82490d32019-04-09 00:12:30 -07001192 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001193 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1194 key = os.path.join(self.testdata_dir, 'testkey.key')
1195 info_dict = {
1196 'avb_avbtool': 'avbtool',
1197 'avb_product_key_path': key,
1198 'avb_product_rollback_index_location': 2,
1199 }
1200 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1201 self.assertEqual(3, len(args))
1202 self.assertEqual('product', args[0])
1203 self.assertEqual('2', args[1])
1204 self.assertTrue(os.path.exists(args[2]))
1205
Tao Bao82490d32019-04-09 00:12:30 -07001206 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001207 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1208 info_dict = {
1209 'avb_avbtool': 'avbtool',
1210 'avb_system_key_path': 'does-not-exist',
1211 'avb_system_rollback_index_location': 2,
1212 }
1213 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1214 args = common.GetAvbChainedPartitionArg(
1215 'system', info_dict, pubkey).split(':')
1216 self.assertEqual(3, len(args))
1217 self.assertEqual('system', args[0])
1218 self.assertEqual('2', args[1])
1219 self.assertTrue(os.path.exists(args[2]))
1220
Tao Bao82490d32019-04-09 00:12:30 -07001221 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001222 def test_GetAvbChainedPartitionArg_invalidKey(self):
1223 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1224 info_dict = {
1225 'avb_avbtool': 'avbtool',
1226 'avb_system_key_path': pubkey,
1227 'avb_system_rollback_index_location': 2,
1228 }
1229 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001230 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1231 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001232
Tao Baoa57ab9f2018-08-24 12:08:38 -07001233 INFO_DICT_DEFAULT = {
1234 'recovery_api_version': 3,
1235 'fstab_version': 2,
1236 'system_root_image': 'true',
1237 'no_recovery' : 'true',
1238 'recovery_as_boot': 'true',
1239 }
1240
Daniel Norman4cc9df62019-07-18 10:11:07 -07001241 def test_LoadListFromFile(self):
1242 file_path = os.path.join(self.testdata_dir,
1243 'merge_config_framework_item_list')
1244 contents = common.LoadListFromFile(file_path)
1245 expected_contents = [
1246 'META/apkcerts.txt',
1247 'META/filesystem_config.txt',
1248 'META/root_filesystem_config.txt',
1249 'META/system_manifest.xml',
1250 'META/system_matrix.xml',
1251 'META/update_engine_config.txt',
1252 'PRODUCT/*',
1253 'ROOT/*',
1254 'SYSTEM/*',
1255 ]
1256 self.assertEqual(sorted(contents), sorted(expected_contents))
1257
Tao Baoa57ab9f2018-08-24 12:08:38 -07001258 @staticmethod
1259 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1260 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1261 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1262 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001263 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001264 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1265
1266 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1267 if info_dict.get('system_root_image') == 'true':
1268 fstab_values = FSTAB_TEMPLATE.format('/')
1269 else:
1270 fstab_values = FSTAB_TEMPLATE.format('/system')
1271 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001272
1273 common.ZipWriteStr(
1274 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001275 return target_files
1276
1277 def test_LoadInfoDict(self):
1278 target_files = self._test_LoadInfoDict_createTargetFiles(
1279 self.INFO_DICT_DEFAULT,
1280 'BOOT/RAMDISK/system/etc/recovery.fstab')
1281 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1282 loaded_dict = common.LoadInfoDict(target_files_zip)
1283 self.assertEqual(3, loaded_dict['recovery_api_version'])
1284 self.assertEqual(2, loaded_dict['fstab_version'])
1285 self.assertIn('/', loaded_dict['fstab'])
1286 self.assertIn('/system', loaded_dict['fstab'])
1287
1288 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1289 target_files = self._test_LoadInfoDict_createTargetFiles(
1290 self.INFO_DICT_DEFAULT,
1291 'BOOT/RAMDISK/etc/recovery.fstab')
1292 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1293 loaded_dict = common.LoadInfoDict(target_files_zip)
1294 self.assertEqual(3, loaded_dict['recovery_api_version'])
1295 self.assertEqual(2, loaded_dict['fstab_version'])
1296 self.assertIn('/', loaded_dict['fstab'])
1297 self.assertIn('/system', loaded_dict['fstab'])
1298
Tao Bao82490d32019-04-09 00:12:30 -07001299 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001300 def test_LoadInfoDict_dirInput(self):
1301 target_files = self._test_LoadInfoDict_createTargetFiles(
1302 self.INFO_DICT_DEFAULT,
1303 'BOOT/RAMDISK/system/etc/recovery.fstab')
1304 unzipped = common.UnzipTemp(target_files)
1305 loaded_dict = common.LoadInfoDict(unzipped)
1306 self.assertEqual(3, loaded_dict['recovery_api_version'])
1307 self.assertEqual(2, loaded_dict['fstab_version'])
1308 self.assertIn('/', loaded_dict['fstab'])
1309 self.assertIn('/system', loaded_dict['fstab'])
1310
Tao Bao82490d32019-04-09 00:12:30 -07001311 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001312 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1313 target_files = self._test_LoadInfoDict_createTargetFiles(
1314 self.INFO_DICT_DEFAULT,
1315 'BOOT/RAMDISK/system/etc/recovery.fstab')
1316 unzipped = common.UnzipTemp(target_files)
1317 loaded_dict = common.LoadInfoDict(unzipped)
1318 self.assertEqual(3, loaded_dict['recovery_api_version'])
1319 self.assertEqual(2, loaded_dict['fstab_version'])
1320 self.assertIn('/', loaded_dict['fstab'])
1321 self.assertIn('/system', loaded_dict['fstab'])
1322
1323 def test_LoadInfoDict_systemRootImageFalse(self):
1324 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1325 # launched prior to P will likely have this config.
1326 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1327 del info_dict['no_recovery']
1328 del info_dict['system_root_image']
1329 del info_dict['recovery_as_boot']
1330 target_files = self._test_LoadInfoDict_createTargetFiles(
1331 info_dict,
1332 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1333 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1334 loaded_dict = common.LoadInfoDict(target_files_zip)
1335 self.assertEqual(3, loaded_dict['recovery_api_version'])
1336 self.assertEqual(2, loaded_dict['fstab_version'])
1337 self.assertNotIn('/', loaded_dict['fstab'])
1338 self.assertIn('/system', loaded_dict['fstab'])
1339
1340 def test_LoadInfoDict_recoveryAsBootFalse(self):
1341 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1342 # devices launched since P will likely have this config.
1343 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1344 del info_dict['no_recovery']
1345 del info_dict['recovery_as_boot']
1346 target_files = self._test_LoadInfoDict_createTargetFiles(
1347 info_dict,
1348 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1349 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1350 loaded_dict = common.LoadInfoDict(target_files_zip)
1351 self.assertEqual(3, loaded_dict['recovery_api_version'])
1352 self.assertEqual(2, loaded_dict['fstab_version'])
1353 self.assertIn('/', loaded_dict['fstab'])
1354 self.assertIn('/system', loaded_dict['fstab'])
1355
1356 def test_LoadInfoDict_noRecoveryTrue(self):
1357 # Device doesn't have a recovery partition at all.
1358 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1359 del info_dict['recovery_as_boot']
1360 target_files = self._test_LoadInfoDict_createTargetFiles(
1361 info_dict,
1362 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1363 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1364 loaded_dict = common.LoadInfoDict(target_files_zip)
1365 self.assertEqual(3, loaded_dict['recovery_api_version'])
1366 self.assertEqual(2, loaded_dict['fstab_version'])
1367 self.assertIsNone(loaded_dict['fstab'])
1368
Tao Bao82490d32019-04-09 00:12:30 -07001369 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001370 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1371 target_files = self._test_LoadInfoDict_createTargetFiles(
1372 self.INFO_DICT_DEFAULT,
1373 'BOOT/RAMDISK/system/etc/recovery.fstab')
1374 common.ZipDelete(target_files, 'META/misc_info.txt')
1375 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1376 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1377
Tao Bao82490d32019-04-09 00:12:30 -07001378 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001379 def test_LoadInfoDict_repacking(self):
1380 target_files = self._test_LoadInfoDict_createTargetFiles(
1381 self.INFO_DICT_DEFAULT,
1382 'BOOT/RAMDISK/system/etc/recovery.fstab')
1383 unzipped = common.UnzipTemp(target_files)
1384 loaded_dict = common.LoadInfoDict(unzipped, True)
1385 self.assertEqual(3, loaded_dict['recovery_api_version'])
1386 self.assertEqual(2, loaded_dict['fstab_version'])
1387 self.assertIn('/', loaded_dict['fstab'])
1388 self.assertIn('/system', loaded_dict['fstab'])
1389 self.assertEqual(
1390 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1391 self.assertEqual(
1392 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1393 loaded_dict['root_fs_config'])
1394
1395 def test_LoadInfoDict_repackingWithZipFileInput(self):
1396 target_files = self._test_LoadInfoDict_createTargetFiles(
1397 self.INFO_DICT_DEFAULT,
1398 'BOOT/RAMDISK/system/etc/recovery.fstab')
1399 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1400 self.assertRaises(
1401 AssertionError, common.LoadInfoDict, target_files_zip, True)
1402
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001403 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1404 framework_dict = {
1405 'super_partition_groups': 'group_a',
1406 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001407 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001408 }
1409 vendor_dict = {
1410 'super_partition_groups': 'group_a group_b',
1411 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001412 'super_group_a_partition_list': 'vendor',
1413 'super_group_a_group_size': '1000',
1414 'super_group_b_partition_list': 'product',
1415 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001416 }
1417 merged_dict = common.MergeDynamicPartitionInfoDicts(
1418 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001419 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001420 expected_merged_dict = {
1421 'super_partition_groups': 'group_a group_b',
1422 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001423 'super_group_a_partition_list': 'system vendor',
1424 'super_group_a_group_size': '1000',
1425 'super_group_b_partition_list': 'product',
1426 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001427 }
1428 self.assertEqual(merged_dict, expected_merged_dict)
1429
1430 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1431 framework_dict = {
1432 'super_partition_groups': 'group_a',
1433 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001434 'super_group_a_partition_list': 'system',
1435 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001436 }
1437 vendor_dict = {
1438 'super_partition_groups': 'group_a group_b',
1439 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001440 'super_group_a_partition_list': 'vendor',
1441 'super_group_a_group_size': '1000',
1442 'super_group_b_partition_list': 'product',
1443 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001444 }
1445 merged_dict = common.MergeDynamicPartitionInfoDicts(
1446 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001447 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001448 expected_merged_dict = {
1449 'super_partition_groups': 'group_a group_b',
1450 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001451 'super_group_a_partition_list': 'system vendor',
1452 'super_group_a_group_size': '1000',
1453 'super_group_b_partition_list': 'product',
1454 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001455 }
1456 self.assertEqual(merged_dict, expected_merged_dict)
1457
Daniel Norman276f0622019-07-26 14:13:51 -07001458 def test_GetAvbPartitionArg(self):
1459 info_dict = {}
1460 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1461 self.assertEqual(
1462 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1463
1464 @test_utils.SkipIfExternalToolsUnavailable()
1465 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1466 testdata_dir = test_utils.get_testdata_dir()
1467 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1468 info_dict = {
1469 'avb_avbtool': 'avbtool',
1470 'avb_vendor_key_path': pubkey,
1471 'avb_vendor_rollback_index_location': 5,
1472 }
1473 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1474 self.assertEqual(2, len(cmd))
1475 self.assertEqual('--chain_partition', cmd[0])
1476 chained_partition_args = cmd[1].split(':')
1477 self.assertEqual(3, len(chained_partition_args))
1478 self.assertEqual('vendor', chained_partition_args[0])
1479 self.assertEqual('5', chained_partition_args[1])
1480 self.assertTrue(os.path.exists(chained_partition_args[2]))
1481
Tao Bao3612c882019-10-14 17:49:31 -07001482 @test_utils.SkipIfExternalToolsUnavailable()
1483 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1484 testdata_dir = test_utils.get_testdata_dir()
1485 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1486 info_dict = {
1487 'avb_avbtool': 'avbtool',
1488 'avb_recovery_key_path': pubkey,
1489 'avb_recovery_rollback_index_location': 3,
1490 }
1491 cmd = common.GetAvbPartitionArg(
1492 'recovery', '/path/to/recovery.img', info_dict)
1493 self.assertFalse(cmd)
1494
1495 @test_utils.SkipIfExternalToolsUnavailable()
1496 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1497 testdata_dir = test_utils.get_testdata_dir()
1498 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1499 info_dict = {
1500 'ab_update': 'true',
1501 'avb_avbtool': 'avbtool',
1502 'avb_recovery_key_path': pubkey,
1503 'avb_recovery_rollback_index_location': 3,
1504 }
1505 cmd = common.GetAvbPartitionArg(
1506 'recovery', '/path/to/recovery.img', info_dict)
1507 self.assertEqual(2, len(cmd))
1508 self.assertEqual('--chain_partition', cmd[0])
1509 chained_partition_args = cmd[1].split(':')
1510 self.assertEqual(3, len(chained_partition_args))
1511 self.assertEqual('recovery', chained_partition_args[0])
1512 self.assertEqual('3', chained_partition_args[1])
1513 self.assertTrue(os.path.exists(chained_partition_args[2]))
1514
Tianjie20dd8f22020-04-19 15:51:16 -07001515 def test_BuildVBMeta_appendAftlCommandSyntax(self):
1516 testdata_dir = test_utils.get_testdata_dir()
1517 common.OPTIONS.info_dict = {
1518 'ab_update': 'true',
1519 'avb_avbtool': 'avbtool',
1520 'build.prop': {
1521 'ro.build.version.incremental': '6285659',
1522 'ro.product.device': 'coral',
1523 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1524 '6285659:userdebug/dev-keys'
1525 }
1526 }
1527 common.OPTIONS.aftl_tool_path = 'aftltool'
1528 common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
1529 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1530 'test_transparency_key.pub')
1531 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1532 testdata_dir, 'test_aftl_rsa4096.pem')
1533
1534 vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
1535 cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
1536 expected_cmd = [
1537 'aftltool', 'make_icp_from_vbmeta',
1538 '--vbmeta_image_path', 'place_holder',
1539 '--output', vbmeta_image.name,
1540 '--version_incremental', '6285659',
1541 '--transparency_log_servers',
1542 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
1543 common.OPTIONS.aftl_key_path),
1544 '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
1545 '--algorithm', 'SHA256_RSA4096',
1546 '--padding', '4096']
1547
1548 # ignore the place holder, i.e. path to a temp file
1549 self.assertEqual(cmd[:3], expected_cmd[:3])
1550 self.assertEqual(cmd[4:], expected_cmd[4:])
1551
1552 @unittest.skip("enable after we have a server for public")
1553 def test_BuildVBMeta_appendAftlContactServer(self):
Tianjie Xueaed60c2020-03-12 00:33:28 -07001554 testdata_dir = test_utils.get_testdata_dir()
1555 common.OPTIONS.info_dict = {
1556 'ab_update': 'true',
1557 'avb_avbtool': 'avbtool',
1558 'build.prop': {
1559 'ro.build.version.incremental': '6285659',
1560 'ro.product.device': 'coral',
1561 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1562 '6285659:userdebug/dev-keys'
1563 }
1564 }
Tianjie0f307452020-04-01 12:20:21 -07001565 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001566 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1567 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1568 'test_transparency_key.pub')
1569 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1570 testdata_dir, 'test_aftl_rsa4096.pem')
1571
1572 input_dir = common.MakeTempDir()
1573 system_image = common.MakeTempFile()
1574 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1575 '/system', str(4096 * 100), '-j', '0', '-s']
1576 common.RunAndCheckOutput(build_image_cmd)
1577
1578 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1579 '--partition_size', str(4096 * 150),
1580 '--partition_name', 'system',
1581 '--image', system_image]
1582 common.RunAndCheckOutput(add_footer_cmd)
1583
1584 vbmeta_image = common.MakeTempFile()
1585 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1586 ['system'])
1587
1588 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1589 vbmeta_image, '--transparency_log_pub_keys',
1590 common.OPTIONS.aftl_key_path]
1591 common.RunAndCheckOutput(verify_cmd)
1592
Tao Baofc7e0e02018-02-13 13:54:02 -08001593
Tao Bao65b94e92018-10-11 21:57:26 -07001594class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001595 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001596
Tao Bao1c830bf2017-12-25 10:43:47 -08001597 Its format should match between common.py and validate_target_files.py.
1598 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001599
1600 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001601 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001602 # Create a dummy dict that contains the fstab info for boot&recovery.
1603 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001604 dummy_fstab = [
1605 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1606 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001607 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001608 # Construct the gzipped recovery.img and boot.img
1609 self.recovery_data = bytearray([
1610 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1611 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1612 0x08, 0x00, 0x00, 0x00
1613 ])
1614 # echo -n "boot" | gzip -f | hd
1615 self.boot_data = bytearray([
1616 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1617 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1618 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001619
1620 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1621 loc = os.path.join(self._tempdir, prefix, name)
1622 if not os.path.exists(os.path.dirname(loc)):
1623 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001624 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001625 f.write(data)
1626
1627 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001628 recovery_image = common.File("recovery.img", self.recovery_data)
1629 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001630 self._info["full_recovery_image"] = "true"
1631
1632 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1633 recovery_image, boot_image, self._info)
1634 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1635 self._info)
1636
Tao Bao82490d32019-04-09 00:12:30 -07001637 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001638 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001639 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001640 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001641 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001642 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1643
1644 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1645 recovery_image, boot_image, self._info)
1646 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1647 self._info)
1648 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001649 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001650 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1651 recovery_image, boot_image, self._info)
1652 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1653 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001654
1655
Yifan Hong45433e42019-01-18 13:55:25 -08001656class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001657
Yifan Hong45433e42019-01-18 13:55:25 -08001658 def __init__(self, partition, tgt, src=None):
1659 self.partition = partition
1660 self.tgt = tgt
1661 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001662
Yifan Hong45433e42019-01-18 13:55:25 -08001663 def WriteScript(self, script, _, progress=None,
1664 write_verify_script=False):
1665 if progress:
1666 script.AppendExtra("progress({})".format(progress))
1667 script.AppendExtra("patch({});".format(self.partition))
1668 if write_verify_script:
1669 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001670
Yifan Hong45433e42019-01-18 13:55:25 -08001671 def WritePostInstallVerifyScript(self, script):
1672 script.AppendExtra("verify({});".format(self.partition))
1673
1674
1675class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001676
Yifan Hong45433e42019-01-18 13:55:25 -08001677 def __init__(self, size):
1678 self.blocksize = 4096
1679 self.total_blocks = size // 4096
1680 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1681
1682
1683class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001684
Yifan Hong45433e42019-01-18 13:55:25 -08001685 @staticmethod
1686 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001687 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001688 with output_zip.open('dynamic_partitions_op_list') as op_list:
1689 return [line.decode().strip() for line in op_list.readlines()
1690 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001691
1692 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001693 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001694 self.output_path = common.MakeTempFile(suffix='.zip')
1695
1696 def test_full(self):
1697 target_info = common.LoadDictionaryFromLines("""
1698dynamic_partition_list=system vendor
1699super_partition_groups=group_foo
1700super_group_foo_group_size={group_size}
1701super_group_foo_partition_list=system vendor
1702""".format(group_size=4 * GiB).split("\n"))
1703 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1704 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1705
1706 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1707 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1708 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1709
1710 self.assertEqual(str(self.script).strip(), """
1711assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001712patch(system);
1713verify(system);
1714unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001715patch(vendor);
1716verify(vendor);
1717unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001718""".strip())
1719
1720 lines = self.get_op_list(self.output_path)
1721
1722 remove_all_groups = lines.index("remove_all_groups")
1723 add_group = lines.index("add_group group_foo 4294967296")
1724 add_vendor = lines.index("add vendor group_foo")
1725 add_system = lines.index("add system group_foo")
1726 resize_vendor = lines.index("resize vendor 1073741824")
1727 resize_system = lines.index("resize system 3221225472")
1728
1729 self.assertLess(remove_all_groups, add_group,
1730 "Should add groups after removing all groups")
1731 self.assertLess(add_group, min(add_vendor, add_system),
1732 "Should add partitions after adding group")
1733 self.assertLess(add_system, resize_system,
1734 "Should resize system after adding it")
1735 self.assertLess(add_vendor, resize_vendor,
1736 "Should resize vendor after adding it")
1737
1738 def test_inc_groups(self):
1739 source_info = common.LoadDictionaryFromLines("""
1740super_partition_groups=group_foo group_bar group_baz
1741super_group_foo_group_size={group_foo_size}
1742super_group_bar_group_size={group_bar_size}
1743""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1744 target_info = common.LoadDictionaryFromLines("""
1745super_partition_groups=group_foo group_baz group_qux
1746super_group_foo_group_size={group_foo_size}
1747super_group_baz_group_size={group_baz_size}
1748super_group_qux_group_size={group_qux_size}
1749""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1750 group_qux_size=1 * GiB).split("\n"))
1751
1752 dp_diff = common.DynamicPartitionsDifference(target_info,
1753 block_diffs=[],
1754 source_info_dict=source_info)
1755 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1756 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1757
1758 lines = self.get_op_list(self.output_path)
1759
1760 removed = lines.index("remove_group group_bar")
1761 shrunk = lines.index("resize_group group_foo 3221225472")
1762 grown = lines.index("resize_group group_baz 4294967296")
1763 added = lines.index("add_group group_qux 1073741824")
1764
Tao Baof1113e92019-06-18 12:10:14 -07001765 self.assertLess(max(removed, shrunk),
1766 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001767 "ops that remove / shrink partitions must precede ops that "
1768 "grow / add partitions")
1769
Yifan Hongbb2658d2019-01-25 12:30:58 -08001770 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001771 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001772dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001773super_partition_groups=group_foo
1774super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001775super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001776""".format(group_foo_size=4 * GiB).split("\n"))
1777 target_info = common.LoadDictionaryFromLines("""
1778dynamic_partition_list=system vendor product odm
1779super_partition_groups=group_foo group_bar
1780super_group_foo_group_size={group_foo_size}
1781super_group_foo_partition_list=system vendor odm
1782super_group_bar_group_size={group_bar_size}
1783super_group_bar_partition_list=product
1784""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1785
1786 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1787 src=FakeSparseImage(1024 * MiB)),
1788 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1789 src=FakeSparseImage(1024 * MiB)),
1790 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1791 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001792 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001793 src=FakeSparseImage(1024 * MiB)),
1794 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1795 src=None)]
1796
1797 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1798 source_info_dict=source_info)
1799 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1800 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1801
1802 metadata_idx = self.script.lines.index(
1803 'assert(update_dynamic_partitions(package_extract_file('
1804 '"dynamic_partitions_op_list")));')
1805 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1806 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1807 for p in ("product", "system", "odm"):
1808 patch_idx = self.script.lines.index("patch({});".format(p))
1809 verify_idx = self.script.lines.index("verify({});".format(p))
1810 self.assertLess(metadata_idx, patch_idx,
1811 "Should patch {} after updating metadata".format(p))
1812 self.assertLess(patch_idx, verify_idx,
1813 "Should verify {} after patching".format(p))
1814
Justin Yun6151e3f2019-06-25 15:58:13 +09001815 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001816
1817 lines = self.get_op_list(self.output_path)
1818
Justin Yun6151e3f2019-06-25 15:58:13 +09001819 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001820 move_product_out = lines.index("move product default")
1821 shrink = lines.index("resize vendor 536870912")
1822 shrink_group = lines.index("resize_group group_foo 3221225472")
1823 add_group_bar = lines.index("add_group group_bar 1073741824")
1824 add_odm = lines.index("add odm group_foo")
1825 grow_existing = lines.index("resize system 1610612736")
1826 grow_added = lines.index("resize odm 1073741824")
1827 move_product_in = lines.index("move product group_bar")
1828
1829 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1830 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1831
1832 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1833 "Must shrink group after partitions inside group are shrunk"
1834 " / removed")
1835
1836 self.assertLess(add_group_bar, move_product_in,
1837 "Must add partitions to group after group is added")
1838
1839 self.assertLess(max_idx_move_partition_out_foo,
1840 min_idx_move_partition_in_foo,
1841 "Must shrink partitions / remove partitions from group"
1842 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001843
1844 def test_remove_partition(self):
1845 source_info = common.LoadDictionaryFromLines("""
1846blockimgdiff_versions=3,4
1847use_dynamic_partitions=true
1848dynamic_partition_list=foo
1849super_partition_groups=group_foo
1850super_group_foo_group_size={group_foo_size}
1851super_group_foo_partition_list=foo
1852""".format(group_foo_size=4 * GiB).split("\n"))
1853 target_info = common.LoadDictionaryFromLines("""
1854blockimgdiff_versions=3,4
1855use_dynamic_partitions=true
1856super_partition_groups=group_foo
1857super_group_foo_group_size={group_foo_size}
1858""".format(group_foo_size=4 * GiB).split("\n"))
1859
1860 common.OPTIONS.info_dict = target_info
1861 common.OPTIONS.target_info_dict = target_info
1862 common.OPTIONS.source_info_dict = source_info
1863 common.OPTIONS.cache_size = 4 * 4096
1864
1865 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1866 src=DataImage("source", pad=True))]
1867
1868 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1869 source_info_dict=source_info)
1870 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1871 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1872
1873 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001874 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001875
1876 lines = self.get_op_list(self.output_path)
1877 self.assertEqual(lines, ["remove foo"])