blob: 029d46f07bc3718377d83c3bad7c9d499cf9302f [file] [log] [blame]
Dan Albert8e0178d2015-01-27 15:53:15 -08001#
2# Copyright (C) 2015 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
Tao Baofc7e0e02018-02-13 13:54:02 -080016
Tao Baoa57ab9f2018-08-24 12:08:38 -070017import copy
Dan Albert8e0178d2015-01-27 15:53:15 -080018import os
Tao Bao17e4e612018-02-16 17:12:54 -080019import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080020import tempfile
21import time
Dan Albert8e0178d2015-01-27 15:53:15 -080022import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080023from hashlib import sha1
24
Dan Albert8e0178d2015-01-27 15:53:15 -080025import common
Tao Bao04e1f012018-02-04 12:13:35 -080026import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070027import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070028from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080029from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080030
Tao Bao04e1f012018-02-04 12:13:35 -080031
Tao Bao31b08072017-11-08 15:50:59 -080032KiB = 1024
33MiB = 1024 * KiB
34GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080035
Tao Bao1c830bf2017-12-25 10:43:47 -080036
Tao Baof3282b42015-04-01 11:21:55 -070037def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080038 size = int(2 * GiB + 1)
39 block_size = 4 * KiB
40 step_size = 4 * MiB
41 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
42 for _ in range(0, size, step_size):
43 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070044 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070045
Dan Albert8e0178d2015-01-27 15:53:15 -080046
Tao Bao1c320f82019-10-04 23:25:12 -070047class BuildInfoTest(test_utils.ReleaseToolsTestCase):
48
49 TEST_INFO_DICT = {
50 'build.prop' : {
51 'ro.product.device' : 'product-device',
52 'ro.product.name' : 'product-name',
53 'ro.build.fingerprint' : 'build-fingerprint',
54 'ro.build.foo' : 'build-foo',
55 },
Daniel Normanab5acef2020-01-08 17:01:11 -080056 'system.build.prop' : {
57 'ro.product.system.brand' : 'product-brand',
58 'ro.product.system.name' : 'product-name',
59 'ro.product.system.device' : 'product-device',
60 'ro.system.build.version.release' : 'version-release',
61 'ro.system.build.id' : 'build-id',
62 'ro.system.build.version.incremental' : 'version-incremental',
63 'ro.system.build.type' : 'build-type',
64 'ro.system.build.tags' : 'build-tags',
65 'ro.system.build.foo' : 'build-foo',
66 },
Tao Bao1c320f82019-10-04 23:25:12 -070067 'vendor.build.prop' : {
Daniel Normanab5acef2020-01-08 17:01:11 -080068 'ro.product.vendor.brand' : 'vendor-product-brand',
69 'ro.product.vendor.name' : 'vendor-product-name',
70 'ro.product.vendor.device' : 'vendor-product-device',
71 'ro.vendor.build.version.release' : 'vendor-version-release',
72 'ro.vendor.build.id' : 'vendor-build-id',
73 'ro.vendor.build.version.incremental' : 'vendor-version-incremental',
74 'ro.vendor.build.type' : 'vendor-build-type',
75 'ro.vendor.build.tags' : 'vendor-build-tags',
Tao Bao1c320f82019-10-04 23:25:12 -070076 },
77 'property1' : 'value1',
78 'property2' : 4096,
79 }
80
81 TEST_INFO_DICT_USES_OEM_PROPS = {
82 'build.prop' : {
83 'ro.product.name' : 'product-name',
84 'ro.build.thumbprint' : 'build-thumbprint',
85 'ro.build.bar' : 'build-bar',
86 },
87 'vendor.build.prop' : {
88 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
89 },
90 'property1' : 'value1',
91 'property2' : 4096,
92 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
93 }
94
95 TEST_OEM_DICTS = [
96 {
97 'ro.product.brand' : 'brand1',
98 'ro.product.device' : 'device1',
99 },
100 {
101 'ro.product.brand' : 'brand2',
102 'ro.product.device' : 'device2',
103 },
104 {
105 'ro.product.brand' : 'brand3',
106 'ro.product.device' : 'device3',
107 },
108 ]
109
Steven Laverdd33d752020-04-27 16:26:31 -0700110 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
111 'build.prop' : {
112 'ro.build.fingerprint' : 'build-fingerprint',
113 'ro.product.property_source_order' :
114 'product,odm,vendor,system_ext,system',
115 },
116 'system.build.prop' : {
117 'ro.product.system.device' : 'system-product-device',
118 },
119 'vendor.build.prop' : {
120 'ro.product.vendor.device' : 'vendor-product-device',
121 },
122 }
123
124 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
125 'build.prop' : {
126 'ro.build.fingerprint' : 'build-fingerprint',
127 'ro.product.property_source_order' :
128 'product,product_services,odm,vendor,system',
129 'ro.build.version.release' : '10',
130 'ro.build.version.codename' : 'REL',
131 },
132 'system.build.prop' : {
133 'ro.product.system.device' : 'system-product-device',
134 },
135 'vendor.build.prop' : {
136 'ro.product.vendor.device' : 'vendor-product-device',
137 },
138 }
139
140 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
141 'build.prop' : {
142 'ro.product.device' : 'product-device',
143 'ro.build.fingerprint' : 'build-fingerprint',
144 'ro.build.version.release' : '9',
145 'ro.build.version.codename' : 'REL',
146 },
147 'system.build.prop' : {
148 'ro.product.system.device' : 'system-product-device',
149 },
150 'vendor.build.prop' : {
151 'ro.product.vendor.device' : 'vendor-product-device',
152 },
153 }
154
Tao Bao1c320f82019-10-04 23:25:12 -0700155 def test_init(self):
156 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
157 self.assertEqual('product-device', target_info.device)
158 self.assertEqual('build-fingerprint', target_info.fingerprint)
159 self.assertFalse(target_info.is_ab)
160 self.assertIsNone(target_info.oem_props)
161
162 def test_init_with_oem_props(self):
163 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
164 self.TEST_OEM_DICTS)
165 self.assertEqual('device1', target_info.device)
166 self.assertEqual('brand1/product-name/device1:build-thumbprint',
167 target_info.fingerprint)
168
169 # Swap the order in oem_dicts, which would lead to different BuildInfo.
170 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
171 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
172 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
173 oem_dicts)
174 self.assertEqual('device3', target_info.device)
175 self.assertEqual('brand3/product-name/device3:build-thumbprint',
176 target_info.fingerprint)
177
178 # Missing oem_dict should be rejected.
179 self.assertRaises(AssertionError, common.BuildInfo,
180 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
181
182 def test_init_badFingerprint(self):
183 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
184 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
185 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
186
187 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
188 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
189
190 def test___getitem__(self):
191 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
192 self.assertEqual('value1', target_info['property1'])
193 self.assertEqual(4096, target_info['property2'])
194 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
195
196 def test___getitem__with_oem_props(self):
197 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
198 self.TEST_OEM_DICTS)
199 self.assertEqual('value1', target_info['property1'])
200 self.assertEqual(4096, target_info['property2'])
201 self.assertRaises(KeyError,
202 lambda: target_info['build.prop']['ro.build.foo'])
203
204 def test___setitem__(self):
205 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
206 self.assertEqual('value1', target_info['property1'])
207 target_info['property1'] = 'value2'
208 self.assertEqual('value2', target_info['property1'])
209
210 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
211 target_info['build.prop']['ro.build.foo'] = 'build-bar'
212 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
213
214 def test_get(self):
215 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
216 self.assertEqual('value1', target_info.get('property1'))
217 self.assertEqual(4096, target_info.get('property2'))
218 self.assertEqual(4096, target_info.get('property2', 1024))
219 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
220 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
221
222 def test_get_with_oem_props(self):
223 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
224 self.TEST_OEM_DICTS)
225 self.assertEqual('value1', target_info.get('property1'))
226 self.assertEqual(4096, target_info.get('property2'))
227 self.assertEqual(4096, target_info.get('property2', 1024))
228 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
229 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
230 self.assertRaises(KeyError,
231 lambda: target_info.get('build.prop')['ro.build.foo'])
232
233 def test_items(self):
234 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
235 items = target_info.items()
236 self.assertIn(('property1', 'value1'), items)
237 self.assertIn(('property2', 4096), items)
238
239 def test_GetBuildProp(self):
240 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
241 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
242 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
243 'ro.build.nonexistent')
244
245 def test_GetBuildProp_with_oem_props(self):
246 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
247 self.TEST_OEM_DICTS)
248 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
249 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
250 'ro.build.nonexistent')
251
Daniel Normanab5acef2020-01-08 17:01:11 -0800252 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700253 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normanab5acef2020-01-08 17:01:11 -0800254 self.assertEqual(
255 target_info.GetPartitionFingerprint('vendor'),
256 'vendor-product-brand/vendor-product-name/vendor-product-device'
257 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
258 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700259
Daniel Normanab5acef2020-01-08 17:01:11 -0800260 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700261 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normanab5acef2020-01-08 17:01:11 -0800262 self.assertEqual(
263 target_info.GetPartitionFingerprint('system_other'),
264 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700265
Daniel Normanab5acef2020-01-08 17:01:11 -0800266 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
267 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
268 info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
269 target_info = common.BuildInfo(info_dict, None)
270 self.assertEqual(
271 target_info.GetPartitionFingerprint('vendor'),
272 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700273
274 def test_WriteMountOemScript(self):
275 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
276 self.TEST_OEM_DICTS)
277 script_writer = test_utils.MockScriptWriter()
278 target_info.WriteMountOemScript(script_writer)
279 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
280
281 def test_WriteDeviceAssertions(self):
282 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
283 script_writer = test_utils.MockScriptWriter()
284 target_info.WriteDeviceAssertions(script_writer, False)
285 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
286
287 def test_WriteDeviceAssertions_with_oem_props(self):
288 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
289 self.TEST_OEM_DICTS)
290 script_writer = test_utils.MockScriptWriter()
291 target_info.WriteDeviceAssertions(script_writer, False)
292 self.assertEqual(
293 [
294 ('AssertOemProperty', 'ro.product.device',
295 ['device1', 'device2', 'device3'], False),
296 ('AssertOemProperty', 'ro.product.brand',
297 ['brand1', 'brand2', 'brand3'], False),
298 ],
299 script_writer.lines)
300
Steven Laverdd33d752020-04-27 16:26:31 -0700301 def test_ResolveRoProductProperty_FromVendor(self):
302 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
303 info = common.BuildInfo(info_dict, None)
304 self.assertEqual('vendor-product-device',
305 info.GetBuildProp('ro.product.device'))
306
307 def test_ResolveRoProductProperty_FromSystem(self):
308 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
309 del info_dict['vendor.build.prop']['ro.product.vendor.device']
310 info = common.BuildInfo(info_dict, None)
311 self.assertEqual('system-product-device',
312 info.GetBuildProp('ro.product.device'))
313
314 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
315 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
316 info_dict['build.prop']['ro.product.property_source_order'] = 'bad-source'
317 with self.assertRaisesRegexp(common.ExternalError,
318 'Invalid ro.product.property_source_order'):
319 info = common.BuildInfo(info_dict, None)
320 info.GetBuildProp('ro.product.device')
321
322 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
323 info_dict = copy.deepcopy(
324 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
325 info = common.BuildInfo(info_dict, None)
326 self.assertEqual('vendor-product-device',
327 info.GetBuildProp('ro.product.device'))
328
329 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
330 info_dict = copy.deepcopy(
331 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
332 info = common.BuildInfo(info_dict, None)
333 self.assertEqual('product-device',
334 info.GetBuildProp('ro.product.device'))
335
Tao Bao1c320f82019-10-04 23:25:12 -0700336
Tao Bao65b94e92018-10-11 21:57:26 -0700337class CommonZipTest(test_utils.ReleaseToolsTestCase):
338
Tao Bao31b08072017-11-08 15:50:59 -0800339 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700340 test_file_name=None, expected_stat=None, expected_mode=0o644,
341 expected_compress_type=zipfile.ZIP_STORED):
342 # Verify the stat if present.
343 if test_file_name is not None:
344 new_stat = os.stat(test_file_name)
345 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
346 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
347
348 # Reopen the zip file to verify.
349 zip_file = zipfile.ZipFile(zip_file_name, "r")
350
351 # Verify the timestamp.
352 info = zip_file.getinfo(arcname)
353 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
354
355 # Verify the file mode.
356 mode = (info.external_attr >> 16) & 0o777
357 self.assertEqual(mode, expected_mode)
358
359 # Verify the compress type.
360 self.assertEqual(info.compress_type, expected_compress_type)
361
362 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800363 entry = zip_file.open(arcname)
364 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700365 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800366 sha1_hash.update(chunk)
367 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700368 self.assertIsNone(zip_file.testzip())
369
Dan Albert8e0178d2015-01-27 15:53:15 -0800370 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
371 extra_zipwrite_args = dict(extra_zipwrite_args or {})
372
373 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800374 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700375
376 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800377 zip_file_name = zip_file.name
378
379 # File names within an archive strip the leading slash.
380 arcname = extra_zipwrite_args.get("arcname", test_file_name)
381 if arcname[0] == "/":
382 arcname = arcname[1:]
383
384 zip_file.close()
385 zip_file = zipfile.ZipFile(zip_file_name, "w")
386
387 try:
Tao Bao31b08072017-11-08 15:50:59 -0800388 sha1_hash = sha1()
389 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700390 sha1_hash.update(bytes(data))
391 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800392 test_file.close()
393
Tao Baof3282b42015-04-01 11:21:55 -0700394 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800395 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700396 expected_compress_type = extra_zipwrite_args.get("compress_type",
397 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800398 time.sleep(5) # Make sure the atime/mtime will change measurably.
399
400 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700401 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800402
Tao Bao31b08072017-11-08 15:50:59 -0800403 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
404 test_file_name, expected_stat, expected_mode,
405 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800406 finally:
407 os.remove(test_file_name)
408 os.remove(zip_file_name)
409
Tao Baof3282b42015-04-01 11:21:55 -0700410 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
411 extra_args = dict(extra_args or {})
412
413 zip_file = tempfile.NamedTemporaryFile(delete=False)
414 zip_file_name = zip_file.name
415 zip_file.close()
416
417 zip_file = zipfile.ZipFile(zip_file_name, "w")
418
419 try:
420 expected_compress_type = extra_args.get("compress_type",
421 zipfile.ZIP_STORED)
422 time.sleep(5) # Make sure the atime/mtime will change measurably.
423
424 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700425 arcname = zinfo_or_arcname
426 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700427 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700428 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700429 if zinfo_or_arcname.external_attr:
430 zinfo_perms = zinfo_or_arcname.external_attr >> 16
431 else:
432 zinfo_perms = 0o600
433 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700434
Tao Bao58c1b962015-05-20 09:32:18 -0700435 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700436 common.ZipClose(zip_file)
437
Tao Bao31b08072017-11-08 15:50:59 -0800438 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700439 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700440 expected_compress_type=expected_compress_type)
441 finally:
442 os.remove(zip_file_name)
443
444 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
445 extra_args = dict(extra_args or {})
446
447 zip_file = tempfile.NamedTemporaryFile(delete=False)
448 zip_file_name = zip_file.name
449
450 test_file = tempfile.NamedTemporaryFile(delete=False)
451 test_file_name = test_file.name
452
453 arcname_large = test_file_name
454 arcname_small = "bar"
455
456 # File names within an archive strip the leading slash.
457 if arcname_large[0] == "/":
458 arcname_large = arcname_large[1:]
459
460 zip_file.close()
461 zip_file = zipfile.ZipFile(zip_file_name, "w")
462
463 try:
Tao Bao31b08072017-11-08 15:50:59 -0800464 sha1_hash = sha1()
465 for data in large:
466 sha1_hash.update(data)
467 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700468 test_file.close()
469
470 expected_stat = os.stat(test_file_name)
471 expected_mode = 0o644
472 expected_compress_type = extra_args.get("compress_type",
473 zipfile.ZIP_STORED)
474 time.sleep(5) # Make sure the atime/mtime will change measurably.
475
476 common.ZipWrite(zip_file, test_file_name, **extra_args)
477 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
478 common.ZipClose(zip_file)
479
480 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800481 self._verify(zip_file, zip_file_name, arcname_large,
482 sha1_hash.hexdigest(), test_file_name, expected_stat,
483 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700484
485 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800486 self._verify(zip_file, zip_file_name, arcname_small,
487 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700488 expected_compress_type=expected_compress_type)
489 finally:
490 os.remove(zip_file_name)
491 os.remove(test_file_name)
492
493 def _test_reset_ZIP64_LIMIT(self, func, *args):
494 default_limit = (1 << 31) - 1
495 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
496 func(*args)
497 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
498
Dan Albert8e0178d2015-01-27 15:53:15 -0800499 def test_ZipWrite(self):
500 file_contents = os.urandom(1024)
501 self._test_ZipWrite(file_contents)
502
503 def test_ZipWrite_with_opts(self):
504 file_contents = os.urandom(1024)
505 self._test_ZipWrite(file_contents, {
506 "arcname": "foobar",
507 "perms": 0o777,
508 "compress_type": zipfile.ZIP_DEFLATED,
509 })
Tao Baof3282b42015-04-01 11:21:55 -0700510 self._test_ZipWrite(file_contents, {
511 "arcname": "foobar",
512 "perms": 0o700,
513 "compress_type": zipfile.ZIP_STORED,
514 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800515
516 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700517 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800518 self._test_ZipWrite(file_contents, {
519 "compress_type": zipfile.ZIP_DEFLATED,
520 })
521
522 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700523 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
524
525 def test_ZipWriteStr(self):
526 random_string = os.urandom(1024)
527 # Passing arcname
528 self._test_ZipWriteStr("foo", random_string)
529
530 # Passing zinfo
531 zinfo = zipfile.ZipInfo(filename="foo")
532 self._test_ZipWriteStr(zinfo, random_string)
533
534 # Timestamp in the zinfo should be overwritten.
535 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
536 self._test_ZipWriteStr(zinfo, random_string)
537
538 def test_ZipWriteStr_with_opts(self):
539 random_string = os.urandom(1024)
540 # Passing arcname
541 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700542 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700543 "compress_type": zipfile.ZIP_DEFLATED,
544 })
Tao Bao58c1b962015-05-20 09:32:18 -0700545 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700546 "compress_type": zipfile.ZIP_STORED,
547 })
548
549 # Passing zinfo
550 zinfo = zipfile.ZipInfo(filename="foo")
551 self._test_ZipWriteStr(zinfo, random_string, {
552 "compress_type": zipfile.ZIP_DEFLATED,
553 })
554 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700555 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700556 "compress_type": zipfile.ZIP_STORED,
557 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700558 self._test_ZipWriteStr(zinfo, random_string, {
559 "perms": 0o000,
560 "compress_type": zipfile.ZIP_STORED,
561 })
Tao Baof3282b42015-04-01 11:21:55 -0700562
563 def test_ZipWriteStr_large_file(self):
564 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
565 # the workaround. We will only test the case of writing a string into a
566 # large archive.
567 long_string = get_2gb_string()
568 short_string = os.urandom(1024)
569 self._test_ZipWriteStr_large_file(long_string, short_string, {
570 "compress_type": zipfile.ZIP_DEFLATED,
571 })
572
573 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700574 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700575 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700576 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700577
578 def test_bug21309935(self):
579 zip_file = tempfile.NamedTemporaryFile(delete=False)
580 zip_file_name = zip_file.name
581 zip_file.close()
582
583 try:
584 random_string = os.urandom(1024)
585 zip_file = zipfile.ZipFile(zip_file_name, "w")
586 # Default perms should be 0o644 when passing the filename.
587 common.ZipWriteStr(zip_file, "foo", random_string)
588 # Honor the specified perms.
589 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
590 # The perms in zinfo should be untouched.
591 zinfo = zipfile.ZipInfo(filename="baz")
592 zinfo.external_attr = 0o740 << 16
593 common.ZipWriteStr(zip_file, zinfo, random_string)
594 # Explicitly specified perms has the priority.
595 zinfo = zipfile.ZipInfo(filename="qux")
596 zinfo.external_attr = 0o700 << 16
597 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
598 common.ZipClose(zip_file)
599
Tao Bao31b08072017-11-08 15:50:59 -0800600 self._verify(zip_file, zip_file_name, "foo",
601 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700602 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800603 self._verify(zip_file, zip_file_name, "bar",
604 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700605 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800606 self._verify(zip_file, zip_file_name, "baz",
607 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700608 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800609 self._verify(zip_file, zip_file_name, "qux",
610 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700611 expected_mode=0o400)
612 finally:
613 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700614
Tao Bao82490d32019-04-09 00:12:30 -0700615 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800616 def test_ZipDelete(self):
617 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
618 output_zip = zipfile.ZipFile(zip_file.name, 'w',
619 compression=zipfile.ZIP_DEFLATED)
620 with tempfile.NamedTemporaryFile() as entry_file:
621 entry_file.write(os.urandom(1024))
622 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
623 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
624 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
625 common.ZipClose(output_zip)
626 zip_file.close()
627
628 try:
629 common.ZipDelete(zip_file.name, 'Test2')
630 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
631 entries = check_zip.namelist()
632 self.assertTrue('Test1' in entries)
633 self.assertFalse('Test2' in entries)
634 self.assertTrue('Test3' in entries)
635
Tao Bao986ee862018-10-04 15:46:16 -0700636 self.assertRaises(
637 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800638 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
639 entries = check_zip.namelist()
640 self.assertTrue('Test1' in entries)
641 self.assertFalse('Test2' in entries)
642 self.assertTrue('Test3' in entries)
643
644 common.ZipDelete(zip_file.name, ['Test3'])
645 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
646 entries = check_zip.namelist()
647 self.assertTrue('Test1' in entries)
648 self.assertFalse('Test2' in entries)
649 self.assertFalse('Test3' in entries)
650
651 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
652 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
653 entries = check_zip.namelist()
654 self.assertFalse('Test1' in entries)
655 self.assertFalse('Test2' in entries)
656 self.assertFalse('Test3' in entries)
657 finally:
658 os.remove(zip_file.name)
659
Tao Bao0ff15de2019-03-20 11:26:06 -0700660 @staticmethod
661 def _test_UnzipTemp_createZipFile():
662 zip_file = common.MakeTempFile(suffix='.zip')
663 output_zip = zipfile.ZipFile(
664 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
665 contents = os.urandom(1024)
666 with tempfile.NamedTemporaryFile() as entry_file:
667 entry_file.write(contents)
668 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
669 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
670 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
671 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
672 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
673 common.ZipClose(output_zip)
674 common.ZipClose(output_zip)
675 return zip_file
676
Tao Bao82490d32019-04-09 00:12:30 -0700677 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700678 def test_UnzipTemp(self):
679 zip_file = self._test_UnzipTemp_createZipFile()
680 unzipped_dir = common.UnzipTemp(zip_file)
681 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
682 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
683 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
684 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
685 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
686
Tao Bao82490d32019-04-09 00:12:30 -0700687 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700688 def test_UnzipTemp_withPatterns(self):
689 zip_file = self._test_UnzipTemp_createZipFile()
690
691 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
692 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
693 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
694 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
695 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
696 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
697
698 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
699 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
700 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
701 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
702 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
703 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
704
705 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
706 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
707 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
708 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
709 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
710 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
711
712 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
713 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
714 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
715 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
716 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
717 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
718
719 def test_UnzipTemp_withEmptyPatterns(self):
720 zip_file = self._test_UnzipTemp_createZipFile()
721 unzipped_dir = common.UnzipTemp(zip_file, [])
722 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
723 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
724 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
725 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
726 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
727
Tao Bao82490d32019-04-09 00:12:30 -0700728 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700729 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
730 zip_file = self._test_UnzipTemp_createZipFile()
731 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
732 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
733 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
734 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
735 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
736 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
737
738 def test_UnzipTemp_withNoMatchingPatterns(self):
739 zip_file = self._test_UnzipTemp_createZipFile()
740 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
741 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
742 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
743 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
744 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
745 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
746
Tao Bao89d7ab22017-12-14 17:05:33 -0800747
Tao Bao65b94e92018-10-11 21:57:26 -0700748class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800749 """Tests the APK utils related functions."""
750
751 APKCERTS_TXT1 = (
752 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
753 ' private_key="certs/devkey.pk8"\n'
754 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700755 ' certificate="build/make/target/product/security/platform.x509.pem"'
756 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800757 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
758 )
759
760 APKCERTS_CERTMAP1 = {
761 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700762 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800763 'TV.apk' : 'PRESIGNED',
764 }
765
766 APKCERTS_TXT2 = (
767 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
768 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
769 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
770 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
771 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
772 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
773 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
774 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
775 )
776
777 APKCERTS_CERTMAP2 = {
778 'Compressed1.apk' : 'certs/compressed1',
779 'Compressed2a.apk' : 'certs/compressed2',
780 'Compressed2b.apk' : 'certs/compressed2',
781 'Compressed3.apk' : 'certs/compressed3',
782 }
783
784 APKCERTS_TXT3 = (
785 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
786 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
787 )
788
789 APKCERTS_CERTMAP3 = {
790 'Compressed4.apk' : 'certs/compressed4',
791 }
792
Bill Peckham96c9e6e2020-04-03 15:36:23 -0700793 # Test parsing with no optional fields, both optional fields, and only the
794 # partition optional field.
795 APKCERTS_TXT4 = (
796 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
797 ' private_key="certs/devkey.pk8"\n'
798 'name="Settings.apk"'
799 ' certificate="build/make/target/product/security/platform.x509.pem"'
800 ' private_key="build/make/target/product/security/platform.pk8"'
801 ' compressed="gz" partition="system"\n'
802 'name="TV.apk" certificate="PRESIGNED" private_key=""'
803 ' partition="product"\n'
804 )
805
806 APKCERTS_CERTMAP4 = {
807 'RecoveryLocalizer.apk' : 'certs/devkey',
808 'Settings.apk' : 'build/make/target/product/security/platform',
809 'TV.apk' : 'PRESIGNED',
810 }
811
Tao Bao17e4e612018-02-16 17:12:54 -0800812 def setUp(self):
813 self.testdata_dir = test_utils.get_testdata_dir()
814
Tao Bao818ddf52018-01-05 11:17:34 -0800815 @staticmethod
816 def _write_apkcerts_txt(apkcerts_txt, additional=None):
817 if additional is None:
818 additional = []
819 target_files = common.MakeTempFile(suffix='.zip')
820 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
821 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
822 for entry in additional:
823 target_files_zip.writestr(entry, '')
824 return target_files
825
826 def test_ReadApkCerts_NoncompressedApks(self):
827 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
828 with zipfile.ZipFile(target_files, 'r') as input_zip:
829 certmap, ext = common.ReadApkCerts(input_zip)
830
831 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
832 self.assertIsNone(ext)
833
834 def test_ReadApkCerts_CompressedApks(self):
835 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
836 # not stored in '.gz' format, so it shouldn't be considered as installed.
837 target_files = self._write_apkcerts_txt(
838 self.APKCERTS_TXT2,
839 ['Compressed1.apk.gz', 'Compressed3.apk'])
840
841 with zipfile.ZipFile(target_files, 'r') as input_zip:
842 certmap, ext = common.ReadApkCerts(input_zip)
843
844 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
845 self.assertEqual('.gz', ext)
846
847 # Alternative case with '.xz'.
848 target_files = self._write_apkcerts_txt(
849 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
850
851 with zipfile.ZipFile(target_files, 'r') as input_zip:
852 certmap, ext = common.ReadApkCerts(input_zip)
853
854 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
855 self.assertEqual('.xz', ext)
856
857 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
858 target_files = self._write_apkcerts_txt(
859 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
860 ['Compressed1.apk.gz', 'Compressed3.apk'])
861
862 with zipfile.ZipFile(target_files, 'r') as input_zip:
863 certmap, ext = common.ReadApkCerts(input_zip)
864
865 certmap_merged = self.APKCERTS_CERTMAP1.copy()
866 certmap_merged.update(self.APKCERTS_CERTMAP2)
867 self.assertDictEqual(certmap_merged, certmap)
868 self.assertEqual('.gz', ext)
869
870 def test_ReadApkCerts_MultipleCompressionMethods(self):
871 target_files = self._write_apkcerts_txt(
872 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
873 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
874
875 with zipfile.ZipFile(target_files, 'r') as input_zip:
876 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
877
878 def test_ReadApkCerts_MismatchingKeys(self):
879 malformed_apkcerts_txt = (
880 'name="App1.apk" certificate="certs/cert1.x509.pem"'
881 ' private_key="certs/cert2.pk8"\n'
882 )
883 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
884
885 with zipfile.ZipFile(target_files, 'r') as input_zip:
886 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
887
Bill Peckham96c9e6e2020-04-03 15:36:23 -0700888 def test_ReadApkCerts_WithWithoutOptionalFields(self):
889 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
890 with zipfile.ZipFile(target_files, 'r') as input_zip:
891 certmap, ext = common.ReadApkCerts(input_zip)
892
893 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
894 self.assertIsNone(ext)
895
Tao Bao04e1f012018-02-04 12:13:35 -0800896 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800897 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
898 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800899 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800900 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
901
902 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800903 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800904 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
905
Tao Bao82490d32019-04-09 00:12:30 -0700906 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700907 def test_ExtractAvbPublicKey(self):
908 privkey = os.path.join(self.testdata_dir, 'testkey.key')
909 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700910 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
911 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
912 with open(extracted_from_privkey, 'rb') as privkey_fp, \
913 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700914 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
915
Tao Bao17e4e612018-02-16 17:12:54 -0800916 def test_ParseCertificate(self):
917 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
918
919 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800920 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
921 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800922 expected, _ = proc.communicate()
923 self.assertEqual(0, proc.returncode)
924
925 with open(cert) as cert_fp:
926 actual = common.ParseCertificate(cert_fp.read())
927 self.assertEqual(expected, actual)
928
Tao Bao82490d32019-04-09 00:12:30 -0700929 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700930 def test_GetMinSdkVersion(self):
931 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
932 self.assertEqual('24', common.GetMinSdkVersion(test_app))
933
Tao Bao82490d32019-04-09 00:12:30 -0700934 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700935 def test_GetMinSdkVersion_invalidInput(self):
936 self.assertRaises(
937 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
938
Tao Bao82490d32019-04-09 00:12:30 -0700939 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700940 def test_GetMinSdkVersionInt(self):
941 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
942 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
943
Tao Bao82490d32019-04-09 00:12:30 -0700944 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700945 def test_GetMinSdkVersionInt_invalidInput(self):
946 self.assertRaises(
947 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
948 {})
949
Tao Bao818ddf52018-01-05 11:17:34 -0800950
Tao Bao65b94e92018-10-11 21:57:26 -0700951class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800952
Tao Bao02a08592018-07-22 12:40:45 -0700953 def setUp(self):
954 self.testdata_dir = test_utils.get_testdata_dir()
955
Tao Bao82490d32019-04-09 00:12:30 -0700956 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800957 def test_GetSparseImage_emptyBlockMapFile(self):
958 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
959 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
960 target_files_zip.write(
961 test_utils.construct_sparse_image([
962 (0xCAC1, 6),
963 (0xCAC3, 3),
964 (0xCAC1, 4)]),
965 arcname='IMAGES/system.img')
966 target_files_zip.writestr('IMAGES/system.map', '')
967 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
968 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
969
Tao Baodba59ee2018-01-09 13:21:02 -0800970 tempdir = common.UnzipTemp(target_files)
971 with zipfile.ZipFile(target_files, 'r') as input_zip:
972 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800973
974 self.assertDictEqual(
975 {
976 '__COPY': RangeSet("0"),
977 '__NONZERO-0': RangeSet("1-5 9-12"),
978 },
979 sparse_image.file_map)
980
Tao Baob2de7d92019-04-10 10:01:47 -0700981 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800982 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700983 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
984 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800985 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700986 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
987 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800988
Tao Bao82490d32019-04-09 00:12:30 -0700989 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800990 def test_GetSparseImage_missingBlockMapFile(self):
991 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
992 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
993 target_files_zip.write(
994 test_utils.construct_sparse_image([
995 (0xCAC1, 6),
996 (0xCAC3, 3),
997 (0xCAC1, 4)]),
998 arcname='IMAGES/system.img')
999 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1000 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1001
Tao Baodba59ee2018-01-09 13:21:02 -08001002 tempdir = common.UnzipTemp(target_files)
1003 with zipfile.ZipFile(target_files, 'r') as input_zip:
1004 self.assertRaises(
1005 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1006 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001007
Tao Bao82490d32019-04-09 00:12:30 -07001008 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001009 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1010 """Tests the case of having overlapping blocks but disallowed."""
1011 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1012 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1013 target_files_zip.write(
1014 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1015 arcname='IMAGES/system.img')
1016 # Block 10 is shared between two files.
1017 target_files_zip.writestr(
1018 'IMAGES/system.map',
1019 '\n'.join([
1020 '/system/file1 1-5 9-10',
1021 '/system/file2 10-12']))
1022 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1023 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1024
Tao Baodba59ee2018-01-09 13:21:02 -08001025 tempdir = common.UnzipTemp(target_files)
1026 with zipfile.ZipFile(target_files, 'r') as input_zip:
1027 self.assertRaises(
1028 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1029 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001030
Tao Bao82490d32019-04-09 00:12:30 -07001031 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001032 def test_GetSparseImage_sharedBlocks_allowed(self):
1033 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1034 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1035 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1036 # Construct an image with a care_map of "0-5 9-12".
1037 target_files_zip.write(
1038 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1039 arcname='IMAGES/system.img')
1040 # Block 10 is shared between two files.
1041 target_files_zip.writestr(
1042 'IMAGES/system.map',
1043 '\n'.join([
1044 '/system/file1 1-5 9-10',
1045 '/system/file2 10-12']))
1046 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1047 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1048
Tao Baodba59ee2018-01-09 13:21:02 -08001049 tempdir = common.UnzipTemp(target_files)
1050 with zipfile.ZipFile(target_files, 'r') as input_zip:
1051 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001052
1053 self.assertDictEqual(
1054 {
1055 '__COPY': RangeSet("0"),
1056 '__NONZERO-0': RangeSet("6-8 13-15"),
1057 '/system/file1': RangeSet("1-5 9-10"),
1058 '/system/file2': RangeSet("11-12"),
1059 },
1060 sparse_image.file_map)
1061
1062 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1063 # 'incomplete'.
1064 self.assertTrue(
1065 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1066 self.assertNotIn(
1067 'incomplete', sparse_image.file_map['/system/file2'].extra)
1068
Tao Baoa264fef2019-10-06 21:55:20 -07001069 # '/system/file1' will only contain one field -- a copy of the input text.
1070 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1071
1072 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001073 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1074 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001075
Tao Bao82490d32019-04-09 00:12:30 -07001076 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001077 def test_GetSparseImage_incompleteRanges(self):
1078 """Tests the case of ext4 images with holes."""
1079 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1080 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1081 target_files_zip.write(
1082 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1083 arcname='IMAGES/system.img')
1084 target_files_zip.writestr(
1085 'IMAGES/system.map',
1086 '\n'.join([
1087 '/system/file1 1-5 9-10',
1088 '/system/file2 11-12']))
1089 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1090 # '/system/file2' has less blocks listed (2) than actual (3).
1091 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1092
Tao Baodba59ee2018-01-09 13:21:02 -08001093 tempdir = common.UnzipTemp(target_files)
1094 with zipfile.ZipFile(target_files, 'r') as input_zip:
1095 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001096
Tao Baoa264fef2019-10-06 21:55:20 -07001097 self.assertEqual(
1098 '1-5 9-10',
1099 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001100 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1101
Tao Bao82490d32019-04-09 00:12:30 -07001102 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001103 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1104 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1105 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1106 target_files_zip.write(
1107 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1108 arcname='IMAGES/system.img')
1109 target_files_zip.writestr(
1110 'IMAGES/system.map',
1111 '\n'.join([
1112 '//system/file1 1-5 9-10',
1113 '//system/file2 11-12',
1114 '/system/app/file3 13-15']))
1115 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1116 # '/system/file2' has less blocks listed (2) than actual (3).
1117 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1118 # '/system/app/file3' has less blocks listed (3) than actual (4).
1119 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1120
1121 tempdir = common.UnzipTemp(target_files)
1122 with zipfile.ZipFile(target_files, 'r') as input_zip:
1123 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1124
Tao Baoa264fef2019-10-06 21:55:20 -07001125 self.assertEqual(
1126 '1-5 9-10',
1127 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001128 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1129 self.assertTrue(
1130 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1131
Tao Bao82490d32019-04-09 00:12:30 -07001132 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001133 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1134 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1135 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1136 target_files_zip.write(
1137 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1138 arcname='IMAGES/system.img')
1139 target_files_zip.writestr(
1140 'IMAGES/system.map',
1141 '\n'.join([
1142 '//system/file1 1-5 9-10',
1143 '//init.rc 13-15']))
1144 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1145 # '/init.rc' has less blocks listed (3) than actual (4).
1146 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1147
1148 tempdir = common.UnzipTemp(target_files)
1149 with zipfile.ZipFile(target_files, 'r') as input_zip:
1150 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1151
Tao Baoa264fef2019-10-06 21:55:20 -07001152 self.assertEqual(
1153 '1-5 9-10',
1154 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001155 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1156
Tao Bao82490d32019-04-09 00:12:30 -07001157 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001158 def test_GetSparseImage_fileNotFound(self):
1159 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1160 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1161 target_files_zip.write(
1162 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1163 arcname='IMAGES/system.img')
1164 target_files_zip.writestr(
1165 'IMAGES/system.map',
1166 '\n'.join([
1167 '//system/file1 1-5 9-10',
1168 '//system/file2 11-12']))
1169 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1170
1171 tempdir = common.UnzipTemp(target_files)
1172 with zipfile.ZipFile(target_files, 'r') as input_zip:
1173 self.assertRaises(
1174 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1175 False)
1176
Tao Bao82490d32019-04-09 00:12:30 -07001177 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001178 def test_GetAvbChainedPartitionArg(self):
1179 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1180 info_dict = {
1181 'avb_avbtool': 'avbtool',
1182 'avb_system_key_path': pubkey,
1183 'avb_system_rollback_index_location': 2,
1184 }
1185 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1186 self.assertEqual(3, len(args))
1187 self.assertEqual('system', args[0])
1188 self.assertEqual('2', args[1])
1189 self.assertTrue(os.path.exists(args[2]))
1190
Tao Bao82490d32019-04-09 00:12:30 -07001191 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001192 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1193 key = os.path.join(self.testdata_dir, 'testkey.key')
1194 info_dict = {
1195 'avb_avbtool': 'avbtool',
1196 'avb_product_key_path': key,
1197 'avb_product_rollback_index_location': 2,
1198 }
1199 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1200 self.assertEqual(3, len(args))
1201 self.assertEqual('product', args[0])
1202 self.assertEqual('2', args[1])
1203 self.assertTrue(os.path.exists(args[2]))
1204
Tao Bao82490d32019-04-09 00:12:30 -07001205 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001206 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1207 info_dict = {
1208 'avb_avbtool': 'avbtool',
1209 'avb_system_key_path': 'does-not-exist',
1210 'avb_system_rollback_index_location': 2,
1211 }
1212 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1213 args = common.GetAvbChainedPartitionArg(
1214 'system', info_dict, pubkey).split(':')
1215 self.assertEqual(3, len(args))
1216 self.assertEqual('system', args[0])
1217 self.assertEqual('2', args[1])
1218 self.assertTrue(os.path.exists(args[2]))
1219
Tao Bao82490d32019-04-09 00:12:30 -07001220 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001221 def test_GetAvbChainedPartitionArg_invalidKey(self):
1222 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1223 info_dict = {
1224 'avb_avbtool': 'avbtool',
1225 'avb_system_key_path': pubkey,
1226 'avb_system_rollback_index_location': 2,
1227 }
1228 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001229 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1230 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001231
Tao Baoa57ab9f2018-08-24 12:08:38 -07001232 INFO_DICT_DEFAULT = {
1233 'recovery_api_version': 3,
1234 'fstab_version': 2,
1235 'system_root_image': 'true',
1236 'no_recovery' : 'true',
1237 'recovery_as_boot': 'true',
1238 }
1239
Daniel Norman4cc9df62019-07-18 10:11:07 -07001240 def test_LoadListFromFile(self):
1241 file_path = os.path.join(self.testdata_dir,
1242 'merge_config_framework_item_list')
1243 contents = common.LoadListFromFile(file_path)
1244 expected_contents = [
1245 'META/apkcerts.txt',
1246 'META/filesystem_config.txt',
1247 'META/root_filesystem_config.txt',
1248 'META/system_manifest.xml',
1249 'META/system_matrix.xml',
1250 'META/update_engine_config.txt',
1251 'PRODUCT/*',
1252 'ROOT/*',
1253 'SYSTEM/*',
1254 ]
1255 self.assertEqual(sorted(contents), sorted(expected_contents))
1256
Tao Baoa57ab9f2018-08-24 12:08:38 -07001257 @staticmethod
1258 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1259 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1260 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1261 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001262 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001263 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1264
1265 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1266 if info_dict.get('system_root_image') == 'true':
1267 fstab_values = FSTAB_TEMPLATE.format('/')
1268 else:
1269 fstab_values = FSTAB_TEMPLATE.format('/system')
1270 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001271
1272 common.ZipWriteStr(
1273 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001274 return target_files
1275
1276 def test_LoadInfoDict(self):
1277 target_files = self._test_LoadInfoDict_createTargetFiles(
1278 self.INFO_DICT_DEFAULT,
1279 'BOOT/RAMDISK/system/etc/recovery.fstab')
1280 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1281 loaded_dict = common.LoadInfoDict(target_files_zip)
1282 self.assertEqual(3, loaded_dict['recovery_api_version'])
1283 self.assertEqual(2, loaded_dict['fstab_version'])
1284 self.assertIn('/', loaded_dict['fstab'])
1285 self.assertIn('/system', loaded_dict['fstab'])
1286
1287 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1288 target_files = self._test_LoadInfoDict_createTargetFiles(
1289 self.INFO_DICT_DEFAULT,
1290 'BOOT/RAMDISK/etc/recovery.fstab')
1291 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1292 loaded_dict = common.LoadInfoDict(target_files_zip)
1293 self.assertEqual(3, loaded_dict['recovery_api_version'])
1294 self.assertEqual(2, loaded_dict['fstab_version'])
1295 self.assertIn('/', loaded_dict['fstab'])
1296 self.assertIn('/system', loaded_dict['fstab'])
1297
Tao Bao82490d32019-04-09 00:12:30 -07001298 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001299 def test_LoadInfoDict_dirInput(self):
1300 target_files = self._test_LoadInfoDict_createTargetFiles(
1301 self.INFO_DICT_DEFAULT,
1302 'BOOT/RAMDISK/system/etc/recovery.fstab')
1303 unzipped = common.UnzipTemp(target_files)
1304 loaded_dict = common.LoadInfoDict(unzipped)
1305 self.assertEqual(3, loaded_dict['recovery_api_version'])
1306 self.assertEqual(2, loaded_dict['fstab_version'])
1307 self.assertIn('/', loaded_dict['fstab'])
1308 self.assertIn('/system', loaded_dict['fstab'])
1309
Tao Bao82490d32019-04-09 00:12:30 -07001310 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001311 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1312 target_files = self._test_LoadInfoDict_createTargetFiles(
1313 self.INFO_DICT_DEFAULT,
1314 'BOOT/RAMDISK/system/etc/recovery.fstab')
1315 unzipped = common.UnzipTemp(target_files)
1316 loaded_dict = common.LoadInfoDict(unzipped)
1317 self.assertEqual(3, loaded_dict['recovery_api_version'])
1318 self.assertEqual(2, loaded_dict['fstab_version'])
1319 self.assertIn('/', loaded_dict['fstab'])
1320 self.assertIn('/system', loaded_dict['fstab'])
1321
1322 def test_LoadInfoDict_systemRootImageFalse(self):
1323 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1324 # launched prior to P will likely have this config.
1325 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1326 del info_dict['no_recovery']
1327 del info_dict['system_root_image']
1328 del info_dict['recovery_as_boot']
1329 target_files = self._test_LoadInfoDict_createTargetFiles(
1330 info_dict,
1331 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1332 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1333 loaded_dict = common.LoadInfoDict(target_files_zip)
1334 self.assertEqual(3, loaded_dict['recovery_api_version'])
1335 self.assertEqual(2, loaded_dict['fstab_version'])
1336 self.assertNotIn('/', loaded_dict['fstab'])
1337 self.assertIn('/system', loaded_dict['fstab'])
1338
1339 def test_LoadInfoDict_recoveryAsBootFalse(self):
1340 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1341 # devices launched since P will likely have this config.
1342 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1343 del info_dict['no_recovery']
1344 del info_dict['recovery_as_boot']
1345 target_files = self._test_LoadInfoDict_createTargetFiles(
1346 info_dict,
1347 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1348 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1349 loaded_dict = common.LoadInfoDict(target_files_zip)
1350 self.assertEqual(3, loaded_dict['recovery_api_version'])
1351 self.assertEqual(2, loaded_dict['fstab_version'])
1352 self.assertIn('/', loaded_dict['fstab'])
1353 self.assertIn('/system', loaded_dict['fstab'])
1354
1355 def test_LoadInfoDict_noRecoveryTrue(self):
1356 # Device doesn't have a recovery partition at all.
1357 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1358 del info_dict['recovery_as_boot']
1359 target_files = self._test_LoadInfoDict_createTargetFiles(
1360 info_dict,
1361 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1362 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1363 loaded_dict = common.LoadInfoDict(target_files_zip)
1364 self.assertEqual(3, loaded_dict['recovery_api_version'])
1365 self.assertEqual(2, loaded_dict['fstab_version'])
1366 self.assertIsNone(loaded_dict['fstab'])
1367
Tao Bao82490d32019-04-09 00:12:30 -07001368 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001369 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1370 target_files = self._test_LoadInfoDict_createTargetFiles(
1371 self.INFO_DICT_DEFAULT,
1372 'BOOT/RAMDISK/system/etc/recovery.fstab')
1373 common.ZipDelete(target_files, 'META/misc_info.txt')
1374 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1375 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1376
Tao Bao82490d32019-04-09 00:12:30 -07001377 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001378 def test_LoadInfoDict_repacking(self):
1379 target_files = self._test_LoadInfoDict_createTargetFiles(
1380 self.INFO_DICT_DEFAULT,
1381 'BOOT/RAMDISK/system/etc/recovery.fstab')
1382 unzipped = common.UnzipTemp(target_files)
1383 loaded_dict = common.LoadInfoDict(unzipped, True)
1384 self.assertEqual(3, loaded_dict['recovery_api_version'])
1385 self.assertEqual(2, loaded_dict['fstab_version'])
1386 self.assertIn('/', loaded_dict['fstab'])
1387 self.assertIn('/system', loaded_dict['fstab'])
1388 self.assertEqual(
1389 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1390 self.assertEqual(
1391 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1392 loaded_dict['root_fs_config'])
1393
1394 def test_LoadInfoDict_repackingWithZipFileInput(self):
1395 target_files = self._test_LoadInfoDict_createTargetFiles(
1396 self.INFO_DICT_DEFAULT,
1397 'BOOT/RAMDISK/system/etc/recovery.fstab')
1398 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1399 self.assertRaises(
1400 AssertionError, common.LoadInfoDict, target_files_zip, True)
1401
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001402 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1403 framework_dict = {
1404 'super_partition_groups': 'group_a',
1405 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001406 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001407 }
1408 vendor_dict = {
1409 'super_partition_groups': 'group_a group_b',
1410 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001411 'super_group_a_partition_list': 'vendor',
1412 'super_group_a_group_size': '1000',
1413 'super_group_b_partition_list': 'product',
1414 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001415 }
1416 merged_dict = common.MergeDynamicPartitionInfoDicts(
1417 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001418 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001419 expected_merged_dict = {
1420 'super_partition_groups': 'group_a group_b',
1421 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001422 'super_group_a_partition_list': 'system vendor',
1423 'super_group_a_group_size': '1000',
1424 'super_group_b_partition_list': 'product',
1425 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001426 }
1427 self.assertEqual(merged_dict, expected_merged_dict)
1428
1429 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1430 framework_dict = {
1431 'super_partition_groups': 'group_a',
1432 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001433 'super_group_a_partition_list': 'system',
1434 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001435 }
1436 vendor_dict = {
1437 'super_partition_groups': 'group_a group_b',
1438 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001439 'super_group_a_partition_list': 'vendor',
1440 'super_group_a_group_size': '1000',
1441 'super_group_b_partition_list': 'product',
1442 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001443 }
1444 merged_dict = common.MergeDynamicPartitionInfoDicts(
1445 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001446 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001447 expected_merged_dict = {
1448 'super_partition_groups': 'group_a group_b',
1449 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001450 'super_group_a_partition_list': 'system vendor',
1451 'super_group_a_group_size': '1000',
1452 'super_group_b_partition_list': 'product',
1453 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001454 }
1455 self.assertEqual(merged_dict, expected_merged_dict)
1456
Daniel Norman276f0622019-07-26 14:13:51 -07001457 def test_GetAvbPartitionArg(self):
1458 info_dict = {}
1459 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1460 self.assertEqual(
1461 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1462
1463 @test_utils.SkipIfExternalToolsUnavailable()
1464 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1465 testdata_dir = test_utils.get_testdata_dir()
1466 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1467 info_dict = {
1468 'avb_avbtool': 'avbtool',
1469 'avb_vendor_key_path': pubkey,
1470 'avb_vendor_rollback_index_location': 5,
1471 }
1472 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1473 self.assertEqual(2, len(cmd))
1474 self.assertEqual('--chain_partition', cmd[0])
1475 chained_partition_args = cmd[1].split(':')
1476 self.assertEqual(3, len(chained_partition_args))
1477 self.assertEqual('vendor', chained_partition_args[0])
1478 self.assertEqual('5', chained_partition_args[1])
1479 self.assertTrue(os.path.exists(chained_partition_args[2]))
1480
Tao Bao3612c882019-10-14 17:49:31 -07001481 @test_utils.SkipIfExternalToolsUnavailable()
1482 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1483 testdata_dir = test_utils.get_testdata_dir()
1484 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1485 info_dict = {
1486 'avb_avbtool': 'avbtool',
1487 'avb_recovery_key_path': pubkey,
1488 'avb_recovery_rollback_index_location': 3,
1489 }
1490 cmd = common.GetAvbPartitionArg(
1491 'recovery', '/path/to/recovery.img', info_dict)
1492 self.assertFalse(cmd)
1493
1494 @test_utils.SkipIfExternalToolsUnavailable()
1495 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1496 testdata_dir = test_utils.get_testdata_dir()
1497 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1498 info_dict = {
1499 'ab_update': 'true',
1500 'avb_avbtool': 'avbtool',
1501 'avb_recovery_key_path': pubkey,
1502 'avb_recovery_rollback_index_location': 3,
1503 }
1504 cmd = common.GetAvbPartitionArg(
1505 'recovery', '/path/to/recovery.img', info_dict)
1506 self.assertEqual(2, len(cmd))
1507 self.assertEqual('--chain_partition', cmd[0])
1508 chained_partition_args = cmd[1].split(':')
1509 self.assertEqual(3, len(chained_partition_args))
1510 self.assertEqual('recovery', chained_partition_args[0])
1511 self.assertEqual('3', chained_partition_args[1])
1512 self.assertTrue(os.path.exists(chained_partition_args[2]))
1513
Tao Baofc7e0e02018-02-13 13:54:02 -08001514
Tao Bao65b94e92018-10-11 21:57:26 -07001515class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001516 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001517
Tao Bao1c830bf2017-12-25 10:43:47 -08001518 Its format should match between common.py and validate_target_files.py.
1519 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001520
1521 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001522 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001523 # Create a dummy dict that contains the fstab info for boot&recovery.
1524 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001525 dummy_fstab = [
1526 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1527 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001528 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001529 # Construct the gzipped recovery.img and boot.img
1530 self.recovery_data = bytearray([
1531 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1532 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1533 0x08, 0x00, 0x00, 0x00
1534 ])
1535 # echo -n "boot" | gzip -f | hd
1536 self.boot_data = bytearray([
1537 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1538 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1539 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001540
1541 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1542 loc = os.path.join(self._tempdir, prefix, name)
1543 if not os.path.exists(os.path.dirname(loc)):
1544 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001545 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001546 f.write(data)
1547
1548 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001549 recovery_image = common.File("recovery.img", self.recovery_data)
1550 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001551 self._info["full_recovery_image"] = "true"
1552
1553 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1554 recovery_image, boot_image, self._info)
1555 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1556 self._info)
1557
Tao Bao82490d32019-04-09 00:12:30 -07001558 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001559 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001560 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001561 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001562 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001563 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1564
1565 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1566 recovery_image, boot_image, self._info)
1567 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1568 self._info)
1569 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001570 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001571 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1572 recovery_image, boot_image, self._info)
1573 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1574 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001575
1576
Yifan Hong45433e42019-01-18 13:55:25 -08001577class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001578
Yifan Hong45433e42019-01-18 13:55:25 -08001579 def __init__(self, partition, tgt, src=None):
1580 self.partition = partition
1581 self.tgt = tgt
1582 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001583
Yifan Hong45433e42019-01-18 13:55:25 -08001584 def WriteScript(self, script, _, progress=None,
1585 write_verify_script=False):
1586 if progress:
1587 script.AppendExtra("progress({})".format(progress))
1588 script.AppendExtra("patch({});".format(self.partition))
1589 if write_verify_script:
1590 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001591
Yifan Hong45433e42019-01-18 13:55:25 -08001592 def WritePostInstallVerifyScript(self, script):
1593 script.AppendExtra("verify({});".format(self.partition))
1594
1595
1596class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001597
Yifan Hong45433e42019-01-18 13:55:25 -08001598 def __init__(self, size):
1599 self.blocksize = 4096
1600 self.total_blocks = size // 4096
1601 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1602
1603
1604class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001605
Yifan Hong45433e42019-01-18 13:55:25 -08001606 @staticmethod
1607 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001608 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001609 with output_zip.open('dynamic_partitions_op_list') as op_list:
1610 return [line.decode().strip() for line in op_list.readlines()
1611 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001612
1613 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001614 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001615 self.output_path = common.MakeTempFile(suffix='.zip')
1616
1617 def test_full(self):
1618 target_info = common.LoadDictionaryFromLines("""
1619dynamic_partition_list=system vendor
1620super_partition_groups=group_foo
1621super_group_foo_group_size={group_size}
1622super_group_foo_partition_list=system vendor
1623""".format(group_size=4 * GiB).split("\n"))
1624 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1625 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1626
1627 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1628 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1629 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1630
1631 self.assertEqual(str(self.script).strip(), """
1632assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001633patch(system);
1634verify(system);
1635unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001636patch(vendor);
1637verify(vendor);
1638unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001639""".strip())
1640
1641 lines = self.get_op_list(self.output_path)
1642
1643 remove_all_groups = lines.index("remove_all_groups")
1644 add_group = lines.index("add_group group_foo 4294967296")
1645 add_vendor = lines.index("add vendor group_foo")
1646 add_system = lines.index("add system group_foo")
1647 resize_vendor = lines.index("resize vendor 1073741824")
1648 resize_system = lines.index("resize system 3221225472")
1649
1650 self.assertLess(remove_all_groups, add_group,
1651 "Should add groups after removing all groups")
1652 self.assertLess(add_group, min(add_vendor, add_system),
1653 "Should add partitions after adding group")
1654 self.assertLess(add_system, resize_system,
1655 "Should resize system after adding it")
1656 self.assertLess(add_vendor, resize_vendor,
1657 "Should resize vendor after adding it")
1658
1659 def test_inc_groups(self):
1660 source_info = common.LoadDictionaryFromLines("""
1661super_partition_groups=group_foo group_bar group_baz
1662super_group_foo_group_size={group_foo_size}
1663super_group_bar_group_size={group_bar_size}
1664""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1665 target_info = common.LoadDictionaryFromLines("""
1666super_partition_groups=group_foo group_baz group_qux
1667super_group_foo_group_size={group_foo_size}
1668super_group_baz_group_size={group_baz_size}
1669super_group_qux_group_size={group_qux_size}
1670""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1671 group_qux_size=1 * GiB).split("\n"))
1672
1673 dp_diff = common.DynamicPartitionsDifference(target_info,
1674 block_diffs=[],
1675 source_info_dict=source_info)
1676 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1677 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1678
1679 lines = self.get_op_list(self.output_path)
1680
1681 removed = lines.index("remove_group group_bar")
1682 shrunk = lines.index("resize_group group_foo 3221225472")
1683 grown = lines.index("resize_group group_baz 4294967296")
1684 added = lines.index("add_group group_qux 1073741824")
1685
Tao Baof1113e92019-06-18 12:10:14 -07001686 self.assertLess(max(removed, shrunk),
1687 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001688 "ops that remove / shrink partitions must precede ops that "
1689 "grow / add partitions")
1690
Yifan Hongbb2658d2019-01-25 12:30:58 -08001691 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001692 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001693dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001694super_partition_groups=group_foo
1695super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001696super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001697""".format(group_foo_size=4 * GiB).split("\n"))
1698 target_info = common.LoadDictionaryFromLines("""
1699dynamic_partition_list=system vendor product odm
1700super_partition_groups=group_foo group_bar
1701super_group_foo_group_size={group_foo_size}
1702super_group_foo_partition_list=system vendor odm
1703super_group_bar_group_size={group_bar_size}
1704super_group_bar_partition_list=product
1705""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1706
1707 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1708 src=FakeSparseImage(1024 * MiB)),
1709 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1710 src=FakeSparseImage(1024 * MiB)),
1711 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1712 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001713 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001714 src=FakeSparseImage(1024 * MiB)),
1715 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1716 src=None)]
1717
1718 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1719 source_info_dict=source_info)
1720 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1721 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1722
1723 metadata_idx = self.script.lines.index(
1724 'assert(update_dynamic_partitions(package_extract_file('
1725 '"dynamic_partitions_op_list")));')
1726 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1727 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1728 for p in ("product", "system", "odm"):
1729 patch_idx = self.script.lines.index("patch({});".format(p))
1730 verify_idx = self.script.lines.index("verify({});".format(p))
1731 self.assertLess(metadata_idx, patch_idx,
1732 "Should patch {} after updating metadata".format(p))
1733 self.assertLess(patch_idx, verify_idx,
1734 "Should verify {} after patching".format(p))
1735
Justin Yun6151e3f2019-06-25 15:58:13 +09001736 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001737
1738 lines = self.get_op_list(self.output_path)
1739
Justin Yun6151e3f2019-06-25 15:58:13 +09001740 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001741 move_product_out = lines.index("move product default")
1742 shrink = lines.index("resize vendor 536870912")
1743 shrink_group = lines.index("resize_group group_foo 3221225472")
1744 add_group_bar = lines.index("add_group group_bar 1073741824")
1745 add_odm = lines.index("add odm group_foo")
1746 grow_existing = lines.index("resize system 1610612736")
1747 grow_added = lines.index("resize odm 1073741824")
1748 move_product_in = lines.index("move product group_bar")
1749
1750 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1751 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1752
1753 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1754 "Must shrink group after partitions inside group are shrunk"
1755 " / removed")
1756
1757 self.assertLess(add_group_bar, move_product_in,
1758 "Must add partitions to group after group is added")
1759
1760 self.assertLess(max_idx_move_partition_out_foo,
1761 min_idx_move_partition_in_foo,
1762 "Must shrink partitions / remove partitions from group"
1763 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001764
1765 def test_remove_partition(self):
1766 source_info = common.LoadDictionaryFromLines("""
1767blockimgdiff_versions=3,4
1768use_dynamic_partitions=true
1769dynamic_partition_list=foo
1770super_partition_groups=group_foo
1771super_group_foo_group_size={group_foo_size}
1772super_group_foo_partition_list=foo
1773""".format(group_foo_size=4 * GiB).split("\n"))
1774 target_info = common.LoadDictionaryFromLines("""
1775blockimgdiff_versions=3,4
1776use_dynamic_partitions=true
1777super_partition_groups=group_foo
1778super_group_foo_group_size={group_foo_size}
1779""".format(group_foo_size=4 * GiB).split("\n"))
1780
1781 common.OPTIONS.info_dict = target_info
1782 common.OPTIONS.target_info_dict = target_info
1783 common.OPTIONS.source_info_dict = source_info
1784 common.OPTIONS.cache_size = 4 * 4096
1785
1786 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1787 src=DataImage("source", pad=True))]
1788
1789 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1790 source_info_dict=source_info)
1791 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1792 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1793
1794 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001795 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001796
1797 lines = self.get_op_list(self.output_path)
1798 self.assertEqual(lines, ["remove foo"])