blob: 7058da74444ade7cec5ecb76d561a48a0bee4a88 [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 Normand5fe8622020-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 Normand5fe8622020-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
110 def test_init(self):
111 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
112 self.assertEqual('product-device', target_info.device)
113 self.assertEqual('build-fingerprint', target_info.fingerprint)
114 self.assertFalse(target_info.is_ab)
115 self.assertIsNone(target_info.oem_props)
116
117 def test_init_with_oem_props(self):
118 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
119 self.TEST_OEM_DICTS)
120 self.assertEqual('device1', target_info.device)
121 self.assertEqual('brand1/product-name/device1:build-thumbprint',
122 target_info.fingerprint)
123
124 # Swap the order in oem_dicts, which would lead to different BuildInfo.
125 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
126 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
127 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
128 oem_dicts)
129 self.assertEqual('device3', target_info.device)
130 self.assertEqual('brand3/product-name/device3:build-thumbprint',
131 target_info.fingerprint)
132
133 # Missing oem_dict should be rejected.
134 self.assertRaises(AssertionError, common.BuildInfo,
135 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
136
137 def test_init_badFingerprint(self):
138 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
139 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
140 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
141
142 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
143 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
144
145 def test___getitem__(self):
146 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
147 self.assertEqual('value1', target_info['property1'])
148 self.assertEqual(4096, target_info['property2'])
149 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
150
151 def test___getitem__with_oem_props(self):
152 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
153 self.TEST_OEM_DICTS)
154 self.assertEqual('value1', target_info['property1'])
155 self.assertEqual(4096, target_info['property2'])
156 self.assertRaises(KeyError,
157 lambda: target_info['build.prop']['ro.build.foo'])
158
159 def test___setitem__(self):
160 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
161 self.assertEqual('value1', target_info['property1'])
162 target_info['property1'] = 'value2'
163 self.assertEqual('value2', target_info['property1'])
164
165 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
166 target_info['build.prop']['ro.build.foo'] = 'build-bar'
167 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
168
169 def test_get(self):
170 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
171 self.assertEqual('value1', target_info.get('property1'))
172 self.assertEqual(4096, target_info.get('property2'))
173 self.assertEqual(4096, target_info.get('property2', 1024))
174 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
175 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
176
177 def test_get_with_oem_props(self):
178 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
179 self.TEST_OEM_DICTS)
180 self.assertEqual('value1', target_info.get('property1'))
181 self.assertEqual(4096, target_info.get('property2'))
182 self.assertEqual(4096, target_info.get('property2', 1024))
183 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
184 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
185 self.assertRaises(KeyError,
186 lambda: target_info.get('build.prop')['ro.build.foo'])
187
188 def test_items(self):
189 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
190 items = target_info.items()
191 self.assertIn(('property1', 'value1'), items)
192 self.assertIn(('property2', 4096), items)
193
194 def test_GetBuildProp(self):
195 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
196 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
197 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
198 'ro.build.nonexistent')
199
200 def test_GetBuildProp_with_oem_props(self):
201 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
202 self.TEST_OEM_DICTS)
203 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
204 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
205 'ro.build.nonexistent')
206
Daniel Normand5fe8622020-01-08 17:01:11 -0800207 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700208 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800209 self.assertEqual(
210 target_info.GetPartitionFingerprint('vendor'),
211 'vendor-product-brand/vendor-product-name/vendor-product-device'
212 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
213 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700214
Daniel Normand5fe8622020-01-08 17:01:11 -0800215 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700216 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800217 self.assertEqual(
218 target_info.GetPartitionFingerprint('system_other'),
219 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700220
Daniel Normand5fe8622020-01-08 17:01:11 -0800221 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
222 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
223 info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
224 target_info = common.BuildInfo(info_dict, None)
225 self.assertEqual(
226 target_info.GetPartitionFingerprint('vendor'),
227 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700228
229 def test_WriteMountOemScript(self):
230 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
231 self.TEST_OEM_DICTS)
232 script_writer = test_utils.MockScriptWriter()
233 target_info.WriteMountOemScript(script_writer)
234 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
235
236 def test_WriteDeviceAssertions(self):
237 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
238 script_writer = test_utils.MockScriptWriter()
239 target_info.WriteDeviceAssertions(script_writer, False)
240 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
241
242 def test_WriteDeviceAssertions_with_oem_props(self):
243 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
244 self.TEST_OEM_DICTS)
245 script_writer = test_utils.MockScriptWriter()
246 target_info.WriteDeviceAssertions(script_writer, False)
247 self.assertEqual(
248 [
249 ('AssertOemProperty', 'ro.product.device',
250 ['device1', 'device2', 'device3'], False),
251 ('AssertOemProperty', 'ro.product.brand',
252 ['brand1', 'brand2', 'brand3'], False),
253 ],
254 script_writer.lines)
255
256
Tao Bao65b94e92018-10-11 21:57:26 -0700257class CommonZipTest(test_utils.ReleaseToolsTestCase):
258
Tao Bao31b08072017-11-08 15:50:59 -0800259 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700260 test_file_name=None, expected_stat=None, expected_mode=0o644,
261 expected_compress_type=zipfile.ZIP_STORED):
262 # Verify the stat if present.
263 if test_file_name is not None:
264 new_stat = os.stat(test_file_name)
265 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
266 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
267
268 # Reopen the zip file to verify.
269 zip_file = zipfile.ZipFile(zip_file_name, "r")
270
271 # Verify the timestamp.
272 info = zip_file.getinfo(arcname)
273 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
274
275 # Verify the file mode.
276 mode = (info.external_attr >> 16) & 0o777
277 self.assertEqual(mode, expected_mode)
278
279 # Verify the compress type.
280 self.assertEqual(info.compress_type, expected_compress_type)
281
282 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800283 entry = zip_file.open(arcname)
284 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700285 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800286 sha1_hash.update(chunk)
287 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700288 self.assertIsNone(zip_file.testzip())
289
Dan Albert8e0178d2015-01-27 15:53:15 -0800290 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
291 extra_zipwrite_args = dict(extra_zipwrite_args or {})
292
293 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800294 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700295
296 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800297 zip_file_name = zip_file.name
298
299 # File names within an archive strip the leading slash.
300 arcname = extra_zipwrite_args.get("arcname", test_file_name)
301 if arcname[0] == "/":
302 arcname = arcname[1:]
303
304 zip_file.close()
305 zip_file = zipfile.ZipFile(zip_file_name, "w")
306
307 try:
Tao Bao31b08072017-11-08 15:50:59 -0800308 sha1_hash = sha1()
309 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700310 sha1_hash.update(bytes(data))
311 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800312 test_file.close()
313
Tao Baof3282b42015-04-01 11:21:55 -0700314 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800315 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700316 expected_compress_type = extra_zipwrite_args.get("compress_type",
317 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800318 time.sleep(5) # Make sure the atime/mtime will change measurably.
319
320 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700321 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800322
Tao Bao31b08072017-11-08 15:50:59 -0800323 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
324 test_file_name, expected_stat, expected_mode,
325 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800326 finally:
327 os.remove(test_file_name)
328 os.remove(zip_file_name)
329
Tao Baof3282b42015-04-01 11:21:55 -0700330 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
331 extra_args = dict(extra_args or {})
332
333 zip_file = tempfile.NamedTemporaryFile(delete=False)
334 zip_file_name = zip_file.name
335 zip_file.close()
336
337 zip_file = zipfile.ZipFile(zip_file_name, "w")
338
339 try:
340 expected_compress_type = extra_args.get("compress_type",
341 zipfile.ZIP_STORED)
342 time.sleep(5) # Make sure the atime/mtime will change measurably.
343
344 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700345 arcname = zinfo_or_arcname
346 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700347 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700348 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700349 if zinfo_or_arcname.external_attr:
350 zinfo_perms = zinfo_or_arcname.external_attr >> 16
351 else:
352 zinfo_perms = 0o600
353 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700354
Tao Bao58c1b962015-05-20 09:32:18 -0700355 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700356 common.ZipClose(zip_file)
357
Tao Bao31b08072017-11-08 15:50:59 -0800358 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700359 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700360 expected_compress_type=expected_compress_type)
361 finally:
362 os.remove(zip_file_name)
363
364 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
365 extra_args = dict(extra_args or {})
366
367 zip_file = tempfile.NamedTemporaryFile(delete=False)
368 zip_file_name = zip_file.name
369
370 test_file = tempfile.NamedTemporaryFile(delete=False)
371 test_file_name = test_file.name
372
373 arcname_large = test_file_name
374 arcname_small = "bar"
375
376 # File names within an archive strip the leading slash.
377 if arcname_large[0] == "/":
378 arcname_large = arcname_large[1:]
379
380 zip_file.close()
381 zip_file = zipfile.ZipFile(zip_file_name, "w")
382
383 try:
Tao Bao31b08072017-11-08 15:50:59 -0800384 sha1_hash = sha1()
385 for data in large:
386 sha1_hash.update(data)
387 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700388 test_file.close()
389
390 expected_stat = os.stat(test_file_name)
391 expected_mode = 0o644
392 expected_compress_type = extra_args.get("compress_type",
393 zipfile.ZIP_STORED)
394 time.sleep(5) # Make sure the atime/mtime will change measurably.
395
396 common.ZipWrite(zip_file, test_file_name, **extra_args)
397 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
398 common.ZipClose(zip_file)
399
400 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800401 self._verify(zip_file, zip_file_name, arcname_large,
402 sha1_hash.hexdigest(), test_file_name, expected_stat,
403 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700404
405 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800406 self._verify(zip_file, zip_file_name, arcname_small,
407 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700408 expected_compress_type=expected_compress_type)
409 finally:
410 os.remove(zip_file_name)
411 os.remove(test_file_name)
412
413 def _test_reset_ZIP64_LIMIT(self, func, *args):
414 default_limit = (1 << 31) - 1
415 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
416 func(*args)
417 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
418
Dan Albert8e0178d2015-01-27 15:53:15 -0800419 def test_ZipWrite(self):
420 file_contents = os.urandom(1024)
421 self._test_ZipWrite(file_contents)
422
423 def test_ZipWrite_with_opts(self):
424 file_contents = os.urandom(1024)
425 self._test_ZipWrite(file_contents, {
426 "arcname": "foobar",
427 "perms": 0o777,
428 "compress_type": zipfile.ZIP_DEFLATED,
429 })
Tao Baof3282b42015-04-01 11:21:55 -0700430 self._test_ZipWrite(file_contents, {
431 "arcname": "foobar",
432 "perms": 0o700,
433 "compress_type": zipfile.ZIP_STORED,
434 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800435
436 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700437 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800438 self._test_ZipWrite(file_contents, {
439 "compress_type": zipfile.ZIP_DEFLATED,
440 })
441
442 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700443 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
444
445 def test_ZipWriteStr(self):
446 random_string = os.urandom(1024)
447 # Passing arcname
448 self._test_ZipWriteStr("foo", random_string)
449
450 # Passing zinfo
451 zinfo = zipfile.ZipInfo(filename="foo")
452 self._test_ZipWriteStr(zinfo, random_string)
453
454 # Timestamp in the zinfo should be overwritten.
455 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
456 self._test_ZipWriteStr(zinfo, random_string)
457
458 def test_ZipWriteStr_with_opts(self):
459 random_string = os.urandom(1024)
460 # Passing arcname
461 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700462 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700463 "compress_type": zipfile.ZIP_DEFLATED,
464 })
Tao Bao58c1b962015-05-20 09:32:18 -0700465 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700466 "compress_type": zipfile.ZIP_STORED,
467 })
468
469 # Passing zinfo
470 zinfo = zipfile.ZipInfo(filename="foo")
471 self._test_ZipWriteStr(zinfo, random_string, {
472 "compress_type": zipfile.ZIP_DEFLATED,
473 })
474 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700475 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700476 "compress_type": zipfile.ZIP_STORED,
477 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700478 self._test_ZipWriteStr(zinfo, random_string, {
479 "perms": 0o000,
480 "compress_type": zipfile.ZIP_STORED,
481 })
Tao Baof3282b42015-04-01 11:21:55 -0700482
483 def test_ZipWriteStr_large_file(self):
484 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
485 # the workaround. We will only test the case of writing a string into a
486 # large archive.
487 long_string = get_2gb_string()
488 short_string = os.urandom(1024)
489 self._test_ZipWriteStr_large_file(long_string, short_string, {
490 "compress_type": zipfile.ZIP_DEFLATED,
491 })
492
493 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700494 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700495 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700496 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700497
498 def test_bug21309935(self):
499 zip_file = tempfile.NamedTemporaryFile(delete=False)
500 zip_file_name = zip_file.name
501 zip_file.close()
502
503 try:
504 random_string = os.urandom(1024)
505 zip_file = zipfile.ZipFile(zip_file_name, "w")
506 # Default perms should be 0o644 when passing the filename.
507 common.ZipWriteStr(zip_file, "foo", random_string)
508 # Honor the specified perms.
509 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
510 # The perms in zinfo should be untouched.
511 zinfo = zipfile.ZipInfo(filename="baz")
512 zinfo.external_attr = 0o740 << 16
513 common.ZipWriteStr(zip_file, zinfo, random_string)
514 # Explicitly specified perms has the priority.
515 zinfo = zipfile.ZipInfo(filename="qux")
516 zinfo.external_attr = 0o700 << 16
517 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
518 common.ZipClose(zip_file)
519
Tao Bao31b08072017-11-08 15:50:59 -0800520 self._verify(zip_file, zip_file_name, "foo",
521 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700522 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800523 self._verify(zip_file, zip_file_name, "bar",
524 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700525 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800526 self._verify(zip_file, zip_file_name, "baz",
527 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700528 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800529 self._verify(zip_file, zip_file_name, "qux",
530 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700531 expected_mode=0o400)
532 finally:
533 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700534
Tao Bao82490d32019-04-09 00:12:30 -0700535 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800536 def test_ZipDelete(self):
537 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
538 output_zip = zipfile.ZipFile(zip_file.name, 'w',
539 compression=zipfile.ZIP_DEFLATED)
540 with tempfile.NamedTemporaryFile() as entry_file:
541 entry_file.write(os.urandom(1024))
542 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
543 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
544 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
545 common.ZipClose(output_zip)
546 zip_file.close()
547
548 try:
549 common.ZipDelete(zip_file.name, 'Test2')
550 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
551 entries = check_zip.namelist()
552 self.assertTrue('Test1' in entries)
553 self.assertFalse('Test2' in entries)
554 self.assertTrue('Test3' in entries)
555
Tao Bao986ee862018-10-04 15:46:16 -0700556 self.assertRaises(
557 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800558 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
559 entries = check_zip.namelist()
560 self.assertTrue('Test1' in entries)
561 self.assertFalse('Test2' in entries)
562 self.assertTrue('Test3' in entries)
563
564 common.ZipDelete(zip_file.name, ['Test3'])
565 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
566 entries = check_zip.namelist()
567 self.assertTrue('Test1' in entries)
568 self.assertFalse('Test2' in entries)
569 self.assertFalse('Test3' in entries)
570
571 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
572 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
573 entries = check_zip.namelist()
574 self.assertFalse('Test1' in entries)
575 self.assertFalse('Test2' in entries)
576 self.assertFalse('Test3' in entries)
577 finally:
578 os.remove(zip_file.name)
579
Tao Bao0ff15de2019-03-20 11:26:06 -0700580 @staticmethod
581 def _test_UnzipTemp_createZipFile():
582 zip_file = common.MakeTempFile(suffix='.zip')
583 output_zip = zipfile.ZipFile(
584 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
585 contents = os.urandom(1024)
586 with tempfile.NamedTemporaryFile() as entry_file:
587 entry_file.write(contents)
588 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
589 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
590 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
591 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
592 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
593 common.ZipClose(output_zip)
594 common.ZipClose(output_zip)
595 return zip_file
596
Tao Bao82490d32019-04-09 00:12:30 -0700597 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700598 def test_UnzipTemp(self):
599 zip_file = self._test_UnzipTemp_createZipFile()
600 unzipped_dir = common.UnzipTemp(zip_file)
601 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
602 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
603 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
604 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
605 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
606
Tao Bao82490d32019-04-09 00:12:30 -0700607 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700608 def test_UnzipTemp_withPatterns(self):
609 zip_file = self._test_UnzipTemp_createZipFile()
610
611 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
612 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
613 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
614 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
615 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
616 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
617
618 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
619 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
620 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
621 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
622 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
623 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
624
625 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
626 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
627 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
628 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
629 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
630 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
631
632 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
633 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
634 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
635 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
636 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
637 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
638
639 def test_UnzipTemp_withEmptyPatterns(self):
640 zip_file = self._test_UnzipTemp_createZipFile()
641 unzipped_dir = common.UnzipTemp(zip_file, [])
642 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
643 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
644 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
645 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
646 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
647
Tao Bao82490d32019-04-09 00:12:30 -0700648 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700649 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
650 zip_file = self._test_UnzipTemp_createZipFile()
651 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
652 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
653 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
654 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
655 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
656 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
657
658 def test_UnzipTemp_withNoMatchingPatterns(self):
659 zip_file = self._test_UnzipTemp_createZipFile()
660 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
661 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
662 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
663 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
664 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
665 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
666
Tao Bao89d7ab22017-12-14 17:05:33 -0800667
Tao Bao65b94e92018-10-11 21:57:26 -0700668class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800669 """Tests the APK utils related functions."""
670
671 APKCERTS_TXT1 = (
672 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
673 ' private_key="certs/devkey.pk8"\n'
674 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700675 ' certificate="build/make/target/product/security/platform.x509.pem"'
676 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800677 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
678 )
679
680 APKCERTS_CERTMAP1 = {
681 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700682 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800683 'TV.apk' : 'PRESIGNED',
684 }
685
686 APKCERTS_TXT2 = (
687 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
688 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
689 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
690 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
691 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
692 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
693 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
694 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
695 )
696
697 APKCERTS_CERTMAP2 = {
698 'Compressed1.apk' : 'certs/compressed1',
699 'Compressed2a.apk' : 'certs/compressed2',
700 'Compressed2b.apk' : 'certs/compressed2',
701 'Compressed3.apk' : 'certs/compressed3',
702 }
703
704 APKCERTS_TXT3 = (
705 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
706 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
707 )
708
709 APKCERTS_CERTMAP3 = {
710 'Compressed4.apk' : 'certs/compressed4',
711 }
712
Bill Peckham5c7b0342020-04-03 15:36:23 -0700713 # Test parsing with no optional fields, both optional fields, and only the
714 # partition optional field.
715 APKCERTS_TXT4 = (
716 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
717 ' private_key="certs/devkey.pk8"\n'
718 'name="Settings.apk"'
719 ' certificate="build/make/target/product/security/platform.x509.pem"'
720 ' private_key="build/make/target/product/security/platform.pk8"'
721 ' compressed="gz" partition="system"\n'
722 'name="TV.apk" certificate="PRESIGNED" private_key=""'
723 ' partition="product"\n'
724 )
725
726 APKCERTS_CERTMAP4 = {
727 'RecoveryLocalizer.apk' : 'certs/devkey',
728 'Settings.apk' : 'build/make/target/product/security/platform',
729 'TV.apk' : 'PRESIGNED',
730 }
731
Tao Bao17e4e612018-02-16 17:12:54 -0800732 def setUp(self):
733 self.testdata_dir = test_utils.get_testdata_dir()
734
Tao Bao818ddf52018-01-05 11:17:34 -0800735 @staticmethod
736 def _write_apkcerts_txt(apkcerts_txt, additional=None):
737 if additional is None:
738 additional = []
739 target_files = common.MakeTempFile(suffix='.zip')
740 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
741 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
742 for entry in additional:
743 target_files_zip.writestr(entry, '')
744 return target_files
745
746 def test_ReadApkCerts_NoncompressedApks(self):
747 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
748 with zipfile.ZipFile(target_files, 'r') as input_zip:
749 certmap, ext = common.ReadApkCerts(input_zip)
750
751 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
752 self.assertIsNone(ext)
753
754 def test_ReadApkCerts_CompressedApks(self):
755 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
756 # not stored in '.gz' format, so it shouldn't be considered as installed.
757 target_files = self._write_apkcerts_txt(
758 self.APKCERTS_TXT2,
759 ['Compressed1.apk.gz', 'Compressed3.apk'])
760
761 with zipfile.ZipFile(target_files, 'r') as input_zip:
762 certmap, ext = common.ReadApkCerts(input_zip)
763
764 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
765 self.assertEqual('.gz', ext)
766
767 # Alternative case with '.xz'.
768 target_files = self._write_apkcerts_txt(
769 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
770
771 with zipfile.ZipFile(target_files, 'r') as input_zip:
772 certmap, ext = common.ReadApkCerts(input_zip)
773
774 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
775 self.assertEqual('.xz', ext)
776
777 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
778 target_files = self._write_apkcerts_txt(
779 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
780 ['Compressed1.apk.gz', 'Compressed3.apk'])
781
782 with zipfile.ZipFile(target_files, 'r') as input_zip:
783 certmap, ext = common.ReadApkCerts(input_zip)
784
785 certmap_merged = self.APKCERTS_CERTMAP1.copy()
786 certmap_merged.update(self.APKCERTS_CERTMAP2)
787 self.assertDictEqual(certmap_merged, certmap)
788 self.assertEqual('.gz', ext)
789
790 def test_ReadApkCerts_MultipleCompressionMethods(self):
791 target_files = self._write_apkcerts_txt(
792 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
793 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
794
795 with zipfile.ZipFile(target_files, 'r') as input_zip:
796 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
797
798 def test_ReadApkCerts_MismatchingKeys(self):
799 malformed_apkcerts_txt = (
800 'name="App1.apk" certificate="certs/cert1.x509.pem"'
801 ' private_key="certs/cert2.pk8"\n'
802 )
803 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
804
805 with zipfile.ZipFile(target_files, 'r') as input_zip:
806 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
807
Bill Peckham5c7b0342020-04-03 15:36:23 -0700808 def test_ReadApkCerts_WithWithoutOptionalFields(self):
809 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
810 with zipfile.ZipFile(target_files, 'r') as input_zip:
811 certmap, ext = common.ReadApkCerts(input_zip)
812
813 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
814 self.assertIsNone(ext)
815
Tao Bao04e1f012018-02-04 12:13:35 -0800816 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800817 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
818 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800819 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800820 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
821
822 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800823 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800824 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
825
Tao Bao82490d32019-04-09 00:12:30 -0700826 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700827 def test_ExtractAvbPublicKey(self):
828 privkey = os.path.join(self.testdata_dir, 'testkey.key')
829 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700830 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
831 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
832 with open(extracted_from_privkey, 'rb') as privkey_fp, \
833 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700834 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
835
Tao Bao17e4e612018-02-16 17:12:54 -0800836 def test_ParseCertificate(self):
837 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
838
839 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800840 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
841 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800842 expected, _ = proc.communicate()
843 self.assertEqual(0, proc.returncode)
844
845 with open(cert) as cert_fp:
846 actual = common.ParseCertificate(cert_fp.read())
847 self.assertEqual(expected, actual)
848
Tao Bao82490d32019-04-09 00:12:30 -0700849 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700850 def test_GetMinSdkVersion(self):
851 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
852 self.assertEqual('24', common.GetMinSdkVersion(test_app))
853
Tao Bao82490d32019-04-09 00:12:30 -0700854 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700855 def test_GetMinSdkVersion_invalidInput(self):
856 self.assertRaises(
857 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
858
Tao Bao82490d32019-04-09 00:12:30 -0700859 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700860 def test_GetMinSdkVersionInt(self):
861 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
862 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
863
Tao Bao82490d32019-04-09 00:12:30 -0700864 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700865 def test_GetMinSdkVersionInt_invalidInput(self):
866 self.assertRaises(
867 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
868 {})
869
Tao Bao818ddf52018-01-05 11:17:34 -0800870
Tao Bao65b94e92018-10-11 21:57:26 -0700871class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800872
Tao Bao02a08592018-07-22 12:40:45 -0700873 def setUp(self):
874 self.testdata_dir = test_utils.get_testdata_dir()
875
Tao Bao82490d32019-04-09 00:12:30 -0700876 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800877 def test_GetSparseImage_emptyBlockMapFile(self):
878 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
879 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
880 target_files_zip.write(
881 test_utils.construct_sparse_image([
882 (0xCAC1, 6),
883 (0xCAC3, 3),
884 (0xCAC1, 4)]),
885 arcname='IMAGES/system.img')
886 target_files_zip.writestr('IMAGES/system.map', '')
887 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
888 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
889
Tao Baodba59ee2018-01-09 13:21:02 -0800890 tempdir = common.UnzipTemp(target_files)
891 with zipfile.ZipFile(target_files, 'r') as input_zip:
892 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800893
894 self.assertDictEqual(
895 {
896 '__COPY': RangeSet("0"),
897 '__NONZERO-0': RangeSet("1-5 9-12"),
898 },
899 sparse_image.file_map)
900
Tao Baob2de7d92019-04-10 10:01:47 -0700901 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800902 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700903 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
904 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800905 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700906 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
907 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800908
Tao Bao82490d32019-04-09 00:12:30 -0700909 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800910 def test_GetSparseImage_missingBlockMapFile(self):
911 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
912 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
913 target_files_zip.write(
914 test_utils.construct_sparse_image([
915 (0xCAC1, 6),
916 (0xCAC3, 3),
917 (0xCAC1, 4)]),
918 arcname='IMAGES/system.img')
919 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
920 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
921
Tao Baodba59ee2018-01-09 13:21:02 -0800922 tempdir = common.UnzipTemp(target_files)
923 with zipfile.ZipFile(target_files, 'r') as input_zip:
924 self.assertRaises(
925 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
926 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800927
Tao Bao82490d32019-04-09 00:12:30 -0700928 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800929 def test_GetSparseImage_sharedBlocks_notAllowed(self):
930 """Tests the case of having overlapping blocks but disallowed."""
931 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
932 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
933 target_files_zip.write(
934 test_utils.construct_sparse_image([(0xCAC2, 16)]),
935 arcname='IMAGES/system.img')
936 # Block 10 is shared between two files.
937 target_files_zip.writestr(
938 'IMAGES/system.map',
939 '\n'.join([
940 '/system/file1 1-5 9-10',
941 '/system/file2 10-12']))
942 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
943 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
944
Tao Baodba59ee2018-01-09 13:21:02 -0800945 tempdir = common.UnzipTemp(target_files)
946 with zipfile.ZipFile(target_files, 'r') as input_zip:
947 self.assertRaises(
948 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
949 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800950
Tao Bao82490d32019-04-09 00:12:30 -0700951 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800952 def test_GetSparseImage_sharedBlocks_allowed(self):
953 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
954 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
955 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
956 # Construct an image with a care_map of "0-5 9-12".
957 target_files_zip.write(
958 test_utils.construct_sparse_image([(0xCAC2, 16)]),
959 arcname='IMAGES/system.img')
960 # Block 10 is shared between two files.
961 target_files_zip.writestr(
962 'IMAGES/system.map',
963 '\n'.join([
964 '/system/file1 1-5 9-10',
965 '/system/file2 10-12']))
966 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
967 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
968
Tao Baodba59ee2018-01-09 13:21:02 -0800969 tempdir = common.UnzipTemp(target_files)
970 with zipfile.ZipFile(target_files, 'r') as input_zip:
971 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -0800972
973 self.assertDictEqual(
974 {
975 '__COPY': RangeSet("0"),
976 '__NONZERO-0': RangeSet("6-8 13-15"),
977 '/system/file1': RangeSet("1-5 9-10"),
978 '/system/file2': RangeSet("11-12"),
979 },
980 sparse_image.file_map)
981
982 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
983 # 'incomplete'.
984 self.assertTrue(
985 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
986 self.assertNotIn(
987 'incomplete', sparse_image.file_map['/system/file2'].extra)
988
Tao Baoa264fef2019-10-06 21:55:20 -0700989 # '/system/file1' will only contain one field -- a copy of the input text.
990 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
991
992 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -0800993 self.assertFalse(sparse_image.file_map['__COPY'].extra)
994 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -0800995
Tao Bao82490d32019-04-09 00:12:30 -0700996 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800997 def test_GetSparseImage_incompleteRanges(self):
998 """Tests the case of ext4 images with holes."""
999 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1000 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1001 target_files_zip.write(
1002 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1003 arcname='IMAGES/system.img')
1004 target_files_zip.writestr(
1005 'IMAGES/system.map',
1006 '\n'.join([
1007 '/system/file1 1-5 9-10',
1008 '/system/file2 11-12']))
1009 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1010 # '/system/file2' has less blocks listed (2) than actual (3).
1011 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1012
Tao Baodba59ee2018-01-09 13:21:02 -08001013 tempdir = common.UnzipTemp(target_files)
1014 with zipfile.ZipFile(target_files, 'r') as input_zip:
1015 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001016
Tao Baoa264fef2019-10-06 21:55:20 -07001017 self.assertEqual(
1018 '1-5 9-10',
1019 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001020 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1021
Tao Bao82490d32019-04-09 00:12:30 -07001022 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001023 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1024 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1025 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1026 target_files_zip.write(
1027 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1028 arcname='IMAGES/system.img')
1029 target_files_zip.writestr(
1030 'IMAGES/system.map',
1031 '\n'.join([
1032 '//system/file1 1-5 9-10',
1033 '//system/file2 11-12',
1034 '/system/app/file3 13-15']))
1035 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1036 # '/system/file2' has less blocks listed (2) than actual (3).
1037 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1038 # '/system/app/file3' has less blocks listed (3) than actual (4).
1039 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1040
1041 tempdir = common.UnzipTemp(target_files)
1042 with zipfile.ZipFile(target_files, 'r') as input_zip:
1043 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1044
Tao Baoa264fef2019-10-06 21:55:20 -07001045 self.assertEqual(
1046 '1-5 9-10',
1047 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001048 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1049 self.assertTrue(
1050 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1051
Tao Bao82490d32019-04-09 00:12:30 -07001052 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001053 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1054 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1055 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1056 target_files_zip.write(
1057 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1058 arcname='IMAGES/system.img')
1059 target_files_zip.writestr(
1060 'IMAGES/system.map',
1061 '\n'.join([
1062 '//system/file1 1-5 9-10',
1063 '//init.rc 13-15']))
1064 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1065 # '/init.rc' has less blocks listed (3) than actual (4).
1066 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1067
1068 tempdir = common.UnzipTemp(target_files)
1069 with zipfile.ZipFile(target_files, 'r') as input_zip:
1070 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1071
Tao Baoa264fef2019-10-06 21:55:20 -07001072 self.assertEqual(
1073 '1-5 9-10',
1074 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001075 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1076
Tao Bao82490d32019-04-09 00:12:30 -07001077 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001078 def test_GetSparseImage_fileNotFound(self):
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
1091 tempdir = common.UnzipTemp(target_files)
1092 with zipfile.ZipFile(target_files, 'r') as input_zip:
1093 self.assertRaises(
1094 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1095 False)
1096
Tao Bao82490d32019-04-09 00:12:30 -07001097 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001098 def test_GetAvbChainedPartitionArg(self):
1099 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1100 info_dict = {
1101 'avb_avbtool': 'avbtool',
1102 'avb_system_key_path': pubkey,
1103 'avb_system_rollback_index_location': 2,
1104 }
1105 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1106 self.assertEqual(3, len(args))
1107 self.assertEqual('system', args[0])
1108 self.assertEqual('2', args[1])
1109 self.assertTrue(os.path.exists(args[2]))
1110
Tao Bao82490d32019-04-09 00:12:30 -07001111 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001112 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1113 key = os.path.join(self.testdata_dir, 'testkey.key')
1114 info_dict = {
1115 'avb_avbtool': 'avbtool',
1116 'avb_product_key_path': key,
1117 'avb_product_rollback_index_location': 2,
1118 }
1119 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1120 self.assertEqual(3, len(args))
1121 self.assertEqual('product', args[0])
1122 self.assertEqual('2', args[1])
1123 self.assertTrue(os.path.exists(args[2]))
1124
Tao Bao82490d32019-04-09 00:12:30 -07001125 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001126 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1127 info_dict = {
1128 'avb_avbtool': 'avbtool',
1129 'avb_system_key_path': 'does-not-exist',
1130 'avb_system_rollback_index_location': 2,
1131 }
1132 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1133 args = common.GetAvbChainedPartitionArg(
1134 'system', info_dict, pubkey).split(':')
1135 self.assertEqual(3, len(args))
1136 self.assertEqual('system', args[0])
1137 self.assertEqual('2', args[1])
1138 self.assertTrue(os.path.exists(args[2]))
1139
Tao Bao82490d32019-04-09 00:12:30 -07001140 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001141 def test_GetAvbChainedPartitionArg_invalidKey(self):
1142 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1143 info_dict = {
1144 'avb_avbtool': 'avbtool',
1145 'avb_system_key_path': pubkey,
1146 'avb_system_rollback_index_location': 2,
1147 }
1148 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001149 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1150 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001151
Tao Baoa57ab9f2018-08-24 12:08:38 -07001152 INFO_DICT_DEFAULT = {
1153 'recovery_api_version': 3,
1154 'fstab_version': 2,
1155 'system_root_image': 'true',
1156 'no_recovery' : 'true',
1157 'recovery_as_boot': 'true',
1158 }
1159
Daniel Norman4cc9df62019-07-18 10:11:07 -07001160 def test_LoadListFromFile(self):
1161 file_path = os.path.join(self.testdata_dir,
1162 'merge_config_framework_item_list')
1163 contents = common.LoadListFromFile(file_path)
1164 expected_contents = [
1165 'META/apkcerts.txt',
1166 'META/filesystem_config.txt',
1167 'META/root_filesystem_config.txt',
1168 'META/system_manifest.xml',
1169 'META/system_matrix.xml',
1170 'META/update_engine_config.txt',
1171 'PRODUCT/*',
1172 'ROOT/*',
1173 'SYSTEM/*',
1174 ]
1175 self.assertEqual(sorted(contents), sorted(expected_contents))
1176
Tao Baoa57ab9f2018-08-24 12:08:38 -07001177 @staticmethod
1178 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1179 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1180 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1181 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001182 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001183 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1184
1185 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1186 if info_dict.get('system_root_image') == 'true':
1187 fstab_values = FSTAB_TEMPLATE.format('/')
1188 else:
1189 fstab_values = FSTAB_TEMPLATE.format('/system')
1190 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001191
1192 common.ZipWriteStr(
1193 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001194 return target_files
1195
1196 def test_LoadInfoDict(self):
1197 target_files = self._test_LoadInfoDict_createTargetFiles(
1198 self.INFO_DICT_DEFAULT,
1199 'BOOT/RAMDISK/system/etc/recovery.fstab')
1200 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1201 loaded_dict = common.LoadInfoDict(target_files_zip)
1202 self.assertEqual(3, loaded_dict['recovery_api_version'])
1203 self.assertEqual(2, loaded_dict['fstab_version'])
1204 self.assertIn('/', loaded_dict['fstab'])
1205 self.assertIn('/system', loaded_dict['fstab'])
1206
1207 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1208 target_files = self._test_LoadInfoDict_createTargetFiles(
1209 self.INFO_DICT_DEFAULT,
1210 'BOOT/RAMDISK/etc/recovery.fstab')
1211 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1212 loaded_dict = common.LoadInfoDict(target_files_zip)
1213 self.assertEqual(3, loaded_dict['recovery_api_version'])
1214 self.assertEqual(2, loaded_dict['fstab_version'])
1215 self.assertIn('/', loaded_dict['fstab'])
1216 self.assertIn('/system', loaded_dict['fstab'])
1217
Tao Bao82490d32019-04-09 00:12:30 -07001218 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001219 def test_LoadInfoDict_dirInput(self):
1220 target_files = self._test_LoadInfoDict_createTargetFiles(
1221 self.INFO_DICT_DEFAULT,
1222 'BOOT/RAMDISK/system/etc/recovery.fstab')
1223 unzipped = common.UnzipTemp(target_files)
1224 loaded_dict = common.LoadInfoDict(unzipped)
1225 self.assertEqual(3, loaded_dict['recovery_api_version'])
1226 self.assertEqual(2, loaded_dict['fstab_version'])
1227 self.assertIn('/', loaded_dict['fstab'])
1228 self.assertIn('/system', loaded_dict['fstab'])
1229
Tao Bao82490d32019-04-09 00:12:30 -07001230 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001231 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1232 target_files = self._test_LoadInfoDict_createTargetFiles(
1233 self.INFO_DICT_DEFAULT,
1234 'BOOT/RAMDISK/system/etc/recovery.fstab')
1235 unzipped = common.UnzipTemp(target_files)
1236 loaded_dict = common.LoadInfoDict(unzipped)
1237 self.assertEqual(3, loaded_dict['recovery_api_version'])
1238 self.assertEqual(2, loaded_dict['fstab_version'])
1239 self.assertIn('/', loaded_dict['fstab'])
1240 self.assertIn('/system', loaded_dict['fstab'])
1241
1242 def test_LoadInfoDict_systemRootImageFalse(self):
1243 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1244 # launched prior to P will likely have this config.
1245 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1246 del info_dict['no_recovery']
1247 del info_dict['system_root_image']
1248 del info_dict['recovery_as_boot']
1249 target_files = self._test_LoadInfoDict_createTargetFiles(
1250 info_dict,
1251 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1252 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1253 loaded_dict = common.LoadInfoDict(target_files_zip)
1254 self.assertEqual(3, loaded_dict['recovery_api_version'])
1255 self.assertEqual(2, loaded_dict['fstab_version'])
1256 self.assertNotIn('/', loaded_dict['fstab'])
1257 self.assertIn('/system', loaded_dict['fstab'])
1258
1259 def test_LoadInfoDict_recoveryAsBootFalse(self):
1260 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1261 # devices launched since P will likely have this config.
1262 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1263 del info_dict['no_recovery']
1264 del info_dict['recovery_as_boot']
1265 target_files = self._test_LoadInfoDict_createTargetFiles(
1266 info_dict,
1267 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1268 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1269 loaded_dict = common.LoadInfoDict(target_files_zip)
1270 self.assertEqual(3, loaded_dict['recovery_api_version'])
1271 self.assertEqual(2, loaded_dict['fstab_version'])
1272 self.assertIn('/', loaded_dict['fstab'])
1273 self.assertIn('/system', loaded_dict['fstab'])
1274
1275 def test_LoadInfoDict_noRecoveryTrue(self):
1276 # Device doesn't have a recovery partition at all.
1277 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1278 del info_dict['recovery_as_boot']
1279 target_files = self._test_LoadInfoDict_createTargetFiles(
1280 info_dict,
1281 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1282 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1283 loaded_dict = common.LoadInfoDict(target_files_zip)
1284 self.assertEqual(3, loaded_dict['recovery_api_version'])
1285 self.assertEqual(2, loaded_dict['fstab_version'])
1286 self.assertIsNone(loaded_dict['fstab'])
1287
Tao Bao82490d32019-04-09 00:12:30 -07001288 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001289 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1290 target_files = self._test_LoadInfoDict_createTargetFiles(
1291 self.INFO_DICT_DEFAULT,
1292 'BOOT/RAMDISK/system/etc/recovery.fstab')
1293 common.ZipDelete(target_files, 'META/misc_info.txt')
1294 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1295 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1296
Tao Bao82490d32019-04-09 00:12:30 -07001297 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001298 def test_LoadInfoDict_repacking(self):
1299 target_files = self._test_LoadInfoDict_createTargetFiles(
1300 self.INFO_DICT_DEFAULT,
1301 'BOOT/RAMDISK/system/etc/recovery.fstab')
1302 unzipped = common.UnzipTemp(target_files)
1303 loaded_dict = common.LoadInfoDict(unzipped, True)
1304 self.assertEqual(3, loaded_dict['recovery_api_version'])
1305 self.assertEqual(2, loaded_dict['fstab_version'])
1306 self.assertIn('/', loaded_dict['fstab'])
1307 self.assertIn('/system', loaded_dict['fstab'])
1308 self.assertEqual(
1309 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1310 self.assertEqual(
1311 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1312 loaded_dict['root_fs_config'])
1313
1314 def test_LoadInfoDict_repackingWithZipFileInput(self):
1315 target_files = self._test_LoadInfoDict_createTargetFiles(
1316 self.INFO_DICT_DEFAULT,
1317 'BOOT/RAMDISK/system/etc/recovery.fstab')
1318 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1319 self.assertRaises(
1320 AssertionError, common.LoadInfoDict, target_files_zip, True)
1321
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001322 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1323 framework_dict = {
1324 'super_partition_groups': 'group_a',
1325 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001326 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001327 }
1328 vendor_dict = {
1329 'super_partition_groups': 'group_a group_b',
1330 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001331 'super_group_a_partition_list': 'vendor',
1332 'super_group_a_group_size': '1000',
1333 'super_group_b_partition_list': 'product',
1334 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001335 }
1336 merged_dict = common.MergeDynamicPartitionInfoDicts(
1337 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001338 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001339 expected_merged_dict = {
1340 'super_partition_groups': 'group_a group_b',
1341 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001342 'super_group_a_partition_list': 'system vendor',
1343 'super_group_a_group_size': '1000',
1344 'super_group_b_partition_list': 'product',
1345 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001346 }
1347 self.assertEqual(merged_dict, expected_merged_dict)
1348
1349 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1350 framework_dict = {
1351 'super_partition_groups': 'group_a',
1352 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001353 'super_group_a_partition_list': 'system',
1354 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001355 }
1356 vendor_dict = {
1357 'super_partition_groups': 'group_a group_b',
1358 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001359 'super_group_a_partition_list': 'vendor',
1360 'super_group_a_group_size': '1000',
1361 'super_group_b_partition_list': 'product',
1362 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001363 }
1364 merged_dict = common.MergeDynamicPartitionInfoDicts(
1365 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001366 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001367 expected_merged_dict = {
1368 'super_partition_groups': 'group_a group_b',
1369 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001370 'super_group_a_partition_list': 'system vendor',
1371 'super_group_a_group_size': '1000',
1372 'super_group_b_partition_list': 'product',
1373 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001374 }
1375 self.assertEqual(merged_dict, expected_merged_dict)
1376
Daniel Norman276f0622019-07-26 14:13:51 -07001377 def test_GetAvbPartitionArg(self):
1378 info_dict = {}
1379 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1380 self.assertEqual(
1381 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1382
1383 @test_utils.SkipIfExternalToolsUnavailable()
1384 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1385 testdata_dir = test_utils.get_testdata_dir()
1386 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1387 info_dict = {
1388 'avb_avbtool': 'avbtool',
1389 'avb_vendor_key_path': pubkey,
1390 'avb_vendor_rollback_index_location': 5,
1391 }
1392 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1393 self.assertEqual(2, len(cmd))
1394 self.assertEqual('--chain_partition', cmd[0])
1395 chained_partition_args = cmd[1].split(':')
1396 self.assertEqual(3, len(chained_partition_args))
1397 self.assertEqual('vendor', chained_partition_args[0])
1398 self.assertEqual('5', chained_partition_args[1])
1399 self.assertTrue(os.path.exists(chained_partition_args[2]))
1400
Tao Bao3612c882019-10-14 17:49:31 -07001401 @test_utils.SkipIfExternalToolsUnavailable()
1402 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1403 testdata_dir = test_utils.get_testdata_dir()
1404 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1405 info_dict = {
1406 'avb_avbtool': 'avbtool',
1407 'avb_recovery_key_path': pubkey,
1408 'avb_recovery_rollback_index_location': 3,
1409 }
1410 cmd = common.GetAvbPartitionArg(
1411 'recovery', '/path/to/recovery.img', info_dict)
1412 self.assertFalse(cmd)
1413
1414 @test_utils.SkipIfExternalToolsUnavailable()
1415 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1416 testdata_dir = test_utils.get_testdata_dir()
1417 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1418 info_dict = {
1419 'ab_update': 'true',
1420 'avb_avbtool': 'avbtool',
1421 'avb_recovery_key_path': pubkey,
1422 'avb_recovery_rollback_index_location': 3,
1423 }
1424 cmd = common.GetAvbPartitionArg(
1425 'recovery', '/path/to/recovery.img', info_dict)
1426 self.assertEqual(2, len(cmd))
1427 self.assertEqual('--chain_partition', cmd[0])
1428 chained_partition_args = cmd[1].split(':')
1429 self.assertEqual(3, len(chained_partition_args))
1430 self.assertEqual('recovery', chained_partition_args[0])
1431 self.assertEqual('3', chained_partition_args[1])
1432 self.assertTrue(os.path.exists(chained_partition_args[2]))
1433
Tianjie Xueaed60c2020-03-12 00:33:28 -07001434 @test_utils.SkipIfExternalToolsUnavailable()
1435 def test_BuildVBMeta_appendAftl(self):
1436 testdata_dir = test_utils.get_testdata_dir()
1437 common.OPTIONS.info_dict = {
1438 'ab_update': 'true',
1439 'avb_avbtool': 'avbtool',
1440 'build.prop': {
1441 'ro.build.version.incremental': '6285659',
1442 'ro.product.device': 'coral',
1443 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1444 '6285659:userdebug/dev-keys'
1445 }
1446 }
Tianjie0f307452020-04-01 12:20:21 -07001447 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001448 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1449 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1450 'test_transparency_key.pub')
1451 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1452 testdata_dir, 'test_aftl_rsa4096.pem')
1453
1454 input_dir = common.MakeTempDir()
1455 system_image = common.MakeTempFile()
1456 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1457 '/system', str(4096 * 100), '-j', '0', '-s']
1458 common.RunAndCheckOutput(build_image_cmd)
1459
1460 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1461 '--partition_size', str(4096 * 150),
1462 '--partition_name', 'system',
1463 '--image', system_image]
1464 common.RunAndCheckOutput(add_footer_cmd)
1465
1466 vbmeta_image = common.MakeTempFile()
1467 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1468 ['system'])
1469
1470 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1471 vbmeta_image, '--transparency_log_pub_keys',
1472 common.OPTIONS.aftl_key_path]
1473 common.RunAndCheckOutput(verify_cmd)
1474
Tao Baofc7e0e02018-02-13 13:54:02 -08001475
Tao Bao65b94e92018-10-11 21:57:26 -07001476class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001477 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001478
Tao Bao1c830bf2017-12-25 10:43:47 -08001479 Its format should match between common.py and validate_target_files.py.
1480 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001481
1482 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001483 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001484 # Create a dummy dict that contains the fstab info for boot&recovery.
1485 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001486 dummy_fstab = [
1487 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1488 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001489 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001490 # Construct the gzipped recovery.img and boot.img
1491 self.recovery_data = bytearray([
1492 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1493 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1494 0x08, 0x00, 0x00, 0x00
1495 ])
1496 # echo -n "boot" | gzip -f | hd
1497 self.boot_data = bytearray([
1498 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1499 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1500 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001501
1502 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1503 loc = os.path.join(self._tempdir, prefix, name)
1504 if not os.path.exists(os.path.dirname(loc)):
1505 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001506 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001507 f.write(data)
1508
1509 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001510 recovery_image = common.File("recovery.img", self.recovery_data)
1511 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001512 self._info["full_recovery_image"] = "true"
1513
1514 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1515 recovery_image, boot_image, self._info)
1516 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1517 self._info)
1518
Tao Bao82490d32019-04-09 00:12:30 -07001519 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001520 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001521 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001522 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001523 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001524 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1525
1526 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1527 recovery_image, boot_image, self._info)
1528 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1529 self._info)
1530 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001531 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001532 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1533 recovery_image, boot_image, self._info)
1534 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1535 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001536
1537
Yifan Hong45433e42019-01-18 13:55:25 -08001538class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001539
Yifan Hong45433e42019-01-18 13:55:25 -08001540 def __init__(self, partition, tgt, src=None):
1541 self.partition = partition
1542 self.tgt = tgt
1543 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001544
Yifan Hong45433e42019-01-18 13:55:25 -08001545 def WriteScript(self, script, _, progress=None,
1546 write_verify_script=False):
1547 if progress:
1548 script.AppendExtra("progress({})".format(progress))
1549 script.AppendExtra("patch({});".format(self.partition))
1550 if write_verify_script:
1551 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001552
Yifan Hong45433e42019-01-18 13:55:25 -08001553 def WritePostInstallVerifyScript(self, script):
1554 script.AppendExtra("verify({});".format(self.partition))
1555
1556
1557class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001558
Yifan Hong45433e42019-01-18 13:55:25 -08001559 def __init__(self, size):
1560 self.blocksize = 4096
1561 self.total_blocks = size // 4096
1562 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1563
1564
1565class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001566
Yifan Hong45433e42019-01-18 13:55:25 -08001567 @staticmethod
1568 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001569 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001570 with output_zip.open('dynamic_partitions_op_list') as op_list:
1571 return [line.decode().strip() for line in op_list.readlines()
1572 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001573
1574 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001575 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001576 self.output_path = common.MakeTempFile(suffix='.zip')
1577
1578 def test_full(self):
1579 target_info = common.LoadDictionaryFromLines("""
1580dynamic_partition_list=system vendor
1581super_partition_groups=group_foo
1582super_group_foo_group_size={group_size}
1583super_group_foo_partition_list=system vendor
1584""".format(group_size=4 * GiB).split("\n"))
1585 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1586 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1587
1588 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1589 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1590 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1591
1592 self.assertEqual(str(self.script).strip(), """
1593assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001594patch(system);
1595verify(system);
1596unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001597patch(vendor);
1598verify(vendor);
1599unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001600""".strip())
1601
1602 lines = self.get_op_list(self.output_path)
1603
1604 remove_all_groups = lines.index("remove_all_groups")
1605 add_group = lines.index("add_group group_foo 4294967296")
1606 add_vendor = lines.index("add vendor group_foo")
1607 add_system = lines.index("add system group_foo")
1608 resize_vendor = lines.index("resize vendor 1073741824")
1609 resize_system = lines.index("resize system 3221225472")
1610
1611 self.assertLess(remove_all_groups, add_group,
1612 "Should add groups after removing all groups")
1613 self.assertLess(add_group, min(add_vendor, add_system),
1614 "Should add partitions after adding group")
1615 self.assertLess(add_system, resize_system,
1616 "Should resize system after adding it")
1617 self.assertLess(add_vendor, resize_vendor,
1618 "Should resize vendor after adding it")
1619
1620 def test_inc_groups(self):
1621 source_info = common.LoadDictionaryFromLines("""
1622super_partition_groups=group_foo group_bar group_baz
1623super_group_foo_group_size={group_foo_size}
1624super_group_bar_group_size={group_bar_size}
1625""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1626 target_info = common.LoadDictionaryFromLines("""
1627super_partition_groups=group_foo group_baz group_qux
1628super_group_foo_group_size={group_foo_size}
1629super_group_baz_group_size={group_baz_size}
1630super_group_qux_group_size={group_qux_size}
1631""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1632 group_qux_size=1 * GiB).split("\n"))
1633
1634 dp_diff = common.DynamicPartitionsDifference(target_info,
1635 block_diffs=[],
1636 source_info_dict=source_info)
1637 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1638 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1639
1640 lines = self.get_op_list(self.output_path)
1641
1642 removed = lines.index("remove_group group_bar")
1643 shrunk = lines.index("resize_group group_foo 3221225472")
1644 grown = lines.index("resize_group group_baz 4294967296")
1645 added = lines.index("add_group group_qux 1073741824")
1646
Tao Baof1113e92019-06-18 12:10:14 -07001647 self.assertLess(max(removed, shrunk),
1648 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001649 "ops that remove / shrink partitions must precede ops that "
1650 "grow / add partitions")
1651
Yifan Hongbb2658d2019-01-25 12:30:58 -08001652 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001653 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001654dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001655super_partition_groups=group_foo
1656super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001657super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001658""".format(group_foo_size=4 * GiB).split("\n"))
1659 target_info = common.LoadDictionaryFromLines("""
1660dynamic_partition_list=system vendor product odm
1661super_partition_groups=group_foo group_bar
1662super_group_foo_group_size={group_foo_size}
1663super_group_foo_partition_list=system vendor odm
1664super_group_bar_group_size={group_bar_size}
1665super_group_bar_partition_list=product
1666""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1667
1668 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1669 src=FakeSparseImage(1024 * MiB)),
1670 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1671 src=FakeSparseImage(1024 * MiB)),
1672 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1673 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001674 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001675 src=FakeSparseImage(1024 * MiB)),
1676 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1677 src=None)]
1678
1679 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1680 source_info_dict=source_info)
1681 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1682 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1683
1684 metadata_idx = self.script.lines.index(
1685 'assert(update_dynamic_partitions(package_extract_file('
1686 '"dynamic_partitions_op_list")));')
1687 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1688 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1689 for p in ("product", "system", "odm"):
1690 patch_idx = self.script.lines.index("patch({});".format(p))
1691 verify_idx = self.script.lines.index("verify({});".format(p))
1692 self.assertLess(metadata_idx, patch_idx,
1693 "Should patch {} after updating metadata".format(p))
1694 self.assertLess(patch_idx, verify_idx,
1695 "Should verify {} after patching".format(p))
1696
Justin Yun6151e3f2019-06-25 15:58:13 +09001697 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001698
1699 lines = self.get_op_list(self.output_path)
1700
Justin Yun6151e3f2019-06-25 15:58:13 +09001701 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001702 move_product_out = lines.index("move product default")
1703 shrink = lines.index("resize vendor 536870912")
1704 shrink_group = lines.index("resize_group group_foo 3221225472")
1705 add_group_bar = lines.index("add_group group_bar 1073741824")
1706 add_odm = lines.index("add odm group_foo")
1707 grow_existing = lines.index("resize system 1610612736")
1708 grow_added = lines.index("resize odm 1073741824")
1709 move_product_in = lines.index("move product group_bar")
1710
1711 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1712 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1713
1714 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1715 "Must shrink group after partitions inside group are shrunk"
1716 " / removed")
1717
1718 self.assertLess(add_group_bar, move_product_in,
1719 "Must add partitions to group after group is added")
1720
1721 self.assertLess(max_idx_move_partition_out_foo,
1722 min_idx_move_partition_in_foo,
1723 "Must shrink partitions / remove partitions from group"
1724 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001725
1726 def test_remove_partition(self):
1727 source_info = common.LoadDictionaryFromLines("""
1728blockimgdiff_versions=3,4
1729use_dynamic_partitions=true
1730dynamic_partition_list=foo
1731super_partition_groups=group_foo
1732super_group_foo_group_size={group_foo_size}
1733super_group_foo_partition_list=foo
1734""".format(group_foo_size=4 * GiB).split("\n"))
1735 target_info = common.LoadDictionaryFromLines("""
1736blockimgdiff_versions=3,4
1737use_dynamic_partitions=true
1738super_partition_groups=group_foo
1739super_group_foo_group_size={group_foo_size}
1740""".format(group_foo_size=4 * GiB).split("\n"))
1741
1742 common.OPTIONS.info_dict = target_info
1743 common.OPTIONS.target_info_dict = target_info
1744 common.OPTIONS.source_info_dict = source_info
1745 common.OPTIONS.cache_size = 4 * 4096
1746
1747 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1748 src=DataImage("source", pad=True))]
1749
1750 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1751 source_info_dict=source_info)
1752 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1753 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1754
1755 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001756 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001757
1758 lines = self.get_op_list(self.output_path)
1759 self.assertEqual(lines, ["remove foo"])