blob: 8052821567f6d5d64def2f8cc5df17745a35efba [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
Tao Baofc7e0e02018-02-13 13:54:02 -080029from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080030
Tao Bao04e1f012018-02-04 12:13:35 -080031
Tao Bao31b08072017-11-08 15:50:59 -080032KiB = 1024
33MiB = 1024 * KiB
34GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080035
Tao Bao1c830bf2017-12-25 10:43:47 -080036
Kelvin Zhang8aa65252023-08-30 18:29:59 -070037def get_2gb_file():
Tao Bao31b08072017-11-08 15:50:59 -080038 size = int(2 * GiB + 1)
39 block_size = 4 * KiB
40 step_size = 4 * MiB
Kelvin Zhang8aa65252023-08-30 18:29:59 -070041 tmpfile = tempfile.NamedTemporaryFile()
42 tmpfile.truncate(size)
Tao Bao31b08072017-11-08 15:50:59 -080043 for _ in range(0, size, step_size):
Kelvin Zhang8aa65252023-08-30 18:29:59 -070044 tmpfile.write(os.urandom(block_size))
45 tmpfile.seek(step_size - block_size, os.SEEK_CUR)
46 return tmpfile
47
48
49def hash_file(filename):
50 sha1_hash = sha1()
51 with open(filename, "rb") as fp:
52 for data in iter(lambda: fp.read(4*MiB), b''):
53 sha1_hash.update(data)
54 return sha1_hash
Tao Baof3282b42015-04-01 11:21:55 -070055
Dan Albert8e0178d2015-01-27 15:53:15 -080056
Tao Bao1c320f82019-10-04 23:25:12 -070057class BuildInfoTest(test_utils.ReleaseToolsTestCase):
58
Tianjiefdda51d2021-05-05 14:46:35 -070059 TEST_INFO_FINGERPRINT_DICT = {
60 'build.prop': common.PartitionBuildProps.FromDictionary(
61 'system', {
62 'ro.product.brand': 'product-brand',
63 'ro.product.name': 'product-name',
64 'ro.product.device': 'product-device',
65 'ro.build.version.release': 'version-release',
66 'ro.build.id': 'build-id',
67 'ro.build.version.incremental': 'version-incremental',
68 'ro.build.type': 'build-type',
69 'ro.build.tags': 'build-tags',
70 'ro.build.version.sdk': 30,
71 }
72 ),
73 }
74
Tao Bao1c320f82019-10-04 23:25:12 -070075 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000076 'build.prop': common.PartitionBuildProps.FromDictionary(
77 'system', {
78 'ro.product.device': 'product-device',
79 'ro.product.name': 'product-name',
80 'ro.build.fingerprint': 'build-fingerprint',
81 'ro.build.foo': 'build-foo'}
82 ),
83 'system.build.prop': common.PartitionBuildProps.FromDictionary(
84 'system', {
85 'ro.product.system.brand': 'product-brand',
86 'ro.product.system.name': 'product-name',
87 'ro.product.system.device': 'product-device',
88 'ro.system.build.version.release': 'version-release',
89 'ro.system.build.id': 'build-id',
90 'ro.system.build.version.incremental': 'version-incremental',
91 'ro.system.build.type': 'build-type',
92 'ro.system.build.tags': 'build-tags',
93 'ro.system.build.foo': 'build-foo'}
94 ),
95 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
96 'vendor', {
97 'ro.product.vendor.brand': 'vendor-product-brand',
98 'ro.product.vendor.name': 'vendor-product-name',
99 'ro.product.vendor.device': 'vendor-product-device',
100 'ro.vendor.build.version.release': 'vendor-version-release',
101 'ro.vendor.build.id': 'vendor-build-id',
102 'ro.vendor.build.version.incremental':
103 'vendor-version-incremental',
104 'ro.vendor.build.type': 'vendor-build-type',
105 'ro.vendor.build.tags': 'vendor-build-tags'}
106 ),
107 'property1': 'value1',
108 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -0700109 }
110
111 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000112 'build.prop': common.PartitionBuildProps.FromDictionary(
113 'system', {
114 'ro.product.name': 'product-name',
115 'ro.build.thumbprint': 'build-thumbprint',
116 'ro.build.bar': 'build-bar'}
117 ),
118 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
119 'vendor', {
120 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
121 ),
122 'property1': 'value1',
123 'property2': 4096,
124 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700125 }
126
127 TEST_OEM_DICTS = [
128 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000129 'ro.product.brand': 'brand1',
130 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700131 },
132 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000133 'ro.product.brand': 'brand2',
134 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700135 },
136 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000137 'ro.product.brand': 'brand3',
138 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700139 },
140 ]
141
Steven Laver8e2086e2020-04-27 16:26:31 -0700142 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000143 'build.prop': common.PartitionBuildProps.FromDictionary(
144 'system', {
145 'ro.build.fingerprint': 'build-fingerprint',
146 'ro.product.property_source_order':
147 'product,odm,vendor,system_ext,system'}
148 ),
149 'system.build.prop': common.PartitionBuildProps.FromDictionary(
150 'system', {
151 'ro.product.system.device': 'system-product-device'}
152 ),
153 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
154 'vendor', {
155 'ro.product.vendor.device': 'vendor-product-device'}
156 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700157 }
158
159 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000160 'build.prop': common.PartitionBuildProps.FromDictionary(
161 'system', {
162 'ro.build.fingerprint': 'build-fingerprint',
163 'ro.product.property_source_order':
164 'product,product_services,odm,vendor,system',
165 'ro.build.version.release': '10',
166 'ro.build.version.codename': 'REL'}
167 ),
168 'system.build.prop': common.PartitionBuildProps.FromDictionary(
169 'system', {
170 'ro.product.system.device': 'system-product-device'}
171 ),
172 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
173 'vendor', {
174 'ro.product.vendor.device': 'vendor-product-device'}
175 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700176 }
177
178 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000179 'build.prop': common.PartitionBuildProps.FromDictionary(
180 'system', {
181 'ro.product.device': 'product-device',
182 'ro.build.fingerprint': 'build-fingerprint',
183 'ro.build.version.release': '9',
184 'ro.build.version.codename': 'REL'}
185 ),
186 'system.build.prop': common.PartitionBuildProps.FromDictionary(
187 'system', {
188 'ro.product.system.device': 'system-product-device'}
189 ),
190 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
191 'vendor', {
192 'ro.product.vendor.device': 'vendor-product-device'}
193 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700194 }
195
Tao Bao1c320f82019-10-04 23:25:12 -0700196 def test_init(self):
197 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
198 self.assertEqual('product-device', target_info.device)
199 self.assertEqual('build-fingerprint', target_info.fingerprint)
200 self.assertFalse(target_info.is_ab)
201 self.assertIsNone(target_info.oem_props)
202
203 def test_init_with_oem_props(self):
204 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
205 self.TEST_OEM_DICTS)
206 self.assertEqual('device1', target_info.device)
207 self.assertEqual('brand1/product-name/device1:build-thumbprint',
208 target_info.fingerprint)
209
210 # Swap the order in oem_dicts, which would lead to different BuildInfo.
211 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
212 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
213 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
214 oem_dicts)
215 self.assertEqual('device3', target_info.device)
216 self.assertEqual('brand3/product-name/device3:build-thumbprint',
217 target_info.fingerprint)
218
Tao Bao1c320f82019-10-04 23:25:12 -0700219 def test_init_badFingerprint(self):
220 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000221 info_dict['build.prop'].build_props[
222 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700223 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
224
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000225 info_dict['build.prop'].build_props[
226 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700227 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
228
Tianjiefdda51d2021-05-05 14:46:35 -0700229 def test_init_goodFingerprint(self):
230 info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
231 build_info = common.BuildInfo(info_dict)
232 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700233 'product-brand/product-name/product-device:version-release/build-id/'
234 'version-incremental:build-type/build-tags', build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700235
236 build_props = info_dict['build.prop'].build_props
237 del build_props['ro.build.id']
238 build_props['ro.build.legacy.id'] = 'legacy-build-id'
239 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
240 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700241 'product-brand/product-name/product-device:version-release/'
242 'legacy-build-id/version-incremental:build-type/build-tags',
243 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700244
245 self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
246 False)
247
248 info_dict['avb_enable'] = 'true'
249 info_dict['vbmeta_digest'] = 'abcde12345'
250 build_info = common.BuildInfo(info_dict, use_legacy_id=False)
251 self.assertEqual(
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700252 'product-brand/product-name/product-device:version-release/'
253 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
254 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700255
Tao Bao1c320f82019-10-04 23:25:12 -0700256 def test___getitem__(self):
257 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
258 self.assertEqual('value1', target_info['property1'])
259 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000260 self.assertEqual('build-foo',
261 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700262
263 def test___getitem__with_oem_props(self):
264 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
265 self.TEST_OEM_DICTS)
266 self.assertEqual('value1', target_info['property1'])
267 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000268 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700269
270 def test___setitem__(self):
271 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
272 self.assertEqual('value1', target_info['property1'])
273 target_info['property1'] = 'value2'
274 self.assertEqual('value2', target_info['property1'])
275
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000276 self.assertEqual('build-foo',
277 target_info['build.prop'].GetProp('ro.build.foo'))
278 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
279 self.assertEqual('build-bar',
280 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700281
282 def test_get(self):
283 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
284 self.assertEqual('value1', target_info.get('property1'))
285 self.assertEqual(4096, target_info.get('property2'))
286 self.assertEqual(4096, target_info.get('property2', 1024))
287 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000288 self.assertEqual('build-foo',
289 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700290
291 def test_get_with_oem_props(self):
292 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
293 self.TEST_OEM_DICTS)
294 self.assertEqual('value1', target_info.get('property1'))
295 self.assertEqual(4096, target_info.get('property2'))
296 self.assertEqual(4096, target_info.get('property2', 1024))
297 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000298 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700299
300 def test_items(self):
301 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
302 items = target_info.items()
303 self.assertIn(('property1', 'value1'), items)
304 self.assertIn(('property2', 4096), items)
305
306 def test_GetBuildProp(self):
307 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
308 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
309 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
310 'ro.build.nonexistent')
311
312 def test_GetBuildProp_with_oem_props(self):
313 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
314 self.TEST_OEM_DICTS)
315 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
316 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
317 'ro.build.nonexistent')
318
Daniel Normand5fe8622020-01-08 17:01:11 -0800319 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700320 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800321 self.assertEqual(
322 target_info.GetPartitionFingerprint('vendor'),
323 'vendor-product-brand/vendor-product-name/vendor-product-device'
324 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
325 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700326
Daniel Normand5fe8622020-01-08 17:01:11 -0800327 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700328 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800329 self.assertEqual(
330 target_info.GetPartitionFingerprint('system_other'),
331 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700332
Daniel Normand5fe8622020-01-08 17:01:11 -0800333 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
334 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000335 info_dict['vendor.build.prop'].build_props[
336 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800337 target_info = common.BuildInfo(info_dict, None)
338 self.assertEqual(
339 target_info.GetPartitionFingerprint('vendor'),
340 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700341
342 def test_WriteMountOemScript(self):
343 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
344 self.TEST_OEM_DICTS)
345 script_writer = test_utils.MockScriptWriter()
346 target_info.WriteMountOemScript(script_writer)
347 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
348
349 def test_WriteDeviceAssertions(self):
350 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
351 script_writer = test_utils.MockScriptWriter()
352 target_info.WriteDeviceAssertions(script_writer, False)
353 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
354
355 def test_WriteDeviceAssertions_with_oem_props(self):
356 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
357 self.TEST_OEM_DICTS)
358 script_writer = test_utils.MockScriptWriter()
359 target_info.WriteDeviceAssertions(script_writer, False)
360 self.assertEqual(
361 [
362 ('AssertOemProperty', 'ro.product.device',
363 ['device1', 'device2', 'device3'], False),
364 ('AssertOemProperty', 'ro.product.brand',
365 ['brand1', 'brand2', 'brand3'], False),
366 ],
367 script_writer.lines)
368
Steven Laver8e2086e2020-04-27 16:26:31 -0700369 def test_ResolveRoProductProperty_FromVendor(self):
370 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
371 info = common.BuildInfo(info_dict, None)
372 self.assertEqual('vendor-product-device',
373 info.GetBuildProp('ro.product.device'))
374
375 def test_ResolveRoProductProperty_FromSystem(self):
376 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000377 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700378 info = common.BuildInfo(info_dict, None)
379 self.assertEqual('system-product-device',
380 info.GetBuildProp('ro.product.device'))
381
382 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
383 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000384 info_dict['build.prop'].build_props[
385 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700386 with self.assertRaisesRegexp(common.ExternalError,
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700387 'Invalid ro.product.property_source_order'):
Steven Laver8e2086e2020-04-27 16:26:31 -0700388 info = common.BuildInfo(info_dict, None)
389 info.GetBuildProp('ro.product.device')
390
391 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
392 info_dict = copy.deepcopy(
393 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
394 info = common.BuildInfo(info_dict, None)
395 self.assertEqual('vendor-product-device',
396 info.GetBuildProp('ro.product.device'))
397
398 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
399 info_dict = copy.deepcopy(
400 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
401 info = common.BuildInfo(info_dict, None)
402 self.assertEqual('product-device',
403 info.GetBuildProp('ro.product.device'))
404
Tao Bao1c320f82019-10-04 23:25:12 -0700405
Tao Bao65b94e92018-10-11 21:57:26 -0700406class CommonZipTest(test_utils.ReleaseToolsTestCase):
407
Tao Bao31b08072017-11-08 15:50:59 -0800408 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700409 test_file_name=None, expected_stat=None, expected_mode=0o644,
410 expected_compress_type=zipfile.ZIP_STORED):
411 # Verify the stat if present.
412 if test_file_name is not None:
413 new_stat = os.stat(test_file_name)
414 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
415 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
416
417 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400418 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700419
420 # Verify the timestamp.
421 info = zip_file.getinfo(arcname)
422 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
423
424 # Verify the file mode.
425 mode = (info.external_attr >> 16) & 0o777
426 self.assertEqual(mode, expected_mode)
427
428 # Verify the compress type.
429 self.assertEqual(info.compress_type, expected_compress_type)
430
431 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800432 entry = zip_file.open(arcname)
433 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700434 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800435 sha1_hash.update(chunk)
436 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700437 self.assertIsNone(zip_file.testzip())
438
Dan Albert8e0178d2015-01-27 15:53:15 -0800439 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700440 with tempfile.NamedTemporaryFile() as test_file:
441 test_file_name = test_file.name
442 for data in contents:
443 test_file.write(bytes(data))
444 return self._test_ZipWriteFile(test_file_name, extra_zipwrite_args)
445
446 def _test_ZipWriteFile(self, test_file_name, extra_zipwrite_args=None):
Dan Albert8e0178d2015-01-27 15:53:15 -0800447 extra_zipwrite_args = dict(extra_zipwrite_args or {})
448
449 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800450 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700451
452 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800453 zip_file_name = zip_file.name
454
455 # File names within an archive strip the leading slash.
456 arcname = extra_zipwrite_args.get("arcname", test_file_name)
457 if arcname[0] == "/":
458 arcname = arcname[1:]
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700459 sha1_hash = hash_file(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800460
461 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400462 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800463
464 try:
Dan Albert8e0178d2015-01-27 15:53:15 -0800465 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700466 expected_compress_type = extra_zipwrite_args.get("compress_type",
467 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800468
Kelvin Zhangea84d422023-04-11 12:50:34 -0700469 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
470 # the timestamp after writing.
471 os.utime(test_file_name, (1234567, 1234567))
472 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800473 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000474 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800475
Tao Bao31b08072017-11-08 15:50:59 -0800476 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
477 test_file_name, expected_stat, expected_mode,
478 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800479 finally:
Dan Albert8e0178d2015-01-27 15:53:15 -0800480 os.remove(zip_file_name)
481
Tao Baof3282b42015-04-01 11:21:55 -0700482 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
483 extra_args = dict(extra_args or {})
484
485 zip_file = tempfile.NamedTemporaryFile(delete=False)
486 zip_file_name = zip_file.name
487 zip_file.close()
488
Kelvin Zhang928c2342020-09-22 16:15:57 -0400489 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700490
491 try:
492 expected_compress_type = extra_args.get("compress_type",
493 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700494 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700495 arcname = zinfo_or_arcname
496 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700497 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700498 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700499 if zinfo_or_arcname.external_attr:
500 zinfo_perms = zinfo_or_arcname.external_attr >> 16
501 else:
502 zinfo_perms = 0o600
503 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700504
Tao Bao58c1b962015-05-20 09:32:18 -0700505 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000506 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700507
Tao Bao31b08072017-11-08 15:50:59 -0800508 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700509 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700510 expected_compress_type=expected_compress_type)
511 finally:
512 os.remove(zip_file_name)
513
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700514 def _test_ZipWriteStr_large_file(self, large_file: BinaryIO, small, extra_args=None):
Tao Baof3282b42015-04-01 11:21:55 -0700515 extra_args = dict(extra_args or {})
516
517 zip_file = tempfile.NamedTemporaryFile(delete=False)
518 zip_file_name = zip_file.name
519
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700520 test_file_name = large_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700521
522 arcname_large = test_file_name
523 arcname_small = "bar"
524
525 # File names within an archive strip the leading slash.
526 if arcname_large[0] == "/":
527 arcname_large = arcname_large[1:]
528
529 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400530 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700531
532 try:
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700533 sha1_hash = hash_file(test_file_name)
Tao Baof3282b42015-04-01 11:21:55 -0700534
Kelvin Zhangea84d422023-04-11 12:50:34 -0700535 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
536 # the timestamp after writing.
537 os.utime(test_file_name, (1234567, 1234567))
Tao Baof3282b42015-04-01 11:21:55 -0700538 expected_stat = os.stat(test_file_name)
539 expected_mode = 0o644
540 expected_compress_type = extra_args.get("compress_type",
541 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700542
543 common.ZipWrite(zip_file, test_file_name, **extra_args)
544 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000545 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700546
547 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800548 self._verify(zip_file, zip_file_name, arcname_large,
549 sha1_hash.hexdigest(), test_file_name, expected_stat,
550 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700551
552 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800553 self._verify(zip_file, zip_file_name, arcname_small,
554 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700555 expected_compress_type=expected_compress_type)
556 finally:
557 os.remove(zip_file_name)
Tao Baof3282b42015-04-01 11:21:55 -0700558
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000559 def _test_reset_ZIP64_LIMIT(self, func, *args):
560 default_limit = (1 << 31) - 1
561 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
562 func(*args)
563 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
564
Dan Albert8e0178d2015-01-27 15:53:15 -0800565 def test_ZipWrite(self):
566 file_contents = os.urandom(1024)
567 self._test_ZipWrite(file_contents)
568
569 def test_ZipWrite_with_opts(self):
570 file_contents = os.urandom(1024)
571 self._test_ZipWrite(file_contents, {
572 "arcname": "foobar",
573 "perms": 0o777,
574 "compress_type": zipfile.ZIP_DEFLATED,
575 })
Tao Baof3282b42015-04-01 11:21:55 -0700576 self._test_ZipWrite(file_contents, {
577 "arcname": "foobar",
578 "perms": 0o700,
579 "compress_type": zipfile.ZIP_STORED,
580 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800581
582 def test_ZipWrite_large_file(self):
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700583 with get_2gb_file() as tmpfile:
584 self._test_ZipWriteFile(tmpfile.name, {
585 "compress_type": zipfile.ZIP_DEFLATED,
586 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800587
588 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000589 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
Tao Baof3282b42015-04-01 11:21:55 -0700590
591 def test_ZipWriteStr(self):
592 random_string = os.urandom(1024)
593 # Passing arcname
594 self._test_ZipWriteStr("foo", random_string)
595
596 # Passing zinfo
597 zinfo = zipfile.ZipInfo(filename="foo")
598 self._test_ZipWriteStr(zinfo, random_string)
599
600 # Timestamp in the zinfo should be overwritten.
601 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
602 self._test_ZipWriteStr(zinfo, random_string)
603
604 def test_ZipWriteStr_with_opts(self):
605 random_string = os.urandom(1024)
606 # Passing arcname
607 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700608 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700609 "compress_type": zipfile.ZIP_DEFLATED,
610 })
Tao Bao58c1b962015-05-20 09:32:18 -0700611 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700612 "compress_type": zipfile.ZIP_STORED,
613 })
614
615 # Passing zinfo
616 zinfo = zipfile.ZipInfo(filename="foo")
617 self._test_ZipWriteStr(zinfo, random_string, {
618 "compress_type": zipfile.ZIP_DEFLATED,
619 })
620 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700621 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700622 "compress_type": zipfile.ZIP_STORED,
623 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700624 self._test_ZipWriteStr(zinfo, random_string, {
625 "perms": 0o000,
626 "compress_type": zipfile.ZIP_STORED,
627 })
Tao Baof3282b42015-04-01 11:21:55 -0700628
629 def test_ZipWriteStr_large_file(self):
630 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
631 # the workaround. We will only test the case of writing a string into a
632 # large archive.
Tao Baof3282b42015-04-01 11:21:55 -0700633 short_string = os.urandom(1024)
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700634 with get_2gb_file() as large_file:
635 self._test_ZipWriteStr_large_file(large_file, short_string, {
636 "compress_type": zipfile.ZIP_DEFLATED,
637 })
Tao Baof3282b42015-04-01 11:21:55 -0700638
639 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000640 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700641 zinfo = zipfile.ZipInfo(filename="foo")
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000642 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700643
644 def test_bug21309935(self):
645 zip_file = tempfile.NamedTemporaryFile(delete=False)
646 zip_file_name = zip_file.name
647 zip_file.close()
648
649 try:
650 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400651 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700652 # Default perms should be 0o644 when passing the filename.
653 common.ZipWriteStr(zip_file, "foo", random_string)
654 # Honor the specified perms.
655 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
656 # The perms in zinfo should be untouched.
657 zinfo = zipfile.ZipInfo(filename="baz")
658 zinfo.external_attr = 0o740 << 16
659 common.ZipWriteStr(zip_file, zinfo, random_string)
660 # Explicitly specified perms has the priority.
661 zinfo = zipfile.ZipInfo(filename="qux")
662 zinfo.external_attr = 0o700 << 16
663 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000664 common.ZipClose(zip_file)
Tao Bao58c1b962015-05-20 09:32:18 -0700665
Tao Bao31b08072017-11-08 15:50:59 -0800666 self._verify(zip_file, zip_file_name, "foo",
667 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700668 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800669 self._verify(zip_file, zip_file_name, "bar",
670 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700671 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800672 self._verify(zip_file, zip_file_name, "baz",
673 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700674 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800675 self._verify(zip_file, zip_file_name, "qux",
676 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700677 expected_mode=0o400)
678 finally:
679 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700680
Tao Bao82490d32019-04-09 00:12:30 -0700681 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800682 def test_ZipDelete(self):
683 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
684 output_zip = zipfile.ZipFile(zip_file.name, 'w',
685 compression=zipfile.ZIP_DEFLATED)
686 with tempfile.NamedTemporaryFile() as entry_file:
687 entry_file.write(os.urandom(1024))
688 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
689 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
690 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000691 common.ZipClose(output_zip)
Tao Bao89d7ab22017-12-14 17:05:33 -0800692 zip_file.close()
693
694 try:
695 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400696 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800697 entries = check_zip.namelist()
698 self.assertTrue('Test1' in entries)
699 self.assertFalse('Test2' in entries)
700 self.assertTrue('Test3' in entries)
701
Tao Bao986ee862018-10-04 15:46:16 -0700702 self.assertRaises(
703 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400704 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800705 entries = check_zip.namelist()
706 self.assertTrue('Test1' in entries)
707 self.assertFalse('Test2' in entries)
708 self.assertTrue('Test3' in entries)
709
710 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400711 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800712 entries = check_zip.namelist()
713 self.assertTrue('Test1' in entries)
714 self.assertFalse('Test2' in entries)
715 self.assertFalse('Test3' in entries)
716
717 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400718 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800719 entries = check_zip.namelist()
720 self.assertFalse('Test1' in entries)
721 self.assertFalse('Test2' in entries)
722 self.assertFalse('Test3' in entries)
723 finally:
724 os.remove(zip_file.name)
725
Tao Bao0ff15de2019-03-20 11:26:06 -0700726 @staticmethod
727 def _test_UnzipTemp_createZipFile():
728 zip_file = common.MakeTempFile(suffix='.zip')
729 output_zip = zipfile.ZipFile(
730 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
731 contents = os.urandom(1024)
732 with tempfile.NamedTemporaryFile() as entry_file:
733 entry_file.write(contents)
734 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
735 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
736 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
737 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
738 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000739 common.ZipClose(output_zip)
740 common.ZipClose(output_zip)
Tao Bao0ff15de2019-03-20 11:26:06 -0700741 return zip_file
742
Tao Bao82490d32019-04-09 00:12:30 -0700743 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700744 def test_UnzipTemp(self):
745 zip_file = self._test_UnzipTemp_createZipFile()
746 unzipped_dir = common.UnzipTemp(zip_file)
747 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
748 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
749 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
750 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
751 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
752
Tao Bao82490d32019-04-09 00:12:30 -0700753 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700754 def test_UnzipTemp_withPatterns(self):
755 zip_file = self._test_UnzipTemp_createZipFile()
756
757 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
758 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
759 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
760 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
761 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
762 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
763
764 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
765 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
766 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
767 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
768 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
769 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
770
771 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
772 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
773 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
774 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
775 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
776 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
777
778 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
779 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
780 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
781 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
782 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
783 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
784
785 def test_UnzipTemp_withEmptyPatterns(self):
786 zip_file = self._test_UnzipTemp_createZipFile()
787 unzipped_dir = common.UnzipTemp(zip_file, [])
788 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
789 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
790 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
791 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
792 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
793
Tao Bao82490d32019-04-09 00:12:30 -0700794 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700795 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
796 zip_file = self._test_UnzipTemp_createZipFile()
797 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
798 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
799 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
800 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
801 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
802 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
803
804 def test_UnzipTemp_withNoMatchingPatterns(self):
805 zip_file = self._test_UnzipTemp_createZipFile()
806 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
807 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
808 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
809 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
810 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
811 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
812
Tao Bao89d7ab22017-12-14 17:05:33 -0800813
Tao Bao65b94e92018-10-11 21:57:26 -0700814class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800815 """Tests the APK utils related functions."""
816
817 APKCERTS_TXT1 = (
818 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
819 ' private_key="certs/devkey.pk8"\n'
820 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700821 ' certificate="build/make/target/product/security/platform.x509.pem"'
822 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800823 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
824 )
825
826 APKCERTS_CERTMAP1 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700827 'RecoveryLocalizer.apk': 'certs/devkey',
828 'Settings.apk': 'build/make/target/product/security/platform',
829 'TV.apk': 'PRESIGNED',
Tao Bao818ddf52018-01-05 11:17:34 -0800830 }
831
832 APKCERTS_TXT2 = (
833 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
834 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
835 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
836 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
837 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
838 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
839 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
840 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
841 )
842
843 APKCERTS_CERTMAP2 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700844 'Compressed1.apk': 'certs/compressed1',
845 'Compressed2a.apk': 'certs/compressed2',
846 'Compressed2b.apk': 'certs/compressed2',
847 'Compressed3.apk': 'certs/compressed3',
Tao Bao818ddf52018-01-05 11:17:34 -0800848 }
849
850 APKCERTS_TXT3 = (
851 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
852 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
853 )
854
855 APKCERTS_CERTMAP3 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700856 'Compressed4.apk': 'certs/compressed4',
Tao Bao818ddf52018-01-05 11:17:34 -0800857 }
858
Bill Peckham5c7b0342020-04-03 15:36:23 -0700859 # Test parsing with no optional fields, both optional fields, and only the
860 # partition optional field.
861 APKCERTS_TXT4 = (
862 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
863 ' private_key="certs/devkey.pk8"\n'
864 'name="Settings.apk"'
865 ' certificate="build/make/target/product/security/platform.x509.pem"'
866 ' private_key="build/make/target/product/security/platform.pk8"'
867 ' compressed="gz" partition="system"\n'
868 'name="TV.apk" certificate="PRESIGNED" private_key=""'
869 ' partition="product"\n'
870 )
871
872 APKCERTS_CERTMAP4 = {
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700873 'RecoveryLocalizer.apk': 'certs/devkey',
874 'Settings.apk': 'build/make/target/product/security/platform',
875 'TV.apk': 'PRESIGNED',
Bill Peckham5c7b0342020-04-03 15:36:23 -0700876 }
877
Tao Bao17e4e612018-02-16 17:12:54 -0800878 def setUp(self):
879 self.testdata_dir = test_utils.get_testdata_dir()
880
Tao Bao818ddf52018-01-05 11:17:34 -0800881 @staticmethod
882 def _write_apkcerts_txt(apkcerts_txt, additional=None):
883 if additional is None:
884 additional = []
885 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400886 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800887 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
888 for entry in additional:
889 target_files_zip.writestr(entry, '')
890 return target_files
891
892 def test_ReadApkCerts_NoncompressedApks(self):
893 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400894 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800895 certmap, ext = common.ReadApkCerts(input_zip)
896
897 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
898 self.assertIsNone(ext)
899
900 def test_ReadApkCerts_CompressedApks(self):
901 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
902 # not stored in '.gz' format, so it shouldn't be considered as installed.
903 target_files = self._write_apkcerts_txt(
904 self.APKCERTS_TXT2,
905 ['Compressed1.apk.gz', 'Compressed3.apk'])
906
Kelvin Zhang928c2342020-09-22 16:15:57 -0400907 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800908 certmap, ext = common.ReadApkCerts(input_zip)
909
910 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
911 self.assertEqual('.gz', ext)
912
913 # Alternative case with '.xz'.
914 target_files = self._write_apkcerts_txt(
915 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
916
Kelvin Zhang928c2342020-09-22 16:15:57 -0400917 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800918 certmap, ext = common.ReadApkCerts(input_zip)
919
920 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
921 self.assertEqual('.xz', ext)
922
923 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
924 target_files = self._write_apkcerts_txt(
925 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
926 ['Compressed1.apk.gz', 'Compressed3.apk'])
927
Kelvin Zhang928c2342020-09-22 16:15:57 -0400928 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800929 certmap, ext = common.ReadApkCerts(input_zip)
930
931 certmap_merged = self.APKCERTS_CERTMAP1.copy()
932 certmap_merged.update(self.APKCERTS_CERTMAP2)
933 self.assertDictEqual(certmap_merged, certmap)
934 self.assertEqual('.gz', ext)
935
936 def test_ReadApkCerts_MultipleCompressionMethods(self):
937 target_files = self._write_apkcerts_txt(
938 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
939 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
940
Kelvin Zhang928c2342020-09-22 16:15:57 -0400941 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800942 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
943
944 def test_ReadApkCerts_MismatchingKeys(self):
945 malformed_apkcerts_txt = (
946 'name="App1.apk" certificate="certs/cert1.x509.pem"'
947 ' private_key="certs/cert2.pk8"\n'
948 )
949 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
950
Kelvin Zhang928c2342020-09-22 16:15:57 -0400951 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800952 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
953
Bill Peckham5c7b0342020-04-03 15:36:23 -0700954 def test_ReadApkCerts_WithWithoutOptionalFields(self):
955 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400956 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700957 certmap, ext = common.ReadApkCerts(input_zip)
958
959 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
960 self.assertIsNone(ext)
961
Tao Bao04e1f012018-02-04 12:13:35 -0800962 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800963 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
964 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800965 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800966 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
967
968 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800969 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800970 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
971
Tao Bao82490d32019-04-09 00:12:30 -0700972 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700973 def test_ExtractAvbPublicKey(self):
974 privkey = os.path.join(self.testdata_dir, 'testkey.key')
975 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700976 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
977 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
978 with open(extracted_from_privkey, 'rb') as privkey_fp, \
Kelvin Zhang8aa65252023-08-30 18:29:59 -0700979 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700980 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
981
Tao Bao17e4e612018-02-16 17:12:54 -0800982 def test_ParseCertificate(self):
983 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
984
985 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800986 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
987 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800988 expected, _ = proc.communicate()
989 self.assertEqual(0, proc.returncode)
990
991 with open(cert) as cert_fp:
992 actual = common.ParseCertificate(cert_fp.read())
993 self.assertEqual(expected, actual)
994
Tao Bao82490d32019-04-09 00:12:30 -0700995 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700996 def test_GetMinSdkVersion(self):
997 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
998 self.assertEqual('24', common.GetMinSdkVersion(test_app))
999
Tao Bao82490d32019-04-09 00:12:30 -07001000 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001001 def test_GetMinSdkVersion_invalidInput(self):
1002 self.assertRaises(
1003 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
1004
Tao Bao82490d32019-04-09 00:12:30 -07001005 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001006 def test_GetMinSdkVersionInt(self):
1007 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
1008 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
1009
Tao Bao82490d32019-04-09 00:12:30 -07001010 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001011 def test_GetMinSdkVersionInt_invalidInput(self):
1012 self.assertRaises(
1013 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1014 {})
1015
Tao Bao818ddf52018-01-05 11:17:34 -08001016
Tao Bao65b94e92018-10-11 21:57:26 -07001017class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001018
Tao Bao02a08592018-07-22 12:40:45 -07001019 def setUp(self):
1020 self.testdata_dir = test_utils.get_testdata_dir()
1021
Tao Bao82490d32019-04-09 00:12:30 -07001022 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001023 def test_GetSparseImage_emptyBlockMapFile(self):
1024 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001025 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001026 target_files_zip.write(
1027 test_utils.construct_sparse_image([
1028 (0xCAC1, 6),
1029 (0xCAC3, 3),
1030 (0xCAC1, 4)]),
1031 arcname='IMAGES/system.img')
1032 target_files_zip.writestr('IMAGES/system.map', '')
1033 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1034 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1035
Tao Baodba59ee2018-01-09 13:21:02 -08001036 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001037 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001038 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001039
1040 self.assertDictEqual(
1041 {
1042 '__COPY': RangeSet("0"),
1043 '__NONZERO-0': RangeSet("1-5 9-12"),
1044 },
1045 sparse_image.file_map)
1046
Daniel Norman21c34f72020-11-11 17:25:50 -08001047 def test_PartitionMapFromTargetFiles(self):
1048 target_files_dir = common.MakeTempDir()
1049 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1050 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1051 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1052 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1053 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1054 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1055 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1056 self.assertDictEqual(
1057 partition_map,
1058 {
1059 'system': 'SYSTEM',
1060 'vendor': 'SYSTEM/vendor',
1061 # Prefer PRODUCT over SYSTEM/product
1062 'product': 'PRODUCT',
1063 'odm': 'SYSTEM/vendor/odm',
1064 'vendor_dlkm': 'VENDOR_DLKM',
1065 # No system_ext or odm_dlkm
1066 })
1067
Daniel Normand3351562020-10-29 12:33:11 -07001068 def test_SharedUidPartitionViolations(self):
1069 uid_dict = {
1070 'android.uid.phone': {
1071 'system': ['system_phone.apk'],
1072 'system_ext': ['system_ext_phone.apk'],
1073 },
1074 'android.uid.wifi': {
1075 'vendor': ['vendor_wifi.apk'],
1076 'odm': ['odm_wifi.apk'],
1077 },
1078 }
1079 errors = common.SharedUidPartitionViolations(
1080 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1081 self.assertEqual(errors, [])
1082
1083 def test_SharedUidPartitionViolations_Violation(self):
1084 uid_dict = {
1085 'android.uid.phone': {
1086 'system': ['system_phone.apk'],
1087 'vendor': ['vendor_phone.apk'],
1088 },
1089 }
1090 errors = common.SharedUidPartitionViolations(
1091 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1092 self.assertIn(
1093 ('APK sharedUserId "android.uid.phone" found across partition groups '
1094 'in partitions "system,vendor"'), errors)
1095
Tao Baob2de7d92019-04-10 10:01:47 -07001096 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001097 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001098 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1099 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001100 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001101 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1102 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001103
Tao Bao82490d32019-04-09 00:12:30 -07001104 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001105 def test_GetSparseImage_missingBlockMapFile(self):
1106 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001107 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001108 target_files_zip.write(
1109 test_utils.construct_sparse_image([
1110 (0xCAC1, 6),
1111 (0xCAC3, 3),
1112 (0xCAC1, 4)]),
1113 arcname='IMAGES/system.img')
1114 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1115 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1116
Tao Baodba59ee2018-01-09 13:21:02 -08001117 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001118 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001119 self.assertRaises(
1120 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1121 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001122
Tao Bao82490d32019-04-09 00:12:30 -07001123 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001124 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1125 """Tests the case of having overlapping blocks but disallowed."""
1126 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001127 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001128 target_files_zip.write(
1129 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1130 arcname='IMAGES/system.img')
1131 # Block 10 is shared between two files.
1132 target_files_zip.writestr(
1133 'IMAGES/system.map',
1134 '\n'.join([
1135 '/system/file1 1-5 9-10',
1136 '/system/file2 10-12']))
1137 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1138 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1139
Tao Baodba59ee2018-01-09 13:21:02 -08001140 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001141 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001142 self.assertRaises(
1143 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1144 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001145
Tao Bao82490d32019-04-09 00:12:30 -07001146 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001147 def test_GetSparseImage_sharedBlocks_allowed(self):
1148 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1149 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001150 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001151 # Construct an image with a care_map of "0-5 9-12".
1152 target_files_zip.write(
1153 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1154 arcname='IMAGES/system.img')
1155 # Block 10 is shared between two files.
1156 target_files_zip.writestr(
1157 'IMAGES/system.map',
1158 '\n'.join([
1159 '/system/file1 1-5 9-10',
1160 '/system/file2 10-12']))
1161 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1162 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1163
Tao Baodba59ee2018-01-09 13:21:02 -08001164 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001165 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001166 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001167
1168 self.assertDictEqual(
1169 {
1170 '__COPY': RangeSet("0"),
1171 '__NONZERO-0': RangeSet("6-8 13-15"),
1172 '/system/file1': RangeSet("1-5 9-10"),
1173 '/system/file2': RangeSet("11-12"),
1174 },
1175 sparse_image.file_map)
1176
1177 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1178 # 'incomplete'.
1179 self.assertTrue(
1180 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1181 self.assertNotIn(
1182 'incomplete', sparse_image.file_map['/system/file2'].extra)
1183
Tao Baoa264fef2019-10-06 21:55:20 -07001184 # '/system/file1' will only contain one field -- a copy of the input text.
1185 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1186
1187 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001188 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1189 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001190
Tao Bao82490d32019-04-09 00:12:30 -07001191 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001192 def test_GetSparseImage_incompleteRanges(self):
1193 """Tests the case of ext4 images with holes."""
1194 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001195 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001196 target_files_zip.write(
1197 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1198 arcname='IMAGES/system.img')
1199 target_files_zip.writestr(
1200 'IMAGES/system.map',
1201 '\n'.join([
1202 '/system/file1 1-5 9-10',
1203 '/system/file2 11-12']))
1204 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1205 # '/system/file2' has less blocks listed (2) than actual (3).
1206 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1207
Tao Baodba59ee2018-01-09 13:21:02 -08001208 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001209 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001210 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001211
Tao Baoa264fef2019-10-06 21:55:20 -07001212 self.assertEqual(
1213 '1-5 9-10',
1214 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001215 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1216
Tao Bao82490d32019-04-09 00:12:30 -07001217 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001218 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1219 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001220 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001221 target_files_zip.write(
1222 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1223 arcname='IMAGES/system.img')
1224 target_files_zip.writestr(
1225 'IMAGES/system.map',
1226 '\n'.join([
1227 '//system/file1 1-5 9-10',
1228 '//system/file2 11-12',
1229 '/system/app/file3 13-15']))
1230 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1231 # '/system/file2' has less blocks listed (2) than actual (3).
1232 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1233 # '/system/app/file3' has less blocks listed (3) than actual (4).
1234 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1235
1236 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001237 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001238 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1239
Tao Baoa264fef2019-10-06 21:55:20 -07001240 self.assertEqual(
1241 '1-5 9-10',
1242 sparse_image.file_map['//system/file1'].extra['text_str'])
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001243 self.assertTrue(
1244 sparse_image.file_map['//system/file2'].extra['incomplete'])
Tao Baod3554e62018-07-10 15:31:22 -07001245 self.assertTrue(
1246 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1247
Tao Bao82490d32019-04-09 00:12:30 -07001248 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001249 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1250 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001251 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001252 target_files_zip.write(
1253 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1254 arcname='IMAGES/system.img')
1255 target_files_zip.writestr(
1256 'IMAGES/system.map',
1257 '\n'.join([
1258 '//system/file1 1-5 9-10',
1259 '//init.rc 13-15']))
1260 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1261 # '/init.rc' has less blocks listed (3) than actual (4).
1262 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1263
1264 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001265 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001266 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1267
Tao Baoa264fef2019-10-06 21:55:20 -07001268 self.assertEqual(
1269 '1-5 9-10',
1270 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001271 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1272
Tao Bao82490d32019-04-09 00:12:30 -07001273 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001274 def test_GetSparseImage_fileNotFound(self):
1275 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001276 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001277 target_files_zip.write(
1278 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1279 arcname='IMAGES/system.img')
1280 target_files_zip.writestr(
1281 'IMAGES/system.map',
1282 '\n'.join([
1283 '//system/file1 1-5 9-10',
1284 '//system/file2 11-12']))
1285 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1286
1287 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001288 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001289 self.assertRaises(
1290 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1291 False)
1292
Tao Bao82490d32019-04-09 00:12:30 -07001293 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001294 def test_GetAvbChainedPartitionArg(self):
1295 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1296 info_dict = {
1297 'avb_avbtool': 'avbtool',
1298 'avb_system_key_path': pubkey,
1299 'avb_system_rollback_index_location': 2,
1300 }
Dennis Song4aae62e2023-10-02 04:31:34 +00001301 chained_partition_args = common.GetAvbChainedPartitionArg(
1302 'system', info_dict)
1303 self.assertEqual('system', chained_partition_args.partition)
1304 self.assertEqual(2, chained_partition_args.rollback_index_location)
1305 self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
Tao Bao02a08592018-07-22 12:40:45 -07001306
Tao Bao82490d32019-04-09 00:12:30 -07001307 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001308 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1309 key = os.path.join(self.testdata_dir, 'testkey.key')
1310 info_dict = {
1311 'avb_avbtool': 'avbtool',
1312 'avb_product_key_path': key,
1313 'avb_product_rollback_index_location': 2,
1314 }
Dennis Song4aae62e2023-10-02 04:31:34 +00001315 chained_partition_args = common.GetAvbChainedPartitionArg(
1316 'product', info_dict)
1317 self.assertEqual('product', chained_partition_args.partition)
1318 self.assertEqual(2, chained_partition_args.rollback_index_location)
1319 self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
Tao Bao02a08592018-07-22 12:40:45 -07001320
Tao Bao82490d32019-04-09 00:12:30 -07001321 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001322 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1323 info_dict = {
1324 'avb_avbtool': 'avbtool',
1325 'avb_system_key_path': 'does-not-exist',
1326 'avb_system_rollback_index_location': 2,
1327 }
1328 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Dennis Song4aae62e2023-10-02 04:31:34 +00001329 chained_partition_args = common.GetAvbChainedPartitionArg(
1330 'system', info_dict, pubkey)
1331 self.assertEqual('system', chained_partition_args.partition)
1332 self.assertEqual(2, chained_partition_args.rollback_index_location)
1333 self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
Tao Bao02a08592018-07-22 12:40:45 -07001334
Tao Bao82490d32019-04-09 00:12:30 -07001335 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001336 def test_GetAvbChainedPartitionArg_invalidKey(self):
1337 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1338 info_dict = {
1339 'avb_avbtool': 'avbtool',
1340 'avb_system_key_path': pubkey,
1341 'avb_system_rollback_index_location': 2,
1342 }
1343 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001344 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1345 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001346
Tao Baoa57ab9f2018-08-24 12:08:38 -07001347 INFO_DICT_DEFAULT = {
1348 'recovery_api_version': 3,
1349 'fstab_version': 2,
1350 'system_root_image': 'true',
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001351 'no_recovery': 'true',
Tao Baoa57ab9f2018-08-24 12:08:38 -07001352 'recovery_as_boot': 'true',
1353 }
1354
Daniel Norman4cc9df62019-07-18 10:11:07 -07001355 def test_LoadListFromFile(self):
1356 file_path = os.path.join(self.testdata_dir,
1357 'merge_config_framework_item_list')
1358 contents = common.LoadListFromFile(file_path)
1359 expected_contents = [
1360 'META/apkcerts.txt',
1361 'META/filesystem_config.txt',
1362 'META/root_filesystem_config.txt',
1363 'META/system_manifest.xml',
1364 'META/system_matrix.xml',
1365 'META/update_engine_config.txt',
1366 'PRODUCT/*',
1367 'ROOT/*',
1368 'SYSTEM/*',
1369 ]
1370 self.assertEqual(sorted(contents), sorted(expected_contents))
1371
Tao Baoa57ab9f2018-08-24 12:08:38 -07001372 @staticmethod
1373 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1374 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001375 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001376 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001377 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001378 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1379
1380 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1381 if info_dict.get('system_root_image') == 'true':
1382 fstab_values = FSTAB_TEMPLATE.format('/')
1383 else:
1384 fstab_values = FSTAB_TEMPLATE.format('/system')
1385 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001386
1387 common.ZipWriteStr(
1388 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001389 return target_files
1390
1391 def test_LoadInfoDict(self):
1392 target_files = self._test_LoadInfoDict_createTargetFiles(
1393 self.INFO_DICT_DEFAULT,
1394 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001395 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001396 loaded_dict = common.LoadInfoDict(target_files_zip)
1397 self.assertEqual(3, loaded_dict['recovery_api_version'])
1398 self.assertEqual(2, loaded_dict['fstab_version'])
1399 self.assertIn('/', loaded_dict['fstab'])
1400 self.assertIn('/system', loaded_dict['fstab'])
1401
1402 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1403 target_files = self._test_LoadInfoDict_createTargetFiles(
1404 self.INFO_DICT_DEFAULT,
1405 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001406 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001407 loaded_dict = common.LoadInfoDict(target_files_zip)
1408 self.assertEqual(3, loaded_dict['recovery_api_version'])
1409 self.assertEqual(2, loaded_dict['fstab_version'])
1410 self.assertIn('/', loaded_dict['fstab'])
1411 self.assertIn('/system', loaded_dict['fstab'])
1412
Tao Bao82490d32019-04-09 00:12:30 -07001413 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001414 def test_LoadInfoDict_dirInput(self):
1415 target_files = self._test_LoadInfoDict_createTargetFiles(
1416 self.INFO_DICT_DEFAULT,
1417 'BOOT/RAMDISK/system/etc/recovery.fstab')
1418 unzipped = common.UnzipTemp(target_files)
1419 loaded_dict = common.LoadInfoDict(unzipped)
1420 self.assertEqual(3, loaded_dict['recovery_api_version'])
1421 self.assertEqual(2, loaded_dict['fstab_version'])
1422 self.assertIn('/', loaded_dict['fstab'])
1423 self.assertIn('/system', loaded_dict['fstab'])
1424
Tao Bao82490d32019-04-09 00:12:30 -07001425 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001426 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1427 target_files = self._test_LoadInfoDict_createTargetFiles(
1428 self.INFO_DICT_DEFAULT,
1429 'BOOT/RAMDISK/system/etc/recovery.fstab')
1430 unzipped = common.UnzipTemp(target_files)
1431 loaded_dict = common.LoadInfoDict(unzipped)
1432 self.assertEqual(3, loaded_dict['recovery_api_version'])
1433 self.assertEqual(2, loaded_dict['fstab_version'])
1434 self.assertIn('/', loaded_dict['fstab'])
1435 self.assertIn('/system', loaded_dict['fstab'])
1436
1437 def test_LoadInfoDict_systemRootImageFalse(self):
1438 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1439 # launched prior to P will likely have this config.
1440 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1441 del info_dict['no_recovery']
1442 del info_dict['system_root_image']
1443 del info_dict['recovery_as_boot']
1444 target_files = self._test_LoadInfoDict_createTargetFiles(
1445 info_dict,
1446 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001447 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001448 loaded_dict = common.LoadInfoDict(target_files_zip)
1449 self.assertEqual(3, loaded_dict['recovery_api_version'])
1450 self.assertEqual(2, loaded_dict['fstab_version'])
1451 self.assertNotIn('/', loaded_dict['fstab'])
1452 self.assertIn('/system', loaded_dict['fstab'])
1453
1454 def test_LoadInfoDict_recoveryAsBootFalse(self):
1455 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1456 # devices launched since P will likely have this config.
1457 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1458 del info_dict['no_recovery']
1459 del info_dict['recovery_as_boot']
1460 target_files = self._test_LoadInfoDict_createTargetFiles(
1461 info_dict,
1462 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001463 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001464 loaded_dict = common.LoadInfoDict(target_files_zip)
1465 self.assertEqual(3, loaded_dict['recovery_api_version'])
1466 self.assertEqual(2, loaded_dict['fstab_version'])
1467 self.assertIn('/', loaded_dict['fstab'])
1468 self.assertIn('/system', loaded_dict['fstab'])
1469
1470 def test_LoadInfoDict_noRecoveryTrue(self):
1471 # Device doesn't have a recovery partition at all.
1472 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1473 del info_dict['recovery_as_boot']
1474 target_files = self._test_LoadInfoDict_createTargetFiles(
1475 info_dict,
1476 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001477 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001478 loaded_dict = common.LoadInfoDict(target_files_zip)
1479 self.assertEqual(3, loaded_dict['recovery_api_version'])
1480 self.assertEqual(2, loaded_dict['fstab_version'])
1481 self.assertIsNone(loaded_dict['fstab'])
1482
Tao Bao82490d32019-04-09 00:12:30 -07001483 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001484 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1485 target_files = self._test_LoadInfoDict_createTargetFiles(
1486 self.INFO_DICT_DEFAULT,
1487 'BOOT/RAMDISK/system/etc/recovery.fstab')
1488 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001489 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001490 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1491
Tao Bao82490d32019-04-09 00:12:30 -07001492 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001493 def test_LoadInfoDict_repacking(self):
1494 target_files = self._test_LoadInfoDict_createTargetFiles(
1495 self.INFO_DICT_DEFAULT,
1496 'BOOT/RAMDISK/system/etc/recovery.fstab')
1497 unzipped = common.UnzipTemp(target_files)
1498 loaded_dict = common.LoadInfoDict(unzipped, True)
1499 self.assertEqual(3, loaded_dict['recovery_api_version'])
1500 self.assertEqual(2, loaded_dict['fstab_version'])
1501 self.assertIn('/', loaded_dict['fstab'])
1502 self.assertIn('/system', loaded_dict['fstab'])
1503 self.assertEqual(
1504 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1505 self.assertEqual(
1506 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1507 loaded_dict['root_fs_config'])
1508
1509 def test_LoadInfoDict_repackingWithZipFileInput(self):
1510 target_files = self._test_LoadInfoDict_createTargetFiles(
1511 self.INFO_DICT_DEFAULT,
1512 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001513 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001514 self.assertRaises(
1515 AssertionError, common.LoadInfoDict, target_files_zip, True)
1516
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001517 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1518 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001519 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001520 'super_partition_groups': 'group_a',
1521 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001522 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001523 }
1524 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001525 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001526 'super_partition_groups': 'group_a group_b',
1527 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001528 'super_block_devices': 'super',
1529 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001530 'super_group_a_partition_list': 'vendor',
1531 'super_group_a_group_size': '1000',
1532 'super_group_b_partition_list': 'product',
1533 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001534 }
1535 merged_dict = common.MergeDynamicPartitionInfoDicts(
1536 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001537 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001538 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001539 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001540 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001541 'dynamic_partition_list': 'product system vendor',
1542 'super_block_devices': 'super',
1543 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001544 'super_group_a_partition_list': 'system vendor',
1545 'super_group_a_group_size': '1000',
1546 'super_group_b_partition_list': 'product',
1547 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001548 }
1549 self.assertEqual(merged_dict, expected_merged_dict)
1550
1551 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1552 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001553 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001554 'super_partition_groups': 'group_a',
1555 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001556 'super_group_a_partition_list': 'system',
1557 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001558 }
1559 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001560 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001561 'super_partition_groups': 'group_a group_b',
1562 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001563 'super_group_a_partition_list': 'vendor',
1564 'super_group_a_group_size': '1000',
1565 'super_group_b_partition_list': 'product',
1566 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001567 }
1568 merged_dict = common.MergeDynamicPartitionInfoDicts(
1569 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001570 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001571 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001572 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001573 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001574 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001575 'super_group_a_partition_list': 'system vendor',
1576 'super_group_a_group_size': '1000',
1577 'super_group_b_partition_list': 'product',
1578 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001579 }
1580 self.assertEqual(merged_dict, expected_merged_dict)
1581
Daniel Norman276f0622019-07-26 14:13:51 -07001582 def test_GetAvbPartitionArg(self):
1583 info_dict = {}
1584 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1585 self.assertEqual(
Dennis Song6e5e44d2023-10-03 02:18:06 +00001586 [common.AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, '/path/to/system.img'],
1587 cmd)
Daniel Norman276f0622019-07-26 14:13:51 -07001588
1589 @test_utils.SkipIfExternalToolsUnavailable()
1590 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1591 testdata_dir = test_utils.get_testdata_dir()
1592 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1593 info_dict = {
1594 'avb_avbtool': 'avbtool',
1595 'avb_vendor_key_path': pubkey,
1596 'avb_vendor_rollback_index_location': 5,
1597 }
1598 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1599 self.assertEqual(2, len(cmd))
Dennis Song6e5e44d2023-10-03 02:18:06 +00001600 self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
Dennis Song4aae62e2023-10-02 04:31:34 +00001601 chained_partition_args = cmd[1]
1602 self.assertEqual('vendor', chained_partition_args.partition)
1603 self.assertEqual(5, chained_partition_args.rollback_index_location)
1604 self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
Daniel Norman276f0622019-07-26 14:13:51 -07001605
Tao Bao3612c882019-10-14 17:49:31 -07001606 @test_utils.SkipIfExternalToolsUnavailable()
1607 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1608 testdata_dir = test_utils.get_testdata_dir()
1609 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1610 info_dict = {
1611 'avb_avbtool': 'avbtool',
1612 'avb_recovery_key_path': pubkey,
1613 'avb_recovery_rollback_index_location': 3,
1614 }
1615 cmd = common.GetAvbPartitionArg(
1616 'recovery', '/path/to/recovery.img', info_dict)
1617 self.assertFalse(cmd)
1618
1619 @test_utils.SkipIfExternalToolsUnavailable()
1620 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1621 testdata_dir = test_utils.get_testdata_dir()
1622 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1623 info_dict = {
1624 'ab_update': 'true',
1625 'avb_avbtool': 'avbtool',
1626 'avb_recovery_key_path': pubkey,
1627 'avb_recovery_rollback_index_location': 3,
1628 }
1629 cmd = common.GetAvbPartitionArg(
1630 'recovery', '/path/to/recovery.img', info_dict)
1631 self.assertEqual(2, len(cmd))
Dennis Song6e5e44d2023-10-03 02:18:06 +00001632 self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
Dennis Song4aae62e2023-10-02 04:31:34 +00001633 chained_partition_args = cmd[1]
1634 self.assertEqual('recovery', chained_partition_args.partition)
1635 self.assertEqual(3, chained_partition_args.rollback_index_location)
1636 self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
Tao Bao3612c882019-10-14 17:49:31 -07001637
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001638 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001639 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1640 self.assertFalse(os.path.exists(pubkey))
1641
1642 common.OPTIONS.info_dict = {
1643 'gki_signing_key_path': pubkey,
1644 'gki_signing_algorithm': 'SHA256_RSA4096',
1645 'gki_signing_signature_args': '--prop foo:bar',
1646 }
Kelvin Zhang03dc6ee2023-06-28 10:10:39 -07001647 common.OPTIONS.search_path = None
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001648 test_file = tempfile.NamedTemporaryFile()
1649 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001650 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001651
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001652 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001653 pubkey = 'no_testkey_gki.pem'
1654 self.assertFalse(os.path.exists(pubkey))
1655
1656 # Tests it should raise ExternalError if no key found under
1657 # OPTIONS.search_path.
1658 search_path_dir = common.MakeTempDir()
1659 search_pubkey = os.path.join(search_path_dir, pubkey)
1660 self.assertFalse(os.path.exists(search_pubkey))
1661
1662 common.OPTIONS.search_path = search_path_dir
1663 common.OPTIONS.info_dict = {
1664 'gki_signing_key_path': pubkey,
1665 'gki_signing_algorithm': 'SHA256_RSA4096',
1666 'gki_signing_signature_args': '--prop foo:bar',
1667 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001668 test_file = tempfile.NamedTemporaryFile()
1669 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001670 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001671
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001672
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001673class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1674 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001675 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001676 'ro.odm.build.date.utc=1578430045',
1677 'ro.odm.build.fingerprint='
1678 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1679 'ro.product.odm.device=coral',
1680 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1681 ]
1682
1683 @staticmethod
1684 def _BuildZipFile(entries):
1685 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001686 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001687 for name, content in entries.items():
1688 input_zip.writestr(name, content)
1689
1690 return input_file
1691
1692 def test_parseBuildProps_noImportStatement(self):
1693 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001694 'ro.odm.build.date.utc=1578430045',
1695 'ro.odm.build.fingerprint='
1696 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1697 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001698 ]
1699 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001700 'ODM/etc/build.prop': '\n'.join(build_prop),
1701 })
1702
Kelvin Zhang928c2342020-09-22 16:15:57 -04001703 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001704 placeholder_values = {
1705 'ro.boot.product.device_name': ['std', 'pro']
1706 }
1707 partition_props = common.PartitionBuildProps.FromInputFile(
1708 input_zip, 'odm', placeholder_values)
1709
1710 self.assertEqual({
1711 'ro.odm.build.date.utc': '1578430045',
1712 'ro.odm.build.fingerprint':
1713 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1714 'ro.product.odm.device': 'coral',
1715 }, partition_props.build_props)
1716
1717 self.assertEqual(set(), partition_props.prop_overrides)
1718
1719 def test_parseBuildProps_singleImportStatement(self):
1720 build_std_prop = [
1721 'ro.product.odm.device=coral',
1722 'ro.product.odm.name=product1',
1723 ]
1724 build_pro_prop = [
1725 'ro.product.odm.device=coralpro',
1726 'ro.product.odm.name=product2',
1727 ]
1728
1729 input_file = self._BuildZipFile({
1730 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
1731 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
1732 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
1733 })
1734
Kelvin Zhang928c2342020-09-22 16:15:57 -04001735 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001736 placeholder_values = {
1737 'ro.boot.product.device_name': 'std'
1738 }
1739 partition_props = common.PartitionBuildProps.FromInputFile(
1740 input_zip, 'odm', placeholder_values)
1741
1742 self.assertEqual({
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001743 'ro.odm.build.date.utc': '1578430045',
1744 'ro.odm.build.fingerprint':
1745 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1746 'ro.product.odm.device': 'coral',
1747 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00001748 }, partition_props.build_props)
1749
Kelvin Zhang928c2342020-09-22 16:15:57 -04001750 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001751 placeholder_values = {
1752 'ro.boot.product.device_name': 'pro'
1753 }
1754 partition_props = common.PartitionBuildProps.FromInputFile(
1755 input_zip, 'odm', placeholder_values)
1756
1757 self.assertEqual({
1758 'ro.odm.build.date.utc': '1578430045',
1759 'ro.odm.build.fingerprint':
1760 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1761 'ro.product.odm.device': 'coralpro',
1762 'ro.product.odm.name': 'product2',
1763 }, partition_props.build_props)
1764
1765 def test_parseBuildProps_noPlaceHolders(self):
1766 build_prop = copy.copy(self.odm_build_prop)
1767 input_file = self._BuildZipFile({
1768 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001769 })
1770
Kelvin Zhang928c2342020-09-22 16:15:57 -04001771 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001772 partition_props = common.PartitionBuildProps.FromInputFile(
1773 input_zip, 'odm')
1774
1775 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001776 'ro.odm.build.date.utc': '1578430045',
1777 'ro.odm.build.fingerprint':
1778 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1779 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001780 }, partition_props.build_props)
1781
Tianjie Xu9afb2212020-05-10 21:48:15 +00001782 self.assertEqual(set(), partition_props.prop_overrides)
1783
1784 def test_parseBuildProps_multipleImportStatements(self):
1785 build_prop = copy.deepcopy(self.odm_build_prop)
1786 build_prop.append(
1787 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
1788
1789 build_std_prop = [
1790 'ro.product.odm.device=coral',
1791 ]
1792 build_pro_prop = [
1793 'ro.product.odm.device=coralpro',
1794 ]
1795
1796 product1_prop = [
1797 'ro.product.odm.name=product1',
1798 'ro.product.not_care=not_care',
1799 ]
1800
1801 product2_prop = [
1802 'ro.product.odm.name=product2',
1803 'ro.product.not_care=not_care',
1804 ]
1805
1806 input_file = self._BuildZipFile({
1807 'ODM/etc/build.prop': '\n'.join(build_prop),
1808 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
1809 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
1810 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
1811 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
1812 })
1813
Kelvin Zhang928c2342020-09-22 16:15:57 -04001814 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001815 placeholder_values = {
1816 'ro.boot.product.device_name': 'std',
1817 'ro.boot.product.product_name': 'product1',
1818 'ro.boot.product.not_care': 'not_care',
1819 }
1820 partition_props = common.PartitionBuildProps.FromInputFile(
1821 input_zip, 'odm', placeholder_values)
1822
1823 self.assertEqual({
1824 'ro.odm.build.date.utc': '1578430045',
1825 'ro.odm.build.fingerprint':
1826 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1827 'ro.product.odm.device': 'coral',
1828 'ro.product.odm.name': 'product1'
1829 }, partition_props.build_props)
1830
Kelvin Zhang928c2342020-09-22 16:15:57 -04001831 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001832 placeholder_values = {
1833 'ro.boot.product.device_name': 'pro',
1834 'ro.boot.product.product_name': 'product2',
1835 'ro.boot.product.not_care': 'not_care',
1836 }
1837 partition_props = common.PartitionBuildProps.FromInputFile(
1838 input_zip, 'odm', placeholder_values)
1839
1840 self.assertEqual({
1841 'ro.odm.build.date.utc': '1578430045',
1842 'ro.odm.build.fingerprint':
1843 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1844 'ro.product.odm.device': 'coralpro',
1845 'ro.product.odm.name': 'product2'
1846 }, partition_props.build_props)
1847
1848 def test_parseBuildProps_defineAfterOverride(self):
1849 build_prop = copy.deepcopy(self.odm_build_prop)
1850 build_prop.append('ro.product.odm.device=coral')
1851
1852 build_std_prop = [
1853 'ro.product.odm.device=coral',
1854 ]
1855 build_pro_prop = [
1856 'ro.product.odm.device=coralpro',
1857 ]
1858
1859 input_file = self._BuildZipFile({
1860 'ODM/etc/build.prop': '\n'.join(build_prop),
1861 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
1862 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
1863 })
1864
Kelvin Zhang928c2342020-09-22 16:15:57 -04001865 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001866 placeholder_values = {
1867 'ro.boot.product.device_name': 'std',
1868 }
1869
1870 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
1871 input_zip, 'odm', placeholder_values)
1872
1873 def test_parseBuildProps_duplicateOverride(self):
1874 build_prop = copy.deepcopy(self.odm_build_prop)
1875 build_prop.append(
1876 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
1877
1878 build_std_prop = [
1879 'ro.product.odm.device=coral',
1880 'ro.product.odm.name=product1',
1881 ]
1882 build_pro_prop = [
1883 'ro.product.odm.device=coralpro',
1884 ]
1885
1886 product1_prop = [
1887 'ro.product.odm.name=product1',
1888 ]
1889
1890 product2_prop = [
1891 'ro.product.odm.name=product2',
1892 ]
1893
1894 input_file = self._BuildZipFile({
1895 'ODM/etc/build.prop': '\n'.join(build_prop),
1896 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
1897 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
1898 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
1899 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
1900 })
1901
Kelvin Zhang928c2342020-09-22 16:15:57 -04001902 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001903 placeholder_values = {
1904 'ro.boot.product.device_name': 'std',
1905 'ro.boot.product.product_name': 'product1',
1906 }
1907 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
1908 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08001909
1910 def test_partitionBuildProps_fromInputFile_deepcopy(self):
1911 build_prop = [
1912 'ro.odm.build.date.utc=1578430045',
1913 'ro.odm.build.fingerprint='
1914 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1915 'ro.product.odm.device=coral',
1916 ]
1917 input_file = self._BuildZipFile({
1918 'ODM/etc/build.prop': '\n'.join(build_prop),
1919 })
1920
1921 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
1922 placeholder_values = {
1923 'ro.boot.product.device_name': ['std', 'pro']
1924 }
1925 partition_props = common.PartitionBuildProps.FromInputFile(
1926 input_zip, 'odm', placeholder_values)
1927
1928 copied_props = copy.deepcopy(partition_props)
1929 self.assertEqual({
Kelvin Zhang8aa65252023-08-30 18:29:59 -07001930 'ro.odm.build.date.utc': '1578430045',
1931 'ro.odm.build.fingerprint':
1932 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1933 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08001934 }, copied_props.build_props)