blob: 53b5b76bdd2bd02a8ab1ccfb9410c59743e14814 [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 },
56 'vendor.build.prop' : {
57 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
58 },
59 'property1' : 'value1',
60 'property2' : 4096,
61 }
62
63 TEST_INFO_DICT_USES_OEM_PROPS = {
64 'build.prop' : {
65 'ro.product.name' : 'product-name',
66 'ro.build.thumbprint' : 'build-thumbprint',
67 'ro.build.bar' : 'build-bar',
68 },
69 'vendor.build.prop' : {
70 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
71 },
72 'property1' : 'value1',
73 'property2' : 4096,
74 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
75 }
76
77 TEST_OEM_DICTS = [
78 {
79 'ro.product.brand' : 'brand1',
80 'ro.product.device' : 'device1',
81 },
82 {
83 'ro.product.brand' : 'brand2',
84 'ro.product.device' : 'device2',
85 },
86 {
87 'ro.product.brand' : 'brand3',
88 'ro.product.device' : 'device3',
89 },
90 ]
91
92 def test_init(self):
93 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
94 self.assertEqual('product-device', target_info.device)
95 self.assertEqual('build-fingerprint', target_info.fingerprint)
96 self.assertFalse(target_info.is_ab)
97 self.assertIsNone(target_info.oem_props)
98
99 def test_init_with_oem_props(self):
100 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
101 self.TEST_OEM_DICTS)
102 self.assertEqual('device1', target_info.device)
103 self.assertEqual('brand1/product-name/device1:build-thumbprint',
104 target_info.fingerprint)
105
106 # Swap the order in oem_dicts, which would lead to different BuildInfo.
107 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
108 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
109 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
110 oem_dicts)
111 self.assertEqual('device3', target_info.device)
112 self.assertEqual('brand3/product-name/device3:build-thumbprint',
113 target_info.fingerprint)
114
115 # Missing oem_dict should be rejected.
116 self.assertRaises(AssertionError, common.BuildInfo,
117 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
118
119 def test_init_badFingerprint(self):
120 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
121 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
122 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
123
124 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
125 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
126
127 def test___getitem__(self):
128 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
129 self.assertEqual('value1', target_info['property1'])
130 self.assertEqual(4096, target_info['property2'])
131 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
132
133 def test___getitem__with_oem_props(self):
134 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
135 self.TEST_OEM_DICTS)
136 self.assertEqual('value1', target_info['property1'])
137 self.assertEqual(4096, target_info['property2'])
138 self.assertRaises(KeyError,
139 lambda: target_info['build.prop']['ro.build.foo'])
140
141 def test___setitem__(self):
142 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
143 self.assertEqual('value1', target_info['property1'])
144 target_info['property1'] = 'value2'
145 self.assertEqual('value2', target_info['property1'])
146
147 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
148 target_info['build.prop']['ro.build.foo'] = 'build-bar'
149 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
150
151 def test_get(self):
152 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
153 self.assertEqual('value1', target_info.get('property1'))
154 self.assertEqual(4096, target_info.get('property2'))
155 self.assertEqual(4096, target_info.get('property2', 1024))
156 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
157 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
158
159 def test_get_with_oem_props(self):
160 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
161 self.TEST_OEM_DICTS)
162 self.assertEqual('value1', target_info.get('property1'))
163 self.assertEqual(4096, target_info.get('property2'))
164 self.assertEqual(4096, target_info.get('property2', 1024))
165 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
166 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
167 self.assertRaises(KeyError,
168 lambda: target_info.get('build.prop')['ro.build.foo'])
169
170 def test_items(self):
171 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
172 items = target_info.items()
173 self.assertIn(('property1', 'value1'), items)
174 self.assertIn(('property2', 4096), items)
175
176 def test_GetBuildProp(self):
177 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
178 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
179 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
180 'ro.build.nonexistent')
181
182 def test_GetBuildProp_with_oem_props(self):
183 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
184 self.TEST_OEM_DICTS)
185 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
186 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
187 'ro.build.nonexistent')
188
189 def test_GetVendorBuildProp(self):
190 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
191 self.assertEqual('vendor-build-fingerprint',
192 target_info.GetVendorBuildProp(
193 'ro.vendor.build.fingerprint'))
194 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
195 'ro.build.nonexistent')
196
197 def test_GetVendorBuildProp_with_oem_props(self):
198 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
199 self.TEST_OEM_DICTS)
200 self.assertEqual('vendor-build-fingerprint',
201 target_info.GetVendorBuildProp(
202 'ro.vendor.build.fingerprint'))
203 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
204 'ro.build.nonexistent')
205
206 def test_vendor_fingerprint(self):
207 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
208 self.assertEqual('vendor-build-fingerprint',
209 target_info.vendor_fingerprint)
210
211 def test_vendor_fingerprint_blacklisted(self):
212 target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
213 del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
214 target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
215 self.assertIsNone(target_info.vendor_fingerprint)
216
217 def test_vendor_fingerprint_without_vendor_build_prop(self):
218 target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
219 del target_info_dict['vendor.build.prop']
220 target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
221 self.assertIsNone(target_info.vendor_fingerprint)
222
223 def test_WriteMountOemScript(self):
224 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
225 self.TEST_OEM_DICTS)
226 script_writer = test_utils.MockScriptWriter()
227 target_info.WriteMountOemScript(script_writer)
228 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
229
230 def test_WriteDeviceAssertions(self):
231 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
232 script_writer = test_utils.MockScriptWriter()
233 target_info.WriteDeviceAssertions(script_writer, False)
234 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
235
236 def test_WriteDeviceAssertions_with_oem_props(self):
237 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
238 self.TEST_OEM_DICTS)
239 script_writer = test_utils.MockScriptWriter()
240 target_info.WriteDeviceAssertions(script_writer, False)
241 self.assertEqual(
242 [
243 ('AssertOemProperty', 'ro.product.device',
244 ['device1', 'device2', 'device3'], False),
245 ('AssertOemProperty', 'ro.product.brand',
246 ['brand1', 'brand2', 'brand3'], False),
247 ],
248 script_writer.lines)
249
250
Tao Bao65b94e92018-10-11 21:57:26 -0700251class CommonZipTest(test_utils.ReleaseToolsTestCase):
252
Tao Bao31b08072017-11-08 15:50:59 -0800253 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700254 test_file_name=None, expected_stat=None, expected_mode=0o644,
255 expected_compress_type=zipfile.ZIP_STORED):
256 # Verify the stat if present.
257 if test_file_name is not None:
258 new_stat = os.stat(test_file_name)
259 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
260 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
261
262 # Reopen the zip file to verify.
263 zip_file = zipfile.ZipFile(zip_file_name, "r")
264
265 # Verify the timestamp.
266 info = zip_file.getinfo(arcname)
267 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
268
269 # Verify the file mode.
270 mode = (info.external_attr >> 16) & 0o777
271 self.assertEqual(mode, expected_mode)
272
273 # Verify the compress type.
274 self.assertEqual(info.compress_type, expected_compress_type)
275
276 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800277 entry = zip_file.open(arcname)
278 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700279 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800280 sha1_hash.update(chunk)
281 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700282 self.assertIsNone(zip_file.testzip())
283
Dan Albert8e0178d2015-01-27 15:53:15 -0800284 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
285 extra_zipwrite_args = dict(extra_zipwrite_args or {})
286
287 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800288 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700289
290 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800291 zip_file_name = zip_file.name
292
293 # File names within an archive strip the leading slash.
294 arcname = extra_zipwrite_args.get("arcname", test_file_name)
295 if arcname[0] == "/":
296 arcname = arcname[1:]
297
298 zip_file.close()
299 zip_file = zipfile.ZipFile(zip_file_name, "w")
300
301 try:
Tao Bao31b08072017-11-08 15:50:59 -0800302 sha1_hash = sha1()
303 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700304 sha1_hash.update(bytes(data))
305 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800306 test_file.close()
307
Tao Baof3282b42015-04-01 11:21:55 -0700308 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800309 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700310 expected_compress_type = extra_zipwrite_args.get("compress_type",
311 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800312 time.sleep(5) # Make sure the atime/mtime will change measurably.
313
314 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700315 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800316
Tao Bao31b08072017-11-08 15:50:59 -0800317 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
318 test_file_name, expected_stat, expected_mode,
319 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800320 finally:
321 os.remove(test_file_name)
322 os.remove(zip_file_name)
323
Tao Baof3282b42015-04-01 11:21:55 -0700324 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
325 extra_args = dict(extra_args or {})
326
327 zip_file = tempfile.NamedTemporaryFile(delete=False)
328 zip_file_name = zip_file.name
329 zip_file.close()
330
331 zip_file = zipfile.ZipFile(zip_file_name, "w")
332
333 try:
334 expected_compress_type = extra_args.get("compress_type",
335 zipfile.ZIP_STORED)
336 time.sleep(5) # Make sure the atime/mtime will change measurably.
337
338 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700339 arcname = zinfo_or_arcname
340 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700341 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700342 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700343 if zinfo_or_arcname.external_attr:
344 zinfo_perms = zinfo_or_arcname.external_attr >> 16
345 else:
346 zinfo_perms = 0o600
347 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700348
Tao Bao58c1b962015-05-20 09:32:18 -0700349 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700350 common.ZipClose(zip_file)
351
Tao Bao31b08072017-11-08 15:50:59 -0800352 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700353 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700354 expected_compress_type=expected_compress_type)
355 finally:
356 os.remove(zip_file_name)
357
358 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
359 extra_args = dict(extra_args or {})
360
361 zip_file = tempfile.NamedTemporaryFile(delete=False)
362 zip_file_name = zip_file.name
363
364 test_file = tempfile.NamedTemporaryFile(delete=False)
365 test_file_name = test_file.name
366
367 arcname_large = test_file_name
368 arcname_small = "bar"
369
370 # File names within an archive strip the leading slash.
371 if arcname_large[0] == "/":
372 arcname_large = arcname_large[1:]
373
374 zip_file.close()
375 zip_file = zipfile.ZipFile(zip_file_name, "w")
376
377 try:
Tao Bao31b08072017-11-08 15:50:59 -0800378 sha1_hash = sha1()
379 for data in large:
380 sha1_hash.update(data)
381 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700382 test_file.close()
383
384 expected_stat = os.stat(test_file_name)
385 expected_mode = 0o644
386 expected_compress_type = extra_args.get("compress_type",
387 zipfile.ZIP_STORED)
388 time.sleep(5) # Make sure the atime/mtime will change measurably.
389
390 common.ZipWrite(zip_file, test_file_name, **extra_args)
391 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
392 common.ZipClose(zip_file)
393
394 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800395 self._verify(zip_file, zip_file_name, arcname_large,
396 sha1_hash.hexdigest(), test_file_name, expected_stat,
397 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700398
399 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800400 self._verify(zip_file, zip_file_name, arcname_small,
401 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700402 expected_compress_type=expected_compress_type)
403 finally:
404 os.remove(zip_file_name)
405 os.remove(test_file_name)
406
407 def _test_reset_ZIP64_LIMIT(self, func, *args):
408 default_limit = (1 << 31) - 1
409 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
410 func(*args)
411 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
412
Dan Albert8e0178d2015-01-27 15:53:15 -0800413 def test_ZipWrite(self):
414 file_contents = os.urandom(1024)
415 self._test_ZipWrite(file_contents)
416
417 def test_ZipWrite_with_opts(self):
418 file_contents = os.urandom(1024)
419 self._test_ZipWrite(file_contents, {
420 "arcname": "foobar",
421 "perms": 0o777,
422 "compress_type": zipfile.ZIP_DEFLATED,
423 })
Tao Baof3282b42015-04-01 11:21:55 -0700424 self._test_ZipWrite(file_contents, {
425 "arcname": "foobar",
426 "perms": 0o700,
427 "compress_type": zipfile.ZIP_STORED,
428 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800429
430 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700431 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800432 self._test_ZipWrite(file_contents, {
433 "compress_type": zipfile.ZIP_DEFLATED,
434 })
435
436 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700437 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
438
439 def test_ZipWriteStr(self):
440 random_string = os.urandom(1024)
441 # Passing arcname
442 self._test_ZipWriteStr("foo", random_string)
443
444 # Passing zinfo
445 zinfo = zipfile.ZipInfo(filename="foo")
446 self._test_ZipWriteStr(zinfo, random_string)
447
448 # Timestamp in the zinfo should be overwritten.
449 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
450 self._test_ZipWriteStr(zinfo, random_string)
451
452 def test_ZipWriteStr_with_opts(self):
453 random_string = os.urandom(1024)
454 # Passing arcname
455 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700456 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700457 "compress_type": zipfile.ZIP_DEFLATED,
458 })
Tao Bao58c1b962015-05-20 09:32:18 -0700459 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700460 "compress_type": zipfile.ZIP_STORED,
461 })
462
463 # Passing zinfo
464 zinfo = zipfile.ZipInfo(filename="foo")
465 self._test_ZipWriteStr(zinfo, random_string, {
466 "compress_type": zipfile.ZIP_DEFLATED,
467 })
468 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700469 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700470 "compress_type": zipfile.ZIP_STORED,
471 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700472 self._test_ZipWriteStr(zinfo, random_string, {
473 "perms": 0o000,
474 "compress_type": zipfile.ZIP_STORED,
475 })
Tao Baof3282b42015-04-01 11:21:55 -0700476
477 def test_ZipWriteStr_large_file(self):
478 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
479 # the workaround. We will only test the case of writing a string into a
480 # large archive.
481 long_string = get_2gb_string()
482 short_string = os.urandom(1024)
483 self._test_ZipWriteStr_large_file(long_string, short_string, {
484 "compress_type": zipfile.ZIP_DEFLATED,
485 })
486
487 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700488 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700489 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700490 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700491
492 def test_bug21309935(self):
493 zip_file = tempfile.NamedTemporaryFile(delete=False)
494 zip_file_name = zip_file.name
495 zip_file.close()
496
497 try:
498 random_string = os.urandom(1024)
499 zip_file = zipfile.ZipFile(zip_file_name, "w")
500 # Default perms should be 0o644 when passing the filename.
501 common.ZipWriteStr(zip_file, "foo", random_string)
502 # Honor the specified perms.
503 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
504 # The perms in zinfo should be untouched.
505 zinfo = zipfile.ZipInfo(filename="baz")
506 zinfo.external_attr = 0o740 << 16
507 common.ZipWriteStr(zip_file, zinfo, random_string)
508 # Explicitly specified perms has the priority.
509 zinfo = zipfile.ZipInfo(filename="qux")
510 zinfo.external_attr = 0o700 << 16
511 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
512 common.ZipClose(zip_file)
513
Tao Bao31b08072017-11-08 15:50:59 -0800514 self._verify(zip_file, zip_file_name, "foo",
515 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700516 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800517 self._verify(zip_file, zip_file_name, "bar",
518 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700519 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800520 self._verify(zip_file, zip_file_name, "baz",
521 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700522 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800523 self._verify(zip_file, zip_file_name, "qux",
524 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700525 expected_mode=0o400)
526 finally:
527 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700528
Tao Bao82490d32019-04-09 00:12:30 -0700529 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800530 def test_ZipDelete(self):
531 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
532 output_zip = zipfile.ZipFile(zip_file.name, 'w',
533 compression=zipfile.ZIP_DEFLATED)
534 with tempfile.NamedTemporaryFile() as entry_file:
535 entry_file.write(os.urandom(1024))
536 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
537 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
538 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
539 common.ZipClose(output_zip)
540 zip_file.close()
541
542 try:
543 common.ZipDelete(zip_file.name, 'Test2')
544 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
545 entries = check_zip.namelist()
546 self.assertTrue('Test1' in entries)
547 self.assertFalse('Test2' in entries)
548 self.assertTrue('Test3' in entries)
549
Tao Bao986ee862018-10-04 15:46:16 -0700550 self.assertRaises(
551 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800552 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
553 entries = check_zip.namelist()
554 self.assertTrue('Test1' in entries)
555 self.assertFalse('Test2' in entries)
556 self.assertTrue('Test3' in entries)
557
558 common.ZipDelete(zip_file.name, ['Test3'])
559 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.assertFalse('Test3' in entries)
564
565 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
566 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
567 entries = check_zip.namelist()
568 self.assertFalse('Test1' in entries)
569 self.assertFalse('Test2' in entries)
570 self.assertFalse('Test3' in entries)
571 finally:
572 os.remove(zip_file.name)
573
Tao Bao0ff15de2019-03-20 11:26:06 -0700574 @staticmethod
575 def _test_UnzipTemp_createZipFile():
576 zip_file = common.MakeTempFile(suffix='.zip')
577 output_zip = zipfile.ZipFile(
578 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
579 contents = os.urandom(1024)
580 with tempfile.NamedTemporaryFile() as entry_file:
581 entry_file.write(contents)
582 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
583 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
584 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
585 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
586 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
587 common.ZipClose(output_zip)
588 common.ZipClose(output_zip)
589 return zip_file
590
Tao Bao82490d32019-04-09 00:12:30 -0700591 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700592 def test_UnzipTemp(self):
593 zip_file = self._test_UnzipTemp_createZipFile()
594 unzipped_dir = common.UnzipTemp(zip_file)
595 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
596 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
597 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
598 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
599 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
600
Tao Bao82490d32019-04-09 00:12:30 -0700601 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700602 def test_UnzipTemp_withPatterns(self):
603 zip_file = self._test_UnzipTemp_createZipFile()
604
605 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
606 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
607 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
608 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
609 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
610 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
611
612 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
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.assertTrue(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, ['Test*', 'Foo3*'])
620 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
621 self.assertTrue(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, ['*Test1', '*Baz*'])
627 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
628 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
629 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
630 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
631 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
632
633 def test_UnzipTemp_withEmptyPatterns(self):
634 zip_file = self._test_UnzipTemp_createZipFile()
635 unzipped_dir = common.UnzipTemp(zip_file, [])
636 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
637 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
638 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
639 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
640 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
641
Tao Bao82490d32019-04-09 00:12:30 -0700642 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700643 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
644 zip_file = self._test_UnzipTemp_createZipFile()
645 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
646 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
647 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
648 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
649 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
650 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
651
652 def test_UnzipTemp_withNoMatchingPatterns(self):
653 zip_file = self._test_UnzipTemp_createZipFile()
654 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
655 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
656 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
657 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
658 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
659 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
660
Tao Bao89d7ab22017-12-14 17:05:33 -0800661
Tao Bao65b94e92018-10-11 21:57:26 -0700662class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800663 """Tests the APK utils related functions."""
664
665 APKCERTS_TXT1 = (
666 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
667 ' private_key="certs/devkey.pk8"\n'
668 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700669 ' certificate="build/make/target/product/security/platform.x509.pem"'
670 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800671 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
672 )
673
674 APKCERTS_CERTMAP1 = {
675 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700676 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800677 'TV.apk' : 'PRESIGNED',
678 }
679
680 APKCERTS_TXT2 = (
681 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
682 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
683 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
684 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
685 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
686 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
687 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
688 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
689 )
690
691 APKCERTS_CERTMAP2 = {
692 'Compressed1.apk' : 'certs/compressed1',
693 'Compressed2a.apk' : 'certs/compressed2',
694 'Compressed2b.apk' : 'certs/compressed2',
695 'Compressed3.apk' : 'certs/compressed3',
696 }
697
698 APKCERTS_TXT3 = (
699 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
700 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
701 )
702
703 APKCERTS_CERTMAP3 = {
704 'Compressed4.apk' : 'certs/compressed4',
705 }
706
Tao Bao17e4e612018-02-16 17:12:54 -0800707 def setUp(self):
708 self.testdata_dir = test_utils.get_testdata_dir()
709
Tao Bao818ddf52018-01-05 11:17:34 -0800710 @staticmethod
711 def _write_apkcerts_txt(apkcerts_txt, additional=None):
712 if additional is None:
713 additional = []
714 target_files = common.MakeTempFile(suffix='.zip')
715 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
716 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
717 for entry in additional:
718 target_files_zip.writestr(entry, '')
719 return target_files
720
721 def test_ReadApkCerts_NoncompressedApks(self):
722 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
723 with zipfile.ZipFile(target_files, 'r') as input_zip:
724 certmap, ext = common.ReadApkCerts(input_zip)
725
726 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
727 self.assertIsNone(ext)
728
729 def test_ReadApkCerts_CompressedApks(self):
730 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
731 # not stored in '.gz' format, so it shouldn't be considered as installed.
732 target_files = self._write_apkcerts_txt(
733 self.APKCERTS_TXT2,
734 ['Compressed1.apk.gz', 'Compressed3.apk'])
735
736 with zipfile.ZipFile(target_files, 'r') as input_zip:
737 certmap, ext = common.ReadApkCerts(input_zip)
738
739 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
740 self.assertEqual('.gz', ext)
741
742 # Alternative case with '.xz'.
743 target_files = self._write_apkcerts_txt(
744 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
745
746 with zipfile.ZipFile(target_files, 'r') as input_zip:
747 certmap, ext = common.ReadApkCerts(input_zip)
748
749 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
750 self.assertEqual('.xz', ext)
751
752 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
753 target_files = self._write_apkcerts_txt(
754 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
755 ['Compressed1.apk.gz', 'Compressed3.apk'])
756
757 with zipfile.ZipFile(target_files, 'r') as input_zip:
758 certmap, ext = common.ReadApkCerts(input_zip)
759
760 certmap_merged = self.APKCERTS_CERTMAP1.copy()
761 certmap_merged.update(self.APKCERTS_CERTMAP2)
762 self.assertDictEqual(certmap_merged, certmap)
763 self.assertEqual('.gz', ext)
764
765 def test_ReadApkCerts_MultipleCompressionMethods(self):
766 target_files = self._write_apkcerts_txt(
767 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
768 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
769
770 with zipfile.ZipFile(target_files, 'r') as input_zip:
771 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
772
773 def test_ReadApkCerts_MismatchingKeys(self):
774 malformed_apkcerts_txt = (
775 'name="App1.apk" certificate="certs/cert1.x509.pem"'
776 ' private_key="certs/cert2.pk8"\n'
777 )
778 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
779
780 with zipfile.ZipFile(target_files, 'r') as input_zip:
781 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
782
Tao Bao04e1f012018-02-04 12:13:35 -0800783 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800784 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
785 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800786 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800787 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
788
789 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800790 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800791 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
792
Tao Bao82490d32019-04-09 00:12:30 -0700793 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700794 def test_ExtractAvbPublicKey(self):
795 privkey = os.path.join(self.testdata_dir, 'testkey.key')
796 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700797 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
798 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
799 with open(extracted_from_privkey, 'rb') as privkey_fp, \
800 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700801 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
802
Tao Bao17e4e612018-02-16 17:12:54 -0800803 def test_ParseCertificate(self):
804 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
805
806 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800807 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
808 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800809 expected, _ = proc.communicate()
810 self.assertEqual(0, proc.returncode)
811
812 with open(cert) as cert_fp:
813 actual = common.ParseCertificate(cert_fp.read())
814 self.assertEqual(expected, actual)
815
Tao Bao82490d32019-04-09 00:12:30 -0700816 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700817 def test_GetMinSdkVersion(self):
818 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
819 self.assertEqual('24', common.GetMinSdkVersion(test_app))
820
Tao Bao82490d32019-04-09 00:12:30 -0700821 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700822 def test_GetMinSdkVersion_invalidInput(self):
823 self.assertRaises(
824 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
825
Tao Bao82490d32019-04-09 00:12:30 -0700826 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700827 def test_GetMinSdkVersionInt(self):
828 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
829 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
830
Tao Bao82490d32019-04-09 00:12:30 -0700831 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700832 def test_GetMinSdkVersionInt_invalidInput(self):
833 self.assertRaises(
834 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
835 {})
836
Tao Bao818ddf52018-01-05 11:17:34 -0800837
Tao Bao65b94e92018-10-11 21:57:26 -0700838class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800839
Tao Bao02a08592018-07-22 12:40:45 -0700840 def setUp(self):
841 self.testdata_dir = test_utils.get_testdata_dir()
842
Tao Bao82490d32019-04-09 00:12:30 -0700843 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800844 def test_GetSparseImage_emptyBlockMapFile(self):
845 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
846 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
847 target_files_zip.write(
848 test_utils.construct_sparse_image([
849 (0xCAC1, 6),
850 (0xCAC3, 3),
851 (0xCAC1, 4)]),
852 arcname='IMAGES/system.img')
853 target_files_zip.writestr('IMAGES/system.map', '')
854 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
855 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
856
Tao Baodba59ee2018-01-09 13:21:02 -0800857 tempdir = common.UnzipTemp(target_files)
858 with zipfile.ZipFile(target_files, 'r') as input_zip:
859 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800860
861 self.assertDictEqual(
862 {
863 '__COPY': RangeSet("0"),
864 '__NONZERO-0': RangeSet("1-5 9-12"),
865 },
866 sparse_image.file_map)
867
Tao Baob2de7d92019-04-10 10:01:47 -0700868 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800869 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700870 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
871 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800872 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700873 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
874 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800875
Tao Bao82490d32019-04-09 00:12:30 -0700876 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800877 def test_GetSparseImage_missingBlockMapFile(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('SYSTEM/file1', os.urandom(4096 * 8))
887 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
888
Tao Baodba59ee2018-01-09 13:21:02 -0800889 tempdir = common.UnzipTemp(target_files)
890 with zipfile.ZipFile(target_files, 'r') as input_zip:
891 self.assertRaises(
892 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
893 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800894
Tao Bao82490d32019-04-09 00:12:30 -0700895 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800896 def test_GetSparseImage_sharedBlocks_notAllowed(self):
897 """Tests the case of having overlapping blocks but disallowed."""
898 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
899 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
900 target_files_zip.write(
901 test_utils.construct_sparse_image([(0xCAC2, 16)]),
902 arcname='IMAGES/system.img')
903 # Block 10 is shared between two files.
904 target_files_zip.writestr(
905 'IMAGES/system.map',
906 '\n'.join([
907 '/system/file1 1-5 9-10',
908 '/system/file2 10-12']))
909 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
910 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
911
Tao Baodba59ee2018-01-09 13:21:02 -0800912 tempdir = common.UnzipTemp(target_files)
913 with zipfile.ZipFile(target_files, 'r') as input_zip:
914 self.assertRaises(
915 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
916 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800917
Tao Bao82490d32019-04-09 00:12:30 -0700918 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800919 def test_GetSparseImage_sharedBlocks_allowed(self):
920 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
921 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
922 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
923 # Construct an image with a care_map of "0-5 9-12".
924 target_files_zip.write(
925 test_utils.construct_sparse_image([(0xCAC2, 16)]),
926 arcname='IMAGES/system.img')
927 # Block 10 is shared between two files.
928 target_files_zip.writestr(
929 'IMAGES/system.map',
930 '\n'.join([
931 '/system/file1 1-5 9-10',
932 '/system/file2 10-12']))
933 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
934 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
935
Tao Baodba59ee2018-01-09 13:21:02 -0800936 tempdir = common.UnzipTemp(target_files)
937 with zipfile.ZipFile(target_files, 'r') as input_zip:
938 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -0800939
940 self.assertDictEqual(
941 {
942 '__COPY': RangeSet("0"),
943 '__NONZERO-0': RangeSet("6-8 13-15"),
944 '/system/file1': RangeSet("1-5 9-10"),
945 '/system/file2': RangeSet("11-12"),
946 },
947 sparse_image.file_map)
948
949 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
950 # 'incomplete'.
951 self.assertTrue(
952 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
953 self.assertNotIn(
954 'incomplete', sparse_image.file_map['/system/file2'].extra)
955
Tao Baoa264fef2019-10-06 21:55:20 -0700956 # '/system/file1' will only contain one field -- a copy of the input text.
957 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
958
959 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -0800960 self.assertFalse(sparse_image.file_map['__COPY'].extra)
961 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -0800962
Tao Bao82490d32019-04-09 00:12:30 -0700963 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800964 def test_GetSparseImage_incompleteRanges(self):
965 """Tests the case of ext4 images with holes."""
966 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
967 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
968 target_files_zip.write(
969 test_utils.construct_sparse_image([(0xCAC2, 16)]),
970 arcname='IMAGES/system.img')
971 target_files_zip.writestr(
972 'IMAGES/system.map',
973 '\n'.join([
974 '/system/file1 1-5 9-10',
975 '/system/file2 11-12']))
976 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
977 # '/system/file2' has less blocks listed (2) than actual (3).
978 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
979
Tao Baodba59ee2018-01-09 13:21:02 -0800980 tempdir = common.UnzipTemp(target_files)
981 with zipfile.ZipFile(target_files, 'r') as input_zip:
982 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800983
Tao Baoa264fef2019-10-06 21:55:20 -0700984 self.assertEqual(
985 '1-5 9-10',
986 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -0800987 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
988
Tao Bao82490d32019-04-09 00:12:30 -0700989 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -0700990 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
991 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
992 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
993 target_files_zip.write(
994 test_utils.construct_sparse_image([(0xCAC2, 16)]),
995 arcname='IMAGES/system.img')
996 target_files_zip.writestr(
997 'IMAGES/system.map',
998 '\n'.join([
999 '//system/file1 1-5 9-10',
1000 '//system/file2 11-12',
1001 '/system/app/file3 13-15']))
1002 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1003 # '/system/file2' has less blocks listed (2) than actual (3).
1004 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1005 # '/system/app/file3' has less blocks listed (3) than actual (4).
1006 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1007
1008 tempdir = common.UnzipTemp(target_files)
1009 with zipfile.ZipFile(target_files, 'r') as input_zip:
1010 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1011
Tao Baoa264fef2019-10-06 21:55:20 -07001012 self.assertEqual(
1013 '1-5 9-10',
1014 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001015 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1016 self.assertTrue(
1017 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1018
Tao Bao82490d32019-04-09 00:12:30 -07001019 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001020 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1021 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1022 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1023 target_files_zip.write(
1024 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1025 arcname='IMAGES/system.img')
1026 target_files_zip.writestr(
1027 'IMAGES/system.map',
1028 '\n'.join([
1029 '//system/file1 1-5 9-10',
1030 '//init.rc 13-15']))
1031 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1032 # '/init.rc' has less blocks listed (3) than actual (4).
1033 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1034
1035 tempdir = common.UnzipTemp(target_files)
1036 with zipfile.ZipFile(target_files, 'r') as input_zip:
1037 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1038
Tao Baoa264fef2019-10-06 21:55:20 -07001039 self.assertEqual(
1040 '1-5 9-10',
1041 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001042 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1043
Tao Bao82490d32019-04-09 00:12:30 -07001044 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001045 def test_GetSparseImage_fileNotFound(self):
1046 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1047 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1048 target_files_zip.write(
1049 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1050 arcname='IMAGES/system.img')
1051 target_files_zip.writestr(
1052 'IMAGES/system.map',
1053 '\n'.join([
1054 '//system/file1 1-5 9-10',
1055 '//system/file2 11-12']))
1056 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1057
1058 tempdir = common.UnzipTemp(target_files)
1059 with zipfile.ZipFile(target_files, 'r') as input_zip:
1060 self.assertRaises(
1061 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1062 False)
1063
Tao Bao82490d32019-04-09 00:12:30 -07001064 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001065 def test_GetAvbChainedPartitionArg(self):
1066 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1067 info_dict = {
1068 'avb_avbtool': 'avbtool',
1069 'avb_system_key_path': pubkey,
1070 'avb_system_rollback_index_location': 2,
1071 }
1072 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1073 self.assertEqual(3, len(args))
1074 self.assertEqual('system', args[0])
1075 self.assertEqual('2', args[1])
1076 self.assertTrue(os.path.exists(args[2]))
1077
Tao Bao82490d32019-04-09 00:12:30 -07001078 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001079 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1080 key = os.path.join(self.testdata_dir, 'testkey.key')
1081 info_dict = {
1082 'avb_avbtool': 'avbtool',
1083 'avb_product_key_path': key,
1084 'avb_product_rollback_index_location': 2,
1085 }
1086 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1087 self.assertEqual(3, len(args))
1088 self.assertEqual('product', args[0])
1089 self.assertEqual('2', args[1])
1090 self.assertTrue(os.path.exists(args[2]))
1091
Tao Bao82490d32019-04-09 00:12:30 -07001092 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001093 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1094 info_dict = {
1095 'avb_avbtool': 'avbtool',
1096 'avb_system_key_path': 'does-not-exist',
1097 'avb_system_rollback_index_location': 2,
1098 }
1099 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1100 args = common.GetAvbChainedPartitionArg(
1101 'system', info_dict, pubkey).split(':')
1102 self.assertEqual(3, len(args))
1103 self.assertEqual('system', args[0])
1104 self.assertEqual('2', args[1])
1105 self.assertTrue(os.path.exists(args[2]))
1106
Tao Bao82490d32019-04-09 00:12:30 -07001107 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001108 def test_GetAvbChainedPartitionArg_invalidKey(self):
1109 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1110 info_dict = {
1111 'avb_avbtool': 'avbtool',
1112 'avb_system_key_path': pubkey,
1113 'avb_system_rollback_index_location': 2,
1114 }
1115 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001116 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1117 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001118
Tao Baoa57ab9f2018-08-24 12:08:38 -07001119 INFO_DICT_DEFAULT = {
1120 'recovery_api_version': 3,
1121 'fstab_version': 2,
1122 'system_root_image': 'true',
1123 'no_recovery' : 'true',
1124 'recovery_as_boot': 'true',
1125 }
1126
Daniel Norman4cc9df62019-07-18 10:11:07 -07001127 def test_LoadListFromFile(self):
1128 file_path = os.path.join(self.testdata_dir,
1129 'merge_config_framework_item_list')
1130 contents = common.LoadListFromFile(file_path)
1131 expected_contents = [
1132 'META/apkcerts.txt',
1133 'META/filesystem_config.txt',
1134 'META/root_filesystem_config.txt',
1135 'META/system_manifest.xml',
1136 'META/system_matrix.xml',
1137 'META/update_engine_config.txt',
1138 'PRODUCT/*',
1139 'ROOT/*',
1140 'SYSTEM/*',
1141 ]
1142 self.assertEqual(sorted(contents), sorted(expected_contents))
1143
Tao Baoa57ab9f2018-08-24 12:08:38 -07001144 @staticmethod
1145 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1146 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1147 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1148 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001149 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001150 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1151
1152 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1153 if info_dict.get('system_root_image') == 'true':
1154 fstab_values = FSTAB_TEMPLATE.format('/')
1155 else:
1156 fstab_values = FSTAB_TEMPLATE.format('/system')
1157 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001158
1159 common.ZipWriteStr(
1160 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001161 return target_files
1162
1163 def test_LoadInfoDict(self):
1164 target_files = self._test_LoadInfoDict_createTargetFiles(
1165 self.INFO_DICT_DEFAULT,
1166 'BOOT/RAMDISK/system/etc/recovery.fstab')
1167 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1168 loaded_dict = common.LoadInfoDict(target_files_zip)
1169 self.assertEqual(3, loaded_dict['recovery_api_version'])
1170 self.assertEqual(2, loaded_dict['fstab_version'])
1171 self.assertIn('/', loaded_dict['fstab'])
1172 self.assertIn('/system', loaded_dict['fstab'])
1173
1174 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1175 target_files = self._test_LoadInfoDict_createTargetFiles(
1176 self.INFO_DICT_DEFAULT,
1177 'BOOT/RAMDISK/etc/recovery.fstab')
1178 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1179 loaded_dict = common.LoadInfoDict(target_files_zip)
1180 self.assertEqual(3, loaded_dict['recovery_api_version'])
1181 self.assertEqual(2, loaded_dict['fstab_version'])
1182 self.assertIn('/', loaded_dict['fstab'])
1183 self.assertIn('/system', loaded_dict['fstab'])
1184
Tao Bao82490d32019-04-09 00:12:30 -07001185 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001186 def test_LoadInfoDict_dirInput(self):
1187 target_files = self._test_LoadInfoDict_createTargetFiles(
1188 self.INFO_DICT_DEFAULT,
1189 'BOOT/RAMDISK/system/etc/recovery.fstab')
1190 unzipped = common.UnzipTemp(target_files)
1191 loaded_dict = common.LoadInfoDict(unzipped)
1192 self.assertEqual(3, loaded_dict['recovery_api_version'])
1193 self.assertEqual(2, loaded_dict['fstab_version'])
1194 self.assertIn('/', loaded_dict['fstab'])
1195 self.assertIn('/system', loaded_dict['fstab'])
1196
Tao Bao82490d32019-04-09 00:12:30 -07001197 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001198 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1199 target_files = self._test_LoadInfoDict_createTargetFiles(
1200 self.INFO_DICT_DEFAULT,
1201 'BOOT/RAMDISK/system/etc/recovery.fstab')
1202 unzipped = common.UnzipTemp(target_files)
1203 loaded_dict = common.LoadInfoDict(unzipped)
1204 self.assertEqual(3, loaded_dict['recovery_api_version'])
1205 self.assertEqual(2, loaded_dict['fstab_version'])
1206 self.assertIn('/', loaded_dict['fstab'])
1207 self.assertIn('/system', loaded_dict['fstab'])
1208
1209 def test_LoadInfoDict_systemRootImageFalse(self):
1210 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1211 # launched prior to P will likely have this config.
1212 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1213 del info_dict['no_recovery']
1214 del info_dict['system_root_image']
1215 del info_dict['recovery_as_boot']
1216 target_files = self._test_LoadInfoDict_createTargetFiles(
1217 info_dict,
1218 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1219 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1220 loaded_dict = common.LoadInfoDict(target_files_zip)
1221 self.assertEqual(3, loaded_dict['recovery_api_version'])
1222 self.assertEqual(2, loaded_dict['fstab_version'])
1223 self.assertNotIn('/', loaded_dict['fstab'])
1224 self.assertIn('/system', loaded_dict['fstab'])
1225
1226 def test_LoadInfoDict_recoveryAsBootFalse(self):
1227 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1228 # devices launched since P will likely have this config.
1229 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1230 del info_dict['no_recovery']
1231 del info_dict['recovery_as_boot']
1232 target_files = self._test_LoadInfoDict_createTargetFiles(
1233 info_dict,
1234 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1235 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1236 loaded_dict = common.LoadInfoDict(target_files_zip)
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_noRecoveryTrue(self):
1243 # Device doesn't have a recovery partition at all.
1244 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1245 del info_dict['recovery_as_boot']
1246 target_files = self._test_LoadInfoDict_createTargetFiles(
1247 info_dict,
1248 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1249 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1250 loaded_dict = common.LoadInfoDict(target_files_zip)
1251 self.assertEqual(3, loaded_dict['recovery_api_version'])
1252 self.assertEqual(2, loaded_dict['fstab_version'])
1253 self.assertIsNone(loaded_dict['fstab'])
1254
Tao Bao82490d32019-04-09 00:12:30 -07001255 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001256 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1257 target_files = self._test_LoadInfoDict_createTargetFiles(
1258 self.INFO_DICT_DEFAULT,
1259 'BOOT/RAMDISK/system/etc/recovery.fstab')
1260 common.ZipDelete(target_files, 'META/misc_info.txt')
1261 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1262 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1263
Tao Bao82490d32019-04-09 00:12:30 -07001264 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001265 def test_LoadInfoDict_repacking(self):
1266 target_files = self._test_LoadInfoDict_createTargetFiles(
1267 self.INFO_DICT_DEFAULT,
1268 'BOOT/RAMDISK/system/etc/recovery.fstab')
1269 unzipped = common.UnzipTemp(target_files)
1270 loaded_dict = common.LoadInfoDict(unzipped, True)
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 self.assertEqual(
1276 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1277 self.assertEqual(
1278 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1279 loaded_dict['root_fs_config'])
1280
1281 def test_LoadInfoDict_repackingWithZipFileInput(self):
1282 target_files = self._test_LoadInfoDict_createTargetFiles(
1283 self.INFO_DICT_DEFAULT,
1284 'BOOT/RAMDISK/system/etc/recovery.fstab')
1285 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1286 self.assertRaises(
1287 AssertionError, common.LoadInfoDict, target_files_zip, True)
1288
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001289 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1290 framework_dict = {
1291 'super_partition_groups': 'group_a',
1292 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001293 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001294 }
1295 vendor_dict = {
1296 'super_partition_groups': 'group_a group_b',
1297 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001298 'super_group_a_partition_list': 'vendor',
1299 'super_group_a_group_size': '1000',
1300 'super_group_b_partition_list': 'product',
1301 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001302 }
1303 merged_dict = common.MergeDynamicPartitionInfoDicts(
1304 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001305 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001306 expected_merged_dict = {
1307 'super_partition_groups': 'group_a group_b',
1308 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001309 'super_group_a_partition_list': 'system vendor',
1310 'super_group_a_group_size': '1000',
1311 'super_group_b_partition_list': 'product',
1312 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001313 }
1314 self.assertEqual(merged_dict, expected_merged_dict)
1315
1316 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1317 framework_dict = {
1318 'super_partition_groups': 'group_a',
1319 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001320 'super_group_a_partition_list': 'system',
1321 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001322 }
1323 vendor_dict = {
1324 'super_partition_groups': 'group_a group_b',
1325 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001326 'super_group_a_partition_list': 'vendor',
1327 'super_group_a_group_size': '1000',
1328 'super_group_b_partition_list': 'product',
1329 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001330 }
1331 merged_dict = common.MergeDynamicPartitionInfoDicts(
1332 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001333 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001334 expected_merged_dict = {
1335 'super_partition_groups': 'group_a group_b',
1336 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001337 'super_group_a_partition_list': 'system vendor',
1338 'super_group_a_group_size': '1000',
1339 'super_group_b_partition_list': 'product',
1340 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001341 }
1342 self.assertEqual(merged_dict, expected_merged_dict)
1343
Daniel Norman276f0622019-07-26 14:13:51 -07001344 def test_GetAvbPartitionArg(self):
1345 info_dict = {}
1346 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1347 self.assertEqual(
1348 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1349
1350 @test_utils.SkipIfExternalToolsUnavailable()
1351 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1352 testdata_dir = test_utils.get_testdata_dir()
1353 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1354 info_dict = {
1355 'avb_avbtool': 'avbtool',
1356 'avb_vendor_key_path': pubkey,
1357 'avb_vendor_rollback_index_location': 5,
1358 }
1359 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1360 self.assertEqual(2, len(cmd))
1361 self.assertEqual('--chain_partition', cmd[0])
1362 chained_partition_args = cmd[1].split(':')
1363 self.assertEqual(3, len(chained_partition_args))
1364 self.assertEqual('vendor', chained_partition_args[0])
1365 self.assertEqual('5', chained_partition_args[1])
1366 self.assertTrue(os.path.exists(chained_partition_args[2]))
1367
Tao Bao3612c882019-10-14 17:49:31 -07001368 @test_utils.SkipIfExternalToolsUnavailable()
1369 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1370 testdata_dir = test_utils.get_testdata_dir()
1371 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1372 info_dict = {
1373 'avb_avbtool': 'avbtool',
1374 'avb_recovery_key_path': pubkey,
1375 'avb_recovery_rollback_index_location': 3,
1376 }
1377 cmd = common.GetAvbPartitionArg(
1378 'recovery', '/path/to/recovery.img', info_dict)
1379 self.assertFalse(cmd)
1380
1381 @test_utils.SkipIfExternalToolsUnavailable()
1382 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1383 testdata_dir = test_utils.get_testdata_dir()
1384 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1385 info_dict = {
1386 'ab_update': 'true',
1387 'avb_avbtool': 'avbtool',
1388 'avb_recovery_key_path': pubkey,
1389 'avb_recovery_rollback_index_location': 3,
1390 }
1391 cmd = common.GetAvbPartitionArg(
1392 'recovery', '/path/to/recovery.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('recovery', chained_partition_args[0])
1398 self.assertEqual('3', chained_partition_args[1])
1399 self.assertTrue(os.path.exists(chained_partition_args[2]))
1400
Tao Baofc7e0e02018-02-13 13:54:02 -08001401
Tao Bao65b94e92018-10-11 21:57:26 -07001402class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001403 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001404
Tao Bao1c830bf2017-12-25 10:43:47 -08001405 Its format should match between common.py and validate_target_files.py.
1406 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001407
1408 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001409 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001410 # Create a dummy dict that contains the fstab info for boot&recovery.
1411 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001412 dummy_fstab = [
1413 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1414 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001415 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001416 # Construct the gzipped recovery.img and boot.img
1417 self.recovery_data = bytearray([
1418 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1419 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1420 0x08, 0x00, 0x00, 0x00
1421 ])
1422 # echo -n "boot" | gzip -f | hd
1423 self.boot_data = bytearray([
1424 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1425 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1426 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001427
1428 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1429 loc = os.path.join(self._tempdir, prefix, name)
1430 if not os.path.exists(os.path.dirname(loc)):
1431 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001432 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001433 f.write(data)
1434
1435 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001436 recovery_image = common.File("recovery.img", self.recovery_data)
1437 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001438 self._info["full_recovery_image"] = "true"
1439
1440 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1441 recovery_image, boot_image, self._info)
1442 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1443 self._info)
1444
Tao Bao82490d32019-04-09 00:12:30 -07001445 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001446 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001447 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001448 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001449 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001450 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1451
1452 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1453 recovery_image, boot_image, self._info)
1454 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1455 self._info)
1456 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001457 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001458 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1459 recovery_image, boot_image, self._info)
1460 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1461 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001462
1463
Yifan Hong45433e42019-01-18 13:55:25 -08001464class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001465
Yifan Hong45433e42019-01-18 13:55:25 -08001466 def __init__(self, partition, tgt, src=None):
1467 self.partition = partition
1468 self.tgt = tgt
1469 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001470
Yifan Hong45433e42019-01-18 13:55:25 -08001471 def WriteScript(self, script, _, progress=None,
1472 write_verify_script=False):
1473 if progress:
1474 script.AppendExtra("progress({})".format(progress))
1475 script.AppendExtra("patch({});".format(self.partition))
1476 if write_verify_script:
1477 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001478
Yifan Hong45433e42019-01-18 13:55:25 -08001479 def WritePostInstallVerifyScript(self, script):
1480 script.AppendExtra("verify({});".format(self.partition))
1481
1482
1483class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001484
Yifan Hong45433e42019-01-18 13:55:25 -08001485 def __init__(self, size):
1486 self.blocksize = 4096
1487 self.total_blocks = size // 4096
1488 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1489
1490
1491class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001492
Yifan Hong45433e42019-01-18 13:55:25 -08001493 @staticmethod
1494 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001495 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001496 with output_zip.open('dynamic_partitions_op_list') as op_list:
1497 return [line.decode().strip() for line in op_list.readlines()
1498 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001499
1500 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001501 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001502 self.output_path = common.MakeTempFile(suffix='.zip')
1503
1504 def test_full(self):
1505 target_info = common.LoadDictionaryFromLines("""
1506dynamic_partition_list=system vendor
1507super_partition_groups=group_foo
1508super_group_foo_group_size={group_size}
1509super_group_foo_partition_list=system vendor
1510""".format(group_size=4 * GiB).split("\n"))
1511 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1512 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1513
1514 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1515 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1516 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1517
1518 self.assertEqual(str(self.script).strip(), """
1519assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001520patch(system);
1521verify(system);
1522unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001523patch(vendor);
1524verify(vendor);
1525unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001526""".strip())
1527
1528 lines = self.get_op_list(self.output_path)
1529
1530 remove_all_groups = lines.index("remove_all_groups")
1531 add_group = lines.index("add_group group_foo 4294967296")
1532 add_vendor = lines.index("add vendor group_foo")
1533 add_system = lines.index("add system group_foo")
1534 resize_vendor = lines.index("resize vendor 1073741824")
1535 resize_system = lines.index("resize system 3221225472")
1536
1537 self.assertLess(remove_all_groups, add_group,
1538 "Should add groups after removing all groups")
1539 self.assertLess(add_group, min(add_vendor, add_system),
1540 "Should add partitions after adding group")
1541 self.assertLess(add_system, resize_system,
1542 "Should resize system after adding it")
1543 self.assertLess(add_vendor, resize_vendor,
1544 "Should resize vendor after adding it")
1545
1546 def test_inc_groups(self):
1547 source_info = common.LoadDictionaryFromLines("""
1548super_partition_groups=group_foo group_bar group_baz
1549super_group_foo_group_size={group_foo_size}
1550super_group_bar_group_size={group_bar_size}
1551""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1552 target_info = common.LoadDictionaryFromLines("""
1553super_partition_groups=group_foo group_baz group_qux
1554super_group_foo_group_size={group_foo_size}
1555super_group_baz_group_size={group_baz_size}
1556super_group_qux_group_size={group_qux_size}
1557""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1558 group_qux_size=1 * GiB).split("\n"))
1559
1560 dp_diff = common.DynamicPartitionsDifference(target_info,
1561 block_diffs=[],
1562 source_info_dict=source_info)
1563 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1564 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1565
1566 lines = self.get_op_list(self.output_path)
1567
1568 removed = lines.index("remove_group group_bar")
1569 shrunk = lines.index("resize_group group_foo 3221225472")
1570 grown = lines.index("resize_group group_baz 4294967296")
1571 added = lines.index("add_group group_qux 1073741824")
1572
Tao Baof1113e92019-06-18 12:10:14 -07001573 self.assertLess(max(removed, shrunk),
1574 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001575 "ops that remove / shrink partitions must precede ops that "
1576 "grow / add partitions")
1577
Yifan Hongbb2658d2019-01-25 12:30:58 -08001578 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001579 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001580dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001581super_partition_groups=group_foo
1582super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001583super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001584""".format(group_foo_size=4 * GiB).split("\n"))
1585 target_info = common.LoadDictionaryFromLines("""
1586dynamic_partition_list=system vendor product odm
1587super_partition_groups=group_foo group_bar
1588super_group_foo_group_size={group_foo_size}
1589super_group_foo_partition_list=system vendor odm
1590super_group_bar_group_size={group_bar_size}
1591super_group_bar_partition_list=product
1592""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1593
1594 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1595 src=FakeSparseImage(1024 * MiB)),
1596 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1597 src=FakeSparseImage(1024 * MiB)),
1598 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1599 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001600 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001601 src=FakeSparseImage(1024 * MiB)),
1602 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1603 src=None)]
1604
1605 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1606 source_info_dict=source_info)
1607 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1608 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1609
1610 metadata_idx = self.script.lines.index(
1611 'assert(update_dynamic_partitions(package_extract_file('
1612 '"dynamic_partitions_op_list")));')
1613 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1614 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1615 for p in ("product", "system", "odm"):
1616 patch_idx = self.script.lines.index("patch({});".format(p))
1617 verify_idx = self.script.lines.index("verify({});".format(p))
1618 self.assertLess(metadata_idx, patch_idx,
1619 "Should patch {} after updating metadata".format(p))
1620 self.assertLess(patch_idx, verify_idx,
1621 "Should verify {} after patching".format(p))
1622
Justin Yun6151e3f2019-06-25 15:58:13 +09001623 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001624
1625 lines = self.get_op_list(self.output_path)
1626
Justin Yun6151e3f2019-06-25 15:58:13 +09001627 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001628 move_product_out = lines.index("move product default")
1629 shrink = lines.index("resize vendor 536870912")
1630 shrink_group = lines.index("resize_group group_foo 3221225472")
1631 add_group_bar = lines.index("add_group group_bar 1073741824")
1632 add_odm = lines.index("add odm group_foo")
1633 grow_existing = lines.index("resize system 1610612736")
1634 grow_added = lines.index("resize odm 1073741824")
1635 move_product_in = lines.index("move product group_bar")
1636
1637 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1638 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1639
1640 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1641 "Must shrink group after partitions inside group are shrunk"
1642 " / removed")
1643
1644 self.assertLess(add_group_bar, move_product_in,
1645 "Must add partitions to group after group is added")
1646
1647 self.assertLess(max_idx_move_partition_out_foo,
1648 min_idx_move_partition_in_foo,
1649 "Must shrink partitions / remove partitions from group"
1650 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001651
1652 def test_remove_partition(self):
1653 source_info = common.LoadDictionaryFromLines("""
1654blockimgdiff_versions=3,4
1655use_dynamic_partitions=true
1656dynamic_partition_list=foo
1657super_partition_groups=group_foo
1658super_group_foo_group_size={group_foo_size}
1659super_group_foo_partition_list=foo
1660""".format(group_foo_size=4 * GiB).split("\n"))
1661 target_info = common.LoadDictionaryFromLines("""
1662blockimgdiff_versions=3,4
1663use_dynamic_partitions=true
1664super_partition_groups=group_foo
1665super_group_foo_group_size={group_foo_size}
1666""".format(group_foo_size=4 * GiB).split("\n"))
1667
1668 common.OPTIONS.info_dict = target_info
1669 common.OPTIONS.target_info_dict = target_info
1670 common.OPTIONS.source_info_dict = source_info
1671 common.OPTIONS.cache_size = 4 * 4096
1672
1673 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1674 src=DataImage("source", pad=True))]
1675
1676 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1677 source_info_dict=source_info)
1678 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1679 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1680
1681 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001682 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001683
1684 lines = self.get_op_list(self.output_path)
1685 self.assertEqual(lines, ["remove foo"])