mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-22 02:03:01 +02:00
Additional tests and docstrings.
This commit is contained in:
@@ -9,6 +9,11 @@ import borgmatic.logger
|
||||
|
||||
|
||||
class Rich_color_formatter(logging.Formatter):
|
||||
'''
|
||||
A Python logging formatter that formats log records with Rich-compatible color markup according
|
||||
to their levels.
|
||||
'''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.prefix = None
|
||||
super().__init__(
|
||||
@@ -19,6 +24,10 @@ class Rich_color_formatter(logging.Formatter):
|
||||
)
|
||||
|
||||
def format(self, record):
|
||||
'''
|
||||
Given a log record, format it with Rich-compatibe color markup correponding to its log
|
||||
level.
|
||||
'''
|
||||
borgmatic.logger.add_custom_log_levels()
|
||||
|
||||
color = {
|
||||
@@ -35,12 +44,23 @@ class Rich_color_formatter(logging.Formatter):
|
||||
|
||||
|
||||
class Browse_log_handler(logging.Handler):
|
||||
'''
|
||||
A Python log handler that writes any log records to a logging widget.
|
||||
'''
|
||||
|
||||
def __init__(self, logs_widget):
|
||||
'''
|
||||
Given a logs widget, save it for use below.
|
||||
'''
|
||||
self.logs_widget = logs_widget
|
||||
|
||||
super().__init__()
|
||||
|
||||
def emit(self, record):
|
||||
'''
|
||||
Given a log record, format it and log it to the logs widgets. This works whether or not the
|
||||
logging is happening in the main thread.
|
||||
'''
|
||||
message = self.format(record)
|
||||
|
||||
try:
|
||||
@@ -72,6 +92,11 @@ def log_to_widget(logs_widget):
|
||||
|
||||
|
||||
class Logs(textual.widgets.RichLog):
|
||||
'''
|
||||
A widget for viewing borgmatic logs in realtime. The log level is determined by borgmatic's
|
||||
current verbosity level.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(markup=True, id='logs', classes='panel')
|
||||
self.border_title = '🪵 logs'
|
||||
|
||||
@@ -200,7 +200,9 @@ def test_directory_list_on_mount_with_non_root_directory_adds_archive_paths():
|
||||
).and_return(flexmock())
|
||||
flexmock(module).should_receive('add_archive_paths').once()
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
@@ -216,7 +218,9 @@ def test_on_archive_path_loaded_with_loading_done_signal_removes_loading_indicat
|
||||
).and_return(flexmock())
|
||||
flexmock(module).should_receive('add_archive_paths').never()
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
@@ -233,7 +237,9 @@ def test_on_archive_path_loaded_with_path_loaded_signal_adds_archive_path():
|
||||
).and_return(flexmock())
|
||||
flexmock(module).should_receive('add_archive_paths').once()
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
@@ -248,7 +254,9 @@ def test_directory_list_on_option_list_option_highlighted_with_highlighted_none_
|
||||
'app'
|
||||
).and_return(flexmock())
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
@@ -265,7 +273,9 @@ def test_directory_list_on_option_list_option_highlighted_with_highlighted_zero_
|
||||
'app'
|
||||
).and_return(flexmock())
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
@@ -283,7 +293,9 @@ def test_directory_list_on_option_list_option_highlighted_with_highlighted_non_z
|
||||
'app'
|
||||
).and_return(flexmock())
|
||||
directory_list = module.Directory_list(
|
||||
config=flexmock(), repository=flexmock(), archive_name='archive',
|
||||
config=flexmock(),
|
||||
repository=flexmock(),
|
||||
archive_name='archive',
|
||||
path_loaded=flexmock(complete=False, path_hierarchy={'etc': {}}),
|
||||
path_components=('etc',),
|
||||
)
|
||||
|
||||
@@ -8,9 +8,7 @@ import textual.widgets
|
||||
|
||||
def test_update_inline_loading_indicator_with_option_list_adds_a_dot():
|
||||
widget = textual.widgets.OptionList()
|
||||
widget.add_option(
|
||||
textual.widgets.option_list.Option('HOLD.', id='loading-indicator')
|
||||
)
|
||||
widget.add_option(textual.widgets.option_list.Option('HOLD.', id='loading-indicator'))
|
||||
|
||||
module.update_inline_loading_indicator(widget)
|
||||
|
||||
@@ -20,9 +18,7 @@ def test_update_inline_loading_indicator_with_option_list_adds_a_dot():
|
||||
|
||||
def test_update_inline_loading_indicator_with_option_list_wraps_dots_beyond_three():
|
||||
widget = textual.widgets.OptionList()
|
||||
widget.add_option(
|
||||
textual.widgets.option_list.Option('HOLD...', id='loading-indicator')
|
||||
)
|
||||
widget.add_option(textual.widgets.option_list.Option('HOLD...', id='loading-indicator'))
|
||||
|
||||
module.update_inline_loading_indicator(widget)
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import contextlib
|
||||
|
||||
from borgmatic.actions.browse import logs as module
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
|
||||
def test_log_to_widget_adds_our_handler_and_removes_default_handler():
|
||||
default_handler = module.borgmatic.logger.Multi_stream_handler({})
|
||||
root_logger = module.logging.getLogger()
|
||||
root_logger.addHandler(default_handler)
|
||||
browse_log_handler = None
|
||||
|
||||
try:
|
||||
module.log_to_widget(flexmock())
|
||||
|
||||
with contextlib.suppress(StopIteration):
|
||||
browse_log_handler = next(
|
||||
handler for handler in root_logger.handlers
|
||||
if isinstance(handler, module.Browse_log_handler)
|
||||
)
|
||||
|
||||
assert browse_log_handler
|
||||
finally:
|
||||
if browse_log_handler:
|
||||
root_logger.removeHandler(browse_log_handler)
|
||||
|
||||
assert not any(
|
||||
handler for handler in root_logger.handlers
|
||||
if isinstance(handler, module.borgmatic.logger.Multi_stream_handler)
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_logs_does_not_raise():
|
||||
module.Logs()
|
||||
@@ -0,0 +1,87 @@
|
||||
from borgmatic.actions.browse import logs as module
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
|
||||
def test_rich_color_formatter_format_colors_log_record_based_on_level():
|
||||
formatted = module.Rich_color_formatter().format(
|
||||
module.logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=module.logging.ERROR,
|
||||
levelname='ERROR',
|
||||
msg='oh no',
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
assert formatted == '[bright_red]oh no[/bright_red]'
|
||||
|
||||
|
||||
def test_rich_color_formatter_format_includes_prefix():
|
||||
formatter = module.Rich_color_formatter()
|
||||
formatter.prefix = 'sup'
|
||||
formatted = formatter.format(
|
||||
module.logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=module.logging.ERROR,
|
||||
levelname='ERROR',
|
||||
msg='oh no',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assert formatted == '[bright_red]sup: oh no[/bright_red]'
|
||||
|
||||
|
||||
def test_browse_log_handler_emit_from_worker_thread_calls_write_in_main_thread():
|
||||
flexmock(module.textual.worker).should_receive('get_current_worker')
|
||||
logs_widget = flexmock(write=lambda message: None, app=flexmock())
|
||||
flexmock(logs_widget.app).should_receive('call_from_thread').with_args(
|
||||
logs_widget.write, 'hi'
|
||||
).once()
|
||||
|
||||
module.Browse_log_handler(logs_widget).emit(
|
||||
module.logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=module.logging.DEBUG,
|
||||
levelname='DEBUG',
|
||||
msg='hi',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_browse_log_handler_emit_from_main_thread_calls_write_directly():
|
||||
flexmock(module.textual.worker).should_receive('get_current_worker').and_raise(RuntimeError)
|
||||
logs_widget = flexmock(write=lambda message: None, app=flexmock())
|
||||
flexmock(logs_widget.app).should_receive('call_from_thread').never()
|
||||
flexmock(logs_widget).should_receive('write').with_args('hi').once()
|
||||
|
||||
module.Browse_log_handler(logs_widget).emit(
|
||||
module.logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=module.logging.DEBUG,
|
||||
levelname='DEBUG',
|
||||
msg='hi',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_browse_log_handler_emit_from_main_thread_with_no_active_app_does_not_raise():
|
||||
flexmock(module.textual.worker).should_receive('get_current_worker').and_raise(RuntimeError)
|
||||
logs_widget = flexmock(write=lambda message: None, app=flexmock())
|
||||
flexmock(logs_widget.app).should_receive('call_from_thread').never()
|
||||
flexmock(logs_widget).should_receive('write').with_args('hi').and_raise(
|
||||
module.textual._context.NoActiveAppError
|
||||
)
|
||||
|
||||
module.Browse_log_handler(logs_widget).emit(
|
||||
module.logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=module.logging.DEBUG,
|
||||
levelname='DEBUG',
|
||||
msg='hi',
|
||||
),
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user