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