blob: 9b76734fab03f75a0584bf1d197089bd8bb1fd73 [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
Tao Baofc7e0e02018-02-13 13:54:02 -080028from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080029
Yifan Hongbb2658d2019-01-25 12:30:58 -080030from blockimgdiff import EmptyImage, DataImage
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)
44 yield '\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070045
Dan Albert8e0178d2015-01-27 15:53:15 -080046
Tao Bao65b94e92018-10-11 21:57:26 -070047class CommonZipTest(test_utils.ReleaseToolsTestCase):
48
Tao Bao31b08072017-11-08 15:50:59 -080049 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -070050 test_file_name=None, expected_stat=None, expected_mode=0o644,
51 expected_compress_type=zipfile.ZIP_STORED):
52 # Verify the stat if present.
53 if test_file_name is not None:
54 new_stat = os.stat(test_file_name)
55 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
56 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
57
58 # Reopen the zip file to verify.
59 zip_file = zipfile.ZipFile(zip_file_name, "r")
60
61 # Verify the timestamp.
62 info = zip_file.getinfo(arcname)
63 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
64
65 # Verify the file mode.
66 mode = (info.external_attr >> 16) & 0o777
67 self.assertEqual(mode, expected_mode)
68
69 # Verify the compress type.
70 self.assertEqual(info.compress_type, expected_compress_type)
71
72 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -080073 entry = zip_file.open(arcname)
74 sha1_hash = sha1()
75 for chunk in iter(lambda: entry.read(4 * MiB), ''):
76 sha1_hash.update(chunk)
77 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -070078 self.assertIsNone(zip_file.testzip())
79
Dan Albert8e0178d2015-01-27 15:53:15 -080080 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
81 extra_zipwrite_args = dict(extra_zipwrite_args or {})
82
83 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -080084 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -070085
86 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -080087 zip_file_name = zip_file.name
88
89 # File names within an archive strip the leading slash.
90 arcname = extra_zipwrite_args.get("arcname", test_file_name)
91 if arcname[0] == "/":
92 arcname = arcname[1:]
93
94 zip_file.close()
95 zip_file = zipfile.ZipFile(zip_file_name, "w")
96
97 try:
Tao Bao31b08072017-11-08 15:50:59 -080098 sha1_hash = sha1()
99 for data in contents:
100 sha1_hash.update(data)
101 test_file.write(data)
Dan Albert8e0178d2015-01-27 15:53:15 -0800102 test_file.close()
103
Tao Baof3282b42015-04-01 11:21:55 -0700104 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800105 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700106 expected_compress_type = extra_zipwrite_args.get("compress_type",
107 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800108 time.sleep(5) # Make sure the atime/mtime will change measurably.
109
110 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700111 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800112
Tao Bao31b08072017-11-08 15:50:59 -0800113 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
114 test_file_name, expected_stat, expected_mode,
115 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800116 finally:
117 os.remove(test_file_name)
118 os.remove(zip_file_name)
119
Tao Baof3282b42015-04-01 11:21:55 -0700120 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
121 extra_args = dict(extra_args or {})
122
123 zip_file = tempfile.NamedTemporaryFile(delete=False)
124 zip_file_name = zip_file.name
125 zip_file.close()
126
127 zip_file = zipfile.ZipFile(zip_file_name, "w")
128
129 try:
130 expected_compress_type = extra_args.get("compress_type",
131 zipfile.ZIP_STORED)
132 time.sleep(5) # Make sure the atime/mtime will change measurably.
133
134 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700135 arcname = zinfo_or_arcname
136 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700137 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700138 arcname = zinfo_or_arcname.filename
139 expected_mode = extra_args.get("perms",
140 zinfo_or_arcname.external_attr >> 16)
Tao Baof3282b42015-04-01 11:21:55 -0700141
Tao Bao58c1b962015-05-20 09:32:18 -0700142 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700143 common.ZipClose(zip_file)
144
Tao Bao31b08072017-11-08 15:50:59 -0800145 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700146 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700147 expected_compress_type=expected_compress_type)
148 finally:
149 os.remove(zip_file_name)
150
151 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
152 extra_args = dict(extra_args or {})
153
154 zip_file = tempfile.NamedTemporaryFile(delete=False)
155 zip_file_name = zip_file.name
156
157 test_file = tempfile.NamedTemporaryFile(delete=False)
158 test_file_name = test_file.name
159
160 arcname_large = test_file_name
161 arcname_small = "bar"
162
163 # File names within an archive strip the leading slash.
164 if arcname_large[0] == "/":
165 arcname_large = arcname_large[1:]
166
167 zip_file.close()
168 zip_file = zipfile.ZipFile(zip_file_name, "w")
169
170 try:
Tao Bao31b08072017-11-08 15:50:59 -0800171 sha1_hash = sha1()
172 for data in large:
173 sha1_hash.update(data)
174 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700175 test_file.close()
176
177 expected_stat = os.stat(test_file_name)
178 expected_mode = 0o644
179 expected_compress_type = extra_args.get("compress_type",
180 zipfile.ZIP_STORED)
181 time.sleep(5) # Make sure the atime/mtime will change measurably.
182
183 common.ZipWrite(zip_file, test_file_name, **extra_args)
184 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
185 common.ZipClose(zip_file)
186
187 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800188 self._verify(zip_file, zip_file_name, arcname_large,
189 sha1_hash.hexdigest(), test_file_name, expected_stat,
190 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700191
192 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800193 self._verify(zip_file, zip_file_name, arcname_small,
194 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700195 expected_compress_type=expected_compress_type)
196 finally:
197 os.remove(zip_file_name)
198 os.remove(test_file_name)
199
200 def _test_reset_ZIP64_LIMIT(self, func, *args):
201 default_limit = (1 << 31) - 1
202 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
203 func(*args)
204 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
205
Dan Albert8e0178d2015-01-27 15:53:15 -0800206 def test_ZipWrite(self):
207 file_contents = os.urandom(1024)
208 self._test_ZipWrite(file_contents)
209
210 def test_ZipWrite_with_opts(self):
211 file_contents = os.urandom(1024)
212 self._test_ZipWrite(file_contents, {
213 "arcname": "foobar",
214 "perms": 0o777,
215 "compress_type": zipfile.ZIP_DEFLATED,
216 })
Tao Baof3282b42015-04-01 11:21:55 -0700217 self._test_ZipWrite(file_contents, {
218 "arcname": "foobar",
219 "perms": 0o700,
220 "compress_type": zipfile.ZIP_STORED,
221 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800222
223 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700224 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800225 self._test_ZipWrite(file_contents, {
226 "compress_type": zipfile.ZIP_DEFLATED,
227 })
228
229 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700230 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
231
232 def test_ZipWriteStr(self):
233 random_string = os.urandom(1024)
234 # Passing arcname
235 self._test_ZipWriteStr("foo", random_string)
236
237 # Passing zinfo
238 zinfo = zipfile.ZipInfo(filename="foo")
239 self._test_ZipWriteStr(zinfo, random_string)
240
241 # Timestamp in the zinfo should be overwritten.
242 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
243 self._test_ZipWriteStr(zinfo, random_string)
244
245 def test_ZipWriteStr_with_opts(self):
246 random_string = os.urandom(1024)
247 # Passing arcname
248 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700249 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700250 "compress_type": zipfile.ZIP_DEFLATED,
251 })
Tao Bao58c1b962015-05-20 09:32:18 -0700252 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700253 "compress_type": zipfile.ZIP_STORED,
254 })
255
256 # Passing zinfo
257 zinfo = zipfile.ZipInfo(filename="foo")
258 self._test_ZipWriteStr(zinfo, random_string, {
259 "compress_type": zipfile.ZIP_DEFLATED,
260 })
261 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700262 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700263 "compress_type": zipfile.ZIP_STORED,
264 })
265
266 def test_ZipWriteStr_large_file(self):
267 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
268 # the workaround. We will only test the case of writing a string into a
269 # large archive.
270 long_string = get_2gb_string()
271 short_string = os.urandom(1024)
272 self._test_ZipWriteStr_large_file(long_string, short_string, {
273 "compress_type": zipfile.ZIP_DEFLATED,
274 })
275
276 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
277 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, "foo", "")
278 zinfo = zipfile.ZipInfo(filename="foo")
279 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, "")
Tao Bao58c1b962015-05-20 09:32:18 -0700280
281 def test_bug21309935(self):
282 zip_file = tempfile.NamedTemporaryFile(delete=False)
283 zip_file_name = zip_file.name
284 zip_file.close()
285
286 try:
287 random_string = os.urandom(1024)
288 zip_file = zipfile.ZipFile(zip_file_name, "w")
289 # Default perms should be 0o644 when passing the filename.
290 common.ZipWriteStr(zip_file, "foo", random_string)
291 # Honor the specified perms.
292 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
293 # The perms in zinfo should be untouched.
294 zinfo = zipfile.ZipInfo(filename="baz")
295 zinfo.external_attr = 0o740 << 16
296 common.ZipWriteStr(zip_file, zinfo, random_string)
297 # Explicitly specified perms has the priority.
298 zinfo = zipfile.ZipInfo(filename="qux")
299 zinfo.external_attr = 0o700 << 16
300 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
301 common.ZipClose(zip_file)
302
Tao Bao31b08072017-11-08 15:50:59 -0800303 self._verify(zip_file, zip_file_name, "foo",
304 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700305 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800306 self._verify(zip_file, zip_file_name, "bar",
307 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700308 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800309 self._verify(zip_file, zip_file_name, "baz",
310 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700311 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800312 self._verify(zip_file, zip_file_name, "qux",
313 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700314 expected_mode=0o400)
315 finally:
316 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700317
Tao Bao82490d32019-04-09 00:12:30 -0700318 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800319 def test_ZipDelete(self):
320 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
321 output_zip = zipfile.ZipFile(zip_file.name, 'w',
322 compression=zipfile.ZIP_DEFLATED)
323 with tempfile.NamedTemporaryFile() as entry_file:
324 entry_file.write(os.urandom(1024))
325 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
326 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
327 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
328 common.ZipClose(output_zip)
329 zip_file.close()
330
331 try:
332 common.ZipDelete(zip_file.name, 'Test2')
333 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
334 entries = check_zip.namelist()
335 self.assertTrue('Test1' in entries)
336 self.assertFalse('Test2' in entries)
337 self.assertTrue('Test3' in entries)
338
Tao Bao986ee862018-10-04 15:46:16 -0700339 self.assertRaises(
340 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800341 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
342 entries = check_zip.namelist()
343 self.assertTrue('Test1' in entries)
344 self.assertFalse('Test2' in entries)
345 self.assertTrue('Test3' in entries)
346
347 common.ZipDelete(zip_file.name, ['Test3'])
348 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
349 entries = check_zip.namelist()
350 self.assertTrue('Test1' in entries)
351 self.assertFalse('Test2' in entries)
352 self.assertFalse('Test3' in entries)
353
354 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
355 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
356 entries = check_zip.namelist()
357 self.assertFalse('Test1' in entries)
358 self.assertFalse('Test2' in entries)
359 self.assertFalse('Test3' in entries)
360 finally:
361 os.remove(zip_file.name)
362
Tao Bao0ff15de2019-03-20 11:26:06 -0700363 @staticmethod
364 def _test_UnzipTemp_createZipFile():
365 zip_file = common.MakeTempFile(suffix='.zip')
366 output_zip = zipfile.ZipFile(
367 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
368 contents = os.urandom(1024)
369 with tempfile.NamedTemporaryFile() as entry_file:
370 entry_file.write(contents)
371 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
372 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
373 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
374 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
375 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
376 common.ZipClose(output_zip)
377 common.ZipClose(output_zip)
378 return zip_file
379
Tao Bao82490d32019-04-09 00:12:30 -0700380 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700381 def test_UnzipTemp(self):
382 zip_file = self._test_UnzipTemp_createZipFile()
383 unzipped_dir = common.UnzipTemp(zip_file)
384 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
385 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
386 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
387 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
388 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
389
Tao Bao82490d32019-04-09 00:12:30 -0700390 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700391 def test_UnzipTemp_withPatterns(self):
392 zip_file = self._test_UnzipTemp_createZipFile()
393
394 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
395 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
396 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
397 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
398 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
399 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
400
401 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
402 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
403 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
404 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
405 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
406 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
407
408 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
409 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
410 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
411 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
412 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
413 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
414
415 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
416 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
417 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
418 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
419 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
420 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
421
422 def test_UnzipTemp_withEmptyPatterns(self):
423 zip_file = self._test_UnzipTemp_createZipFile()
424 unzipped_dir = common.UnzipTemp(zip_file, [])
425 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
426 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
427 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
428 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
429 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
430
Tao Bao82490d32019-04-09 00:12:30 -0700431 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700432 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
433 zip_file = self._test_UnzipTemp_createZipFile()
434 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
435 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
436 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
437 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
438 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
439 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
440
441 def test_UnzipTemp_withNoMatchingPatterns(self):
442 zip_file = self._test_UnzipTemp_createZipFile()
443 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
444 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
445 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
446 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
447 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
448 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
449
Tao Bao89d7ab22017-12-14 17:05:33 -0800450
Tao Bao65b94e92018-10-11 21:57:26 -0700451class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800452 """Tests the APK utils related functions."""
453
454 APKCERTS_TXT1 = (
455 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
456 ' private_key="certs/devkey.pk8"\n'
457 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700458 ' certificate="build/make/target/product/security/platform.x509.pem"'
459 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800460 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
461 )
462
463 APKCERTS_CERTMAP1 = {
464 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700465 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800466 'TV.apk' : 'PRESIGNED',
467 }
468
469 APKCERTS_TXT2 = (
470 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
471 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
472 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
473 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
474 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
475 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
476 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
477 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
478 )
479
480 APKCERTS_CERTMAP2 = {
481 'Compressed1.apk' : 'certs/compressed1',
482 'Compressed2a.apk' : 'certs/compressed2',
483 'Compressed2b.apk' : 'certs/compressed2',
484 'Compressed3.apk' : 'certs/compressed3',
485 }
486
487 APKCERTS_TXT3 = (
488 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
489 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
490 )
491
492 APKCERTS_CERTMAP3 = {
493 'Compressed4.apk' : 'certs/compressed4',
494 }
495
Tao Bao17e4e612018-02-16 17:12:54 -0800496 def setUp(self):
497 self.testdata_dir = test_utils.get_testdata_dir()
498
Tao Bao818ddf52018-01-05 11:17:34 -0800499 @staticmethod
500 def _write_apkcerts_txt(apkcerts_txt, additional=None):
501 if additional is None:
502 additional = []
503 target_files = common.MakeTempFile(suffix='.zip')
504 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
505 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
506 for entry in additional:
507 target_files_zip.writestr(entry, '')
508 return target_files
509
510 def test_ReadApkCerts_NoncompressedApks(self):
511 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
512 with zipfile.ZipFile(target_files, 'r') as input_zip:
513 certmap, ext = common.ReadApkCerts(input_zip)
514
515 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
516 self.assertIsNone(ext)
517
518 def test_ReadApkCerts_CompressedApks(self):
519 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
520 # not stored in '.gz' format, so it shouldn't be considered as installed.
521 target_files = self._write_apkcerts_txt(
522 self.APKCERTS_TXT2,
523 ['Compressed1.apk.gz', 'Compressed3.apk'])
524
525 with zipfile.ZipFile(target_files, 'r') as input_zip:
526 certmap, ext = common.ReadApkCerts(input_zip)
527
528 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
529 self.assertEqual('.gz', ext)
530
531 # Alternative case with '.xz'.
532 target_files = self._write_apkcerts_txt(
533 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
534
535 with zipfile.ZipFile(target_files, 'r') as input_zip:
536 certmap, ext = common.ReadApkCerts(input_zip)
537
538 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
539 self.assertEqual('.xz', ext)
540
541 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
542 target_files = self._write_apkcerts_txt(
543 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
544 ['Compressed1.apk.gz', 'Compressed3.apk'])
545
546 with zipfile.ZipFile(target_files, 'r') as input_zip:
547 certmap, ext = common.ReadApkCerts(input_zip)
548
549 certmap_merged = self.APKCERTS_CERTMAP1.copy()
550 certmap_merged.update(self.APKCERTS_CERTMAP2)
551 self.assertDictEqual(certmap_merged, certmap)
552 self.assertEqual('.gz', ext)
553
554 def test_ReadApkCerts_MultipleCompressionMethods(self):
555 target_files = self._write_apkcerts_txt(
556 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
557 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
558
559 with zipfile.ZipFile(target_files, 'r') as input_zip:
560 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
561
562 def test_ReadApkCerts_MismatchingKeys(self):
563 malformed_apkcerts_txt = (
564 'name="App1.apk" certificate="certs/cert1.x509.pem"'
565 ' private_key="certs/cert2.pk8"\n'
566 )
567 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
568
569 with zipfile.ZipFile(target_files, 'r') as input_zip:
570 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
571
Tao Bao04e1f012018-02-04 12:13:35 -0800572 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800573 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
574 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao04e1f012018-02-04 12:13:35 -0800575 with open(pubkey, 'rb') as pubkey_fp:
576 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
577
578 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800579 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800580 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
581
Tao Bao82490d32019-04-09 00:12:30 -0700582 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700583 def test_ExtractAvbPublicKey(self):
584 privkey = os.path.join(self.testdata_dir, 'testkey.key')
585 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
586 with open(common.ExtractAvbPublicKey(privkey)) as privkey_fp, \
587 open(common.ExtractAvbPublicKey(pubkey)) as pubkey_fp:
588 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
589
Tao Bao17e4e612018-02-16 17:12:54 -0800590 def test_ParseCertificate(self):
591 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
592
593 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
594 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
595 expected, _ = proc.communicate()
596 self.assertEqual(0, proc.returncode)
597
598 with open(cert) as cert_fp:
599 actual = common.ParseCertificate(cert_fp.read())
600 self.assertEqual(expected, actual)
601
Tao Bao82490d32019-04-09 00:12:30 -0700602 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700603 def test_GetMinSdkVersion(self):
604 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
605 self.assertEqual('24', common.GetMinSdkVersion(test_app))
606
Tao Bao82490d32019-04-09 00:12:30 -0700607 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700608 def test_GetMinSdkVersion_invalidInput(self):
609 self.assertRaises(
610 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
611
Tao Bao82490d32019-04-09 00:12:30 -0700612 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700613 def test_GetMinSdkVersionInt(self):
614 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
615 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
616
Tao Bao82490d32019-04-09 00:12:30 -0700617 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700618 def test_GetMinSdkVersionInt_invalidInput(self):
619 self.assertRaises(
620 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
621 {})
622
Tao Bao818ddf52018-01-05 11:17:34 -0800623
Tao Bao65b94e92018-10-11 21:57:26 -0700624class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800625
Tao Bao02a08592018-07-22 12:40:45 -0700626 def setUp(self):
627 self.testdata_dir = test_utils.get_testdata_dir()
628
Tao Bao82490d32019-04-09 00:12:30 -0700629 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800630 def test_GetSparseImage_emptyBlockMapFile(self):
631 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
632 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
633 target_files_zip.write(
634 test_utils.construct_sparse_image([
635 (0xCAC1, 6),
636 (0xCAC3, 3),
637 (0xCAC1, 4)]),
638 arcname='IMAGES/system.img')
639 target_files_zip.writestr('IMAGES/system.map', '')
640 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
641 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
642
Tao Baodba59ee2018-01-09 13:21:02 -0800643 tempdir = common.UnzipTemp(target_files)
644 with zipfile.ZipFile(target_files, 'r') as input_zip:
645 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800646
647 self.assertDictEqual(
648 {
649 '__COPY': RangeSet("0"),
650 '__NONZERO-0': RangeSet("1-5 9-12"),
651 },
652 sparse_image.file_map)
653
Tao Baob2de7d92019-04-10 10:01:47 -0700654 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800655 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700656 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
657 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800658 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700659 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
660 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800661
Tao Bao82490d32019-04-09 00:12:30 -0700662 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800663 def test_GetSparseImage_missingBlockMapFile(self):
664 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
665 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
666 target_files_zip.write(
667 test_utils.construct_sparse_image([
668 (0xCAC1, 6),
669 (0xCAC3, 3),
670 (0xCAC1, 4)]),
671 arcname='IMAGES/system.img')
672 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
673 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
674
Tao Baodba59ee2018-01-09 13:21:02 -0800675 tempdir = common.UnzipTemp(target_files)
676 with zipfile.ZipFile(target_files, 'r') as input_zip:
677 self.assertRaises(
678 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
679 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800680
Tao Bao82490d32019-04-09 00:12:30 -0700681 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800682 def test_GetSparseImage_sharedBlocks_notAllowed(self):
683 """Tests the case of having overlapping blocks but disallowed."""
684 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
685 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
686 target_files_zip.write(
687 test_utils.construct_sparse_image([(0xCAC2, 16)]),
688 arcname='IMAGES/system.img')
689 # Block 10 is shared between two files.
690 target_files_zip.writestr(
691 'IMAGES/system.map',
692 '\n'.join([
693 '/system/file1 1-5 9-10',
694 '/system/file2 10-12']))
695 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
696 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
697
Tao Baodba59ee2018-01-09 13:21:02 -0800698 tempdir = common.UnzipTemp(target_files)
699 with zipfile.ZipFile(target_files, 'r') as input_zip:
700 self.assertRaises(
701 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
702 False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800703
Tao Bao82490d32019-04-09 00:12:30 -0700704 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800705 def test_GetSparseImage_sharedBlocks_allowed(self):
706 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
707 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
708 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
709 # Construct an image with a care_map of "0-5 9-12".
710 target_files_zip.write(
711 test_utils.construct_sparse_image([(0xCAC2, 16)]),
712 arcname='IMAGES/system.img')
713 # Block 10 is shared between two files.
714 target_files_zip.writestr(
715 'IMAGES/system.map',
716 '\n'.join([
717 '/system/file1 1-5 9-10',
718 '/system/file2 10-12']))
719 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
720 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
721
Tao Baodba59ee2018-01-09 13:21:02 -0800722 tempdir = common.UnzipTemp(target_files)
723 with zipfile.ZipFile(target_files, 'r') as input_zip:
724 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -0800725
726 self.assertDictEqual(
727 {
728 '__COPY': RangeSet("0"),
729 '__NONZERO-0': RangeSet("6-8 13-15"),
730 '/system/file1': RangeSet("1-5 9-10"),
731 '/system/file2': RangeSet("11-12"),
732 },
733 sparse_image.file_map)
734
735 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
736 # 'incomplete'.
737 self.assertTrue(
738 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
739 self.assertNotIn(
740 'incomplete', sparse_image.file_map['/system/file2'].extra)
741
742 # All other entries should look normal without any tags.
743 self.assertFalse(sparse_image.file_map['__COPY'].extra)
744 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
745 self.assertFalse(sparse_image.file_map['/system/file1'].extra)
746
Tao Bao82490d32019-04-09 00:12:30 -0700747 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800748 def test_GetSparseImage_incompleteRanges(self):
749 """Tests the case of ext4 images with holes."""
750 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
751 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
752 target_files_zip.write(
753 test_utils.construct_sparse_image([(0xCAC2, 16)]),
754 arcname='IMAGES/system.img')
755 target_files_zip.writestr(
756 'IMAGES/system.map',
757 '\n'.join([
758 '/system/file1 1-5 9-10',
759 '/system/file2 11-12']))
760 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
761 # '/system/file2' has less blocks listed (2) than actual (3).
762 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
763
Tao Baodba59ee2018-01-09 13:21:02 -0800764 tempdir = common.UnzipTemp(target_files)
765 with zipfile.ZipFile(target_files, 'r') as input_zip:
766 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800767
768 self.assertFalse(sparse_image.file_map['/system/file1'].extra)
769 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
770
Tao Bao82490d32019-04-09 00:12:30 -0700771 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -0700772 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
773 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
774 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
775 target_files_zip.write(
776 test_utils.construct_sparse_image([(0xCAC2, 16)]),
777 arcname='IMAGES/system.img')
778 target_files_zip.writestr(
779 'IMAGES/system.map',
780 '\n'.join([
781 '//system/file1 1-5 9-10',
782 '//system/file2 11-12',
783 '/system/app/file3 13-15']))
784 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
785 # '/system/file2' has less blocks listed (2) than actual (3).
786 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
787 # '/system/app/file3' has less blocks listed (3) than actual (4).
788 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
789
790 tempdir = common.UnzipTemp(target_files)
791 with zipfile.ZipFile(target_files, 'r') as input_zip:
792 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
793
794 self.assertFalse(sparse_image.file_map['//system/file1'].extra)
795 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
796 self.assertTrue(
797 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
798
Tao Bao82490d32019-04-09 00:12:30 -0700799 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -0700800 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
801 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
802 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
803 target_files_zip.write(
804 test_utils.construct_sparse_image([(0xCAC2, 16)]),
805 arcname='IMAGES/system.img')
806 target_files_zip.writestr(
807 'IMAGES/system.map',
808 '\n'.join([
809 '//system/file1 1-5 9-10',
810 '//init.rc 13-15']))
811 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
812 # '/init.rc' has less blocks listed (3) than actual (4).
813 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
814
815 tempdir = common.UnzipTemp(target_files)
816 with zipfile.ZipFile(target_files, 'r') as input_zip:
817 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
818
819 self.assertFalse(sparse_image.file_map['//system/file1'].extra)
820 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
821
Tao Bao82490d32019-04-09 00:12:30 -0700822 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -0700823 def test_GetSparseImage_fileNotFound(self):
824 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
825 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
826 target_files_zip.write(
827 test_utils.construct_sparse_image([(0xCAC2, 16)]),
828 arcname='IMAGES/system.img')
829 target_files_zip.writestr(
830 'IMAGES/system.map',
831 '\n'.join([
832 '//system/file1 1-5 9-10',
833 '//system/file2 11-12']))
834 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
835
836 tempdir = common.UnzipTemp(target_files)
837 with zipfile.ZipFile(target_files, 'r') as input_zip:
838 self.assertRaises(
839 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
840 False)
841
Tao Bao82490d32019-04-09 00:12:30 -0700842 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -0700843 def test_GetAvbChainedPartitionArg(self):
844 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
845 info_dict = {
846 'avb_avbtool': 'avbtool',
847 'avb_system_key_path': pubkey,
848 'avb_system_rollback_index_location': 2,
849 }
850 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
851 self.assertEqual(3, len(args))
852 self.assertEqual('system', args[0])
853 self.assertEqual('2', args[1])
854 self.assertTrue(os.path.exists(args[2]))
855
Tao Bao82490d32019-04-09 00:12:30 -0700856 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -0700857 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
858 key = os.path.join(self.testdata_dir, 'testkey.key')
859 info_dict = {
860 'avb_avbtool': 'avbtool',
861 'avb_product_key_path': key,
862 'avb_product_rollback_index_location': 2,
863 }
864 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
865 self.assertEqual(3, len(args))
866 self.assertEqual('product', args[0])
867 self.assertEqual('2', args[1])
868 self.assertTrue(os.path.exists(args[2]))
869
Tao Bao82490d32019-04-09 00:12:30 -0700870 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -0700871 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
872 info_dict = {
873 'avb_avbtool': 'avbtool',
874 'avb_system_key_path': 'does-not-exist',
875 'avb_system_rollback_index_location': 2,
876 }
877 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
878 args = common.GetAvbChainedPartitionArg(
879 'system', info_dict, pubkey).split(':')
880 self.assertEqual(3, len(args))
881 self.assertEqual('system', args[0])
882 self.assertEqual('2', args[1])
883 self.assertTrue(os.path.exists(args[2]))
884
Tao Bao82490d32019-04-09 00:12:30 -0700885 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -0700886 def test_GetAvbChainedPartitionArg_invalidKey(self):
887 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
888 info_dict = {
889 'avb_avbtool': 'avbtool',
890 'avb_system_key_path': pubkey,
891 'avb_system_rollback_index_location': 2,
892 }
893 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -0700894 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
895 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -0700896
Tao Baoa57ab9f2018-08-24 12:08:38 -0700897 INFO_DICT_DEFAULT = {
898 'recovery_api_version': 3,
899 'fstab_version': 2,
900 'system_root_image': 'true',
901 'no_recovery' : 'true',
902 'recovery_as_boot': 'true',
903 }
904
905 @staticmethod
906 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
907 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
908 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
909 info_values = ''.join(
910 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.iteritems())])
911 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
912
913 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
914 if info_dict.get('system_root_image') == 'true':
915 fstab_values = FSTAB_TEMPLATE.format('/')
916 else:
917 fstab_values = FSTAB_TEMPLATE.format('/system')
918 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -0700919
920 common.ZipWriteStr(
921 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -0700922 return target_files
923
924 def test_LoadInfoDict(self):
925 target_files = self._test_LoadInfoDict_createTargetFiles(
926 self.INFO_DICT_DEFAULT,
927 'BOOT/RAMDISK/system/etc/recovery.fstab')
928 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
929 loaded_dict = common.LoadInfoDict(target_files_zip)
930 self.assertEqual(3, loaded_dict['recovery_api_version'])
931 self.assertEqual(2, loaded_dict['fstab_version'])
932 self.assertIn('/', loaded_dict['fstab'])
933 self.assertIn('/system', loaded_dict['fstab'])
934
935 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
936 target_files = self._test_LoadInfoDict_createTargetFiles(
937 self.INFO_DICT_DEFAULT,
938 'BOOT/RAMDISK/etc/recovery.fstab')
939 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
940 loaded_dict = common.LoadInfoDict(target_files_zip)
941 self.assertEqual(3, loaded_dict['recovery_api_version'])
942 self.assertEqual(2, loaded_dict['fstab_version'])
943 self.assertIn('/', loaded_dict['fstab'])
944 self.assertIn('/system', loaded_dict['fstab'])
945
Tao Bao82490d32019-04-09 00:12:30 -0700946 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -0700947 def test_LoadInfoDict_dirInput(self):
948 target_files = self._test_LoadInfoDict_createTargetFiles(
949 self.INFO_DICT_DEFAULT,
950 'BOOT/RAMDISK/system/etc/recovery.fstab')
951 unzipped = common.UnzipTemp(target_files)
952 loaded_dict = common.LoadInfoDict(unzipped)
953 self.assertEqual(3, loaded_dict['recovery_api_version'])
954 self.assertEqual(2, loaded_dict['fstab_version'])
955 self.assertIn('/', loaded_dict['fstab'])
956 self.assertIn('/system', loaded_dict['fstab'])
957
Tao Bao82490d32019-04-09 00:12:30 -0700958 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -0700959 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
960 target_files = self._test_LoadInfoDict_createTargetFiles(
961 self.INFO_DICT_DEFAULT,
962 'BOOT/RAMDISK/system/etc/recovery.fstab')
963 unzipped = common.UnzipTemp(target_files)
964 loaded_dict = common.LoadInfoDict(unzipped)
965 self.assertEqual(3, loaded_dict['recovery_api_version'])
966 self.assertEqual(2, loaded_dict['fstab_version'])
967 self.assertIn('/', loaded_dict['fstab'])
968 self.assertIn('/system', loaded_dict['fstab'])
969
970 def test_LoadInfoDict_systemRootImageFalse(self):
971 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
972 # launched prior to P will likely have this config.
973 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
974 del info_dict['no_recovery']
975 del info_dict['system_root_image']
976 del info_dict['recovery_as_boot']
977 target_files = self._test_LoadInfoDict_createTargetFiles(
978 info_dict,
979 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
980 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
981 loaded_dict = common.LoadInfoDict(target_files_zip)
982 self.assertEqual(3, loaded_dict['recovery_api_version'])
983 self.assertEqual(2, loaded_dict['fstab_version'])
984 self.assertNotIn('/', loaded_dict['fstab'])
985 self.assertIn('/system', loaded_dict['fstab'])
986
987 def test_LoadInfoDict_recoveryAsBootFalse(self):
988 # Devices using system-as-root, but with standalone recovery image. Non-A/B
989 # devices launched since P will likely have this config.
990 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
991 del info_dict['no_recovery']
992 del info_dict['recovery_as_boot']
993 target_files = self._test_LoadInfoDict_createTargetFiles(
994 info_dict,
995 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
996 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
997 loaded_dict = common.LoadInfoDict(target_files_zip)
998 self.assertEqual(3, loaded_dict['recovery_api_version'])
999 self.assertEqual(2, loaded_dict['fstab_version'])
1000 self.assertIn('/', loaded_dict['fstab'])
1001 self.assertIn('/system', loaded_dict['fstab'])
1002
1003 def test_LoadInfoDict_noRecoveryTrue(self):
1004 # Device doesn't have a recovery partition at all.
1005 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1006 del info_dict['recovery_as_boot']
1007 target_files = self._test_LoadInfoDict_createTargetFiles(
1008 info_dict,
1009 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1010 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1011 loaded_dict = common.LoadInfoDict(target_files_zip)
1012 self.assertEqual(3, loaded_dict['recovery_api_version'])
1013 self.assertEqual(2, loaded_dict['fstab_version'])
1014 self.assertIsNone(loaded_dict['fstab'])
1015
Tao Bao82490d32019-04-09 00:12:30 -07001016 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001017 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1018 target_files = self._test_LoadInfoDict_createTargetFiles(
1019 self.INFO_DICT_DEFAULT,
1020 'BOOT/RAMDISK/system/etc/recovery.fstab')
1021 common.ZipDelete(target_files, 'META/misc_info.txt')
1022 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1023 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1024
Tao Bao82490d32019-04-09 00:12:30 -07001025 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001026 def test_LoadInfoDict_repacking(self):
1027 target_files = self._test_LoadInfoDict_createTargetFiles(
1028 self.INFO_DICT_DEFAULT,
1029 'BOOT/RAMDISK/system/etc/recovery.fstab')
1030 unzipped = common.UnzipTemp(target_files)
1031 loaded_dict = common.LoadInfoDict(unzipped, True)
1032 self.assertEqual(3, loaded_dict['recovery_api_version'])
1033 self.assertEqual(2, loaded_dict['fstab_version'])
1034 self.assertIn('/', loaded_dict['fstab'])
1035 self.assertIn('/system', loaded_dict['fstab'])
1036 self.assertEqual(
1037 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1038 self.assertEqual(
1039 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1040 loaded_dict['root_fs_config'])
1041
1042 def test_LoadInfoDict_repackingWithZipFileInput(self):
1043 target_files = self._test_LoadInfoDict_createTargetFiles(
1044 self.INFO_DICT_DEFAULT,
1045 'BOOT/RAMDISK/system/etc/recovery.fstab')
1046 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1047 self.assertRaises(
1048 AssertionError, common.LoadInfoDict, target_files_zip, True)
1049
Tao Baofc7e0e02018-02-13 13:54:02 -08001050
Tao Bao65b94e92018-10-11 21:57:26 -07001051class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001052 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001053
Tao Bao1c830bf2017-12-25 10:43:47 -08001054 Its format should match between common.py and validate_target_files.py.
1055 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001056
1057 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001058 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001059 # Create a dummy dict that contains the fstab info for boot&recovery.
1060 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001061 dummy_fstab = [
1062 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1063 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001064 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001065 # Construct the gzipped recovery.img and boot.img
1066 self.recovery_data = bytearray([
1067 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1068 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1069 0x08, 0x00, 0x00, 0x00
1070 ])
1071 # echo -n "boot" | gzip -f | hd
1072 self.boot_data = bytearray([
1073 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1074 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1075 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001076
1077 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1078 loc = os.path.join(self._tempdir, prefix, name)
1079 if not os.path.exists(os.path.dirname(loc)):
1080 os.makedirs(os.path.dirname(loc))
1081 with open(loc, "w+") as f:
1082 f.write(data)
1083
1084 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001085 recovery_image = common.File("recovery.img", self.recovery_data)
1086 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001087 self._info["full_recovery_image"] = "true"
1088
1089 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1090 recovery_image, boot_image, self._info)
1091 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1092 self._info)
1093
Tao Bao82490d32019-04-09 00:12:30 -07001094 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001095 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001096 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001097 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001098 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001099 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1100
1101 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1102 recovery_image, boot_image, self._info)
1103 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1104 self._info)
1105 # Validate 'recovery-from-boot' with bonus argument.
1106 self._out_tmp_sink("etc/recovery-resource.dat", "bonus", "SYSTEM")
1107 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1108 recovery_image, boot_image, self._info)
1109 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1110 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001111
1112
1113class MockScriptWriter(object):
1114 """A class that mocks edify_generator.EdifyGenerator.
1115 """
1116 def __init__(self, enable_comments=False):
1117 self.lines = []
1118 self.enable_comments = enable_comments
1119 def Comment(self, comment):
1120 if self.enable_comments:
1121 self.lines.append("# {}".format(comment))
1122 def AppendExtra(self, extra):
1123 self.lines.append(extra)
1124 def __str__(self):
1125 return "\n".join(self.lines)
1126
1127
1128class MockBlockDifference(object):
1129 def __init__(self, partition, tgt, src=None):
1130 self.partition = partition
1131 self.tgt = tgt
1132 self.src = src
1133 def WriteScript(self, script, _, progress=None,
1134 write_verify_script=False):
1135 if progress:
1136 script.AppendExtra("progress({})".format(progress))
1137 script.AppendExtra("patch({});".format(self.partition))
1138 if write_verify_script:
1139 self.WritePostInstallVerifyScript(script)
1140 def WritePostInstallVerifyScript(self, script):
1141 script.AppendExtra("verify({});".format(self.partition))
1142
1143
1144class FakeSparseImage(object):
1145 def __init__(self, size):
1146 self.blocksize = 4096
1147 self.total_blocks = size // 4096
1148 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1149
1150
1151class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
1152 @staticmethod
1153 def get_op_list(output_path):
1154 with zipfile.ZipFile(output_path, 'r') as output_zip:
1155 with output_zip.open("dynamic_partitions_op_list") as op_list:
1156 return [line.strip() for line in op_list.readlines()
1157 if not line.startswith("#")]
1158
1159 def setUp(self):
1160 self.script = MockScriptWriter()
1161 self.output_path = common.MakeTempFile(suffix='.zip')
1162
1163 def test_full(self):
1164 target_info = common.LoadDictionaryFromLines("""
1165dynamic_partition_list=system vendor
1166super_partition_groups=group_foo
1167super_group_foo_group_size={group_size}
1168super_group_foo_partition_list=system vendor
1169""".format(group_size=4 * GiB).split("\n"))
1170 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1171 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1172
1173 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1174 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1175 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1176
1177 self.assertEqual(str(self.script).strip(), """
1178assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
1179patch(vendor);
1180verify(vendor);
1181unmap_partition("vendor");
1182patch(system);
1183verify(system);
1184unmap_partition("system");
1185""".strip())
1186
1187 lines = self.get_op_list(self.output_path)
1188
1189 remove_all_groups = lines.index("remove_all_groups")
1190 add_group = lines.index("add_group group_foo 4294967296")
1191 add_vendor = lines.index("add vendor group_foo")
1192 add_system = lines.index("add system group_foo")
1193 resize_vendor = lines.index("resize vendor 1073741824")
1194 resize_system = lines.index("resize system 3221225472")
1195
1196 self.assertLess(remove_all_groups, add_group,
1197 "Should add groups after removing all groups")
1198 self.assertLess(add_group, min(add_vendor, add_system),
1199 "Should add partitions after adding group")
1200 self.assertLess(add_system, resize_system,
1201 "Should resize system after adding it")
1202 self.assertLess(add_vendor, resize_vendor,
1203 "Should resize vendor after adding it")
1204
1205 def test_inc_groups(self):
1206 source_info = common.LoadDictionaryFromLines("""
1207super_partition_groups=group_foo group_bar group_baz
1208super_group_foo_group_size={group_foo_size}
1209super_group_bar_group_size={group_bar_size}
1210""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1211 target_info = common.LoadDictionaryFromLines("""
1212super_partition_groups=group_foo group_baz group_qux
1213super_group_foo_group_size={group_foo_size}
1214super_group_baz_group_size={group_baz_size}
1215super_group_qux_group_size={group_qux_size}
1216""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1217 group_qux_size=1 * GiB).split("\n"))
1218
1219 dp_diff = common.DynamicPartitionsDifference(target_info,
1220 block_diffs=[],
1221 source_info_dict=source_info)
1222 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1223 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1224
1225 lines = self.get_op_list(self.output_path)
1226
1227 removed = lines.index("remove_group group_bar")
1228 shrunk = lines.index("resize_group group_foo 3221225472")
1229 grown = lines.index("resize_group group_baz 4294967296")
1230 added = lines.index("add_group group_qux 1073741824")
1231
1232 self.assertLess(max(removed, shrunk) < min(grown, added),
1233 "ops that remove / shrink partitions must precede ops that "
1234 "grow / add partitions")
1235
Yifan Hongbb2658d2019-01-25 12:30:58 -08001236 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001237 source_info = common.LoadDictionaryFromLines("""
1238dynamic_partition_list=system vendor product product_services
1239super_partition_groups=group_foo
1240super_group_foo_group_size={group_foo_size}
1241super_group_foo_partition_list=system vendor product product_services
1242""".format(group_foo_size=4 * GiB).split("\n"))
1243 target_info = common.LoadDictionaryFromLines("""
1244dynamic_partition_list=system vendor product odm
1245super_partition_groups=group_foo group_bar
1246super_group_foo_group_size={group_foo_size}
1247super_group_foo_partition_list=system vendor odm
1248super_group_bar_group_size={group_bar_size}
1249super_group_bar_partition_list=product
1250""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1251
1252 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1253 src=FakeSparseImage(1024 * MiB)),
1254 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1255 src=FakeSparseImage(1024 * MiB)),
1256 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1257 src=FakeSparseImage(1024 * MiB)),
1258 MockBlockDifference("product_services", None,
1259 src=FakeSparseImage(1024 * MiB)),
1260 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1261 src=None)]
1262
1263 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1264 source_info_dict=source_info)
1265 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1266 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1267
1268 metadata_idx = self.script.lines.index(
1269 'assert(update_dynamic_partitions(package_extract_file('
1270 '"dynamic_partitions_op_list")));')
1271 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1272 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1273 for p in ("product", "system", "odm"):
1274 patch_idx = self.script.lines.index("patch({});".format(p))
1275 verify_idx = self.script.lines.index("verify({});".format(p))
1276 self.assertLess(metadata_idx, patch_idx,
1277 "Should patch {} after updating metadata".format(p))
1278 self.assertLess(patch_idx, verify_idx,
1279 "Should verify {} after patching".format(p))
1280
1281 self.assertNotIn("patch(product_services);", self.script.lines)
1282
1283 lines = self.get_op_list(self.output_path)
1284
1285 remove = lines.index("remove product_services")
1286 move_product_out = lines.index("move product default")
1287 shrink = lines.index("resize vendor 536870912")
1288 shrink_group = lines.index("resize_group group_foo 3221225472")
1289 add_group_bar = lines.index("add_group group_bar 1073741824")
1290 add_odm = lines.index("add odm group_foo")
1291 grow_existing = lines.index("resize system 1610612736")
1292 grow_added = lines.index("resize odm 1073741824")
1293 move_product_in = lines.index("move product group_bar")
1294
1295 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1296 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1297
1298 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1299 "Must shrink group after partitions inside group are shrunk"
1300 " / removed")
1301
1302 self.assertLess(add_group_bar, move_product_in,
1303 "Must add partitions to group after group is added")
1304
1305 self.assertLess(max_idx_move_partition_out_foo,
1306 min_idx_move_partition_in_foo,
1307 "Must shrink partitions / remove partitions from group"
1308 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001309
1310 def test_remove_partition(self):
1311 source_info = common.LoadDictionaryFromLines("""
1312blockimgdiff_versions=3,4
1313use_dynamic_partitions=true
1314dynamic_partition_list=foo
1315super_partition_groups=group_foo
1316super_group_foo_group_size={group_foo_size}
1317super_group_foo_partition_list=foo
1318""".format(group_foo_size=4 * GiB).split("\n"))
1319 target_info = common.LoadDictionaryFromLines("""
1320blockimgdiff_versions=3,4
1321use_dynamic_partitions=true
1322super_partition_groups=group_foo
1323super_group_foo_group_size={group_foo_size}
1324""".format(group_foo_size=4 * GiB).split("\n"))
1325
1326 common.OPTIONS.info_dict = target_info
1327 common.OPTIONS.target_info_dict = target_info
1328 common.OPTIONS.source_info_dict = source_info
1329 common.OPTIONS.cache_size = 4 * 4096
1330
1331 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1332 src=DataImage("source", pad=True))]
1333
1334 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1335 source_info_dict=source_info)
1336 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1337 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1338
1339 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001340 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001341
1342 lines = self.get_op_list(self.output_path)
1343 self.assertEqual(lines, ["remove foo"])