blob: 0e4626b7d97e91f3507fb006503ecc212484ef04 [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
Daniel Normand3351562020-10-29 12:33:11 -070018import json
Dan Albert8e0178d2015-01-27 15:53:15 -080019import os
Tao Bao17e4e612018-02-16 17:12:54 -080020import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080021import tempfile
22import time
Tianjie20dd8f22020-04-19 15:51:16 -070023import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080024import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080025from hashlib import sha1
26
Dan Albert8e0178d2015-01-27 15:53:15 -080027import common
Tao Bao04e1f012018-02-04 12:13:35 -080028import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070029import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070030from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080031from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080032
Tao Bao04e1f012018-02-04 12:13:35 -080033
Tao Bao31b08072017-11-08 15:50:59 -080034KiB = 1024
35MiB = 1024 * KiB
36GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080037
Tao Bao1c830bf2017-12-25 10:43:47 -080038
Tao Baof3282b42015-04-01 11:21:55 -070039def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080040 size = int(2 * GiB + 1)
41 block_size = 4 * KiB
42 step_size = 4 * MiB
43 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
44 for _ in range(0, size, step_size):
45 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070046 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070047
Dan Albert8e0178d2015-01-27 15:53:15 -080048
Tao Bao1c320f82019-10-04 23:25:12 -070049class BuildInfoTest(test_utils.ReleaseToolsTestCase):
50
Tianjiefdda51d2021-05-05 14:46:35 -070051 TEST_INFO_FINGERPRINT_DICT = {
52 'build.prop': common.PartitionBuildProps.FromDictionary(
53 'system', {
54 'ro.product.brand': 'product-brand',
55 'ro.product.name': 'product-name',
56 'ro.product.device': 'product-device',
57 'ro.build.version.release': 'version-release',
58 'ro.build.id': 'build-id',
59 'ro.build.version.incremental': 'version-incremental',
60 'ro.build.type': 'build-type',
61 'ro.build.tags': 'build-tags',
62 'ro.build.version.sdk': 30,
63 }
64 ),
65 }
66
Tao Bao1c320f82019-10-04 23:25:12 -070067 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000068 'build.prop': common.PartitionBuildProps.FromDictionary(
69 'system', {
70 'ro.product.device': 'product-device',
71 'ro.product.name': 'product-name',
72 'ro.build.fingerprint': 'build-fingerprint',
73 'ro.build.foo': 'build-foo'}
74 ),
75 'system.build.prop': common.PartitionBuildProps.FromDictionary(
76 'system', {
77 'ro.product.system.brand': 'product-brand',
78 'ro.product.system.name': 'product-name',
79 'ro.product.system.device': 'product-device',
80 'ro.system.build.version.release': 'version-release',
81 'ro.system.build.id': 'build-id',
82 'ro.system.build.version.incremental': 'version-incremental',
83 'ro.system.build.type': 'build-type',
84 'ro.system.build.tags': 'build-tags',
85 'ro.system.build.foo': 'build-foo'}
86 ),
87 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
88 'vendor', {
89 'ro.product.vendor.brand': 'vendor-product-brand',
90 'ro.product.vendor.name': 'vendor-product-name',
91 'ro.product.vendor.device': 'vendor-product-device',
92 'ro.vendor.build.version.release': 'vendor-version-release',
93 'ro.vendor.build.id': 'vendor-build-id',
94 'ro.vendor.build.version.incremental':
95 'vendor-version-incremental',
96 'ro.vendor.build.type': 'vendor-build-type',
97 'ro.vendor.build.tags': 'vendor-build-tags'}
98 ),
99 'property1': 'value1',
100 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -0700101 }
102
103 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000104 'build.prop': common.PartitionBuildProps.FromDictionary(
105 'system', {
106 'ro.product.name': 'product-name',
107 'ro.build.thumbprint': 'build-thumbprint',
108 'ro.build.bar': 'build-bar'}
109 ),
110 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
111 'vendor', {
112 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
113 ),
114 'property1': 'value1',
115 'property2': 4096,
116 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700117 }
118
119 TEST_OEM_DICTS = [
120 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000121 'ro.product.brand': 'brand1',
122 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700123 },
124 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000125 'ro.product.brand': 'brand2',
126 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700127 },
128 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000129 'ro.product.brand': 'brand3',
130 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700131 },
132 ]
133
Steven Laver8e2086e2020-04-27 16:26:31 -0700134 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000135 'build.prop': common.PartitionBuildProps.FromDictionary(
136 'system', {
137 'ro.build.fingerprint': 'build-fingerprint',
138 'ro.product.property_source_order':
139 'product,odm,vendor,system_ext,system'}
140 ),
141 'system.build.prop': common.PartitionBuildProps.FromDictionary(
142 'system', {
143 'ro.product.system.device': 'system-product-device'}
144 ),
145 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
146 'vendor', {
147 'ro.product.vendor.device': 'vendor-product-device'}
148 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700149 }
150
151 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000152 'build.prop': common.PartitionBuildProps.FromDictionary(
153 'system', {
154 'ro.build.fingerprint': 'build-fingerprint',
155 'ro.product.property_source_order':
156 'product,product_services,odm,vendor,system',
157 'ro.build.version.release': '10',
158 'ro.build.version.codename': 'REL'}
159 ),
160 'system.build.prop': common.PartitionBuildProps.FromDictionary(
161 'system', {
162 'ro.product.system.device': 'system-product-device'}
163 ),
164 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
165 'vendor', {
166 'ro.product.vendor.device': 'vendor-product-device'}
167 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700168 }
169
170 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000171 'build.prop': common.PartitionBuildProps.FromDictionary(
172 'system', {
173 'ro.product.device': 'product-device',
174 'ro.build.fingerprint': 'build-fingerprint',
175 'ro.build.version.release': '9',
176 'ro.build.version.codename': 'REL'}
177 ),
178 'system.build.prop': common.PartitionBuildProps.FromDictionary(
179 'system', {
180 'ro.product.system.device': 'system-product-device'}
181 ),
182 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
183 'vendor', {
184 'ro.product.vendor.device': 'vendor-product-device'}
185 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700186 }
187
Tao Bao1c320f82019-10-04 23:25:12 -0700188 def test_init(self):
189 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
190 self.assertEqual('product-device', target_info.device)
191 self.assertEqual('build-fingerprint', target_info.fingerprint)
192 self.assertFalse(target_info.is_ab)
193 self.assertIsNone(target_info.oem_props)
194
195 def test_init_with_oem_props(self):
196 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
197 self.TEST_OEM_DICTS)
198 self.assertEqual('device1', target_info.device)
199 self.assertEqual('brand1/product-name/device1:build-thumbprint',
200 target_info.fingerprint)
201
202 # Swap the order in oem_dicts, which would lead to different BuildInfo.
203 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
204 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
205 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
206 oem_dicts)
207 self.assertEqual('device3', target_info.device)
208 self.assertEqual('brand3/product-name/device3:build-thumbprint',
209 target_info.fingerprint)
210
Tao Bao1c320f82019-10-04 23:25:12 -0700211 def test_init_badFingerprint(self):
212 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000213 info_dict['build.prop'].build_props[
214 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700215 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
216
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000217 info_dict['build.prop'].build_props[
218 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700219 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
220
Tianjiefdda51d2021-05-05 14:46:35 -0700221 def test_init_goodFingerprint(self):
222 info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
223 build_info = common.BuildInfo(info_dict)
224 self.assertEqual(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700225 'product-brand/product-name/product-device:version-release/build-id/'
226 'version-incremental:build-type/build-tags', build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700227
228 build_props = info_dict['build.prop'].build_props
229 del build_props['ro.build.id']
230 build_props['ro.build.legacy.id'] = 'legacy-build-id'
231 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
232 self.assertEqual(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700233 'product-brand/product-name/product-device:version-release/'
234 'legacy-build-id/version-incremental:build-type/build-tags',
235 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700236
237 self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
238 False)
239
240 info_dict['avb_enable'] = 'true'
241 info_dict['vbmeta_digest'] = 'abcde12345'
242 build_info = common.BuildInfo(info_dict, use_legacy_id=False)
243 self.assertEqual(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700244 'product-brand/product-name/product-device:version-release/'
245 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
246 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700247
Tao Bao1c320f82019-10-04 23:25:12 -0700248 def test___getitem__(self):
249 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
250 self.assertEqual('value1', target_info['property1'])
251 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000252 self.assertEqual('build-foo',
253 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700254
255 def test___getitem__with_oem_props(self):
256 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
257 self.TEST_OEM_DICTS)
258 self.assertEqual('value1', target_info['property1'])
259 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000260 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700261
262 def test___setitem__(self):
263 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
264 self.assertEqual('value1', target_info['property1'])
265 target_info['property1'] = 'value2'
266 self.assertEqual('value2', target_info['property1'])
267
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000268 self.assertEqual('build-foo',
269 target_info['build.prop'].GetProp('ro.build.foo'))
270 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
271 self.assertEqual('build-bar',
272 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700273
274 def test_get(self):
275 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
276 self.assertEqual('value1', target_info.get('property1'))
277 self.assertEqual(4096, target_info.get('property2'))
278 self.assertEqual(4096, target_info.get('property2', 1024))
279 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000280 self.assertEqual('build-foo',
281 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700282
283 def test_get_with_oem_props(self):
284 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
285 self.TEST_OEM_DICTS)
286 self.assertEqual('value1', target_info.get('property1'))
287 self.assertEqual(4096, target_info.get('property2'))
288 self.assertEqual(4096, target_info.get('property2', 1024))
289 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000290 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700291
292 def test_items(self):
293 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
294 items = target_info.items()
295 self.assertIn(('property1', 'value1'), items)
296 self.assertIn(('property2', 4096), items)
297
298 def test_GetBuildProp(self):
299 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
300 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
301 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
302 'ro.build.nonexistent')
303
304 def test_GetBuildProp_with_oem_props(self):
305 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
306 self.TEST_OEM_DICTS)
307 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
308 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
309 'ro.build.nonexistent')
310
Daniel Normand5fe8622020-01-08 17:01:11 -0800311 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700312 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800313 self.assertEqual(
314 target_info.GetPartitionFingerprint('vendor'),
315 'vendor-product-brand/vendor-product-name/vendor-product-device'
316 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
317 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700318
Daniel Normand5fe8622020-01-08 17:01:11 -0800319 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700320 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800321 self.assertEqual(
322 target_info.GetPartitionFingerprint('system_other'),
323 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700324
Daniel Normand5fe8622020-01-08 17:01:11 -0800325 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
326 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000327 info_dict['vendor.build.prop'].build_props[
328 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800329 target_info = common.BuildInfo(info_dict, None)
330 self.assertEqual(
331 target_info.GetPartitionFingerprint('vendor'),
332 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700333
334 def test_WriteMountOemScript(self):
335 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
336 self.TEST_OEM_DICTS)
337 script_writer = test_utils.MockScriptWriter()
338 target_info.WriteMountOemScript(script_writer)
339 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
340
341 def test_WriteDeviceAssertions(self):
342 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
343 script_writer = test_utils.MockScriptWriter()
344 target_info.WriteDeviceAssertions(script_writer, False)
345 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
346
347 def test_WriteDeviceAssertions_with_oem_props(self):
348 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
349 self.TEST_OEM_DICTS)
350 script_writer = test_utils.MockScriptWriter()
351 target_info.WriteDeviceAssertions(script_writer, False)
352 self.assertEqual(
353 [
354 ('AssertOemProperty', 'ro.product.device',
355 ['device1', 'device2', 'device3'], False),
356 ('AssertOemProperty', 'ro.product.brand',
357 ['brand1', 'brand2', 'brand3'], False),
358 ],
359 script_writer.lines)
360
Steven Laver8e2086e2020-04-27 16:26:31 -0700361 def test_ResolveRoProductProperty_FromVendor(self):
362 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
363 info = common.BuildInfo(info_dict, None)
364 self.assertEqual('vendor-product-device',
365 info.GetBuildProp('ro.product.device'))
366
367 def test_ResolveRoProductProperty_FromSystem(self):
368 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000369 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700370 info = common.BuildInfo(info_dict, None)
371 self.assertEqual('system-product-device',
372 info.GetBuildProp('ro.product.device'))
373
374 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
375 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000376 info_dict['build.prop'].build_props[
377 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700378 with self.assertRaisesRegexp(common.ExternalError,
Kelvin Zhang37a42902022-10-26 12:49:03 -0700379 'Invalid ro.product.property_source_order'):
Steven Laver8e2086e2020-04-27 16:26:31 -0700380 info = common.BuildInfo(info_dict, None)
381 info.GetBuildProp('ro.product.device')
382
383 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
384 info_dict = copy.deepcopy(
385 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
386 info = common.BuildInfo(info_dict, None)
387 self.assertEqual('vendor-product-device',
388 info.GetBuildProp('ro.product.device'))
389
390 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
391 info_dict = copy.deepcopy(
392 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
393 info = common.BuildInfo(info_dict, None)
394 self.assertEqual('product-device',
395 info.GetBuildProp('ro.product.device'))
396
Tao Bao1c320f82019-10-04 23:25:12 -0700397
Tao Bao65b94e92018-10-11 21:57:26 -0700398class CommonZipTest(test_utils.ReleaseToolsTestCase):
399
Tao Bao31b08072017-11-08 15:50:59 -0800400 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700401 test_file_name=None, expected_stat=None, expected_mode=0o644,
402 expected_compress_type=zipfile.ZIP_STORED):
403 # Verify the stat if present.
404 if test_file_name is not None:
405 new_stat = os.stat(test_file_name)
406 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
407 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
408
409 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400410 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700411
412 # Verify the timestamp.
413 info = zip_file.getinfo(arcname)
414 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
415
416 # Verify the file mode.
417 mode = (info.external_attr >> 16) & 0o777
418 self.assertEqual(mode, expected_mode)
419
420 # Verify the compress type.
421 self.assertEqual(info.compress_type, expected_compress_type)
422
423 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800424 entry = zip_file.open(arcname)
425 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700426 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800427 sha1_hash.update(chunk)
428 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700429 self.assertIsNone(zip_file.testzip())
430
Dan Albert8e0178d2015-01-27 15:53:15 -0800431 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
432 extra_zipwrite_args = dict(extra_zipwrite_args or {})
433
434 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800435 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700436
437 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800438 zip_file_name = zip_file.name
439
440 # File names within an archive strip the leading slash.
441 arcname = extra_zipwrite_args.get("arcname", test_file_name)
442 if arcname[0] == "/":
443 arcname = arcname[1:]
444
445 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400446 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800447
448 try:
Tao Bao31b08072017-11-08 15:50:59 -0800449 sha1_hash = sha1()
450 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700451 sha1_hash.update(bytes(data))
452 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800453 test_file.close()
454
Dan Albert8e0178d2015-01-27 15:53:15 -0800455 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700456 expected_compress_type = extra_zipwrite_args.get("compress_type",
457 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800458
Kelvin Zhangea84d422023-04-11 12:50:34 -0700459 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
460 # the timestamp after writing.
461 os.utime(test_file_name, (1234567, 1234567))
462 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800463 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700464 zip_file.close()
Dan Albert8e0178d2015-01-27 15:53:15 -0800465
Tao Bao31b08072017-11-08 15:50:59 -0800466 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
467 test_file_name, expected_stat, expected_mode,
468 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800469 finally:
470 os.remove(test_file_name)
471 os.remove(zip_file_name)
472
Tao Baof3282b42015-04-01 11:21:55 -0700473 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
474 extra_args = dict(extra_args or {})
475
476 zip_file = tempfile.NamedTemporaryFile(delete=False)
477 zip_file_name = zip_file.name
478 zip_file.close()
479
Kelvin Zhang928c2342020-09-22 16:15:57 -0400480 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700481
482 try:
483 expected_compress_type = extra_args.get("compress_type",
484 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700485 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700486 arcname = zinfo_or_arcname
487 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700488 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700489 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700490 if zinfo_or_arcname.external_attr:
491 zinfo_perms = zinfo_or_arcname.external_attr >> 16
492 else:
493 zinfo_perms = 0o600
494 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700495
Tao Bao58c1b962015-05-20 09:32:18 -0700496 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700497 zip_file.close()
Tao Baof3282b42015-04-01 11:21:55 -0700498
Tao Bao31b08072017-11-08 15:50:59 -0800499 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700500 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700501 expected_compress_type=expected_compress_type)
502 finally:
503 os.remove(zip_file_name)
504
505 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
506 extra_args = dict(extra_args or {})
507
508 zip_file = tempfile.NamedTemporaryFile(delete=False)
509 zip_file_name = zip_file.name
510
511 test_file = tempfile.NamedTemporaryFile(delete=False)
512 test_file_name = test_file.name
513
514 arcname_large = test_file_name
515 arcname_small = "bar"
516
517 # File names within an archive strip the leading slash.
518 if arcname_large[0] == "/":
519 arcname_large = arcname_large[1:]
520
521 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400522 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700523
524 try:
Tao Bao31b08072017-11-08 15:50:59 -0800525 sha1_hash = sha1()
526 for data in large:
527 sha1_hash.update(data)
528 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700529 test_file.close()
530
Kelvin Zhangea84d422023-04-11 12:50:34 -0700531 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
532 # the timestamp after writing.
533 os.utime(test_file_name, (1234567, 1234567))
Tao Baof3282b42015-04-01 11:21:55 -0700534 expected_stat = os.stat(test_file_name)
535 expected_mode = 0o644
536 expected_compress_type = extra_args.get("compress_type",
537 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700538
539 common.ZipWrite(zip_file, test_file_name, **extra_args)
540 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700541 zip_file.close()
Tao Baof3282b42015-04-01 11:21:55 -0700542
543 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800544 self._verify(zip_file, zip_file_name, arcname_large,
545 sha1_hash.hexdigest(), test_file_name, expected_stat,
546 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700547
548 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800549 self._verify(zip_file, zip_file_name, arcname_small,
550 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700551 expected_compress_type=expected_compress_type)
552 finally:
553 os.remove(zip_file_name)
554 os.remove(test_file_name)
555
Dan Albert8e0178d2015-01-27 15:53:15 -0800556 def test_ZipWrite(self):
557 file_contents = os.urandom(1024)
558 self._test_ZipWrite(file_contents)
559
560 def test_ZipWrite_with_opts(self):
561 file_contents = os.urandom(1024)
562 self._test_ZipWrite(file_contents, {
563 "arcname": "foobar",
564 "perms": 0o777,
565 "compress_type": zipfile.ZIP_DEFLATED,
566 })
Tao Baof3282b42015-04-01 11:21:55 -0700567 self._test_ZipWrite(file_contents, {
568 "arcname": "foobar",
569 "perms": 0o700,
570 "compress_type": zipfile.ZIP_STORED,
571 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800572
573 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700574 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800575 self._test_ZipWrite(file_contents, {
576 "compress_type": zipfile.ZIP_DEFLATED,
577 })
578
579 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Kelvin Zhang37a42902022-10-26 12:49:03 -0700580 self._test_ZipWrite("")
Tao Baof3282b42015-04-01 11:21:55 -0700581
582 def test_ZipWriteStr(self):
583 random_string = os.urandom(1024)
584 # Passing arcname
585 self._test_ZipWriteStr("foo", random_string)
586
587 # Passing zinfo
588 zinfo = zipfile.ZipInfo(filename="foo")
589 self._test_ZipWriteStr(zinfo, random_string)
590
591 # Timestamp in the zinfo should be overwritten.
592 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
593 self._test_ZipWriteStr(zinfo, random_string)
594
595 def test_ZipWriteStr_with_opts(self):
596 random_string = os.urandom(1024)
597 # Passing arcname
598 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700599 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700600 "compress_type": zipfile.ZIP_DEFLATED,
601 })
Tao Bao58c1b962015-05-20 09:32:18 -0700602 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700603 "compress_type": zipfile.ZIP_STORED,
604 })
605
606 # Passing zinfo
607 zinfo = zipfile.ZipInfo(filename="foo")
608 self._test_ZipWriteStr(zinfo, random_string, {
609 "compress_type": zipfile.ZIP_DEFLATED,
610 })
611 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700612 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700613 "compress_type": zipfile.ZIP_STORED,
614 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700615 self._test_ZipWriteStr(zinfo, random_string, {
616 "perms": 0o000,
617 "compress_type": zipfile.ZIP_STORED,
618 })
Tao Baof3282b42015-04-01 11:21:55 -0700619
620 def test_ZipWriteStr_large_file(self):
621 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
622 # the workaround. We will only test the case of writing a string into a
623 # large archive.
624 long_string = get_2gb_string()
625 short_string = os.urandom(1024)
626 self._test_ZipWriteStr_large_file(long_string, short_string, {
627 "compress_type": zipfile.ZIP_DEFLATED,
628 })
629
630 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Kelvin Zhang37a42902022-10-26 12:49:03 -0700631 self._test_ZipWriteStr('foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700632 zinfo = zipfile.ZipInfo(filename="foo")
Kelvin Zhang37a42902022-10-26 12:49:03 -0700633 self._test_ZipWriteStr(zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700634
635 def test_bug21309935(self):
636 zip_file = tempfile.NamedTemporaryFile(delete=False)
637 zip_file_name = zip_file.name
638 zip_file.close()
639
640 try:
641 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400642 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700643 # Default perms should be 0o644 when passing the filename.
644 common.ZipWriteStr(zip_file, "foo", random_string)
645 # Honor the specified perms.
646 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
647 # The perms in zinfo should be untouched.
648 zinfo = zipfile.ZipInfo(filename="baz")
649 zinfo.external_attr = 0o740 << 16
650 common.ZipWriteStr(zip_file, zinfo, random_string)
651 # Explicitly specified perms has the priority.
652 zinfo = zipfile.ZipInfo(filename="qux")
653 zinfo.external_attr = 0o700 << 16
654 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700655 zip_file.close()
Tao Bao58c1b962015-05-20 09:32:18 -0700656
Tao Bao31b08072017-11-08 15:50:59 -0800657 self._verify(zip_file, zip_file_name, "foo",
658 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700659 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800660 self._verify(zip_file, zip_file_name, "bar",
661 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700662 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800663 self._verify(zip_file, zip_file_name, "baz",
664 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700665 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800666 self._verify(zip_file, zip_file_name, "qux",
667 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700668 expected_mode=0o400)
669 finally:
670 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700671
Tao Bao82490d32019-04-09 00:12:30 -0700672 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800673 def test_ZipDelete(self):
674 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
675 output_zip = zipfile.ZipFile(zip_file.name, 'w',
676 compression=zipfile.ZIP_DEFLATED)
677 with tempfile.NamedTemporaryFile() as entry_file:
678 entry_file.write(os.urandom(1024))
679 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
680 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
681 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
Kelvin Zhang37a42902022-10-26 12:49:03 -0700682 output_zip.close()
Tao Bao89d7ab22017-12-14 17:05:33 -0800683 zip_file.close()
684
685 try:
686 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400687 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800688 entries = check_zip.namelist()
689 self.assertTrue('Test1' in entries)
690 self.assertFalse('Test2' in entries)
691 self.assertTrue('Test3' in entries)
692
Tao Bao986ee862018-10-04 15:46:16 -0700693 self.assertRaises(
694 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400695 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800696 entries = check_zip.namelist()
697 self.assertTrue('Test1' in entries)
698 self.assertFalse('Test2' in entries)
699 self.assertTrue('Test3' in entries)
700
701 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400702 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800703 entries = check_zip.namelist()
704 self.assertTrue('Test1' in entries)
705 self.assertFalse('Test2' in entries)
706 self.assertFalse('Test3' in entries)
707
708 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400709 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800710 entries = check_zip.namelist()
711 self.assertFalse('Test1' in entries)
712 self.assertFalse('Test2' in entries)
713 self.assertFalse('Test3' in entries)
714 finally:
715 os.remove(zip_file.name)
716
Tao Bao0ff15de2019-03-20 11:26:06 -0700717 @staticmethod
718 def _test_UnzipTemp_createZipFile():
719 zip_file = common.MakeTempFile(suffix='.zip')
720 output_zip = zipfile.ZipFile(
721 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
722 contents = os.urandom(1024)
723 with tempfile.NamedTemporaryFile() as entry_file:
724 entry_file.write(contents)
725 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
726 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
727 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
728 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
729 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
Kelvin Zhang37a42902022-10-26 12:49:03 -0700730 output_zip.close()
731 output_zip.close()
Tao Bao0ff15de2019-03-20 11:26:06 -0700732 return zip_file
733
Tao Bao82490d32019-04-09 00:12:30 -0700734 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700735 def test_UnzipTemp(self):
736 zip_file = self._test_UnzipTemp_createZipFile()
737 unzipped_dir = common.UnzipTemp(zip_file)
738 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
739 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
740 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
741 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
742 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
743
Tao Bao82490d32019-04-09 00:12:30 -0700744 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700745 def test_UnzipTemp_withPatterns(self):
746 zip_file = self._test_UnzipTemp_createZipFile()
747
748 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
749 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
750 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
751 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
752 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
753 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
754
755 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
756 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
757 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
758 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
759 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
760 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
761
762 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
763 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
764 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
765 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
766 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
767 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
768
769 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
770 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
771 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
772 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
773 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
774 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
775
776 def test_UnzipTemp_withEmptyPatterns(self):
777 zip_file = self._test_UnzipTemp_createZipFile()
778 unzipped_dir = common.UnzipTemp(zip_file, [])
779 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
780 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
781 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
782 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
783 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
784
Tao Bao82490d32019-04-09 00:12:30 -0700785 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700786 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
787 zip_file = self._test_UnzipTemp_createZipFile()
788 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
789 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
790 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
791 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
792 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
793 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
794
795 def test_UnzipTemp_withNoMatchingPatterns(self):
796 zip_file = self._test_UnzipTemp_createZipFile()
797 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
798 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
799 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
800 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
801 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
802 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
803
Tao Bao89d7ab22017-12-14 17:05:33 -0800804
Tao Bao65b94e92018-10-11 21:57:26 -0700805class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800806 """Tests the APK utils related functions."""
807
808 APKCERTS_TXT1 = (
809 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
810 ' private_key="certs/devkey.pk8"\n'
811 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700812 ' certificate="build/make/target/product/security/platform.x509.pem"'
813 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800814 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
815 )
816
817 APKCERTS_CERTMAP1 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700818 'RecoveryLocalizer.apk': 'certs/devkey',
819 'Settings.apk': 'build/make/target/product/security/platform',
820 'TV.apk': 'PRESIGNED',
Tao Bao818ddf52018-01-05 11:17:34 -0800821 }
822
823 APKCERTS_TXT2 = (
824 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
825 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
826 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
827 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
828 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
829 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
830 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
831 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
832 )
833
834 APKCERTS_CERTMAP2 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700835 'Compressed1.apk': 'certs/compressed1',
836 'Compressed2a.apk': 'certs/compressed2',
837 'Compressed2b.apk': 'certs/compressed2',
838 'Compressed3.apk': 'certs/compressed3',
Tao Bao818ddf52018-01-05 11:17:34 -0800839 }
840
841 APKCERTS_TXT3 = (
842 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
843 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
844 )
845
846 APKCERTS_CERTMAP3 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700847 'Compressed4.apk': 'certs/compressed4',
Tao Bao818ddf52018-01-05 11:17:34 -0800848 }
849
Bill Peckham5c7b0342020-04-03 15:36:23 -0700850 # Test parsing with no optional fields, both optional fields, and only the
851 # partition optional field.
852 APKCERTS_TXT4 = (
853 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
854 ' private_key="certs/devkey.pk8"\n'
855 'name="Settings.apk"'
856 ' certificate="build/make/target/product/security/platform.x509.pem"'
857 ' private_key="build/make/target/product/security/platform.pk8"'
858 ' compressed="gz" partition="system"\n'
859 'name="TV.apk" certificate="PRESIGNED" private_key=""'
860 ' partition="product"\n'
861 )
862
863 APKCERTS_CERTMAP4 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700864 'RecoveryLocalizer.apk': 'certs/devkey',
865 'Settings.apk': 'build/make/target/product/security/platform',
866 'TV.apk': 'PRESIGNED',
Bill Peckham5c7b0342020-04-03 15:36:23 -0700867 }
868
Tao Bao17e4e612018-02-16 17:12:54 -0800869 def setUp(self):
870 self.testdata_dir = test_utils.get_testdata_dir()
871
Tao Bao818ddf52018-01-05 11:17:34 -0800872 @staticmethod
873 def _write_apkcerts_txt(apkcerts_txt, additional=None):
874 if additional is None:
875 additional = []
876 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400877 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800878 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
879 for entry in additional:
880 target_files_zip.writestr(entry, '')
881 return target_files
882
883 def test_ReadApkCerts_NoncompressedApks(self):
884 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400885 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800886 certmap, ext = common.ReadApkCerts(input_zip)
887
888 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
889 self.assertIsNone(ext)
890
891 def test_ReadApkCerts_CompressedApks(self):
892 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
893 # not stored in '.gz' format, so it shouldn't be considered as installed.
894 target_files = self._write_apkcerts_txt(
895 self.APKCERTS_TXT2,
896 ['Compressed1.apk.gz', 'Compressed3.apk'])
897
Kelvin Zhang928c2342020-09-22 16:15:57 -0400898 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800899 certmap, ext = common.ReadApkCerts(input_zip)
900
901 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
902 self.assertEqual('.gz', ext)
903
904 # Alternative case with '.xz'.
905 target_files = self._write_apkcerts_txt(
906 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
907
Kelvin Zhang928c2342020-09-22 16:15:57 -0400908 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800909 certmap, ext = common.ReadApkCerts(input_zip)
910
911 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
912 self.assertEqual('.xz', ext)
913
914 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
915 target_files = self._write_apkcerts_txt(
916 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
917 ['Compressed1.apk.gz', 'Compressed3.apk'])
918
Kelvin Zhang928c2342020-09-22 16:15:57 -0400919 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800920 certmap, ext = common.ReadApkCerts(input_zip)
921
922 certmap_merged = self.APKCERTS_CERTMAP1.copy()
923 certmap_merged.update(self.APKCERTS_CERTMAP2)
924 self.assertDictEqual(certmap_merged, certmap)
925 self.assertEqual('.gz', ext)
926
927 def test_ReadApkCerts_MultipleCompressionMethods(self):
928 target_files = self._write_apkcerts_txt(
929 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
930 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
931
Kelvin Zhang928c2342020-09-22 16:15:57 -0400932 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800933 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
934
935 def test_ReadApkCerts_MismatchingKeys(self):
936 malformed_apkcerts_txt = (
937 'name="App1.apk" certificate="certs/cert1.x509.pem"'
938 ' private_key="certs/cert2.pk8"\n'
939 )
940 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
941
Kelvin Zhang928c2342020-09-22 16:15:57 -0400942 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800943 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
944
Bill Peckham5c7b0342020-04-03 15:36:23 -0700945 def test_ReadApkCerts_WithWithoutOptionalFields(self):
946 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400947 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700948 certmap, ext = common.ReadApkCerts(input_zip)
949
950 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
951 self.assertIsNone(ext)
952
Tao Bao04e1f012018-02-04 12:13:35 -0800953 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800954 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
955 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800956 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800957 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
958
959 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800960 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800961 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
962
Tao Bao82490d32019-04-09 00:12:30 -0700963 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700964 def test_ExtractAvbPublicKey(self):
965 privkey = os.path.join(self.testdata_dir, 'testkey.key')
966 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700967 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
968 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
969 with open(extracted_from_privkey, 'rb') as privkey_fp, \
Kelvin Zhang37a42902022-10-26 12:49:03 -0700970 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700971 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
972
Tao Bao17e4e612018-02-16 17:12:54 -0800973 def test_ParseCertificate(self):
974 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
975
976 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800977 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
978 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800979 expected, _ = proc.communicate()
980 self.assertEqual(0, proc.returncode)
981
982 with open(cert) as cert_fp:
983 actual = common.ParseCertificate(cert_fp.read())
984 self.assertEqual(expected, actual)
985
Tao Bao82490d32019-04-09 00:12:30 -0700986 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700987 def test_GetMinSdkVersion(self):
988 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
989 self.assertEqual('24', common.GetMinSdkVersion(test_app))
990
Tao Bao82490d32019-04-09 00:12:30 -0700991 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700992 def test_GetMinSdkVersion_invalidInput(self):
993 self.assertRaises(
994 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
995
Tao Bao82490d32019-04-09 00:12:30 -0700996 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700997 def test_GetMinSdkVersionInt(self):
998 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
999 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
1000
Tao Bao82490d32019-04-09 00:12:30 -07001001 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001002 def test_GetMinSdkVersionInt_invalidInput(self):
1003 self.assertRaises(
1004 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1005 {})
1006
Tao Bao818ddf52018-01-05 11:17:34 -08001007
Tao Bao65b94e92018-10-11 21:57:26 -07001008class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001009
Tao Bao02a08592018-07-22 12:40:45 -07001010 def setUp(self):
1011 self.testdata_dir = test_utils.get_testdata_dir()
1012
Tao Bao82490d32019-04-09 00:12:30 -07001013 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001014 def test_GetSparseImage_emptyBlockMapFile(self):
1015 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001016 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001017 target_files_zip.write(
1018 test_utils.construct_sparse_image([
1019 (0xCAC1, 6),
1020 (0xCAC3, 3),
1021 (0xCAC1, 4)]),
1022 arcname='IMAGES/system.img')
1023 target_files_zip.writestr('IMAGES/system.map', '')
1024 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1025 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1026
Tao Baodba59ee2018-01-09 13:21:02 -08001027 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001028 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001029 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001030
1031 self.assertDictEqual(
1032 {
1033 '__COPY': RangeSet("0"),
1034 '__NONZERO-0': RangeSet("1-5 9-12"),
1035 },
1036 sparse_image.file_map)
1037
Daniel Norman21c34f72020-11-11 17:25:50 -08001038 def test_PartitionMapFromTargetFiles(self):
1039 target_files_dir = common.MakeTempDir()
1040 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1041 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1042 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1043 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1044 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1045 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1046 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1047 self.assertDictEqual(
1048 partition_map,
1049 {
1050 'system': 'SYSTEM',
1051 'vendor': 'SYSTEM/vendor',
1052 # Prefer PRODUCT over SYSTEM/product
1053 'product': 'PRODUCT',
1054 'odm': 'SYSTEM/vendor/odm',
1055 'vendor_dlkm': 'VENDOR_DLKM',
1056 # No system_ext or odm_dlkm
1057 })
1058
Daniel Normand3351562020-10-29 12:33:11 -07001059 def test_SharedUidPartitionViolations(self):
1060 uid_dict = {
1061 'android.uid.phone': {
1062 'system': ['system_phone.apk'],
1063 'system_ext': ['system_ext_phone.apk'],
1064 },
1065 'android.uid.wifi': {
1066 'vendor': ['vendor_wifi.apk'],
1067 'odm': ['odm_wifi.apk'],
1068 },
1069 }
1070 errors = common.SharedUidPartitionViolations(
1071 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1072 self.assertEqual(errors, [])
1073
1074 def test_SharedUidPartitionViolations_Violation(self):
1075 uid_dict = {
1076 'android.uid.phone': {
1077 'system': ['system_phone.apk'],
1078 'vendor': ['vendor_phone.apk'],
1079 },
1080 }
1081 errors = common.SharedUidPartitionViolations(
1082 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1083 self.assertIn(
1084 ('APK sharedUserId "android.uid.phone" found across partition groups '
1085 'in partitions "system,vendor"'), errors)
1086
Tao Baob2de7d92019-04-10 10:01:47 -07001087 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001088 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001089 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1090 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001091 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001092 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1093 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001094
Tao Bao82490d32019-04-09 00:12:30 -07001095 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001096 def test_GetSparseImage_missingBlockMapFile(self):
1097 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001098 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001099 target_files_zip.write(
1100 test_utils.construct_sparse_image([
1101 (0xCAC1, 6),
1102 (0xCAC3, 3),
1103 (0xCAC1, 4)]),
1104 arcname='IMAGES/system.img')
1105 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1106 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1107
Tao Baodba59ee2018-01-09 13:21:02 -08001108 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001109 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001110 self.assertRaises(
1111 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1112 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001113
Tao Bao82490d32019-04-09 00:12:30 -07001114 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001115 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1116 """Tests the case of having overlapping blocks but disallowed."""
1117 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001118 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001119 target_files_zip.write(
1120 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1121 arcname='IMAGES/system.img')
1122 # Block 10 is shared between two files.
1123 target_files_zip.writestr(
1124 'IMAGES/system.map',
1125 '\n'.join([
1126 '/system/file1 1-5 9-10',
1127 '/system/file2 10-12']))
1128 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1129 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1130
Tao Baodba59ee2018-01-09 13:21:02 -08001131 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001132 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001133 self.assertRaises(
1134 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1135 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001136
Tao Bao82490d32019-04-09 00:12:30 -07001137 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001138 def test_GetSparseImage_sharedBlocks_allowed(self):
1139 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1140 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001141 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001142 # Construct an image with a care_map of "0-5 9-12".
1143 target_files_zip.write(
1144 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1145 arcname='IMAGES/system.img')
1146 # Block 10 is shared between two files.
1147 target_files_zip.writestr(
1148 'IMAGES/system.map',
1149 '\n'.join([
1150 '/system/file1 1-5 9-10',
1151 '/system/file2 10-12']))
1152 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1153 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1154
Tao Baodba59ee2018-01-09 13:21:02 -08001155 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001156 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001157 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001158
1159 self.assertDictEqual(
1160 {
1161 '__COPY': RangeSet("0"),
1162 '__NONZERO-0': RangeSet("6-8 13-15"),
1163 '/system/file1': RangeSet("1-5 9-10"),
1164 '/system/file2': RangeSet("11-12"),
1165 },
1166 sparse_image.file_map)
1167
1168 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1169 # 'incomplete'.
1170 self.assertTrue(
1171 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1172 self.assertNotIn(
1173 'incomplete', sparse_image.file_map['/system/file2'].extra)
1174
Tao Baoa264fef2019-10-06 21:55:20 -07001175 # '/system/file1' will only contain one field -- a copy of the input text.
1176 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1177
1178 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001179 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1180 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001181
Tao Bao82490d32019-04-09 00:12:30 -07001182 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001183 def test_GetSparseImage_incompleteRanges(self):
1184 """Tests the case of ext4 images with holes."""
1185 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001186 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001187 target_files_zip.write(
1188 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1189 arcname='IMAGES/system.img')
1190 target_files_zip.writestr(
1191 'IMAGES/system.map',
1192 '\n'.join([
1193 '/system/file1 1-5 9-10',
1194 '/system/file2 11-12']))
1195 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1196 # '/system/file2' has less blocks listed (2) than actual (3).
1197 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1198
Tao Baodba59ee2018-01-09 13:21:02 -08001199 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001200 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001201 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001202
Tao Baoa264fef2019-10-06 21:55:20 -07001203 self.assertEqual(
1204 '1-5 9-10',
1205 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001206 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1207
Tao Bao82490d32019-04-09 00:12:30 -07001208 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001209 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1210 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001211 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001212 target_files_zip.write(
1213 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1214 arcname='IMAGES/system.img')
1215 target_files_zip.writestr(
1216 'IMAGES/system.map',
1217 '\n'.join([
1218 '//system/file1 1-5 9-10',
1219 '//system/file2 11-12',
1220 '/system/app/file3 13-15']))
1221 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1222 # '/system/file2' has less blocks listed (2) than actual (3).
1223 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1224 # '/system/app/file3' has less blocks listed (3) than actual (4).
1225 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1226
1227 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001228 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001229 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1230
Tao Baoa264fef2019-10-06 21:55:20 -07001231 self.assertEqual(
1232 '1-5 9-10',
1233 sparse_image.file_map['//system/file1'].extra['text_str'])
Kelvin Zhang37a42902022-10-26 12:49:03 -07001234 self.assertTrue(
1235 sparse_image.file_map['//system/file2'].extra['incomplete'])
Tao Baod3554e62018-07-10 15:31:22 -07001236 self.assertTrue(
1237 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1238
Tao Bao82490d32019-04-09 00:12:30 -07001239 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001240 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1241 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001242 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001243 target_files_zip.write(
1244 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1245 arcname='IMAGES/system.img')
1246 target_files_zip.writestr(
1247 'IMAGES/system.map',
1248 '\n'.join([
1249 '//system/file1 1-5 9-10',
1250 '//init.rc 13-15']))
1251 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1252 # '/init.rc' has less blocks listed (3) than actual (4).
1253 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1254
1255 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001256 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001257 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1258
Tao Baoa264fef2019-10-06 21:55:20 -07001259 self.assertEqual(
1260 '1-5 9-10',
1261 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001262 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1263
Tao Bao82490d32019-04-09 00:12:30 -07001264 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001265 def test_GetSparseImage_fileNotFound(self):
1266 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001267 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001268 target_files_zip.write(
1269 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1270 arcname='IMAGES/system.img')
1271 target_files_zip.writestr(
1272 'IMAGES/system.map',
1273 '\n'.join([
1274 '//system/file1 1-5 9-10',
1275 '//system/file2 11-12']))
1276 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1277
1278 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001279 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001280 self.assertRaises(
1281 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1282 False)
1283
Tao Bao82490d32019-04-09 00:12:30 -07001284 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001285 def test_GetAvbChainedPartitionArg(self):
1286 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1287 info_dict = {
1288 'avb_avbtool': 'avbtool',
1289 'avb_system_key_path': pubkey,
1290 'avb_system_rollback_index_location': 2,
1291 }
1292 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1293 self.assertEqual(3, len(args))
1294 self.assertEqual('system', args[0])
1295 self.assertEqual('2', args[1])
1296 self.assertTrue(os.path.exists(args[2]))
1297
Tao Bao82490d32019-04-09 00:12:30 -07001298 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001299 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1300 key = os.path.join(self.testdata_dir, 'testkey.key')
1301 info_dict = {
1302 'avb_avbtool': 'avbtool',
1303 'avb_product_key_path': key,
1304 'avb_product_rollback_index_location': 2,
1305 }
1306 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1307 self.assertEqual(3, len(args))
1308 self.assertEqual('product', args[0])
1309 self.assertEqual('2', args[1])
1310 self.assertTrue(os.path.exists(args[2]))
1311
Tao Bao82490d32019-04-09 00:12:30 -07001312 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001313 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1314 info_dict = {
1315 'avb_avbtool': 'avbtool',
1316 'avb_system_key_path': 'does-not-exist',
1317 'avb_system_rollback_index_location': 2,
1318 }
1319 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1320 args = common.GetAvbChainedPartitionArg(
1321 'system', info_dict, pubkey).split(':')
1322 self.assertEqual(3, len(args))
1323 self.assertEqual('system', args[0])
1324 self.assertEqual('2', args[1])
1325 self.assertTrue(os.path.exists(args[2]))
1326
Tao Bao82490d32019-04-09 00:12:30 -07001327 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001328 def test_GetAvbChainedPartitionArg_invalidKey(self):
1329 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1330 info_dict = {
1331 'avb_avbtool': 'avbtool',
1332 'avb_system_key_path': pubkey,
1333 'avb_system_rollback_index_location': 2,
1334 }
1335 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001336 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1337 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001338
Tao Baoa57ab9f2018-08-24 12:08:38 -07001339 INFO_DICT_DEFAULT = {
1340 'recovery_api_version': 3,
1341 'fstab_version': 2,
1342 'system_root_image': 'true',
Kelvin Zhang37a42902022-10-26 12:49:03 -07001343 'no_recovery': 'true',
Tao Baoa57ab9f2018-08-24 12:08:38 -07001344 'recovery_as_boot': 'true',
1345 }
1346
Daniel Norman4cc9df62019-07-18 10:11:07 -07001347 def test_LoadListFromFile(self):
1348 file_path = os.path.join(self.testdata_dir,
1349 'merge_config_framework_item_list')
1350 contents = common.LoadListFromFile(file_path)
1351 expected_contents = [
1352 'META/apkcerts.txt',
1353 'META/filesystem_config.txt',
1354 'META/root_filesystem_config.txt',
1355 'META/system_manifest.xml',
1356 'META/system_matrix.xml',
1357 'META/update_engine_config.txt',
1358 'PRODUCT/*',
1359 'ROOT/*',
1360 'SYSTEM/*',
1361 ]
1362 self.assertEqual(sorted(contents), sorted(expected_contents))
1363
Tao Baoa57ab9f2018-08-24 12:08:38 -07001364 @staticmethod
1365 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1366 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001367 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001368 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001369 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001370 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1371
1372 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1373 if info_dict.get('system_root_image') == 'true':
1374 fstab_values = FSTAB_TEMPLATE.format('/')
1375 else:
1376 fstab_values = FSTAB_TEMPLATE.format('/system')
1377 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001378
1379 common.ZipWriteStr(
1380 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001381 return target_files
1382
1383 def test_LoadInfoDict(self):
1384 target_files = self._test_LoadInfoDict_createTargetFiles(
1385 self.INFO_DICT_DEFAULT,
1386 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001387 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001388 loaded_dict = common.LoadInfoDict(target_files_zip)
1389 self.assertEqual(3, loaded_dict['recovery_api_version'])
1390 self.assertEqual(2, loaded_dict['fstab_version'])
1391 self.assertIn('/', loaded_dict['fstab'])
1392 self.assertIn('/system', loaded_dict['fstab'])
1393
1394 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1395 target_files = self._test_LoadInfoDict_createTargetFiles(
1396 self.INFO_DICT_DEFAULT,
1397 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001398 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001399 loaded_dict = common.LoadInfoDict(target_files_zip)
1400 self.assertEqual(3, loaded_dict['recovery_api_version'])
1401 self.assertEqual(2, loaded_dict['fstab_version'])
1402 self.assertIn('/', loaded_dict['fstab'])
1403 self.assertIn('/system', loaded_dict['fstab'])
1404
Tao Bao82490d32019-04-09 00:12:30 -07001405 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001406 def test_LoadInfoDict_dirInput(self):
1407 target_files = self._test_LoadInfoDict_createTargetFiles(
1408 self.INFO_DICT_DEFAULT,
1409 'BOOT/RAMDISK/system/etc/recovery.fstab')
1410 unzipped = common.UnzipTemp(target_files)
1411 loaded_dict = common.LoadInfoDict(unzipped)
1412 self.assertEqual(3, loaded_dict['recovery_api_version'])
1413 self.assertEqual(2, loaded_dict['fstab_version'])
1414 self.assertIn('/', loaded_dict['fstab'])
1415 self.assertIn('/system', loaded_dict['fstab'])
1416
Tao Bao82490d32019-04-09 00:12:30 -07001417 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001418 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1419 target_files = self._test_LoadInfoDict_createTargetFiles(
1420 self.INFO_DICT_DEFAULT,
1421 'BOOT/RAMDISK/system/etc/recovery.fstab')
1422 unzipped = common.UnzipTemp(target_files)
1423 loaded_dict = common.LoadInfoDict(unzipped)
1424 self.assertEqual(3, loaded_dict['recovery_api_version'])
1425 self.assertEqual(2, loaded_dict['fstab_version'])
1426 self.assertIn('/', loaded_dict['fstab'])
1427 self.assertIn('/system', loaded_dict['fstab'])
1428
1429 def test_LoadInfoDict_systemRootImageFalse(self):
1430 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1431 # launched prior to P will likely have this config.
1432 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1433 del info_dict['no_recovery']
1434 del info_dict['system_root_image']
1435 del info_dict['recovery_as_boot']
1436 target_files = self._test_LoadInfoDict_createTargetFiles(
1437 info_dict,
1438 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001439 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001440 loaded_dict = common.LoadInfoDict(target_files_zip)
1441 self.assertEqual(3, loaded_dict['recovery_api_version'])
1442 self.assertEqual(2, loaded_dict['fstab_version'])
1443 self.assertNotIn('/', loaded_dict['fstab'])
1444 self.assertIn('/system', loaded_dict['fstab'])
1445
1446 def test_LoadInfoDict_recoveryAsBootFalse(self):
1447 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1448 # devices launched since P will likely have this config.
1449 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1450 del info_dict['no_recovery']
1451 del info_dict['recovery_as_boot']
1452 target_files = self._test_LoadInfoDict_createTargetFiles(
1453 info_dict,
1454 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001455 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001456 loaded_dict = common.LoadInfoDict(target_files_zip)
1457 self.assertEqual(3, loaded_dict['recovery_api_version'])
1458 self.assertEqual(2, loaded_dict['fstab_version'])
1459 self.assertIn('/', loaded_dict['fstab'])
1460 self.assertIn('/system', loaded_dict['fstab'])
1461
1462 def test_LoadInfoDict_noRecoveryTrue(self):
1463 # Device doesn't have a recovery partition at all.
1464 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1465 del info_dict['recovery_as_boot']
1466 target_files = self._test_LoadInfoDict_createTargetFiles(
1467 info_dict,
1468 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001469 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001470 loaded_dict = common.LoadInfoDict(target_files_zip)
1471 self.assertEqual(3, loaded_dict['recovery_api_version'])
1472 self.assertEqual(2, loaded_dict['fstab_version'])
1473 self.assertIsNone(loaded_dict['fstab'])
1474
Tao Bao82490d32019-04-09 00:12:30 -07001475 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001476 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1477 target_files = self._test_LoadInfoDict_createTargetFiles(
1478 self.INFO_DICT_DEFAULT,
1479 'BOOT/RAMDISK/system/etc/recovery.fstab')
1480 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001481 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001482 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1483
Tao Bao82490d32019-04-09 00:12:30 -07001484 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001485 def test_LoadInfoDict_repacking(self):
1486 target_files = self._test_LoadInfoDict_createTargetFiles(
1487 self.INFO_DICT_DEFAULT,
1488 'BOOT/RAMDISK/system/etc/recovery.fstab')
1489 unzipped = common.UnzipTemp(target_files)
1490 loaded_dict = common.LoadInfoDict(unzipped, True)
1491 self.assertEqual(3, loaded_dict['recovery_api_version'])
1492 self.assertEqual(2, loaded_dict['fstab_version'])
1493 self.assertIn('/', loaded_dict['fstab'])
1494 self.assertIn('/system', loaded_dict['fstab'])
1495 self.assertEqual(
1496 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1497 self.assertEqual(
1498 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1499 loaded_dict['root_fs_config'])
1500
1501 def test_LoadInfoDict_repackingWithZipFileInput(self):
1502 target_files = self._test_LoadInfoDict_createTargetFiles(
1503 self.INFO_DICT_DEFAULT,
1504 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001505 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001506 self.assertRaises(
1507 AssertionError, common.LoadInfoDict, target_files_zip, True)
1508
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001509 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1510 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001511 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001512 'super_partition_groups': 'group_a',
1513 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001514 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001515 }
1516 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001517 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001518 'super_partition_groups': 'group_a group_b',
1519 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001520 'super_block_devices': 'super',
1521 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001522 'super_group_a_partition_list': 'vendor',
1523 'super_group_a_group_size': '1000',
1524 'super_group_b_partition_list': 'product',
1525 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001526 }
1527 merged_dict = common.MergeDynamicPartitionInfoDicts(
1528 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001529 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001530 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001531 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001532 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001533 'dynamic_partition_list': 'product system vendor',
1534 'super_block_devices': 'super',
1535 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001536 'super_group_a_partition_list': 'system vendor',
1537 'super_group_a_group_size': '1000',
1538 'super_group_b_partition_list': 'product',
1539 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001540 }
1541 self.assertEqual(merged_dict, expected_merged_dict)
1542
1543 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1544 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001545 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001546 'super_partition_groups': 'group_a',
1547 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001548 'super_group_a_partition_list': 'system',
1549 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001550 }
1551 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001552 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001553 'super_partition_groups': 'group_a group_b',
1554 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001555 'super_group_a_partition_list': 'vendor',
1556 'super_group_a_group_size': '1000',
1557 'super_group_b_partition_list': 'product',
1558 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001559 }
1560 merged_dict = common.MergeDynamicPartitionInfoDicts(
1561 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001562 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001563 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001564 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001565 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001566 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001567 'super_group_a_partition_list': 'system vendor',
1568 'super_group_a_group_size': '1000',
1569 'super_group_b_partition_list': 'product',
1570 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001571 }
1572 self.assertEqual(merged_dict, expected_merged_dict)
1573
Daniel Norman276f0622019-07-26 14:13:51 -07001574 def test_GetAvbPartitionArg(self):
1575 info_dict = {}
1576 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1577 self.assertEqual(
1578 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1579
1580 @test_utils.SkipIfExternalToolsUnavailable()
1581 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1582 testdata_dir = test_utils.get_testdata_dir()
1583 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1584 info_dict = {
1585 'avb_avbtool': 'avbtool',
1586 'avb_vendor_key_path': pubkey,
1587 'avb_vendor_rollback_index_location': 5,
1588 }
1589 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1590 self.assertEqual(2, len(cmd))
1591 self.assertEqual('--chain_partition', cmd[0])
1592 chained_partition_args = cmd[1].split(':')
1593 self.assertEqual(3, len(chained_partition_args))
1594 self.assertEqual('vendor', chained_partition_args[0])
1595 self.assertEqual('5', chained_partition_args[1])
1596 self.assertTrue(os.path.exists(chained_partition_args[2]))
1597
Tao Bao3612c882019-10-14 17:49:31 -07001598 @test_utils.SkipIfExternalToolsUnavailable()
1599 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1600 testdata_dir = test_utils.get_testdata_dir()
1601 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1602 info_dict = {
1603 'avb_avbtool': 'avbtool',
1604 'avb_recovery_key_path': pubkey,
1605 'avb_recovery_rollback_index_location': 3,
1606 }
1607 cmd = common.GetAvbPartitionArg(
1608 'recovery', '/path/to/recovery.img', info_dict)
1609 self.assertFalse(cmd)
1610
1611 @test_utils.SkipIfExternalToolsUnavailable()
1612 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1613 testdata_dir = test_utils.get_testdata_dir()
1614 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1615 info_dict = {
1616 'ab_update': 'true',
1617 'avb_avbtool': 'avbtool',
1618 'avb_recovery_key_path': pubkey,
1619 'avb_recovery_rollback_index_location': 3,
1620 }
1621 cmd = common.GetAvbPartitionArg(
1622 'recovery', '/path/to/recovery.img', info_dict)
1623 self.assertEqual(2, len(cmd))
1624 self.assertEqual('--chain_partition', cmd[0])
1625 chained_partition_args = cmd[1].split(':')
1626 self.assertEqual(3, len(chained_partition_args))
1627 self.assertEqual('recovery', chained_partition_args[0])
1628 self.assertEqual('3', chained_partition_args[1])
1629 self.assertTrue(os.path.exists(chained_partition_args[2]))
1630
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001631 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001632 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1633 self.assertFalse(os.path.exists(pubkey))
1634
1635 common.OPTIONS.info_dict = {
1636 'gki_signing_key_path': pubkey,
1637 'gki_signing_algorithm': 'SHA256_RSA4096',
1638 'gki_signing_signature_args': '--prop foo:bar',
1639 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001640 test_file = tempfile.NamedTemporaryFile()
1641 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001642 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001643
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001644 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001645 pubkey = 'no_testkey_gki.pem'
1646 self.assertFalse(os.path.exists(pubkey))
1647
1648 # Tests it should raise ExternalError if no key found under
1649 # OPTIONS.search_path.
1650 search_path_dir = common.MakeTempDir()
1651 search_pubkey = os.path.join(search_path_dir, pubkey)
1652 self.assertFalse(os.path.exists(search_pubkey))
1653
1654 common.OPTIONS.search_path = search_path_dir
1655 common.OPTIONS.info_dict = {
1656 'gki_signing_key_path': pubkey,
1657 'gki_signing_algorithm': 'SHA256_RSA4096',
1658 'gki_signing_signature_args': '--prop foo:bar',
1659 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001660 test_file = tempfile.NamedTemporaryFile()
1661 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001662 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001663
Kelvin Zhang37a42902022-10-26 12:49:03 -07001664
Tao Bao65b94e92018-10-11 21:57:26 -07001665class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001666 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001667
Tao Bao1c830bf2017-12-25 10:43:47 -08001668 Its format should match between common.py and validate_target_files.py.
1669 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001670
1671 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001672 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001673 # Create a fake dict that contains the fstab info for boot&recovery.
Kelvin Zhang37a42902022-10-26 12:49:03 -07001674 self._info = {"fstab": {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001675 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001676 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1677 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001678 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001679 # Construct the gzipped recovery.img and boot.img
1680 self.recovery_data = bytearray([
1681 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1682 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1683 0x08, 0x00, 0x00, 0x00
1684 ])
1685 # echo -n "boot" | gzip -f | hd
1686 self.boot_data = bytearray([
1687 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1688 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1689 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001690
1691 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1692 loc = os.path.join(self._tempdir, prefix, name)
1693 if not os.path.exists(os.path.dirname(loc)):
1694 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001695 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001696 f.write(data)
1697
1698 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001699 recovery_image = common.File("recovery.img", self.recovery_data)
1700 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001701 self._info["full_recovery_image"] = "true"
1702
1703 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1704 recovery_image, boot_image, self._info)
1705 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1706 self._info)
1707
Tao Bao82490d32019-04-09 00:12:30 -07001708 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001709 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001710 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001711 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001712 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001713 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1714
1715 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1716 recovery_image, boot_image, self._info)
1717 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1718 self._info)
1719 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001720 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001721 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1722 recovery_image, boot_image, self._info)
1723 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1724 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001725
1726
Yifan Hong45433e42019-01-18 13:55:25 -08001727class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001728
Yifan Hong45433e42019-01-18 13:55:25 -08001729 def __init__(self, partition, tgt, src=None):
1730 self.partition = partition
1731 self.tgt = tgt
1732 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001733
Yifan Hong45433e42019-01-18 13:55:25 -08001734 def WriteScript(self, script, _, progress=None,
1735 write_verify_script=False):
1736 if progress:
1737 script.AppendExtra("progress({})".format(progress))
1738 script.AppendExtra("patch({});".format(self.partition))
1739 if write_verify_script:
1740 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001741
Yifan Hong45433e42019-01-18 13:55:25 -08001742 def WritePostInstallVerifyScript(self, script):
1743 script.AppendExtra("verify({});".format(self.partition))
1744
1745
1746class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001747
Yifan Hong45433e42019-01-18 13:55:25 -08001748 def __init__(self, size):
1749 self.blocksize = 4096
1750 self.total_blocks = size // 4096
1751 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1752
1753
1754class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001755
Yifan Hong45433e42019-01-18 13:55:25 -08001756 @staticmethod
1757 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001758 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001759 with output_zip.open('dynamic_partitions_op_list') as op_list:
1760 return [line.decode().strip() for line in op_list.readlines()
1761 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001762
1763 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001764 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001765 self.output_path = common.MakeTempFile(suffix='.zip')
1766
1767 def test_full(self):
1768 target_info = common.LoadDictionaryFromLines("""
1769dynamic_partition_list=system vendor
1770super_partition_groups=group_foo
1771super_group_foo_group_size={group_size}
1772super_group_foo_partition_list=system vendor
1773""".format(group_size=4 * GiB).split("\n"))
1774 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1775 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1776
1777 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001778 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001779 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1780
1781 self.assertEqual(str(self.script).strip(), """
1782assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001783patch(system);
1784verify(system);
1785unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001786patch(vendor);
1787verify(vendor);
1788unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001789""".strip())
1790
1791 lines = self.get_op_list(self.output_path)
1792
1793 remove_all_groups = lines.index("remove_all_groups")
1794 add_group = lines.index("add_group group_foo 4294967296")
1795 add_vendor = lines.index("add vendor group_foo")
1796 add_system = lines.index("add system group_foo")
1797 resize_vendor = lines.index("resize vendor 1073741824")
1798 resize_system = lines.index("resize system 3221225472")
1799
1800 self.assertLess(remove_all_groups, add_group,
1801 "Should add groups after removing all groups")
1802 self.assertLess(add_group, min(add_vendor, add_system),
1803 "Should add partitions after adding group")
1804 self.assertLess(add_system, resize_system,
1805 "Should resize system after adding it")
1806 self.assertLess(add_vendor, resize_vendor,
1807 "Should resize vendor after adding it")
1808
1809 def test_inc_groups(self):
1810 source_info = common.LoadDictionaryFromLines("""
1811super_partition_groups=group_foo group_bar group_baz
1812super_group_foo_group_size={group_foo_size}
1813super_group_bar_group_size={group_bar_size}
1814""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1815 target_info = common.LoadDictionaryFromLines("""
1816super_partition_groups=group_foo group_baz group_qux
1817super_group_foo_group_size={group_foo_size}
1818super_group_baz_group_size={group_baz_size}
1819super_group_qux_group_size={group_qux_size}
1820""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1821 group_qux_size=1 * GiB).split("\n"))
1822
1823 dp_diff = common.DynamicPartitionsDifference(target_info,
1824 block_diffs=[],
1825 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001826 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001827 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1828
1829 lines = self.get_op_list(self.output_path)
1830
1831 removed = lines.index("remove_group group_bar")
1832 shrunk = lines.index("resize_group group_foo 3221225472")
1833 grown = lines.index("resize_group group_baz 4294967296")
1834 added = lines.index("add_group group_qux 1073741824")
1835
Tao Baof1113e92019-06-18 12:10:14 -07001836 self.assertLess(max(removed, shrunk),
1837 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001838 "ops that remove / shrink partitions must precede ops that "
1839 "grow / add partitions")
1840
Yifan Hongbb2658d2019-01-25 12:30:58 -08001841 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001842 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001843dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001844super_partition_groups=group_foo
1845super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001846super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001847""".format(group_foo_size=4 * GiB).split("\n"))
1848 target_info = common.LoadDictionaryFromLines("""
1849dynamic_partition_list=system vendor product odm
1850super_partition_groups=group_foo group_bar
1851super_group_foo_group_size={group_foo_size}
1852super_group_foo_partition_list=system vendor odm
1853super_group_bar_group_size={group_bar_size}
1854super_group_bar_partition_list=product
1855""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1856
1857 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1858 src=FakeSparseImage(1024 * MiB)),
1859 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1860 src=FakeSparseImage(1024 * MiB)),
1861 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1862 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001863 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001864 src=FakeSparseImage(1024 * MiB)),
1865 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1866 src=None)]
1867
1868 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1869 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001870 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001871 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1872
1873 metadata_idx = self.script.lines.index(
1874 'assert(update_dynamic_partitions(package_extract_file('
1875 '"dynamic_partitions_op_list")));')
1876 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1877 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1878 for p in ("product", "system", "odm"):
1879 patch_idx = self.script.lines.index("patch({});".format(p))
1880 verify_idx = self.script.lines.index("verify({});".format(p))
1881 self.assertLess(metadata_idx, patch_idx,
1882 "Should patch {} after updating metadata".format(p))
1883 self.assertLess(patch_idx, verify_idx,
1884 "Should verify {} after patching".format(p))
1885
Justin Yun6151e3f2019-06-25 15:58:13 +09001886 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001887
1888 lines = self.get_op_list(self.output_path)
1889
Justin Yun6151e3f2019-06-25 15:58:13 +09001890 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001891 move_product_out = lines.index("move product default")
1892 shrink = lines.index("resize vendor 536870912")
1893 shrink_group = lines.index("resize_group group_foo 3221225472")
1894 add_group_bar = lines.index("add_group group_bar 1073741824")
1895 add_odm = lines.index("add odm group_foo")
1896 grow_existing = lines.index("resize system 1610612736")
1897 grow_added = lines.index("resize odm 1073741824")
1898 move_product_in = lines.index("move product group_bar")
1899
1900 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1901 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1902
1903 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1904 "Must shrink group after partitions inside group are shrunk"
1905 " / removed")
1906
1907 self.assertLess(add_group_bar, move_product_in,
1908 "Must add partitions to group after group is added")
1909
1910 self.assertLess(max_idx_move_partition_out_foo,
1911 min_idx_move_partition_in_foo,
1912 "Must shrink partitions / remove partitions from group"
1913 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001914
1915 def test_remove_partition(self):
1916 source_info = common.LoadDictionaryFromLines("""
1917blockimgdiff_versions=3,4
1918use_dynamic_partitions=true
1919dynamic_partition_list=foo
1920super_partition_groups=group_foo
1921super_group_foo_group_size={group_foo_size}
1922super_group_foo_partition_list=foo
1923""".format(group_foo_size=4 * GiB).split("\n"))
1924 target_info = common.LoadDictionaryFromLines("""
1925blockimgdiff_versions=3,4
1926use_dynamic_partitions=true
1927super_partition_groups=group_foo
1928super_group_foo_group_size={group_foo_size}
1929""".format(group_foo_size=4 * GiB).split("\n"))
1930
1931 common.OPTIONS.info_dict = target_info
1932 common.OPTIONS.target_info_dict = target_info
1933 common.OPTIONS.source_info_dict = source_info
1934 common.OPTIONS.cache_size = 4 * 4096
1935
1936 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1937 src=DataImage("source", pad=True))]
1938
1939 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1940 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001941 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001942 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1943
1944 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001945 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001946
1947 lines = self.get_op_list(self.output_path)
1948 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001949
1950
1951class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1952 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001953 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001954 'ro.odm.build.date.utc=1578430045',
1955 'ro.odm.build.fingerprint='
1956 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1957 'ro.product.odm.device=coral',
1958 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1959 ]
1960
1961 @staticmethod
1962 def _BuildZipFile(entries):
1963 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001964 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001965 for name, content in entries.items():
1966 input_zip.writestr(name, content)
1967
1968 return input_file
1969
1970 def test_parseBuildProps_noImportStatement(self):
1971 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001972 'ro.odm.build.date.utc=1578430045',
1973 'ro.odm.build.fingerprint='
1974 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1975 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001976 ]
1977 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001978 'ODM/etc/build.prop': '\n'.join(build_prop),
1979 })
1980
Kelvin Zhang928c2342020-09-22 16:15:57 -04001981 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001982 placeholder_values = {
1983 'ro.boot.product.device_name': ['std', 'pro']
1984 }
1985 partition_props = common.PartitionBuildProps.FromInputFile(
1986 input_zip, 'odm', placeholder_values)
1987
1988 self.assertEqual({
1989 'ro.odm.build.date.utc': '1578430045',
1990 'ro.odm.build.fingerprint':
1991 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1992 'ro.product.odm.device': 'coral',
1993 }, partition_props.build_props)
1994
1995 self.assertEqual(set(), partition_props.prop_overrides)
1996
1997 def test_parseBuildProps_singleImportStatement(self):
1998 build_std_prop = [
1999 'ro.product.odm.device=coral',
2000 'ro.product.odm.name=product1',
2001 ]
2002 build_pro_prop = [
2003 'ro.product.odm.device=coralpro',
2004 'ro.product.odm.name=product2',
2005 ]
2006
2007 input_file = self._BuildZipFile({
2008 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2009 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2010 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2011 })
2012
Kelvin Zhang928c2342020-09-22 16:15:57 -04002013 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002014 placeholder_values = {
2015 'ro.boot.product.device_name': 'std'
2016 }
2017 partition_props = common.PartitionBuildProps.FromInputFile(
2018 input_zip, 'odm', placeholder_values)
2019
2020 self.assertEqual({
Kelvin Zhang37a42902022-10-26 12:49:03 -07002021 'ro.odm.build.date.utc': '1578430045',
2022 'ro.odm.build.fingerprint':
2023 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2024 'ro.product.odm.device': 'coral',
2025 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00002026 }, partition_props.build_props)
2027
Kelvin Zhang928c2342020-09-22 16:15:57 -04002028 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002029 placeholder_values = {
2030 'ro.boot.product.device_name': 'pro'
2031 }
2032 partition_props = common.PartitionBuildProps.FromInputFile(
2033 input_zip, 'odm', placeholder_values)
2034
2035 self.assertEqual({
2036 'ro.odm.build.date.utc': '1578430045',
2037 'ro.odm.build.fingerprint':
2038 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2039 'ro.product.odm.device': 'coralpro',
2040 'ro.product.odm.name': 'product2',
2041 }, partition_props.build_props)
2042
2043 def test_parseBuildProps_noPlaceHolders(self):
2044 build_prop = copy.copy(self.odm_build_prop)
2045 input_file = self._BuildZipFile({
2046 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002047 })
2048
Kelvin Zhang928c2342020-09-22 16:15:57 -04002049 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002050 partition_props = common.PartitionBuildProps.FromInputFile(
2051 input_zip, 'odm')
2052
2053 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002054 'ro.odm.build.date.utc': '1578430045',
2055 'ro.odm.build.fingerprint':
2056 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2057 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002058 }, partition_props.build_props)
2059
Tianjie Xu9afb2212020-05-10 21:48:15 +00002060 self.assertEqual(set(), partition_props.prop_overrides)
2061
2062 def test_parseBuildProps_multipleImportStatements(self):
2063 build_prop = copy.deepcopy(self.odm_build_prop)
2064 build_prop.append(
2065 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2066
2067 build_std_prop = [
2068 'ro.product.odm.device=coral',
2069 ]
2070 build_pro_prop = [
2071 'ro.product.odm.device=coralpro',
2072 ]
2073
2074 product1_prop = [
2075 'ro.product.odm.name=product1',
2076 'ro.product.not_care=not_care',
2077 ]
2078
2079 product2_prop = [
2080 'ro.product.odm.name=product2',
2081 'ro.product.not_care=not_care',
2082 ]
2083
2084 input_file = self._BuildZipFile({
2085 'ODM/etc/build.prop': '\n'.join(build_prop),
2086 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2087 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2088 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2089 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2090 })
2091
Kelvin Zhang928c2342020-09-22 16:15:57 -04002092 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002093 placeholder_values = {
2094 'ro.boot.product.device_name': 'std',
2095 'ro.boot.product.product_name': 'product1',
2096 'ro.boot.product.not_care': 'not_care',
2097 }
2098 partition_props = common.PartitionBuildProps.FromInputFile(
2099 input_zip, 'odm', placeholder_values)
2100
2101 self.assertEqual({
2102 'ro.odm.build.date.utc': '1578430045',
2103 'ro.odm.build.fingerprint':
2104 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2105 'ro.product.odm.device': 'coral',
2106 'ro.product.odm.name': 'product1'
2107 }, partition_props.build_props)
2108
Kelvin Zhang928c2342020-09-22 16:15:57 -04002109 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002110 placeholder_values = {
2111 'ro.boot.product.device_name': 'pro',
2112 'ro.boot.product.product_name': 'product2',
2113 'ro.boot.product.not_care': 'not_care',
2114 }
2115 partition_props = common.PartitionBuildProps.FromInputFile(
2116 input_zip, 'odm', placeholder_values)
2117
2118 self.assertEqual({
2119 'ro.odm.build.date.utc': '1578430045',
2120 'ro.odm.build.fingerprint':
2121 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2122 'ro.product.odm.device': 'coralpro',
2123 'ro.product.odm.name': 'product2'
2124 }, partition_props.build_props)
2125
2126 def test_parseBuildProps_defineAfterOverride(self):
2127 build_prop = copy.deepcopy(self.odm_build_prop)
2128 build_prop.append('ro.product.odm.device=coral')
2129
2130 build_std_prop = [
2131 'ro.product.odm.device=coral',
2132 ]
2133 build_pro_prop = [
2134 'ro.product.odm.device=coralpro',
2135 ]
2136
2137 input_file = self._BuildZipFile({
2138 'ODM/etc/build.prop': '\n'.join(build_prop),
2139 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2140 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2141 })
2142
Kelvin Zhang928c2342020-09-22 16:15:57 -04002143 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002144 placeholder_values = {
2145 'ro.boot.product.device_name': 'std',
2146 }
2147
2148 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2149 input_zip, 'odm', placeholder_values)
2150
2151 def test_parseBuildProps_duplicateOverride(self):
2152 build_prop = copy.deepcopy(self.odm_build_prop)
2153 build_prop.append(
2154 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2155
2156 build_std_prop = [
2157 'ro.product.odm.device=coral',
2158 'ro.product.odm.name=product1',
2159 ]
2160 build_pro_prop = [
2161 'ro.product.odm.device=coralpro',
2162 ]
2163
2164 product1_prop = [
2165 'ro.product.odm.name=product1',
2166 ]
2167
2168 product2_prop = [
2169 'ro.product.odm.name=product2',
2170 ]
2171
2172 input_file = self._BuildZipFile({
2173 'ODM/etc/build.prop': '\n'.join(build_prop),
2174 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2175 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2176 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2177 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2178 })
2179
Kelvin Zhang928c2342020-09-22 16:15:57 -04002180 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002181 placeholder_values = {
2182 'ro.boot.product.device_name': 'std',
2183 'ro.boot.product.product_name': 'product1',
2184 }
2185 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2186 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08002187
2188 def test_partitionBuildProps_fromInputFile_deepcopy(self):
2189 build_prop = [
2190 'ro.odm.build.date.utc=1578430045',
2191 'ro.odm.build.fingerprint='
2192 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2193 'ro.product.odm.device=coral',
2194 ]
2195 input_file = self._BuildZipFile({
2196 'ODM/etc/build.prop': '\n'.join(build_prop),
2197 })
2198
2199 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
2200 placeholder_values = {
2201 'ro.boot.product.device_name': ['std', 'pro']
2202 }
2203 partition_props = common.PartitionBuildProps.FromInputFile(
2204 input_zip, 'odm', placeholder_values)
2205
2206 copied_props = copy.deepcopy(partition_props)
2207 self.assertEqual({
Kelvin Zhang37a42902022-10-26 12:49:03 -07002208 'ro.odm.build.date.utc': '1578430045',
2209 'ro.odm.build.fingerprint':
2210 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2211 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08002212 }, copied_props.build_props)