From patchwork Fri Jan 9 18:31:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1388 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=1767983541; bh=Y9vJTmSvl4Hr/dVOyfSUDoCi67YEwL/hCIbXA+guU7Y=; 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=qnUNJZ+QOuy0zSDUlH0mdvvp9bijvLmdlPJ5lQRetbOAIOJfYh3krFH8A1jEh1Ma3 GTI4NgYpgrDRQuXEdPT+L0qHAr2kUvh++c5pfAKl0ZW2YmJrA+yMkpQWrDuJVHekVl Vf6KHCnRf55QT0gdi8gdawxCsgVPz0Lyim4vHywEDA4em6jWF8TWXfsA5sCe41GovK YzQc872BeKy6zD5Y4DuJmQ3AnCeDB9ms/2HnHggaaRJzF/5R1rIZlbSUW+ZtJcPD6e EPaUaM/qMHtOC16wqTJeoPCnPPx5SZNyMfmB0ViAdNTS2zZVd+w93LRZ3OjXjftXI6 VWkwBhCopFThw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A012969221 for ; Fri, 9 Jan 2026 11:32:21 -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 5nL8IT_-xBIl for ; Fri, 9 Jan 2026 11:32:21 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983541; bh=Y9vJTmSvl4Hr/dVOyfSUDoCi67YEwL/hCIbXA+guU7Y=; 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=qnUNJZ+QOuy0zSDUlH0mdvvp9bijvLmdlPJ5lQRetbOAIOJfYh3krFH8A1jEh1Ma3 GTI4NgYpgrDRQuXEdPT+L0qHAr2kUvh++c5pfAKl0ZW2YmJrA+yMkpQWrDuJVHekVl Vf6KHCnRf55QT0gdi8gdawxCsgVPz0Lyim4vHywEDA4em6jWF8TWXfsA5sCe41GovK YzQc872BeKy6zD5Y4DuJmQ3AnCeDB9ms/2HnHggaaRJzF/5R1rIZlbSUW+ZtJcPD6e EPaUaM/qMHtOC16wqTJeoPCnPPx5SZNyMfmB0ViAdNTS2zZVd+w93LRZ3OjXjftXI6 VWkwBhCopFThw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8C66169216 for ; Fri, 9 Jan 2026 11:32:21 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983539; bh=fPdyV2PpX9+XgmjnC6YX3BzL2hxOgTLHlqGrpCmi9EQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=to+GsUalmLuRMi+OP4rODix9bPtcLpX9ybLa+A023Hr9TsBNk4RkaAlRupbY/KT5o naaeFC6cwxTwhLv3a6/JNIz7he6av3Hu9u8YkQCODeqau0kGKiIql0XJuWrPXYkz+J ozdvPoTVLogB9UxhvZRxlSKRF+GLxSsf3oY1HBI8VCRiAdxYYQMDQUvzlnf2GKG+qb hjJyIDDYRVwjSs4hlEF8W/D81yTm3M3up8xBcP9qt+EobLjJxp8HMCZlIGTk5AmBt0 HPUGomi4oOD2Zg7MJ1w1LUim35+j+r2SlJii8k4R/E165r8be4VFLWNYrofI77TKFr rbhUa/x2uasng== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 835DD69211; Fri, 9 Jan 2026 11:32:19 -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 x1iqFU0gkzwV; Fri, 9 Jan 2026 11:32:19 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1767983535; bh=tPw46yFAfsuJJwXBHv37py0C+MKT0L7GDj2w5mRIfxU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o20kMOuXXkxLgO0xU/y9gJokvxeW2gLdDyGD8DWZR/mKLRp3be6nWl4iOhTJ3aoND wXQM4Z7HClX10pP6xx3kMrgjY27voEqNy0z9sOq+/GHrkJJFC3YRZV0qIoEpO18z8q tKtDorwFItjypreLBWaoJi6/XNBf2d8c11dPxqWmWzThC9flWrUySaxg1T2fxNou9c zz51veGuBlSzZaLQ3St95pHy0Uu3+nvSlevEbmF7wh3SaaGbutkynyNbqUu+MywY0U 6JTmNPZrwuujpe0tBQaVQBg2V1JMc2fYSVLYo+SlQpSMxtyNUkJvZDGhYmtM7QSxUf 5bX33wajzgp4w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id F162069209; Fri, 9 Jan 2026 11:32:14 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Fri, 9 Jan 2026 11:31:02 -0700 Message-ID: <20260109183116.3262115-11-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: ENERRQ3RYUDAHRTIIM37Q3BMBP3VG3KT X-Message-ID-Hash: ENERRQ3RYUDAHRTIIM37Q3BMBP3VG3KT 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 10/18] buildman: Add unit tests for _prepare_thread() 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 TestPrepareThread class to test_builder.py with tests covering: - No git setup (setup_git=None) - Existing clone (fetches updates) - Existing worktree (no action needed) - Invalid git_dir (neither file nor directory) - Creating new worktree - Creating new clone - Clone with setup_git=True - Invalid setup_git value Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- tools/buildman/main.py | 1 + tools/buildman/test_builder.py | 118 +++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/tools/buildman/main.py b/tools/buildman/main.py index 70547b2991d..6e780b9e07e 100755 --- a/tools/buildman/main.py +++ b/tools/buildman/main.py @@ -54,6 +54,7 @@ def run_tests(skip_net_tests, debug, verbose, args): func_test.TestFunctional, test_boards.TestBoards, test_bsettings.TestBsettings, test_builder.TestPrintFuncSizeDetail, + test_builder.TestPrepareThread, 'buildman.toolchain']) return (0 if result.wasSuccessful() else 1) diff --git a/tools/buildman/test_builder.py b/tools/buildman/test_builder.py index 5d003de1966..042dacc2f34 100644 --- a/tools/buildman/test_builder.py +++ b/tools/buildman/test_builder.py @@ -4,9 +4,13 @@ """Unit tests for builder.py""" +import os import unittest +from unittest import mock from buildman import builder +from buildman import builderthread +from u_boot_pylib import gitutil from u_boot_pylib import terminal @@ -140,5 +144,119 @@ class TestPrintFuncSizeDetail(unittest.TestCase): self.assertEqual(len(lines), 0) +class TestPrepareThread(unittest.TestCase): + """Tests for Builder._prepare_thread()""" + + 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) + terminal.set_print_test_mode() + + def tearDown(self): + """Clean up after tests""" + terminal.set_print_test_mode(False) + + @mock.patch.object(builderthread, 'mkdir') + def test_no_setup_git(self, mock_mkdir): + """Test with setup_git=None (no git setup needed)""" + self.builder._prepare_thread(0, None) + mock_mkdir.assert_called_once() + + @mock.patch.object(gitutil, 'fetch') + @mock.patch.object(os.path, 'isdir', return_value=True) + @mock.patch.object(builderthread, 'mkdir') + def test_existing_clone(self, mock_mkdir, mock_isdir, mock_fetch): + """Test with existing git clone (fetches updates)""" + terminal.get_print_test_lines() # Clear + self.builder._prepare_thread(0, 'clone') + + mock_fetch.assert_called_once() + lines = terminal.get_print_test_lines() + self.assertEqual(len(lines), 1) + self.assertIn('Fetching repo', lines[0].text) + + @mock.patch.object(os.path, 'isfile', return_value=True) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_existing_worktree(self, mock_mkdir, mock_isdir, mock_isfile): + """Test with existing worktree (no action needed)""" + terminal.get_print_test_lines() # Clear + self.builder._prepare_thread(0, 'worktree') + + # No git operations should be called + lines = terminal.get_print_test_lines() + self.assertEqual(len(lines), 0) + + @mock.patch.object(os.path, 'exists', return_value=True) + @mock.patch.object(os.path, 'isfile', return_value=False) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_invalid_git_dir(self, mock_mkdir, mock_isdir, mock_isfile, + mock_exists): + """Test with git_dir that exists but is neither file nor directory""" + with self.assertRaises(ValueError) as ctx: + self.builder._prepare_thread(0, 'clone') + self.assertIn('exists, but is not a file or a directory', + str(ctx.exception)) + + @mock.patch.object(gitutil, 'add_worktree') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(os.path, 'isfile', return_value=False) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_create_worktree(self, mock_mkdir, mock_isdir, mock_isfile, + mock_exists, mock_add_worktree): + """Test creating a new worktree""" + terminal.get_print_test_lines() # Clear + self.builder._prepare_thread(0, 'worktree') + + mock_add_worktree.assert_called_once() + lines = terminal.get_print_test_lines() + self.assertEqual(len(lines), 1) + self.assertIn('Checking out worktree', lines[0].text) + + @mock.patch.object(gitutil, 'clone') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(os.path, 'isfile', return_value=False) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_create_clone(self, mock_mkdir, mock_isdir, mock_isfile, + mock_exists, mock_clone): + """Test creating a new clone""" + terminal.get_print_test_lines() # Clear + self.builder._prepare_thread(0, 'clone') + + mock_clone.assert_called_once() + lines = terminal.get_print_test_lines() + self.assertEqual(len(lines), 1) + self.assertIn('Cloning repo', lines[0].text) + + @mock.patch.object(gitutil, 'clone') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(os.path, 'isfile', return_value=False) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_create_clone_with_true(self, mock_mkdir, mock_isdir, mock_isfile, + mock_exists, mock_clone): + """Test creating a clone when setup_git=True""" + terminal.get_print_test_lines() # Clear + self.builder._prepare_thread(0, True) + + mock_clone.assert_called_once() + + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(os.path, 'isfile', return_value=False) + @mock.patch.object(os.path, 'isdir', return_value=False) + @mock.patch.object(builderthread, 'mkdir') + def test_invalid_setup_git(self, mock_mkdir, mock_isdir, mock_isfile, + mock_exists): + """Test with invalid setup_git value""" + with self.assertRaises(ValueError) as ctx: + self.builder._prepare_thread(0, 'invalid') + self.assertIn("Can't setup git repo", str(ctx.exception)) + + if __name__ == '__main__': unittest.main()