From: Simon Glass Date: Tue, 2 Oct 2018 03:12:47 +0000 (-0600) Subject: binman: Run tests concurrently X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=11ae93eef40c09280f36104b6a8cf5d807f0bb0d;p=u-boot.git binman: Run tests concurrently At present the tests run one after the other using a single CPU. This is not very efficient. Bring in the concurrencytest module and run the tests concurrently, using one process for each CPU by default. A -P option allows this to be overridden, which is necessary for code-coverage to function correctly. This requires fixing a few tests which are currently not fully independent. At some point we might consider doing this across all pytests in U-Boot. There is a pytest version that supports specifying the number of processes to use, but it did not work for me. Signed-off-by: Simon Glass --- diff --git a/.travis.yml b/.travis.yml index 2b759c9d68..f78749afb5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,7 @@ install: - virtualenv /tmp/venv - . /tmp/venv/bin/activate - pip install pytest + - pip install python-subunit - grub-mkimage -o ~/grub_x86.efi -O i386-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd - mkdir ~/grub2-arm - ( cd ~/grub2-arm; wget -O - http://download.opensuse.org/ports/armv7hl/distribution/leap/42.2/repo/oss/suse/armv7hl/grub2-arm-efi-2.02~beta2-87.1.armv7hl.rpm | rpm2cpio | cpio -di ) diff --git a/test/py/README.md b/test/py/README.md index aed2fd063a..4d9d2b81d1 100644 --- a/test/py/README.md +++ b/test/py/README.md @@ -29,6 +29,7 @@ tests. Similar package names should exist in other distributions. | -------------- | ----------------------------- | | python | 2.7.5-5ubuntu3 | | python-pytest | 2.5.1-1 | +| python-subunit | - | | gdisk | 0.8.8-1ubuntu0.1 | | dfu-util | 0.5-1 | | dtc | 1.4.0+dfsg-1 | diff --git a/tools/binman/binman.py b/tools/binman/binman.py index f5af5359f3..439908e665 100755 --- a/tools/binman/binman.py +++ b/tools/binman/binman.py @@ -10,6 +10,7 @@ """See README for more information""" import glob +import multiprocessing import os import sys import traceback @@ -17,7 +18,7 @@ import unittest # Bring in the patman and dtoc libraries our_path = os.path.dirname(os.path.realpath(__file__)) -for dirname in ['../patman', '../dtoc', '..']: +for dirname in ['../patman', '../dtoc', '..', '../concurrencytest']: sys.path.insert(0, os.path.join(our_path, dirname)) # Bring in the libfdt module @@ -27,16 +28,22 @@ sys.path.insert(0, os.path.join(our_path, import cmdline import command +use_concurrent = True +try: + from concurrencytest import ConcurrentTestSuite, fork_for_tests +except: + use_concurrent = False import control import test_util -def RunTests(debug, args): +def RunTests(debug, processes, args): """Run the functional tests and any embedded doctests Args: debug: True to enable debugging, which shows a full stack trace on error args: List of positional args provided to binman. This can hold a test name to execute (as in 'binman -t testSections', for example) + processes: Number of processes to use to run tests (None=same as #CPUs) """ import elf_test import entry_test @@ -54,19 +61,28 @@ def RunTests(debug, args): sys.argv = [sys.argv[0]] if debug: sys.argv.append('-D') + if debug: + sys.argv.append('-D') # Run the entry tests first ,since these need to be the first to import the # 'entry' module. test_name = args and args[0] or None + suite = unittest.TestSuite() + loader = unittest.TestLoader() for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf, image_test.TestImage): if test_name: try: - suite = unittest.TestLoader().loadTestsFromName(test_name, module) + suite.addTests(loader.loadTestsFromName(test_name, module)) except AttributeError: continue else: - suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.addTests(loader.loadTestsFromTestCase(module)) + if use_concurrent and processes != 1: + concurrent_suite = ConcurrentTestSuite(suite, + fork_for_tests(processes or multiprocessing.cpu_count())) + concurrent_suite.run(result) + else: suite.run(result) print result @@ -115,7 +131,7 @@ def RunBinman(options, args): sys.tracebacklimit = 0 if options.test: - ret_code = RunTests(options.debug, args[1:]) + ret_code = RunTests(options.debug, options.processes, args[1:]) elif options.test_coverage: RunTestCoverage() diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index f8caa7d284..3886d52b3a 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -46,6 +46,8 @@ def ParseArgs(argv): parser.add_option('-p', '--preserve', action='store_true',\ help='Preserve temporary output directory even if option -O is not ' 'given') + parser.add_option('-P', '--processes', type=int, + help='set number of processes to use for running tests') parser.add_option('-t', '--test', action='store_true', default=False, help='run tests') parser.add_option('-T', '--test-coverage', action='store_true', diff --git a/tools/binman/entry_test.py b/tools/binman/entry_test.py index a8bc938f9e..17ab229014 100644 --- a/tools/binman/entry_test.py +++ b/tools/binman/entry_test.py @@ -13,6 +13,8 @@ import fdt import fdt_util import tools +entry = None + class TestEntry(unittest.TestCase): def setUp(self): tools.PrepareOutputDir(None) @@ -38,7 +40,10 @@ class TestEntry(unittest.TestCase): def test2EntryImportLib(self): del sys.modules['importlib'] global entry - reload(entry) + if entry: + reload(entry) + else: + import entry entry.Entry.Create(None, self.GetNode(), 'u-boot-spl') del entry diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index ed78774560..9f21027bd4 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -367,6 +367,16 @@ class TestFunctional(unittest.TestCase): os.makedirs(pathname) return pathname + @classmethod + def _SetupSplElf(self, src_fname='bss_data'): + """Set up an ELF file with a '_dt_ucode_base_size' symbol + + Args: + Filename of ELF file to use as SPL + """ + with open(self.TestFile(src_fname)) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + @classmethod def TestFile(self, fname): return os.path.join(self._binman_dir, 'test', fname) @@ -715,8 +725,7 @@ class TestFunctional(unittest.TestCase): def testImagePadByte(self): """Test that the image pad byte can be specified""" - with open(self.TestFile('bss_data')) as fd: - TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + self._SetupSplElf() data = self._DoReadFile('21_image_pad.dts') self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data) @@ -739,6 +748,7 @@ class TestFunctional(unittest.TestCase): def testPackSorted(self): """Test that entries can be sorted""" + self._SetupSplElf() data = self._DoReadFile('24_sorted.dts') self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 + U_BOOT_DATA, data) @@ -781,6 +791,7 @@ class TestFunctional(unittest.TestCase): def testPackX86Rom(self): """Test that a basic x86 ROM can be created""" + self._SetupSplElf() data = self._DoReadFile('29_x86-rom.dts') self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA + chr(0) * 2, data) @@ -1017,15 +1028,13 @@ class TestFunctional(unittest.TestCase): def testSplBssPad(self): """Test that we can pad SPL's BSS with zeros""" # ELF file with a '__bss_size' symbol - with open(self.TestFile('bss_data')) as fd: - TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + self._SetupSplElf() data = self._DoReadFile('47_spl_bss_pad.dts') self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data) def testSplBssPadMissing(self): """Test that a missing symbol is detected""" - with open(self.TestFile('u_boot_ucode_ptr')) as fd: - TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + self._SetupSplElf('u_boot_ucode_ptr') with self.assertRaises(ValueError) as e: self._DoReadFile('47_spl_bss_pad.dts') self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', @@ -1050,9 +1059,7 @@ class TestFunctional(unittest.TestCase): ucode_second: True if the microsecond entry is second instead of third """ - # ELF file with a '_dt_ucode_base_size' symbol - with open(self.TestFile('u_boot_ucode_ptr')) as fd: - TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + self._SetupSplElf('u_boot_ucode_ptr') first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA, ucode_second=ucode_second) self.assertEqual('splnodtb with microc' + pos_and_size + @@ -1094,8 +1101,7 @@ class TestFunctional(unittest.TestCase): addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr) - with open(self.TestFile('u_boot_binman_syms')) as fd: - TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + self._SetupSplElf('u_boot_binman_syms') data = self._DoReadFile('53_symbols.dts') sym_values = struct.pack('