1+ """
2+ Unit tests for the HistoryMsgTextCtrl class.
3+
4+ This module contains comprehensive tests for the history message text control,
5+ including tests for message management, navigation, text control behavior,
6+ edge cases, and error conditions.
7+
8+ Testing Framework: unittest
9+ """
10+
11+ import unittest
12+ from unittest .mock import Mock , patch , MagicMock , mock_open
13+ import sys
14+ import os
15+ import tempfile
16+
17+ # Add the GUI module to the path
18+ gui_path = os .path .join (os .path .dirname (__file__ ), '..' , 'gui' )
19+ if gui_path not in sys .path :
20+ sys .path .insert (0 , gui_path )
21+
22+ try :
23+ from history_msg_text_ctrl import HistoryMsgTextCtrl
24+ except ImportError as e :
25+ print (f"Warning: Could not import HistoryMsgTextCtrl: { e } " )
26+ # Create a mock class for testing infrastructure
27+ class HistoryMsgTextCtrl :
28+ def __init__ (self , parent = None ):
29+ self .parent = parent
30+ self .history = []
31+ self .position = - 1
32+
33+ class TestHistoryMsgTextCtrl (unittest .TestCase ):
34+ def setUp (self ):
35+ """Set up test fixtures before each test method."""
36+ self .mock_parent = Mock ()
37+ self .history_ctrl = HistoryMsgTextCtrl (self .mock_parent )
38+
39+ def tearDown (self ):
40+ """Clean up after each test method."""
41+ if hasattr (self .history_ctrl , 'Destroy' ):
42+ self .history_ctrl .Destroy ()
43+ self .history_ctrl = None
44+
45+ def test_init_with_parent (self ):
46+ """Test initialization with parent parameter."""
47+ ctrl = HistoryMsgTextCtrl (self .mock_parent )
48+ self .assertIsNotNone (ctrl )
49+
50+ def test_init_without_parent (self ):
51+ """Test initialization without parent parameter."""
52+ with self .assertRaises (TypeError ):
53+ HistoryMsgTextCtrl ()
54+
55+ def test_init_with_none_parent (self ):
56+ """Test initialization with None parent."""
57+ ctrl = HistoryMsgTextCtrl (None )
58+ self .assertIsNotNone (ctrl )
59+
60+ def test_add_message_to_history (self ):
61+ """Test adding a single message to history."""
62+ test_message = "Test message"
63+ result = self .history_ctrl .add_message (test_message )
64+ self .assertTrue (result )
65+
66+ def test_add_empty_message (self ):
67+ """Test adding empty message to history."""
68+ result = self .history_ctrl .add_message ("" )
69+ self .assertIsNotNone (result )
70+
71+ def test_add_none_message (self ):
72+ """Test adding None message to history."""
73+ with self .assertRaises ((TypeError , ValueError )):
74+ self .history_ctrl .add_message (None )
75+
76+ def test_add_multiple_messages (self ):
77+ """Test adding multiple messages to history."""
78+ messages = ["Message 1" , "Message 2" , "Message 3" ]
79+ for msg in messages :
80+ self .history_ctrl .add_message (msg )
81+ history = self .history_ctrl .get_history ()
82+ self .assertEqual (len (history ), 3 )
83+ for msg in messages :
84+ self .assertIn (msg , history )
85+
86+ def test_get_history_empty (self ):
87+ """Test getting history when no messages added."""
88+ history = self .history_ctrl .get_history ()
89+ self .assertEqual (history , [])
90+
91+ def test_clear_history (self ):
92+ """Test clearing message history."""
93+ self .history_ctrl .add_message ("Test message" )
94+ self .history_ctrl .clear_history ()
95+ history = self .history_ctrl .get_history ()
96+ self .assertEqual (len (history ), 0 )
97+
98+ def test_get_previous_message (self ):
99+ """Test retrieving previous message from history."""
100+ messages = ["Message 1" , "Message 2" , "Message 3" ]
101+ for msg in messages :
102+ self .history_ctrl .add_message (msg )
103+ prev_msg = self .history_ctrl .get_previous_message ()
104+ self .assertEqual (prev_msg , "Message 3" )
105+
106+ def test_get_next_message (self ):
107+ """Test retrieving next message from history."""
108+ messages = ["Message 1" , "Message 2" , "Message 3" ]
109+ for msg in messages :
110+ self .history_ctrl .add_message (msg )
111+ self .history_ctrl .get_previous_message ()
112+ self .history_ctrl .get_previous_message ()
113+ next_msg = self .history_ctrl .get_next_message ()
114+ self .assertEqual (next_msg , "Message 3" )
115+
116+ def test_navigate_beyond_history_bounds (self ):
117+ """Test navigation beyond history boundaries."""
118+ self .history_ctrl .add_message ("Only message" )
119+ msg1 = self .history_ctrl .get_previous_message ()
120+ msg2 = self .history_ctrl .get_previous_message ()
121+ self .assertEqual (msg1 , msg2 )
122+
123+ def test_navigate_empty_history (self ):
124+ """Test navigation when history is empty."""
125+ prev_msg = self .history_ctrl .get_previous_message ()
126+ next_msg = self .history_ctrl .get_next_message ()
127+ self .assertIsNone (prev_msg )
128+ self .assertIsNone (next_msg )
129+
130+ def test_set_current_text (self ):
131+ """Test setting current text in the control."""
132+ test_text = "Current text"
133+ self .history_ctrl .set_current_text (test_text )
134+ current = self .history_ctrl .get_current_text ()
135+ self .assertEqual (current , test_text )
136+
137+ def test_get_current_text_empty (self ):
138+ """Test getting current text when empty."""
139+ current = self .history_ctrl .get_current_text ()
140+ self .assertEqual (current , "" )
141+
142+ def test_text_control_focus (self ):
143+ """Test setting focus to the text control."""
144+ with patch .object (self .history_ctrl , 'SetFocus' ) as mock_focus :
145+ self .history_ctrl .set_focus ()
146+ mock_focus .assert_called_once ()
147+
148+ def test_history_position_tracking (self ):
149+ """Test that history position is tracked correctly."""
150+ messages = ["Msg 1" , "Msg 2" , "Msg 3" ]
151+ for msg in messages :
152+ self .history_ctrl .add_message (msg )
153+ pos = self .history_ctrl .get_history_position ()
154+ self .assertEqual (pos , - 1 )
155+ self .history_ctrl .get_previous_message ()
156+ pos = self .history_ctrl .get_history_position ()
157+ self .assertEqual (pos , 2 )
158+
159+ def test_maximum_history_size (self ):
160+ """Test behavior when history reaches maximum size."""
161+ max_size = getattr (self .history_ctrl , 'MAX_HISTORY_SIZE' , 1000 )
162+ for i in range (max_size + 10 ):
163+ self .history_ctrl .add_message (f"Message { i } " )
164+ history = self .history_ctrl .get_history ()
165+ self .assertLessEqual (len (history ), max_size )
166+
167+ def test_very_long_message (self ):
168+ """Test handling of very long messages."""
169+ long_message = "A" * 10000
170+ result = self .history_ctrl .add_message (long_message )
171+ self .assertTrue (result )
172+ retrieved = self .history_ctrl .get_previous_message ()
173+ self .assertEqual (retrieved , long_message )
174+
175+ def test_unicode_messages (self ):
176+ """Test handling of unicode characters in messages."""
177+ unicode_messages = ["Hello äøē" , "š Test" , "CafĆ© naĆÆve rĆ©sumĆ©" ]
178+ for msg in unicode_messages :
179+ self .history_ctrl .add_message (msg )
180+ for msg in reversed (unicode_messages ):
181+ retrieved = self .history_ctrl .get_previous_message ()
182+ self .assertEqual (retrieved , msg )
183+
184+ def test_multiline_messages (self ):
185+ """Test handling of multiline messages."""
186+ multiline_msg = "Line 1\n Line 2\n Line 3"
187+ self .history_ctrl .add_message (multiline_msg )
188+ retrieved = self .history_ctrl .get_previous_message ()
189+ self .assertEqual (retrieved , multiline_msg )
190+
191+ def test_duplicate_messages (self ):
192+ """Test adding duplicate messages to history."""
193+ msg = "Duplicate message"
194+ self .history_ctrl .add_message (msg )
195+ self .history_ctrl .add_message (msg )
196+ history = self .history_ctrl .get_history ()
197+ self .assertGreaterEqual (len (history ), 1 )
198+
199+ def test_save_history_to_file (self ):
200+ """Test saving history to file if supported."""
201+ if hasattr (self .history_ctrl , 'save_history' ):
202+ messages = ["Save test 1" , "Save test 2" ]
203+ for msg in messages :
204+ self .history_ctrl .add_message (msg )
205+ with patch ('builtins.open' , mock_open ()) as mock_file :
206+ result = self .history_ctrl .save_history ("test_history.txt" )
207+ self .assertTrue (result )
208+ mock_file .assert_called_once ()
209+
210+ def test_load_history_from_file (self ):
211+ """Test loading history from file if supported."""
212+ if hasattr (self .history_ctrl , 'load_history' ):
213+ mock_data = "Message 1\n Message 2\n Message 3"
214+ with patch ('builtins.open' , mock_open (read_data = mock_data )):
215+ result = self .history_ctrl .load_history ("test_history.txt" )
216+ self .assertTrue (result )
217+ history = self .history_ctrl .get_history ()
218+ self .assertEqual (len (history ), 3 )
219+
220+ def test_configuration_settings (self ):
221+ """Test configuration settings if supported."""
222+ if hasattr (self .history_ctrl , 'configure' ):
223+ config = {'max_history' : 50 , 'save_duplicates' : False }
224+ self .history_ctrl .configure (config )
225+ current_config = self .history_ctrl .get_configuration ()
226+ self .assertEqual (current_config ['max_history' ], 50 )
227+
228+ def test_str_representation (self ):
229+ """Test string representation of the control."""
230+ str_repr = str (self .history_ctrl )
231+ self .assertIsInstance (str_repr , str )
232+ self .assertIn ('HistoryMsgTextCtrl' , str_repr )
233+
234+ def test_repr_representation (self ):
235+ """Test repr representation of the control."""
236+ repr_str = repr (self .history_ctrl )
237+ self .assertIsInstance (repr_str , str )
238+
239+ @patch ('sys.platform' , 'win32' )
240+ def test_platform_specific_behavior_windows (self ):
241+ """Test platform-specific behavior on Windows."""
242+ pass
243+
244+ @patch ('sys.platform' , 'linux' )
245+ def test_platform_specific_behavior_linux (self ):
246+ """Test platform-specific behavior on Linux."""
247+ pass
248+
249+ if __name__ == '__main__' :
250+ loader = unittest .TestLoader ()
251+ suite = loader .loadTestsFromTestCase (TestHistoryMsgTextCtrl )
252+ runner = unittest .TextTestRunner (verbosity = 2 )
253+ result = runner .run (suite )
254+ sys .exit (0 if result .wasSuccessful () else 1 )
0 commit comments