blob: 2dfd8c714ab9d1a39c5f92e26c175b459c510b78 [file] [log] [blame]
Dan Albert8e0178d2015-01-27 15:53:15 -08001#
2# Copyright (C) 2015 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
Tao Baofc7e0e02018-02-13 13:54:02 -080016
Tao Baoa57ab9f2018-08-24 12:08:38 -070017import copy
Daniel Normand3351562020-10-29 12:33:11 -070018import json
Dan Albert8e0178d2015-01-27 15:53:15 -080019import os
Tao Bao17e4e612018-02-16 17:12:54 -080020import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080021import tempfile
22import time
Tianjie20dd8f22020-04-19 15:51:16 -070023import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080024import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080025from hashlib import sha1
26
Dan Albert8e0178d2015-01-27 15:53:15 -080027import common
Tao Bao04e1f012018-02-04 12:13:35 -080028import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070029import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070030from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080031from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080032
Tao Bao04e1f012018-02-04 12:13:35 -080033
Tao Bao31b08072017-11-08 15:50:59 -080034KiB = 1024
35MiB = 1024 * KiB
36GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080037
Tao Bao1c830bf2017-12-25 10:43:47 -080038
Tao Baof3282b42015-04-01 11:21:55 -070039def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080040 size = int(2 * GiB + 1)
41 block_size = 4 * KiB
42 step_size = 4 * MiB
43 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
44 for _ in range(0, size, step_size):
45 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070046 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070047
Dan Albert8e0178d2015-01-27 15:53:15 -080048
Tao Bao1c320f82019-10-04 23:25:12 -070049class BuildInfoTest(test_utils.ReleaseToolsTestCase):
50
Tianjiefdda51d2021-05-05 14:46:35 -070051 TEST_INFO_FINGERPRINT_DICT = {
52 'build.prop': common.PartitionBuildProps.FromDictionary(
53 'system', {
54 'ro.product.brand': 'product-brand',
55 'ro.product.name': 'product-name',
56 'ro.product.device': 'product-device',
57 'ro.build.version.release': 'version-release',
58 'ro.build.id': 'build-id',
59 'ro.build.version.incremental': 'version-incremental',
60 'ro.build.type': 'build-type',
61 'ro.build.tags': 'build-tags',
62 'ro.build.version.sdk': 30,
63 }
64 ),
65 }
66
Tao Bao1c320f82019-10-04 23:25:12 -070067 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000068 'build.prop': common.PartitionBuildProps.FromDictionary(
69 'system', {
70 'ro.product.device': 'product-device',
71 'ro.product.name': 'product-name',
72 'ro.build.fingerprint': 'build-fingerprint',
73 'ro.build.foo': 'build-foo'}
74 ),
75 'system.build.prop': common.PartitionBuildProps.FromDictionary(
76 'system', {
77 'ro.product.system.brand': 'product-brand',
78 'ro.product.system.name': 'product-name',
79 'ro.product.system.device': 'product-device',
80 'ro.system.build.version.release': 'version-release',
81 'ro.system.build.id': 'build-id',
82 'ro.system.build.version.incremental': 'version-incremental',
83 'ro.system.build.type': 'build-type',
84 'ro.system.build.tags': 'build-tags',
85 'ro.system.build.foo': 'build-foo'}
86 ),
87 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
88 'vendor', {
89 'ro.product.vendor.brand': 'vendor-product-brand',
90 'ro.product.vendor.name': 'vendor-product-name',
91 'ro.product.vendor.device': 'vendor-product-device',
92 'ro.vendor.build.version.release': 'vendor-version-release',
93 'ro.vendor.build.id': 'vendor-build-id',
94 'ro.vendor.build.version.incremental':
95 'vendor-version-incremental',
96 'ro.vendor.build.type': 'vendor-build-type',
97 'ro.vendor.build.tags': 'vendor-build-tags'}
98 ),
99 'property1': 'value1',
100 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -0700101 }
102
103 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000104 'build.prop': common.PartitionBuildProps.FromDictionary(
105 'system', {
106 'ro.product.name': 'product-name',
107 'ro.build.thumbprint': 'build-thumbprint',
108 'ro.build.bar': 'build-bar'}
109 ),
110 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
111 'vendor', {
112 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
113 ),
114 'property1': 'value1',
115 'property2': 4096,
116 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700117 }
118
119 TEST_OEM_DICTS = [
120 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000121 'ro.product.brand': 'brand1',
122 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700123 },
124 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000125 'ro.product.brand': 'brand2',
126 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700127 },
128 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000129 'ro.product.brand': 'brand3',
130 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700131 },
132 ]
133
Steven Laver8e2086e2020-04-27 16:26:31 -0700134 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000135 'build.prop': common.PartitionBuildProps.FromDictionary(
136 'system', {
137 'ro.build.fingerprint': 'build-fingerprint',
138 'ro.product.property_source_order':
139 'product,odm,vendor,system_ext,system'}
140 ),
141 'system.build.prop': common.PartitionBuildProps.FromDictionary(
142 'system', {
143 'ro.product.system.device': 'system-product-device'}
144 ),
145 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
146 'vendor', {
147 'ro.product.vendor.device': 'vendor-product-device'}
148 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700149 }
150
151 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000152 'build.prop': common.PartitionBuildProps.FromDictionary(
153 'system', {
154 'ro.build.fingerprint': 'build-fingerprint',
155 'ro.product.property_source_order':
156 'product,product_services,odm,vendor,system',
157 'ro.build.version.release': '10',
158 'ro.build.version.codename': 'REL'}
159 ),
160 'system.build.prop': common.PartitionBuildProps.FromDictionary(
161 'system', {
162 'ro.product.system.device': 'system-product-device'}
163 ),
164 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
165 'vendor', {
166 'ro.product.vendor.device': 'vendor-product-device'}
167 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700168 }
169
170 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000171 'build.prop': common.PartitionBuildProps.FromDictionary(
172 'system', {
173 'ro.product.device': 'product-device',
174 'ro.build.fingerprint': 'build-fingerprint',
175 'ro.build.version.release': '9',
176 'ro.build.version.codename': 'REL'}
177 ),
178 'system.build.prop': common.PartitionBuildProps.FromDictionary(
179 'system', {
180 'ro.product.system.device': 'system-product-device'}
181 ),
182 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
183 'vendor', {
184 'ro.product.vendor.device': 'vendor-product-device'}
185 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700186 }
187
Tao Bao1c320f82019-10-04 23:25:12 -0700188 def test_init(self):
189 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
190 self.assertEqual('product-device', target_info.device)
191 self.assertEqual('build-fingerprint', target_info.fingerprint)
192 self.assertFalse(target_info.is_ab)
193 self.assertIsNone(target_info.oem_props)
194
195 def test_init_with_oem_props(self):
196 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
197 self.TEST_OEM_DICTS)
198 self.assertEqual('device1', target_info.device)
199 self.assertEqual('brand1/product-name/device1:build-thumbprint',
200 target_info.fingerprint)
201
202 # Swap the order in oem_dicts, which would lead to different BuildInfo.
203 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
204 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
205 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
206 oem_dicts)
207 self.assertEqual('device3', target_info.device)
208 self.assertEqual('brand3/product-name/device3:build-thumbprint',
209 target_info.fingerprint)
210
Tao Bao1c320f82019-10-04 23:25:12 -0700211 def test_init_badFingerprint(self):
212 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000213 info_dict['build.prop'].build_props[
214 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700215 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
216
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000217 info_dict['build.prop'].build_props[
218 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700219 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
220
Tianjiefdda51d2021-05-05 14:46:35 -0700221 def test_init_goodFingerprint(self):
222 info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
223 build_info = common.BuildInfo(info_dict)
224 self.assertEqual(
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000225 'product-brand/product-name/product-device:version-release/build-id/'
226 'version-incremental:build-type/build-tags', build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700227
228 build_props = info_dict['build.prop'].build_props
229 del build_props['ro.build.id']
230 build_props['ro.build.legacy.id'] = 'legacy-build-id'
231 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
232 self.assertEqual(
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000233 'product-brand/product-name/product-device:version-release/'
234 'legacy-build-id/version-incremental:build-type/build-tags',
235 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700236
237 self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
238 False)
239
240 info_dict['avb_enable'] = 'true'
241 info_dict['vbmeta_digest'] = 'abcde12345'
242 build_info = common.BuildInfo(info_dict, use_legacy_id=False)
243 self.assertEqual(
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000244 'product-brand/product-name/product-device:version-release/'
245 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
246 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700247
Tao Bao1c320f82019-10-04 23:25:12 -0700248 def test___getitem__(self):
249 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
250 self.assertEqual('value1', target_info['property1'])
251 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000252 self.assertEqual('build-foo',
253 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700254
255 def test___getitem__with_oem_props(self):
256 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
257 self.TEST_OEM_DICTS)
258 self.assertEqual('value1', target_info['property1'])
259 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000260 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700261
262 def test___setitem__(self):
263 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
264 self.assertEqual('value1', target_info['property1'])
265 target_info['property1'] = 'value2'
266 self.assertEqual('value2', target_info['property1'])
267
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000268 self.assertEqual('build-foo',
269 target_info['build.prop'].GetProp('ro.build.foo'))
270 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
271 self.assertEqual('build-bar',
272 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700273
274 def test_get(self):
275 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
276 self.assertEqual('value1', target_info.get('property1'))
277 self.assertEqual(4096, target_info.get('property2'))
278 self.assertEqual(4096, target_info.get('property2', 1024))
279 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000280 self.assertEqual('build-foo',
281 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700282
283 def test_get_with_oem_props(self):
284 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
285 self.TEST_OEM_DICTS)
286 self.assertEqual('value1', target_info.get('property1'))
287 self.assertEqual(4096, target_info.get('property2'))
288 self.assertEqual(4096, target_info.get('property2', 1024))
289 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000290 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700291
292 def test_items(self):
293 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
294 items = target_info.items()
295 self.assertIn(('property1', 'value1'), items)
296 self.assertIn(('property2', 4096), items)
297
298 def test_GetBuildProp(self):
299 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
300 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
301 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
302 'ro.build.nonexistent')
303
304 def test_GetBuildProp_with_oem_props(self):
305 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
306 self.TEST_OEM_DICTS)
307 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
308 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
309 'ro.build.nonexistent')
310
Daniel Normand5fe8622020-01-08 17:01:11 -0800311 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700312 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800313 self.assertEqual(
314 target_info.GetPartitionFingerprint('vendor'),
315 'vendor-product-brand/vendor-product-name/vendor-product-device'
316 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
317 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700318
Daniel Normand5fe8622020-01-08 17:01:11 -0800319 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700320 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800321 self.assertEqual(
322 target_info.GetPartitionFingerprint('system_other'),
323 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700324
Daniel Normand5fe8622020-01-08 17:01:11 -0800325 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
326 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000327 info_dict['vendor.build.prop'].build_props[
328 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800329 target_info = common.BuildInfo(info_dict, None)
330 self.assertEqual(
331 target_info.GetPartitionFingerprint('vendor'),
332 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700333
334 def test_WriteMountOemScript(self):
335 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
336 self.TEST_OEM_DICTS)
337 script_writer = test_utils.MockScriptWriter()
338 target_info.WriteMountOemScript(script_writer)
339 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
340
341 def test_WriteDeviceAssertions(self):
342 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
343 script_writer = test_utils.MockScriptWriter()
344 target_info.WriteDeviceAssertions(script_writer, False)
345 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
346
347 def test_WriteDeviceAssertions_with_oem_props(self):
348 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
349 self.TEST_OEM_DICTS)
350 script_writer = test_utils.MockScriptWriter()
351 target_info.WriteDeviceAssertions(script_writer, False)
352 self.assertEqual(
353 [
354 ('AssertOemProperty', 'ro.product.device',
355 ['device1', 'device2', 'device3'], False),
356 ('AssertOemProperty', 'ro.product.brand',
357 ['brand1', 'brand2', 'brand3'], False),
358 ],
359 script_writer.lines)
360
Steven Laver8e2086e2020-04-27 16:26:31 -0700361 def test_ResolveRoProductProperty_FromVendor(self):
362 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
363 info = common.BuildInfo(info_dict, None)
364 self.assertEqual('vendor-product-device',
365 info.GetBuildProp('ro.product.device'))
366
367 def test_ResolveRoProductProperty_FromSystem(self):
368 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000369 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700370 info = common.BuildInfo(info_dict, None)
371 self.assertEqual('system-product-device',
372 info.GetBuildProp('ro.product.device'))
373
374 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
375 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000376 info_dict['build.prop'].build_props[
377 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700378 with self.assertRaisesRegexp(common.ExternalError,
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000379 'Invalid ro.product.property_source_order'):
Steven Laver8e2086e2020-04-27 16:26:31 -0700380 info = common.BuildInfo(info_dict, None)
381 info.GetBuildProp('ro.product.device')
382
383 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
384 info_dict = copy.deepcopy(
385 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
386 info = common.BuildInfo(info_dict, None)
387 self.assertEqual('vendor-product-device',
388 info.GetBuildProp('ro.product.device'))
389
390 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
391 info_dict = copy.deepcopy(
392 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
393 info = common.BuildInfo(info_dict, None)
394 self.assertEqual('product-device',
395 info.GetBuildProp('ro.product.device'))
396
Tao Bao1c320f82019-10-04 23:25:12 -0700397
Tao Bao65b94e92018-10-11 21:57:26 -0700398class CommonZipTest(test_utils.ReleaseToolsTestCase):
399
Tao Bao31b08072017-11-08 15:50:59 -0800400 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700401 test_file_name=None, expected_stat=None, expected_mode=0o644,
402 expected_compress_type=zipfile.ZIP_STORED):
403 # Verify the stat if present.
404 if test_file_name is not None:
405 new_stat = os.stat(test_file_name)
406 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
407 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
408
409 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400410 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700411
412 # Verify the timestamp.
413 info = zip_file.getinfo(arcname)
414 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
415
416 # Verify the file mode.
417 mode = (info.external_attr >> 16) & 0o777
418 self.assertEqual(mode, expected_mode)
419
420 # Verify the compress type.
421 self.assertEqual(info.compress_type, expected_compress_type)
422
423 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800424 entry = zip_file.open(arcname)
425 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700426 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800427 sha1_hash.update(chunk)
428 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700429 self.assertIsNone(zip_file.testzip())
430
Dan Albert8e0178d2015-01-27 15:53:15 -0800431 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
432 extra_zipwrite_args = dict(extra_zipwrite_args or {})
433
434 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800435 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700436
437 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800438 zip_file_name = zip_file.name
439
440 # File names within an archive strip the leading slash.
441 arcname = extra_zipwrite_args.get("arcname", test_file_name)
442 if arcname[0] == "/":
443 arcname = arcname[1:]
444
445 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400446 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800447
448 try:
Tao Bao31b08072017-11-08 15:50:59 -0800449 sha1_hash = sha1()
450 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700451 sha1_hash.update(bytes(data))
452 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800453 test_file.close()
454
Dan Albert8e0178d2015-01-27 15:53:15 -0800455 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700456 expected_compress_type = extra_zipwrite_args.get("compress_type",
457 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800458
Kelvin Zhangea84d422023-04-11 12:50:34 -0700459 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
460 # the timestamp after writing.
461 os.utime(test_file_name, (1234567, 1234567))
462 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800463 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000464 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800465
Tao Bao31b08072017-11-08 15:50:59 -0800466 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
467 test_file_name, expected_stat, expected_mode,
468 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800469 finally:
470 os.remove(test_file_name)
471 os.remove(zip_file_name)
472
Tao Baof3282b42015-04-01 11:21:55 -0700473 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
474 extra_args = dict(extra_args or {})
475
476 zip_file = tempfile.NamedTemporaryFile(delete=False)
477 zip_file_name = zip_file.name
478 zip_file.close()
479
Kelvin Zhang928c2342020-09-22 16:15:57 -0400480 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700481
482 try:
483 expected_compress_type = extra_args.get("compress_type",
484 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700485 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700486 arcname = zinfo_or_arcname
487 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700488 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700489 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700490 if zinfo_or_arcname.external_attr:
491 zinfo_perms = zinfo_or_arcname.external_attr >> 16
492 else:
493 zinfo_perms = 0o600
494 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700495
Tao Bao58c1b962015-05-20 09:32:18 -0700496 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000497 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700498
Tao Bao31b08072017-11-08 15:50:59 -0800499 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700500 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700501 expected_compress_type=expected_compress_type)
502 finally:
503 os.remove(zip_file_name)
504
505 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
506 extra_args = dict(extra_args or {})
507
508 zip_file = tempfile.NamedTemporaryFile(delete=False)
509 zip_file_name = zip_file.name
510
511 test_file = tempfile.NamedTemporaryFile(delete=False)
512 test_file_name = test_file.name
513
514 arcname_large = test_file_name
515 arcname_small = "bar"
516
517 # File names within an archive strip the leading slash.
518 if arcname_large[0] == "/":
519 arcname_large = arcname_large[1:]
520
521 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400522 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700523
524 try:
Tao Bao31b08072017-11-08 15:50:59 -0800525 sha1_hash = sha1()
526 for data in large:
527 sha1_hash.update(data)
528 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700529 test_file.close()
530
Kelvin Zhangea84d422023-04-11 12:50:34 -0700531 # Arbitrary timestamp, just to make sure common.ZipWrite() restores
532 # the timestamp after writing.
533 os.utime(test_file_name, (1234567, 1234567))
Tao Baof3282b42015-04-01 11:21:55 -0700534 expected_stat = os.stat(test_file_name)
535 expected_mode = 0o644
536 expected_compress_type = extra_args.get("compress_type",
537 zipfile.ZIP_STORED)
Tao Baof3282b42015-04-01 11:21:55 -0700538
539 common.ZipWrite(zip_file, test_file_name, **extra_args)
540 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000541 common.ZipClose(zip_file)
Tao Baof3282b42015-04-01 11:21:55 -0700542
543 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800544 self._verify(zip_file, zip_file_name, arcname_large,
545 sha1_hash.hexdigest(), test_file_name, expected_stat,
546 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700547
548 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800549 self._verify(zip_file, zip_file_name, arcname_small,
550 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700551 expected_compress_type=expected_compress_type)
552 finally:
553 os.remove(zip_file_name)
554 os.remove(test_file_name)
555
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000556 def _test_reset_ZIP64_LIMIT(self, func, *args):
557 default_limit = (1 << 31) - 1
558 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
559 func(*args)
560 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
561
Dan Albert8e0178d2015-01-27 15:53:15 -0800562 def test_ZipWrite(self):
563 file_contents = os.urandom(1024)
564 self._test_ZipWrite(file_contents)
565
566 def test_ZipWrite_with_opts(self):
567 file_contents = os.urandom(1024)
568 self._test_ZipWrite(file_contents, {
569 "arcname": "foobar",
570 "perms": 0o777,
571 "compress_type": zipfile.ZIP_DEFLATED,
572 })
Tao Baof3282b42015-04-01 11:21:55 -0700573 self._test_ZipWrite(file_contents, {
574 "arcname": "foobar",
575 "perms": 0o700,
576 "compress_type": zipfile.ZIP_STORED,
577 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800578
579 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700580 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800581 self._test_ZipWrite(file_contents, {
582 "compress_type": zipfile.ZIP_DEFLATED,
583 })
584
585 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000586 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
Tao Baof3282b42015-04-01 11:21:55 -0700587
588 def test_ZipWriteStr(self):
589 random_string = os.urandom(1024)
590 # Passing arcname
591 self._test_ZipWriteStr("foo", random_string)
592
593 # Passing zinfo
594 zinfo = zipfile.ZipInfo(filename="foo")
595 self._test_ZipWriteStr(zinfo, random_string)
596
597 # Timestamp in the zinfo should be overwritten.
598 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
599 self._test_ZipWriteStr(zinfo, random_string)
600
601 def test_ZipWriteStr_with_opts(self):
602 random_string = os.urandom(1024)
603 # Passing arcname
604 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700605 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700606 "compress_type": zipfile.ZIP_DEFLATED,
607 })
Tao Bao58c1b962015-05-20 09:32:18 -0700608 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700609 "compress_type": zipfile.ZIP_STORED,
610 })
611
612 # Passing zinfo
613 zinfo = zipfile.ZipInfo(filename="foo")
614 self._test_ZipWriteStr(zinfo, random_string, {
615 "compress_type": zipfile.ZIP_DEFLATED,
616 })
617 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700618 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700619 "compress_type": zipfile.ZIP_STORED,
620 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700621 self._test_ZipWriteStr(zinfo, random_string, {
622 "perms": 0o000,
623 "compress_type": zipfile.ZIP_STORED,
624 })
Tao Baof3282b42015-04-01 11:21:55 -0700625
626 def test_ZipWriteStr_large_file(self):
627 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
628 # the workaround. We will only test the case of writing a string into a
629 # large archive.
630 long_string = get_2gb_string()
631 short_string = os.urandom(1024)
632 self._test_ZipWriteStr_large_file(long_string, short_string, {
633 "compress_type": zipfile.ZIP_DEFLATED,
634 })
635
636 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000637 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700638 zinfo = zipfile.ZipInfo(filename="foo")
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000639 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700640
641 def test_bug21309935(self):
642 zip_file = tempfile.NamedTemporaryFile(delete=False)
643 zip_file_name = zip_file.name
644 zip_file.close()
645
646 try:
647 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400648 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700649 # Default perms should be 0o644 when passing the filename.
650 common.ZipWriteStr(zip_file, "foo", random_string)
651 # Honor the specified perms.
652 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
653 # The perms in zinfo should be untouched.
654 zinfo = zipfile.ZipInfo(filename="baz")
655 zinfo.external_attr = 0o740 << 16
656 common.ZipWriteStr(zip_file, zinfo, random_string)
657 # Explicitly specified perms has the priority.
658 zinfo = zipfile.ZipInfo(filename="qux")
659 zinfo.external_attr = 0o700 << 16
660 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000661 common.ZipClose(zip_file)
Tao Bao58c1b962015-05-20 09:32:18 -0700662
Tao Bao31b08072017-11-08 15:50:59 -0800663 self._verify(zip_file, zip_file_name, "foo",
664 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700665 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800666 self._verify(zip_file, zip_file_name, "bar",
667 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700668 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800669 self._verify(zip_file, zip_file_name, "baz",
670 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700671 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800672 self._verify(zip_file, zip_file_name, "qux",
673 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700674 expected_mode=0o400)
675 finally:
676 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700677
Tao Bao82490d32019-04-09 00:12:30 -0700678 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800679 def test_ZipDelete(self):
680 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
681 output_zip = zipfile.ZipFile(zip_file.name, 'w',
682 compression=zipfile.ZIP_DEFLATED)
683 with tempfile.NamedTemporaryFile() as entry_file:
684 entry_file.write(os.urandom(1024))
685 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
686 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
687 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000688 common.ZipClose(output_zip)
Tao Bao89d7ab22017-12-14 17:05:33 -0800689 zip_file.close()
690
691 try:
692 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400693 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800694 entries = check_zip.namelist()
695 self.assertTrue('Test1' in entries)
696 self.assertFalse('Test2' in entries)
697 self.assertTrue('Test3' in entries)
698
Tao Bao986ee862018-10-04 15:46:16 -0700699 self.assertRaises(
700 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400701 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800702 entries = check_zip.namelist()
703 self.assertTrue('Test1' in entries)
704 self.assertFalse('Test2' in entries)
705 self.assertTrue('Test3' in entries)
706
707 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400708 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800709 entries = check_zip.namelist()
710 self.assertTrue('Test1' in entries)
711 self.assertFalse('Test2' in entries)
712 self.assertFalse('Test3' in entries)
713
714 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400715 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800716 entries = check_zip.namelist()
717 self.assertFalse('Test1' in entries)
718 self.assertFalse('Test2' in entries)
719 self.assertFalse('Test3' in entries)
720 finally:
721 os.remove(zip_file.name)
722
Tao Bao0ff15de2019-03-20 11:26:06 -0700723 @staticmethod
724 def _test_UnzipTemp_createZipFile():
725 zip_file = common.MakeTempFile(suffix='.zip')
726 output_zip = zipfile.ZipFile(
727 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
728 contents = os.urandom(1024)
729 with tempfile.NamedTemporaryFile() as entry_file:
730 entry_file.write(contents)
731 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
732 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
733 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
734 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
735 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000736 common.ZipClose(output_zip)
737 common.ZipClose(output_zip)
Tao Bao0ff15de2019-03-20 11:26:06 -0700738 return zip_file
739
Tao Bao82490d32019-04-09 00:12:30 -0700740 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700741 def test_UnzipTemp(self):
742 zip_file = self._test_UnzipTemp_createZipFile()
743 unzipped_dir = common.UnzipTemp(zip_file)
744 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
745 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
746 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
747 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
748 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
749
Tao Bao82490d32019-04-09 00:12:30 -0700750 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700751 def test_UnzipTemp_withPatterns(self):
752 zip_file = self._test_UnzipTemp_createZipFile()
753
754 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
755 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
756 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
757 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
758 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
759 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
760
761 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
762 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
763 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
764 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
765 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
766 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
767
768 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
769 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
770 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
771 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
772 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
773 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
774
775 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
776 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
777 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
778 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
779 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
780 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
781
782 def test_UnzipTemp_withEmptyPatterns(self):
783 zip_file = self._test_UnzipTemp_createZipFile()
784 unzipped_dir = common.UnzipTemp(zip_file, [])
785 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
786 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
787 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
788 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
789 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
790
Tao Bao82490d32019-04-09 00:12:30 -0700791 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700792 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
793 zip_file = self._test_UnzipTemp_createZipFile()
794 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
795 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
796 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
797 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
798 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
799 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
800
801 def test_UnzipTemp_withNoMatchingPatterns(self):
802 zip_file = self._test_UnzipTemp_createZipFile()
803 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
804 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
805 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
806 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
807 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
808 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
809
Tao Bao89d7ab22017-12-14 17:05:33 -0800810
Tao Bao65b94e92018-10-11 21:57:26 -0700811class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800812 """Tests the APK utils related functions."""
813
814 APKCERTS_TXT1 = (
815 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
816 ' private_key="certs/devkey.pk8"\n'
817 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700818 ' certificate="build/make/target/product/security/platform.x509.pem"'
819 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800820 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
821 )
822
823 APKCERTS_CERTMAP1 = {
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000824 'RecoveryLocalizer.apk' : 'certs/devkey',
825 'Settings.apk' : 'build/make/target/product/security/platform',
826 'TV.apk' : 'PRESIGNED',
Tao Bao818ddf52018-01-05 11:17:34 -0800827 }
828
829 APKCERTS_TXT2 = (
830 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
831 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
832 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
833 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
834 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
835 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
836 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
837 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
838 )
839
840 APKCERTS_CERTMAP2 = {
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000841 'Compressed1.apk' : 'certs/compressed1',
842 'Compressed2a.apk' : 'certs/compressed2',
843 'Compressed2b.apk' : 'certs/compressed2',
844 'Compressed3.apk' : 'certs/compressed3',
Tao Bao818ddf52018-01-05 11:17:34 -0800845 }
846
847 APKCERTS_TXT3 = (
848 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
849 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
850 )
851
852 APKCERTS_CERTMAP3 = {
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000853 'Compressed4.apk' : 'certs/compressed4',
Tao Bao818ddf52018-01-05 11:17:34 -0800854 }
855
Bill Peckham5c7b0342020-04-03 15:36:23 -0700856 # Test parsing with no optional fields, both optional fields, and only the
857 # partition optional field.
858 APKCERTS_TXT4 = (
859 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
860 ' private_key="certs/devkey.pk8"\n'
861 'name="Settings.apk"'
862 ' certificate="build/make/target/product/security/platform.x509.pem"'
863 ' private_key="build/make/target/product/security/platform.pk8"'
864 ' compressed="gz" partition="system"\n'
865 'name="TV.apk" certificate="PRESIGNED" private_key=""'
866 ' partition="product"\n'
867 )
868
869 APKCERTS_CERTMAP4 = {
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000870 'RecoveryLocalizer.apk' : 'certs/devkey',
871 'Settings.apk' : 'build/make/target/product/security/platform',
872 'TV.apk' : 'PRESIGNED',
Bill Peckham5c7b0342020-04-03 15:36:23 -0700873 }
874
Tao Bao17e4e612018-02-16 17:12:54 -0800875 def setUp(self):
876 self.testdata_dir = test_utils.get_testdata_dir()
877
Tao Bao818ddf52018-01-05 11:17:34 -0800878 @staticmethod
879 def _write_apkcerts_txt(apkcerts_txt, additional=None):
880 if additional is None:
881 additional = []
882 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400883 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800884 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
885 for entry in additional:
886 target_files_zip.writestr(entry, '')
887 return target_files
888
889 def test_ReadApkCerts_NoncompressedApks(self):
890 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400891 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800892 certmap, ext = common.ReadApkCerts(input_zip)
893
894 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
895 self.assertIsNone(ext)
896
897 def test_ReadApkCerts_CompressedApks(self):
898 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
899 # not stored in '.gz' format, so it shouldn't be considered as installed.
900 target_files = self._write_apkcerts_txt(
901 self.APKCERTS_TXT2,
902 ['Compressed1.apk.gz', 'Compressed3.apk'])
903
Kelvin Zhang928c2342020-09-22 16:15:57 -0400904 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800905 certmap, ext = common.ReadApkCerts(input_zip)
906
907 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
908 self.assertEqual('.gz', ext)
909
910 # Alternative case with '.xz'.
911 target_files = self._write_apkcerts_txt(
912 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
913
Kelvin Zhang928c2342020-09-22 16:15:57 -0400914 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800915 certmap, ext = common.ReadApkCerts(input_zip)
916
917 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
918 self.assertEqual('.xz', ext)
919
920 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
921 target_files = self._write_apkcerts_txt(
922 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
923 ['Compressed1.apk.gz', 'Compressed3.apk'])
924
Kelvin Zhang928c2342020-09-22 16:15:57 -0400925 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800926 certmap, ext = common.ReadApkCerts(input_zip)
927
928 certmap_merged = self.APKCERTS_CERTMAP1.copy()
929 certmap_merged.update(self.APKCERTS_CERTMAP2)
930 self.assertDictEqual(certmap_merged, certmap)
931 self.assertEqual('.gz', ext)
932
933 def test_ReadApkCerts_MultipleCompressionMethods(self):
934 target_files = self._write_apkcerts_txt(
935 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
936 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
937
Kelvin Zhang928c2342020-09-22 16:15:57 -0400938 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800939 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
940
941 def test_ReadApkCerts_MismatchingKeys(self):
942 malformed_apkcerts_txt = (
943 'name="App1.apk" certificate="certs/cert1.x509.pem"'
944 ' private_key="certs/cert2.pk8"\n'
945 )
946 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
947
Kelvin Zhang928c2342020-09-22 16:15:57 -0400948 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800949 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
950
Bill Peckham5c7b0342020-04-03 15:36:23 -0700951 def test_ReadApkCerts_WithWithoutOptionalFields(self):
952 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400953 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700954 certmap, ext = common.ReadApkCerts(input_zip)
955
956 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
957 self.assertIsNone(ext)
958
Tao Bao04e1f012018-02-04 12:13:35 -0800959 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800960 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
961 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800962 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800963 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
964
965 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800966 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800967 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
968
Tao Bao82490d32019-04-09 00:12:30 -0700969 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700970 def test_ExtractAvbPublicKey(self):
971 privkey = os.path.join(self.testdata_dir, 'testkey.key')
972 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700973 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
974 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
975 with open(extracted_from_privkey, 'rb') as privkey_fp, \
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000976 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700977 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
978
Tao Bao17e4e612018-02-16 17:12:54 -0800979 def test_ParseCertificate(self):
980 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
981
982 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800983 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
984 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800985 expected, _ = proc.communicate()
986 self.assertEqual(0, proc.returncode)
987
988 with open(cert) as cert_fp:
989 actual = common.ParseCertificate(cert_fp.read())
990 self.assertEqual(expected, actual)
991
Tao Bao82490d32019-04-09 00:12:30 -0700992 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700993 def test_GetMinSdkVersion(self):
994 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
995 self.assertEqual('24', common.GetMinSdkVersion(test_app))
996
Tao Bao82490d32019-04-09 00:12:30 -0700997 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700998 def test_GetMinSdkVersion_invalidInput(self):
999 self.assertRaises(
1000 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
1001
Tao Bao82490d32019-04-09 00:12:30 -07001002 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001003 def test_GetMinSdkVersionInt(self):
1004 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
1005 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
1006
Tao Bao82490d32019-04-09 00:12:30 -07001007 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001008 def test_GetMinSdkVersionInt_invalidInput(self):
1009 self.assertRaises(
1010 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1011 {})
1012
Tao Bao818ddf52018-01-05 11:17:34 -08001013
Tao Bao65b94e92018-10-11 21:57:26 -07001014class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001015
Tao Bao02a08592018-07-22 12:40:45 -07001016 def setUp(self):
1017 self.testdata_dir = test_utils.get_testdata_dir()
1018
Tao Bao82490d32019-04-09 00:12:30 -07001019 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001020 def test_GetSparseImage_emptyBlockMapFile(self):
1021 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001022 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001023 target_files_zip.write(
1024 test_utils.construct_sparse_image([
1025 (0xCAC1, 6),
1026 (0xCAC3, 3),
1027 (0xCAC1, 4)]),
1028 arcname='IMAGES/system.img')
1029 target_files_zip.writestr('IMAGES/system.map', '')
1030 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1031 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1032
Tao Baodba59ee2018-01-09 13:21:02 -08001033 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001034 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001035 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001036
1037 self.assertDictEqual(
1038 {
1039 '__COPY': RangeSet("0"),
1040 '__NONZERO-0': RangeSet("1-5 9-12"),
1041 },
1042 sparse_image.file_map)
1043
Daniel Norman21c34f72020-11-11 17:25:50 -08001044 def test_PartitionMapFromTargetFiles(self):
1045 target_files_dir = common.MakeTempDir()
1046 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1047 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1048 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1049 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1050 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1051 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1052 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1053 self.assertDictEqual(
1054 partition_map,
1055 {
1056 'system': 'SYSTEM',
1057 'vendor': 'SYSTEM/vendor',
1058 # Prefer PRODUCT over SYSTEM/product
1059 'product': 'PRODUCT',
1060 'odm': 'SYSTEM/vendor/odm',
1061 'vendor_dlkm': 'VENDOR_DLKM',
1062 # No system_ext or odm_dlkm
1063 })
1064
Daniel Normand3351562020-10-29 12:33:11 -07001065 def test_SharedUidPartitionViolations(self):
1066 uid_dict = {
1067 'android.uid.phone': {
1068 'system': ['system_phone.apk'],
1069 'system_ext': ['system_ext_phone.apk'],
1070 },
1071 'android.uid.wifi': {
1072 'vendor': ['vendor_wifi.apk'],
1073 'odm': ['odm_wifi.apk'],
1074 },
1075 }
1076 errors = common.SharedUidPartitionViolations(
1077 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1078 self.assertEqual(errors, [])
1079
1080 def test_SharedUidPartitionViolations_Violation(self):
1081 uid_dict = {
1082 'android.uid.phone': {
1083 'system': ['system_phone.apk'],
1084 'vendor': ['vendor_phone.apk'],
1085 },
1086 }
1087 errors = common.SharedUidPartitionViolations(
1088 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1089 self.assertIn(
1090 ('APK sharedUserId "android.uid.phone" found across partition groups '
1091 'in partitions "system,vendor"'), errors)
1092
Tao Baob2de7d92019-04-10 10:01:47 -07001093 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001094 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001095 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1096 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001097 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001098 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1099 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001100
Tao Bao82490d32019-04-09 00:12:30 -07001101 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001102 def test_GetSparseImage_missingBlockMapFile(self):
1103 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001104 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001105 target_files_zip.write(
1106 test_utils.construct_sparse_image([
1107 (0xCAC1, 6),
1108 (0xCAC3, 3),
1109 (0xCAC1, 4)]),
1110 arcname='IMAGES/system.img')
1111 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1112 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1113
Tao Baodba59ee2018-01-09 13:21:02 -08001114 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001115 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001116 self.assertRaises(
1117 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1118 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001119
Tao Bao82490d32019-04-09 00:12:30 -07001120 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001121 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1122 """Tests the case of having overlapping blocks but disallowed."""
1123 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001124 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001125 target_files_zip.write(
1126 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1127 arcname='IMAGES/system.img')
1128 # Block 10 is shared between two files.
1129 target_files_zip.writestr(
1130 'IMAGES/system.map',
1131 '\n'.join([
1132 '/system/file1 1-5 9-10',
1133 '/system/file2 10-12']))
1134 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1135 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1136
Tao Baodba59ee2018-01-09 13:21:02 -08001137 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001138 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001139 self.assertRaises(
1140 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1141 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001142
Tao Bao82490d32019-04-09 00:12:30 -07001143 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001144 def test_GetSparseImage_sharedBlocks_allowed(self):
1145 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1146 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001147 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001148 # Construct an image with a care_map of "0-5 9-12".
1149 target_files_zip.write(
1150 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1151 arcname='IMAGES/system.img')
1152 # Block 10 is shared between two files.
1153 target_files_zip.writestr(
1154 'IMAGES/system.map',
1155 '\n'.join([
1156 '/system/file1 1-5 9-10',
1157 '/system/file2 10-12']))
1158 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1159 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1160
Tao Baodba59ee2018-01-09 13:21:02 -08001161 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001162 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001163 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001164
1165 self.assertDictEqual(
1166 {
1167 '__COPY': RangeSet("0"),
1168 '__NONZERO-0': RangeSet("6-8 13-15"),
1169 '/system/file1': RangeSet("1-5 9-10"),
1170 '/system/file2': RangeSet("11-12"),
1171 },
1172 sparse_image.file_map)
1173
1174 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1175 # 'incomplete'.
1176 self.assertTrue(
1177 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1178 self.assertNotIn(
1179 'incomplete', sparse_image.file_map['/system/file2'].extra)
1180
Tao Baoa264fef2019-10-06 21:55:20 -07001181 # '/system/file1' will only contain one field -- a copy of the input text.
1182 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1183
1184 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001185 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1186 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001187
Tao Bao82490d32019-04-09 00:12:30 -07001188 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001189 def test_GetSparseImage_incompleteRanges(self):
1190 """Tests the case of ext4 images with holes."""
1191 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001192 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001193 target_files_zip.write(
1194 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1195 arcname='IMAGES/system.img')
1196 target_files_zip.writestr(
1197 'IMAGES/system.map',
1198 '\n'.join([
1199 '/system/file1 1-5 9-10',
1200 '/system/file2 11-12']))
1201 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1202 # '/system/file2' has less blocks listed (2) than actual (3).
1203 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1204
Tao Baodba59ee2018-01-09 13:21:02 -08001205 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001206 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001207 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001208
Tao Baoa264fef2019-10-06 21:55:20 -07001209 self.assertEqual(
1210 '1-5 9-10',
1211 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001212 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1213
Tao Bao82490d32019-04-09 00:12:30 -07001214 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001215 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1216 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001217 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001218 target_files_zip.write(
1219 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1220 arcname='IMAGES/system.img')
1221 target_files_zip.writestr(
1222 'IMAGES/system.map',
1223 '\n'.join([
1224 '//system/file1 1-5 9-10',
1225 '//system/file2 11-12',
1226 '/system/app/file3 13-15']))
1227 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1228 # '/system/file2' has less blocks listed (2) than actual (3).
1229 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1230 # '/system/app/file3' has less blocks listed (3) than actual (4).
1231 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1232
1233 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001234 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001235 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1236
Tao Baoa264fef2019-10-06 21:55:20 -07001237 self.assertEqual(
1238 '1-5 9-10',
1239 sparse_image.file_map['//system/file1'].extra['text_str'])
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001240 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
Tao Baod3554e62018-07-10 15:31:22 -07001241 self.assertTrue(
1242 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1243
Tao Bao82490d32019-04-09 00:12:30 -07001244 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001245 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1246 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001247 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001248 target_files_zip.write(
1249 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1250 arcname='IMAGES/system.img')
1251 target_files_zip.writestr(
1252 'IMAGES/system.map',
1253 '\n'.join([
1254 '//system/file1 1-5 9-10',
1255 '//init.rc 13-15']))
1256 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1257 # '/init.rc' has less blocks listed (3) than actual (4).
1258 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1259
1260 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001261 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001262 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1263
Tao Baoa264fef2019-10-06 21:55:20 -07001264 self.assertEqual(
1265 '1-5 9-10',
1266 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001267 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1268
Tao Bao82490d32019-04-09 00:12:30 -07001269 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001270 def test_GetSparseImage_fileNotFound(self):
1271 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001272 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001273 target_files_zip.write(
1274 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1275 arcname='IMAGES/system.img')
1276 target_files_zip.writestr(
1277 'IMAGES/system.map',
1278 '\n'.join([
1279 '//system/file1 1-5 9-10',
1280 '//system/file2 11-12']))
1281 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1282
1283 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001284 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001285 self.assertRaises(
1286 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1287 False)
1288
Tao Bao82490d32019-04-09 00:12:30 -07001289 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001290 def test_GetAvbChainedPartitionArg(self):
1291 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1292 info_dict = {
1293 'avb_avbtool': 'avbtool',
1294 'avb_system_key_path': pubkey,
1295 'avb_system_rollback_index_location': 2,
1296 }
1297 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1298 self.assertEqual(3, len(args))
1299 self.assertEqual('system', args[0])
1300 self.assertEqual('2', args[1])
1301 self.assertTrue(os.path.exists(args[2]))
1302
Tao Bao82490d32019-04-09 00:12:30 -07001303 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001304 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1305 key = os.path.join(self.testdata_dir, 'testkey.key')
1306 info_dict = {
1307 'avb_avbtool': 'avbtool',
1308 'avb_product_key_path': key,
1309 'avb_product_rollback_index_location': 2,
1310 }
1311 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1312 self.assertEqual(3, len(args))
1313 self.assertEqual('product', args[0])
1314 self.assertEqual('2', args[1])
1315 self.assertTrue(os.path.exists(args[2]))
1316
Tao Bao82490d32019-04-09 00:12:30 -07001317 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001318 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1319 info_dict = {
1320 'avb_avbtool': 'avbtool',
1321 'avb_system_key_path': 'does-not-exist',
1322 'avb_system_rollback_index_location': 2,
1323 }
1324 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1325 args = common.GetAvbChainedPartitionArg(
1326 'system', info_dict, pubkey).split(':')
1327 self.assertEqual(3, len(args))
1328 self.assertEqual('system', args[0])
1329 self.assertEqual('2', args[1])
1330 self.assertTrue(os.path.exists(args[2]))
1331
Tao Bao82490d32019-04-09 00:12:30 -07001332 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001333 def test_GetAvbChainedPartitionArg_invalidKey(self):
1334 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1335 info_dict = {
1336 'avb_avbtool': 'avbtool',
1337 'avb_system_key_path': pubkey,
1338 'avb_system_rollback_index_location': 2,
1339 }
1340 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001341 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1342 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001343
Tao Baoa57ab9f2018-08-24 12:08:38 -07001344 INFO_DICT_DEFAULT = {
1345 'recovery_api_version': 3,
1346 'fstab_version': 2,
1347 'system_root_image': 'true',
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001348 'no_recovery' : 'true',
Tao Baoa57ab9f2018-08-24 12:08:38 -07001349 'recovery_as_boot': 'true',
1350 }
1351
Daniel Norman4cc9df62019-07-18 10:11:07 -07001352 def test_LoadListFromFile(self):
1353 file_path = os.path.join(self.testdata_dir,
1354 'merge_config_framework_item_list')
1355 contents = common.LoadListFromFile(file_path)
1356 expected_contents = [
1357 'META/apkcerts.txt',
1358 'META/filesystem_config.txt',
1359 'META/root_filesystem_config.txt',
1360 'META/system_manifest.xml',
1361 'META/system_matrix.xml',
1362 'META/update_engine_config.txt',
1363 'PRODUCT/*',
1364 'ROOT/*',
1365 'SYSTEM/*',
1366 ]
1367 self.assertEqual(sorted(contents), sorted(expected_contents))
1368
Tao Baoa57ab9f2018-08-24 12:08:38 -07001369 @staticmethod
1370 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1371 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001372 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001373 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001374 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001375 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1376
1377 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1378 if info_dict.get('system_root_image') == 'true':
1379 fstab_values = FSTAB_TEMPLATE.format('/')
1380 else:
1381 fstab_values = FSTAB_TEMPLATE.format('/system')
1382 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001383
1384 common.ZipWriteStr(
1385 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001386 return target_files
1387
1388 def test_LoadInfoDict(self):
1389 target_files = self._test_LoadInfoDict_createTargetFiles(
1390 self.INFO_DICT_DEFAULT,
1391 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001392 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001393 loaded_dict = common.LoadInfoDict(target_files_zip)
1394 self.assertEqual(3, loaded_dict['recovery_api_version'])
1395 self.assertEqual(2, loaded_dict['fstab_version'])
1396 self.assertIn('/', loaded_dict['fstab'])
1397 self.assertIn('/system', loaded_dict['fstab'])
1398
1399 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1400 target_files = self._test_LoadInfoDict_createTargetFiles(
1401 self.INFO_DICT_DEFAULT,
1402 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001403 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001404 loaded_dict = common.LoadInfoDict(target_files_zip)
1405 self.assertEqual(3, loaded_dict['recovery_api_version'])
1406 self.assertEqual(2, loaded_dict['fstab_version'])
1407 self.assertIn('/', loaded_dict['fstab'])
1408 self.assertIn('/system', loaded_dict['fstab'])
1409
Tao Bao82490d32019-04-09 00:12:30 -07001410 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001411 def test_LoadInfoDict_dirInput(self):
1412 target_files = self._test_LoadInfoDict_createTargetFiles(
1413 self.INFO_DICT_DEFAULT,
1414 'BOOT/RAMDISK/system/etc/recovery.fstab')
1415 unzipped = common.UnzipTemp(target_files)
1416 loaded_dict = common.LoadInfoDict(unzipped)
1417 self.assertEqual(3, loaded_dict['recovery_api_version'])
1418 self.assertEqual(2, loaded_dict['fstab_version'])
1419 self.assertIn('/', loaded_dict['fstab'])
1420 self.assertIn('/system', loaded_dict['fstab'])
1421
Tao Bao82490d32019-04-09 00:12:30 -07001422 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001423 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1424 target_files = self._test_LoadInfoDict_createTargetFiles(
1425 self.INFO_DICT_DEFAULT,
1426 'BOOT/RAMDISK/system/etc/recovery.fstab')
1427 unzipped = common.UnzipTemp(target_files)
1428 loaded_dict = common.LoadInfoDict(unzipped)
1429 self.assertEqual(3, loaded_dict['recovery_api_version'])
1430 self.assertEqual(2, loaded_dict['fstab_version'])
1431 self.assertIn('/', loaded_dict['fstab'])
1432 self.assertIn('/system', loaded_dict['fstab'])
1433
1434 def test_LoadInfoDict_systemRootImageFalse(self):
1435 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1436 # launched prior to P will likely have this config.
1437 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1438 del info_dict['no_recovery']
1439 del info_dict['system_root_image']
1440 del info_dict['recovery_as_boot']
1441 target_files = self._test_LoadInfoDict_createTargetFiles(
1442 info_dict,
1443 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001444 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001445 loaded_dict = common.LoadInfoDict(target_files_zip)
1446 self.assertEqual(3, loaded_dict['recovery_api_version'])
1447 self.assertEqual(2, loaded_dict['fstab_version'])
1448 self.assertNotIn('/', loaded_dict['fstab'])
1449 self.assertIn('/system', loaded_dict['fstab'])
1450
1451 def test_LoadInfoDict_recoveryAsBootFalse(self):
1452 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1453 # devices launched since P will likely have this config.
1454 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1455 del info_dict['no_recovery']
1456 del info_dict['recovery_as_boot']
1457 target_files = self._test_LoadInfoDict_createTargetFiles(
1458 info_dict,
1459 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001460 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001461 loaded_dict = common.LoadInfoDict(target_files_zip)
1462 self.assertEqual(3, loaded_dict['recovery_api_version'])
1463 self.assertEqual(2, loaded_dict['fstab_version'])
1464 self.assertIn('/', loaded_dict['fstab'])
1465 self.assertIn('/system', loaded_dict['fstab'])
1466
1467 def test_LoadInfoDict_noRecoveryTrue(self):
1468 # Device doesn't have a recovery partition at all.
1469 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1470 del info_dict['recovery_as_boot']
1471 target_files = self._test_LoadInfoDict_createTargetFiles(
1472 info_dict,
1473 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001474 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001475 loaded_dict = common.LoadInfoDict(target_files_zip)
1476 self.assertEqual(3, loaded_dict['recovery_api_version'])
1477 self.assertEqual(2, loaded_dict['fstab_version'])
1478 self.assertIsNone(loaded_dict['fstab'])
1479
Tao Bao82490d32019-04-09 00:12:30 -07001480 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001481 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1482 target_files = self._test_LoadInfoDict_createTargetFiles(
1483 self.INFO_DICT_DEFAULT,
1484 'BOOT/RAMDISK/system/etc/recovery.fstab')
1485 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001486 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001487 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1488
Tao Bao82490d32019-04-09 00:12:30 -07001489 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001490 def test_LoadInfoDict_repacking(self):
1491 target_files = self._test_LoadInfoDict_createTargetFiles(
1492 self.INFO_DICT_DEFAULT,
1493 'BOOT/RAMDISK/system/etc/recovery.fstab')
1494 unzipped = common.UnzipTemp(target_files)
1495 loaded_dict = common.LoadInfoDict(unzipped, True)
1496 self.assertEqual(3, loaded_dict['recovery_api_version'])
1497 self.assertEqual(2, loaded_dict['fstab_version'])
1498 self.assertIn('/', loaded_dict['fstab'])
1499 self.assertIn('/system', loaded_dict['fstab'])
1500 self.assertEqual(
1501 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1502 self.assertEqual(
1503 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1504 loaded_dict['root_fs_config'])
1505
1506 def test_LoadInfoDict_repackingWithZipFileInput(self):
1507 target_files = self._test_LoadInfoDict_createTargetFiles(
1508 self.INFO_DICT_DEFAULT,
1509 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001510 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001511 self.assertRaises(
1512 AssertionError, common.LoadInfoDict, target_files_zip, True)
1513
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001514 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1515 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001516 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001517 'super_partition_groups': 'group_a',
1518 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001519 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001520 }
1521 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001522 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001523 'super_partition_groups': 'group_a group_b',
1524 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001525 'super_block_devices': 'super',
1526 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001527 'super_group_a_partition_list': 'vendor',
1528 'super_group_a_group_size': '1000',
1529 'super_group_b_partition_list': 'product',
1530 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001531 }
1532 merged_dict = common.MergeDynamicPartitionInfoDicts(
1533 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001534 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001535 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001536 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001537 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001538 'dynamic_partition_list': 'product system vendor',
1539 'super_block_devices': 'super',
1540 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001541 'super_group_a_partition_list': 'system vendor',
1542 'super_group_a_group_size': '1000',
1543 'super_group_b_partition_list': 'product',
1544 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001545 }
1546 self.assertEqual(merged_dict, expected_merged_dict)
1547
1548 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1549 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001550 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001551 'super_partition_groups': 'group_a',
1552 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001553 'super_group_a_partition_list': 'system',
1554 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001555 }
1556 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001557 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001558 'super_partition_groups': 'group_a group_b',
1559 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001560 'super_group_a_partition_list': 'vendor',
1561 'super_group_a_group_size': '1000',
1562 'super_group_b_partition_list': 'product',
1563 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001564 }
1565 merged_dict = common.MergeDynamicPartitionInfoDicts(
1566 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001567 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001568 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001569 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001570 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001571 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001572 'super_group_a_partition_list': 'system vendor',
1573 'super_group_a_group_size': '1000',
1574 'super_group_b_partition_list': 'product',
1575 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001576 }
1577 self.assertEqual(merged_dict, expected_merged_dict)
1578
Daniel Norman276f0622019-07-26 14:13:51 -07001579 def test_GetAvbPartitionArg(self):
1580 info_dict = {}
1581 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1582 self.assertEqual(
1583 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1584
1585 @test_utils.SkipIfExternalToolsUnavailable()
1586 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1587 testdata_dir = test_utils.get_testdata_dir()
1588 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1589 info_dict = {
1590 'avb_avbtool': 'avbtool',
1591 'avb_vendor_key_path': pubkey,
1592 'avb_vendor_rollback_index_location': 5,
1593 }
1594 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1595 self.assertEqual(2, len(cmd))
1596 self.assertEqual('--chain_partition', cmd[0])
1597 chained_partition_args = cmd[1].split(':')
1598 self.assertEqual(3, len(chained_partition_args))
1599 self.assertEqual('vendor', chained_partition_args[0])
1600 self.assertEqual('5', chained_partition_args[1])
1601 self.assertTrue(os.path.exists(chained_partition_args[2]))
1602
Tao Bao3612c882019-10-14 17:49:31 -07001603 @test_utils.SkipIfExternalToolsUnavailable()
1604 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1605 testdata_dir = test_utils.get_testdata_dir()
1606 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1607 info_dict = {
1608 'avb_avbtool': 'avbtool',
1609 'avb_recovery_key_path': pubkey,
1610 'avb_recovery_rollback_index_location': 3,
1611 }
1612 cmd = common.GetAvbPartitionArg(
1613 'recovery', '/path/to/recovery.img', info_dict)
1614 self.assertFalse(cmd)
1615
1616 @test_utils.SkipIfExternalToolsUnavailable()
1617 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1618 testdata_dir = test_utils.get_testdata_dir()
1619 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1620 info_dict = {
1621 'ab_update': 'true',
1622 'avb_avbtool': 'avbtool',
1623 'avb_recovery_key_path': pubkey,
1624 'avb_recovery_rollback_index_location': 3,
1625 }
1626 cmd = common.GetAvbPartitionArg(
1627 'recovery', '/path/to/recovery.img', info_dict)
1628 self.assertEqual(2, len(cmd))
1629 self.assertEqual('--chain_partition', cmd[0])
1630 chained_partition_args = cmd[1].split(':')
1631 self.assertEqual(3, len(chained_partition_args))
1632 self.assertEqual('recovery', chained_partition_args[0])
1633 self.assertEqual('3', chained_partition_args[1])
1634 self.assertTrue(os.path.exists(chained_partition_args[2]))
1635
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001636 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001637 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1638 self.assertFalse(os.path.exists(pubkey))
1639
1640 common.OPTIONS.info_dict = {
1641 'gki_signing_key_path': pubkey,
1642 'gki_signing_algorithm': 'SHA256_RSA4096',
1643 'gki_signing_signature_args': '--prop foo:bar',
1644 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001645 test_file = tempfile.NamedTemporaryFile()
1646 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001647 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001648
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001649 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001650 pubkey = 'no_testkey_gki.pem'
1651 self.assertFalse(os.path.exists(pubkey))
1652
1653 # Tests it should raise ExternalError if no key found under
1654 # OPTIONS.search_path.
1655 search_path_dir = common.MakeTempDir()
1656 search_pubkey = os.path.join(search_path_dir, pubkey)
1657 self.assertFalse(os.path.exists(search_pubkey))
1658
1659 common.OPTIONS.search_path = search_path_dir
1660 common.OPTIONS.info_dict = {
1661 'gki_signing_key_path': pubkey,
1662 'gki_signing_algorithm': 'SHA256_RSA4096',
1663 'gki_signing_signature_args': '--prop foo:bar',
1664 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001665 test_file = tempfile.NamedTemporaryFile()
1666 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001667 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001668
Tao Bao65b94e92018-10-11 21:57:26 -07001669class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001670 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001671
Tao Bao1c830bf2017-12-25 10:43:47 -08001672 Its format should match between common.py and validate_target_files.py.
1673 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001674
1675 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001676 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001677 # Create a fake dict that contains the fstab info for boot&recovery.
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001678 self._info = {"fstab" : {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001679 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001680 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1681 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001682 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001683 # Construct the gzipped recovery.img and boot.img
1684 self.recovery_data = bytearray([
1685 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1686 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1687 0x08, 0x00, 0x00, 0x00
1688 ])
1689 # echo -n "boot" | gzip -f | hd
1690 self.boot_data = bytearray([
1691 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1692 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1693 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001694
1695 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1696 loc = os.path.join(self._tempdir, prefix, name)
1697 if not os.path.exists(os.path.dirname(loc)):
1698 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001699 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001700 f.write(data)
1701
1702 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001703 recovery_image = common.File("recovery.img", self.recovery_data)
1704 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001705 self._info["full_recovery_image"] = "true"
1706
1707 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1708 recovery_image, boot_image, self._info)
1709 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1710 self._info)
1711
Tao Bao82490d32019-04-09 00:12:30 -07001712 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001713 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001714 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001715 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001716 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001717 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1718
1719 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1720 recovery_image, boot_image, self._info)
1721 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1722 self._info)
1723 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001724 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001725 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1726 recovery_image, boot_image, self._info)
1727 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1728 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001729
1730
Yifan Hong45433e42019-01-18 13:55:25 -08001731class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001732
Yifan Hong45433e42019-01-18 13:55:25 -08001733 def __init__(self, partition, tgt, src=None):
1734 self.partition = partition
1735 self.tgt = tgt
1736 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001737
Yifan Hong45433e42019-01-18 13:55:25 -08001738 def WriteScript(self, script, _, progress=None,
1739 write_verify_script=False):
1740 if progress:
1741 script.AppendExtra("progress({})".format(progress))
1742 script.AppendExtra("patch({});".format(self.partition))
1743 if write_verify_script:
1744 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001745
Yifan Hong45433e42019-01-18 13:55:25 -08001746 def WritePostInstallVerifyScript(self, script):
1747 script.AppendExtra("verify({});".format(self.partition))
1748
1749
1750class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001751
Yifan Hong45433e42019-01-18 13:55:25 -08001752 def __init__(self, size):
1753 self.blocksize = 4096
1754 self.total_blocks = size // 4096
1755 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1756
1757
1758class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001759
Yifan Hong45433e42019-01-18 13:55:25 -08001760 @staticmethod
1761 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001762 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001763 with output_zip.open('dynamic_partitions_op_list') as op_list:
1764 return [line.decode().strip() for line in op_list.readlines()
1765 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001766
1767 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001768 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001769 self.output_path = common.MakeTempFile(suffix='.zip')
1770
1771 def test_full(self):
1772 target_info = common.LoadDictionaryFromLines("""
1773dynamic_partition_list=system vendor
1774super_partition_groups=group_foo
1775super_group_foo_group_size={group_size}
1776super_group_foo_partition_list=system vendor
1777""".format(group_size=4 * GiB).split("\n"))
1778 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1779 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1780
1781 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001782 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001783 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1784
1785 self.assertEqual(str(self.script).strip(), """
1786assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001787patch(system);
1788verify(system);
1789unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001790patch(vendor);
1791verify(vendor);
1792unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001793""".strip())
1794
1795 lines = self.get_op_list(self.output_path)
1796
1797 remove_all_groups = lines.index("remove_all_groups")
1798 add_group = lines.index("add_group group_foo 4294967296")
1799 add_vendor = lines.index("add vendor group_foo")
1800 add_system = lines.index("add system group_foo")
1801 resize_vendor = lines.index("resize vendor 1073741824")
1802 resize_system = lines.index("resize system 3221225472")
1803
1804 self.assertLess(remove_all_groups, add_group,
1805 "Should add groups after removing all groups")
1806 self.assertLess(add_group, min(add_vendor, add_system),
1807 "Should add partitions after adding group")
1808 self.assertLess(add_system, resize_system,
1809 "Should resize system after adding it")
1810 self.assertLess(add_vendor, resize_vendor,
1811 "Should resize vendor after adding it")
1812
1813 def test_inc_groups(self):
1814 source_info = common.LoadDictionaryFromLines("""
1815super_partition_groups=group_foo group_bar group_baz
1816super_group_foo_group_size={group_foo_size}
1817super_group_bar_group_size={group_bar_size}
1818""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1819 target_info = common.LoadDictionaryFromLines("""
1820super_partition_groups=group_foo group_baz group_qux
1821super_group_foo_group_size={group_foo_size}
1822super_group_baz_group_size={group_baz_size}
1823super_group_qux_group_size={group_qux_size}
1824""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1825 group_qux_size=1 * GiB).split("\n"))
1826
1827 dp_diff = common.DynamicPartitionsDifference(target_info,
1828 block_diffs=[],
1829 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001830 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001831 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1832
1833 lines = self.get_op_list(self.output_path)
1834
1835 removed = lines.index("remove_group group_bar")
1836 shrunk = lines.index("resize_group group_foo 3221225472")
1837 grown = lines.index("resize_group group_baz 4294967296")
1838 added = lines.index("add_group group_qux 1073741824")
1839
Tao Baof1113e92019-06-18 12:10:14 -07001840 self.assertLess(max(removed, shrunk),
1841 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001842 "ops that remove / shrink partitions must precede ops that "
1843 "grow / add partitions")
1844
Yifan Hongbb2658d2019-01-25 12:30:58 -08001845 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001846 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001847dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001848super_partition_groups=group_foo
1849super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001850super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001851""".format(group_foo_size=4 * GiB).split("\n"))
1852 target_info = common.LoadDictionaryFromLines("""
1853dynamic_partition_list=system vendor product odm
1854super_partition_groups=group_foo group_bar
1855super_group_foo_group_size={group_foo_size}
1856super_group_foo_partition_list=system vendor odm
1857super_group_bar_group_size={group_bar_size}
1858super_group_bar_partition_list=product
1859""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1860
1861 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1862 src=FakeSparseImage(1024 * MiB)),
1863 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1864 src=FakeSparseImage(1024 * MiB)),
1865 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1866 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001867 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001868 src=FakeSparseImage(1024 * MiB)),
1869 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1870 src=None)]
1871
1872 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1873 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001874 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001875 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1876
1877 metadata_idx = self.script.lines.index(
1878 'assert(update_dynamic_partitions(package_extract_file('
1879 '"dynamic_partitions_op_list")));')
1880 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1881 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1882 for p in ("product", "system", "odm"):
1883 patch_idx = self.script.lines.index("patch({});".format(p))
1884 verify_idx = self.script.lines.index("verify({});".format(p))
1885 self.assertLess(metadata_idx, patch_idx,
1886 "Should patch {} after updating metadata".format(p))
1887 self.assertLess(patch_idx, verify_idx,
1888 "Should verify {} after patching".format(p))
1889
Justin Yun6151e3f2019-06-25 15:58:13 +09001890 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001891
1892 lines = self.get_op_list(self.output_path)
1893
Justin Yun6151e3f2019-06-25 15:58:13 +09001894 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001895 move_product_out = lines.index("move product default")
1896 shrink = lines.index("resize vendor 536870912")
1897 shrink_group = lines.index("resize_group group_foo 3221225472")
1898 add_group_bar = lines.index("add_group group_bar 1073741824")
1899 add_odm = lines.index("add odm group_foo")
1900 grow_existing = lines.index("resize system 1610612736")
1901 grow_added = lines.index("resize odm 1073741824")
1902 move_product_in = lines.index("move product group_bar")
1903
1904 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1905 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1906
1907 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1908 "Must shrink group after partitions inside group are shrunk"
1909 " / removed")
1910
1911 self.assertLess(add_group_bar, move_product_in,
1912 "Must add partitions to group after group is added")
1913
1914 self.assertLess(max_idx_move_partition_out_foo,
1915 min_idx_move_partition_in_foo,
1916 "Must shrink partitions / remove partitions from group"
1917 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001918
1919 def test_remove_partition(self):
1920 source_info = common.LoadDictionaryFromLines("""
1921blockimgdiff_versions=3,4
1922use_dynamic_partitions=true
1923dynamic_partition_list=foo
1924super_partition_groups=group_foo
1925super_group_foo_group_size={group_foo_size}
1926super_group_foo_partition_list=foo
1927""".format(group_foo_size=4 * GiB).split("\n"))
1928 target_info = common.LoadDictionaryFromLines("""
1929blockimgdiff_versions=3,4
1930use_dynamic_partitions=true
1931super_partition_groups=group_foo
1932super_group_foo_group_size={group_foo_size}
1933""".format(group_foo_size=4 * GiB).split("\n"))
1934
1935 common.OPTIONS.info_dict = target_info
1936 common.OPTIONS.target_info_dict = target_info
1937 common.OPTIONS.source_info_dict = source_info
1938 common.OPTIONS.cache_size = 4 * 4096
1939
1940 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1941 src=DataImage("source", pad=True))]
1942
1943 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1944 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001945 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001946 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1947
1948 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001949 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001950
1951 lines = self.get_op_list(self.output_path)
1952 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001953
1954
1955class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1956 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001957 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001958 'ro.odm.build.date.utc=1578430045',
1959 'ro.odm.build.fingerprint='
1960 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1961 'ro.product.odm.device=coral',
1962 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1963 ]
1964
1965 @staticmethod
1966 def _BuildZipFile(entries):
1967 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001968 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001969 for name, content in entries.items():
1970 input_zip.writestr(name, content)
1971
1972 return input_file
1973
1974 def test_parseBuildProps_noImportStatement(self):
1975 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001976 'ro.odm.build.date.utc=1578430045',
1977 'ro.odm.build.fingerprint='
1978 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1979 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001980 ]
1981 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001982 'ODM/etc/build.prop': '\n'.join(build_prop),
1983 })
1984
Kelvin Zhang928c2342020-09-22 16:15:57 -04001985 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001986 placeholder_values = {
1987 'ro.boot.product.device_name': ['std', 'pro']
1988 }
1989 partition_props = common.PartitionBuildProps.FromInputFile(
1990 input_zip, 'odm', placeholder_values)
1991
1992 self.assertEqual({
1993 'ro.odm.build.date.utc': '1578430045',
1994 'ro.odm.build.fingerprint':
1995 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1996 'ro.product.odm.device': 'coral',
1997 }, partition_props.build_props)
1998
1999 self.assertEqual(set(), partition_props.prop_overrides)
2000
2001 def test_parseBuildProps_singleImportStatement(self):
2002 build_std_prop = [
2003 'ro.product.odm.device=coral',
2004 'ro.product.odm.name=product1',
2005 ]
2006 build_pro_prop = [
2007 'ro.product.odm.device=coralpro',
2008 'ro.product.odm.name=product2',
2009 ]
2010
2011 input_file = self._BuildZipFile({
2012 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2013 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2014 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2015 })
2016
Kelvin Zhang928c2342020-09-22 16:15:57 -04002017 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002018 placeholder_values = {
2019 'ro.boot.product.device_name': 'std'
2020 }
2021 partition_props = common.PartitionBuildProps.FromInputFile(
2022 input_zip, 'odm', placeholder_values)
2023
2024 self.assertEqual({
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00002025 'ro.odm.build.date.utc': '1578430045',
2026 'ro.odm.build.fingerprint':
2027 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2028 'ro.product.odm.device': 'coral',
2029 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00002030 }, partition_props.build_props)
2031
Kelvin Zhang928c2342020-09-22 16:15:57 -04002032 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002033 placeholder_values = {
2034 'ro.boot.product.device_name': 'pro'
2035 }
2036 partition_props = common.PartitionBuildProps.FromInputFile(
2037 input_zip, 'odm', placeholder_values)
2038
2039 self.assertEqual({
2040 'ro.odm.build.date.utc': '1578430045',
2041 'ro.odm.build.fingerprint':
2042 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2043 'ro.product.odm.device': 'coralpro',
2044 'ro.product.odm.name': 'product2',
2045 }, partition_props.build_props)
2046
2047 def test_parseBuildProps_noPlaceHolders(self):
2048 build_prop = copy.copy(self.odm_build_prop)
2049 input_file = self._BuildZipFile({
2050 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002051 })
2052
Kelvin Zhang928c2342020-09-22 16:15:57 -04002053 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002054 partition_props = common.PartitionBuildProps.FromInputFile(
2055 input_zip, 'odm')
2056
2057 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002058 'ro.odm.build.date.utc': '1578430045',
2059 'ro.odm.build.fingerprint':
2060 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2061 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002062 }, partition_props.build_props)
2063
Tianjie Xu9afb2212020-05-10 21:48:15 +00002064 self.assertEqual(set(), partition_props.prop_overrides)
2065
2066 def test_parseBuildProps_multipleImportStatements(self):
2067 build_prop = copy.deepcopy(self.odm_build_prop)
2068 build_prop.append(
2069 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2070
2071 build_std_prop = [
2072 'ro.product.odm.device=coral',
2073 ]
2074 build_pro_prop = [
2075 'ro.product.odm.device=coralpro',
2076 ]
2077
2078 product1_prop = [
2079 'ro.product.odm.name=product1',
2080 'ro.product.not_care=not_care',
2081 ]
2082
2083 product2_prop = [
2084 'ro.product.odm.name=product2',
2085 'ro.product.not_care=not_care',
2086 ]
2087
2088 input_file = self._BuildZipFile({
2089 'ODM/etc/build.prop': '\n'.join(build_prop),
2090 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2091 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2092 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2093 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2094 })
2095
Kelvin Zhang928c2342020-09-22 16:15:57 -04002096 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002097 placeholder_values = {
2098 'ro.boot.product.device_name': 'std',
2099 'ro.boot.product.product_name': 'product1',
2100 'ro.boot.product.not_care': 'not_care',
2101 }
2102 partition_props = common.PartitionBuildProps.FromInputFile(
2103 input_zip, 'odm', placeholder_values)
2104
2105 self.assertEqual({
2106 'ro.odm.build.date.utc': '1578430045',
2107 'ro.odm.build.fingerprint':
2108 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2109 'ro.product.odm.device': 'coral',
2110 'ro.product.odm.name': 'product1'
2111 }, partition_props.build_props)
2112
Kelvin Zhang928c2342020-09-22 16:15:57 -04002113 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002114 placeholder_values = {
2115 'ro.boot.product.device_name': 'pro',
2116 'ro.boot.product.product_name': 'product2',
2117 'ro.boot.product.not_care': 'not_care',
2118 }
2119 partition_props = common.PartitionBuildProps.FromInputFile(
2120 input_zip, 'odm', placeholder_values)
2121
2122 self.assertEqual({
2123 'ro.odm.build.date.utc': '1578430045',
2124 'ro.odm.build.fingerprint':
2125 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2126 'ro.product.odm.device': 'coralpro',
2127 'ro.product.odm.name': 'product2'
2128 }, partition_props.build_props)
2129
2130 def test_parseBuildProps_defineAfterOverride(self):
2131 build_prop = copy.deepcopy(self.odm_build_prop)
2132 build_prop.append('ro.product.odm.device=coral')
2133
2134 build_std_prop = [
2135 'ro.product.odm.device=coral',
2136 ]
2137 build_pro_prop = [
2138 'ro.product.odm.device=coralpro',
2139 ]
2140
2141 input_file = self._BuildZipFile({
2142 'ODM/etc/build.prop': '\n'.join(build_prop),
2143 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2144 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2145 })
2146
Kelvin Zhang928c2342020-09-22 16:15:57 -04002147 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002148 placeholder_values = {
2149 'ro.boot.product.device_name': 'std',
2150 }
2151
2152 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2153 input_zip, 'odm', placeholder_values)
2154
2155 def test_parseBuildProps_duplicateOverride(self):
2156 build_prop = copy.deepcopy(self.odm_build_prop)
2157 build_prop.append(
2158 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2159
2160 build_std_prop = [
2161 'ro.product.odm.device=coral',
2162 'ro.product.odm.name=product1',
2163 ]
2164 build_pro_prop = [
2165 'ro.product.odm.device=coralpro',
2166 ]
2167
2168 product1_prop = [
2169 'ro.product.odm.name=product1',
2170 ]
2171
2172 product2_prop = [
2173 'ro.product.odm.name=product2',
2174 ]
2175
2176 input_file = self._BuildZipFile({
2177 'ODM/etc/build.prop': '\n'.join(build_prop),
2178 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2179 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2180 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2181 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2182 })
2183
Kelvin Zhang928c2342020-09-22 16:15:57 -04002184 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002185 placeholder_values = {
2186 'ro.boot.product.device_name': 'std',
2187 'ro.boot.product.product_name': 'product1',
2188 }
2189 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2190 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08002191
2192 def test_partitionBuildProps_fromInputFile_deepcopy(self):
2193 build_prop = [
2194 'ro.odm.build.date.utc=1578430045',
2195 'ro.odm.build.fingerprint='
2196 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2197 'ro.product.odm.device=coral',
2198 ]
2199 input_file = self._BuildZipFile({
2200 'ODM/etc/build.prop': '\n'.join(build_prop),
2201 })
2202
2203 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
2204 placeholder_values = {
2205 'ro.boot.product.device_name': ['std', 'pro']
2206 }
2207 partition_props = common.PartitionBuildProps.FromInputFile(
2208 input_zip, 'odm', placeholder_values)
2209
2210 copied_props = copy.deepcopy(partition_props)
2211 self.assertEqual({
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00002212 'ro.odm.build.date.utc': '1578430045',
2213 'ro.odm.build.fingerprint':
2214 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2215 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08002216 }, copied_props.build_props)