[Concept,12/18] buildman: Add unit tests for _prepare_output_space()

Message ID 20260109183116.3262115-13-sjg@u-boot.org
State New
Headers
Series buildman: Improve test coverage for builder.py |

Commit Message

Simon Glass Jan. 9, 2026, 6:31 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add TestPrepareOutputSpace class to test_builder.py with tests:
- _get_output_space_removals() with no commits
- _get_output_space_removals() with no old directories
- _get_output_space_removals() identifying old directories by pattern
- _prepare_output_space() with nothing to remove
- _prepare_output_space() removing directories and printing message

The test verifies that the 'Removing' message is printed with
newline=False so it can be overwritten by subsequent output.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 tools/buildman/main.py         |  1 +
 tools/buildman/test_builder.py | 84 ++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+)
  

Patch

diff --git a/tools/buildman/main.py b/tools/buildman/main.py
index 74da226c2cf..449263b48e8 100755
--- a/tools/buildman/main.py
+++ b/tools/buildman/main.py
@@ -56,6 +56,7 @@  def run_tests(skip_net_tests, debug, verbose, args):
          test_builder.TestPrintFuncSizeDetail,
          test_builder.TestPrepareThread,
          test_builder.TestPrepareWorkingSpace,
+         test_builder.TestPrepareOutputSpace,
          'buildman.toolchain'])
 
     return (0 if result.wasSuccessful() else 1)
diff --git a/tools/buildman/test_builder.py b/tools/buildman/test_builder.py
index d7359477ef2..b17d7942e1d 100644
--- a/tools/buildman/test_builder.py
+++ b/tools/buildman/test_builder.py
@@ -5,6 +5,7 @@ 
 """Unit tests for builder.py"""
 
 import os
+import shutil
 import unittest
 from unittest import mock
 
@@ -338,5 +339,88 @@  class TestPrepareWorkingSpace(unittest.TestCase):
         mock_prepare_thread.assert_any_call(1, True)
 
 
+class TestPrepareOutputSpace(unittest.TestCase):
+    """Tests for Builder._prepare_output_space() and _get_output_space_removals()"""
+
+    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)
+
+    def test_get_removals_no_commits(self):
+        """Test _get_output_space_removals with no commits"""
+        self.builder.commits = None
+        result = self.builder._get_output_space_removals()
+        self.assertEqual(result, [])
+
+    @mock.patch.object(builder.Builder, 'get_output_dir')
+    @mock.patch('glob.glob')
+    def test_get_removals_no_old_dirs(self, mock_glob, mock_get_output_dir):
+        """Test _get_output_space_removals with no old directories"""
+        self.builder.commits = [mock.Mock()]  # Non-empty to trigger logic
+        self.builder.commit_count = 1
+        mock_get_output_dir.return_value = '/tmp/test/01_gabcdef1_test'
+        mock_glob.return_value = []
+
+        result = self.builder._get_output_space_removals()
+        self.assertEqual(result, [])
+
+    @mock.patch.object(builder.Builder, 'get_output_dir')
+    @mock.patch('glob.glob')
+    def test_get_removals_with_old_dirs(self, mock_glob, mock_get_output_dir):
+        """Test _get_output_space_removals identifies old directories"""
+        self.builder.commits = [mock.Mock()]  # Non-empty to trigger logic
+        self.builder.commit_count = 1
+        mock_get_output_dir.return_value = '/tmp/test/01_gabcdef1_current'
+        # Simulate old directories with buildman naming pattern
+        mock_glob.return_value = [
+            '/tmp/test/01_gabcdef1_current',  # Current - should not remove
+            '/tmp/test/02_g1234567_old',      # Old - should remove
+            '/tmp/test/random_dir',           # Not matching pattern - keep
+        ]
+
+        result = self.builder._get_output_space_removals()
+        self.assertEqual(result, ['/tmp/test/02_g1234567_old'])
+
+    @mock.patch.object(builder.Builder, '_get_output_space_removals')
+    def test_prepare_output_space_nothing_to_remove(self, mock_get_removals):
+        """Test _prepare_output_space with nothing to remove"""
+        mock_get_removals.return_value = []
+        terminal.get_print_test_lines()  # Clear
+
+        self.builder._prepare_output_space()
+
+        lines = terminal.get_print_test_lines()
+        self.assertEqual(len(lines), 0)
+
+    @mock.patch.object(shutil, 'rmtree')
+    @mock.patch.object(builder.Builder, '_get_output_space_removals')
+    def test_prepare_output_space_removes_dirs(self, mock_get_removals,
+                                               mock_rmtree):
+        """Test _prepare_output_space removes old directories"""
+        mock_get_removals.return_value = ['/tmp/test/old1', '/tmp/test/old2']
+        terminal.get_print_test_lines()  # Clear
+
+        self.builder._prepare_output_space()
+
+        # Check rmtree was called for each directory
+        self.assertEqual(mock_rmtree.call_count, 2)
+        mock_rmtree.assert_any_call('/tmp/test/old1')
+        mock_rmtree.assert_any_call('/tmp/test/old2')
+
+        # Check 'Removing' message was printed
+        lines = terminal.get_print_test_lines()
+        self.assertEqual(len(lines), 1)
+        self.assertIn('Removing 2 old build directories', lines[0].text)
+        # Check newline=False was used (message should be overwritten)
+        self.assertFalse(lines[0].newline)
+
+
 if __name__ == '__main__':
     unittest.main()