@@ -1251,37 +1251,8 @@ class Builder:
# Wait until we have processed all output
self.out_queue.join()
if not self._opts.ide:
- self._print_build_summary()
+ self._result_handler.print_build_summary(
+ self.count, self.already_done, self.kconfig_reconfig,
+ self._start_time, self.thread_exceptions)
return (self.fail, self.warned, self.thread_exceptions)
-
- def _print_build_summary(self):
- """Print a summary of the build results
-
- Show the number of boards built, how many were already done, duration
- and build rate. Also show any thread exceptions that occurred.
- """
- tprint()
-
- msg = f'Completed: {self.count} total built'
- if self.already_done or self.kconfig_reconfig:
- parts = []
- if self.already_done:
- parts.append(f'{self.already_done} previously')
- if self.already_done != self.count:
- parts.append(f'{self.count - self.already_done} newly')
- if self.kconfig_reconfig:
- parts.append(f'{self.kconfig_reconfig} reconfig')
- msg += ' (' + ', '.join(parts) + ')'
- duration = datetime.now() - self._start_time
- if duration > timedelta(microseconds=1000000):
- if duration.microseconds >= 500000:
- duration = duration + timedelta(seconds=1)
- duration -= timedelta(microseconds=duration.microseconds)
- rate = float(self.count) / duration.total_seconds()
- msg += f', duration {duration}, rate {rate:1.2f}'
- tprint(msg)
- if self.thread_exceptions:
- tprint(
- f'Failed: {len(self.thread_exceptions)} thread exceptions',
- colour=self.col.RED)
@@ -6,6 +6,7 @@
"""Result writer for buildman build results"""
+from datetime import datetime, timedelta
import sys
from buildman.outcome import (BoardStatus, ErrLine, Outcome,
@@ -185,6 +186,45 @@ class ResultHandler:
"""
return self._error_lines
+ def print_build_summary(self, count, already_done, kconfig_reconfig,
+ start_time, thread_exceptions):
+ """Print a summary of the build results
+
+ Show the number of boards built, how many were already done, duration
+ and build rate. Also show any thread exceptions that occurred.
+
+ Args:
+ count (int): Total number of builds
+ already_done (int): Number of builds already completed previously
+ kconfig_reconfig (int): Number of builds triggered by Kconfig changes
+ start_time (datetime): When the build started
+ thread_exceptions (list): List of thread exceptions that occurred
+ """
+ tprint()
+
+ msg = f'Completed: {count} total built'
+ if already_done or kconfig_reconfig:
+ parts = []
+ if already_done:
+ parts.append(f'{already_done} previously')
+ if already_done != count:
+ parts.append(f'{count - already_done} newly')
+ if kconfig_reconfig:
+ parts.append(f'{kconfig_reconfig} reconfig')
+ msg += ' (' + ', '.join(parts) + ')'
+ duration = datetime.now() - start_time
+ if duration > timedelta(microseconds=1000000):
+ if duration.microseconds >= 500000:
+ duration = duration + timedelta(seconds=1)
+ duration -= timedelta(microseconds=duration.microseconds)
+ rate = float(count) / duration.total_seconds()
+ msg += f', duration {duration}, rate {rate:1.2f}'
+ tprint(msg)
+ if thread_exceptions:
+ tprint(
+ f'Failed: {len(thread_exceptions)} thread exceptions',
+ colour=self._col.RED)
+
def colour_num(self, num):
"""Format a number with colour depending on its value
@@ -769,7 +769,7 @@ class TestMake(unittest.TestCase):
class TestPrintBuildSummary(unittest.TestCase):
- """Tests for Builder._print_build_summary()"""
+ """Tests for ResultHandler.print_build_summary()"""
def setUp(self):
"""Set up test fixtures"""
@@ -785,8 +785,7 @@ class TestPrintBuildSummary(unittest.TestCase):
result_handler=self.result_handler)
# 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 = []
+ self.start_time = datetime.now()
terminal.set_print_test_mode()
def tearDown(self):
@@ -795,12 +794,8 @@ class TestPrintBuildSummary(unittest.TestCase):
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()
+ self.result_handler.print_build_summary(10, 0, 0, self.start_time, [])
lines = terminal.get_print_test_lines()
# First line is blank, second is the message
@@ -811,12 +806,8 @@ class TestPrintBuildSummary(unittest.TestCase):
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()
+ self.result_handler.print_build_summary(5, 5, 0, self.start_time, [])
lines = terminal.get_print_test_lines()
self.assertIn('5 previously', lines[1].text)
@@ -824,12 +815,8 @@ class TestPrintBuildSummary(unittest.TestCase):
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()
+ self.result_handler.print_build_summary(10, 6, 0, self.start_time, [])
lines = terminal.get_print_test_lines()
self.assertIn('6 previously', lines[1].text)
@@ -837,68 +824,51 @@ class TestPrintBuildSummary(unittest.TestCase):
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()
+ self.result_handler.print_build_summary(8, 0, 3, self.start_time, [])
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')]
+ exceptions = [Exception('err1'), Exception('err2')]
terminal.get_print_test_lines() # Clear
- self.builder._print_build_summary()
+ self.result_handler.print_build_summary(5, 0, 0, self.start_time, exceptions)
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')
+ @mock.patch('buildman.resulthandler.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()
+ self.result_handler.print_build_summary(100, 0, 0, start_time, [])
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')
+ @mock.patch('buildman.resulthandler.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()
+ self.result_handler.print_build_summary(100, 0, 0, start_time, [])
lines = terminal.get_print_test_lines()
# Duration should be rounded up to 11 seconds