Coverage for nilearn/_utils/tests/test_helpers.py: 0%

88 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-20 10:58 +0200

1import warnings 

2from pathlib import Path 

3from unittest.mock import patch 

4 

5import pytest 

6 

7from nilearn._utils.helpers import ( 

8 _warn_deprecated_params, 

9 compare_version, 

10 is_kaleido_installed, 

11 is_matplotlib_installed, 

12 is_plotly_installed, 

13 rename_parameters, 

14 set_mpl_backend, 

15 stringify_path, 

16 transfer_deprecated_param_vals, 

17) 

18from nilearn._utils.testing import on_windows_with_old_mpl_and_new_numpy 

19 

20 

21def _mock_args_for_testing_replace_parameter(): 

22 """Create mock deprecated & replacement parameters for use \ 

23 with testing functions related to replace_parameters(). 

24 """ 

25 mock_kwargs_with_deprecated_params_used = { 

26 "unchanged_param_0": "unchanged_param_0_val", 

27 "deprecated_param_0": "deprecated_param_0_val", 

28 "deprecated_param_1": "deprecated_param_1_val", 

29 "unchanged_param_1": "unchanged_param_1_val", 

30 } 

31 replacement_params = { 

32 "deprecated_param_0": "replacement_param_0", 

33 "deprecated_param_1": "replacement_param_1", 

34 } 

35 return mock_kwargs_with_deprecated_params_used, replacement_params 

36 

37 

38@pytest.mark.skipif( 

39 on_windows_with_old_mpl_and_new_numpy(), 

40 reason="Old matplotlib not compatible with numpy 2.0 on windows.", 

41) 

42@pytest.mark.skipif( 

43 is_matplotlib_installed(), 

44 reason="Test requires matplotlib not to be installed.", 

45) 

46def test_should_raise_custom_warning_if_mpl_not_installed(): 

47 """Tests if, when provided, custom message is displayed together with 

48 default warning. 

49 """ 

50 warning = "This package requires nilearn.plotting package." 

51 with ( 

52 pytest.warns(UserWarning, match=warning + "\nSome dependencies"), 

53 pytest.raises( 

54 ModuleNotFoundError, match="No module named 'matplotlib'" 

55 ), 

56 ): 

57 set_mpl_backend(warning) 

58 

59 

60@pytest.mark.skipif( 

61 on_windows_with_old_mpl_and_new_numpy(), 

62 reason="Old matplotlib not compatible with numpy 2.0 on windows.", 

63) 

64@pytest.mark.skipif( 

65 is_matplotlib_installed(), 

66 reason="Test requires matplotlib not to be installed.", 

67) 

68def test_should_raise_warning_if_mpl_not_installed(): 

69 """Tests if default warning is displayed when no custom message is 

70 specified. 

71 """ 

72 with ( 

73 pytest.warns( 

74 UserWarning, match="Some dependencies of nilearn.plotting" 

75 ), 

76 pytest.raises( 

77 ModuleNotFoundError, match="No module named 'matplotlib'" 

78 ), 

79 ): 

80 set_mpl_backend() 

81 

82 

83@pytest.mark.skipif( 

84 not is_matplotlib_installed(), 

85 reason="Test requires matplotlib to be installed.", 

86) 

87@patch("matplotlib.use") 

88@patch("matplotlib.get_backend", side_effect=["backend_1", "backend_2"]) 

89def test_should_raise_warning_if_backend_changes(*_): 

90 # The backend values returned by matplotlib.get_backend are different. 

91 # Warning should be raised to inform user of the backend switch. 

92 with pytest.warns(UserWarning, match="Backend changed to backend_2..."): 

93 set_mpl_backend() 

94 

95 

96@pytest.mark.skipif( 

97 not is_matplotlib_installed(), 

98 reason="Test requires matplotlib to be installed.", 

99) 

100@patch("matplotlib.use") 

101@patch("matplotlib.get_backend", side_effect=["backend_1", "backend_1"]) 

102def test_should_not_raise_warning_if_backend_is_not_changed(*_): 

103 # The backend values returned by matplotlib.get_backend are identical. 

104 # Warning should not be raised. 

105 with warnings.catch_warnings(): 

106 warnings.simplefilter("error") 

107 set_mpl_backend() 

108 

109 

110@pytest.mark.skipif( 

111 not is_matplotlib_installed(), 

112 reason="Test requires matplotlib to be installed.", 

113) 

114@patch( 

115 "matplotlib.use", side_effect=[Exception("Failed to switch backend"), True] 

116) 

117def test_should_switch_to_agg_backend_if_current_backend_fails(use_mock): 

118 # First call to `matplotlib.use` raises an exception, hence the default Agg 

119 # backend should be triggered 

120 set_mpl_backend() 

121 

122 assert use_mock.call_count == 2 

123 # Check that the most recent call to `matplotlib.use` has arg `Agg` 

124 use_mock.assert_called_with("Agg") 

125 

126 

127@pytest.mark.skipif( 

128 not is_matplotlib_installed(), 

129 reason="Test requires matplotlib to be installed.", 

130) 

131@patch("matplotlib.__version__", "0.0.0") 

132def test_should_raise_import_error_for_version_check(): 

133 with pytest.raises(ImportError, match="A matplotlib version of at least"): 

134 set_mpl_backend() 

135 

136 

137def test_rename_parameters(): 

138 """Test deprecated mock parameters in a mock function. 

139 

140 Checks that the deprecated parameters transfer their values correctly 

141 to replacement parameters and all deprecation warning are raised as 

142 expected. 

143 """ 

144 mock_input, replacement_params = _mock_args_for_testing_replace_parameter() 

145 expected_output = ("dp0", "dp1", "up0", "up1") 

146 expected_warnings = [ 

147 ( 

148 'The parameter "deprecated_param_0" will be removed in 0.6.1rc ' 

149 "release of other_lib. " 

150 'Please use the parameter "replacement_param_0" instead.' 

151 ), 

152 ( 

153 'The parameter "deprecated_param_1" will be removed in 0.6.1rc ' 

154 "release of other_lib. " 

155 'Please use the parameter "replacement_param_1" instead.' 

156 ), 

157 ] 

158 

159 @rename_parameters( 

160 replacement_params, 

161 "0.6.1rc", 

162 "other_lib", 

163 ) 

164 def mock_function( 

165 replacement_param_0, 

166 replacement_param_1, 

167 unchanged_param_0, 

168 unchanged_param_1, 

169 ): 

170 return ( 

171 replacement_param_0, 

172 replacement_param_1, 

173 unchanged_param_0, 

174 unchanged_param_1, 

175 ) 

176 

177 with warnings.catch_warnings(record=True) as raised_warnings: 

178 actual_output = mock_function( 

179 deprecated_param_0="dp0", 

180 deprecated_param_1="dp1", 

181 unchanged_param_0="up0", 

182 unchanged_param_1="up1", 

183 ) 

184 

185 assert actual_output == expected_output 

186 

187 expected_warnings.sort() 

188 raised_warnings.sort(key=lambda mem: str(mem.message)) 

189 for raised_warning_, expected_warning_ in zip( 

190 raised_warnings, expected_warnings 

191 ): 

192 assert raised_warning_.category is DeprecationWarning 

193 assert str(raised_warning_.message) == expected_warning_ 

194 

195 

196def test_transfer_deprecated_param_vals(): 

197 """Check that values assigned to deprecated parameters are \ 

198 correctly reassigned to the replacement parameters. 

199 """ 

200 mock_input, replacement_params = _mock_args_for_testing_replace_parameter() 

201 expected_output = { 

202 "unchanged_param_0": "unchanged_param_0_val", 

203 "replacement_param_0": "deprecated_param_0_val", 

204 "replacement_param_1": "deprecated_param_1_val", 

205 "unchanged_param_1": "unchanged_param_1_val", 

206 } 

207 actual_output = transfer_deprecated_param_vals( 

208 replacement_params, 

209 mock_input, 

210 ) 

211 assert actual_output == expected_output 

212 

213 

214def test_future_warn_deprecated_params(): 

215 """Check that the correct warning is displayed.""" 

216 mock_input, replacement_params = _mock_args_for_testing_replace_parameter() 

217 expected_warnings = [ 

218 ( 

219 'The parameter "deprecated_param_0" will be removed in sometime ' 

220 "release of somelib. " 

221 'Please use the parameter "replacement_param_0" instead.' 

222 ), 

223 ( 

224 'The parameter "deprecated_param_1" will be removed in sometime ' 

225 "release of somelib. " 

226 'Please use the parameter "replacement_param_1" instead.' 

227 ), 

228 ] 

229 with warnings.catch_warnings(record=True) as raised_warnings: 

230 _warn_deprecated_params( 

231 replacement_params, 

232 end_version="sometime", 

233 lib_name="somelib", 

234 kwargs=mock_input, 

235 ) 

236 expected_warnings.sort() 

237 raised_warnings.sort(key=lambda mem: str(mem.message)) 

238 for raised_warning_, expected_warning_ in zip( 

239 raised_warnings, expected_warnings 

240 ): 

241 assert raised_warning_.category is DeprecationWarning 

242 assert str(raised_warning_.message) == expected_warning_ 

243 

244 

245@pytest.mark.parametrize( 

246 "version_a,operator,version_b", 

247 [ 

248 ("0.1.0", ">", "0.0.1"), 

249 ("0.1.0", ">=", "0.0.1"), 

250 ("0.1", "==", "0.1.0"), 

251 ("0.0.0", "<", "0.1.0"), 

252 ("1.0", "!=", "0.1.0"), 

253 ], 

254) 

255def test_compare_version(version_a, operator, version_b): 

256 assert compare_version(version_a, operator, version_b) 

257 

258 

259def test_compare_version_error(): 

260 with pytest.raises( 

261 ValueError, 

262 match="'compare_version' received an unexpected operator <>.", 

263 ): 

264 compare_version("0.1.0", "<>", "1.1.0") 

265 

266 

267def test_is_plotly_installed(): 

268 is_plotly_installed() 

269 

270 

271def test_is_kaleido_installed(): 

272 is_kaleido_installed() 

273 

274 

275def test_stringify_path(): 

276 assert isinstance(stringify_path(Path("foo") / "bar"), str) 

277 assert stringify_path([]) == []