blob: 551f626b9f402bcc284e38c83f53c9b59a82899d [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
Tianjie20dd8f22020-04-19 15:51:16 -070022import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080023import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080024from hashlib import sha1
25
Dan Albert8e0178d2015-01-27 15:53:15 -080026import common
Tao Bao04e1f012018-02-04 12:13:35 -080027import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070028import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070029from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080030from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080031
Tao Bao04e1f012018-02-04 12:13:35 -080032
Tao Bao31b08072017-11-08 15:50:59 -080033KiB = 1024
34MiB = 1024 * KiB
35GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080036
Tao Bao1c830bf2017-12-25 10:43:47 -080037
Tao Baof3282b42015-04-01 11:21:55 -070038def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080039 size = int(2 * GiB + 1)
40 block_size = 4 * KiB
41 step_size = 4 * MiB
42 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
43 for _ in range(0, size, step_size):
44 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070045 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070046
Dan Albert8e0178d2015-01-27 15:53:15 -080047
Tao Bao1c320f82019-10-04 23:25:12 -070048class BuildInfoTest(test_utils.ReleaseToolsTestCase):
49
50 TEST_INFO_DICT = {
51 'build.prop' : {
52 'ro.product.device' : 'product-device',
53 'ro.product.name' : 'product-name',
54 'ro.build.fingerprint' : 'build-fingerprint',
55 'ro.build.foo' : 'build-foo',
56 },
Daniel Normand5fe8622020-01-08 17:01:11 -080057 'system.build.prop' : {
58 'ro.product.system.brand' : 'product-brand',
59 'ro.product.system.name' : 'product-name',
60 'ro.product.system.device' : 'product-device',
61 'ro.system.build.version.release' : 'version-release',
62 'ro.system.build.id' : 'build-id',
63 'ro.system.build.version.incremental' : 'version-incremental',
64 'ro.system.build.type' : 'build-type',
65 'ro.system.build.tags' : 'build-tags',
66 'ro.system.build.foo' : 'build-foo',
67 },
Tao Bao1c320f82019-10-04 23:25:12 -070068 'vendor.build.prop' : {
Daniel Normand5fe8622020-01-08 17:01:11 -080069 'ro.product.vendor.brand' : 'vendor-product-brand',
70 'ro.product.vendor.name' : 'vendor-product-name',
71 'ro.product.vendor.device' : 'vendor-product-device',
72 'ro.vendor.build.version.release' : 'vendor-version-release',
73 'ro.vendor.build.id' : 'vendor-build-id',
74 'ro.vendor.build.version.incremental' : 'vendor-version-incremental',
75 'ro.vendor.build.type' : 'vendor-build-type',
76 'ro.vendor.build.tags' : 'vendor-build-tags',
Tao Bao1c320f82019-10-04 23:25:12 -070077 },
78 'property1' : 'value1',
79 'property2' : 4096,
80 }
81
82 TEST_INFO_DICT_USES_OEM_PROPS = {
83 'build.prop' : {
84 'ro.product.name' : 'product-name',
85 'ro.build.thumbprint' : 'build-thumbprint',
86 'ro.build.bar' : 'build-bar',
87 },
88 'vendor.build.prop' : {
89 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
90 },
91 'property1' : 'value1',
92 'property2' : 4096,
93 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
94 }
95
96 TEST_OEM_DICTS = [
97 {
98 'ro.product.brand' : 'brand1',
99 'ro.product.device' : 'device1',
100 },
101 {
102 'ro.product.brand' : 'brand2',
103 'ro.product.device' : 'device2',
104 },
105 {
106 'ro.product.brand' : 'brand3',
107 'ro.product.device' : 'device3',
108 },
109 ]
110
111 def test_init(self):
112 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
113 self.assertEqual('product-device', target_info.device)
114 self.assertEqual('build-fingerprint', target_info.fingerprint)
115 self.assertFalse(target_info.is_ab)
116 self.assertIsNone(target_info.oem_props)
117
118 def test_init_with_oem_props(self):
119 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
120 self.TEST_OEM_DICTS)
121 self.assertEqual('device1', target_info.device)
122 self.assertEqual('brand1/product-name/device1:build-thumbprint',
123 target_info.fingerprint)
124
125 # Swap the order in oem_dicts, which would lead to different BuildInfo.
126 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
127 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
128 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
129 oem_dicts)
130 self.assertEqual('device3', target_info.device)
131 self.assertEqual('brand3/product-name/device3:build-thumbprint',
132 target_info.fingerprint)
133
134 # Missing oem_dict should be rejected.
135 self.assertRaises(AssertionError, common.BuildInfo,
136 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
137
138 def test_init_badFingerprint(self):
139 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
140 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
141 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
142
143 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
144 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
145
146 def test___getitem__(self):
147 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
148 self.assertEqual('value1', target_info['property1'])
149 self.assertEqual(4096, target_info['property2'])
150 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
151
152 def test___getitem__with_oem_props(self):
153 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
154 self.TEST_OEM_DICTS)
155 self.assertEqual('value1', target_info['property1'])
156 self.assertEqual(4096, target_info['property2'])
157 self.assertRaises(KeyError,
158 lambda: target_info['build.prop']['ro.build.foo'])
159
160 def test___setitem__(self):
161 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
162 self.assertEqual('value1', target_info['property1'])
163 target_info['property1'] = 'value2'
164 self.assertEqual('value2', target_info['property1'])
165
166 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
167 target_info['build.prop']['ro.build.foo'] = 'build-bar'
168 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
169
170 def test_get(self):
171 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
172 self.assertEqual('value1', target_info.get('property1'))
173 self.assertEqual(4096, target_info.get('property2'))
174 self.assertEqual(4096, target_info.get('property2', 1024))
175 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
176 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
177
178 def test_get_with_oem_props(self):
179 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
180 self.TEST_OEM_DICTS)
181 self.assertEqual('value1', target_info.get('property1'))
182 self.assertEqual(4096, target_info.get('property2'))
183 self.assertEqual(4096, target_info.get('property2', 1024))
184 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
185 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
186 self.assertRaises(KeyError,
187 lambda: target_info.get('build.prop')['ro.build.foo'])
188
189 def test_items(self):
190 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
191 items = target_info.items()
192 self.assertIn(('property1', 'value1'), items)
193 self.assertIn(('property2', 4096), items)
194
195 def test_GetBuildProp(self):
196 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
197 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
198 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
199 'ro.build.nonexistent')
200
201 def test_GetBuildProp_with_oem_props(self):
202 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
203 self.TEST_OEM_DICTS)
204 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
205 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
206 'ro.build.nonexistent')
207
Daniel Normand5fe8622020-01-08 17:01:11 -0800208 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700209 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800210 self.assertEqual(
211 target_info.GetPartitionFingerprint('vendor'),
212 'vendor-product-brand/vendor-product-name/vendor-product-device'
213 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
214 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700215
Daniel Normand5fe8622020-01-08 17:01:11 -0800216 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700217 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800218 self.assertEqual(
219 target_info.GetPartitionFingerprint('system_other'),
220 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700221
Daniel Normand5fe8622020-01-08 17:01:11 -0800222 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
223 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
224 info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
225 target_info = common.BuildInfo(info_dict, None)
226 self.assertEqual(
227 target_info.GetPartitionFingerprint('vendor'),
228 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700229
230 def test_WriteMountOemScript(self):
231 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
232 self.TEST_OEM_DICTS)
233 script_writer = test_utils.MockScriptWriter()
234 target_info.WriteMountOemScript(script_writer)
235 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
236
237 def test_WriteDeviceAssertions(self):
238 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
239 script_writer = test_utils.MockScriptWriter()
240 target_info.WriteDeviceAssertions(script_writer, False)
241 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
242
243 def test_WriteDeviceAssertions_with_oem_props(self):
244 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
245 self.TEST_OEM_DICTS)
246 script_writer = test_utils.MockScriptWriter()
247 target_info.WriteDeviceAssertions(script_writer, False)
248 self.assertEqual(
249 [
250 ('AssertOemProperty', 'ro.product.device',
251 ['device1', 'device2', 'device3'], False),
252 ('AssertOemProperty', 'ro.product.brand',
253 ['brand1', 'brand2', 'brand3'], False),
254 ],
255 script_writer.lines)
256
257
Tao Bao65b94e92018-10-11 21:57:26 -0700258class CommonZipTest(test_utils.ReleaseToolsTestCase):
259
Tao Bao31b08072017-11-08 15:50:59 -0800260 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700261 test_file_name=None, expected_stat=None, expected_mode=0o644,
262 expected_compress_type=zipfile.ZIP_STORED):
263 # Verify the stat if present.
264 if test_file_name is not None:
265 new_stat = os.stat(test_file_name)
266 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
267 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
268
269 # Reopen the zip file to verify.
270 zip_file = zipfile.ZipFile(zip_file_name, "r")
271
272 # Verify the timestamp.
273 info = zip_file.getinfo(arcname)
274 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
275
276 # Verify the file mode.
277 mode = (info.external_attr >> 16) & 0o777
278 self.assertEqual(mode, expected_mode)
279
280 # Verify the compress type.
281 self.assertEqual(info.compress_type, expected_compress_type)
282
283 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800284 entry = zip_file.open(arcname)
285 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700286 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800287 sha1_hash.update(chunk)
288 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700289 self.assertIsNone(zip_file.testzip())
290
Dan Albert8e0178d2015-01-27 15:53:15 -0800291 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
292 extra_zipwrite_args = dict(extra_zipwrite_args or {})
293
294 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800295 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700296
297 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800298 zip_file_name = zip_file.name
299
300 # File names within an archive strip the leading slash.
301 arcname = extra_zipwrite_args.get("arcname", test_file_name)
302 if arcname[0] == "/":
303 arcname = arcname[1:]
304
305 zip_file.close()
306 zip_file = zipfile.ZipFile(zip_file_name, "w")
307
308 try:
Tao Bao31b08072017-11-08 15:50:59 -0800309 sha1_hash = sha1()
310 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700311 sha1_hash.update(bytes(data))
312 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800313 test_file.close()
314
Tao Baof3282b42015-04-01 11:21:55 -0700315 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800316 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700317 expected_compress_type = extra_zipwrite_args.get("compress_type",
318 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800319 time.sleep(5) # Make sure the atime/mtime will change measurably.
320
321 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700322 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800323
Tao Bao31b08072017-11-08 15:50:59 -0800324 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
325 test_file_name, expected_stat, expected_mode,
326 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800327 finally:
328 os.remove(test_file_name)
329 os.remove(zip_file_name)
330
Tao Baof3282b42015-04-01 11:21:55 -0700331 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
332 extra_args = dict(extra_args or {})
333
334 zip_file = tempfile.NamedTemporaryFile(delete=False)
335 zip_file_name = zip_file.name
336 zip_file.close()
337
338 zip_file = zipfile.ZipFile(zip_file_name, "w")
339
340 try:
341 expected_compress_type = extra_args.get("compress_type",
342 zipfile.ZIP_STORED)
343 time.sleep(5) # Make sure the atime/mtime will change measurably.
344
345 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700346 arcname = zinfo_or_arcname
347 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700348 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700349 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700350 if zinfo_or_arcname.external_attr:
351 zinfo_perms = zinfo_or_arcname.external_attr >> 16
352 else:
353 zinfo_perms = 0o600
354 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700355
Tao Bao58c1b962015-05-20 09:32:18 -0700356 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700357 common.ZipClose(zip_file)
358
Tao Bao31b08072017-11-08 15:50:59 -0800359 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700360 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700361 expected_compress_type=expected_compress_type)
362 finally:
363 os.remove(zip_file_name)
364
365 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
366 extra_args = dict(extra_args or {})
367
368 zip_file = tempfile.NamedTemporaryFile(delete=False)
369 zip_file_name = zip_file.name
370
371 test_file = tempfile.NamedTemporaryFile(delete=False)
372 test_file_name = test_file.name
373
374 arcname_large = test_file_name
375 arcname_small = "bar"
376
377 # File names within an archive strip the leading slash.
378 if arcname_large[0] == "/":
379 arcname_large = arcname_large[1:]
380
381 zip_file.close()
382 zip_file = zipfile.ZipFile(zip_file_name, "w")
383
384 try:
Tao Bao31b08072017-11-08 15:50:59 -0800385 sha1_hash = sha1()
386 for data in large:
387 sha1_hash.update(data)
388 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700389 test_file.close()
390
391 expected_stat = os.stat(test_file_name)
392 expected_mode = 0o644
393 expected_compress_type = extra_args.get("compress_type",
394 zipfile.ZIP_STORED)
395 time.sleep(5) # Make sure the atime/mtime will change measurably.
396
397 common.ZipWrite(zip_file, test_file_name, **extra_args)
398 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
399 common.ZipClose(zip_file)
400
401 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800402 self._verify(zip_file, zip_file_name, arcname_large,
403 sha1_hash.hexdigest(), test_file_name, expected_stat,
404 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700405
406 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800407 self._verify(zip_file, zip_file_name, arcname_small,
408 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700409 expected_compress_type=expected_compress_type)
410 finally:
411 os.remove(zip_file_name)
412 os.remove(test_file_name)
413
414 def _test_reset_ZIP64_LIMIT(self, func, *args):
415 default_limit = (1 << 31) - 1
416 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
417 func(*args)
418 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
419
Dan Albert8e0178d2015-01-27 15:53:15 -0800420 def test_ZipWrite(self):
421 file_contents = os.urandom(1024)
422 self._test_ZipWrite(file_contents)
423
424 def test_ZipWrite_with_opts(self):
425 file_contents = os.urandom(1024)
426 self._test_ZipWrite(file_contents, {
427 "arcname": "foobar",
428 "perms": 0o777,
429 "compress_type": zipfile.ZIP_DEFLATED,
430 })
Tao Baof3282b42015-04-01 11:21:55 -0700431 self._test_ZipWrite(file_contents, {
432 "arcname": "foobar",
433 "perms": 0o700,
434 "compress_type": zipfile.ZIP_STORED,
435 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800436
437 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700438 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800439 self._test_ZipWrite(file_contents, {
440 "compress_type": zipfile.ZIP_DEFLATED,
441 })
442
443 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700444 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
445
446 def test_ZipWriteStr(self):
447 random_string = os.urandom(1024)
448 # Passing arcname
449 self._test_ZipWriteStr("foo", random_string)
450
451 # Passing zinfo
452 zinfo = zipfile.ZipInfo(filename="foo")
453 self._test_ZipWriteStr(zinfo, random_string)
454
455 # Timestamp in the zinfo should be overwritten.
456 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
457 self._test_ZipWriteStr(zinfo, random_string)
458
459 def test_ZipWriteStr_with_opts(self):
460 random_string = os.urandom(1024)
461 # Passing arcname
462 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700463 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700464 "compress_type": zipfile.ZIP_DEFLATED,
465 })
Tao Bao58c1b962015-05-20 09:32:18 -0700466 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700467 "compress_type": zipfile.ZIP_STORED,
468 })
469
470 # Passing zinfo
471 zinfo = zipfile.ZipInfo(filename="foo")
472 self._test_ZipWriteStr(zinfo, random_string, {
473 "compress_type": zipfile.ZIP_DEFLATED,
474 })
475 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700476 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700477 "compress_type": zipfile.ZIP_STORED,
478 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700479 self._test_ZipWriteStr(zinfo, random_string, {
480 "perms": 0o000,
481 "compress_type": zipfile.ZIP_STORED,
482 })
Tao Baof3282b42015-04-01 11:21:55 -0700483
484 def test_ZipWriteStr_large_file(self):
485 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
486 # the workaround. We will only test the case of writing a string into a
487 # large archive.
488 long_string = get_2gb_string()
489 short_string = os.urandom(1024)
490 self._test_ZipWriteStr_large_file(long_string, short_string, {
491 "compress_type": zipfile.ZIP_DEFLATED,
492 })
493
494 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700495 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700496 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700497 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700498
499 def test_bug21309935(self):
500 zip_file = tempfile.NamedTemporaryFile(delete=False)
501 zip_file_name = zip_file.name
502 zip_file.close()
503
504 try:
505 random_string = os.urandom(1024)
506 zip_file = zipfile.ZipFile(zip_file_name, "w")
507 # Default perms should be 0o644 when passing the filename.
508 common.ZipWriteStr(zip_file, "foo", random_string)
509 # Honor the specified perms.
510 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
511 # The perms in zinfo should be untouched.
512 zinfo = zipfile.ZipInfo(filename="baz")
513 zinfo.external_attr = 0o740 << 16
514 common.ZipWriteStr(zip_file, zinfo, random_string)
515 # Explicitly specified perms has the priority.
516 zinfo = zipfile.ZipInfo(filename="qux")
517 zinfo.external_attr = 0o700 << 16
518 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
519 common.ZipClose(zip_file)
520
Tao Bao31b08072017-11-08 15:50:59 -0800521 self._verify(zip_file, zip_file_name, "foo",
522 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700523 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800524 self._verify(zip_file, zip_file_name, "bar",
525 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700526 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800527 self._verify(zip_file, zip_file_name, "baz",
528 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700529 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800530 self._verify(zip_file, zip_file_name, "qux",
531 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700532 expected_mode=0o400)
533 finally:
534 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700535
Tao Bao82490d32019-04-09 00:12:30 -0700536 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800537 def test_ZipDelete(self):
538 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
539 output_zip = zipfile.ZipFile(zip_file.name, 'w',
540 compression=zipfile.ZIP_DEFLATED)
541 with tempfile.NamedTemporaryFile() as entry_file:
542 entry_file.write(os.urandom(1024))
543 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
544 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
545 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
546 common.ZipClose(output_zip)
547 zip_file.close()
548
549 try:
550 common.ZipDelete(zip_file.name, 'Test2')
551 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
552 entries = check_zip.namelist()
553 self.assertTrue('Test1' in entries)
554 self.assertFalse('Test2' in entries)
555 self.assertTrue('Test3' in entries)
556
Tao Bao986ee862018-10-04 15:46:16 -0700557 self.assertRaises(
558 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800559 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
560 entries = check_zip.namelist()
561 self.assertTrue('Test1' in entries)
562 self.assertFalse('Test2' in entries)
563 self.assertTrue('Test3' in entries)
564
565 common.ZipDelete(zip_file.name, ['Test3'])
566 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
567 entries = check_zip.namelist()
568 self.assertTrue('Test1' in entries)
569 self.assertFalse('Test2' in entries)
570 self.assertFalse('Test3' in entries)
571
572 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
573 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
574 entries = check_zip.namelist()
575 self.assertFalse('Test1' in entries)
576 self.assertFalse('Test2' in entries)
577 self.assertFalse('Test3' in entries)
578 finally:
579 os.remove(zip_file.name)
580
Tao Bao0ff15de2019-03-20 11:26:06 -0700581 @staticmethod
582 def _test_UnzipTemp_createZipFile():
583 zip_file = common.MakeTempFile(suffix='.zip')
584 output_zip = zipfile.ZipFile(
585 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
586 contents = os.urandom(1024)
587 with tempfile.NamedTemporaryFile() as entry_file:
588 entry_file.write(contents)
589 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
590 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
591 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
592 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
593 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
594 common.ZipClose(output_zip)
595 common.ZipClose(output_zip)
596 return zip_file
597
Tao Bao82490d32019-04-09 00:12:30 -0700598 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700599 def test_UnzipTemp(self):
600 zip_file = self._test_UnzipTemp_createZipFile()
601 unzipped_dir = common.UnzipTemp(zip_file)
602 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
603 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
604 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
605 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
606 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
607
Tao Bao82490d32019-04-09 00:12:30 -0700608 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700609 def test_UnzipTemp_withPatterns(self):
610 zip_file = self._test_UnzipTemp_createZipFile()
611
612 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
613 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
614 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
615 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
616 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
617 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
618
619 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
620 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
621 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
622 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
623 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
624 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
625
626 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
627 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
628 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
629 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
630 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
631 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
632
633 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
634 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
635 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
636 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
637 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
638 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
639
640 def test_UnzipTemp_withEmptyPatterns(self):
641 zip_file = self._test_UnzipTemp_createZipFile()
642 unzipped_dir = common.UnzipTemp(zip_file, [])
643 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
644 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
645 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
646 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
647 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
648
Tao Bao82490d32019-04-09 00:12:30 -0700649 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700650 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
651 zip_file = self._test_UnzipTemp_createZipFile()
652 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
653 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
654 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
655 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
656 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
657 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
658
659 def test_UnzipTemp_withNoMatchingPatterns(self):
660 zip_file = self._test_UnzipTemp_createZipFile()
661 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
662 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
663 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
664 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
665 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
666 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
667
Tao Bao89d7ab22017-12-14 17:05:33 -0800668
Tao Bao65b94e92018-10-11 21:57:26 -0700669class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800670 """Tests the APK utils related functions."""
671
672 APKCERTS_TXT1 = (
673 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
674 ' private_key="certs/devkey.pk8"\n'
675 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700676 ' certificate="build/make/target/product/security/platform.x509.pem"'
677 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800678 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
679 )
680
681 APKCERTS_CERTMAP1 = {
682 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700683 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800684 'TV.apk' : 'PRESIGNED',
685 }
686
687 APKCERTS_TXT2 = (
688 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
689 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
690 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
691 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
692 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
693 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
694 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
695 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
696 )
697
698 APKCERTS_CERTMAP2 = {
699 'Compressed1.apk' : 'certs/compressed1',
700 'Compressed2a.apk' : 'certs/compressed2',
701 'Compressed2b.apk' : 'certs/compressed2',
702 'Compressed3.apk' : 'certs/compressed3',
703 }
704
705 APKCERTS_TXT3 = (
706 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
707 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
708 )
709
710 APKCERTS_CERTMAP3 = {
711 'Compressed4.apk' : 'certs/compressed4',
712 }
713
Bill Peckham5c7b0342020-04-03 15:36:23 -0700714 # Test parsing with no optional fields, both optional fields, and only the
715 # partition optional field.
716 APKCERTS_TXT4 = (
717 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
718 ' private_key="certs/devkey.pk8"\n'
719 'name="Settings.apk"'
720 ' certificate="build/make/target/product/security/platform.x509.pem"'
721 ' private_key="build/make/target/product/security/platform.pk8"'
722 ' compressed="gz" partition="system"\n'
723 'name="TV.apk" certificate="PRESIGNED" private_key=""'
724 ' partition="product"\n'
725 )
726
727 APKCERTS_CERTMAP4 = {
728 'RecoveryLocalizer.apk' : 'certs/devkey',
729 'Settings.apk' : 'build/make/target/product/security/platform',
730 'TV.apk' : 'PRESIGNED',
731 }
732
Tao Bao17e4e612018-02-16 17:12:54 -0800733 def setUp(self):
734 self.testdata_dir = test_utils.get_testdata_dir()
735
Tao Bao818ddf52018-01-05 11:17:34 -0800736 @staticmethod
737 def _write_apkcerts_txt(apkcerts_txt, additional=None):
738 if additional is None:
739 additional = []
740 target_files = common.MakeTempFile(suffix='.zip')
741 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
742 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
743 for entry in additional:
744 target_files_zip.writestr(entry, '')
745 return target_files
746
747 def test_ReadApkCerts_NoncompressedApks(self):
748 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
749 with zipfile.ZipFile(target_files, 'r') as input_zip:
750 certmap, ext = common.ReadApkCerts(input_zip)
751
752 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
753 self.assertIsNone(ext)
754
755 def test_ReadApkCerts_CompressedApks(self):
756 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
757 # not stored in '.gz' format, so it shouldn't be considered as installed.
758 target_files = self._write_apkcerts_txt(
759 self.APKCERTS_TXT2,
760 ['Compressed1.apk.gz', 'Compressed3.apk'])
761
762 with zipfile.ZipFile(target_files, 'r') as input_zip:
763 certmap, ext = common.ReadApkCerts(input_zip)
764
765 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
766 self.assertEqual('.gz', ext)
767
768 # Alternative case with '.xz'.
769 target_files = self._write_apkcerts_txt(
770 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
771
772 with zipfile.ZipFile(target_files, 'r') as input_zip:
773 certmap, ext = common.ReadApkCerts(input_zip)
774
775 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
776 self.assertEqual('.xz', ext)
777
778 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
779 target_files = self._write_apkcerts_txt(
780 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
781 ['Compressed1.apk.gz', 'Compressed3.apk'])
782
783 with zipfile.ZipFile(target_files, 'r') as input_zip:
784 certmap, ext = common.ReadApkCerts(input_zip)
785
786 certmap_merged = self.APKCERTS_CERTMAP1.copy()
787 certmap_merged.update(self.APKCERTS_CERTMAP2)
788 self.assertDictEqual(certmap_merged, certmap)
789 self.assertEqual('.gz', ext)
790
791 def test_ReadApkCerts_MultipleCompressionMethods(self):
792 target_files = self._write_apkcerts_txt(
793 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
794 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
795
796 with zipfile.ZipFile(target_files, 'r') as input_zip:
797 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
798
799 def test_ReadApkCerts_MismatchingKeys(self):
800 malformed_apkcerts_txt = (
801 'name="App1.apk" certificate="certs/cert1.x509.pem"'
802 ' private_key="certs/cert2.pk8"\n'
803 )
804 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
805
806 with zipfile.ZipFile(target_files, 'r') as input_zip:
807 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
808
Bill Peckham5c7b0342020-04-03 15:36:23 -0700809 def test_ReadApkCerts_WithWithoutOptionalFields(self):
810 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
811 with zipfile.ZipFile(target_files, 'r') as input_zip:
812 certmap, ext = common.ReadApkCerts(input_zip)
813
814 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
815 self.assertIsNone(ext)
816
Tao Bao04e1f012018-02-04 12:13:35 -0800817 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800818 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
819 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800820 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800821 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
822
823 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800824 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800825 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
826
Tao Bao82490d32019-04-09 00:12:30 -0700827 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700828 def test_ExtractAvbPublicKey(self):
829 privkey = os.path.join(self.testdata_dir, 'testkey.key')
830 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700831 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
832 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
833 with open(extracted_from_privkey, 'rb') as privkey_fp, \
834 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700835 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
836
Tao Bao17e4e612018-02-16 17:12:54 -0800837 def test_ParseCertificate(self):
838 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
839
840 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800841 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
842 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800843 expected, _ = proc.communicate()
844 self.assertEqual(0, proc.returncode)
845
846 with open(cert) as cert_fp:
847 actual = common.ParseCertificate(cert_fp.read())
848 self.assertEqual(expected, actual)
849
Tao Bao82490d32019-04-09 00:12:30 -0700850 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700851 def test_GetMinSdkVersion(self):
852 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
853 self.assertEqual('24', common.GetMinSdkVersion(test_app))
854
Tao Bao82490d32019-04-09 00:12:30 -0700855 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700856 def test_GetMinSdkVersion_invalidInput(self):
857 self.assertRaises(
858 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
859
Tao Bao82490d32019-04-09 00:12:30 -0700860 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700861 def test_GetMinSdkVersionInt(self):
862 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
863 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
864
Tao Bao82490d32019-04-09 00:12:30 -0700865 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700866 def test_GetMinSdkVersionInt_invalidInput(self):
867 self.assertRaises(
868 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
869 {})
870
Tao Bao818ddf52018-01-05 11:17:34 -0800871
Tao Bao65b94e92018-10-11 21:57:26 -0700872class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800873
Tao Bao02a08592018-07-22 12:40:45 -0700874 def setUp(self):
875 self.testdata_dir = test_utils.get_testdata_dir()
876
Tao Bao82490d32019-04-09 00:12:30 -0700877 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800878 def test_GetSparseImage_emptyBlockMapFile(self):
879 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
880 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
881 target_files_zip.write(
882 test_utils.construct_sparse_image([
883 (0xCAC1, 6),
884 (0xCAC3, 3),
885 (0xCAC1, 4)]),
886 arcname='IMAGES/system.img')
887 target_files_zip.writestr('IMAGES/system.map', '')
888 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
889 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
890
Tao Baodba59ee2018-01-09 13:21:02 -0800891 tempdir = common.UnzipTemp(target_files)
892 with zipfile.ZipFile(target_files, 'r') as input_zip:
893 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800894
895 self.assertDictEqual(
896 {
897 '__COPY': RangeSet("0"),
898 '__NONZERO-0': RangeSet("1-5 9-12"),
899 },
900 sparse_image.file_map)
901
Tao Baob2de7d92019-04-10 10:01:47 -0700902 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800903 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700904 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
905 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800906 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700907 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
908 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800909
Tao Bao82490d32019-04-09 00:12:30 -0700910 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800911 def test_GetSparseImage_missingBlockMapFile(self):
912 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
913 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
914 target_files_zip.write(
915 test_utils.construct_sparse_image([
916 (0xCAC1, 6),
917 (0xCAC3, 3),
918 (0xCAC1, 4)]),
919 arcname='IMAGES/system.img')
920 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
921 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
922
Tao Baodba59ee2018-01-09 13:21:02 -0800923 tempdir = common.UnzipTemp(target_files)
924 with zipfile.ZipFile(target_files, 'r') as input_zip:
925 self.assertRaises(
926 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
927 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800928
Tao Bao82490d32019-04-09 00:12:30 -0700929 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800930 def test_GetSparseImage_sharedBlocks_notAllowed(self):
931 """Tests the case of having overlapping blocks but disallowed."""
932 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
933 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
934 target_files_zip.write(
935 test_utils.construct_sparse_image([(0xCAC2, 16)]),
936 arcname='IMAGES/system.img')
937 # Block 10 is shared between two files.
938 target_files_zip.writestr(
939 'IMAGES/system.map',
940 '\n'.join([
941 '/system/file1 1-5 9-10',
942 '/system/file2 10-12']))
943 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
944 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
945
Tao Baodba59ee2018-01-09 13:21:02 -0800946 tempdir = common.UnzipTemp(target_files)
947 with zipfile.ZipFile(target_files, 'r') as input_zip:
948 self.assertRaises(
949 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
950 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800951
Tao Bao82490d32019-04-09 00:12:30 -0700952 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800953 def test_GetSparseImage_sharedBlocks_allowed(self):
954 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
955 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
956 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
957 # Construct an image with a care_map of "0-5 9-12".
958 target_files_zip.write(
959 test_utils.construct_sparse_image([(0xCAC2, 16)]),
960 arcname='IMAGES/system.img')
961 # Block 10 is shared between two files.
962 target_files_zip.writestr(
963 'IMAGES/system.map',
964 '\n'.join([
965 '/system/file1 1-5 9-10',
966 '/system/file2 10-12']))
967 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
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, True)
Tao Baofc7e0e02018-02-13 13:54:02 -0800973
974 self.assertDictEqual(
975 {
976 '__COPY': RangeSet("0"),
977 '__NONZERO-0': RangeSet("6-8 13-15"),
978 '/system/file1': RangeSet("1-5 9-10"),
979 '/system/file2': RangeSet("11-12"),
980 },
981 sparse_image.file_map)
982
983 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
984 # 'incomplete'.
985 self.assertTrue(
986 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
987 self.assertNotIn(
988 'incomplete', sparse_image.file_map['/system/file2'].extra)
989
Tao Baoa264fef2019-10-06 21:55:20 -0700990 # '/system/file1' will only contain one field -- a copy of the input text.
991 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
992
993 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -0800994 self.assertFalse(sparse_image.file_map['__COPY'].extra)
995 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -0800996
Tao Bao82490d32019-04-09 00:12:30 -0700997 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800998 def test_GetSparseImage_incompleteRanges(self):
999 """Tests the case of ext4 images with holes."""
1000 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1001 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1002 target_files_zip.write(
1003 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1004 arcname='IMAGES/system.img')
1005 target_files_zip.writestr(
1006 'IMAGES/system.map',
1007 '\n'.join([
1008 '/system/file1 1-5 9-10',
1009 '/system/file2 11-12']))
1010 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1011 # '/system/file2' has less blocks listed (2) than actual (3).
1012 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1013
Tao Baodba59ee2018-01-09 13:21:02 -08001014 tempdir = common.UnzipTemp(target_files)
1015 with zipfile.ZipFile(target_files, 'r') as input_zip:
1016 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001017
Tao Baoa264fef2019-10-06 21:55:20 -07001018 self.assertEqual(
1019 '1-5 9-10',
1020 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001021 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1022
Tao Bao82490d32019-04-09 00:12:30 -07001023 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001024 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1025 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1026 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1027 target_files_zip.write(
1028 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1029 arcname='IMAGES/system.img')
1030 target_files_zip.writestr(
1031 'IMAGES/system.map',
1032 '\n'.join([
1033 '//system/file1 1-5 9-10',
1034 '//system/file2 11-12',
1035 '/system/app/file3 13-15']))
1036 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1037 # '/system/file2' has less blocks listed (2) than actual (3).
1038 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1039 # '/system/app/file3' has less blocks listed (3) than actual (4).
1040 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1041
1042 tempdir = common.UnzipTemp(target_files)
1043 with zipfile.ZipFile(target_files, 'r') as input_zip:
1044 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1045
Tao Baoa264fef2019-10-06 21:55:20 -07001046 self.assertEqual(
1047 '1-5 9-10',
1048 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001049 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1050 self.assertTrue(
1051 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1052
Tao Bao82490d32019-04-09 00:12:30 -07001053 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001054 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1055 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1056 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1057 target_files_zip.write(
1058 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1059 arcname='IMAGES/system.img')
1060 target_files_zip.writestr(
1061 'IMAGES/system.map',
1062 '\n'.join([
1063 '//system/file1 1-5 9-10',
1064 '//init.rc 13-15']))
1065 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1066 # '/init.rc' has less blocks listed (3) than actual (4).
1067 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1068
1069 tempdir = common.UnzipTemp(target_files)
1070 with zipfile.ZipFile(target_files, 'r') as input_zip:
1071 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1072
Tao Baoa264fef2019-10-06 21:55:20 -07001073 self.assertEqual(
1074 '1-5 9-10',
1075 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001076 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1077
Tao Bao82490d32019-04-09 00:12:30 -07001078 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001079 def test_GetSparseImage_fileNotFound(self):
1080 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1081 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1082 target_files_zip.write(
1083 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1084 arcname='IMAGES/system.img')
1085 target_files_zip.writestr(
1086 'IMAGES/system.map',
1087 '\n'.join([
1088 '//system/file1 1-5 9-10',
1089 '//system/file2 11-12']))
1090 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1091
1092 tempdir = common.UnzipTemp(target_files)
1093 with zipfile.ZipFile(target_files, 'r') as input_zip:
1094 self.assertRaises(
1095 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1096 False)
1097
Tao Bao82490d32019-04-09 00:12:30 -07001098 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001099 def test_GetAvbChainedPartitionArg(self):
1100 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1101 info_dict = {
1102 'avb_avbtool': 'avbtool',
1103 'avb_system_key_path': pubkey,
1104 'avb_system_rollback_index_location': 2,
1105 }
1106 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1107 self.assertEqual(3, len(args))
1108 self.assertEqual('system', args[0])
1109 self.assertEqual('2', args[1])
1110 self.assertTrue(os.path.exists(args[2]))
1111
Tao Bao82490d32019-04-09 00:12:30 -07001112 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001113 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1114 key = os.path.join(self.testdata_dir, 'testkey.key')
1115 info_dict = {
1116 'avb_avbtool': 'avbtool',
1117 'avb_product_key_path': key,
1118 'avb_product_rollback_index_location': 2,
1119 }
1120 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1121 self.assertEqual(3, len(args))
1122 self.assertEqual('product', args[0])
1123 self.assertEqual('2', args[1])
1124 self.assertTrue(os.path.exists(args[2]))
1125
Tao Bao82490d32019-04-09 00:12:30 -07001126 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001127 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1128 info_dict = {
1129 'avb_avbtool': 'avbtool',
1130 'avb_system_key_path': 'does-not-exist',
1131 'avb_system_rollback_index_location': 2,
1132 }
1133 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1134 args = common.GetAvbChainedPartitionArg(
1135 'system', info_dict, pubkey).split(':')
1136 self.assertEqual(3, len(args))
1137 self.assertEqual('system', args[0])
1138 self.assertEqual('2', args[1])
1139 self.assertTrue(os.path.exists(args[2]))
1140
Tao Bao82490d32019-04-09 00:12:30 -07001141 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001142 def test_GetAvbChainedPartitionArg_invalidKey(self):
1143 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1144 info_dict = {
1145 'avb_avbtool': 'avbtool',
1146 'avb_system_key_path': pubkey,
1147 'avb_system_rollback_index_location': 2,
1148 }
1149 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001150 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1151 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001152
Tao Baoa57ab9f2018-08-24 12:08:38 -07001153 INFO_DICT_DEFAULT = {
1154 'recovery_api_version': 3,
1155 'fstab_version': 2,
1156 'system_root_image': 'true',
1157 'no_recovery' : 'true',
1158 'recovery_as_boot': 'true',
1159 }
1160
Daniel Norman4cc9df62019-07-18 10:11:07 -07001161 def test_LoadListFromFile(self):
1162 file_path = os.path.join(self.testdata_dir,
1163 'merge_config_framework_item_list')
1164 contents = common.LoadListFromFile(file_path)
1165 expected_contents = [
1166 'META/apkcerts.txt',
1167 'META/filesystem_config.txt',
1168 'META/root_filesystem_config.txt',
1169 'META/system_manifest.xml',
1170 'META/system_matrix.xml',
1171 'META/update_engine_config.txt',
1172 'PRODUCT/*',
1173 'ROOT/*',
1174 'SYSTEM/*',
1175 ]
1176 self.assertEqual(sorted(contents), sorted(expected_contents))
1177
Tao Baoa57ab9f2018-08-24 12:08:38 -07001178 @staticmethod
1179 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1180 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1181 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1182 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001183 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001184 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1185
1186 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1187 if info_dict.get('system_root_image') == 'true':
1188 fstab_values = FSTAB_TEMPLATE.format('/')
1189 else:
1190 fstab_values = FSTAB_TEMPLATE.format('/system')
1191 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001192
1193 common.ZipWriteStr(
1194 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001195 return target_files
1196
1197 def test_LoadInfoDict(self):
1198 target_files = self._test_LoadInfoDict_createTargetFiles(
1199 self.INFO_DICT_DEFAULT,
1200 'BOOT/RAMDISK/system/etc/recovery.fstab')
1201 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1202 loaded_dict = common.LoadInfoDict(target_files_zip)
1203 self.assertEqual(3, loaded_dict['recovery_api_version'])
1204 self.assertEqual(2, loaded_dict['fstab_version'])
1205 self.assertIn('/', loaded_dict['fstab'])
1206 self.assertIn('/system', loaded_dict['fstab'])
1207
1208 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1209 target_files = self._test_LoadInfoDict_createTargetFiles(
1210 self.INFO_DICT_DEFAULT,
1211 'BOOT/RAMDISK/etc/recovery.fstab')
1212 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1213 loaded_dict = common.LoadInfoDict(target_files_zip)
1214 self.assertEqual(3, loaded_dict['recovery_api_version'])
1215 self.assertEqual(2, loaded_dict['fstab_version'])
1216 self.assertIn('/', loaded_dict['fstab'])
1217 self.assertIn('/system', loaded_dict['fstab'])
1218
Tao Bao82490d32019-04-09 00:12:30 -07001219 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001220 def test_LoadInfoDict_dirInput(self):
1221 target_files = self._test_LoadInfoDict_createTargetFiles(
1222 self.INFO_DICT_DEFAULT,
1223 'BOOT/RAMDISK/system/etc/recovery.fstab')
1224 unzipped = common.UnzipTemp(target_files)
1225 loaded_dict = common.LoadInfoDict(unzipped)
1226 self.assertEqual(3, loaded_dict['recovery_api_version'])
1227 self.assertEqual(2, loaded_dict['fstab_version'])
1228 self.assertIn('/', loaded_dict['fstab'])
1229 self.assertIn('/system', loaded_dict['fstab'])
1230
Tao Bao82490d32019-04-09 00:12:30 -07001231 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001232 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1233 target_files = self._test_LoadInfoDict_createTargetFiles(
1234 self.INFO_DICT_DEFAULT,
1235 'BOOT/RAMDISK/system/etc/recovery.fstab')
1236 unzipped = common.UnzipTemp(target_files)
1237 loaded_dict = common.LoadInfoDict(unzipped)
1238 self.assertEqual(3, loaded_dict['recovery_api_version'])
1239 self.assertEqual(2, loaded_dict['fstab_version'])
1240 self.assertIn('/', loaded_dict['fstab'])
1241 self.assertIn('/system', loaded_dict['fstab'])
1242
1243 def test_LoadInfoDict_systemRootImageFalse(self):
1244 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1245 # launched prior to P will likely have this config.
1246 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1247 del info_dict['no_recovery']
1248 del info_dict['system_root_image']
1249 del info_dict['recovery_as_boot']
1250 target_files = self._test_LoadInfoDict_createTargetFiles(
1251 info_dict,
1252 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1253 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1254 loaded_dict = common.LoadInfoDict(target_files_zip)
1255 self.assertEqual(3, loaded_dict['recovery_api_version'])
1256 self.assertEqual(2, loaded_dict['fstab_version'])
1257 self.assertNotIn('/', loaded_dict['fstab'])
1258 self.assertIn('/system', loaded_dict['fstab'])
1259
1260 def test_LoadInfoDict_recoveryAsBootFalse(self):
1261 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1262 # devices launched since P will likely have this config.
1263 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1264 del info_dict['no_recovery']
1265 del info_dict['recovery_as_boot']
1266 target_files = self._test_LoadInfoDict_createTargetFiles(
1267 info_dict,
1268 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1269 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1270 loaded_dict = common.LoadInfoDict(target_files_zip)
1271 self.assertEqual(3, loaded_dict['recovery_api_version'])
1272 self.assertEqual(2, loaded_dict['fstab_version'])
1273 self.assertIn('/', loaded_dict['fstab'])
1274 self.assertIn('/system', loaded_dict['fstab'])
1275
1276 def test_LoadInfoDict_noRecoveryTrue(self):
1277 # Device doesn't have a recovery partition at all.
1278 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1279 del info_dict['recovery_as_boot']
1280 target_files = self._test_LoadInfoDict_createTargetFiles(
1281 info_dict,
1282 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1283 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1284 loaded_dict = common.LoadInfoDict(target_files_zip)
1285 self.assertEqual(3, loaded_dict['recovery_api_version'])
1286 self.assertEqual(2, loaded_dict['fstab_version'])
1287 self.assertIsNone(loaded_dict['fstab'])
1288
Tao Bao82490d32019-04-09 00:12:30 -07001289 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001290 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1291 target_files = self._test_LoadInfoDict_createTargetFiles(
1292 self.INFO_DICT_DEFAULT,
1293 'BOOT/RAMDISK/system/etc/recovery.fstab')
1294 common.ZipDelete(target_files, 'META/misc_info.txt')
1295 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1296 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1297
Tao Bao82490d32019-04-09 00:12:30 -07001298 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001299 def test_LoadInfoDict_repacking(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, True)
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 self.assertEqual(
1310 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1311 self.assertEqual(
1312 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1313 loaded_dict['root_fs_config'])
1314
1315 def test_LoadInfoDict_repackingWithZipFileInput(self):
1316 target_files = self._test_LoadInfoDict_createTargetFiles(
1317 self.INFO_DICT_DEFAULT,
1318 'BOOT/RAMDISK/system/etc/recovery.fstab')
1319 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1320 self.assertRaises(
1321 AssertionError, common.LoadInfoDict, target_files_zip, True)
1322
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001323 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1324 framework_dict = {
1325 'super_partition_groups': 'group_a',
1326 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001327 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001328 }
1329 vendor_dict = {
1330 'super_partition_groups': 'group_a group_b',
1331 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001332 'super_group_a_partition_list': 'vendor',
1333 'super_group_a_group_size': '1000',
1334 'super_group_b_partition_list': 'product',
1335 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001336 }
1337 merged_dict = common.MergeDynamicPartitionInfoDicts(
1338 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001339 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001340 expected_merged_dict = {
1341 'super_partition_groups': 'group_a group_b',
1342 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001343 'super_group_a_partition_list': 'system vendor',
1344 'super_group_a_group_size': '1000',
1345 'super_group_b_partition_list': 'product',
1346 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001347 }
1348 self.assertEqual(merged_dict, expected_merged_dict)
1349
1350 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1351 framework_dict = {
1352 'super_partition_groups': 'group_a',
1353 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001354 'super_group_a_partition_list': 'system',
1355 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001356 }
1357 vendor_dict = {
1358 'super_partition_groups': 'group_a group_b',
1359 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001360 'super_group_a_partition_list': 'vendor',
1361 'super_group_a_group_size': '1000',
1362 'super_group_b_partition_list': 'product',
1363 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001364 }
1365 merged_dict = common.MergeDynamicPartitionInfoDicts(
1366 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001367 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001368 expected_merged_dict = {
1369 'super_partition_groups': 'group_a group_b',
1370 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001371 'super_group_a_partition_list': 'system vendor',
1372 'super_group_a_group_size': '1000',
1373 'super_group_b_partition_list': 'product',
1374 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001375 }
1376 self.assertEqual(merged_dict, expected_merged_dict)
1377
Daniel Norman276f0622019-07-26 14:13:51 -07001378 def test_GetAvbPartitionArg(self):
1379 info_dict = {}
1380 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1381 self.assertEqual(
1382 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1383
1384 @test_utils.SkipIfExternalToolsUnavailable()
1385 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1386 testdata_dir = test_utils.get_testdata_dir()
1387 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1388 info_dict = {
1389 'avb_avbtool': 'avbtool',
1390 'avb_vendor_key_path': pubkey,
1391 'avb_vendor_rollback_index_location': 5,
1392 }
1393 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1394 self.assertEqual(2, len(cmd))
1395 self.assertEqual('--chain_partition', cmd[0])
1396 chained_partition_args = cmd[1].split(':')
1397 self.assertEqual(3, len(chained_partition_args))
1398 self.assertEqual('vendor', chained_partition_args[0])
1399 self.assertEqual('5', chained_partition_args[1])
1400 self.assertTrue(os.path.exists(chained_partition_args[2]))
1401
Tao Bao3612c882019-10-14 17:49:31 -07001402 @test_utils.SkipIfExternalToolsUnavailable()
1403 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1404 testdata_dir = test_utils.get_testdata_dir()
1405 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1406 info_dict = {
1407 'avb_avbtool': 'avbtool',
1408 'avb_recovery_key_path': pubkey,
1409 'avb_recovery_rollback_index_location': 3,
1410 }
1411 cmd = common.GetAvbPartitionArg(
1412 'recovery', '/path/to/recovery.img', info_dict)
1413 self.assertFalse(cmd)
1414
1415 @test_utils.SkipIfExternalToolsUnavailable()
1416 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1417 testdata_dir = test_utils.get_testdata_dir()
1418 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1419 info_dict = {
1420 'ab_update': 'true',
1421 'avb_avbtool': 'avbtool',
1422 'avb_recovery_key_path': pubkey,
1423 'avb_recovery_rollback_index_location': 3,
1424 }
1425 cmd = common.GetAvbPartitionArg(
1426 'recovery', '/path/to/recovery.img', info_dict)
1427 self.assertEqual(2, len(cmd))
1428 self.assertEqual('--chain_partition', cmd[0])
1429 chained_partition_args = cmd[1].split(':')
1430 self.assertEqual(3, len(chained_partition_args))
1431 self.assertEqual('recovery', chained_partition_args[0])
1432 self.assertEqual('3', chained_partition_args[1])
1433 self.assertTrue(os.path.exists(chained_partition_args[2]))
1434
Tianjie20dd8f22020-04-19 15:51:16 -07001435 def test_BuildVBMeta_appendAftlCommandSyntax(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 }
1447 common.OPTIONS.aftl_tool_path = 'aftltool'
1448 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 vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
1455 cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
1456 expected_cmd = [
1457 'aftltool', 'make_icp_from_vbmeta',
1458 '--vbmeta_image_path', 'place_holder',
1459 '--output', vbmeta_image.name,
1460 '--version_incremental', '6285659',
1461 '--transparency_log_servers',
1462 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
1463 common.OPTIONS.aftl_key_path),
1464 '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
1465 '--algorithm', 'SHA256_RSA4096',
1466 '--padding', '4096']
1467
1468 # ignore the place holder, i.e. path to a temp file
1469 self.assertEqual(cmd[:3], expected_cmd[:3])
1470 self.assertEqual(cmd[4:], expected_cmd[4:])
1471
1472 @unittest.skip("enable after we have a server for public")
1473 def test_BuildVBMeta_appendAftlContactServer(self):
Tianjie Xueaed60c2020-03-12 00:33:28 -07001474 testdata_dir = test_utils.get_testdata_dir()
1475 common.OPTIONS.info_dict = {
1476 'ab_update': 'true',
1477 'avb_avbtool': 'avbtool',
1478 'build.prop': {
1479 'ro.build.version.incremental': '6285659',
1480 'ro.product.device': 'coral',
1481 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1482 '6285659:userdebug/dev-keys'
1483 }
1484 }
Tianjie0f307452020-04-01 12:20:21 -07001485 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001486 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1487 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1488 'test_transparency_key.pub')
1489 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1490 testdata_dir, 'test_aftl_rsa4096.pem')
1491
1492 input_dir = common.MakeTempDir()
1493 system_image = common.MakeTempFile()
1494 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1495 '/system', str(4096 * 100), '-j', '0', '-s']
1496 common.RunAndCheckOutput(build_image_cmd)
1497
1498 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1499 '--partition_size', str(4096 * 150),
1500 '--partition_name', 'system',
1501 '--image', system_image]
1502 common.RunAndCheckOutput(add_footer_cmd)
1503
1504 vbmeta_image = common.MakeTempFile()
1505 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1506 ['system'])
1507
1508 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1509 vbmeta_image, '--transparency_log_pub_keys',
1510 common.OPTIONS.aftl_key_path]
1511 common.RunAndCheckOutput(verify_cmd)
1512
Tao Baofc7e0e02018-02-13 13:54:02 -08001513
Tao Bao65b94e92018-10-11 21:57:26 -07001514class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001515 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001516
Tao Bao1c830bf2017-12-25 10:43:47 -08001517 Its format should match between common.py and validate_target_files.py.
1518 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001519
1520 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001521 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001522 # Create a dummy dict that contains the fstab info for boot&recovery.
1523 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001524 dummy_fstab = [
1525 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1526 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001527 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001528 # Construct the gzipped recovery.img and boot.img
1529 self.recovery_data = bytearray([
1530 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1531 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1532 0x08, 0x00, 0x00, 0x00
1533 ])
1534 # echo -n "boot" | gzip -f | hd
1535 self.boot_data = bytearray([
1536 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1537 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1538 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001539
1540 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1541 loc = os.path.join(self._tempdir, prefix, name)
1542 if not os.path.exists(os.path.dirname(loc)):
1543 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001544 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001545 f.write(data)
1546
1547 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001548 recovery_image = common.File("recovery.img", self.recovery_data)
1549 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001550 self._info["full_recovery_image"] = "true"
1551
1552 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1553 recovery_image, boot_image, self._info)
1554 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1555 self._info)
1556
Tao Bao82490d32019-04-09 00:12:30 -07001557 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001558 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001559 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001560 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001561 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001562 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1563
1564 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1565 recovery_image, boot_image, self._info)
1566 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1567 self._info)
1568 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001569 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001570 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1571 recovery_image, boot_image, self._info)
1572 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1573 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001574
1575
Yifan Hong45433e42019-01-18 13:55:25 -08001576class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001577
Yifan Hong45433e42019-01-18 13:55:25 -08001578 def __init__(self, partition, tgt, src=None):
1579 self.partition = partition
1580 self.tgt = tgt
1581 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001582
Yifan Hong45433e42019-01-18 13:55:25 -08001583 def WriteScript(self, script, _, progress=None,
1584 write_verify_script=False):
1585 if progress:
1586 script.AppendExtra("progress({})".format(progress))
1587 script.AppendExtra("patch({});".format(self.partition))
1588 if write_verify_script:
1589 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001590
Yifan Hong45433e42019-01-18 13:55:25 -08001591 def WritePostInstallVerifyScript(self, script):
1592 script.AppendExtra("verify({});".format(self.partition))
1593
1594
1595class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001596
Yifan Hong45433e42019-01-18 13:55:25 -08001597 def __init__(self, size):
1598 self.blocksize = 4096
1599 self.total_blocks = size // 4096
1600 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1601
1602
1603class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001604
Yifan Hong45433e42019-01-18 13:55:25 -08001605 @staticmethod
1606 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001607 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001608 with output_zip.open('dynamic_partitions_op_list') as op_list:
1609 return [line.decode().strip() for line in op_list.readlines()
1610 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001611
1612 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001613 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001614 self.output_path = common.MakeTempFile(suffix='.zip')
1615
1616 def test_full(self):
1617 target_info = common.LoadDictionaryFromLines("""
1618dynamic_partition_list=system vendor
1619super_partition_groups=group_foo
1620super_group_foo_group_size={group_size}
1621super_group_foo_partition_list=system vendor
1622""".format(group_size=4 * GiB).split("\n"))
1623 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1624 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1625
1626 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1627 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1628 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1629
1630 self.assertEqual(str(self.script).strip(), """
1631assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001632patch(system);
1633verify(system);
1634unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001635patch(vendor);
1636verify(vendor);
1637unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001638""".strip())
1639
1640 lines = self.get_op_list(self.output_path)
1641
1642 remove_all_groups = lines.index("remove_all_groups")
1643 add_group = lines.index("add_group group_foo 4294967296")
1644 add_vendor = lines.index("add vendor group_foo")
1645 add_system = lines.index("add system group_foo")
1646 resize_vendor = lines.index("resize vendor 1073741824")
1647 resize_system = lines.index("resize system 3221225472")
1648
1649 self.assertLess(remove_all_groups, add_group,
1650 "Should add groups after removing all groups")
1651 self.assertLess(add_group, min(add_vendor, add_system),
1652 "Should add partitions after adding group")
1653 self.assertLess(add_system, resize_system,
1654 "Should resize system after adding it")
1655 self.assertLess(add_vendor, resize_vendor,
1656 "Should resize vendor after adding it")
1657
1658 def test_inc_groups(self):
1659 source_info = common.LoadDictionaryFromLines("""
1660super_partition_groups=group_foo group_bar group_baz
1661super_group_foo_group_size={group_foo_size}
1662super_group_bar_group_size={group_bar_size}
1663""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1664 target_info = common.LoadDictionaryFromLines("""
1665super_partition_groups=group_foo group_baz group_qux
1666super_group_foo_group_size={group_foo_size}
1667super_group_baz_group_size={group_baz_size}
1668super_group_qux_group_size={group_qux_size}
1669""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1670 group_qux_size=1 * GiB).split("\n"))
1671
1672 dp_diff = common.DynamicPartitionsDifference(target_info,
1673 block_diffs=[],
1674 source_info_dict=source_info)
1675 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1676 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1677
1678 lines = self.get_op_list(self.output_path)
1679
1680 removed = lines.index("remove_group group_bar")
1681 shrunk = lines.index("resize_group group_foo 3221225472")
1682 grown = lines.index("resize_group group_baz 4294967296")
1683 added = lines.index("add_group group_qux 1073741824")
1684
Tao Baof1113e92019-06-18 12:10:14 -07001685 self.assertLess(max(removed, shrunk),
1686 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001687 "ops that remove / shrink partitions must precede ops that "
1688 "grow / add partitions")
1689
Yifan Hongbb2658d2019-01-25 12:30:58 -08001690 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001691 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001692dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001693super_partition_groups=group_foo
1694super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001695super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001696""".format(group_foo_size=4 * GiB).split("\n"))
1697 target_info = common.LoadDictionaryFromLines("""
1698dynamic_partition_list=system vendor product odm
1699super_partition_groups=group_foo group_bar
1700super_group_foo_group_size={group_foo_size}
1701super_group_foo_partition_list=system vendor odm
1702super_group_bar_group_size={group_bar_size}
1703super_group_bar_partition_list=product
1704""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1705
1706 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1707 src=FakeSparseImage(1024 * MiB)),
1708 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1709 src=FakeSparseImage(1024 * MiB)),
1710 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1711 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001712 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001713 src=FakeSparseImage(1024 * MiB)),
1714 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1715 src=None)]
1716
1717 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1718 source_info_dict=source_info)
1719 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1720 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1721
1722 metadata_idx = self.script.lines.index(
1723 'assert(update_dynamic_partitions(package_extract_file('
1724 '"dynamic_partitions_op_list")));')
1725 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1726 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1727 for p in ("product", "system", "odm"):
1728 patch_idx = self.script.lines.index("patch({});".format(p))
1729 verify_idx = self.script.lines.index("verify({});".format(p))
1730 self.assertLess(metadata_idx, patch_idx,
1731 "Should patch {} after updating metadata".format(p))
1732 self.assertLess(patch_idx, verify_idx,
1733 "Should verify {} after patching".format(p))
1734
Justin Yun6151e3f2019-06-25 15:58:13 +09001735 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001736
1737 lines = self.get_op_list(self.output_path)
1738
Justin Yun6151e3f2019-06-25 15:58:13 +09001739 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001740 move_product_out = lines.index("move product default")
1741 shrink = lines.index("resize vendor 536870912")
1742 shrink_group = lines.index("resize_group group_foo 3221225472")
1743 add_group_bar = lines.index("add_group group_bar 1073741824")
1744 add_odm = lines.index("add odm group_foo")
1745 grow_existing = lines.index("resize system 1610612736")
1746 grow_added = lines.index("resize odm 1073741824")
1747 move_product_in = lines.index("move product group_bar")
1748
1749 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1750 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1751
1752 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1753 "Must shrink group after partitions inside group are shrunk"
1754 " / removed")
1755
1756 self.assertLess(add_group_bar, move_product_in,
1757 "Must add partitions to group after group is added")
1758
1759 self.assertLess(max_idx_move_partition_out_foo,
1760 min_idx_move_partition_in_foo,
1761 "Must shrink partitions / remove partitions from group"
1762 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001763
1764 def test_remove_partition(self):
1765 source_info = common.LoadDictionaryFromLines("""
1766blockimgdiff_versions=3,4
1767use_dynamic_partitions=true
1768dynamic_partition_list=foo
1769super_partition_groups=group_foo
1770super_group_foo_group_size={group_foo_size}
1771super_group_foo_partition_list=foo
1772""".format(group_foo_size=4 * GiB).split("\n"))
1773 target_info = common.LoadDictionaryFromLines("""
1774blockimgdiff_versions=3,4
1775use_dynamic_partitions=true
1776super_partition_groups=group_foo
1777super_group_foo_group_size={group_foo_size}
1778""".format(group_foo_size=4 * GiB).split("\n"))
1779
1780 common.OPTIONS.info_dict = target_info
1781 common.OPTIONS.target_info_dict = target_info
1782 common.OPTIONS.source_info_dict = source_info
1783 common.OPTIONS.cache_size = 4 * 4096
1784
1785 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1786 src=DataImage("source", pad=True))]
1787
1788 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1789 source_info_dict=source_info)
1790 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1791 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1792
1793 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001794 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001795
1796 lines = self.get_op_list(self.output_path)
1797 self.assertEqual(lines, ["remove foo"])