From patchwork Fri Jan 9 18:31:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1396 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983579; bh=Ae8CTiqIANEokl6wyOdEv4lQ2ii3K/mN1B6gVCJG7HY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=IcB+uLEgLp7ys1rvm7kbr8HRr6pZlpQGYbcdMZO+kO595B69Lr+csG6AKBjFv8M1k +J9hpxuCh0V1mf2hmAcwymOXT7DKFljEtEWn2EOL1dspdg22ke8Kw1smldei1HDMKx YNHx5ZeT0eDAIsWDSaEQys99Xm+HNb9CzoeAC439LpTpm6w1f6l58+EeBUlozmz8MA 629E5EK/FPos91Lk1X6uiqxOb1ZTp0hXsHXYtL+yQTf5T+IHQNkT1xWHtYXt+rzbY0 Fyba+WhpO39k8AfMPIyjr3VU0zMFpFzdQxvHH/9G04YfZF224LC5XQVBamEQTFbswU EZINZw8U25ewA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8988F69221 for ; Fri, 9 Jan 2026 11:32:59 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id BdU1vEGUPmSD for ; Fri, 9 Jan 2026 11:32:59 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983579; bh=Ae8CTiqIANEokl6wyOdEv4lQ2ii3K/mN1B6gVCJG7HY=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=IcB+uLEgLp7ys1rvm7kbr8HRr6pZlpQGYbcdMZO+kO595B69Lr+csG6AKBjFv8M1k +J9hpxuCh0V1mf2hmAcwymOXT7DKFljEtEWn2EOL1dspdg22ke8Kw1smldei1HDMKx YNHx5ZeT0eDAIsWDSaEQys99Xm+HNb9CzoeAC439LpTpm6w1f6l58+EeBUlozmz8MA 629E5EK/FPos91Lk1X6uiqxOb1ZTp0hXsHXYtL+yQTf5T+IHQNkT1xWHtYXt+rzbY0 Fyba+WhpO39k8AfMPIyjr3VU0zMFpFzdQxvHH/9G04YfZF224LC5XQVBamEQTFbswU EZINZw8U25ewA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 78DA169211 for ; Fri, 9 Jan 2026 11:32:59 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983577; bh=1vWzhXXau6V0ctk5C/q6Nu6Ut0eeAeVL2BAQviNgNZw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=n89HohdjiE3DkYwOpxijoYyvtXHt86W0eW9PsDmdz+voN833ROlkgvqQ+ifcd+qm2 rvwSmK0uN6iHD5unS0tuDlpfzRMAW8VJMVBBmDNP9qhqllxkDxcx3hfy63rt/OlG5n haGCFGuOLwapdif29l3K3pj4jz/qqsWBy4CAY68ffdr+ia5Z/cbf7Qx62bRsLM/GFJ GjiasfcJB1avkYp0nydwju6usPcByAcjru/qnpUhsHLO+IVXvonME/Xf59PoinvG6B 0yLtuF8vmylEAkI5HOgMB7ChMmvPt/WlWjMU/V/dbxLKxYx+SWDBsXEXLEJoNmfeaZ ddGVMavhKLZVw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B110B69211; Fri, 9 Jan 2026 11:32:57 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id oTXsFif3Ayqn; Fri, 9 Jan 2026 11:32:57 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983573; bh=83bUgx0F8vAAkFmh/bbOCYiM5emQmDEfj6JrYmLzrGg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=czmMyvSQ65CViF/3BOwlTvga4QEc5Z1b/ySQPWCGLIVJPqr/SBhForK+sPcqZoNu1 05CSF3Xd5SgiAJ99sotkpvAg1aQtzEqj7iW1CECoiyC1sWui7GZo3tO3uA1te99G2x EWkuClK3POHgUy9bYujrKdY255o9CPuVRChkmFjVaNMN/uDFzAdQsqBNxCibtmpOvD nwrDVdHuIph7BNC4aSi6fPrcYTGsGeL8E8Fmc+ZiIbeG7EgPC99X/2WwawSBgTE7Hx o4f5NTFtyHUX+AVTk69dgHFiAUpSMau4+PbkVNWlZLg2h0OSNQAPjR2EO0R9ADgioA JJWg4eaBa9Mfg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2988169209; Fri, 9 Jan 2026 11:32:53 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Fri, 9 Jan 2026 11:31:10 -0700 Message-ID: <20260109183116.3262115-19-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260109183116.3262115-1-sjg@u-boot.org> References: <20260109183116.3262115-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 3ONJYAZSLG3LUDT4KUZGZ2A7ENOGU7CU X-Message-ID-Hash: 3ONJYAZSLG3LUDT4KUZGZ2A7ENOGU7CU X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , "Claude Opus 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 18/18] buildman: Add unit tests for _print_build_summary() List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add unit tests for the _print_build_summary() method covering: - Count-only messages when duration is too short to show rate - Distinguishing previously-done builds from newly-built ones - Kconfig reconfiguration count display - Thread exception warnings - Duration and build-rate display for long builds - Rounding of duration when microseconds >= 500000 Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- tools/buildman/main.py | 1 + tools/buildman/test_builder.py | 131 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/tools/buildman/main.py b/tools/buildman/main.py index 044f7a32ebb..5831dbf1222 100755 --- a/tools/buildman/main.py +++ b/tools/buildman/main.py @@ -60,6 +60,7 @@ def run_tests(skip_net_tests, debug, verbose, args): test_builder.TestPrepareOutputSpace, test_builder.TestCheckOutputForLoop, test_builder.TestMake, + test_builder.TestPrintBuildSummary, 'buildman.toolchain']) return (0 if result.wasSuccessful() else 1) diff --git a/tools/buildman/test_builder.py b/tools/buildman/test_builder.py index 1ec371e7821..fd60767bca0 100644 --- a/tools/buildman/test_builder.py +++ b/tools/buildman/test_builder.py @@ -4,6 +4,7 @@ """Unit tests for builder.py""" +from datetime import datetime import os import shutil import unittest @@ -719,5 +720,135 @@ class TestMake(unittest.TestCase): self.assertFalse(self.builder._terminated) +class TestPrintBuildSummary(unittest.TestCase): + """Tests for Builder._print_build_summary()""" + + def setUp(self): + """Set up test fixtures""" + self.builder = builder.Builder( + toolchains=None, base_dir='/tmp/test', git_dir='/src/repo', + num_threads=4, num_jobs=1) + # Set a start time in the past (less than 1 second ago to avoid + # duration output) + self.builder._start_time = datetime.now() + self.builder.thread_exceptions = [] + terminal.set_print_test_mode() + + def tearDown(self): + """Clean up after tests""" + terminal.set_print_test_mode(False) + + def test_basic_count(self): + """Test basic completed message with just count""" + self.builder.count = 10 + self.builder.already_done = 0 + self.builder.kconfig_reconfig = 0 + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + # First line is blank, second is the message + self.assertEqual(len(lines), 2) + self.assertEqual(lines[0].text, '') + self.assertIn('Completed: 10 total built', lines[1].text) + self.assertNotIn('previously', lines[1].text) + + def test_all_previously_done(self): + """Test message when all builds were already done""" + self.builder.count = 5 + self.builder.already_done = 5 + self.builder.kconfig_reconfig = 0 + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + self.assertIn('5 previously', lines[1].text) + self.assertNotIn('newly', lines[1].text) + + def test_some_newly_built(self): + """Test message with some previously done and some new""" + self.builder.count = 10 + self.builder.already_done = 6 + self.builder.kconfig_reconfig = 0 + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + self.assertIn('6 previously', lines[1].text) + self.assertIn('4 newly', lines[1].text) + + def test_with_kconfig_reconfig(self): + """Test message with kconfig reconfigurations""" + self.builder.count = 8 + self.builder.already_done = 0 + self.builder.kconfig_reconfig = 3 + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + self.assertIn('3 reconfig', lines[1].text) + + def test_thread_exceptions(self): + """Test message with thread exceptions""" + self.builder.count = 5 + self.builder.already_done = 0 + self.builder.kconfig_reconfig = 0 + self.builder.thread_exceptions = [Exception('err1'), Exception('err2')] + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + self.assertEqual(len(lines), 3) + self.assertIn('Failed: 2 thread exceptions', lines[2].text) + + @mock.patch('buildman.builder.datetime') + def test_duration_and_rate(self, mock_datetime): + """Test message includes duration and rate for long builds""" + self.builder.count = 100 + self.builder.already_done = 0 + self.builder.kconfig_reconfig = 0 + + # Mock datetime to simulate a 10 second build + start_time = datetime(2024, 1, 1, 12, 0, 0) + end_time = datetime(2024, 1, 1, 12, 0, 10) + self.builder._start_time = start_time + mock_datetime.now.return_value = end_time + mock_datetime.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs) + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + self.assertIn('duration', lines[1].text) + self.assertIn('rate', lines[1].text) + self.assertIn('10.00', lines[1].text) # 100 boards / 10 seconds + + @mock.patch('buildman.builder.datetime') + def test_duration_rounds_up(self, mock_datetime): + """Test duration rounds up when microseconds >= 500000""" + self.builder.count = 100 + self.builder.already_done = 0 + self.builder.kconfig_reconfig = 0 + + # Mock datetime to simulate a 10.6 second build (should round to 11) + start_time = datetime(2024, 1, 1, 12, 0, 0) + end_time = datetime(2024, 1, 1, 12, 0, 10, 600000) # 10.6 seconds + self.builder._start_time = start_time + mock_datetime.now.return_value = end_time + mock_datetime.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs) + + terminal.get_print_test_lines() # Clear + self.builder._print_build_summary() + lines = terminal.get_print_test_lines() + + # Duration should be rounded up to 11 seconds + self.assertIn('0:00:11', lines[1].text) + + if __name__ == '__main__': unittest.main()