blob: 2bd4f36bd9fd6eceff8e3547961e8c20f10f2cd0 [file] [log] [blame]
Dan Albert8e0178d2015-01-27 15:53:15 -08001#
2# Copyright (C) 2015 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
Tao Baofc7e0e02018-02-13 13:54:02 -080016
Tao Baoa57ab9f2018-08-24 12:08:38 -070017import copy
Dan Albert8e0178d2015-01-27 15:53:15 -080018import os
Tao Bao17e4e612018-02-16 17:12:54 -080019import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080020import tempfile
Tianjie20dd8f22020-04-19 15:51:16 -070021import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080022import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080023from hashlib import sha1
Kelvin Zhang8aa65252023-08-30 18:29:59 -070024from typing import BinaryIO
Tao Bao31b08072017-11-08 15:50:59 -080025
Dan Albert8e0178d2015-01-27 15:53:15 -080026import common
Tao Bao04e1f012018-02-04 12:13:35 -080027import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070028import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070029from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080030from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080031
Tao Bao04e1f012018-02-04 12:13:35 -080032
Tao Bao31b08072017-11-08 15:50:59 -080033KiB = 1024
34MiB = 1024 * KiB
35GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080036
Tao Bao1c830bf2017-12-25 10:43:47 -080037
Kelvin Zhang8aa65252023-08-30 18:29:59 -070038def get_2gb_file():
Tao Bao31b08072017-11-08 15:50:59 -080039 size = int(2 * GiB + 1)
40 block_size = 4 * KiB
41 step_size = 4 * MiB
Kelvin Zhang8aa65252023-08-30 18:29:59 -070042 tmpfile = tempfile.NamedTemporaryFile()
43 tmpfile.truncate(size)
Tao Bao31b08072017-11-08 15:50:59 -080044 for _ in range(0, size, step_size):
Kelvin Zhang8aa65252023-08-30 18:29:59 -070045 tmpfile.write(os.urandom(block_size))
46 tmpfile.seek(step_size - block_size, os.SEEK_CUR)
47 return tmpfile
48
49
50def hash_file(filename):
51 sha1_hash = sha1()
52 with open(filename, "rb") as fp:
53 for data in iter(lambda: fp.read(4*MiB), b''):
54 sha1_hash.update(data)
55 return sha1_hash
Tao Baof3282b42015-04-01 11:21:55 -070056
Dan Albert8e0178d2015-01-27 15:53:15 -080057
Tao Bao1c320f82019-10-04 23:25:12 -070058class BuildInfoTest(test_utils.ReleaseToolsTestCase):
59
Tianjiefdda51d2021-05-05 14:46:35 -070060 TEST_INFO_FINGERPRINT_DICT = {
61 'build.prop': common.PartitionBuildProps.FromDictionary(
62 'system', {
63 'ro.product.brand': 'product-brand',
64 'ro.product.name': 'product-name',
65 'ro.product.device': 'product-device',
66 'ro.build.version.release': 'version-release',
67 'ro.build.id': 'build-id',
68 'ro.build.version.incremental': 'version-incremental',
69 'ro.build.type': 'build-type',
70 'ro.build.tags': 'build-tags',
71 'ro.build.version.sdk': 30,
72 }
73 ),
74 }
75
Tao Bao1c320f82019-10-04 23:25:12 -070076 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000077 'build.prop': common.PartitionBuildProps.FromDictionary(
78 'system', {
79 'ro.product.device': 'product-device',
80 'ro.product.name': 'product-name',
81 'ro.build.fingerprint': 'build-fingerprint',
82 'ro.build.foo': 'build-foo'}
83 ),
84 'system.build.prop': common.PartitionBuildProps.FromDictionary(
85 'system', {
86 'ro.product.system.brand': 'product-brand',
87 'ro.product.system.name': 'product-name',
88 'ro.product.system.device': 'product-device',
89 'ro.system.build.version.release': 'version-release',
90 'ro.system.build.id': 'build-id',
91 'ro.system.build.version.incremental': 'version-incremental',
92 'ro.system.build.type': 'build-type',
93 'ro.system.build.tags': 'build-tags',
94 'ro.system.build.foo': 'build-foo'}
95 ),
96 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
97 'vendor', {
98 'ro.product.vendor.brand': 'vendor-product-brand',
99 'ro.product.vendor.name': 'vendor-product-name',
100 'ro.product.vendor.device': 'vendor-product-device',
101 'ro.vendor.build.version.release': 'vendor-version-release',
102 'ro.vendor.build.id': 'vendor-build-id',
103 'ro.vendor.build.version.incremental':
104 'vendor-version-incremental',
105 'ro.vendor.build.type': 'vendor-build-type',
106 'ro.vendor.build.tags': 'vendor-build-tags'}
107 ),
108 'property1': 'value1',
109 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -0700110 }
111
112 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000113 'build.prop': common.PartitionBuildProps.FromDictionary(
114 'system', {
115 'ro.product.name': 'product-name',
116 'ro.build.thumbprint': 'build-thumbprint',
117 'ro.build.bar': 'build-bar'}
118 ),
119 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
120 'vendor', {
121 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
122 ),
123 'property1': 'value1',
124 'property2': 4096,
125 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700126 }
127
128 TEST_OEM_DICTS = [
129 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000130 'ro.product.brand': 'brand1',
131 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700132 },
133 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000134 'ro.product.brand': 'brand2',
135 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700136 },
137 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000138 'ro.product.brand': 'brand3',
139 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700140 },
141 ]
142
Steven Laver8e2086e2020-04-27 16:26:31 -0700143 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000144 'build.prop': common.PartitionBuildProps.FromDictionary(
145 'system', {
146 'ro.build.fingerprint': 'build-fingerprint',
147 'ro.product.property_source_order':
148 'product,odm,vendor,system_ext,system'}
149 ),
150 'system.build.prop': common.PartitionBuildProps.FromDictionary(
151 'system', {
152 'ro.product.system.device': 'system-product-device'}
153 ),
154 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
155 'vendor', {
156 'ro.product.vendor.device': 'vendor-product-device'}
157 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700158 }
159
160 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000161 'build.prop': common.PartitionBuildProps.FromDictionary(
162 'system', {
163 'ro.build.fingerprint': 'build-fingerprint',
164 'ro.product.property_source_order':
165 'product,product_services,odm,vendor,system',
166 'ro.build.version.release': '10',
167 'ro.build.version.codename': 'REL'}
168 ),
169 'system.build.prop': common.PartitionBuildProps.FromDictionary(
170 'system', {
171 'ro.product.system.device': 'system-product-device'}
172 ),
173 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
174 'vendor', {
175 'ro.product.vendor.device': 'vendor-product-device'}
176 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700177 }
178
179 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000180 'build.prop': common.PartitionBuildProps.FromDictionary(
181 'system', {
182 'ro.product.device': 'product-device',
183 'ro.build.fingerprint': 'build-fingerprint',
184 'ro.build.version.release': '9',
185 'ro.build.version.codename': 'REL'}
186 ),
187 'system.build.prop': common.PartitionBuildProps.FromDictionary(
188 'system', {
189 'ro.product.system.device': 'system-product-device'}
190 ),
191 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
192 'vendor', {
193 'ro.product.vendor.device': 'vendor-product-device'}
194 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700195 }
196
Tao Bao1c320f82019-10-04 23:25:12 -0700197 def test_init(self):
198 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
199 self.assertEqual('product-device', target_info.device)
200 self.assertEqual('build-fingerprint', target_info.fingerprint)
201 self.assertFalse(target_info.is_ab)
202 self.assertIsNone(target_info.oem_props)
203
204 def test_init_with_oem_props(self):
205 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
206 self.TEST_OEM_DICTS)
207 self.assertEqual('device1', target_info.device)
208 self.assertEqual('brand1/product-name/device1:build-thumbprint',
209 target_info.fingerprint)
210
211 # Swap the order in oem_dicts, which would lead to different BuildInfo.
212 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
213 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
214 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
215 oem_dicts)
216 self.assertEqual('device3', target_info.device)
217 self.assertEqual('brand3/product-name/device3:build-thumbprint',
218 target_info.fingerprint)
219
Tao Bao1c320f82019-10-04 23:25:12 -0700220 def test_init_badFingerprint(self):
221 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000222 info_dict['build.prop'].build_props[
223 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700224 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
225
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000226 info_dict['build.prop'].build_props[
227 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700228 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
229
Tianjiefdda51d2021-05-05 14:46:35 -0700230 def test_init_goodFingerprint(self):
231 info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
232 build_info = common.BuildInfo(info_dict)
233 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700234 'product-brand/product-name/product-device:version-release/build-id/'
235 'version-incremental:build-type/build-tags', build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700236
237 build_props = info_dict['build.prop'].build_props
238 del build_props['ro.build.id']
239 build_props['ro.build.legacy.id'] = 'legacy-build-id'
240 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
241 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700242 'product-brand/product-name/product-device:version-release/'
243 'legacy-build-id/version-incremental:build-type/build-tags',
244 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700245
246 self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
247 False)
248
249 info_dict['avb_enable'] = 'true'
250 info_dict['vbmeta_digest'] = 'abcde12345'
251 build_info = common.BuildInfo(info_dict, use_legacy_id=False)
252 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700253 'product-brand/product-name/product-device:version-release/'
254 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
255 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700256
Tao Bao1c320f82019-10-04 23:25:12 -0700257 def test___getitem__(self):
258 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
259 self.assertEqual('value1', target_info['property1'])
260 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000261 self.assertEqual('build-foo',
262 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700263
264 def test___getitem__with_oem_props(self):
265 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
266 self.TEST_OEM_DICTS)
267 self.assertEqual('value1', target_info['property1'])
268 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000269 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700270
271 def test___setitem__(self):
272 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
273 self.assertEqual('value1', target_info['property1'])
274 target_info['property1'] = 'value2'
275 self.assertEqual('value2', target_info['property1'])
276
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000277 self.assertEqual('build-foo',
278 target_info['build.prop'].GetProp('ro.build.foo'))
279 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
280 self.assertEqual('build-bar',
281 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700282
283 def test_get(self):
284 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
285 self.assertEqual('value1', target_info.get('property1'))
286 self.assertEqual(4096, target_info.get('property2'))
287 self.assertEqual(4096, target_info.get('property2', 1024))
288 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000289 self.assertEqual('build-foo',
290 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700291
292 def test_get_with_oem_props(self):
293 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
294 self.TEST_OEM_DICTS)
295 self.assertEqual('value1', target_info.get('property1'))
296 self.assertEqual(4096, target_info.get('property2'))
297 self.assertEqual(4096, target_info.get('property2', 1024))
298 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000299 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700300
301 def test_items(self):
302 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
303 items = target_info.items()
304 self.assertIn(('property1', 'value1'), items)
305 self.assertIn(('property2', 4096), items)
306
307 def test_GetBuildProp(self):
308 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
309 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
310 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
311 'ro.build.nonexistent')
312
313 def test_GetBuildProp_with_oem_props(self):
314 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
315 self.TEST_OEM_DICTS)
316 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
317 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
318 'ro.build.nonexistent')
319
Daniel Normand5fe8622020-01-08 17:01:11 -0800320 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700321 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800322 self.assertEqual(
323 target_info.GetPartitionFingerprint('vendor'),
324 'vendor-product-brand/vendor-product-name/vendor-product-device'
325 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
326 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700327
Daniel Normand5fe8622020-01-08 17:01:11 -0800328 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700329 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800330 self.assertEqual(
331 target_info.GetPartitionFingerprint('system_other'),
332 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700333
Daniel Normand5fe8622020-01-08 17:01:11 -0800334 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
335 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000336 info_dict['vendor.build.prop'].build_props[
337 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800338 target_info = common.BuildInfo(info_dict, None)
339 self.assertEqual(
340 target_info.GetPartitionFingerprint('vendor'),
341 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700342
343 def test_WriteMountOemScript(self):
344 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
345 self.TEST_OEM_DICTS)
346 script_writer = test_utils.MockScriptWriter()
347 target_info.WriteMountOemScript(script_writer)
348 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
349
350 def test_WriteDeviceAssertions(self):
351 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
352 script_writer = test_utils.MockScriptWriter()
353 target_info.WriteDeviceAssertions(script_writer, False)
354 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
355
356 def test_WriteDeviceAssertions_with_oem_props(self):
357 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
358 self.TEST_OEM_DICTS)
359 script_writer = test_utils.MockScriptWriter()
360 target_info.WriteDeviceAssertions(script_writer, False)
361 self.assertEqual(
362 [
363 ('AssertOemProperty', 'ro.product.device',
364 ['device1', 'device2', 'device3'], False),
365 ('AssertOemProperty', 'ro.product.brand',
366 ['brand1', 'brand2', 'brand3'], False),
367 ],
368 script_writer.lines)
369
Steven Laver8e2086e2020-04-27 16:26:31 -0700370 def test_ResolveRoProductProperty_FromVendor(self):
371 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
372 info = common.BuildInfo(info_dict, None)
373 self.assertEqual('vendor-product-device',
374 info.GetBuildProp('ro.product.device'))
375
376 def test_ResolveRoProductProperty_FromSystem(self):
377 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000378 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700379 info = common.BuildInfo(info_dict, None)
380 self.assertEqual('system-product-device',
381 info.GetBuildProp('ro.product.device'))
382
383 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
384 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000385 info_dict['build.prop'].build_props[
386 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700387 with self.assertRaisesRegexp(common.ExternalError,
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700388 'Invalid ro.product.property_source_order'):
Steven Laver8e2086e2020-04-27 16:26:31 -0700389 info = common.BuildInfo(info_dict, None)
390 info.GetBuildProp('ro.product.device')
391
392 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
393 info_dict = copy.deepcopy(
394 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
395 info = common.BuildInfo(info_dict, None)
396 self.assertEqual('vendor-product-device',
397 info.GetBuildProp('ro.product.device'))
398
399 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
400 info_dict = copy.deepcopy(
401 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
402 info = common.BuildInfo(info_dict, None)
403 self.assertEqual('product-device',
404 info.GetBuildProp('ro.product.device'))
405
Tao Bao1c320f82019-10-04 23:25:12 -0700406
Tao Bao65b94e92018-10-11 21:57:26 -0700407class CommonZipTest(test_utils.ReleaseToolsTestCase):
408
Tao Bao31b08072017-11-08 15:50:59 -0800409 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700410 test_file_name=None, expected_stat=None, expected_mode=0o644,
411 expected_compress_type=zipfile.ZIP_STORED):
412 # Verify the stat if present.
413 if test_file_name is not None:
414 new_stat = os.stat(test_file_name)
415 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
416 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
417
418 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400419 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700420
421 # Verify the timestamp.
422 info = zip_file.getinfo(arcname)
423 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
424
425 # Verify the file mode.
426 mode = (info.external_attr >> 16) & 0o777
427 self.assertEqual(mode, expected_mode)
428
429 # Verify the compress type.
430 self.assertEqual(info.compress_type, expected_compress_type)
431
432 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800433 entry = zip_file.open(arcname)
434 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700435 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800436 sha1_hash.update(chunk)
437 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700438 self.assertIsNone(zip_file.testzip())
439
Dan Albert8e0178d2015-01-27 15:53:15 -0800440 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700441 with tempfile.NamedTemporaryFile() as test_file:
442 test_file_name = test_file.name
443 for data in contents:
444 test_file.write(bytes(data))
445 return self._test_ZipWriteFile(test_file_name, extra_zipwrite_args)
446
447 def _test_ZipWriteFile(self, test_file_name, extra_zipwrite_args=None):
Dan Albert8e0178d2015-01-27 15:53:15 -0800448 extra_zipwrite_args = dict(extra_zipwrite_args or {})
449
450 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800451 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700452
453 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800454 zip_file_name = zip_file.name
455
456 # File names within an archive strip the leading slash.
457 arcname = extra_zipwrite_args.get("arcname", test_file_name)
458 if arcname[0] == "/":
459 arcname = arcname[1:]
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700460 sha1_hash = hash_file(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800461
462 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400463 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800464
465 try:
Dan Albert8e0178d2015-01-27 15:53:15 -0800466 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700467 expected_compress_type = extra_zipwrite_args.get("compress_type",
468 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800469
Kelvin Zhangea84d422023-04-11 12:50:34 -0700470 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
471 # the timestamp after writing.
472 os.utime(test_file_name, (1234567, 1234567))
473 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800474 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000475 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800476
Tao Bao31b08072017-11-08 15:50:59 -0800477 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
478 test_file_name, expected_stat, expected_mode,
479 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800480 finally:
Dan Albert8e0178d2015-01-27 15:53:15 -0800481 os.remove(zip_file_name)
482
Tao Baof3282b42015-04-01 11:21:55 -0700483 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
484 extra_args = dict(extra_args or {})
485
486 zip_file = tempfile.NamedTemporaryFile(delete=False)
487 zip_file_name = zip_file.name
488 zip_file.close()
489
Kelvin Zhang928c2342020-09-22 16:15:57 -0400490 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700491
492 try:
493 expected_compress_type = extra_args.get("compress_type",
494 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700495 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700496 arcname = zinfo_or_arcname
497 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700498 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700499 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700500 if zinfo_or_arcname.external_attr:
501 zinfo_perms = zinfo_or_arcname.external_attr >> 16
502 else:
503 zinfo_perms = 0o600
504 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700505
Tao Bao58c1b962015-05-20 09:32:18 -0700506 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000507 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700508
Tao Bao31b08072017-11-08 15:50:59 -0800509 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700510 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700511 expected_compress_type=expected_compress_type)
512 finally:
513 os.remove(zip_file_name)
514
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700515 def _test_ZipWriteStr_large_file(self, large_file: BinaryIO, small, extra_args=None):
Tao Baof3282b42015-04-01 11:21:55 -0700516 extra_args = dict(extra_args or {})
517
518 zip_file = tempfile.NamedTemporaryFile(delete=False)
519 zip_file_name = zip_file.name
520
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700521 test_file_name = large_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700522
523 arcname_large = test_file_name
524 arcname_small = "bar"
525
526 # File names within an archive strip the leading slash.
527 if arcname_large[0] == "/":
528 arcname_large = arcname_large[1:]
529
530 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400531 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700532
533 try:
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700534 sha1_hash = hash_file(test_file_name)
Tao Baof3282b42015-04-01 11:21:55 -0700535
Kelvin Zhangea84d422023-04-11 12:50:34 -0700536 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
537 # the timestamp after writing.
538 os.utime(test_file_name, (1234567, 1234567))
Tao Baof3282b42015-04-01 11:21:55 -0700539 expected_stat = os.stat(test_file_name)
540 expected_mode = 0o644
541 expected_compress_type = extra_args.get("compress_type",
542 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700543
544 common.ZipWrite(zip_file, test_file_name, **extra_args)
545 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000546 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700547
548 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800549 self._verify(zip_file, zip_file_name, arcname_large,
550 sha1_hash.hexdigest(), test_file_name, expected_stat,
551 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700552
553 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800554 self._verify(zip_file, zip_file_name, arcname_small,
555 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700556 expected_compress_type=expected_compress_type)
557 finally:
558 os.remove(zip_file_name)
Tao Baof3282b42015-04-01 11:21:55 -0700559
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000560 def _test_reset_ZIP64_LIMIT(self, func, *args):
561 default_limit = (1 << 31) - 1
562 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
563 func(*args)
564 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
565
Dan Albert8e0178d2015-01-27 15:53:15 -0800566 def test_ZipWrite(self):
567 file_contents = os.urandom(1024)
568 self._test_ZipWrite(file_contents)
569
570 def test_ZipWrite_with_opts(self):
571 file_contents = os.urandom(1024)
572 self._test_ZipWrite(file_contents, {
573 "arcname": "foobar",
574 "perms": 0o777,
575 "compress_type": zipfile.ZIP_DEFLATED,
576 })
Tao Baof3282b42015-04-01 11:21:55 -0700577 self._test_ZipWrite(file_contents, {
578 "arcname": "foobar",
579 "perms": 0o700,
580 "compress_type": zipfile.ZIP_STORED,
581 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800582
583 def test_ZipWrite_large_file(self):
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700584 with get_2gb_file() as tmpfile:
585 self._test_ZipWriteFile(tmpfile.name, {
586 "compress_type": zipfile.ZIP_DEFLATED,
587 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800588
589 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000590 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
Tao Baof3282b42015-04-01 11:21:55 -0700591
592 def test_ZipWriteStr(self):
593 random_string = os.urandom(1024)
594 # Passing arcname
595 self._test_ZipWriteStr("foo", random_string)
596
597 # Passing zinfo
598 zinfo = zipfile.ZipInfo(filename="foo")
599 self._test_ZipWriteStr(zinfo, random_string)
600
601 # Timestamp in the zinfo should be overwritten.
602 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
603 self._test_ZipWriteStr(zinfo, random_string)
604
605 def test_ZipWriteStr_with_opts(self):
606 random_string = os.urandom(1024)
607 # Passing arcname
608 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700609 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700610 "compress_type": zipfile.ZIP_DEFLATED,
611 })
Tao Bao58c1b962015-05-20 09:32:18 -0700612 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700613 "compress_type": zipfile.ZIP_STORED,
614 })
615
616 # Passing zinfo
617 zinfo = zipfile.ZipInfo(filename="foo")
618 self._test_ZipWriteStr(zinfo, random_string, {
619 "compress_type": zipfile.ZIP_DEFLATED,
620 })
621 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700622 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700623 "compress_type": zipfile.ZIP_STORED,
624 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700625 self._test_ZipWriteStr(zinfo, random_string, {
626 "perms": 0o000,
627 "compress_type": zipfile.ZIP_STORED,
628 })
Tao Baof3282b42015-04-01 11:21:55 -0700629
630 def test_ZipWriteStr_large_file(self):
631 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
632 # the workaround. We will only test the case of writing a string into a
633 # large archive.
Tao Baof3282b42015-04-01 11:21:55 -0700634 short_string = os.urandom(1024)
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700635 with get_2gb_file() as large_file:
636 self._test_ZipWriteStr_large_file(large_file, short_string, {
637 "compress_type": zipfile.ZIP_DEFLATED,
638 })
Tao Baof3282b42015-04-01 11:21:55 -0700639
640 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000641 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700642 zinfo = zipfile.ZipInfo(filename="foo")
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000643 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700644
645 def test_bug21309935(self):
646 zip_file = tempfile.NamedTemporaryFile(delete=False)
647 zip_file_name = zip_file.name
648 zip_file.close()
649
650 try:
651 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400652 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700653 # Default perms should be 0o644 when passing the filename.
654 common.ZipWriteStr(zip_file, "foo", random_string)
655 # Honor the specified perms.
656 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
657 # The perms in zinfo should be untouched.
658 zinfo = zipfile.ZipInfo(filename="baz")
659 zinfo.external_attr = 0o740 << 16
660 common.ZipWriteStr(zip_file, zinfo, random_string)
661 # Explicitly specified perms has the priority.
662 zinfo = zipfile.ZipInfo(filename="qux")
663 zinfo.external_attr = 0o700 << 16
664 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000665 common.ZipClose(zip_file)
Tao Bao58c1b962015-05-20 09:32:18 -0700666
Tao Bao31b08072017-11-08 15:50:59 -0800667 self._verify(zip_file, zip_file_name, "foo",
668 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700669 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800670 self._verify(zip_file, zip_file_name, "bar",
671 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700672 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800673 self._verify(zip_file, zip_file_name, "baz",
674 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700675 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800676 self._verify(zip_file, zip_file_name, "qux",
677 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700678 expected_mode=0o400)
679 finally:
680 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700681
Tao Bao82490d32019-04-09 00:12:30 -0700682 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800683 def test_ZipDelete(self):
684 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
685 output_zip = zipfile.ZipFile(zip_file.name, 'w',
686 compression=zipfile.ZIP_DEFLATED)
687 with tempfile.NamedTemporaryFile() as entry_file:
688 entry_file.write(os.urandom(1024))
689 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
690 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
691 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000692 common.ZipClose(output_zip)
Tao Bao89d7ab22017-12-14 17:05:33 -0800693 zip_file.close()
694
695 try:
696 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400697 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800698 entries = check_zip.namelist()
699 self.assertTrue('Test1' in entries)
700 self.assertFalse('Test2' in entries)
701 self.assertTrue('Test3' in entries)
702
Tao Bao986ee862018-10-04 15:46:16 -0700703 self.assertRaises(
704 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400705 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800706 entries = check_zip.namelist()
707 self.assertTrue('Test1' in entries)
708 self.assertFalse('Test2' in entries)
709 self.assertTrue('Test3' in entries)
710
711 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400712 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800713 entries = check_zip.namelist()
714 self.assertTrue('Test1' in entries)
715 self.assertFalse('Test2' in entries)
716 self.assertFalse('Test3' in entries)
717
718 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400719 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800720 entries = check_zip.namelist()
721 self.assertFalse('Test1' in entries)
722 self.assertFalse('Test2' in entries)
723 self.assertFalse('Test3' in entries)
724 finally:
725 os.remove(zip_file.name)
726
Tao Bao0ff15de2019-03-20 11:26:06 -0700727 @staticmethod
728 def _test_UnzipTemp_createZipFile():
729 zip_file = common.MakeTempFile(suffix='.zip')
730 output_zip = zipfile.ZipFile(
731 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
732 contents = os.urandom(1024)
733 with tempfile.NamedTemporaryFile() as entry_file:
734 entry_file.write(contents)
735 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
736 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
737 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
738 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
739 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000740 common.ZipClose(output_zip)
741 common.ZipClose(output_zip)
Tao Bao0ff15de2019-03-20 11:26:06 -0700742 return zip_file
743
Tao Bao82490d32019-04-09 00:12:30 -0700744 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700745 def test_UnzipTemp(self):
746 zip_file = self._test_UnzipTemp_createZipFile()
747 unzipped_dir = common.UnzipTemp(zip_file)
748 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
749 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
750 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
751 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
752 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
753
Tao Bao82490d32019-04-09 00:12:30 -0700754 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700755 def test_UnzipTemp_withPatterns(self):
756 zip_file = self._test_UnzipTemp_createZipFile()
757
758 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
759 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
760 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
761 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
762 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
763 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
764
765 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
766 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
767 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
768 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
769 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
770 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
771
772 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
773 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
774 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
775 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
776 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
777 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
778
779 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
780 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
781 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
782 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
783 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
784 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
785
786 def test_UnzipTemp_withEmptyPatterns(self):
787 zip_file = self._test_UnzipTemp_createZipFile()
788 unzipped_dir = common.UnzipTemp(zip_file, [])
789 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
790 self.assertFalse(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
Tao Bao82490d32019-04-09 00:12:30 -0700795 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700796 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
797 zip_file = self._test_UnzipTemp_createZipFile()
798 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
799 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
800 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
801 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
802 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
803 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
804
805 def test_UnzipTemp_withNoMatchingPatterns(self):
806 zip_file = self._test_UnzipTemp_createZipFile()
807 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
808 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
809 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
810 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
811 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
812 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
813
Tao Bao89d7ab22017-12-14 17:05:33 -0800814
Tao Bao65b94e92018-10-11 21:57:26 -0700815class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800816 """Tests the APK utils related functions."""
817
818 APKCERTS_TXT1 = (
819 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
820 ' private_key="certs/devkey.pk8"\n'
821 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700822 ' certificate="build/make/target/product/security/platform.x509.pem"'
823 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800824 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
825 )
826
827 APKCERTS_CERTMAP1 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700828 'RecoveryLocalizer.apk': 'certs/devkey',
829 'Settings.apk': 'build/make/target/product/security/platform',
830 'TV.apk': 'PRESIGNED',
Tao Bao818ddf52018-01-05 11:17:34 -0800831 }
832
833 APKCERTS_TXT2 = (
834 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
835 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
836 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
837 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
838 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
839 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
840 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
841 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
842 )
843
844 APKCERTS_CERTMAP2 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700845 'Compressed1.apk': 'certs/compressed1',
846 'Compressed2a.apk': 'certs/compressed2',
847 'Compressed2b.apk': 'certs/compressed2',
848 'Compressed3.apk': 'certs/compressed3',
Tao Bao818ddf52018-01-05 11:17:34 -0800849 }
850
851 APKCERTS_TXT3 = (
852 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
853 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
854 )
855
856 APKCERTS_CERTMAP3 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700857 'Compressed4.apk': 'certs/compressed4',
Tao Bao818ddf52018-01-05 11:17:34 -0800858 }
859
Bill Peckham5c7b0342020-04-03 15:36:23 -0700860 # Test parsing with no optional fields, both optional fields, and only the
861 # partition optional field.
862 APKCERTS_TXT4 = (
863 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
864 ' private_key="certs/devkey.pk8"\n'
865 'name="Settings.apk"'
866 ' certificate="build/make/target/product/security/platform.x509.pem"'
867 ' private_key="build/make/target/product/security/platform.pk8"'
868 ' compressed="gz" partition="system"\n'
869 'name="TV.apk" certificate="PRESIGNED" private_key=""'
870 ' partition="product"\n'
871 )
872
873 APKCERTS_CERTMAP4 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700874 'RecoveryLocalizer.apk': 'certs/devkey',
875 'Settings.apk': 'build/make/target/product/security/platform',
876 'TV.apk': 'PRESIGNED',
Bill Peckham5c7b0342020-04-03 15:36:23 -0700877 }
878
Tao Bao17e4e612018-02-16 17:12:54 -0800879 def setUp(self):
880 self.testdata_dir = test_utils.get_testdata_dir()
881
Tao Bao818ddf52018-01-05 11:17:34 -0800882 @staticmethod
883 def _write_apkcerts_txt(apkcerts_txt, additional=None):
884 if additional is None:
885 additional = []
886 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400887 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800888 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
889 for entry in additional:
890 target_files_zip.writestr(entry, '')
891 return target_files
892
893 def test_ReadApkCerts_NoncompressedApks(self):
894 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400895 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800896 certmap, ext = common.ReadApkCerts(input_zip)
897
898 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
899 self.assertIsNone(ext)
900
901 def test_ReadApkCerts_CompressedApks(self):
902 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
903 # not stored in '.gz' format, so it shouldn't be considered as installed.
904 target_files = self._write_apkcerts_txt(
905 self.APKCERTS_TXT2,
906 ['Compressed1.apk.gz', 'Compressed3.apk'])
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_CERTMAP2, certmap)
912 self.assertEqual('.gz', ext)
913
914 # Alternative case with '.xz'.
915 target_files = self._write_apkcerts_txt(
916 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
917
Kelvin Zhang928c2342020-09-22 16:15:57 -0400918 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800919 certmap, ext = common.ReadApkCerts(input_zip)
920
921 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
922 self.assertEqual('.xz', ext)
923
924 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
925 target_files = self._write_apkcerts_txt(
926 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
927 ['Compressed1.apk.gz', 'Compressed3.apk'])
928
Kelvin Zhang928c2342020-09-22 16:15:57 -0400929 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800930 certmap, ext = common.ReadApkCerts(input_zip)
931
932 certmap_merged = self.APKCERTS_CERTMAP1.copy()
933 certmap_merged.update(self.APKCERTS_CERTMAP2)
934 self.assertDictEqual(certmap_merged, certmap)
935 self.assertEqual('.gz', ext)
936
937 def test_ReadApkCerts_MultipleCompressionMethods(self):
938 target_files = self._write_apkcerts_txt(
939 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
940 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
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
945 def test_ReadApkCerts_MismatchingKeys(self):
946 malformed_apkcerts_txt = (
947 'name="App1.apk" certificate="certs/cert1.x509.pem"'
948 ' private_key="certs/cert2.pk8"\n'
949 )
950 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
951
Kelvin Zhang928c2342020-09-22 16:15:57 -0400952 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800953 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
954
Bill Peckham5c7b0342020-04-03 15:36:23 -0700955 def test_ReadApkCerts_WithWithoutOptionalFields(self):
956 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400957 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700958 certmap, ext = common.ReadApkCerts(input_zip)
959
960 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
961 self.assertIsNone(ext)
962
Tao Bao04e1f012018-02-04 12:13:35 -0800963 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800964 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
965 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800966 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800967 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
968
969 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800970 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800971 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
972
Tao Bao82490d32019-04-09 00:12:30 -0700973 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700974 def test_ExtractAvbPublicKey(self):
975 privkey = os.path.join(self.testdata_dir, 'testkey.key')
976 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700977 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
978 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
979 with open(extracted_from_privkey, 'rb') as privkey_fp, \
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700980 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700981 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
982
Tao Bao17e4e612018-02-16 17:12:54 -0800983 def test_ParseCertificate(self):
984 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
985
986 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800987 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
988 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800989 expected, _ = proc.communicate()
990 self.assertEqual(0, proc.returncode)
991
992 with open(cert) as cert_fp:
993 actual = common.ParseCertificate(cert_fp.read())
994 self.assertEqual(expected, actual)
995
Tao Bao82490d32019-04-09 00:12:30 -0700996 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700997 def test_GetMinSdkVersion(self):
998 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
999 self.assertEqual('24', common.GetMinSdkVersion(test_app))
1000
Tao Bao82490d32019-04-09 00:12:30 -07001001 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001002 def test_GetMinSdkVersion_invalidInput(self):
1003 self.assertRaises(
1004 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
1005
Tao Bao82490d32019-04-09 00:12:30 -07001006 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001007 def test_GetMinSdkVersionInt(self):
1008 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
1009 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
1010
Tao Bao82490d32019-04-09 00:12:30 -07001011 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001012 def test_GetMinSdkVersionInt_invalidInput(self):
1013 self.assertRaises(
1014 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1015 {})
1016
Tao Bao818ddf52018-01-05 11:17:34 -08001017
Tao Bao65b94e92018-10-11 21:57:26 -07001018class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001019
Tao Bao02a08592018-07-22 12:40:45 -07001020 def setUp(self):
1021 self.testdata_dir = test_utils.get_testdata_dir()
1022
Tao Bao82490d32019-04-09 00:12:30 -07001023 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001024 def test_GetSparseImage_emptyBlockMapFile(self):
1025 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001026 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001027 target_files_zip.write(
1028 test_utils.construct_sparse_image([
1029 (0xCAC1, 6),
1030 (0xCAC3, 3),
1031 (0xCAC1, 4)]),
1032 arcname='IMAGES/system.img')
1033 target_files_zip.writestr('IMAGES/system.map', '')
1034 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1035 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1036
Tao Baodba59ee2018-01-09 13:21:02 -08001037 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001038 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001039 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001040
1041 self.assertDictEqual(
1042 {
1043 '__COPY': RangeSet("0"),
1044 '__NONZERO-0': RangeSet("1-5 9-12"),
1045 },
1046 sparse_image.file_map)
1047
Daniel Norman21c34f72020-11-11 17:25:50 -08001048 def test_PartitionMapFromTargetFiles(self):
1049 target_files_dir = common.MakeTempDir()
1050 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1051 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1052 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1053 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1054 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1055 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1056 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1057 self.assertDictEqual(
1058 partition_map,
1059 {
1060 'system': 'SYSTEM',
1061 'vendor': 'SYSTEM/vendor',
1062 # Prefer PRODUCT over SYSTEM/product
1063 'product': 'PRODUCT',
1064 'odm': 'SYSTEM/vendor/odm',
1065 'vendor_dlkm': 'VENDOR_DLKM',
1066 # No system_ext or odm_dlkm
1067 })
1068
Daniel Normand3351562020-10-29 12:33:11 -07001069 def test_SharedUidPartitionViolations(self):
1070 uid_dict = {
1071 'android.uid.phone': {
1072 'system': ['system_phone.apk'],
1073 'system_ext': ['system_ext_phone.apk'],
1074 },
1075 'android.uid.wifi': {
1076 'vendor': ['vendor_wifi.apk'],
1077 'odm': ['odm_wifi.apk'],
1078 },
1079 }
1080 errors = common.SharedUidPartitionViolations(
1081 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1082 self.assertEqual(errors, [])
1083
1084 def test_SharedUidPartitionViolations_Violation(self):
1085 uid_dict = {
1086 'android.uid.phone': {
1087 'system': ['system_phone.apk'],
1088 'vendor': ['vendor_phone.apk'],
1089 },
1090 }
1091 errors = common.SharedUidPartitionViolations(
1092 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1093 self.assertIn(
1094 ('APK sharedUserId "android.uid.phone" found across partition groups '
1095 'in partitions "system,vendor"'), errors)
1096
Tao Baob2de7d92019-04-10 10:01:47 -07001097 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001098 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001099 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1100 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001101 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001102 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1103 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001104
Tao Bao82490d32019-04-09 00:12:30 -07001105 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001106 def test_GetSparseImage_missingBlockMapFile(self):
1107 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001108 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001109 target_files_zip.write(
1110 test_utils.construct_sparse_image([
1111 (0xCAC1, 6),
1112 (0xCAC3, 3),
1113 (0xCAC1, 4)]),
1114 arcname='IMAGES/system.img')
1115 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1116 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1117
Tao Baodba59ee2018-01-09 13:21:02 -08001118 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001119 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001120 self.assertRaises(
1121 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1122 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001123
Tao Bao82490d32019-04-09 00:12:30 -07001124 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001125 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1126 """Tests the case of having overlapping blocks but disallowed."""
1127 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001128 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001129 target_files_zip.write(
1130 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1131 arcname='IMAGES/system.img')
1132 # Block 10 is shared between two files.
1133 target_files_zip.writestr(
1134 'IMAGES/system.map',
1135 '\n'.join([
1136 '/system/file1 1-5 9-10',
1137 '/system/file2 10-12']))
1138 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1139 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1140
Tao Baodba59ee2018-01-09 13:21:02 -08001141 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001142 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001143 self.assertRaises(
1144 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1145 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001146
Tao Bao82490d32019-04-09 00:12:30 -07001147 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001148 def test_GetSparseImage_sharedBlocks_allowed(self):
1149 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1150 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001151 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001152 # Construct an image with a care_map of "0-5 9-12".
1153 target_files_zip.write(
1154 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1155 arcname='IMAGES/system.img')
1156 # Block 10 is shared between two files.
1157 target_files_zip.writestr(
1158 'IMAGES/system.map',
1159 '\n'.join([
1160 '/system/file1 1-5 9-10',
1161 '/system/file2 10-12']))
1162 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1163 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1164
Tao Baodba59ee2018-01-09 13:21:02 -08001165 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001166 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001167 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001168
1169 self.assertDictEqual(
1170 {
1171 '__COPY': RangeSet("0"),
1172 '__NONZERO-0': RangeSet("6-8 13-15"),
1173 '/system/file1': RangeSet("1-5 9-10"),
1174 '/system/file2': RangeSet("11-12"),
1175 },
1176 sparse_image.file_map)
1177
1178 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1179 # 'incomplete'.
1180 self.assertTrue(
1181 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1182 self.assertNotIn(
1183 'incomplete', sparse_image.file_map['/system/file2'].extra)
1184
Tao Baoa264fef2019-10-06 21:55:20 -07001185 # '/system/file1' will only contain one field -- a copy of the input text.
1186 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1187
1188 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001189 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1190 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001191
Tao Bao82490d32019-04-09 00:12:30 -07001192 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001193 def test_GetSparseImage_incompleteRanges(self):
1194 """Tests the case of ext4 images with holes."""
1195 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001196 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001197 target_files_zip.write(
1198 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1199 arcname='IMAGES/system.img')
1200 target_files_zip.writestr(
1201 'IMAGES/system.map',
1202 '\n'.join([
1203 '/system/file1 1-5 9-10',
1204 '/system/file2 11-12']))
1205 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1206 # '/system/file2' has less blocks listed (2) than actual (3).
1207 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1208
Tao Baodba59ee2018-01-09 13:21:02 -08001209 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001210 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001211 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001212
Tao Baoa264fef2019-10-06 21:55:20 -07001213 self.assertEqual(
1214 '1-5 9-10',
1215 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001216 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1217
Tao Bao82490d32019-04-09 00:12:30 -07001218 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001219 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1220 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001221 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001222 target_files_zip.write(
1223 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1224 arcname='IMAGES/system.img')
1225 target_files_zip.writestr(
1226 'IMAGES/system.map',
1227 '\n'.join([
1228 '//system/file1 1-5 9-10',
1229 '//system/file2 11-12',
1230 '/system/app/file3 13-15']))
1231 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1232 # '/system/file2' has less blocks listed (2) than actual (3).
1233 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1234 # '/system/app/file3' has less blocks listed (3) than actual (4).
1235 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1236
1237 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001238 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001239 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1240
Tao Baoa264fef2019-10-06 21:55:20 -07001241 self.assertEqual(
1242 '1-5 9-10',
1243 sparse_image.file_map['//system/file1'].extra['text_str'])
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001244 self.assertTrue(
1245 sparse_image.file_map['//system/file2'].extra['incomplete'])
Tao Baod3554e62018-07-10 15:31:22 -07001246 self.assertTrue(
1247 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1248
Tao Bao82490d32019-04-09 00:12:30 -07001249 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001250 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1251 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001252 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001253 target_files_zip.write(
1254 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1255 arcname='IMAGES/system.img')
1256 target_files_zip.writestr(
1257 'IMAGES/system.map',
1258 '\n'.join([
1259 '//system/file1 1-5 9-10',
1260 '//init.rc 13-15']))
1261 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1262 # '/init.rc' has less blocks listed (3) than actual (4).
1263 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1264
1265 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001266 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001267 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1268
Tao Baoa264fef2019-10-06 21:55:20 -07001269 self.assertEqual(
1270 '1-5 9-10',
1271 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001272 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1273
Tao Bao82490d32019-04-09 00:12:30 -07001274 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001275 def test_GetSparseImage_fileNotFound(self):
1276 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001277 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001278 target_files_zip.write(
1279 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1280 arcname='IMAGES/system.img')
1281 target_files_zip.writestr(
1282 'IMAGES/system.map',
1283 '\n'.join([
1284 '//system/file1 1-5 9-10',
1285 '//system/file2 11-12']))
1286 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1287
1288 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001289 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001290 self.assertRaises(
1291 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1292 False)
1293
Tao Bao82490d32019-04-09 00:12:30 -07001294 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001295 def test_GetAvbChainedPartitionArg(self):
1296 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1297 info_dict = {
1298 'avb_avbtool': 'avbtool',
1299 'avb_system_key_path': pubkey,
1300 'avb_system_rollback_index_location': 2,
1301 }
1302 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1303 self.assertEqual(3, len(args))
1304 self.assertEqual('system', args[0])
1305 self.assertEqual('2', args[1])
1306 self.assertTrue(os.path.exists(args[2]))
1307
Tao Bao82490d32019-04-09 00:12:30 -07001308 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001309 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1310 key = os.path.join(self.testdata_dir, 'testkey.key')
1311 info_dict = {
1312 'avb_avbtool': 'avbtool',
1313 'avb_product_key_path': key,
1314 'avb_product_rollback_index_location': 2,
1315 }
1316 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1317 self.assertEqual(3, len(args))
1318 self.assertEqual('product', args[0])
1319 self.assertEqual('2', args[1])
1320 self.assertTrue(os.path.exists(args[2]))
1321
Tao Bao82490d32019-04-09 00:12:30 -07001322 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001323 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1324 info_dict = {
1325 'avb_avbtool': 'avbtool',
1326 'avb_system_key_path': 'does-not-exist',
1327 'avb_system_rollback_index_location': 2,
1328 }
1329 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1330 args = common.GetAvbChainedPartitionArg(
1331 'system', info_dict, pubkey).split(':')
1332 self.assertEqual(3, len(args))
1333 self.assertEqual('system', args[0])
1334 self.assertEqual('2', args[1])
1335 self.assertTrue(os.path.exists(args[2]))
1336
Tao Bao82490d32019-04-09 00:12:30 -07001337 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001338 def test_GetAvbChainedPartitionArg_invalidKey(self):
1339 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1340 info_dict = {
1341 'avb_avbtool': 'avbtool',
1342 'avb_system_key_path': pubkey,
1343 'avb_system_rollback_index_location': 2,
1344 }
1345 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001346 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1347 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001348
Tao Baoa57ab9f2018-08-24 12:08:38 -07001349 INFO_DICT_DEFAULT = {
1350 'recovery_api_version': 3,
1351 'fstab_version': 2,
1352 'system_root_image': 'true',
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001353 'no_recovery': 'true',
Tao Baoa57ab9f2018-08-24 12:08:38 -07001354 'recovery_as_boot': 'true',
1355 }
1356
Daniel Norman4cc9df62019-07-18 10:11:07 -07001357 def test_LoadListFromFile(self):
1358 file_path = os.path.join(self.testdata_dir,
1359 'merge_config_framework_item_list')
1360 contents = common.LoadListFromFile(file_path)
1361 expected_contents = [
1362 'META/apkcerts.txt',
1363 'META/filesystem_config.txt',
1364 'META/root_filesystem_config.txt',
1365 'META/system_manifest.xml',
1366 'META/system_matrix.xml',
1367 'META/update_engine_config.txt',
1368 'PRODUCT/*',
1369 'ROOT/*',
1370 'SYSTEM/*',
1371 ]
1372 self.assertEqual(sorted(contents), sorted(expected_contents))
1373
Tao Baoa57ab9f2018-08-24 12:08:38 -07001374 @staticmethod
1375 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1376 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001377 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001378 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001379 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001380 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1381
1382 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1383 if info_dict.get('system_root_image') == 'true':
1384 fstab_values = FSTAB_TEMPLATE.format('/')
1385 else:
1386 fstab_values = FSTAB_TEMPLATE.format('/system')
1387 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001388
1389 common.ZipWriteStr(
1390 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001391 return target_files
1392
1393 def test_LoadInfoDict(self):
1394 target_files = self._test_LoadInfoDict_createTargetFiles(
1395 self.INFO_DICT_DEFAULT,
1396 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001397 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001398 loaded_dict = common.LoadInfoDict(target_files_zip)
1399 self.assertEqual(3, loaded_dict['recovery_api_version'])
1400 self.assertEqual(2, loaded_dict['fstab_version'])
1401 self.assertIn('/', loaded_dict['fstab'])
1402 self.assertIn('/system', loaded_dict['fstab'])
1403
1404 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1405 target_files = self._test_LoadInfoDict_createTargetFiles(
1406 self.INFO_DICT_DEFAULT,
1407 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001408 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001409 loaded_dict = common.LoadInfoDict(target_files_zip)
1410 self.assertEqual(3, loaded_dict['recovery_api_version'])
1411 self.assertEqual(2, loaded_dict['fstab_version'])
1412 self.assertIn('/', loaded_dict['fstab'])
1413 self.assertIn('/system', loaded_dict['fstab'])
1414
Tao Bao82490d32019-04-09 00:12:30 -07001415 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001416 def test_LoadInfoDict_dirInput(self):
1417 target_files = self._test_LoadInfoDict_createTargetFiles(
1418 self.INFO_DICT_DEFAULT,
1419 'BOOT/RAMDISK/system/etc/recovery.fstab')
1420 unzipped = common.UnzipTemp(target_files)
1421 loaded_dict = common.LoadInfoDict(unzipped)
1422 self.assertEqual(3, loaded_dict['recovery_api_version'])
1423 self.assertEqual(2, loaded_dict['fstab_version'])
1424 self.assertIn('/', loaded_dict['fstab'])
1425 self.assertIn('/system', loaded_dict['fstab'])
1426
Tao Bao82490d32019-04-09 00:12:30 -07001427 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001428 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1429 target_files = self._test_LoadInfoDict_createTargetFiles(
1430 self.INFO_DICT_DEFAULT,
1431 'BOOT/RAMDISK/system/etc/recovery.fstab')
1432 unzipped = common.UnzipTemp(target_files)
1433 loaded_dict = common.LoadInfoDict(unzipped)
1434 self.assertEqual(3, loaded_dict['recovery_api_version'])
1435 self.assertEqual(2, loaded_dict['fstab_version'])
1436 self.assertIn('/', loaded_dict['fstab'])
1437 self.assertIn('/system', loaded_dict['fstab'])
1438
1439 def test_LoadInfoDict_systemRootImageFalse(self):
1440 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1441 # launched prior to P will likely have this config.
1442 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1443 del info_dict['no_recovery']
1444 del info_dict['system_root_image']
1445 del info_dict['recovery_as_boot']
1446 target_files = self._test_LoadInfoDict_createTargetFiles(
1447 info_dict,
1448 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001449 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001450 loaded_dict = common.LoadInfoDict(target_files_zip)
1451 self.assertEqual(3, loaded_dict['recovery_api_version'])
1452 self.assertEqual(2, loaded_dict['fstab_version'])
1453 self.assertNotIn('/', loaded_dict['fstab'])
1454 self.assertIn('/system', loaded_dict['fstab'])
1455
1456 def test_LoadInfoDict_recoveryAsBootFalse(self):
1457 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1458 # devices launched since P will likely have this config.
1459 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1460 del info_dict['no_recovery']
1461 del info_dict['recovery_as_boot']
1462 target_files = self._test_LoadInfoDict_createTargetFiles(
1463 info_dict,
1464 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001465 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001466 loaded_dict = common.LoadInfoDict(target_files_zip)
1467 self.assertEqual(3, loaded_dict['recovery_api_version'])
1468 self.assertEqual(2, loaded_dict['fstab_version'])
1469 self.assertIn('/', loaded_dict['fstab'])
1470 self.assertIn('/system', loaded_dict['fstab'])
1471
1472 def test_LoadInfoDict_noRecoveryTrue(self):
1473 # Device doesn't have a recovery partition at all.
1474 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1475 del info_dict['recovery_as_boot']
1476 target_files = self._test_LoadInfoDict_createTargetFiles(
1477 info_dict,
1478 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001479 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001480 loaded_dict = common.LoadInfoDict(target_files_zip)
1481 self.assertEqual(3, loaded_dict['recovery_api_version'])
1482 self.assertEqual(2, loaded_dict['fstab_version'])
1483 self.assertIsNone(loaded_dict['fstab'])
1484
Tao Bao82490d32019-04-09 00:12:30 -07001485 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001486 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1487 target_files = self._test_LoadInfoDict_createTargetFiles(
1488 self.INFO_DICT_DEFAULT,
1489 'BOOT/RAMDISK/system/etc/recovery.fstab')
1490 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001491 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001492 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1493
Tao Bao82490d32019-04-09 00:12:30 -07001494 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001495 def test_LoadInfoDict_repacking(self):
1496 target_files = self._test_LoadInfoDict_createTargetFiles(
1497 self.INFO_DICT_DEFAULT,
1498 'BOOT/RAMDISK/system/etc/recovery.fstab')
1499 unzipped = common.UnzipTemp(target_files)
1500 loaded_dict = common.LoadInfoDict(unzipped, True)
1501 self.assertEqual(3, loaded_dict['recovery_api_version'])
1502 self.assertEqual(2, loaded_dict['fstab_version'])
1503 self.assertIn('/', loaded_dict['fstab'])
1504 self.assertIn('/system', loaded_dict['fstab'])
1505 self.assertEqual(
1506 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1507 self.assertEqual(
1508 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1509 loaded_dict['root_fs_config'])
1510
1511 def test_LoadInfoDict_repackingWithZipFileInput(self):
1512 target_files = self._test_LoadInfoDict_createTargetFiles(
1513 self.INFO_DICT_DEFAULT,
1514 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001515 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001516 self.assertRaises(
1517 AssertionError, common.LoadInfoDict, target_files_zip, True)
1518
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001519 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1520 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001521 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001522 'super_partition_groups': 'group_a',
1523 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001524 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001525 }
1526 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001527 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001528 'super_partition_groups': 'group_a group_b',
1529 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001530 'super_block_devices': 'super',
1531 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001532 'super_group_a_partition_list': 'vendor',
1533 'super_group_a_group_size': '1000',
1534 'super_group_b_partition_list': 'product',
1535 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001536 }
1537 merged_dict = common.MergeDynamicPartitionInfoDicts(
1538 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001539 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001540 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001541 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001542 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001543 'dynamic_partition_list': 'product system vendor',
1544 'super_block_devices': 'super',
1545 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001546 'super_group_a_partition_list': 'system vendor',
1547 'super_group_a_group_size': '1000',
1548 'super_group_b_partition_list': 'product',
1549 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001550 }
1551 self.assertEqual(merged_dict, expected_merged_dict)
1552
1553 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1554 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001555 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001556 'super_partition_groups': 'group_a',
1557 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001558 'super_group_a_partition_list': 'system',
1559 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001560 }
1561 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001562 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001563 'super_partition_groups': 'group_a group_b',
1564 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001565 'super_group_a_partition_list': 'vendor',
1566 'super_group_a_group_size': '1000',
1567 'super_group_b_partition_list': 'product',
1568 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001569 }
1570 merged_dict = common.MergeDynamicPartitionInfoDicts(
1571 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001572 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001573 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001574 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001575 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001576 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001577 'super_group_a_partition_list': 'system vendor',
1578 'super_group_a_group_size': '1000',
1579 'super_group_b_partition_list': 'product',
1580 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001581 }
1582 self.assertEqual(merged_dict, expected_merged_dict)
1583
Daniel Norman276f0622019-07-26 14:13:51 -07001584 def test_GetAvbPartitionArg(self):
1585 info_dict = {}
1586 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1587 self.assertEqual(
Dennis Song6e5e44d2023-10-03 02:18:06 +00001588 [common.AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, '/path/to/system.img'],
1589 cmd)
Daniel Norman276f0622019-07-26 14:13:51 -07001590
1591 @test_utils.SkipIfExternalToolsUnavailable()
1592 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1593 testdata_dir = test_utils.get_testdata_dir()
1594 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1595 info_dict = {
1596 'avb_avbtool': 'avbtool',
1597 'avb_vendor_key_path': pubkey,
1598 'avb_vendor_rollback_index_location': 5,
1599 }
1600 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1601 self.assertEqual(2, len(cmd))
Dennis Song6e5e44d2023-10-03 02:18:06 +00001602 self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
Daniel Norman276f0622019-07-26 14:13:51 -07001603 chained_partition_args = cmd[1].split(':')
1604 self.assertEqual(3, len(chained_partition_args))
1605 self.assertEqual('vendor', chained_partition_args[0])
1606 self.assertEqual('5', chained_partition_args[1])
1607 self.assertTrue(os.path.exists(chained_partition_args[2]))
1608
Tao Bao3612c882019-10-14 17:49:31 -07001609 @test_utils.SkipIfExternalToolsUnavailable()
1610 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1611 testdata_dir = test_utils.get_testdata_dir()
1612 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1613 info_dict = {
1614 'avb_avbtool': 'avbtool',
1615 'avb_recovery_key_path': pubkey,
1616 'avb_recovery_rollback_index_location': 3,
1617 }
1618 cmd = common.GetAvbPartitionArg(
1619 'recovery', '/path/to/recovery.img', info_dict)
1620 self.assertFalse(cmd)
1621
1622 @test_utils.SkipIfExternalToolsUnavailable()
1623 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1624 testdata_dir = test_utils.get_testdata_dir()
1625 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1626 info_dict = {
1627 'ab_update': 'true',
1628 'avb_avbtool': 'avbtool',
1629 'avb_recovery_key_path': pubkey,
1630 'avb_recovery_rollback_index_location': 3,
1631 }
1632 cmd = common.GetAvbPartitionArg(
1633 'recovery', '/path/to/recovery.img', info_dict)
1634 self.assertEqual(2, len(cmd))
Dennis Song6e5e44d2023-10-03 02:18:06 +00001635 self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
Tao Bao3612c882019-10-14 17:49:31 -07001636 chained_partition_args = cmd[1].split(':')
1637 self.assertEqual(3, len(chained_partition_args))
1638 self.assertEqual('recovery', chained_partition_args[0])
1639 self.assertEqual('3', chained_partition_args[1])
1640 self.assertTrue(os.path.exists(chained_partition_args[2]))
1641
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001642 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001643 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1644 self.assertFalse(os.path.exists(pubkey))
1645
1646 common.OPTIONS.info_dict = {
1647 'gki_signing_key_path': pubkey,
1648 'gki_signing_algorithm': 'SHA256_RSA4096',
1649 'gki_signing_signature_args': '--prop foo:bar',
1650 }
Kelvin Zhang03dc6ee2023-06-28 10:10:39 -07001651 common.OPTIONS.search_path = None
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001652 test_file = tempfile.NamedTemporaryFile()
1653 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001654 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001655
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001656 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001657 pubkey = 'no_testkey_gki.pem'
1658 self.assertFalse(os.path.exists(pubkey))
1659
1660 # Tests it should raise ExternalError if no key found under
1661 # OPTIONS.search_path.
1662 search_path_dir = common.MakeTempDir()
1663 search_pubkey = os.path.join(search_path_dir, pubkey)
1664 self.assertFalse(os.path.exists(search_pubkey))
1665
1666 common.OPTIONS.search_path = search_path_dir
1667 common.OPTIONS.info_dict = {
1668 'gki_signing_key_path': pubkey,
1669 'gki_signing_algorithm': 'SHA256_RSA4096',
1670 'gki_signing_signature_args': '--prop foo:bar',
1671 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001672 test_file = tempfile.NamedTemporaryFile()
1673 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001674 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001675
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001676
Tao Bao65b94e92018-10-11 21:57:26 -07001677class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001678 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001679
Tao Bao1c830bf2017-12-25 10:43:47 -08001680 Its format should match between common.py and validate_target_files.py.
1681 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001682
1683 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001684 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001685 # Create a fake dict that contains the fstab info for boot&recovery.
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001686 self._info = {"fstab": {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001687 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001688 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1689 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001690 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001691 # Construct the gzipped recovery.img and boot.img
1692 self.recovery_data = bytearray([
1693 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1694 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1695 0x08, 0x00, 0x00, 0x00
1696 ])
1697 # echo -n "boot" | gzip -f | hd
1698 self.boot_data = bytearray([
1699 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1700 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1701 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001702
1703 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1704 loc = os.path.join(self._tempdir, prefix, name)
1705 if not os.path.exists(os.path.dirname(loc)):
1706 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001707 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001708 f.write(data)
1709
1710 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001711 recovery_image = common.File("recovery.img", self.recovery_data)
1712 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001713 self._info["full_recovery_image"] = "true"
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
Tao Bao82490d32019-04-09 00:12:30 -07001720 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001721 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001722 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001723 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001724 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001725 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1726
1727 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1728 recovery_image, boot_image, self._info)
1729 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1730 self._info)
1731 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001732 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001733 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1734 recovery_image, boot_image, self._info)
1735 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1736 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001737
1738
Yifan Hong45433e42019-01-18 13:55:25 -08001739class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001740
Yifan Hong45433e42019-01-18 13:55:25 -08001741 def __init__(self, partition, tgt, src=None):
1742 self.partition = partition
1743 self.tgt = tgt
1744 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001745
Yifan Hong45433e42019-01-18 13:55:25 -08001746 def WriteScript(self, script, _, progress=None,
1747 write_verify_script=False):
1748 if progress:
1749 script.AppendExtra("progress({})".format(progress))
1750 script.AppendExtra("patch({});".format(self.partition))
1751 if write_verify_script:
1752 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001753
Yifan Hong45433e42019-01-18 13:55:25 -08001754 def WritePostInstallVerifyScript(self, script):
1755 script.AppendExtra("verify({});".format(self.partition))
1756
1757
1758class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001759
Yifan Hong45433e42019-01-18 13:55:25 -08001760 def __init__(self, size):
1761 self.blocksize = 4096
1762 self.total_blocks = size // 4096
1763 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1764
1765
1766class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001767
Yifan Hong45433e42019-01-18 13:55:25 -08001768 @staticmethod
1769 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001770 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001771 with output_zip.open('dynamic_partitions_op_list') as op_list:
1772 return [line.decode().strip() for line in op_list.readlines()
1773 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001774
1775 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001776 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001777 self.output_path = common.MakeTempFile(suffix='.zip')
1778
1779 def test_full(self):
1780 target_info = common.LoadDictionaryFromLines("""
1781dynamic_partition_list=system vendor
1782super_partition_groups=group_foo
1783super_group_foo_group_size={group_size}
1784super_group_foo_partition_list=system vendor
1785""".format(group_size=4 * GiB).split("\n"))
1786 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1787 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1788
1789 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001790 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001791 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1792
1793 self.assertEqual(str(self.script).strip(), """
1794assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001795patch(system);
1796verify(system);
1797unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001798patch(vendor);
1799verify(vendor);
1800unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001801""".strip())
1802
1803 lines = self.get_op_list(self.output_path)
1804
1805 remove_all_groups = lines.index("remove_all_groups")
1806 add_group = lines.index("add_group group_foo 4294967296")
1807 add_vendor = lines.index("add vendor group_foo")
1808 add_system = lines.index("add system group_foo")
1809 resize_vendor = lines.index("resize vendor 1073741824")
1810 resize_system = lines.index("resize system 3221225472")
1811
1812 self.assertLess(remove_all_groups, add_group,
1813 "Should add groups after removing all groups")
1814 self.assertLess(add_group, min(add_vendor, add_system),
1815 "Should add partitions after adding group")
1816 self.assertLess(add_system, resize_system,
1817 "Should resize system after adding it")
1818 self.assertLess(add_vendor, resize_vendor,
1819 "Should resize vendor after adding it")
1820
1821 def test_inc_groups(self):
1822 source_info = common.LoadDictionaryFromLines("""
1823super_partition_groups=group_foo group_bar group_baz
1824super_group_foo_group_size={group_foo_size}
1825super_group_bar_group_size={group_bar_size}
1826""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1827 target_info = common.LoadDictionaryFromLines("""
1828super_partition_groups=group_foo group_baz group_qux
1829super_group_foo_group_size={group_foo_size}
1830super_group_baz_group_size={group_baz_size}
1831super_group_qux_group_size={group_qux_size}
1832""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1833 group_qux_size=1 * GiB).split("\n"))
1834
1835 dp_diff = common.DynamicPartitionsDifference(target_info,
1836 block_diffs=[],
1837 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001838 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001839 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1840
1841 lines = self.get_op_list(self.output_path)
1842
1843 removed = lines.index("remove_group group_bar")
1844 shrunk = lines.index("resize_group group_foo 3221225472")
1845 grown = lines.index("resize_group group_baz 4294967296")
1846 added = lines.index("add_group group_qux 1073741824")
1847
Tao Baof1113e92019-06-18 12:10:14 -07001848 self.assertLess(max(removed, shrunk),
1849 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001850 "ops that remove / shrink partitions must precede ops that "
1851 "grow / add partitions")
1852
Yifan Hongbb2658d2019-01-25 12:30:58 -08001853 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001854 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001855dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001856super_partition_groups=group_foo
1857super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001858super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001859""".format(group_foo_size=4 * GiB).split("\n"))
1860 target_info = common.LoadDictionaryFromLines("""
1861dynamic_partition_list=system vendor product odm
1862super_partition_groups=group_foo group_bar
1863super_group_foo_group_size={group_foo_size}
1864super_group_foo_partition_list=system vendor odm
1865super_group_bar_group_size={group_bar_size}
1866super_group_bar_partition_list=product
1867""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1868
1869 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1870 src=FakeSparseImage(1024 * MiB)),
1871 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1872 src=FakeSparseImage(1024 * MiB)),
1873 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1874 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001875 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001876 src=FakeSparseImage(1024 * MiB)),
1877 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1878 src=None)]
1879
1880 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1881 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001882 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001883 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1884
1885 metadata_idx = self.script.lines.index(
1886 'assert(update_dynamic_partitions(package_extract_file('
1887 '"dynamic_partitions_op_list")));')
1888 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1889 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1890 for p in ("product", "system", "odm"):
1891 patch_idx = self.script.lines.index("patch({});".format(p))
1892 verify_idx = self.script.lines.index("verify({});".format(p))
1893 self.assertLess(metadata_idx, patch_idx,
1894 "Should patch {} after updating metadata".format(p))
1895 self.assertLess(patch_idx, verify_idx,
1896 "Should verify {} after patching".format(p))
1897
Justin Yun6151e3f2019-06-25 15:58:13 +09001898 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001899
1900 lines = self.get_op_list(self.output_path)
1901
Justin Yun6151e3f2019-06-25 15:58:13 +09001902 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001903 move_product_out = lines.index("move product default")
1904 shrink = lines.index("resize vendor 536870912")
1905 shrink_group = lines.index("resize_group group_foo 3221225472")
1906 add_group_bar = lines.index("add_group group_bar 1073741824")
1907 add_odm = lines.index("add odm group_foo")
1908 grow_existing = lines.index("resize system 1610612736")
1909 grow_added = lines.index("resize odm 1073741824")
1910 move_product_in = lines.index("move product group_bar")
1911
1912 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1913 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1914
1915 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1916 "Must shrink group after partitions inside group are shrunk"
1917 " / removed")
1918
1919 self.assertLess(add_group_bar, move_product_in,
1920 "Must add partitions to group after group is added")
1921
1922 self.assertLess(max_idx_move_partition_out_foo,
1923 min_idx_move_partition_in_foo,
1924 "Must shrink partitions / remove partitions from group"
1925 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001926
1927 def test_remove_partition(self):
1928 source_info = common.LoadDictionaryFromLines("""
1929blockimgdiff_versions=3,4
1930use_dynamic_partitions=true
1931dynamic_partition_list=foo
1932super_partition_groups=group_foo
1933super_group_foo_group_size={group_foo_size}
1934super_group_foo_partition_list=foo
1935""".format(group_foo_size=4 * GiB).split("\n"))
1936 target_info = common.LoadDictionaryFromLines("""
1937blockimgdiff_versions=3,4
1938use_dynamic_partitions=true
1939super_partition_groups=group_foo
1940super_group_foo_group_size={group_foo_size}
1941""".format(group_foo_size=4 * GiB).split("\n"))
1942
1943 common.OPTIONS.info_dict = target_info
1944 common.OPTIONS.target_info_dict = target_info
1945 common.OPTIONS.source_info_dict = source_info
1946 common.OPTIONS.cache_size = 4 * 4096
1947
1948 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1949 src=DataImage("source", pad=True))]
1950
1951 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1952 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001953 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001954 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1955
1956 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001957 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001958
1959 lines = self.get_op_list(self.output_path)
1960 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001961
1962
1963class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1964 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001965 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001966 'ro.odm.build.date.utc=1578430045',
1967 'ro.odm.build.fingerprint='
1968 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1969 'ro.product.odm.device=coral',
1970 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1971 ]
1972
1973 @staticmethod
1974 def _BuildZipFile(entries):
1975 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001976 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001977 for name, content in entries.items():
1978 input_zip.writestr(name, content)
1979
1980 return input_file
1981
1982 def test_parseBuildProps_noImportStatement(self):
1983 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001984 'ro.odm.build.date.utc=1578430045',
1985 'ro.odm.build.fingerprint='
1986 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1987 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001988 ]
1989 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001990 'ODM/etc/build.prop': '\n'.join(build_prop),
1991 })
1992
Kelvin Zhang928c2342020-09-22 16:15:57 -04001993 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001994 placeholder_values = {
1995 'ro.boot.product.device_name': ['std', 'pro']
1996 }
1997 partition_props = common.PartitionBuildProps.FromInputFile(
1998 input_zip, 'odm', placeholder_values)
1999
2000 self.assertEqual({
2001 'ro.odm.build.date.utc': '1578430045',
2002 'ro.odm.build.fingerprint':
2003 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2004 'ro.product.odm.device': 'coral',
2005 }, partition_props.build_props)
2006
2007 self.assertEqual(set(), partition_props.prop_overrides)
2008
2009 def test_parseBuildProps_singleImportStatement(self):
2010 build_std_prop = [
2011 'ro.product.odm.device=coral',
2012 'ro.product.odm.name=product1',
2013 ]
2014 build_pro_prop = [
2015 'ro.product.odm.device=coralpro',
2016 'ro.product.odm.name=product2',
2017 ]
2018
2019 input_file = self._BuildZipFile({
2020 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2021 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2022 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2023 })
2024
Kelvin Zhang928c2342020-09-22 16:15:57 -04002025 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002026 placeholder_values = {
2027 'ro.boot.product.device_name': 'std'
2028 }
2029 partition_props = common.PartitionBuildProps.FromInputFile(
2030 input_zip, 'odm', placeholder_values)
2031
2032 self.assertEqual({
Kelvin Zhang8aa65252023-08-30 18:29:59 -07002033 'ro.odm.build.date.utc': '1578430045',
2034 'ro.odm.build.fingerprint':
2035 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2036 'ro.product.odm.device': 'coral',
2037 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00002038 }, partition_props.build_props)
2039
Kelvin Zhang928c2342020-09-22 16:15:57 -04002040 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002041 placeholder_values = {
2042 'ro.boot.product.device_name': 'pro'
2043 }
2044 partition_props = common.PartitionBuildProps.FromInputFile(
2045 input_zip, 'odm', placeholder_values)
2046
2047 self.assertEqual({
2048 'ro.odm.build.date.utc': '1578430045',
2049 'ro.odm.build.fingerprint':
2050 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2051 'ro.product.odm.device': 'coralpro',
2052 'ro.product.odm.name': 'product2',
2053 }, partition_props.build_props)
2054
2055 def test_parseBuildProps_noPlaceHolders(self):
2056 build_prop = copy.copy(self.odm_build_prop)
2057 input_file = self._BuildZipFile({
2058 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002059 })
2060
Kelvin Zhang928c2342020-09-22 16:15:57 -04002061 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002062 partition_props = common.PartitionBuildProps.FromInputFile(
2063 input_zip, 'odm')
2064
2065 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002066 'ro.odm.build.date.utc': '1578430045',
2067 'ro.odm.build.fingerprint':
2068 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2069 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002070 }, partition_props.build_props)
2071
Tianjie Xu9afb2212020-05-10 21:48:15 +00002072 self.assertEqual(set(), partition_props.prop_overrides)
2073
2074 def test_parseBuildProps_multipleImportStatements(self):
2075 build_prop = copy.deepcopy(self.odm_build_prop)
2076 build_prop.append(
2077 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2078
2079 build_std_prop = [
2080 'ro.product.odm.device=coral',
2081 ]
2082 build_pro_prop = [
2083 'ro.product.odm.device=coralpro',
2084 ]
2085
2086 product1_prop = [
2087 'ro.product.odm.name=product1',
2088 'ro.product.not_care=not_care',
2089 ]
2090
2091 product2_prop = [
2092 'ro.product.odm.name=product2',
2093 'ro.product.not_care=not_care',
2094 ]
2095
2096 input_file = self._BuildZipFile({
2097 'ODM/etc/build.prop': '\n'.join(build_prop),
2098 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2099 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2100 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2101 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2102 })
2103
Kelvin Zhang928c2342020-09-22 16:15:57 -04002104 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002105 placeholder_values = {
2106 'ro.boot.product.device_name': 'std',
2107 'ro.boot.product.product_name': 'product1',
2108 'ro.boot.product.not_care': 'not_care',
2109 }
2110 partition_props = common.PartitionBuildProps.FromInputFile(
2111 input_zip, 'odm', placeholder_values)
2112
2113 self.assertEqual({
2114 'ro.odm.build.date.utc': '1578430045',
2115 'ro.odm.build.fingerprint':
2116 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2117 'ro.product.odm.device': 'coral',
2118 'ro.product.odm.name': 'product1'
2119 }, partition_props.build_props)
2120
Kelvin Zhang928c2342020-09-22 16:15:57 -04002121 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002122 placeholder_values = {
2123 'ro.boot.product.device_name': 'pro',
2124 'ro.boot.product.product_name': 'product2',
2125 'ro.boot.product.not_care': 'not_care',
2126 }
2127 partition_props = common.PartitionBuildProps.FromInputFile(
2128 input_zip, 'odm', placeholder_values)
2129
2130 self.assertEqual({
2131 'ro.odm.build.date.utc': '1578430045',
2132 'ro.odm.build.fingerprint':
2133 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2134 'ro.product.odm.device': 'coralpro',
2135 'ro.product.odm.name': 'product2'
2136 }, partition_props.build_props)
2137
2138 def test_parseBuildProps_defineAfterOverride(self):
2139 build_prop = copy.deepcopy(self.odm_build_prop)
2140 build_prop.append('ro.product.odm.device=coral')
2141
2142 build_std_prop = [
2143 'ro.product.odm.device=coral',
2144 ]
2145 build_pro_prop = [
2146 'ro.product.odm.device=coralpro',
2147 ]
2148
2149 input_file = self._BuildZipFile({
2150 'ODM/etc/build.prop': '\n'.join(build_prop),
2151 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2152 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2153 })
2154
Kelvin Zhang928c2342020-09-22 16:15:57 -04002155 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002156 placeholder_values = {
2157 'ro.boot.product.device_name': 'std',
2158 }
2159
2160 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2161 input_zip, 'odm', placeholder_values)
2162
2163 def test_parseBuildProps_duplicateOverride(self):
2164 build_prop = copy.deepcopy(self.odm_build_prop)
2165 build_prop.append(
2166 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2167
2168 build_std_prop = [
2169 'ro.product.odm.device=coral',
2170 'ro.product.odm.name=product1',
2171 ]
2172 build_pro_prop = [
2173 'ro.product.odm.device=coralpro',
2174 ]
2175
2176 product1_prop = [
2177 'ro.product.odm.name=product1',
2178 ]
2179
2180 product2_prop = [
2181 'ro.product.odm.name=product2',
2182 ]
2183
2184 input_file = self._BuildZipFile({
2185 'ODM/etc/build.prop': '\n'.join(build_prop),
2186 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2187 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2188 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2189 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2190 })
2191
Kelvin Zhang928c2342020-09-22 16:15:57 -04002192 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002193 placeholder_values = {
2194 'ro.boot.product.device_name': 'std',
2195 'ro.boot.product.product_name': 'product1',
2196 }
2197 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2198 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08002199
2200 def test_partitionBuildProps_fromInputFile_deepcopy(self):
2201 build_prop = [
2202 'ro.odm.build.date.utc=1578430045',
2203 'ro.odm.build.fingerprint='
2204 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2205 'ro.product.odm.device=coral',
2206 ]
2207 input_file = self._BuildZipFile({
2208 'ODM/etc/build.prop': '\n'.join(build_prop),
2209 })
2210
2211 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
2212 placeholder_values = {
2213 'ro.boot.product.device_name': ['std', 'pro']
2214 }
2215 partition_props = common.PartitionBuildProps.FromInputFile(
2216 input_zip, 'odm', placeholder_values)
2217
2218 copied_props = copy.deepcopy(partition_props)
2219 self.assertEqual({
Kelvin Zhang8aa65252023-08-30 18:29:59 -07002220 'ro.odm.build.date.utc': '1578430045',
2221 'ro.odm.build.fingerprint':
2222 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2223 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08002224 }, copied_props.build_props)