xref: /aosp_15_r20/external/avb/test/image_handler_unittest.py (revision d289c2ba6de359471b23d594623b906876bc48a0)
1*d289c2baSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*d289c2baSAndroid Build Coastguard Worker
3*d289c2baSAndroid Build Coastguard Worker# Copyright 2016, The Android Open Source Project
4*d289c2baSAndroid Build Coastguard Worker#
5*d289c2baSAndroid Build Coastguard Worker# Permission is hereby granted, free of charge, to any person
6*d289c2baSAndroid Build Coastguard Worker# obtaining a copy of this software and associated documentation
7*d289c2baSAndroid Build Coastguard Worker# files (the "Software"), to deal in the Software without
8*d289c2baSAndroid Build Coastguard Worker# restriction, including without limitation the rights to use, copy,
9*d289c2baSAndroid Build Coastguard Worker# modify, merge, publish, distribute, sublicense, and/or sell copies
10*d289c2baSAndroid Build Coastguard Worker# of the Software, and to permit persons to whom the Software is
11*d289c2baSAndroid Build Coastguard Worker# furnished to do so, subject to the following conditions:
12*d289c2baSAndroid Build Coastguard Worker#
13*d289c2baSAndroid Build Coastguard Worker# The above copyright notice and this permission notice shall be
14*d289c2baSAndroid Build Coastguard Worker# included in all copies or substantial portions of the Software.
15*d289c2baSAndroid Build Coastguard Worker#
16*d289c2baSAndroid Build Coastguard Worker# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*d289c2baSAndroid Build Coastguard Worker# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*d289c2baSAndroid Build Coastguard Worker# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19*d289c2baSAndroid Build Coastguard Worker# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20*d289c2baSAndroid Build Coastguard Worker# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21*d289c2baSAndroid Build Coastguard Worker# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22*d289c2baSAndroid Build Coastguard Worker# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23*d289c2baSAndroid Build Coastguard Worker# SOFTWARE.
24*d289c2baSAndroid Build Coastguard Worker
25*d289c2baSAndroid Build Coastguard Worker
26*d289c2baSAndroid Build Coastguard Worker"""Unit-test for ImageHandler."""
27*d289c2baSAndroid Build Coastguard Worker
28*d289c2baSAndroid Build Coastguard Worker
29*d289c2baSAndroid Build Coastguard Workerimport imp
30*d289c2baSAndroid Build Coastguard Workerimport os
31*d289c2baSAndroid Build Coastguard Workerimport sys
32*d289c2baSAndroid Build Coastguard Workerimport tempfile
33*d289c2baSAndroid Build Coastguard Workerimport unittest
34*d289c2baSAndroid Build Coastguard Worker
35*d289c2baSAndroid Build Coastguard Workersys.dont_write_bytecode = True
36*d289c2baSAndroid Build Coastguard Workeravbtool = imp.load_source('avbtool', './avbtool.py')
37*d289c2baSAndroid Build Coastguard Worker
38*d289c2baSAndroid Build Coastguard Worker# The file test_file.bin and test_file.bin.sparse are generated using
39*d289c2baSAndroid Build Coastguard Worker# the following python code:
40*d289c2baSAndroid Build Coastguard Worker#
41*d289c2baSAndroid Build Coastguard Worker#  with open('test_file.bin', 'w+b') as f:
42*d289c2baSAndroid Build Coastguard Worker#    f.write('Barfoo43'*128*12)
43*d289c2baSAndroid Build Coastguard Worker#  os.system('img2simg test_file.bin test_file.bin.sparse')
44*d289c2baSAndroid Build Coastguard Worker#  image = avbtool.ImageHandler('test_file.bin.sparse')
45*d289c2baSAndroid Build Coastguard Worker#  image.append_dont_care(12*1024)
46*d289c2baSAndroid Build Coastguard Worker#  image.append_fill('\x01\x02\x03\x04', 12*1024)
47*d289c2baSAndroid Build Coastguard Worker#  image.append_raw('Foobar42'*128*12)
48*d289c2baSAndroid Build Coastguard Worker#  image.append_dont_care(12*1024)
49*d289c2baSAndroid Build Coastguard Worker#  del image
50*d289c2baSAndroid Build Coastguard Worker#  os.system('rm -f test_file.bin')
51*d289c2baSAndroid Build Coastguard Worker#  os.system('simg2img test_file.bin.sparse test_file.bin')
52*d289c2baSAndroid Build Coastguard Worker#
53*d289c2baSAndroid Build Coastguard Worker# and manually verified to be correct. The content of the raw and
54*d289c2baSAndroid Build Coastguard Worker# sparse files are as follows (the line with "Fill with 0x04030201" is
55*d289c2baSAndroid Build Coastguard Worker# a simg_dump.py bug):
56*d289c2baSAndroid Build Coastguard Worker#
57*d289c2baSAndroid Build Coastguard Worker# $ hexdump -C test_file.bin
58*d289c2baSAndroid Build Coastguard Worker# 00000000  42 61 72 66 6f 6f 34 33  42 61 72 66 6f 6f 34 33  |Barfoo43Barfoo43|
59*d289c2baSAndroid Build Coastguard Worker# *
60*d289c2baSAndroid Build Coastguard Worker# 00003000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
61*d289c2baSAndroid Build Coastguard Worker# *
62*d289c2baSAndroid Build Coastguard Worker# 00006000  01 02 03 04 01 02 03 04  01 02 03 04 01 02 03 04  |................|
63*d289c2baSAndroid Build Coastguard Worker# *
64*d289c2baSAndroid Build Coastguard Worker# 00009000  46 6f 6f 62 61 72 34 32  46 6f 6f 62 61 72 34 32  |Foobar42Foobar42|
65*d289c2baSAndroid Build Coastguard Worker# *
66*d289c2baSAndroid Build Coastguard Worker# 0000c000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
67*d289c2baSAndroid Build Coastguard Worker# *
68*d289c2baSAndroid Build Coastguard Worker# 0000f000
69*d289c2baSAndroid Build Coastguard Worker#
70*d289c2baSAndroid Build Coastguard Worker# $ system/core/libsparse/simg_dump.py -v test_file.bin.sparse
71*d289c2baSAndroid Build Coastguard Worker# test_file.bin.sparse: Total of 15 4096-byte output blocks in 5 input chunks.
72*d289c2baSAndroid Build Coastguard Worker#             input_bytes      output_blocks
73*d289c2baSAndroid Build Coastguard Worker# chunk    offset     number  offset  number
74*d289c2baSAndroid Build Coastguard Worker#    1         40      12288       0       3 Raw data
75*d289c2baSAndroid Build Coastguard Worker#    2      12340          0       3       3 Don't care
76*d289c2baSAndroid Build Coastguard Worker#    3      12352          4       6       3 Fill with 0x04030201
77*d289c2baSAndroid Build Coastguard Worker#    4      12368      12288       9       3 Raw data
78*d289c2baSAndroid Build Coastguard Worker#    5      24668          0      12       3 Don't care
79*d289c2baSAndroid Build Coastguard Worker#           24668                 15         End
80*d289c2baSAndroid Build Coastguard Worker#
81*d289c2baSAndroid Build Coastguard Worker
82*d289c2baSAndroid Build Coastguard Worker
83*d289c2baSAndroid Build Coastguard Workerclass ImageHandler(unittest.TestCase):
84*d289c2baSAndroid Build Coastguard Worker
85*d289c2baSAndroid Build Coastguard Worker  TEST_FILE_SPARSE_PATH = 'test/data/test_file.bin.sparse'
86*d289c2baSAndroid Build Coastguard Worker  TEST_FILE_PATH = 'test/data/test_file.bin'
87*d289c2baSAndroid Build Coastguard Worker  TEST_FILE_SIZE = 61440
88*d289c2baSAndroid Build Coastguard Worker  TEST_FILE_BLOCK_SIZE = 4096
89*d289c2baSAndroid Build Coastguard Worker
90*d289c2baSAndroid Build Coastguard Worker  def _file_contents_equal(self, path1, path2, size):
91*d289c2baSAndroid Build Coastguard Worker    f1 = open(path1, 'r')
92*d289c2baSAndroid Build Coastguard Worker    f2 = open(path2, 'r')
93*d289c2baSAndroid Build Coastguard Worker    if f1.read(size) != f2.read(size):
94*d289c2baSAndroid Build Coastguard Worker      return False
95*d289c2baSAndroid Build Coastguard Worker    return True
96*d289c2baSAndroid Build Coastguard Worker
97*d289c2baSAndroid Build Coastguard Worker  def _file_size(self, f):
98*d289c2baSAndroid Build Coastguard Worker    old_pos = f.tell()
99*d289c2baSAndroid Build Coastguard Worker    f.seek(0, os.SEEK_END)
100*d289c2baSAndroid Build Coastguard Worker    size = f.tell()
101*d289c2baSAndroid Build Coastguard Worker    f.seek(old_pos)
102*d289c2baSAndroid Build Coastguard Worker    return size
103*d289c2baSAndroid Build Coastguard Worker
104*d289c2baSAndroid Build Coastguard Worker  def _clone_sparse_file(self):
105*d289c2baSAndroid Build Coastguard Worker    f = tempfile.NamedTemporaryFile(mode='wb')
106*d289c2baSAndroid Build Coastguard Worker    f.write(open(self.TEST_FILE_SPARSE_PATH, 'rb').read())
107*d289c2baSAndroid Build Coastguard Worker    f.flush()
108*d289c2baSAndroid Build Coastguard Worker    return f
109*d289c2baSAndroid Build Coastguard Worker
110*d289c2baSAndroid Build Coastguard Worker  def _unsparsify(self, path):
111*d289c2baSAndroid Build Coastguard Worker    f = tempfile.NamedTemporaryFile()
112*d289c2baSAndroid Build Coastguard Worker    os.system('simg2img {} {}'.format(path, f.name))
113*d289c2baSAndroid Build Coastguard Worker    return f
114*d289c2baSAndroid Build Coastguard Worker
115*d289c2baSAndroid Build Coastguard Worker  def testRead(self):
116*d289c2baSAndroid Build Coastguard Worker    """Checks that reading from a sparse file works as intended."""
117*d289c2baSAndroid Build Coastguard Worker    ih = avbtool.ImageHandler(self.TEST_FILE_SPARSE_PATH)
118*d289c2baSAndroid Build Coastguard Worker
119*d289c2baSAndroid Build Coastguard Worker    # Check that we start at offset 0.
120*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.tell(), 0)
121*d289c2baSAndroid Build Coastguard Worker
122*d289c2baSAndroid Build Coastguard Worker    # Check that reading advances the cursor.
123*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(14), bytearray(b'Barfoo43Barfoo'))
124*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.tell(), 14)
125*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(2), bytearray(b'43'))
126*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.tell(), 16)
127*d289c2baSAndroid Build Coastguard Worker
128*d289c2baSAndroid Build Coastguard Worker    # Check reading in the middle of a fill chunk gets the right data.
129*d289c2baSAndroid Build Coastguard Worker    ih.seek(0x6000 + 1)
130*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(4), bytearray(b'\x02\x03\x04\x01'))
131*d289c2baSAndroid Build Coastguard Worker
132*d289c2baSAndroid Build Coastguard Worker    # Check we can cross the chunk boundary correctly.
133*d289c2baSAndroid Build Coastguard Worker    ih.seek(0x3000 - 10)
134*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(12), bytearray(b'43Barfoo43\x00\x00'))
135*d289c2baSAndroid Build Coastguard Worker    ih.seek(0x9000 - 3)
136*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(5), bytearray(b'\x02\x03\x04Fo'))
137*d289c2baSAndroid Build Coastguard Worker
138*d289c2baSAndroid Build Coastguard Worker    # Check reading at end of file is a partial read.
139*d289c2baSAndroid Build Coastguard Worker    ih.seek(0xf000 - 2)
140*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(ih.read(16), bytearray(b'\x00\x00'))
141*d289c2baSAndroid Build Coastguard Worker
142*d289c2baSAndroid Build Coastguard Worker  def testTruncate(self):
143*d289c2baSAndroid Build Coastguard Worker    """Checks that we can truncate a sparse file correctly."""
144*d289c2baSAndroid Build Coastguard Worker    # Check truncation at all possible boundaries (including start and end).
145*d289c2baSAndroid Build Coastguard Worker    for size in range(0, self.TEST_FILE_SIZE + self.TEST_FILE_BLOCK_SIZE,
146*d289c2baSAndroid Build Coastguard Worker                      self.TEST_FILE_BLOCK_SIZE):
147*d289c2baSAndroid Build Coastguard Worker      sparse_file = self._clone_sparse_file()
148*d289c2baSAndroid Build Coastguard Worker      ih = avbtool.ImageHandler(sparse_file.name)
149*d289c2baSAndroid Build Coastguard Worker      ih.truncate(size)
150*d289c2baSAndroid Build Coastguard Worker      unsparse_file = self._unsparsify(sparse_file.name)
151*d289c2baSAndroid Build Coastguard Worker      self.assertEqual(self._file_size(unsparse_file), size)
152*d289c2baSAndroid Build Coastguard Worker      self.assertTrue(self._file_contents_equal(unsparse_file.name,
153*d289c2baSAndroid Build Coastguard Worker                                                self.TEST_FILE_PATH,
154*d289c2baSAndroid Build Coastguard Worker                                                size))
155*d289c2baSAndroid Build Coastguard Worker
156*d289c2baSAndroid Build Coastguard Worker    # Check truncation to grow the file.
157*d289c2baSAndroid Build Coastguard Worker    grow_size = 8192
158*d289c2baSAndroid Build Coastguard Worker    sparse_file = self._clone_sparse_file()
159*d289c2baSAndroid Build Coastguard Worker    ih = avbtool.ImageHandler(sparse_file.name)
160*d289c2baSAndroid Build Coastguard Worker    ih.truncate(self.TEST_FILE_SIZE + grow_size)
161*d289c2baSAndroid Build Coastguard Worker    unsparse_file = self._unsparsify(sparse_file.name)
162*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(self._file_size(unsparse_file),
163*d289c2baSAndroid Build Coastguard Worker                     self.TEST_FILE_SIZE + grow_size)
164*d289c2baSAndroid Build Coastguard Worker    self.assertTrue(self._file_contents_equal(unsparse_file.name,
165*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_PATH,
166*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_SIZE))
167*d289c2baSAndroid Build Coastguard Worker    unsparse_file.seek(self.TEST_FILE_SIZE)
168*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(unsparse_file.read(), b'\0'*grow_size)
169*d289c2baSAndroid Build Coastguard Worker
170*d289c2baSAndroid Build Coastguard Worker  def testAppendRaw(self):
171*d289c2baSAndroid Build Coastguard Worker    """Checks that we can append raw data correctly."""
172*d289c2baSAndroid Build Coastguard Worker    sparse_file = self._clone_sparse_file()
173*d289c2baSAndroid Build Coastguard Worker    ih = avbtool.ImageHandler(sparse_file.name)
174*d289c2baSAndroid Build Coastguard Worker    data = b'SomeData'*4096
175*d289c2baSAndroid Build Coastguard Worker    ih.append_raw(data)
176*d289c2baSAndroid Build Coastguard Worker    unsparse_file = self._unsparsify(sparse_file.name)
177*d289c2baSAndroid Build Coastguard Worker    self.assertTrue(self._file_contents_equal(unsparse_file.name,
178*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_PATH,
179*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_SIZE))
180*d289c2baSAndroid Build Coastguard Worker    unsparse_file.seek(self.TEST_FILE_SIZE)
181*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(unsparse_file.read(), data)
182*d289c2baSAndroid Build Coastguard Worker
183*d289c2baSAndroid Build Coastguard Worker  def testAppendFill(self):
184*d289c2baSAndroid Build Coastguard Worker    """Checks that we can append fill data correctly."""
185*d289c2baSAndroid Build Coastguard Worker    sparse_file = self._clone_sparse_file()
186*d289c2baSAndroid Build Coastguard Worker    ih = avbtool.ImageHandler(sparse_file.name)
187*d289c2baSAndroid Build Coastguard Worker    data = b'ABCD'*4096
188*d289c2baSAndroid Build Coastguard Worker    ih.append_fill(b'ABCD', len(data))
189*d289c2baSAndroid Build Coastguard Worker    unsparse_file = self._unsparsify(sparse_file.name)
190*d289c2baSAndroid Build Coastguard Worker    self.assertTrue(self._file_contents_equal(unsparse_file.name,
191*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_PATH,
192*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_SIZE))
193*d289c2baSAndroid Build Coastguard Worker    unsparse_file.seek(self.TEST_FILE_SIZE)
194*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(unsparse_file.read(), data)
195*d289c2baSAndroid Build Coastguard Worker
196*d289c2baSAndroid Build Coastguard Worker  def testDontCare(self):
197*d289c2baSAndroid Build Coastguard Worker    """Checks that we can append DONT_CARE data correctly."""
198*d289c2baSAndroid Build Coastguard Worker    sparse_file = self._clone_sparse_file()
199*d289c2baSAndroid Build Coastguard Worker    ih = avbtool.ImageHandler(sparse_file.name)
200*d289c2baSAndroid Build Coastguard Worker    data = b'\0'*40960
201*d289c2baSAndroid Build Coastguard Worker    ih.append_dont_care(len(data))
202*d289c2baSAndroid Build Coastguard Worker    unsparse_file = self._unsparsify(sparse_file.name)
203*d289c2baSAndroid Build Coastguard Worker    self.assertTrue(self._file_contents_equal(unsparse_file.name,
204*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_PATH,
205*d289c2baSAndroid Build Coastguard Worker                                              self.TEST_FILE_SIZE))
206*d289c2baSAndroid Build Coastguard Worker    unsparse_file.seek(self.TEST_FILE_SIZE)
207*d289c2baSAndroid Build Coastguard Worker    self.assertEqual(unsparse_file.read(), data)
208*d289c2baSAndroid Build Coastguard Worker
209*d289c2baSAndroid Build Coastguard Worker
210*d289c2baSAndroid Build Coastguard Workerif __name__ == '__main__':
211*d289c2baSAndroid Build Coastguard Worker  unittest.main()
212