Simple "staticstuff" plugin for writing unit tests on plugin static asset linking.
[mediagoblin.git] / mediagoblin / tests / test_pluginapi.py
CommitLineData
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
17import sys
70c06195 18
29b6f917 19from configobj import ConfigObj
70c06195 20import pytest
d3604e29
CAW
21import pkg_resources
22from validate import VdtTypeError
70c06195 23
29b6f917
WKG
24from mediagoblin import mg_globals
25from mediagoblin.init.plugins import setup_plugins
d3604e29 26from mediagoblin.init.config import read_mediagoblin_config
29b6f917 27from mediagoblin.tools import pluginapi
38103094 28from mediagoblin.tests.tools import get_app
29b6f917
WKG
29
30
31def 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
67def 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()
96def 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
110def 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
137def 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()
165def 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
184CONFIG_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 195def 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 239def 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()
288def 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
303def 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()
330def 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 340def 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
349specific thing: in yer specificpage
350global thing: globally appended!
a1099bba
CAW
351something: orother
352doubleme: 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
358global thing: globally appended!
a1099bba
CAW
359lol: cats
360doubleme: joyjoy"""