Commit | Line | Data |
---|---|---|
29b6f917 WKG |
1 | # GNU MediaGoblin -- federated, autonomous media hosting |
2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. | |
3 | # | |
4 | # This program is free software: you can redistribute it and/or modify | |
5 | # it under the terms of the GNU Affero General Public License as published by | |
6 | # the Free Software Foundation, either version 3 of the License, or | |
7 | # (at your option) any later version. | |
8 | # | |
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU Affero General Public License for more details. | |
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | import sys | |
70c06195 | 18 | |
29b6f917 | 19 | from configobj import ConfigObj |
70c06195 | 20 | import pytest |
d3604e29 CAW |
21 | import pkg_resources |
22 | from validate import VdtTypeError | |
70c06195 | 23 | |
29b6f917 WKG |
24 | from mediagoblin import mg_globals |
25 | from mediagoblin.init.plugins import setup_plugins | |
d3604e29 | 26 | from mediagoblin.init.config import read_mediagoblin_config |
29b6f917 | 27 | from mediagoblin.tools import pluginapi |
38103094 | 28 | from mediagoblin.tests.tools import get_app |
29b6f917 WKG |
29 | |
30 | ||
31 | def with_cleanup(*modules_to_delete): | |
32 | def _with_cleanup(fun): | |
33 | """Wrapper that saves and restores mg_globals""" | |
34 | def _with_cleanup_inner(*args, **kwargs): | |
35 | old_app_config = mg_globals.app_config | |
36 | old_global_config = mg_globals.global_config | |
37 | # Need to delete icky modules before and after so as to make | |
38 | # sure things work correctly. | |
39 | for module in modules_to_delete: | |
40 | try: | |
41 | del sys.modules[module] | |
42 | except KeyError: | |
43 | pass | |
44 | # The plugin cache gets populated as a side-effect of | |
45 | # importing, so it's best to clear it before and after a test. | |
05e007c1 WKG |
46 | pman = pluginapi.PluginManager() |
47 | pman.clear() | |
29b6f917 WKG |
48 | try: |
49 | return fun(*args, **kwargs) | |
50 | finally: | |
51 | mg_globals.app_config = old_app_config | |
52 | mg_globals.global_config = old_global_config | |
53 | # Need to delete icky modules before and after so as to make | |
54 | # sure things work correctly. | |
55 | for module in modules_to_delete: | |
56 | try: | |
57 | del sys.modules[module] | |
58 | except KeyError: | |
59 | pass | |
05e007c1 | 60 | pman.clear() |
29b6f917 WKG |
61 | |
62 | _with_cleanup_inner.__name__ = fun.__name__ | |
63 | return _with_cleanup_inner | |
64 | return _with_cleanup | |
65 | ||
66 | ||
67 | def build_config(sections): | |
68 | """Builds a ConfigObj object with specified data | |
69 | ||
70 | :arg sections: list of ``(section_name, section_data, | |
71 | subsection_list)`` tuples where section_data is a dict and | |
72 | subsection_list is a list of ``(section_name, section_data, | |
73 | subsection_list)``, ... | |
74 | ||
75 | For example: | |
76 | ||
77 | >>> build_config([ | |
78 | ... ('mediagoblin', {'key1': 'val1'}, []), | |
79 | ... ('section2', {}, [ | |
80 | ... ('subsection1', {}, []) | |
81 | ... ]) | |
82 | ... ]) | |
83 | """ | |
84 | cfg = ConfigObj() | |
85 | cfg.filename = 'foo' | |
86 | def _iter_section(cfg, section_list): | |
87 | for section_name, data, subsection_list in section_list: | |
88 | cfg[section_name] = data | |
89 | _iter_section(cfg[section_name], subsection_list) | |
90 | ||
91 | _iter_section(cfg, sections) | |
92 | return cfg | |
93 | ||
94 | ||
95 | @with_cleanup() | |
96 | def test_no_plugins(): | |
97 | """Run setup_plugins with no plugins in config""" | |
98 | cfg = build_config([('mediagoblin', {}, [])]) | |
99 | mg_globals.app_config = cfg['mediagoblin'] | |
100 | mg_globals.global_config = cfg | |
101 | ||
05e007c1 | 102 | pman = pluginapi.PluginManager() |
29b6f917 WKG |
103 | setup_plugins() |
104 | ||
105 | # Make sure we didn't load anything. | |
7d503a89 | 106 | assert len(pman.plugins) == 0 |
29b6f917 WKG |
107 | |
108 | ||
05e007c1 | 109 | @with_cleanup('mediagoblin.plugins.sampleplugin') |
29b6f917 WKG |
110 | def test_one_plugin(): |
111 | """Run setup_plugins with a single working plugin""" | |
112 | cfg = build_config([ | |
113 | ('mediagoblin', {}, []), | |
114 | ('plugins', {}, [ | |
115 | ('mediagoblin.plugins.sampleplugin', {}, []) | |
116 | ]) | |
117 | ]) | |
118 | ||
119 | mg_globals.app_config = cfg['mediagoblin'] | |
120 | mg_globals.global_config = cfg | |
121 | ||
05e007c1 | 122 | pman = pluginapi.PluginManager() |
29b6f917 WKG |
123 | setup_plugins() |
124 | ||
05e007c1 | 125 | # Make sure we only found one plugin |
7d503a89 | 126 | assert len(pman.plugins) == 1 |
05e007c1 | 127 | # Make sure the plugin is the one we think it is. |
7d503a89 | 128 | assert pman.plugins[0] == 'mediagoblin.plugins.sampleplugin' |
05e007c1 | 129 | # Make sure there was one hook registered |
7d503a89 | 130 | assert len(pman.hooks) == 1 |
05e007c1 WKG |
131 | # Make sure _setup_plugin_called was called once |
132 | import mediagoblin.plugins.sampleplugin | |
7d503a89 | 133 | assert mediagoblin.plugins.sampleplugin._setup_plugin_called == 1 |
29b6f917 | 134 | |
29b6f917 | 135 | |
05e007c1 | 136 | @with_cleanup('mediagoblin.plugins.sampleplugin') |
29b6f917 WKG |
137 | def test_same_plugin_twice(): |
138 | """Run setup_plugins with a single working plugin twice""" | |
139 | cfg = build_config([ | |
140 | ('mediagoblin', {}, []), | |
141 | ('plugins', {}, [ | |
142 | ('mediagoblin.plugins.sampleplugin', {}, []), | |
143 | ('mediagoblin.plugins.sampleplugin', {}, []), | |
144 | ]) | |
145 | ]) | |
146 | ||
147 | mg_globals.app_config = cfg['mediagoblin'] | |
148 | mg_globals.global_config = cfg | |
149 | ||
05e007c1 | 150 | pman = pluginapi.PluginManager() |
29b6f917 WKG |
151 | setup_plugins() |
152 | ||
05e007c1 | 153 | # Make sure we only found one plugin |
7d503a89 | 154 | assert len(pman.plugins) == 1 |
05e007c1 | 155 | # Make sure the plugin is the one we think it is. |
7d503a89 | 156 | assert pman.plugins[0] == 'mediagoblin.plugins.sampleplugin' |
05e007c1 | 157 | # Make sure there was one hook registered |
7d503a89 | 158 | assert len(pman.hooks) == 1 |
05e007c1 WKG |
159 | # Make sure _setup_plugin_called was called once |
160 | import mediagoblin.plugins.sampleplugin | |
7d503a89 | 161 | assert mediagoblin.plugins.sampleplugin._setup_plugin_called == 1 |
05d8f314 WKG |
162 | |
163 | ||
164 | @with_cleanup() | |
165 | def test_disabled_plugin(): | |
166 | """Run setup_plugins with a single working plugin twice""" | |
167 | cfg = build_config([ | |
168 | ('mediagoblin', {}, []), | |
169 | ('plugins', {}, [ | |
170 | ('-mediagoblin.plugins.sampleplugin', {}, []), | |
171 | ]) | |
172 | ]) | |
173 | ||
174 | mg_globals.app_config = cfg['mediagoblin'] | |
175 | mg_globals.global_config = cfg | |
176 | ||
177 | pman = pluginapi.PluginManager() | |
178 | setup_plugins() | |
179 | ||
180 | # Make sure we didn't load the plugin | |
7d503a89 | 181 | assert len(pman.plugins) == 0 |
70c06195 CAW |
182 | |
183 | ||
12dccc45 E |
184 | CONFIG_ALL_CALLABLES = [ |
185 | ('mediagoblin', {}, []), | |
186 | ('plugins', {}, [ | |
187 | ('mediagoblin.tests.testplugins.callables1', {}, []), | |
188 | ('mediagoblin.tests.testplugins.callables2', {}, []), | |
189 | ('mediagoblin.tests.testplugins.callables3', {}, []), | |
190 | ]) | |
191 | ] | |
192 | ||
193 | ||
70c06195 | 194 | @with_cleanup() |
d1146101 | 195 | def test_hook_handle(): |
70c06195 | 196 | """ |
d1146101 | 197 | Test the hook_handle method |
70c06195 | 198 | """ |
12dccc45 | 199 | cfg = build_config(CONFIG_ALL_CALLABLES) |
70c06195 CAW |
200 | |
201 | mg_globals.app_config = cfg['mediagoblin'] | |
202 | mg_globals.global_config = cfg | |
203 | ||
204 | setup_plugins() | |
205 | ||
206 | # Just one hook provided | |
207 | call_log = [] | |
d1146101 | 208 | assert pluginapi.hook_handle( |
70c06195 CAW |
209 | "just_one", call_log) == "Called just once" |
210 | assert call_log == ["expect this one call"] | |
211 | ||
212 | # Nothing provided and unhandled not okay | |
213 | call_log = [] | |
d1146101 CAW |
214 | pluginapi.hook_handle( |
215 | "nothing_handling", call_log) == None | |
70c06195 CAW |
216 | assert call_log == [] |
217 | ||
218 | # Nothing provided and unhandled okay | |
219 | call_log = [] | |
d1146101 | 220 | assert pluginapi.hook_handle( |
70c06195 CAW |
221 | "nothing_handling", call_log, unhandled_okay=True) is None |
222 | assert call_log == [] | |
223 | ||
224 | # Multiple provided, go with the first! | |
225 | call_log = [] | |
d1146101 | 226 | assert pluginapi.hook_handle( |
cdc821eb | 227 | "multi_handle", call_log) == "the first returns" |
70c06195 CAW |
228 | assert call_log == ["Hi, I'm the first"] |
229 | ||
230 | # Multiple provided, one has CantHandleIt | |
231 | call_log = [] | |
d1146101 | 232 | assert pluginapi.hook_handle( |
70c06195 | 233 | "multi_handle_with_canthandle", |
cdc821eb | 234 | call_log) == "the second returns" |
70c06195 CAW |
235 | assert call_log == ["Hi, I'm the second"] |
236 | ||
237 | ||
238 | @with_cleanup() | |
d1146101 | 239 | def test_hook_runall(): |
70c06195 | 240 | """ |
d1146101 | 241 | Test the hook_runall method |
70c06195 | 242 | """ |
12dccc45 | 243 | cfg = build_config(CONFIG_ALL_CALLABLES) |
70c06195 CAW |
244 | |
245 | mg_globals.app_config = cfg['mediagoblin'] | |
246 | mg_globals.global_config = cfg | |
247 | ||
248 | setup_plugins() | |
249 | ||
250 | # Just one hook, check results | |
251 | call_log = [] | |
d1146101 CAW |
252 | assert pluginapi.hook_runall( |
253 | "just_one", call_log) == ["Called just once"] | |
70c06195 CAW |
254 | assert call_log == ["expect this one call"] |
255 | ||
256 | # None provided, check results | |
257 | call_log = [] | |
d1146101 | 258 | assert pluginapi.hook_runall( |
70c06195 CAW |
259 | "nothing_handling", call_log) == [] |
260 | assert call_log == [] | |
261 | ||
262 | # Multiple provided, check results | |
263 | call_log = [] | |
d1146101 | 264 | assert pluginapi.hook_runall( |
70c06195 CAW |
265 | "multi_handle", call_log) == [ |
266 | "the first returns", | |
267 | "the second returns", | |
268 | "the third returns", | |
269 | ] | |
270 | assert call_log == [ | |
271 | "Hi, I'm the first", | |
272 | "Hi, I'm the second", | |
273 | "Hi, I'm the third"] | |
274 | ||
275 | # Multiple provided, one has CantHandleIt, check results | |
276 | call_log = [] | |
d1146101 | 277 | assert pluginapi.hook_runall( |
70c06195 CAW |
278 | "multi_handle_with_canthandle", call_log) == [ |
279 | "the second returns", | |
280 | "the third returns", | |
281 | ] | |
282 | assert call_log == [ | |
283 | "Hi, I'm the second", | |
284 | "Hi, I'm the third"] | |
d1146101 CAW |
285 | |
286 | ||
a0e7699a CAW |
287 | @with_cleanup() |
288 | def test_hook_transform(): | |
289 | """ | |
290 | Test the hook_transform method | |
291 | """ | |
12dccc45 | 292 | cfg = build_config(CONFIG_ALL_CALLABLES) |
a0e7699a CAW |
293 | |
294 | mg_globals.app_config = cfg['mediagoblin'] | |
295 | mg_globals.global_config = cfg | |
296 | ||
297 | setup_plugins() | |
298 | ||
299 | assert pluginapi.hook_transform( | |
300 | "expand_tuple", (-1, 0)) == (-1, 0, 1, 2, 3) | |
d3604e29 CAW |
301 | |
302 | ||
303 | def test_plugin_config(): | |
304 | """ | |
305 | Make sure plugins can set up their own config | |
306 | """ | |
307 | config, validation_result = read_mediagoblin_config( | |
308 | pkg_resources.resource_filename( | |
309 | 'mediagoblin.tests', 'appconfig_plugin_specs.ini')) | |
310 | ||
311 | pluginspec_section = config['plugins'][ | |
312 | 'mediagoblin.tests.testplugins.pluginspec'] | |
313 | assert pluginspec_section['some_string'] == 'not blork' | |
314 | assert pluginspec_section['dont_change_me'] == 'still the default' | |
315 | ||
316 | # Make sure validation works... this should be an error | |
317 | assert isinstance( | |
318 | validation_result[ | |
319 | 'plugins'][ | |
320 | 'mediagoblin.tests.testplugins.pluginspec'][ | |
321 | 'some_int'], | |
322 | VdtTypeError) | |
323 | ||
324 | # the callables thing shouldn't really have anything though. | |
325 | assert len(config['plugins'][ | |
326 | 'mediagoblin.tests.testplugins.callables1']) == 0 | |
38103094 CAW |
327 | |
328 | ||
329 | @pytest.fixture() | |
330 | def context_modified_app(request): | |
5046ca24 CAW |
331 | """ |
332 | Get a MediaGoblin app fixture using appconfig_context_modified.ini | |
333 | """ | |
f7a5c7c7 | 334 | return get_app( |
38103094 CAW |
335 | request, |
336 | mgoblin_config=pkg_resources.resource_filename( | |
337 | 'mediagoblin.tests', 'appconfig_context_modified.ini')) | |
338 | ||
f7a5c7c7 | 339 | |
38103094 | 340 | def test_modify_context(context_modified_app): |
5046ca24 CAW |
341 | """ |
342 | Test that we can modify both the view/template specific and | |
343 | global contexts for templates. | |
344 | """ | |
f7a5c7c7 CAW |
345 | # Specific thing passed into a page |
346 | result = context_modified_app.get("/modify_context/specific/") | |
347 | assert result.body.strip() == """Specific page! | |
348 | ||
349 | specific thing: in yer specificpage | |
350 | global thing: globally appended! | |
a1099bba CAW |
351 | something: orother |
352 | doubleme: happyhappy""" | |
f7a5c7c7 CAW |
353 | |
354 | # General test, should have global context variable only | |
355 | result = context_modified_app.get("/modify_context/") | |
356 | assert result.body.strip() == """General page! | |
357 | ||
358 | global thing: globally appended! | |
a1099bba CAW |
359 | lol: cats |
360 | doubleme: joyjoy""" |