Coverage for tests/conftest.py: 97%

96 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-03 15:51 +0000

1import shutil 

2import tempfile 

3from unittest.mock import Mock, patch 

4 

5import pytest 

6import sys 

7import os 

8from PyQt6.QtCore import QSettings, QObject, pyqtSignal 

9from PyQt6.QtWidgets import QPushButton, QWidget 

10 

11 

12# Add the project path to PYTHONPATH 

13sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 

14 

15class SignalEmitter(QObject): 

16 """Helper class for mocked signals""" 

17 selected_files = pyqtSignal(list) 

18 language_changed = pyqtSignal(str) 

19 

20@pytest.fixture 

21def signal_emitter(): 

22 return SignalEmitter() 

23 

24@pytest.fixture 

25def temp_workspace(): 

26 """Create a temporary directory for the workspace""" 

27 temp_dir = tempfile.mkdtemp() 

28 os.makedirs(os.path.join(temp_dir, "pipeline"), exist_ok=True) 

29 with open(os.path.join(temp_dir, "pipeline", "output.txt"), "w") as f: 

30 f.write("output content") 

31 os.makedirs(os.path.join(temp_dir, "sub-01", "pet")) 

32 with open(os.path.join(temp_dir, "sub-01", "pet", "pet.nii"), "w") as f: 

33 f.write("PET") 

34 os.makedirs(os.path.join(temp_dir, "sub-01", "anat")) 

35 with open(os.path.join(temp_dir, "sub-01", "anat", "T1w.nii"), "w") as f: 

36 f.write("T1w") 

37 os.makedirs(os.path.join(temp_dir, "sub-02")) 

38 

39 manual_dir = os.path.join(temp_dir, "derivatives", "manual_masks", "sub-01", "anat") 

40 os.makedirs(manual_dir) 

41 with open(os.path.join(manual_dir, "mask.nii.gz"), "w") as f: 

42 f.write("test_mask") 

43 

44 with open(os.path.join(temp_dir, "test.txt"), "w") as f: 

45 f.write("test content") 

46 with open(os.path.join(temp_dir, "brain.nii"), "w") as f: 

47 f.write("nifti data") 

48 with open(os.path.join(temp_dir, "brain.json"), "w") as f: 

49 f.write("{}") 

50 # NIfTI.gz file with JSON 

51 with open(os.path.join(temp_dir, "scan.nii.gz"), "w") as f: 

52 f.write("compressed") 

53 with open(os.path.join(temp_dir, "scan.json"), "w") as f: 

54 f.write("{}") 

55 

56 yield temp_dir 

57 shutil.rmtree(temp_dir, ignore_errors=True) 

58 

59@pytest.fixture 

60def mock_context(temp_workspace, signal_emitter): 

61 """Create a mock context with all necessary components""" 

62 def create_buttons(): 

63 # Return real QPushButtons instead of Mock 

64 return QPushButton("Next"), QPushButton("Back") 

65 

66 context = { 

67 "language_changed": signal_emitter.language_changed, 

68 "selected_files_signal": signal_emitter.selected_files, 

69 "settings": QSettings("TestOrg", "TestApp"), 

70 "workspace_path": temp_workspace, 

71 "create_buttons": create_buttons, 

72 "import_page": Mock(spec=['open_folder_dialog']), 

73 "update_main_buttons": Mock(), 

74 "return_to_import": Mock(), 

75 "main_window": Mock(), 

76 "history": [], 

77 "open_nifti_viewer": Mock(), 

78 "tree_view": Mock() 

79 } 

80 return context 

81 

82@pytest.fixture(autouse=True) 

83def clean_settings(): 

84 """Clean settings before and after each test""" 

85 settings = QSettings("TestOrg", "TestApp") 

86 settings.clear() 

87 yield 

88 settings.clear() 

89 

90@pytest.fixture 

91def mock_logger(): 

92 """Mock for the logger""" 

93 from unittest.mock import Mock 

94 logger = Mock() 

95 logger.info = Mock() 

96 logger.error = Mock() 

97 logger.debug = Mock() 

98 logger.warning = Mock() 

99 return logger 

100 

101class DummySignal: 

102 def connect(self, slot): 

103 pass 

104 

105class DummyFileSelectorWidget(QWidget): 

106 """Mock of FileSelectorWidget used in tests.""" 

107 def __init__(self, *args, **kwargs): 

108 super().__init__() 

109 self.has_file = DummySignal() 

110 self._selected_files = [] 

111 # Mock for verification in tests 

112 self.clear_selected_files = Mock() 

113 

114 def get_selected_files(self): 

115 return self._selected_files 

116 

117 def reset(self): 

118 self._selected_files.clear() 

119 

120 def setEnabled(self, value: bool): 

121 pass 

122 

123@pytest.fixture 

124def mock_file_selector(): 

125 """Patch FileSelectorWidget with a compatible mock.""" 

126 with patch( 

127 "main.ui.skull_stripping_page.FileSelectorWidget", 

128 return_value=DummyFileSelectorWidget() 

129 ) as mock: 

130 yield mock 

131 

132@pytest.fixture 

133def mock_file_selector_mask(): 

134 """Patch FileSelectorWidget in ui_mask_selection_page module with a valid widget.""" 

135 with patch( 

136 "main.ui.nifti_mask_selection_page.FileSelectorWidget", 

137 return_value=DummyFileSelectorWidget() 

138 ) as mock: 

139 yield mock 

140 

141@pytest.fixture 

142def mock_file_selector_dl(): 

143 """Patch FileSelectorWidget in the ui_dl_selection_page module.""" 

144 with patch( 

145 "main.ui.dl_selection_page.FileSelectorWidget", 

146 return_value=DummyFileSelectorWidget() 

147 ) as mock: 

148 yield mock 

149 

150# Pytest configuration 

151def pytest_configure(config): 

152 """Global pytest configuration""" 

153 config.addinivalue_line( 

154 "markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')" 

155 ) 

156 config.addinivalue_line( 

157 "markers", "integration: marks tests as integration tests" 

158 ) 

159 config.addinivalue_line( 

160 "markers", "unit: marks tests as unit tests" 

161 )