diff options
Diffstat (limited to 'tools/binman/ftest.py')
-rw-r--r-- | tools/binman/ftest.py | 260 |
1 files changed, 227 insertions, 33 deletions
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index fa1f421c052..0b1774046f7 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -23,7 +23,6 @@ import urllib.error from binman import bintool from binman import cbfs_util from binman import cmdline -from binman import comp_util from binman import control from binman import elf from binman import elf_test @@ -107,6 +106,8 @@ BASE_DTB_PROPS = ['offset', 'size', 'image-pos'] # Extra properties expected to be in the device tree when allow-repack is used REPACK_DTB_PROPS = ['orig-offset', 'orig-size'] +# Supported compression bintools +COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd'] class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -212,7 +213,9 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('tee.elf', tools.read_file(cls.ElfTestFile('elf_sections'))) - cls.have_lz4 = comp_util.HAVE_LZ4 + cls.comp_bintools = {} + for name in COMP_BINTOOLS: + cls.comp_bintools[name] = bintool.Bintool.create(name) @classmethod def tearDownClass(cls): @@ -242,9 +245,13 @@ class TestFunctional(unittest.TestCase): cls.toolpath = toolpath cls.verbosity = verbosity + def _CheckBintool(self, bintool): + if not bintool.is_present(): + self.skipTest('%s not available' % bintool.name) + def _CheckLz4(self): - if not self.have_lz4: - self.skipTest('lz4 --no-frame-crc not available') + bintool = self.comp_bintools['lz4'] + self._CheckBintool(bintool) def _CleanupOutputDir(self): """Remove the temporary output directory""" @@ -1693,7 +1700,7 @@ class TestFunctional(unittest.TestCase): """Test for an fill entry type with no size""" with self.assertRaises(ValueError) as e: self._DoReadFile('070_fill_no_size.dts') - self.assertIn("'fill' entry must have a size property", + self.assertIn("'fill' entry is missing properties: size", str(e.exception)) def _HandleGbbCommand(self, pipe_list): @@ -1967,7 +1974,8 @@ class TestFunctional(unittest.TestCase): self._ResetDtbs() def _decompress(self, data): - return comp_util.decompress(data, 'lz4') + bintool = self.comp_bintools['lz4'] + return bintool.decompress(data) def testCompress(self): """Test compression of blobs""" @@ -2855,8 +2863,10 @@ class TestFunctional(unittest.TestCase): def testExtractCbfsRaw(self): """Test extracting CBFS compressed data without decompressing it""" + bintool = self.comp_bintools['lzma_alone'] + self._CheckBintool(bintool) data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False) - dtb = comp_util.decompress(data, 'lzma', with_header=False) + dtb = bintool.decompress(data) self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) def testExtractBadEntry(self): @@ -4412,6 +4422,15 @@ class TestFunctional(unittest.TestCase): } self.assertEqual(expected, props) + def testLz4Missing(self): + """Test that binman still produces an image if lz4 is missing""" + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('185_compress_section.dts', + force_missing_bintools='lz4') + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: lz4") + def testCompressExtra(self): """Test compression of a section with no fixed size""" self._CheckLz4() @@ -4427,15 +4446,18 @@ class TestFunctional(unittest.TestCase): rest = base[len(U_BOOT_DATA):] # Check compressed data - section1 = self._decompress(rest) - expect1 = comp_util.compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4') - self.assertEquals(expect1, rest[:len(expect1)]) + bintool = self.comp_bintools['lz4'] + expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA) + data1 = rest[:len(expect1)] + section1 = self._decompress(data1) + self.assertEquals(expect1, data1) self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1) rest1 = rest[len(expect1):] - section2 = self._decompress(rest1) - expect2 = comp_util.compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4') - self.assertEquals(expect2, rest1[:len(expect2)]) + expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA) + data2 = rest1[:len(expect2)] + section2 = self._decompress(data2) + self.assertEquals(expect2, data2) self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2) rest2 = rest1[len(expect2):] @@ -4807,6 +4829,8 @@ class TestFunctional(unittest.TestCase): def testUpdateFdtInElf(self): """Test that we can update the devicetree in an ELF file""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') outfile = os.path.join(self._indir, 'u-boot.out') begin_sym = 'dtb_embed_begin' @@ -4858,6 +4882,8 @@ class TestFunctional(unittest.TestCase): def testUpdateFdtInElfNoSyms(self): """Test that missing symbols are detected with --update-fdt-in-elf""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') infile = elf_fname = self.ElfTestFile('u_boot_binman_embed') outfile = '' begin_sym = 'wrong_begin' @@ -4871,6 +4897,8 @@ class TestFunctional(unittest.TestCase): def testUpdateFdtInElfTooSmall(self): """Test that an over-large dtb is detected with --update-fdt-in-elf""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm') outfile = os.path.join(self._indir, 'u-boot.out') begin_sym = 'dtb_embed_begin' @@ -5209,15 +5237,6 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self._DoBinman(*args) self.assertIn('failed to fetch with all methods', stdout.getvalue()) - def testInvalidCompress(self): - with self.assertRaises(ValueError) as e: - comp_util.compress(b'', 'invalid') - self.assertIn("Unknown algorithm 'invalid'", str(e.exception)) - - with self.assertRaises(ValueError) as e: - comp_util.decompress(b'1234', 'invalid') - self.assertIn("Unknown algorithm 'invalid'", str(e.exception)) - def testBintoolDocs(self): """Test for creation of bintool documentation""" with test_util.capture_sys_output() as (stdout, stderr): @@ -5344,6 +5363,8 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testFitSplitElf(self): """Test an image with an FIT with an split-elf operation""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') entry_args = { 'of-list': 'test-fdt1 test-fdt2', 'default-dt': 'test-fdt2', @@ -5421,6 +5442,8 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testFitSplitElfBadElf(self): """Test a FIT split-elf operation with an invalid ELF file""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100)) entry_args = { 'of-list': 'test-fdt1 test-fdt2', @@ -5440,6 +5463,8 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testFitSplitElfBadDirective(self): """Test a FIT split-elf invalid fit,xxx directive in an image node""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') err = self._check_bad_fit('227_fit_bad_dir.dts') self.assertIn( "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'", @@ -5447,13 +5472,24 @@ fdt fdtmap Extract the devicetree blob from the fdtmap def testFitSplitElfBadDirectiveConfig(self): """Test a FIT split-elf with invalid fit,xxx directive in config""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') err = self._check_bad_fit('228_fit_bad_dir_config.dts') self.assertEqual( "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'", err) def checkFitSplitElf(self, **kwargs): - """Test an split-elf FIT with a missing ELF file""" + """Test an split-elf FIT with a missing ELF file + + Args: + kwargs (dict of str): Arguments to pass to _DoTestFile() + + Returns: + tuple: + str: stdout result + str: stderr result + """ entry_args = { 'of-list': 'test-fdt1 test-fdt2', 'default-dt': 'test-fdt2', @@ -5464,23 +5500,36 @@ fdt fdtmap Extract the devicetree blob from the fdtmap with test_util.capture_sys_output() as (stdout, stderr): self._DoTestFile( '226_fit_split_elf.dts', entry_args=entry_args, - extra_indirs=[test_subdir], **kwargs) - err = stderr.getvalue() - return err + extra_indirs=[test_subdir], verbosity=3, **kwargs) + out = stdout.getvalue() + err = stderr.getvalue() + return out, err def testFitSplitElfMissing(self): """Test an split-elf FIT with a missing ELF file""" - err = self.checkFitSplitElf(allow_missing=True) + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + out, err = self.checkFitSplitElf(allow_missing=True) self.assertRegex( err, "Image '.*' is missing external blobs and is non-functional: .*") + self.assertNotRegex(out, '.*Faked blob.*') + fname = tools.get_output_filename('binman-fake/missing.elf') + self.assertFalse(os.path.exists(fname)) def testFitSplitElfFaked(self): """Test an split-elf FIT with faked ELF file""" - err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True) + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True) self.assertRegex( err, "Image '.*' is missing external blobs and is non-functional: .*") + self.assertRegex( + out, + "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf") + fname = tools.get_output_filename('binman-fake/missing.elf') + self.assertTrue(os.path.exists(fname)) def testPreLoad(self): """Test an image with a pre-load header""" @@ -5694,14 +5743,159 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self.assertIsNotNone(path) self.assertEqual(expected_fdtmap, fdtmap) - @unittest.expectedFailure def testReplaceSectionSimple(self): """Test replacing a simple section with arbitrary data""" new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA) - data, expected_fdtmap, _ = self._RunReplaceCmd( - 'section', new_data, - dts='234_replace_section_simple.dts') - self.assertEqual(new_data, data) + with self.assertRaises(ValueError) as exc: + self._RunReplaceCmd('section', new_data, + dts='234_replace_section_simple.dts') + self.assertIn( + "Node '/section': Replacing sections is not implemented yet", + str(exc.exception)) + + def testMkimageImagename(self): + """Test using mkimage with -n holding the data too""" + data = self._DoReadFile('235_mkimage_name.dts') + + # Check that the data appears in the file somewhere + self.assertIn(U_BOOT_SPL_DATA, data) + + # Get struct image_header -> ih_name + name = data[0x20:0x40] + + # Build the filename that we expect to be placed in there, by virtue of + # the -n paraameter + expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage') + + # Check that the image name is set to the temporary filename used + self.assertEqual(expect.encode('utf-8')[:0x20], name) + + def testMkimageImage(self): + """Test using mkimage with -n holding the data too""" + data = self._DoReadFile('236_mkimage_image.dts') + + # Check that the data appears in the file somewhere + self.assertIn(U_BOOT_SPL_DATA, data) + + # Get struct image_header -> ih_name + name = data[0x20:0x40] + + # Build the filename that we expect to be placed in there, by virtue of + # the -n paraameter + expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage') + + # Check that the image name is set to the temporary filename used + self.assertEqual(expect.encode('utf-8')[:0x20], name) + + # Check the corect data is in the imagename file + self.assertEqual(U_BOOT_DATA, tools.read_file(expect)) + + def testMkimageImageNoContent(self): + """Test using mkimage with -n and no data""" + with self.assertRaises(ValueError) as exc: + self._DoReadFile('237_mkimage_image_no_content.dts') + self.assertIn('Could not complete processing of contents', + str(exc.exception)) + + def testMkimageImageBad(self): + """Test using mkimage with imagename node and data-to-imagename""" + with self.assertRaises(ValueError) as exc: + self._DoReadFile('238_mkimage_image_bad.dts') + self.assertIn('Cannot use both imagename node and data-to-imagename', + str(exc.exception)) + + def testCollectionOther(self): + """Test a collection where the data comes from another section""" + data = self._DoReadFile('239_collection_other.dts') + self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + + tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA + + tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA, + data) + + def testMkimageCollection(self): + """Test using a collection referring to an entry in a mkimage entry""" + data = self._DoReadFile('240_mkimage_coll.dts') + expect = U_BOOT_SPL_DATA + U_BOOT_DATA + self.assertEqual(expect, data[:len(expect)]) + + def testCompressDtbPrependInvalid(self): + """Test that invalid header is detected""" + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb('235_compress_dtb_prepend_invalid.dts') + self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in " + "'u-boot-dtb': 'invalid'", str(e.exception)) + + def testCompressDtbPrependLength(self): + """Test that compress with length header works as expected""" + data = self._DoReadFileRealDtb('236_compress_dtb_prepend_length.dts') + image = control.images['image'] + entries = image.GetEntries() + self.assertIn('u-boot-dtb', entries) + u_boot_dtb = entries['u-boot-dtb'] + self.assertIn('fdtmap', entries) + fdtmap = entries['fdtmap'] + + image_fname = tools.get_output_filename('image.bin') + orig = control.ReadEntry(image_fname, 'u-boot-dtb') + dtb = fdt.Fdt.FromData(orig) + dtb.Scan() + props = self._GetPropTree(dtb, ['size', 'uncomp-size']) + expected = { + 'u-boot:size': len(U_BOOT_DATA), + 'u-boot-dtb:uncomp-size': len(orig), + 'u-boot-dtb:size': u_boot_dtb.size, + 'fdtmap:size': fdtmap.size, + 'size': len(data), + } + self.assertEqual(expected, props) + + # Check implementation + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + rest = data[len(U_BOOT_DATA):] + comp_data_len = struct.unpack('<I', rest[:4])[0] + comp_data = rest[4:4 + comp_data_len] + orig2 = self._decompress(comp_data) + self.assertEqual(orig, orig2) + + def testInvalidCompress(self): + """Test that invalid compress algorithm is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('237_compress_dtb_invalid.dts') + self.assertIn("Unknown algorithm 'invalid'", str(e.exception)) + + def testCompUtilCompressions(self): + """Test compression algorithms""" + for bintool in self.comp_bintools.values(): + self._CheckBintool(bintool) + data = bintool.compress(COMPRESS_DATA) + self.assertNotEqual(COMPRESS_DATA, data) + orig = bintool.decompress(data) + self.assertEquals(COMPRESS_DATA, orig) + + def testCompUtilVersions(self): + """Test tool version of compression algorithms""" + for bintool in self.comp_bintools.values(): + self._CheckBintool(bintool) + version = bintool.version() + self.assertRegex(version, '^v?[0-9]+[0-9.]*') + + def testCompUtilPadding(self): + """Test padding of compression algorithms""" + # Skip zstd because it doesn't support padding + for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']: + self._CheckBintool(bintool) + data = bintool.compress(COMPRESS_DATA) + self.assertNotEqual(COMPRESS_DATA, data) + data += tools.get_bytes(0, 64) + orig = bintool.decompress(data) + self.assertEquals(COMPRESS_DATA, orig) + + def testCompressDtbZstd(self): + """Test that zstd compress of device-tree files failed""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('238_compress_dtb_zstd.dts') + self.assertIn("Node '/binman/u-boot-dtb': The zstd compression " + "requires a length header", str(e.exception)) if __name__ == "__main__": |