Coverage for test\test_HUD_main.py: 99%
345 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
1import pytest
2from unittest.mock import MagicMock, patch
3from PyQt5.QtWidgets import QApplication
4import sys
5import types
6import zmq
8from pathlib import Path
9sys.path.append(str(Path(__file__).parent.parent))
11# Create a mock 'WinTables' module
12win_tables_module = types.ModuleType('WinTables')
13win_tables_module.Table = MagicMock()
15with patch.dict('sys.modules', {'WinTables': win_tables_module}):
16 import HUD_main
18@pytest.fixture
19def app(qtbot):
20 return QApplication.instance()
22@pytest.fixture
23def hud_main(app, qtbot):
24 # Crate mock
25 options = MagicMock()
26 options.dbname = 'test_db'
27 options.config = None
28 options.errorsToConsole = False
29 options.xloc = None
30 options.yloc = None
32 import tempfile
34 with patch('HUD_main.Configuration.Config') as mock_config, \
35 patch('HUD_main.Configuration.set_logfile'), \
36 patch('HUD_main.Database.Database'), \
37 patch('HUD_main.ZMQReceiver'), \
38 patch('sys.exit'), \
39 patch('PyQt5.QtCore.QCoreApplication.quit'):
41 mock_config_instance = MagicMock()
42 mock_config.return_value = mock_config_instance
44 mock_config_instance.dir_log = tempfile.gettempdir()
45 mock_config_instance.os_family = 'Win7'
46 mock_config_instance.get_hud_ui_parameters.return_value = {
47 'deck_type': 'default',
48 'card_back': 'blue',
49 'card_wd': 72,
50 'card_ht': 96,
51 'hud_days': 30,
52 'h_hud_days': 90,
54 }
55 mock_config_instance.graphics_path = tempfile.gettempdir()
56 mock_config_instance.hhcs = {'test_site': MagicMock(converter='some_converter')}
57 mock_config_instance.get_site_parameters.return_value = {
58 'layout_set': 'some_layout',
59 'param1': 'value1',
61 }
62 mock_config_instance.get_layout.return_value = 'some_layout'
64 hm = HUD_main.HUD_main(options, db_name=options.dbname)
65 qtbot.addWidget(hm.main_window)
66 yield hm
68 qtbot.waitExposed(hm.main_window)
69 hm.main_window.close()
76# Verifies that all necessary attributes of the HUD_main instance are correctly initialized.
77def test_hud_main_initialization(hud_main):
78 assert hud_main.db_name == 'test_db'
79 assert hasattr(hud_main, 'config')
80 assert hasattr(hud_main, 'db_connection')
81 assert hasattr(hud_main, 'hud_dict')
82 assert hasattr(hud_main, 'blacklist')
83 assert hasattr(hud_main, 'hud_params')
84 assert hasattr(hud_main, 'deck')
85 assert hasattr(hud_main, 'cache')
86 assert hasattr(hud_main, 'zmq_receiver')
87 assert hasattr(hud_main, 'zmq_worker')
88 assert hasattr(hud_main, 'main_window')
90# Ensures that the handle_message method correctly calls read_stdin when provided with a hand ID.
91def test_handle_message(hud_main):
92 with patch.object(hud_main, 'read_stdin') as mock_read_stdin:
93 hud_main.handle_message('test_hand_id')
94 mock_read_stdin.assert_called_once_with('test_hand_id')
96# Checks that the destroy method properly closes connections and stops processes.
97def test_destroy(hud_main):
98 with patch.object(hud_main.zmq_receiver, 'close') as mock_close, \
99 patch.object(hud_main.zmq_worker, 'stop') as mock_stop, \
100 patch('PyQt5.QtCore.QCoreApplication.quit') as mock_quit:
101 hud_main.destroy()
102 mock_close.assert_called_once()
103 mock_stop.assert_called_once()
104 mock_quit.assert_called_once()
106# Verifies that check_tables calls the correct methods (client_destroyed, client_moved, client_resized) based on the table's status.
107@pytest.mark.parametrize("status, expected_method", [
108 ("client_destroyed", "client_destroyed"),
109 ("client_moved", "client_moved"),
110 ("client_resized", "client_resized"),
111])
112def test_check_tables(hud_main, status, expected_method):
113 mock_hud = MagicMock()
114 mock_hud.table.check_table.return_value = status
115 hud_main.hud_dict = {'test_table': mock_hud}
117 with patch.object(hud_main, expected_method) as mock_method:
118 hud_main.check_tables()
119 mock_method.assert_called_once_with(None, mock_hud)
121# Ensures that create_HUD creates a new HUD and adds it to the hud_dict.
122def test_create_hud(hud_main):
123 with patch.object(HUD_main.Hud, 'Hud') as mock_hud, \
124 patch.object(hud_main, 'idle_create') as mock_idle_create, \
125 patch.object(hud_main.config, 'get_site_parameters', return_value={'layout_set': 'some_layout', 'param1': 'value1'}), \
126 patch.object(hud_main.config, 'get_layout', return_value='some_layout'):
128 mock_table = MagicMock()
129 mock_table.site = 'test_site'
130 hud_main.create_HUD('new_hand_id', mock_table, 'temp_key', 9, 'poker_game', 'cash', {}, {})
132 assert 'temp_key' in hud_main.hud_dict
133 mock_hud.assert_called_once()
134 mock_idle_create.assert_called_once()
139# Verifies that update_HUD properly calls idle_update.
140def test_update_hud(hud_main):
141 with patch.object(hud_main, 'idle_update') as mock_idle_update:
142 hud_main.update_HUD('new_hand_id', 'table_name', hud_main.config)
143 mock_idle_update.assert_called_once_with('new_hand_id', 'table_name', hud_main.config)
145# Ensures that cached data is processed correctly in read_stdin and calls update_HUD.
146def test_read_stdin_cached(hud_main):
147 # Configuration env
148 hud_main.config = MagicMock()
149 hud_main.config.get_supported_sites.return_value = ['test_site']
150 hud_main.config.supported_sites = {'test_site': MagicMock(screen_name='test_hero')}
151 test_hand_id = 'test_hand_id'
152 hud_main.cache[test_hand_id] = (
153 'table_name', 9, 'poker_game', 'cash', False, 1, 'test_site', 9, 'tour_number', 'tab_number'
154 )
155 temp_key = 'table_name'
156 hud_main.hud_dict[temp_key] = MagicMock()
157 hud_main.hud_dict[temp_key].hud_params = {'hud_days': 30, 'h_hud_days': 90}
158 hud_main.hud_dict[temp_key].poker_game = 'poker_game'
159 hud_main.hud_dict[temp_key].max = 9
160 hud_main.hud_dict[temp_key].aux_windows = []
162 with patch.object(hud_main.db_connection, 'get_site_id', return_value=[(1,)]), \
163 patch.object(hud_main.db_connection, 'get_player_id', return_value=123), \
164 patch.object(hud_main.db_connection, 'init_hud_stat_vars'), \
165 patch.object(hud_main.db_connection, 'get_stats_from_hand', return_value={'player1': {'screen_name': 'test_hero'}}), \
166 patch.object(hud_main, 'get_cards', return_value={}), \
167 patch.object(hud_main, 'update_HUD') as mock_update_hud:
168 hud_main.read_stdin(test_hand_id)
169 assert mock_update_hud.called, "update_HUD n'a pas été appelé"
171# Confirms that cached data is used if available when calling read_stdin.
172def test_read_stdin_cache_used(hud_main):
173 hud_main.cache = {'test_hand_id': ('table_name', 9, 'poker_game', 'cash', False, 1, 'test_site', 9, 123, 'tab')}
174 with patch('HUD_main.log.debug') as mock_log_debug:
175 hud_main.read_stdin('test_hand_id')
176 mock_log_debug.assert_any_call('Using cached data for hand test_hand_id')
179# Tests the behavior of read_stdin when no cached data is available, ensuring it calls create_HUD
180def test_read_stdin_not_cached(hud_main):
182 hud_main.config = MagicMock()
183 hud_main.config.get_supported_sites.return_value = ['test_site']
184 hud_main.config.supported_sites = {'test_site': MagicMock(screen_name='test_hero')}
185 hud_main.config.get_site_parameters.return_value = {'aux_enabled': True}
188 hud_main.Tables = MagicMock()
189 hud_main.Tables.Table.return_value = MagicMock()
190 test_hand_id = 'test_hand_id'
193 hud_main.cache = {}
196 table_info = (
197 'table_name', 9, 'poker_game', 'tour', False, 1, 'test_site', 9, 123456, 'Table 789'
198 )
200 with patch.object(hud_main.db_connection, 'get_site_id', return_value=[(1,)]), \
201 patch.object(hud_main.db_connection, 'get_player_id', return_value=123), \
202 patch.object(hud_main.db_connection, 'get_table_info', return_value=table_info), \
203 patch.object(hud_main.db_connection, 'init_hud_stat_vars'), \
204 patch.object(hud_main.db_connection, 'get_stats_from_hand', return_value={'player1': {'screen_name': 'test_hero'}}), \
205 patch.object(hud_main, 'get_cards', return_value={}), \
206 patch.object(hud_main.Tables, 'Table', return_value=MagicMock()) as mock_table, \
207 patch.object(hud_main, 'create_HUD') as mock_create_hud:
209 hud_main.read_stdin(test_hand_id)
210 assert mock_create_hud.called, "create_HUD n'a pas été appelé"
218# Ensures that get_cards retrieves both player and community cards correctly.
219def test_get_cards(hud_main):
220 mock_db = MagicMock()
221 mock_db.get_cards.return_value = {'player1': ['As', 'Kh']}
222 mock_db.get_common_cards.return_value = {'common': ['Jd', 'Qc', 'Tc']}
223 hud_main.db_connection = mock_db
225 cards = hud_main.get_cards('test_hand_id', 'holdem')
226 assert 'player1' in cards
227 assert 'common' in cards
229# Verifies that idle_kill removes the HUD from hud_dict and cleans up widgets.
230def test_idle_kill(hud_main):
231 mock_hud = MagicMock()
232 hud_main.hud_dict['test_table'] = mock_hud
233 hud_main.vb = MagicMock()
235 hud_main.idle_kill('test_table')
237 assert 'test_table' not in hud_main.hud_dict
238 mock_hud.kill.assert_called_once()
239 hud_main.vb.removeWidget.assert_called_once()
241# Checks exception handling in read_stdin when an error occurs in get_table_info.
242def test_read_stdin_exception_handling(hud_main):
244 hud_main.config = MagicMock()
245 hud_main.config.get_supported_sites.return_value = ['test_site']
246 hud_main.config.get_site_parameters.return_value = {'aux_enabled': True}
247 hud_main.hero = {}
248 hud_main.hero_ids = {}
251 hud_main.cache = {}
254 test_hand_id = 'test_hand_id'
256 with patch.object(hud_main.db_connection, 'get_table_info', side_effect=Exception("Database error")), \
257 patch.object(hud_main, 'destroy') as mock_destroy:
259 hud_main.read_stdin(test_hand_id)
262 mock_destroy.assert_not_called()
267# Ensures that ZMQWorker.stop stops the worker properly.
268def test_zmqworker_stop():
269 zmq_receiver = MagicMock()
270 worker = HUD_main.ZMQWorker(zmq_receiver)
271 worker.wait = MagicMock()
272 worker.is_running = True
274 worker.stop()
275 assert not worker.is_running
276 worker.wait.assert_called_once()
279# Verifies that a heartbeat log is generated when no messages are received.
280def test_process_message_heartbeat(hud_main):
281 zmq_receiver = HUD_main.ZMQReceiver()
282 zmq_receiver.socket = MagicMock()
283 zmq_receiver.poller = MagicMock()
285 zmq_receiver.poller.poll.return_value = {}
287 with patch('HUD_main.log.debug') as mock_log_debug:
288 zmq_receiver.process_message()
289 mock_log_debug.assert_called_with('Heartbeat: No message received')
292# Tests the run loop of ZMQWorker, ensuring it stops after processing a message.
293def test_zmqworker_run(hud_main):
294 zmq_receiver = MagicMock()
295 worker = HUD_main.ZMQWorker(zmq_receiver)
297 # Limit the loop to avoid an infinite blockage
298 worker.is_running = True
300 # Use of `side_effect` to stop the loop after the first call to `process_message`.
301 def stop_after_one_iteration(*args, **kwargs):
302 worker.is_running = False
304 with patch('time.sleep', return_value=None), \
305 patch.object(zmq_receiver, 'process_message', side_effect=stop_after_one_iteration) as mock_process_message:
306 worker.run()
307 mock_process_message.assert_called_once()
309# Ensures that process_message logs the correct hand ID received from the socket.
310def test_process_message(hud_main):
311 zmq_receiver = HUD_main.ZMQReceiver()
312 zmq_receiver.socket = MagicMock()
313 zmq_receiver.poller = MagicMock()
315 zmq_receiver.poller.poll.return_value = {zmq_receiver.socket: zmq.POLLIN}
316 zmq_receiver.socket.recv_string.return_value = 'hand_id'
318 with patch('HUD_main.log.debug') as mock_log_debug:
319 zmq_receiver.process_message()
320 mock_log_debug.assert_called_with('Received hand ID: hand_id')
323# Verifies that table_title_changed calls kill_hud when the table's title changes.
324def test_table_title_changed_calls_kill_hud(hud_main):
325 mock_hud = MagicMock()
326 mock_hud.table.key = 'test_table'
327 hud_main.hud_dict['test_table'] = mock_hud
329 with patch.object(hud_main, 'kill_hud') as mock_kill_hud:
330 hud_main.table_title_changed(None, mock_hud)
331 mock_kill_hud.assert_called_once_with(None, 'test_table')
333# Ensures that table_is_stale calls kill_hud for stale tables.
334def test_table_is_stale_calls_kill_hud(hud_main):
335 mock_hud = MagicMock()
336 mock_hud.table.key = 'test_table'
337 hud_main.hud_dict['test_table'] = mock_hud
339 with patch.object(hud_main, 'kill_hud') as mock_kill_hud:
340 hud_main.table_is_stale(mock_hud)
341 mock_kill_hud.assert_called_once_with(None, 'test_table')
344# Verifies that blacklist_hud correctly removes a HUD from hud_dict and adds it to the blacklist.
345def test_blacklist_hud(hud_main):
346 mock_hud = MagicMock()
347 mock_hud.tablenumber = 123
348 hud_main.hud_dict['test_table'] = mock_hud
349 hud_main.vb = MagicMock()
351 hud_main.blacklist_hud(None, 'test_table')
353 assert 123 in hud_main.blacklist
354 assert 'test_table' not in hud_main.hud_dict
355 mock_hud.kill.assert_called_once()
356 hud_main.vb.removeWidget.assert_called_once()
359# Ensures that handle_worker_error logs an error message.
360def test_handle_worker_error(hud_main):
361 with patch('HUD_main.log.error') as mock_log_error:
362 hud_main.handle_worker_error('Test error message')
363 mock_log_error.assert_called_once_with('ZMQWorker encountered an error: Test error message')
365# Verifies that close_event_handler calls destroy and accepts the event.
366def test_close_event_handler(hud_main):
367 mock_event = MagicMock()
368 with patch.object(hud_main, 'destroy') as mock_destroy:
369 hud_main.close_event_handler(mock_event)
370 mock_destroy.assert_called_once()
371 mock_event.accept.assert_called_once()
375# Ensures that idle_move moves the table and auxiliary windows.
376def test_idle_move(hud_main):
377 mock_hud = MagicMock()
378 mock_hud.aux_windows = [MagicMock()]
379 hud_main.idle_move(mock_hud)
381 mock_hud.move_table_position.assert_called_once()
382 for aw in mock_hud.aux_windows:
383 aw.move_windows.assert_called_once()
385# Verifies that idle_resize resizes the table and auxiliary windows.
386def test_idle_resize(hud_main):
387 mock_hud = MagicMock()
388 mock_hud.aux_windows = [MagicMock()]
389 hud_main.idle_resize(mock_hud)
391 mock_hud.resize_windows.assert_called_once()
392 for aw in mock_hud.aux_windows:
393 aw.resize_windows.assert_called_once()
395# Checks that kill_hud removes the HUD from hud_dict and cleans up associated widgets.
396def test_kill_hud(hud_main):
397 mock_hud = MagicMock()
398 hud_main.hud_dict['test_table'] = mock_hud
399 hud_main.vb = MagicMock()
401 hud_main.kill_hud(None, 'test_table')
403 assert 'test_table' not in hud_main.hud_dict
404 mock_hud.kill.assert_called_once()
405 hud_main.vb.removeWidget.assert_called_once()
410# Verifies that client_moved calls idle_move for the given HUD.
411def test_client_moved(hud_main):
412 mock_hud = MagicMock()
413 with patch.object(hud_main, 'idle_move') as mock_idle_move:
414 hud_main.client_moved(None, mock_hud)
415 mock_idle_move.assert_called_once_with(mock_hud)
419# Ensures that client_resized calls idle_resize for the given HUD.
420def test_client_resized(hud_main):
421 mock_hud = MagicMock()
422 with patch.object(hud_main, 'idle_resize') as mock_idle_resize:
423 hud_main.client_resized(None, mock_hud)
424 mock_idle_resize.assert_called_once_with(mock_hud)
426# Checks that client_destroyed calls kill_hud for the appropriate HUD.
427def test_client_destroyed(hud_main):
428 mock_hud = MagicMock()
429 mock_hud.table.key = 'test_table'
430 with patch.object(hud_main, 'kill_hud') as mock_kill_hud:
431 hud_main.client_destroyed(None, mock_hud)
432 mock_kill_hud.assert_called_once_with(None, 'test_table')
434# Verifies that idle_create creates a new HUD and adds it to hud_dict, along with logging.
435@pytest.mark.parametrize("import_path", [
436 'HUD_main.QLabel',
437 'PyQt5.QtWidgets.QLabel'
438])
439def test_idle_create(import_path, hud_main):
441 with patch(import_path) as mock_qlabel, \
442 patch('HUD_main.log') as mock_log:
444 # Configuration
445 mock_hud = MagicMock()
446 mock_hud.tablehudlabel = MagicMock()
447 hud_main.hud_dict = {'test_table': mock_hud}
448 hud_main.vb = MagicMock()
451 new_hand_id = 'new_hand_id'
452 table = MagicMock()
453 table.site = 'test_site'
454 table.number = 123
455 temp_key = 'test_table'
456 max = 9
457 poker_game = 'holdem'
458 hud_type = 'cash'
459 stat_dict = {}
460 cards = {}
463 with patch.object(hud_main, 'get_cards', return_value=cards), \
464 patch.object(hud_main.hud_dict['test_table'], 'create') as mock_create, \
465 patch.object(hud_main.hud_dict['test_table'], 'aux_windows', []):
467 print("Before calling idle_create")
469 # Call idle_create
470 hud_main.idle_create(new_hand_id, table, temp_key, max, poker_game, hud_type, stat_dict, cards)
472 print("After calling idle_create")
473 print(f"mock_qlabel.call_count: {mock_qlabel.call_count}")
474 print(f"mock_qlabel.call_args_list: {mock_qlabel.call_args_list}")
476 # Checks
477 tablehudlabel = hud_main.hud_dict[temp_key].tablehudlabel
478 print(f"tablehudlabel: {tablehudlabel}")
479 print(f"Type of tablehudlabel: {tablehudlabel.__class__}")
480 print(f"Is instance of mocked QLabel: {isinstance(tablehudlabel, mock_qlabel.return_value.__class__)}")
482 # Check taht vb.addWidget is called
483 try:
484 hud_main.vb.addWidget.assert_called_once()
485 print("vb.addWidget assertion passed")
486 except AssertionError as e:
487 print(f"vb.addWidget assertion failed: {e}")
489 # Check attributs
490 assert hud_main.hud_dict[temp_key].tablehudlabel is not None, "tablehudlabel is None"
491 assert hud_main.hud_dict[temp_key].tablenumber == table.number, "tablenumber mismatch"
492 print("hud_dict assertions passed")
494 # Check call
495 try:
496 mock_create.assert_called_once_with(new_hand_id, hud_main.config, stat_dict)
497 print("create method assertion passed")
498 except AssertionError as e:
499 print(f"create method assertion failed: {e}")
501 # Check logs
502 expected_log_message = f"adding label {table.site} - {temp_key}"
503 print(f"Expected log message: {expected_log_message}")
504 print(f"Actual log calls: {mock_log.debug.call_args_list}")
506 log_message_found = any(call[0][0] == expected_log_message for call in mock_log.debug.call_args_list)
507 if log_message_found:
508 print("Log assertion passed")
509 else:
510 print("Log assertion failed: Expected message not found in debug logs")
512# Ensures that idle_update updates the HUD and auxiliary windows.
513def test_idle_update(hud_main):
514 # Create a mock HUD
515 temp_key = 'table_name'
516 mock_hud = MagicMock()
517 hud_main.hud_dict[temp_key] = mock_hud
518 hud_main.hud_dict[temp_key].aux_windows = [MagicMock()]
520 # Call idle_update
521 hud_main.idle_update('new_hand_id', temp_key, hud_main.config)
523 # Assert update method was called
524 mock_hud.update.assert_called_once_with('new_hand_id', hud_main.config)
526 # Assert aux_windows update_gui was called
527 for aw in hud_main.hud_dict[temp_key].aux_windows:
528 aw.update_gui.assert_called_once_with('new_hand_id')
531# Confirms that idle_kill removes widgets from the layout and calls the HUD's kill method.
532def test_idle_kill_widget_removal(hud_main):
533 mock_hud = MagicMock()
534 hud_main.hud_dict['test_table'] = mock_hud
535 hud_main.vb = MagicMock()
537 # Call idle_kill
538 hud_main.idle_kill('test_table')
540 # Assert widget was removed from layout
541 hud_main.vb.removeWidget.assert_called_once_with(mock_hud.tablehudlabel)
543 # Assert kill method on HUD was called
544 mock_hud.kill.assert_called_once()
546 # Assert HUD is removed from the dictionary
547 assert 'test_table' not in hud_main.hud_dict
549# Ensures that check_tables calls the correct methods for different table statuses.
550@pytest.mark.parametrize("status", ["client_destroyed", "client_moved", "client_resized"])
551def test_check_tables_full_coverage(hud_main, status):
552 mock_hud = MagicMock()
553 mock_hud.table.check_table.return_value = status
554 hud_main.hud_dict = {'test_table': mock_hud}
556 # Map status to expected method
557 method_map = {
558 "client_destroyed": "client_destroyed",
559 "client_moved": "client_moved",
560 "client_resized": "client_resized"
561 }
563 with patch.object(hud_main, method_map[status]) as mock_method:
564 hud_main.check_tables()
565 mock_method.assert_called_once_with(None, mock_hud)
567# Verifies that the appropriate idle methods (idle_move, idle_resize, kill_hud) are called for different client actions.
568@pytest.mark.parametrize("method_name, expected_args", [
569 ("client_moved", (MagicMock(),)),
570 ("client_resized", (MagicMock(),)),
571 ("client_destroyed", (None, MagicMock().table.key))
572])
573def test_client_methods(hud_main, method_name, expected_args):
574 mock_hud = MagicMock()
576 # Map method to expected idle method
577 idle_method = {
578 "client_moved": "idle_move",
579 "client_resized": "idle_resize",
580 "client_destroyed": "kill_hud"
581 }[method_name]
583 with patch.object(hud_main, idle_method) as mock_idle_method:
584 getattr(hud_main, method_name)(None, mock_hud)
585 if method_name == "client_destroyed":
586 mock_idle_method.assert_called_once_with(expected_args[0], mock_hud.table.key)
587 else:
588 mock_idle_method.assert_called_once_with(mock_hud)
590# Ensures that auxiliary windows are created and updated properly.
591def test_aux_windows_creation_and_update(hud_main):
592 mock_aux_window = MagicMock()
593 hud_main.hud_dict['test_key'] = MagicMock()
594 hud_main.hud_dict['test_key'].aux_windows = [mock_aux_window]
596 with patch('HUD_main.log.debug') as mock_log_debug:
597 hud_main.idle_create('new_hand_id', MagicMock(), 'test_key', 9, 'poker_game', 'cash', {}, {})
599 mock_aux_window.create.assert_called_once()
600 mock_aux_window.update_gui.assert_called_once_with('new_hand_id')
601 mock_log_debug.assert_called_with(f"idle_create new_hand_id new_hand_id")
605# Verifies that ZMQReceiver.close properly closes the socket and context, and logs the closure.
606def test_zmqreceiver_close(hud_main):
607 zmq_receiver = HUD_main.ZMQReceiver(port="5555")
609 with patch.object(zmq_receiver.socket, 'close') as mock_socket_close, \
610 patch.object(zmq_receiver.context, 'term') as mock_context_term, \
611 patch('HUD_main.log.info') as mock_log_info:
613 zmq_receiver.close()
615 # Ensure socket.close and context.term were called
616 mock_socket_close.assert_called_once()
617 mock_context_term.assert_called_once()
619 # Ensure the closure was logged
620 mock_log_info.assert_called_with('ZMQ receiver closed')