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