summaryrefslogtreecommitdiff
path: root/tools/buildman/func_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/buildman/func_test.py')
-rw-r--r--tools/buildman/func_test.py333
1 files changed, 312 insertions, 21 deletions
diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py
index ebd78f225e1..3115700f07b 100644
--- a/tools/buildman/func_test.py
+++ b/tools/buildman/func_test.py
@@ -3,9 +3,11 @@
#
import os
+from pathlib import Path
import shutil
import sys
import tempfile
+import time
import unittest
from buildman import board
@@ -38,8 +40,8 @@ chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
'''
BOARDS = [
- ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
- ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
+ ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 0', 'board0', ''],
+ ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board1', ''],
['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
]
@@ -184,8 +186,8 @@ class TestFunctional(unittest.TestCase):
self._buildman_pathname = sys.argv[0]
self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
command.test_result = self._HandleCommand
- bsettings.Setup(None)
- bsettings.AddFile(settings_data)
+ bsettings.setup(None)
+ bsettings.add_file(settings_data)
self.setupToolchains()
self._toolchains.Add('arm-gcc', test=False)
self._toolchains.Add('powerpc-gcc', test=False)
@@ -209,6 +211,12 @@ class TestFunctional(unittest.TestCase):
# Set to True to report missing blobs
self._missing = False
+ self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ self._test_dir = os.path.join(self._buildman_dir, 'test')
+
+ # Set up some fake source files
+ shutil.copytree(self._test_dir, self._git_dir)
+
# Avoid sending any output and clear all terminal output
terminal.set_print_test_mode()
terminal.get_print_test_lines()
@@ -225,29 +233,34 @@ class TestFunctional(unittest.TestCase):
return command.run_pipe([[self._buildman_pathname] + list(args)],
capture=True, capture_stderr=True)
- def _RunControl(self, *args, brds=None, clean_dir=False,
- test_thread_exceptions=False):
+ def _RunControl(self, *args, brds=False, clean_dir=False,
+ test_thread_exceptions=False, get_builder=True):
"""Run buildman
Args:
args: List of arguments to pass
- brds: Boards object
+ brds: Boards object, or False to pass self._boards, or None to pass
+ None
clean_dir: Used for tests only, indicates that the existing output_dir
should be removed before starting the build
test_thread_exceptions: Uses for tests only, True to make the threads
raise an exception instead of reporting their result. This simulates
a failure in the code somewhere
+ get_builder (bool): Set self._builder to the resulting builder
Returns:
result code from buildman
"""
sys.argv = [sys.argv[0]] + list(args)
- options, args = cmdline.ParseArgs()
- result = control.DoBuildman(options, args, toolchains=self._toolchains,
- make_func=self._HandleMake, brds=brds or self._boards,
- clean_dir=clean_dir,
- test_thread_exceptions=test_thread_exceptions)
- self._builder = control.builder
+ args = cmdline.parse_args()
+ if brds == False:
+ brds = self._boards
+ result = control.do_buildman(
+ args, toolchains=self._toolchains, make_func=self._HandleMake,
+ brds=brds, clean_dir=clean_dir,
+ test_thread_exceptions=test_thread_exceptions)
+ if get_builder:
+ self._builder = control.TEST_BUILDER
return result
def testFullHelp(self):
@@ -496,10 +509,12 @@ Some images are invalid'''
for commit in range(self._commits):
for brd in self._boards.get_list():
if brd.arch != 'sandbox':
- errfile = self._builder.GetErrFile(commit, brd.target)
+ errfile = self._builder.get_err_file(commit, brd.target)
fd = open(errfile)
- self.assertEqual(fd.readlines(),
- ['No tool chain for %s\n' % brd.arch])
+ self.assertEqual(
+ fd.readlines(),
+ [f'Tool chain error for {brd.arch}: '
+ f"No tool chain found for arch '{brd.arch}'"])
fd.close()
def testBranch(self):
@@ -573,7 +588,8 @@ Some images are invalid'''
def testBranchWithSlash(self):
"""Test building a branch with a '/' in the name"""
self._test_branch = '/__dev/__testbranch'
- self._RunControl('-b', self._test_branch, clean_dir=False)
+ self._RunControl('-b', self._test_branch, '-o', self._output_dir,
+ clean_dir=False)
self.assertEqual(self._builder.count, self._total_builds)
self.assertEqual(self._builder.fail, 0)
@@ -686,7 +702,7 @@ Some images are invalid'''
def testBlobSettingsAlways(self):
"""Test the 'always' policy"""
- bsettings.SetItem('global', 'allow-missing', 'always')
+ bsettings.set_item('global', 'allow-missing', 'always')
self.assertEqual(True,
control.get_allow_missing(False, False, 1, False))
self.assertEqual(False,
@@ -694,7 +710,7 @@ Some images are invalid'''
def testBlobSettingsBranch(self):
"""Test the 'branch' policy"""
- bsettings.SetItem('global', 'allow-missing', 'branch')
+ bsettings.set_item('global', 'allow-missing', 'branch')
self.assertEqual(False,
control.get_allow_missing(False, False, 1, False))
self.assertEqual(True,
@@ -704,7 +720,7 @@ Some images are invalid'''
def testBlobSettingsMultiple(self):
"""Test the 'multiple' policy"""
- bsettings.SetItem('global', 'allow-missing', 'multiple')
+ bsettings.set_item('global', 'allow-missing', 'multiple')
self.assertEqual(False,
control.get_allow_missing(False, False, 1, False))
self.assertEqual(True,
@@ -714,7 +730,7 @@ Some images are invalid'''
def testBlobSettingsBranchMultiple(self):
"""Test the 'branch multiple' policy"""
- bsettings.SetItem('global', 'allow-missing', 'branch multiple')
+ bsettings.set_item('global', 'allow-missing', 'branch multiple')
self.assertEqual(False,
control.get_allow_missing(False, False, 1, False))
self.assertEqual(True,
@@ -779,3 +795,278 @@ Some images are invalid'''
CONFIG_LOCALVERSION=y
''', cfg_data)
self.assertIn('Not dropping LOCALVERSION_AUTO', stdout.getvalue())
+
+ def test_scan_defconfigs(self):
+ """Test scanning the defconfigs to obtain all the boards"""
+ src = self._git_dir
+
+ # Scan the test directory which contains a Kconfig and some *_defconfig
+ # files
+ params, warnings = self._boards.scan_defconfigs(src, src)
+
+ # We should get two boards
+ self.assertEquals(2, len(params))
+ self.assertFalse(warnings)
+ first = 0 if params[0]['target'] == 'board0' else 1
+ board0 = params[first]
+ board2 = params[1 - first]
+
+ self.assertEquals('arm', board0['arch'])
+ self.assertEquals('armv7', board0['cpu'])
+ self.assertEquals('-', board0['soc'])
+ self.assertEquals('Tester', board0['vendor'])
+ self.assertEquals('ARM Board 0', board0['board'])
+ self.assertEquals('config0', board0['config'])
+ self.assertEquals('board0', board0['target'])
+
+ self.assertEquals('powerpc', board2['arch'])
+ self.assertEquals('ppc', board2['cpu'])
+ self.assertEquals('mpc85xx', board2['soc'])
+ self.assertEquals('Tester', board2['vendor'])
+ self.assertEquals('PowerPC board 1', board2['board'])
+ self.assertEquals('config2', board2['config'])
+ self.assertEquals('board2', board2['target'])
+
+ def test_output_is_new(self):
+ """Test detecting new changes to Kconfig"""
+ base = self._base_dir
+ src = self._git_dir
+ config_dir = os.path.join(src, 'configs')
+ delay = 0.02
+
+ # Create a boards.cfg file
+ boards_cfg = os.path.join(base, 'boards.cfg')
+ content = b'''#
+# List of boards
+# Automatically generated by buildman/boards.py: don't edit
+#
+# Status, Arch, CPU, SoC, Vendor, Board, Target, Config, Maintainers
+
+Active aarch64 armv8 - armltd corstone1000 board0
+Active aarch64 armv8 - armltd total_compute board2
+'''
+ # Check missing file
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Check that the board.cfg file is newer
+ time.sleep(delay)
+ tools.write_file(boards_cfg, content)
+ self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Touch the Kconfig files after a show delay to avoid a race
+ time.sleep(delay)
+ Path(os.path.join(src, 'Kconfig')).touch()
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+ Path(boards_cfg).touch()
+ self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Touch a different Kconfig file
+ time.sleep(delay)
+ Path(os.path.join(src, 'Kconfig.something')).touch()
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+ Path(boards_cfg).touch()
+ self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Touch a MAINTAINERS file
+ time.sleep(delay)
+ Path(os.path.join(src, 'MAINTAINERS')).touch()
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+
+ Path(boards_cfg).touch()
+ self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Touch a defconfig file
+ time.sleep(delay)
+ Path(os.path.join(config_dir, 'board0_defconfig')).touch()
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+ Path(boards_cfg).touch()
+ self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src))
+
+ # Remove a board and check that the board.cfg file is now older
+ Path(os.path.join(config_dir, 'board0_defconfig')).unlink()
+ self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src))
+
+ def test_maintainers(self):
+ """Test detecting boards without a MAINTAINERS entry"""
+ src = self._git_dir
+ main = os.path.join(src, 'boards', 'board0', 'MAINTAINERS')
+ other = os.path.join(src, 'boards', 'board2', 'MAINTAINERS')
+ kc_file = os.path.join(src, 'Kconfig')
+ config_dir = os.path.join(src, 'configs')
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+
+ # There should be two boards no warnings
+ self.assertEquals(2, len(params_list))
+ self.assertFalse(warnings)
+
+ # Set an invalid status line in the file
+ orig_data = tools.read_file(main, binary=False)
+ lines = ['S: Other\n' if line.startswith('S:') else line
+ for line in orig_data.splitlines(keepends=True)]
+ tools.write_file(main, ''.join(lines), binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ params = params_list[0]
+ if params['target'] == 'board2':
+ params = params_list[1]
+ self.assertEquals('-', params['status'])
+ self.assertEquals(["WARNING: Other: unknown status for 'board0'"],
+ warnings)
+
+ # Remove the status line (S:) from a file
+ lines = [line for line in orig_data.splitlines(keepends=True)
+ if not line.startswith('S:')]
+ tools.write_file(main, ''.join(lines), binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(["WARNING: -: unknown status for 'board0'"], warnings)
+
+ # Remove the configs/ line (F:) from a file - this is the last line
+ data = ''.join(orig_data.splitlines(keepends=True)[:-1])
+ tools.write_file(main, data, binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(
+ ["WARNING: no maintainers for 'board0'",
+ 'WARNING: orphaned defconfig in boards/board0/MAINTAINERS ending at line 4',
+ ], warnings)
+
+ # Mark a board as orphaned - this should give a warning
+ lines = ['S: Orphaned' if line.startswith('S') else line
+ for line in orig_data.splitlines(keepends=True)]
+ tools.write_file(main, ''.join(lines), binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(["WARNING: no maintainers for 'board0'"], warnings)
+
+ # Change the maintainer to '-' - this should give a warning
+ lines = ['M: -' if line.startswith('M') else line
+ for line in orig_data.splitlines(keepends=True)]
+ tools.write_file(main, ''.join(lines), binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(["WARNING: -: unknown status for 'board0'"], warnings)
+
+ # Remove the maintainer line (M:) from a file
+ lines = [line for line in orig_data.splitlines(keepends=True)
+ if not line.startswith('M:')]
+ tools.write_file(main, ''.join(lines), binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(["WARNING: no maintainers for 'board0'"], warnings)
+
+ # Move the contents of the second file into this one, removing the
+ # second file, to check multiple records in a single file.
+ both_data = orig_data + tools.read_file(other, binary=False)
+ tools.write_file(main, both_data, binary=False)
+ os.remove(other)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertFalse(warnings)
+
+ # Add another record, this should be ignored with a warning
+ extra = '\n\nAnother\nM: Fred\nF: configs/board9_defconfig\nS: other\n'
+ tools.write_file(main, both_data + extra, binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(
+ ['WARNING: orphaned defconfig in boards/board0/MAINTAINERS ending at line 16'],
+ warnings)
+
+ # Add another TARGET to the Kconfig
+ tools.write_file(main, both_data, binary=False)
+ orig_kc_data = tools.read_file(kc_file)
+ extra = (b'''
+if TARGET_BOARD2
+config TARGET_OTHER
+\tbool "other"
+\tdefault y
+endif
+''')
+ tools.write_file(kc_file, orig_kc_data + extra)
+ params_list, warnings = self._boards.build_board_list(config_dir, src,
+ warn_targets=True)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(
+ ['WARNING: board2_defconfig: Duplicate TARGET_xxx: board2 and other'],
+ warnings)
+
+ # Remove the TARGET_BOARD0 Kconfig option
+ lines = [b'' if line == b'config TARGET_BOARD2\n' else line
+ for line in orig_kc_data.splitlines(keepends=True)]
+ tools.write_file(kc_file, b''.join(lines))
+ params_list, warnings = self._boards.build_board_list(config_dir, src,
+ warn_targets=True)
+ self.assertEquals(2, len(params_list))
+ self.assertEquals(
+ ['WARNING: board2_defconfig: No TARGET_BOARD2 enabled'],
+ warnings)
+ tools.write_file(kc_file, orig_kc_data)
+
+ # Replace the last F: line of board 2 with an N: line
+ data = ''.join(both_data.splitlines(keepends=True)[:-1])
+ tools.write_file(main, data + 'N: oa.*2\n', binary=False)
+ params_list, warnings = self._boards.build_board_list(config_dir, src)
+ self.assertEquals(2, len(params_list))
+ self.assertFalse(warnings)
+
+ def testRegenBoards(self):
+ """Test that we can regenerate the boards.cfg file"""
+ outfile = os.path.join(self._output_dir, 'test-boards.cfg')
+ if os.path.exists(outfile):
+ os.remove(outfile)
+ with test_util.capture_sys_output() as (stdout, stderr):
+ result = self._RunControl('-R', outfile, brds=None,
+ get_builder=False)
+ self.assertTrue(os.path.exists(outfile))
+
+ def test_print_prefix(self):
+ """Test that we can print the toolchain prefix"""
+ with test_util.capture_sys_output() as (stdout, stderr):
+ result = self._RunControl('-A', 'board0')
+ self.assertEqual('arm-\n', stdout.getvalue())
+ self.assertEqual('', stderr.getvalue())
+
+ def test_exclude_one(self):
+ """Test excluding a single board from an arch"""
+ self._RunControl('arm', '-x', 'board1', '-o', self._output_dir)
+ self.assertEqual(['board0'],
+ [b.target for b in self._boards.get_selected()])
+
+ def test_exclude_arch(self):
+ """Test excluding an arch"""
+ self._RunControl('-x', 'arm', '-o', self._output_dir)
+ self.assertEqual(['board2', 'board4'],
+ [b.target for b in self._boards.get_selected()])
+
+ def test_exclude_comma(self):
+ """Test excluding a comma-separated list of things"""
+ self._RunControl('-x', 'arm,powerpc', '-o', self._output_dir)
+ self.assertEqual(['board4'],
+ [b.target for b in self._boards.get_selected()])
+
+ def test_exclude_list(self):
+ """Test excluding a list of things"""
+ self._RunControl('-x', 'board2', '-x' 'board4', '-o', self._output_dir)
+ self.assertEqual(['board0', 'board1'],
+ [b.target for b in self._boards.get_selected()])
+
+ def test_single_boards(self):
+ """Test building single boards"""
+ self._RunControl('--boards', 'board1', '-o', self._output_dir)
+ self.assertEqual(1, self._builder.count)
+
+ self._RunControl('--boards', 'board1', '--boards', 'board2',
+ '-o', self._output_dir)
+ self.assertEqual(2, self._builder.count)
+
+ self._RunControl('--boards', 'board1,board2', '--boards', 'board4',
+ '-o', self._output_dir)
+ self.assertEqual(3, self._builder.count)
+
+ def test_print_arch(self):
+ """Test that we can print the board architecture"""
+ with test_util.capture_sys_output() as (stdout, stderr):
+ result = self._RunControl('--print-arch', 'board0')
+ self.assertEqual('arm\n', stdout.getvalue())
+ self.assertEqual('', stderr.getvalue())