blob: 86fb480d0dd98179ddf8b370464338510b09c73e [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 }
Kelvin Zhang03dc6ee2023-06-28 10:10:39 -07001645 common.OPTIONS.search_path = None
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001646 test_file = tempfile.NamedTemporaryFile()
1647 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001648 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001649
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001650 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001651 pubkey = 'no_testkey_gki.pem'
1652 self.assertFalse(os.path.exists(pubkey))
1653
1654 # Tests it should raise ExternalError if no key found under
1655 # OPTIONS.search_path.
1656 search_path_dir = common.MakeTempDir()
1657 search_pubkey = os.path.join(search_path_dir, pubkey)
1658 self.assertFalse(os.path.exists(search_pubkey))
1659
1660 common.OPTIONS.search_path = search_path_dir
1661 common.OPTIONS.info_dict = {
1662 'gki_signing_key_path': pubkey,
1663 'gki_signing_algorithm': 'SHA256_RSA4096',
1664 'gki_signing_signature_args': '--prop foo:bar',
1665 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001666 test_file = tempfile.NamedTemporaryFile()
1667 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001668 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001669
Tao Bao65b94e92018-10-11 21:57:26 -07001670class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001671 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001672
Tao Bao1c830bf2017-12-25 10:43:47 -08001673 Its format should match between common.py and validate_target_files.py.
1674 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001675
1676 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001677 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001678 # Create a fake dict that contains the fstab info for boot&recovery.
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001679 self._info = {"fstab" : {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001680 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001681 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1682 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001683 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001684 # Construct the gzipped recovery.img and boot.img
1685 self.recovery_data = bytearray([
1686 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1687 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1688 0x08, 0x00, 0x00, 0x00
1689 ])
1690 # echo -n "boot" | gzip -f | hd
1691 self.boot_data = bytearray([
1692 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1693 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1694 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001695
1696 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1697 loc = os.path.join(self._tempdir, prefix, name)
1698 if not os.path.exists(os.path.dirname(loc)):
1699 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001700 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001701 f.write(data)
1702
1703 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001704 recovery_image = common.File("recovery.img", self.recovery_data)
1705 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001706 self._info["full_recovery_image"] = "true"
1707
1708 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1709 recovery_image, boot_image, self._info)
1710 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1711 self._info)
1712
Tao Bao82490d32019-04-09 00:12:30 -07001713 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001714 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001715 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001716 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001717 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001718 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1719
1720 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1721 recovery_image, boot_image, self._info)
1722 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1723 self._info)
1724 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001725 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001726 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1727 recovery_image, boot_image, self._info)
1728 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1729 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001730
1731
Yifan Hong45433e42019-01-18 13:55:25 -08001732class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001733
Yifan Hong45433e42019-01-18 13:55:25 -08001734 def __init__(self, partition, tgt, src=None):
1735 self.partition = partition
1736 self.tgt = tgt
1737 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001738
Yifan Hong45433e42019-01-18 13:55:25 -08001739 def WriteScript(self, script, _, progress=None,
1740 write_verify_script=False):
1741 if progress:
1742 script.AppendExtra("progress({})".format(progress))
1743 script.AppendExtra("patch({});".format(self.partition))
1744 if write_verify_script:
1745 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001746
Yifan Hong45433e42019-01-18 13:55:25 -08001747 def WritePostInstallVerifyScript(self, script):
1748 script.AppendExtra("verify({});".format(self.partition))
1749
1750
1751class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001752
Yifan Hong45433e42019-01-18 13:55:25 -08001753 def __init__(self, size):
1754 self.blocksize = 4096
1755 self.total_blocks = size // 4096
1756 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1757
1758
1759class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001760
Yifan Hong45433e42019-01-18 13:55:25 -08001761 @staticmethod
1762 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001763 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001764 with output_zip.open('dynamic_partitions_op_list') as op_list:
1765 return [line.decode().strip() for line in op_list.readlines()
1766 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001767
1768 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001769 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001770 self.output_path = common.MakeTempFile(suffix='.zip')
1771
1772 def test_full(self):
1773 target_info = common.LoadDictionaryFromLines("""
1774dynamic_partition_list=system vendor
1775super_partition_groups=group_foo
1776super_group_foo_group_size={group_size}
1777super_group_foo_partition_list=system vendor
1778""".format(group_size=4 * GiB).split("\n"))
1779 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1780 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1781
1782 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001783 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001784 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1785
1786 self.assertEqual(str(self.script).strip(), """
1787assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001788patch(system);
1789verify(system);
1790unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001791patch(vendor);
1792verify(vendor);
1793unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001794""".strip())
1795
1796 lines = self.get_op_list(self.output_path)
1797
1798 remove_all_groups = lines.index("remove_all_groups")
1799 add_group = lines.index("add_group group_foo 4294967296")
1800 add_vendor = lines.index("add vendor group_foo")
1801 add_system = lines.index("add system group_foo")
1802 resize_vendor = lines.index("resize vendor 1073741824")
1803 resize_system = lines.index("resize system 3221225472")
1804
1805 self.assertLess(remove_all_groups, add_group,
1806 "Should add groups after removing all groups")
1807 self.assertLess(add_group, min(add_vendor, add_system),
1808 "Should add partitions after adding group")
1809 self.assertLess(add_system, resize_system,
1810 "Should resize system after adding it")
1811 self.assertLess(add_vendor, resize_vendor,
1812 "Should resize vendor after adding it")
1813
1814 def test_inc_groups(self):
1815 source_info = common.LoadDictionaryFromLines("""
1816super_partition_groups=group_foo group_bar group_baz
1817super_group_foo_group_size={group_foo_size}
1818super_group_bar_group_size={group_bar_size}
1819""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1820 target_info = common.LoadDictionaryFromLines("""
1821super_partition_groups=group_foo group_baz group_qux
1822super_group_foo_group_size={group_foo_size}
1823super_group_baz_group_size={group_baz_size}
1824super_group_qux_group_size={group_qux_size}
1825""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1826 group_qux_size=1 * GiB).split("\n"))
1827
1828 dp_diff = common.DynamicPartitionsDifference(target_info,
1829 block_diffs=[],
1830 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001831 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001832 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1833
1834 lines = self.get_op_list(self.output_path)
1835
1836 removed = lines.index("remove_group group_bar")
1837 shrunk = lines.index("resize_group group_foo 3221225472")
1838 grown = lines.index("resize_group group_baz 4294967296")
1839 added = lines.index("add_group group_qux 1073741824")
1840
Tao Baof1113e92019-06-18 12:10:14 -07001841 self.assertLess(max(removed, shrunk),
1842 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001843 "ops that remove / shrink partitions must precede ops that "
1844 "grow / add partitions")
1845
Yifan Hongbb2658d2019-01-25 12:30:58 -08001846 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001847 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001848dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001849super_partition_groups=group_foo
1850super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001851super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001852""".format(group_foo_size=4 * GiB).split("\n"))
1853 target_info = common.LoadDictionaryFromLines("""
1854dynamic_partition_list=system vendor product odm
1855super_partition_groups=group_foo group_bar
1856super_group_foo_group_size={group_foo_size}
1857super_group_foo_partition_list=system vendor odm
1858super_group_bar_group_size={group_bar_size}
1859super_group_bar_partition_list=product
1860""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1861
1862 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1863 src=FakeSparseImage(1024 * MiB)),
1864 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1865 src=FakeSparseImage(1024 * MiB)),
1866 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1867 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001868 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001869 src=FakeSparseImage(1024 * MiB)),
1870 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1871 src=None)]
1872
1873 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1874 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001875 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001876 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1877
1878 metadata_idx = self.script.lines.index(
1879 'assert(update_dynamic_partitions(package_extract_file('
1880 '"dynamic_partitions_op_list")));')
1881 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1882 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1883 for p in ("product", "system", "odm"):
1884 patch_idx = self.script.lines.index("patch({});".format(p))
1885 verify_idx = self.script.lines.index("verify({});".format(p))
1886 self.assertLess(metadata_idx, patch_idx,
1887 "Should patch {} after updating metadata".format(p))
1888 self.assertLess(patch_idx, verify_idx,
1889 "Should verify {} after patching".format(p))
1890
Justin Yun6151e3f2019-06-25 15:58:13 +09001891 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001892
1893 lines = self.get_op_list(self.output_path)
1894
Justin Yun6151e3f2019-06-25 15:58:13 +09001895 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001896 move_product_out = lines.index("move product default")
1897 shrink = lines.index("resize vendor 536870912")
1898 shrink_group = lines.index("resize_group group_foo 3221225472")
1899 add_group_bar = lines.index("add_group group_bar 1073741824")
1900 add_odm = lines.index("add odm group_foo")
1901 grow_existing = lines.index("resize system 1610612736")
1902 grow_added = lines.index("resize odm 1073741824")
1903 move_product_in = lines.index("move product group_bar")
1904
1905 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1906 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1907
1908 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1909 "Must shrink group after partitions inside group are shrunk"
1910 " / removed")
1911
1912 self.assertLess(add_group_bar, move_product_in,
1913 "Must add partitions to group after group is added")
1914
1915 self.assertLess(max_idx_move_partition_out_foo,
1916 min_idx_move_partition_in_foo,
1917 "Must shrink partitions / remove partitions from group"
1918 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001919
1920 def test_remove_partition(self):
1921 source_info = common.LoadDictionaryFromLines("""
1922blockimgdiff_versions=3,4
1923use_dynamic_partitions=true
1924dynamic_partition_list=foo
1925super_partition_groups=group_foo
1926super_group_foo_group_size={group_foo_size}
1927super_group_foo_partition_list=foo
1928""".format(group_foo_size=4 * GiB).split("\n"))
1929 target_info = common.LoadDictionaryFromLines("""
1930blockimgdiff_versions=3,4
1931use_dynamic_partitions=true
1932super_partition_groups=group_foo
1933super_group_foo_group_size={group_foo_size}
1934""".format(group_foo_size=4 * GiB).split("\n"))
1935
1936 common.OPTIONS.info_dict = target_info
1937 common.OPTIONS.target_info_dict = target_info
1938 common.OPTIONS.source_info_dict = source_info
1939 common.OPTIONS.cache_size = 4 * 4096
1940
1941 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1942 src=DataImage("source", pad=True))]
1943
1944 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1945 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001946 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001947 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1948
1949 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001950 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001951
1952 lines = self.get_op_list(self.output_path)
1953 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001954
1955
1956class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1957 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001958 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001959 'ro.odm.build.date.utc=1578430045',
1960 'ro.odm.build.fingerprint='
1961 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1962 'ro.product.odm.device=coral',
1963 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1964 ]
1965
1966 @staticmethod
1967 def _BuildZipFile(entries):
1968 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001969 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001970 for name, content in entries.items():
1971 input_zip.writestr(name, content)
1972
1973 return input_file
1974
1975 def test_parseBuildProps_noImportStatement(self):
1976 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001977 'ro.odm.build.date.utc=1578430045',
1978 'ro.odm.build.fingerprint='
1979 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1980 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001981 ]
1982 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001983 'ODM/etc/build.prop': '\n'.join(build_prop),
1984 })
1985
Kelvin Zhang928c2342020-09-22 16:15:57 -04001986 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001987 placeholder_values = {
1988 'ro.boot.product.device_name': ['std', 'pro']
1989 }
1990 partition_props = common.PartitionBuildProps.FromInputFile(
1991 input_zip, 'odm', placeholder_values)
1992
1993 self.assertEqual({
1994 'ro.odm.build.date.utc': '1578430045',
1995 'ro.odm.build.fingerprint':
1996 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1997 'ro.product.odm.device': 'coral',
1998 }, partition_props.build_props)
1999
2000 self.assertEqual(set(), partition_props.prop_overrides)
2001
2002 def test_parseBuildProps_singleImportStatement(self):
2003 build_std_prop = [
2004 'ro.product.odm.device=coral',
2005 'ro.product.odm.name=product1',
2006 ]
2007 build_pro_prop = [
2008 'ro.product.odm.device=coralpro',
2009 'ro.product.odm.name=product2',
2010 ]
2011
2012 input_file = self._BuildZipFile({
2013 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2014 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2015 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2016 })
2017
Kelvin Zhang928c2342020-09-22 16:15:57 -04002018 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002019 placeholder_values = {
2020 'ro.boot.product.device_name': 'std'
2021 }
2022 partition_props = common.PartitionBuildProps.FromInputFile(
2023 input_zip, 'odm', placeholder_values)
2024
2025 self.assertEqual({
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00002026 'ro.odm.build.date.utc': '1578430045',
2027 'ro.odm.build.fingerprint':
2028 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2029 'ro.product.odm.device': 'coral',
2030 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00002031 }, partition_props.build_props)
2032
Kelvin Zhang928c2342020-09-22 16:15:57 -04002033 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002034 placeholder_values = {
2035 'ro.boot.product.device_name': 'pro'
2036 }
2037 partition_props = common.PartitionBuildProps.FromInputFile(
2038 input_zip, 'odm', placeholder_values)
2039
2040 self.assertEqual({
2041 'ro.odm.build.date.utc': '1578430045',
2042 'ro.odm.build.fingerprint':
2043 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2044 'ro.product.odm.device': 'coralpro',
2045 'ro.product.odm.name': 'product2',
2046 }, partition_props.build_props)
2047
2048 def test_parseBuildProps_noPlaceHolders(self):
2049 build_prop = copy.copy(self.odm_build_prop)
2050 input_file = self._BuildZipFile({
2051 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002052 })
2053
Kelvin Zhang928c2342020-09-22 16:15:57 -04002054 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002055 partition_props = common.PartitionBuildProps.FromInputFile(
2056 input_zip, 'odm')
2057
2058 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002059 'ro.odm.build.date.utc': '1578430045',
2060 'ro.odm.build.fingerprint':
2061 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2062 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002063 }, partition_props.build_props)
2064
Tianjie Xu9afb2212020-05-10 21:48:15 +00002065 self.assertEqual(set(), partition_props.prop_overrides)
2066
2067 def test_parseBuildProps_multipleImportStatements(self):
2068 build_prop = copy.deepcopy(self.odm_build_prop)
2069 build_prop.append(
2070 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2071
2072 build_std_prop = [
2073 'ro.product.odm.device=coral',
2074 ]
2075 build_pro_prop = [
2076 'ro.product.odm.device=coralpro',
2077 ]
2078
2079 product1_prop = [
2080 'ro.product.odm.name=product1',
2081 'ro.product.not_care=not_care',
2082 ]
2083
2084 product2_prop = [
2085 'ro.product.odm.name=product2',
2086 'ro.product.not_care=not_care',
2087 ]
2088
2089 input_file = self._BuildZipFile({
2090 'ODM/etc/build.prop': '\n'.join(build_prop),
2091 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2092 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2093 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2094 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2095 })
2096
Kelvin Zhang928c2342020-09-22 16:15:57 -04002097 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002098 placeholder_values = {
2099 'ro.boot.product.device_name': 'std',
2100 'ro.boot.product.product_name': 'product1',
2101 'ro.boot.product.not_care': 'not_care',
2102 }
2103 partition_props = common.PartitionBuildProps.FromInputFile(
2104 input_zip, 'odm', placeholder_values)
2105
2106 self.assertEqual({
2107 'ro.odm.build.date.utc': '1578430045',
2108 'ro.odm.build.fingerprint':
2109 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2110 'ro.product.odm.device': 'coral',
2111 'ro.product.odm.name': 'product1'
2112 }, partition_props.build_props)
2113
Kelvin Zhang928c2342020-09-22 16:15:57 -04002114 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002115 placeholder_values = {
2116 'ro.boot.product.device_name': 'pro',
2117 'ro.boot.product.product_name': 'product2',
2118 'ro.boot.product.not_care': 'not_care',
2119 }
2120 partition_props = common.PartitionBuildProps.FromInputFile(
2121 input_zip, 'odm', placeholder_values)
2122
2123 self.assertEqual({
2124 'ro.odm.build.date.utc': '1578430045',
2125 'ro.odm.build.fingerprint':
2126 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2127 'ro.product.odm.device': 'coralpro',
2128 'ro.product.odm.name': 'product2'
2129 }, partition_props.build_props)
2130
2131 def test_parseBuildProps_defineAfterOverride(self):
2132 build_prop = copy.deepcopy(self.odm_build_prop)
2133 build_prop.append('ro.product.odm.device=coral')
2134
2135 build_std_prop = [
2136 'ro.product.odm.device=coral',
2137 ]
2138 build_pro_prop = [
2139 'ro.product.odm.device=coralpro',
2140 ]
2141
2142 input_file = self._BuildZipFile({
2143 'ODM/etc/build.prop': '\n'.join(build_prop),
2144 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2145 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2146 })
2147
Kelvin Zhang928c2342020-09-22 16:15:57 -04002148 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002149 placeholder_values = {
2150 'ro.boot.product.device_name': 'std',
2151 }
2152
2153 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2154 input_zip, 'odm', placeholder_values)
2155
2156 def test_parseBuildProps_duplicateOverride(self):
2157 build_prop = copy.deepcopy(self.odm_build_prop)
2158 build_prop.append(
2159 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2160
2161 build_std_prop = [
2162 'ro.product.odm.device=coral',
2163 'ro.product.odm.name=product1',
2164 ]
2165 build_pro_prop = [
2166 'ro.product.odm.device=coralpro',
2167 ]
2168
2169 product1_prop = [
2170 'ro.product.odm.name=product1',
2171 ]
2172
2173 product2_prop = [
2174 'ro.product.odm.name=product2',
2175 ]
2176
2177 input_file = self._BuildZipFile({
2178 'ODM/etc/build.prop': '\n'.join(build_prop),
2179 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2180 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2181 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2182 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2183 })
2184
Kelvin Zhang928c2342020-09-22 16:15:57 -04002185 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002186 placeholder_values = {
2187 'ro.boot.product.device_name': 'std',
2188 'ro.boot.product.product_name': 'product1',
2189 }
2190 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2191 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08002192
2193 def test_partitionBuildProps_fromInputFile_deepcopy(self):
2194 build_prop = [
2195 'ro.odm.build.date.utc=1578430045',
2196 'ro.odm.build.fingerprint='
2197 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2198 'ro.product.odm.device=coral',
2199 ]
2200 input_file = self._BuildZipFile({
2201 'ODM/etc/build.prop': '\n'.join(build_prop),
2202 })
2203
2204 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
2205 placeholder_values = {
2206 'ro.boot.product.device_name': ['std', 'pro']
2207 }
2208 partition_props = common.PartitionBuildProps.FromInputFile(
2209 input_zip, 'odm', placeholder_values)
2210
2211 copied_props = copy.deepcopy(partition_props)
2212 self.assertEqual({
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00002213 'ro.odm.build.date.utc': '1578430045',
2214 'ro.odm.build.fingerprint':
2215 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2216 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08002217 }, copied_props.build_props)