Coverage for tests/components/test_file_selector_widget.py: 100%
483 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
1import os
2import pytest
3from unittest.mock import Mock, patch, MagicMock
4from PyQt6.QtWidgets import QListWidgetItem
5from PyQt6.QtCore import Qt, pyqtSignal
7from main.components.file_selector_widget import FileSelectorWidget
10@pytest.fixture
11def mock_context_selector(signal_emitter):
12 """Context per FileSelectorWidget."""
13 context = {
14 "selected_files_signal": signal_emitter.selected_files,
15 "update_main_buttons": Mock(),
16 "language_changed": signal_emitter.language_changed,
17 "workspace_path": "/tmp"
18 }
19 return context
22@pytest.fixture
23def mock_has_existing_function():
24 """Mock per has_existing_function."""
25 return Mock(return_value=False)
28@pytest.fixture
29def test_nifti_files(temp_workspace):
30 """Create test NIfTI file."""
31 files = []
33 # File .nii
34 nii_file = os.path.join(temp_workspace, "test1.nii")
35 with open(nii_file, "w") as f:
36 f.write("nifti data")
37 files.append(nii_file)
39 # File .nii.gz
40 nii_gz_file = os.path.join(temp_workspace, "test2.nii.gz")
41 with open(nii_gz_file, "w") as f:
42 f.write("compressed nifti data")
43 files.append(nii_gz_file)
45 # File non NIfTI
46 txt_file = os.path.join(temp_workspace, "test.txt")
47 with open(txt_file, "w") as f:
48 f.write("text data")
49 files.append(txt_file)
51 return files
54class TestFileSelectorWidgetInitialization:
55 """Test for initialization if FileSelectorWidget."""
57 def test_initialization_basic(self, qtbot, mock_context_selector, mock_has_existing_function):
58 """Test base initialization."""
59 widget = FileSelectorWidget(
60 context=mock_context_selector,
61 has_existing_function=mock_has_existing_function,
62 label="test",
63 allow_multiple=True
64 )
65 qtbot.addWidget(widget)
67 assert widget.context == mock_context_selector
68 assert widget.label == "test"
69 assert widget.allow_multiple is True
70 assert widget.has_existing_function == mock_has_existing_function
71 assert widget.selected_files is None
73 def test_initialization_single_file_mode(self, qtbot, mock_context_selector, mock_has_existing_function):
74 """Test initialization single file modality."""
75 widget = FileSelectorWidget(
76 context=mock_context_selector,
77 has_existing_function=mock_has_existing_function,
78 label="single",
79 allow_multiple=False
80 )
81 qtbot.addWidget(widget)
83 assert widget.allow_multiple is False
85 def test_initialization_with_processing_signal(self, qtbot, mock_context_selector, mock_has_existing_function):
86 """Test initialization with signal processing."""
87 processing_signal = Mock()
88 processing_signal.connect = Mock()
90 widget = FileSelectorWidget(
91 context=mock_context_selector,
92 has_existing_function=mock_has_existing_function,
93 label="test",
94 allow_multiple=True,
95 processing=processing_signal
96 )
97 qtbot.addWidget(widget)
99 # Verifica che il signal sia stato connesso
100 processing_signal.connect.assert_called_once()
102 def test_initialization_with_forced_filters(self, qtbot, mock_context_selector, mock_has_existing_function):
103 """Test initialization with forced_filters."""
104 filters = {"type": "mask"}
106 widget = FileSelectorWidget(
107 context=mock_context_selector,
108 has_existing_function=mock_has_existing_function,
109 label="test",
110 allow_multiple=True,
111 forced_filters=filters
112 )
113 qtbot.addWidget(widget)
115 assert widget.forced_filters == filters
117 def test_ui_elements_created(self, qtbot, mock_context_selector, mock_has_existing_function):
118 """Test creation UI elements"""
119 widget = FileSelectorWidget(
120 context=mock_context_selector,
121 has_existing_function=mock_has_existing_function,
122 label="test",
123 allow_multiple=True
124 )
125 qtbot.addWidget(widget)
127 assert widget.file_list_widget is not None
128 assert widget.file_button is not None
129 assert widget.clear_button is not None
131 def test_clear_button_initially_disabled(self, qtbot, mock_context_selector, mock_has_existing_function):
132 """Test clear button initially disabled"""
133 widget = FileSelectorWidget(
134 context=mock_context_selector,
135 has_existing_function=mock_has_existing_function,
136 label="test",
137 allow_multiple=True
138 )
139 qtbot.addWidget(widget)
141 assert not widget.clear_button.isEnabled()
143 def test_file_list_widget_config(self, qtbot, mock_context_selector, mock_has_existing_function):
144 """Test configuration file_list_widget."""
145 widget = FileSelectorWidget(
146 context=mock_context_selector,
147 has_existing_function=mock_has_existing_function,
148 label="test",
149 allow_multiple=True
150 )
151 qtbot.addWidget(widget)
153 assert widget.file_list_widget.selectionMode() == widget.file_list_widget.SelectionMode.NoSelection
154 assert widget.file_list_widget.focusPolicy() == Qt.FocusPolicy.NoFocus
155 assert widget.file_list_widget.maximumHeight() == 100
158class TestSetSelectedFiles:
159 """Test selected files method."""
161 def test_set_selected_files_multiple_valid(self, qtbot, mock_context_selector, mock_has_existing_function,
162 test_nifti_files):
163 """Test multiple files valid"""
164 widget = FileSelectorWidget(
165 context=mock_context_selector,
166 has_existing_function=mock_has_existing_function,
167 label="test",
168 allow_multiple=True
169 )
170 qtbot.addWidget(widget)
172 # Obly first two are .nii/.nii.gz
173 widget.set_selected_files(test_nifti_files)
175 assert len(widget.selected_files) == 2
177 def test_many_files(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
178 """Test wuth manty files."""
179 files = []
180 for i in range(100):
181 filepath = os.path.join(temp_workspace, f"file{i}.nii")
182 with open(filepath, "w") as f:
183 f.write("data")
184 files.append(filepath)
186 widget = FileSelectorWidget(
187 context=mock_context_selector,
188 has_existing_function=mock_has_existing_function,
189 label="test",
190 allow_multiple=True
191 )
192 qtbot.addWidget(widget)
194 widget.set_selected_files(files)
196 assert len(widget.selected_files) == 100
197 assert widget.file_list_widget.count() == 100
199 def test_unicode_in_filenames(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
200 """Test with unicode chars in file name."""
201 unicode_files = [
202 os.path.join(temp_workspace, "файл.nii"),
203 os.path.join(temp_workspace, "文件.nii"),
204 os.path.join(temp_workspace, "αρχείο.nii.gz")
205 ]
207 for filepath in unicode_files:
208 with open(filepath, "w") as f:
209 f.write("data")
211 widget = FileSelectorWidget(
212 context=mock_context_selector,
213 has_existing_function=mock_has_existing_function,
214 label="test",
215 allow_multiple=True
216 )
217 qtbot.addWidget(widget)
219 widget.set_selected_files(unicode_files)
221 assert len(widget.selected_files) == 3
223 def test_mixed_nii_and_nii_gz(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
224 """Test with file .nii and .nii.gz mixed."""
225 files = []
227 for i in range(5):
228 nii_file = os.path.join(temp_workspace, f"file{i}.nii")
229 with open(nii_file, "w") as f:
230 f.write("data")
231 files.append(nii_file)
233 nii_gz_file = os.path.join(temp_workspace, f"file{i}.nii.gz")
234 with open(nii_gz_file, "w") as f:
235 f.write("data")
236 files.append(nii_gz_file)
238 widget = FileSelectorWidget(
239 context=mock_context_selector,
240 has_existing_function=mock_has_existing_function,
241 label="test",
242 allow_multiple=True
243 )
244 qtbot.addWidget(widget)
246 widget.set_selected_files(files)
248 assert len(widget.selected_files) == 10
250 def test_duplicate_files(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
251 """Test with duplicated file in the list."""
252 filepath = os.path.join(temp_workspace, "duplicate.nii")
253 with open(filepath, "w") as f:
254 f.write("data")
256 widget = FileSelectorWidget(
257 context=mock_context_selector,
258 has_existing_function=mock_has_existing_function,
259 label="test",
260 allow_multiple=True
261 )
262 qtbot.addWidget(widget)
264 # Pass two times same file
265 widget.set_selected_files([filepath, filepath])
267 # Should be both accepted
268 assert len(widget.selected_files) == 2
271class TestIntegration:
272 """Test integration."""
274 def test_full_workflow_select_clear(self, qtbot, mock_context_selector, mock_has_existing_function,
275 test_nifti_files):
276 """Test workflow complete: select -> clear."""
277 widget = FileSelectorWidget(
278 context=mock_context_selector,
279 has_existing_function=mock_has_existing_function,
280 label="test",
281 allow_multiple=True
282 )
283 qtbot.addWidget(widget)
285 # Seleziona file
286 widget.set_selected_files(test_nifti_files)
288 assert len(widget.selected_files) == 2
289 assert widget.clear_button.isEnabled()
290 assert widget.file_list_widget.count() == 2
292 # Pulisci
293 widget.clear_selected_files()
295 assert widget.selected_files == []
296 assert not widget.clear_button.isEnabled()
297 assert widget.file_list_widget.count() == 0
299 def test_full_workflow_multiple_selections(self, qtbot, mock_context_selector, mock_has_existing_function,
300 test_nifti_files):
301 """Test workflow with multiple selection."""
302 widget = FileSelectorWidget(
303 context=mock_context_selector,
304 has_existing_function=mock_has_existing_function,
305 label="test",
306 allow_multiple=True
307 )
308 qtbot.addWidget(widget)
310 # First selection
311 widget.set_selected_files([test_nifti_files[0]])
312 assert len(widget.selected_files) == 1
314 # Second selection
315 widget.set_selected_files(test_nifti_files)
316 assert len(widget.selected_files) == 2
318 # Clear
319 widget.clear_selected_files()
320 assert len(widget.selected_files) == 0
322 # Third selection
323 widget.set_selected_files([test_nifti_files[1]])
324 assert len(widget.selected_files) == 1
326 def test_full_workflow_with_processing(self, qtbot, mock_context_selector, mock_has_existing_function,
327 test_nifti_files):
328 """Test workflow with processing mode."""
329 widget = FileSelectorWidget(
330 context=mock_context_selector,
331 has_existing_function=mock_has_existing_function,
332 label="test",
333 allow_multiple=True
334 )
335 qtbot.addWidget(widget)
337 # Seleziona file
338 widget.set_selected_files(test_nifti_files)
339 assert widget.file_button.isEnabled()
340 assert widget.clear_button.isEnabled()
342 # Avvia processing
343 widget.set_processing_mode(True)
344 assert not widget.file_button.isEnabled()
345 assert not widget.clear_button.isEnabled()
347 # Termina processing
348 widget.set_processing_mode(False)
349 assert widget.file_button.isEnabled()
350 assert widget.clear_button.isEnabled()
353class TestContextHandling:
354 """Test context handling."""
356 def test_context_without_update_main_buttons(self, qtbot, signal_emitter, mock_has_existing_function,
357 test_nifti_files):
358 """Test context without update_main_buttons."""
359 context = {
360 "selected_files_signal": signal_emitter.selected_files,
361 "language_changed": signal_emitter.language_changed
362 }
364 widget = FileSelectorWidget(
365 context=context,
366 has_existing_function=mock_has_existing_function,
367 label="test",
368 allow_multiple=True
369 )
370 qtbot.addWidget(widget)
372 # Should not crash
373 widget.set_selected_files(test_nifti_files)
374 widget.clear_selected_files()
376 def test_context_without_language_changed(self, qtbot, signal_emitter, mock_has_existing_function):
377 """Test context without language_changed."""
378 context = {
379 "selected_files_signal": signal_emitter.selected_files,
380 "update_main_buttons": Mock()
381 }
383 widget = FileSelectorWidget(
384 context=context,
385 has_existing_function=mock_has_existing_function,
386 label="test",
387 allow_multiple=True
388 )
389 qtbot.addWidget(widget)
391 # Non dovrebbe crashare
392 assert widget.context == context
395class TestButtonClicks:
396 """Test for button clicks."""
398 def test_file_button_click(self, qtbot, mock_context_selector, mock_has_existing_function):
399 """Test click on file buttons."""
400 widget = FileSelectorWidget(
401 context=mock_context_selector,
402 has_existing_function=mock_has_existing_function,
403 label="test",
404 allow_multiple=True
405 )
406 qtbot.addWidget(widget)
408 with patch.object(widget, 'open_tree_dialog') as mock_open:
409 widget.file_button.clicked.disconnect()
410 widget.file_button.clicked.connect(widget.open_tree_dialog)
411 widget.file_button.click()
412 mock_open.assert_called_once()
414 def test_clear_button_click(self, qtbot, mock_context_selector, mock_has_existing_function, test_nifti_files):
415 """Test click on clear button."""
416 widget = FileSelectorWidget(
417 context=mock_context_selector,
418 has_existing_function=mock_has_existing_function,
419 label="test",
420 allow_multiple=True
421 )
422 qtbot.addWidget(widget)
424 widget.set_selected_files(test_nifti_files)
426 with patch.object(widget, 'open_tree_dialog') as mock_open:
427 widget.clear_button.clicked.disconnect()
428 widget.clear_button.clicked.connect(widget.open_tree_dialog)
429 widget.clear_button.click()
430 mock_open.assert_called_once()
433class TestFileExtensions:
434 """Tests for file extension handling."""
436 def test_accepts_nii(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
437 """Test that it accepts .nii files."""
438 nii_file = os.path.join(temp_workspace, "test.nii")
439 with open(nii_file, "w") as f:
440 f.write("data")
442 widget = FileSelectorWidget(
443 context=mock_context_selector,
444 has_existing_function=mock_has_existing_function,
445 label="test",
446 allow_multiple=True
447 )
448 qtbot.addWidget(widget)
450 widget.set_selected_files([nii_file])
452 assert len(widget.selected_files) == 1
454 def test_accepts_nii_gz(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
455 """Test that it accepts .nii.gz files."""
456 nii_gz_file = os.path.join(temp_workspace, "test.nii.gz")
457 with open(nii_gz_file, "w") as f:
458 f.write("data")
460 widget = FileSelectorWidget(
461 context=mock_context_selector,
462 has_existing_function=mock_has_existing_function,
463 label="test",
464 allow_multiple=True
465 )
466 qtbot.addWidget(widget)
468 widget.set_selected_files([nii_gz_file])
470 assert len(widget.selected_files) == 1
472 def test_rejects_other_extensions(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
473 """Test that it rejects other extensions."""
474 invalid_files = [
475 os.path.join(temp_workspace, "test.txt"),
476 os.path.join(temp_workspace, "test.nii.bak"),
477 os.path.join(temp_workspace, "test.json"),
478 os.path.join(temp_workspace, "test.nii.txt")
479 ]
481 for filepath in invalid_files:
482 with open(filepath, "w") as f:
483 f.write("data")
485 widget = FileSelectorWidget(
486 context=mock_context_selector,
487 has_existing_function=mock_has_existing_function,
488 label="test",
489 allow_multiple=True
490 )
491 qtbot.addWidget(widget)
493 widget.set_selected_files(invalid_files)
495 assert len(widget.selected_files) == 0
497 def test_case_sensitive_extensions(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
498 """Test case sensitivity of file extensions."""
499 # .nii and .nii.gz should be case sensitive
500 upper_file = os.path.join(temp_workspace, "test.NII")
501 with open(upper_file, "w") as f:
502 f.write("data")
504 widget = FileSelectorWidget(
505 context=mock_context_selector,
506 has_existing_function=mock_has_existing_function,
507 label="test",
508 allow_multiple=True
509 )
510 qtbot.addWidget(widget)
512 widget.set_selected_files([upper_file])
514 # Should be rejected (case sensitive)
515 assert len(widget.selected_files) == 0
517class TestMemoryAndPerformance:
518 """Tests for memory usage and performance."""
520 def test_no_memory_leak_repeated_selections(self, qtbot, mock_context_selector, mock_has_existing_function,
521 test_nifti_files):
522 """Test that there are no memory leaks with repeated selections."""
523 widget = FileSelectorWidget(
524 context=mock_context_selector,
525 has_existing_function=mock_has_existing_function,
526 label="test",
527 allow_multiple=True
528 )
529 qtbot.addWidget(widget)
531 # Select and clear 100 times
532 for _ in range(100):
533 widget.set_selected_files(test_nifti_files)
534 widget.clear_selected_files()
536 # Final state should be clean
537 assert widget.selected_files == []
538 assert widget.file_list_widget.count() == 0
540 def test_performance_many_items(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
541 """Performance test with many items."""
542 files = []
543 for i in range(500):
544 filepath = os.path.join(temp_workspace, f"file{i}.nii")
545 with open(filepath, "w") as f:
546 f.write("data")
547 files.append(filepath)
549 widget = FileSelectorWidget(
550 context=mock_context_selector,
551 has_existing_function=mock_has_existing_function,
552 label="test",
553 allow_multiple=True
554 )
555 qtbot.addWidget(widget)
557 # Should not be too slow
558 widget.set_selected_files(files)
560 assert len(widget.selected_files) == 500
564class TestStateConsistency:
565 """Tests for state consistency."""
567 def test_state_after_set_and_get(self, qtbot, mock_context_selector, mock_has_existing_function, test_nifti_files):
568 """Test state consistency after set and get."""
569 widget = FileSelectorWidget(
570 context=mock_context_selector,
571 has_existing_function=mock_has_existing_function,
572 label="test",
573 allow_multiple=True
574 )
575 qtbot.addWidget(widget)
577 widget.set_selected_files(test_nifti_files)
579 retrieved_files = widget.get_selected_files()
581 assert retrieved_files == widget.selected_files
582 assert len(retrieved_files) == len(widget.selected_files)
584 def test_ui_state_consistency(self, qtbot, mock_context_selector, mock_has_existing_function, test_nifti_files):
585 """Test UI state consistency."""
586 widget = FileSelectorWidget(
587 context=mock_context_selector,
588 has_existing_function=mock_has_existing_function,
589 label="test",
590 allow_multiple=True
591 )
592 qtbot.addWidget(widget)
594 # Initial state
595 assert widget.selected_files is None
596 assert widget.file_list_widget.count() == 0
597 assert not widget.clear_button.isEnabled()
599 # After selection
600 widget.set_selected_files(test_nifti_files)
601 assert widget.selected_files is not None
602 assert widget.file_list_widget.count() == len(widget.selected_files)
603 assert widget.clear_button.isEnabled()
605 # After clearing
606 widget.clear_selected_files()
607 assert widget.selected_files == []
608 assert widget.file_list_widget.count() == 0
609 assert not widget.clear_button.isEnabled()
612class TestAccessibility:
613 """Tests for accessibility."""
615 def test_buttons_have_text(self, qtbot, mock_context_selector, mock_has_existing_function):
616 """Test that the buttons have text."""
617 widget = FileSelectorWidget(
618 context=mock_context_selector,
619 has_existing_function=mock_has_existing_function,
620 label="test",
621 allow_multiple=True
622 )
623 qtbot.addWidget(widget)
625 assert len(widget.file_button.text()) > 0
626 assert len(widget.clear_button.text()) > 0
628 def test_list_items_have_tooltips(self, qtbot, mock_context_selector, mock_has_existing_function, test_nifti_files):
629 """Test that the items have tooltips."""
630 widget = FileSelectorWidget(
631 context=mock_context_selector,
632 has_existing_function=mock_has_existing_function,
633 label="test",
634 allow_multiple=True
635 )
636 qtbot.addWidget(widget)
638 widget.set_selected_files(test_nifti_files)
640 for i in range(widget.file_list_widget.count()):
641 item = widget.file_list_widget.item(i)
642 tooltip = item.toolTip()
643 assert len(tooltip) > 0
644 assert tooltip in test_nifti_files
645 assert all(f.endswith(('.nii', '.nii.gz')) for f in widget.selected_files)
647 def test_set_selected_files_single_mode(self, qtbot, mock_context_selector, mock_has_existing_function,
648 test_nifti_files):
649 """Test setting files in single mode."""
650 widget = FileSelectorWidget(
651 context=mock_context_selector,
652 has_existing_function=mock_has_existing_function,
653 label="test",
654 allow_multiple=False
655 )
656 qtbot.addWidget(widget)
658 widget.set_selected_files(test_nifti_files)
660 # Should keep only the last file
661 assert len(widget.selected_files) == 1
663 def test_set_selected_files_filters_non_nifti(self, qtbot, mock_context_selector, mock_has_existing_function,
664 temp_workspace):
665 """Test that non-NIfTI files are filtered out."""
666 widget = FileSelectorWidget(
667 context=mock_context_selector,
668 has_existing_function=mock_has_existing_function,
669 label="test",
670 allow_multiple=True
671 )
672 qtbot.addWidget(widget)
674 # Create mixed files
675 nifti_file = os.path.join(temp_workspace, "valid.nii")
676 txt_file = os.path.join(temp_workspace, "invalid.txt")
678 with open(nifti_file, "w") as f:
679 f.write("nifti")
680 with open(txt_file, "w") as f:
681 f.write("text")
683 widget.set_selected_files([nifti_file, txt_file])
685 # Only the .nii file should be selected
686 assert len(widget.selected_files) == 1
687 assert widget.selected_files[0] == nifti_file
689 def test_set_selected_files_filters_directories(self, qtbot, mock_context_selector, mock_has_existing_function,
690 temp_workspace):
691 """Test that directories are filtered out."""
692 widget = FileSelectorWidget(
693 context=mock_context_selector,
694 has_existing_function=mock_has_existing_function,
695 label="test",
696 allow_multiple=True
697 )
698 qtbot.addWidget(widget)
700 # Create a directory
701 dir_path = os.path.join(temp_workspace, "test_dir.nii")
702 os.makedirs(dir_path)
704 widget.set_selected_files([dir_path])
706 # Directory should not be accepted
707 assert len(widget.selected_files) == 0
709 def test_set_selected_files_filters_nonexistent(self, qtbot, mock_context_selector, mock_has_existing_function):
710 """Test that nonexistent files are filtered out."""
711 widget = FileSelectorWidget(
712 context=mock_context_selector,
713 has_existing_function=mock_has_existing_function,
714 label="test",
715 allow_multiple=True
716 )
717 qtbot.addWidget(widget)
719 widget.set_selected_files(["/nonexistent/file.nii"])
721 assert len(widget.selected_files) == 0
723 def test_set_selected_files_updates_list_widget(self, qtbot, mock_context_selector, mock_has_existing_function,
724 test_nifti_files):
725 """Test that the list widget is updated."""
726 widget = FileSelectorWidget(
727 context=mock_context_selector,
728 has_existing_function=mock_has_existing_function,
729 label="test",
730 allow_multiple=True
731 )
732 qtbot.addWidget(widget)
734 widget.set_selected_files(test_nifti_files)
736 # There should be 2 items (only .nii/.nii.gz)
737 assert widget.file_list_widget.count() == 2
739 def test_set_selected_files_enables_clear_button(self, qtbot, mock_context_selector, mock_has_existing_function,
740 test_nifti_files):
741 """Test that the clear button is enabled."""
742 widget = FileSelectorWidget(
743 context=mock_context_selector,
744 has_existing_function=mock_has_existing_function,
745 label="test",
746 allow_multiple=True
747 )
748 qtbot.addWidget(widget)
750 assert not widget.clear_button.isEnabled()
752 widget.set_selected_files(test_nifti_files)
754 assert widget.clear_button.isEnabled()
756 def test_set_selected_files_emits_has_file_signal(self, qtbot, mock_context_selector, mock_has_existing_function,
757 test_nifti_files):
758 """Test that the has_file signal is emitted."""
759 widget = FileSelectorWidget(
760 context=mock_context_selector,
761 has_existing_function=mock_has_existing_function,
762 label="test",
763 allow_multiple=True
764 )
765 qtbot.addWidget(widget)
767 with qtbot.waitSignal(widget.has_file) as blocker:
768 widget.set_selected_files(test_nifti_files)
770 # Signal should be True (files exist)
771 assert blocker.args[0] is True
773 def test_set_selected_files_calls_update_main_buttons(self, qtbot, mock_context_selector,
774 mock_has_existing_function, test_nifti_files):
775 """Test that update_main_buttons is called."""
776 widget = FileSelectorWidget(
777 context=mock_context_selector,
778 has_existing_function=mock_has_existing_function,
779 label="test",
780 allow_multiple=True
781 )
782 qtbot.addWidget(widget)
784 widget.set_selected_files(test_nifti_files)
786 mock_context_selector["update_main_buttons"].assert_called()
788 def test_set_selected_files_with_tooltips(self, qtbot, mock_context_selector, mock_has_existing_function,
789 test_nifti_files):
790 """Test that tooltips are set."""
791 widget = FileSelectorWidget(
792 context=mock_context_selector,
793 has_existing_function=mock_has_existing_function,
794 label="test",
795 allow_multiple=True
796 )
797 qtbot.addWidget(widget)
799 widget.set_selected_files(test_nifti_files)
801 # Verify tooltips
802 for i in range(widget.file_list_widget.count()):
803 item = widget.file_list_widget.item(i)
804 assert item.toolTip() != ""
807class TestClearSelectedFiles:
808 """Tests for the clear_selected_files method."""
810 def test_clear_selected_files_basic(self, qtbot, mock_context_selector, mock_has_existing_function,
811 test_nifti_files):
812 """Basic clear files test."""
813 widget = FileSelectorWidget(
814 context=mock_context_selector,
815 has_existing_function=mock_has_existing_function,
816 label="test",
817 allow_multiple=True
818 )
819 qtbot.addWidget(widget)
821 # First select files
822 widget.set_selected_files(test_nifti_files)
823 assert len(widget.selected_files) > 0
825 # Then clear
826 widget.clear_selected_files()
828 assert widget.selected_files == []
829 assert widget.file_list_widget.count() == 0
830 assert not widget.clear_button.isEnabled()
832 def test_clear_selected_files_emits_signal(self, qtbot, mock_context_selector, mock_has_existing_function,
833 test_nifti_files):
834 """Test that it emits has_file signal with False."""
835 widget = FileSelectorWidget(
836 context=mock_context_selector,
837 has_existing_function=mock_has_existing_function,
838 label="test",
839 allow_multiple=True
840 )
841 qtbot.addWidget(widget)
843 widget.set_selected_files(test_nifti_files)
845 with qtbot.waitSignal(widget.has_file) as blocker:
846 widget.clear_selected_files()
848 assert blocker.args[0] is False
850 def test_clear_selected_files_calls_update_main_buttons(self, qtbot, mock_context_selector,
851 mock_has_existing_function, test_nifti_files):
852 """Test that it calls update_main_buttons."""
853 widget = FileSelectorWidget(
854 context=mock_context_selector,
855 has_existing_function=mock_has_existing_function,
856 label="test",
857 allow_multiple=True
858 )
859 qtbot.addWidget(widget)
861 widget.set_selected_files(test_nifti_files)
862 mock_context_selector["update_main_buttons"].reset_mock()
864 widget.clear_selected_files()
866 mock_context_selector["update_main_buttons"].assert_called_once()
868 def test_clear_when_already_empty(self, qtbot, mock_context_selector, mock_has_existing_function):
869 """Test clear when already empty."""
870 widget = FileSelectorWidget(
871 context=mock_context_selector,
872 has_existing_function=mock_has_existing_function,
873 label="test",
874 allow_multiple=True
875 )
876 qtbot.addWidget(widget)
878 # Clear without having selected files
879 widget.clear_selected_files()
881 assert widget.selected_files == []
884class TestGetSelectedFiles:
885 """Tests for the get_selected_files method."""
887 def test_get_selected_files_with_files(self, qtbot, mock_context_selector, mock_has_existing_function,
888 test_nifti_files):
889 """Test get files when files are selected."""
890 widget = FileSelectorWidget(
891 context=mock_context_selector,
892 has_existing_function=mock_has_existing_function,
893 label="test",
894 allow_multiple=True
895 )
896 qtbot.addWidget(widget)
898 widget.set_selected_files(test_nifti_files)
900 files = widget.get_selected_files()
902 assert files == widget.selected_files
903 assert len(files) == 2
905 def test_get_selected_files_empty(self, qtbot, mock_context_selector, mock_has_existing_function):
906 """Test get files with no selection."""
907 widget = FileSelectorWidget(
908 context=mock_context_selector,
909 has_existing_function=mock_has_existing_function,
910 label="test",
911 allow_multiple=True
912 )
913 qtbot.addWidget(widget)
915 files = widget.get_selected_files()
917 assert files is None
920class TestOpenTreeDialog:
921 """Tests for the open_tree_dialog method."""
923 def test_open_tree_dialog_called(self, qtbot, mock_context_selector, mock_has_existing_function):
924 """Test that open_tree_dialog calls NiftiFileDialog."""
925 widget = FileSelectorWidget(
926 context=mock_context_selector,
927 has_existing_function=mock_has_existing_function,
928 label="test",
929 allow_multiple=True
930 )
931 qtbot.addWidget(widget)
933 with patch("main.components.file_selector_widget.NiftiFileDialog") as mock_dialog:
934 mock_dialog.get_files = Mock(return_value=None)
936 widget.open_tree_dialog()
938 mock_dialog.get_files.assert_called_once()
940 def test_open_tree_dialog_with_results_multiple(self, qtbot, mock_context_selector, mock_has_existing_function,
941 test_nifti_files):
942 """Test open_tree_dialog with multiple results."""
943 widget = FileSelectorWidget(
944 context=mock_context_selector,
945 has_existing_function=mock_has_existing_function,
946 label="test",
947 allow_multiple=True
948 )
949 qtbot.addWidget(widget)
951 with patch("main.components.file_selector_widget.NiftiFileDialog") as mock_dialog:
952 mock_dialog.get_files = Mock(return_value=test_nifti_files[:2])
954 widget.open_tree_dialog()
956 assert widget.selected_files == test_nifti_files[:2]
958 def test_open_tree_dialog_with_results_single(self, qtbot, mock_context_selector, mock_has_existing_function,
959 test_nifti_files):
960 """Test open_tree_dialog with results in single mode."""
961 widget = FileSelectorWidget(
962 context=mock_context_selector,
963 has_existing_function=mock_has_existing_function,
964 label="test",
965 allow_multiple=False
966 )
967 qtbot.addWidget(widget)
969 with patch("main.components.file_selector_widget.NiftiFileDialog") as mock_dialog:
970 mock_dialog.get_files = Mock(return_value=test_nifti_files[:2])
972 widget.open_tree_dialog()
974 # Should take only the first file
975 assert len(widget.selected_files) == 1
977 def test_open_tree_dialog_parameters(self, qtbot, mock_context_selector, mock_has_existing_function):
978 """Test that open_tree_dialog passes the correct parameters."""
979 widget = FileSelectorWidget(
980 context=mock_context_selector,
981 has_existing_function=mock_has_existing_function,
982 label="mylabel",
983 allow_multiple=True,
984 forced_filters={"type": "mask"}
985 )
986 qtbot.addWidget(widget)
988 with patch("main.components.file_selector_widget.NiftiFileDialog") as mock_dialog:
989 mock_dialog.get_files = Mock(return_value=None)
991 widget.open_tree_dialog()
993 call_kwargs = mock_dialog.get_files.call_args[1]
994 assert call_kwargs['context'] == mock_context_selector
995 assert call_kwargs['allow_multiple'] is True
996 assert call_kwargs['has_existing_func'] == mock_has_existing_function
997 assert call_kwargs['label'] == "mylabel"
998 assert call_kwargs['forced_filters'] == {"type": "mask"}
1001class TestSetProcessingMode:
1002 """Tests for the set_processing_mode method."""
1004 def test_set_processing_mode_true(self, qtbot, mock_context_selector, mock_has_existing_function):
1005 """Test set_processing_mode True."""
1006 widget = FileSelectorWidget(
1007 context=mock_context_selector,
1008 has_existing_function=mock_has_existing_function,
1009 label="test",
1010 allow_multiple=True
1011 )
1012 qtbot.addWidget(widget)
1014 widget.set_processing_mode(True)
1016 assert not widget.file_button.isEnabled()
1017 assert not widget.clear_button.isEnabled()
1019 def test_set_processing_mode_false_with_files(self, qtbot, mock_context_selector, mock_has_existing_function,
1020 test_nifti_files):
1021 """Test set_processing_mode False with files."""
1022 widget = FileSelectorWidget(
1023 context=mock_context_selector,
1024 has_existing_function=mock_has_existing_function,
1025 label="test",
1026 allow_multiple=True
1027 )
1028 qtbot.addWidget(widget)
1030 widget.set_selected_files(test_nifti_files)
1031 widget.set_processing_mode(False)
1033 assert widget.file_button.isEnabled()
1034 assert widget.clear_button.isEnabled()
1036 def test_set_processing_mode_false_without_files(self, qtbot, mock_context_selector, mock_has_existing_function):
1037 """Test set_processing_mode False without files."""
1038 widget = FileSelectorWidget(
1039 context=mock_context_selector,
1040 has_existing_function=mock_has_existing_function,
1041 label="test",
1042 allow_multiple=True
1043 )
1044 qtbot.addWidget(widget)
1046 widget.set_processing_mode(False)
1048 assert widget.file_button.isEnabled()
1049 assert not widget.clear_button.isEnabled()
1051 def test_set_processing_mode_toggle(self, qtbot, mock_context_selector, mock_has_existing_function,
1052 test_nifti_files):
1053 """Test toggle processing mode."""
1054 widget = FileSelectorWidget(
1055 context=mock_context_selector,
1056 has_existing_function=mock_has_existing_function,
1057 label="test",
1058 allow_multiple=True
1059 )
1060 qtbot.addWidget(widget)
1062 widget.set_selected_files(test_nifti_files)
1064 # Enable processing
1065 widget.set_processing_mode(True)
1066 assert not widget.file_button.isEnabled()
1068 # Disable processing
1069 widget.set_processing_mode(False)
1070 assert widget.file_button.isEnabled()
1073class TestSignals:
1074 """Tests for signals."""
1076 def test_has_file_signal_exists(self, qtbot, mock_context_selector, mock_has_existing_function):
1077 """Test that the has_file signal exists."""
1078 widget = FileSelectorWidget(
1079 context=mock_context_selector,
1080 has_existing_function=mock_has_existing_function,
1081 label="test",
1082 allow_multiple=True
1083 )
1084 qtbot.addWidget(widget)
1086 assert hasattr(widget, 'has_file')
1088 def test_selected_files_signal_connection(self, qtbot, mock_context_selector, mock_has_existing_function):
1089 """Test that the selected_files_signal is connected."""
1090 widget = FileSelectorWidget(
1091 context=mock_context_selector,
1092 has_existing_function=mock_has_existing_function,
1093 label="test",
1094 allow_multiple=True
1095 )
1096 qtbot.addWidget(widget)
1098 # Verify that the signal is connected (by emitting)
1099 test_files = ["/fake/file.nii"]
1100 mock_context_selector["selected_files_signal"].emit(test_files)
1102 # The widget should have received the signal
1105class TestTranslations:
1106 """Tests for translations."""
1108 def test_translate_ui_called_on_init(self, qtbot, mock_context_selector, mock_has_existing_function):
1109 """Test that _translate_ui is called during init."""
1110 with patch.object(FileSelectorWidget, '_translate_ui') as mock_translate:
1111 widget = FileSelectorWidget(
1112 context=mock_context_selector,
1113 has_existing_function=mock_has_existing_function,
1114 label="test",
1115 allow_multiple=True
1116 )
1117 qtbot.addWidget(widget)
1119 mock_translate.assert_called()
1121 def test_translate_ui_updates_buttons(self, qtbot, mock_context_selector, mock_has_existing_function):
1122 """Test that _translate_ui updates the buttons."""
1123 widget = FileSelectorWidget(
1124 context=mock_context_selector,
1125 has_existing_function=mock_has_existing_function,
1126 label="test",
1127 allow_multiple=True
1128 )
1129 qtbot.addWidget(widget)
1131 widget._translate_ui()
1133 assert widget.file_button.text() != ""
1134 assert widget.clear_button.text() != ""
1136 def test_language_changed_signal(self, qtbot, mock_context_selector, signal_emitter, mock_has_existing_function):
1137 """Test that the language_changed signal updates the UI."""
1138 widget = FileSelectorWidget(
1139 context=mock_context_selector,
1140 has_existing_function=mock_has_existing_function,
1141 label="test",
1142 allow_multiple=True
1143 )
1144 qtbot.addWidget(widget)
1146 with patch.object(widget, '_translate_ui') as mock_translate:
1147 mock_context_selector["language_changed"].connect(mock_translate)
1148 mock_context_selector["language_changed"].emit("it")
1150 mock_translate.assert_called()
1153class TestEdgeCases:
1154 """Tests for edge cases."""
1156 def test_empty_file_list(self, qtbot, mock_context_selector, mock_has_existing_function):
1157 """Test with empty file list."""
1158 widget = FileSelectorWidget(
1159 context=mock_context_selector,
1160 has_existing_function=mock_has_existing_function,
1161 label="test",
1162 allow_multiple=True
1163 )
1164 qtbot.addWidget(widget)
1166 widget.set_selected_files([])
1168 assert widget.selected_files == []
1169 assert widget.file_list_widget.count() == 0
1171 def test_very_long_file_paths(self, qtbot, mock_context_selector, mock_has_existing_function, temp_workspace):
1172 """Test with very long file paths."""
1173 long_path = os.path.join(temp_workspace, "a" * 200 + ".nii")
1174 with open(long_path, "w") as f:
1175 f.write("data")
1177 widget = FileSelectorWidget(
1178 context=mock_context_selector,
1179 has_existing_function=mock_has_existing_function,
1180 label="test",
1181 allow_multiple=True
1182 )
1183 qtbot.addWidget(widget)
1185 widget.set_selected_files([long_path])
1187 assert len(widget.selected_files) == 1
1188 assert widget.file_list_widget.count() == 1