Skip to content

Commit 7bc0727

Browse files
committed
test(flight_controller_info): raise abstraction and coverage level
1 parent 0322097 commit 7bc0727

File tree

1 file changed

+206
-58
lines changed

1 file changed

+206
-58
lines changed

tests/test_frontend_tkinter_flightcontroller_info.py

Lines changed: 206 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
SPDX-License-Identifier: GPL-3.0-or-later
1111
"""
1212

13+
import tkinter as tk
1314
from unittest.mock import Mock, patch
1415

1516
import pytest
@@ -907,15 +908,15 @@ def mock_successful_download(callback) -> ParDict:
907908
for name in expected_defaults:
908909
assert name in result
909910

910-
def test_user_receives_none_when_no_parameters_downloaded(
911+
def test_user_receives_empty_parameter_defaults_when_no_parameters_downloaded(
911912
self, mock_tkinter_context, configured_flight_controller: Mock
912913
) -> None:
913914
"""
914-
User receives None when no parameters have been downloaded.
915+
User receives empty parameter defaults when no parameters have been downloaded.
915916
916917
GIVEN: A window that has not completed parameter download
917918
WHEN: The user requests parameter default values
918-
THEN: The method should return None
919+
THEN: An empty but valid parameter dictionary is returned
919920
AND: No errors should occur
920921
"""
921922
# Given
@@ -936,61 +937,6 @@ def test_user_receives_none_when_no_parameters_downloaded(
936937
assert isinstance(result, ParDict)
937938
assert len(result) == 0
938939

939-
def test_user_sees_accurate_progress_tracking_during_parameter_download(
940-
self, mock_tkinter_context, configured_flight_controller: Mock
941-
) -> None:
942-
"""
943-
User sees accurate progress tracking during parameter download.
944-
945-
GIVEN: A flight controller with a known number of parameters
946-
WHEN: The user starts parameter download
947-
THEN: Progress should start at 0%
948-
AND: Progress should increment accurately with each parameter
949-
AND: Progress should reach 100% upon completion
950-
"""
951-
# Given
952-
stack, patches = mock_tkinter_context()
953-
total_parameters = 10
954-
progress_history = []
955-
956-
def mock_tracked_download(callback) -> ParDict:
957-
for current in range(1, total_parameters + 1):
958-
if callback:
959-
callback(current, total_parameters)
960-
progress_history.append((current, total_parameters))
961-
# Return a ParDict with test parameters
962-
result = ParDict()
963-
for i in range(total_parameters):
964-
result[f"PARAM_{i}"] = Par(float(i), f"Test param {i}")
965-
return result
966-
967-
with stack:
968-
for patch_obj in patches:
969-
stack.enter_context(patch_obj)
970-
971-
window = FlightControllerInfoWindow.__new__(FlightControllerInfoWindow)
972-
window.presenter = FlightControllerInfoPresenter(configured_flight_controller)
973-
window.presenter.download_parameters = Mock(side_effect=mock_tracked_download)
974-
window.root = Mock()
975-
window.progress_bar = Mock()
976-
window.progress_label = Mock()
977-
window.progress_frame = Mock()
978-
979-
# Mock the update_progress_bar method to track calls
980-
progress_calls = []
981-
window.update_progress_bar = Mock(side_effect=lambda c, m: progress_calls.append((c, m)))
982-
983-
# When
984-
window._download_flight_controller_parameters()
985-
986-
# Then - The download method was called
987-
window.presenter.download_parameters.assert_called_once()
988-
989-
# And progress tracking would be accurate if callback was used
990-
# (This tests the structure is in place for progress reporting)
991-
assert hasattr(window, "update_progress_bar")
992-
assert callable(window.update_progress_bar)
993-
994940

995941
class TestFlightControllerErrorHandling:
996942
"""Test error handling scenarios for flight controller operations in BDD style."""
@@ -1075,3 +1021,205 @@ def test_user_sees_informative_display_when_flight_controller_info_unavailable(
10751021

10761022
# And no information rows are created for empty info
10771023
assert mock_entry.call_count == 0
1024+
1025+
1026+
class TestFlightControllerInfoWindowInitialization: # pylint: disable=too-few-public-methods
1027+
"""Test window initialization and setup behavior in BDD style."""
1028+
1029+
def test_user_experiences_proper_window_initialization_and_setup(
1030+
self, mock_tkinter_context, configured_flight_controller: Mock
1031+
) -> None:
1032+
"""
1033+
Test that users experience proper window initialization and setup.
1034+
1035+
GIVEN: A flight controller is available and UI environment is ready
1036+
WHEN: A user creates a FlightControllerInfoWindow
1037+
THEN: The window is properly initialized with correct title, geometry, and components
1038+
AND: Flight controller information is logged
1039+
AND: Parameter download is scheduled
1040+
"""
1041+
# Given
1042+
stack, patches = mock_tkinter_context()
1043+
1044+
with stack:
1045+
# Start all patches for UI isolation
1046+
for patch_obj in patches:
1047+
stack.enter_context(patch_obj)
1048+
1049+
mock_root = Mock()
1050+
mock_mainloop = Mock()
1051+
1052+
with (
1053+
patch("tkinter.ttk.Frame"),
1054+
patch("tkinter.ttk.Progressbar"),
1055+
patch("tkinter.ttk.Label"),
1056+
patch.object(FlightControllerInfoWindow, "_create_info_display") as mock_create_display,
1057+
patch.object(FlightControllerInfoWindow, "_download_flight_controller_parameters") as mock_download,
1058+
patch("tkinter.Tk", return_value=mock_root) as mock_tk,
1059+
patch.object(mock_root, "mainloop", mock_mainloop),
1060+
patch.object(mock_root, "after") as mock_after,
1061+
):
1062+
# When
1063+
window = FlightControllerInfoWindow(configured_flight_controller)
1064+
1065+
# Then - Window is properly initialized
1066+
mock_tk.assert_called_once()
1067+
assert window.root == mock_root
1068+
assert isinstance(window.presenter, FlightControllerInfoPresenter)
1069+
assert window.presenter.flight_controller == configured_flight_controller
1070+
1071+
# And window properties are set correctly
1072+
mock_root.title.assert_called_once()
1073+
mock_root.geometry.assert_called_once_with("500x420")
1074+
1075+
# And UI components are created
1076+
mock_create_display.assert_called_once()
1077+
1078+
# And flight controller info is logged
1079+
configured_flight_controller.info.log_flight_controller_info.assert_called_once()
1080+
1081+
# And parameter download is scheduled
1082+
mock_after.assert_called_once_with(50, mock_download)
1083+
1084+
# And mainloop is called to start the UI
1085+
mock_mainloop.assert_called_once()
1086+
1087+
1088+
class TestFlightControllerInfoProgressBarErrorHandling:
1089+
"""Test progress bar error handling in BDD style."""
1090+
1091+
def test_user_sees_graceful_handling_when_progress_bar_widgets_are_destroyed(
1092+
self, mock_tkinter_context, configured_flight_controller: Mock
1093+
) -> None:
1094+
"""
1095+
Test that users see graceful handling when progress bar widgets are destroyed.
1096+
1097+
GIVEN: A flight controller info window with progress bar widgets that get destroyed
1098+
WHEN: The system attempts to update progress
1099+
THEN: The update is safely skipped without errors
1100+
"""
1101+
# Given
1102+
stack, patches = mock_tkinter_context()
1103+
1104+
with stack:
1105+
# Start all patches for UI isolation
1106+
for patch_obj in patches:
1107+
stack.enter_context(patch_obj)
1108+
1109+
with (
1110+
patch("tkinter.ttk.Frame"),
1111+
patch("tkinter.ttk.Progressbar"),
1112+
patch("tkinter.ttk.Label"),
1113+
patch.object(FlightControllerInfoWindow, "_create_info_display"),
1114+
patch.object(FlightControllerInfoWindow, "_download_flight_controller_parameters"),
1115+
patch("tkinter.Tk.mainloop"),
1116+
):
1117+
window = FlightControllerInfoWindow.__new__(FlightControllerInfoWindow)
1118+
window.presenter = FlightControllerInfoPresenter(configured_flight_controller)
1119+
1120+
# Simulate progress bar widget being destroyed
1121+
window.progress_bar = None
1122+
1123+
# When
1124+
window.update_progress_bar(5, 10)
1125+
1126+
# Then - No errors occur, method returns early safely
1127+
1128+
def test_user_sees_graceful_handling_when_window_lift_fails_due_to_tcl_error(
1129+
self, mock_tkinter_context, configured_flight_controller: Mock
1130+
) -> None:
1131+
"""
1132+
Test that users see graceful handling when window lift fails due to TclError.
1133+
1134+
GIVEN: A flight controller info window where tkinter operations may fail
1135+
WHEN: The system attempts to update progress but window lift fails
1136+
THEN: The error is logged and progress update continues safely
1137+
"""
1138+
# Given
1139+
stack, patches = mock_tkinter_context()
1140+
1141+
with stack:
1142+
# Start all patches for UI isolation
1143+
for patch_obj in patches:
1144+
stack.enter_context(patch_obj)
1145+
1146+
with (
1147+
patch("tkinter.ttk.Frame"),
1148+
patch("tkinter.ttk.Progressbar"),
1149+
patch("tkinter.ttk.Label"),
1150+
patch.object(FlightControllerInfoWindow, "_create_info_display"),
1151+
patch.object(FlightControllerInfoWindow, "_download_flight_controller_parameters"),
1152+
patch("tkinter.Tk.mainloop"),
1153+
):
1154+
window = FlightControllerInfoWindow.__new__(FlightControllerInfoWindow)
1155+
window.presenter = FlightControllerInfoPresenter(configured_flight_controller)
1156+
1157+
# Set up mock widgets
1158+
mock_progress_bar = Mock()
1159+
mock_progress_bar.__setitem__ = Mock()
1160+
mock_progress_label = Mock()
1161+
mock_root = Mock()
1162+
mock_root.lift.side_effect = tk.TclError("window not found")
1163+
1164+
window.progress_bar = mock_progress_bar
1165+
window.progress_label = mock_progress_label
1166+
window.root = mock_root
1167+
window.progress_frame = Mock()
1168+
1169+
# When
1170+
with patch("logging.error") as mock_log_error:
1171+
window.update_progress_bar(5, 10)
1172+
1173+
# Then - Error is logged
1174+
mock_log_error.assert_called_once()
1175+
assert "Lifting window:" in mock_log_error.call_args[0][0]
1176+
1177+
# And progress update does NOT continue due to the return statement in the except block
1178+
mock_progress_bar.__setitem__.assert_not_called()
1179+
1180+
def test_user_sees_progress_bar_hidden_when_download_completes(
1181+
self, mock_tkinter_context, configured_flight_controller: Mock
1182+
) -> None:
1183+
"""
1184+
Test that users see progress bar hidden when download completes.
1185+
1186+
GIVEN: A flight controller info window with active progress tracking
1187+
WHEN: The parameter download completes (current == max)
1188+
THEN: The progress bar is automatically hidden
1189+
"""
1190+
# Given
1191+
stack, patches = mock_tkinter_context()
1192+
1193+
with stack:
1194+
# Start all patches for UI isolation
1195+
for patch_obj in patches:
1196+
stack.enter_context(patch_obj)
1197+
1198+
with (
1199+
patch("tkinter.ttk.Frame"),
1200+
patch("tkinter.ttk.Progressbar"),
1201+
patch("tkinter.ttk.Label"),
1202+
patch.object(FlightControllerInfoWindow, "_create_info_display"),
1203+
patch.object(FlightControllerInfoWindow, "_download_flight_controller_parameters"),
1204+
patch("tkinter.Tk.mainloop"),
1205+
):
1206+
window = FlightControllerInfoWindow.__new__(FlightControllerInfoWindow)
1207+
window.presenter = FlightControllerInfoPresenter(configured_flight_controller)
1208+
1209+
# Set up mock widgets
1210+
mock_progress_bar = Mock()
1211+
mock_progress_bar.__setitem__ = Mock()
1212+
mock_progress_label = Mock()
1213+
mock_progress_frame = Mock()
1214+
mock_root = Mock()
1215+
1216+
window.progress_bar = mock_progress_bar
1217+
window.progress_label = mock_progress_label
1218+
window.progress_frame = mock_progress_frame
1219+
window.root = mock_root
1220+
1221+
# When - Download completes
1222+
window.update_progress_bar(10, 10)
1223+
1224+
# Then - Progress bar is hidden
1225+
mock_progress_frame.pack_forget.assert_called_once()

0 commit comments

Comments
 (0)