1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
21 from configobj
import ConfigObj
24 from validate
import VdtTypeError
26 from mediagoblin
import mg_globals
27 from mediagoblin
.init
.plugins
import setup_plugins
28 from mediagoblin
.init
.config
import read_mediagoblin_config
29 from mediagoblin
.gmg_commands
.assetlink
import link_plugin_assets
30 from mediagoblin
.tools
import pluginapi
31 from mediagoblin
.tests
.tools
import get_app
32 from mediagoblin
.tools
.common
import CollectingPrinter
35 def with_cleanup(*modules_to_delete
):
36 def _with_cleanup(fun
):
37 """Wrapper that saves and restores mg_globals"""
38 def _with_cleanup_inner(*args
, **kwargs
):
39 old_app_config
= mg_globals
.app_config
40 old_global_config
= mg_globals
.global_config
41 # Need to delete icky modules before and after so as to make
42 # sure things work correctly.
43 for module
in modules_to_delete
:
45 del sys
.modules
[module
]
48 # The plugin cache gets populated as a side-effect of
49 # importing, so it's best to clear it before and after a test.
50 pman
= pluginapi
.PluginManager()
53 return fun(*args
, **kwargs
)
55 mg_globals
.app_config
= old_app_config
56 mg_globals
.global_config
= old_global_config
57 # Need to delete icky modules before and after so as to make
58 # sure things work correctly.
59 for module
in modules_to_delete
:
61 del sys
.modules
[module
]
66 _with_cleanup_inner
.__name
__ = fun
.__name
__
67 return _with_cleanup_inner
71 def build_config(sections
):
72 """Builds a ConfigObj object with specified data
74 :arg sections: list of ``(section_name, section_data,
75 subsection_list)`` tuples where section_data is a dict and
76 subsection_list is a list of ``(section_name, section_data,
77 subsection_list)``, ...
82 ... ('mediagoblin', {'key1': 'val1'}, []),
83 ... ('section2', {}, [
84 ... ('subsection1', {}, [])
90 def _iter_section(cfg
, section_list
):
91 for section_name
, data
, subsection_list
in section_list
:
92 cfg
[section_name
] = data
93 _iter_section(cfg
[section_name
], subsection_list
)
95 _iter_section(cfg
, sections
)
100 def test_no_plugins():
101 """Run setup_plugins with no plugins in config"""
102 cfg
= build_config([('mediagoblin', {}, [])])
103 mg_globals
.app_config
= cfg
['mediagoblin']
104 mg_globals
.global_config
= cfg
106 pman
= pluginapi
.PluginManager()
109 # Make sure we didn't load anything.
110 assert len(pman
.plugins
) == 0
113 @with_cleanup('mediagoblin.plugins.sampleplugin')
114 def test_one_plugin():
115 """Run setup_plugins with a single working plugin"""
117 ('mediagoblin', {}, []),
119 ('mediagoblin.plugins.sampleplugin', {}, [])
123 mg_globals
.app_config
= cfg
['mediagoblin']
124 mg_globals
.global_config
= cfg
126 pman
= pluginapi
.PluginManager()
129 # Make sure we only found one plugin
130 assert len(pman
.plugins
) == 1
131 # Make sure the plugin is the one we think it is.
132 assert pman
.plugins
[0] == 'mediagoblin.plugins.sampleplugin'
133 # Make sure there was one hook registered
134 assert len(pman
.hooks
) == 1
135 # Make sure _setup_plugin_called was called once
136 import mediagoblin
.plugins
.sampleplugin
137 assert mediagoblin
.plugins
.sampleplugin
._setup
_plugin
_called
== 1
140 @with_cleanup('mediagoblin.plugins.sampleplugin')
141 def test_same_plugin_twice():
142 """Run setup_plugins with a single working plugin twice"""
144 ('mediagoblin', {}, []),
146 ('mediagoblin.plugins.sampleplugin', {}, []),
147 ('mediagoblin.plugins.sampleplugin', {}, []),
151 mg_globals
.app_config
= cfg
['mediagoblin']
152 mg_globals
.global_config
= cfg
154 pman
= pluginapi
.PluginManager()
157 # Make sure we only found one plugin
158 assert len(pman
.plugins
) == 1
159 # Make sure the plugin is the one we think it is.
160 assert pman
.plugins
[0] == 'mediagoblin.plugins.sampleplugin'
161 # Make sure there was one hook registered
162 assert len(pman
.hooks
) == 1
163 # Make sure _setup_plugin_called was called once
164 import mediagoblin
.plugins
.sampleplugin
165 assert mediagoblin
.plugins
.sampleplugin
._setup
_plugin
_called
== 1
169 def test_disabled_plugin():
170 """Run setup_plugins with a single working plugin twice"""
172 ('mediagoblin', {}, []),
174 ('-mediagoblin.plugins.sampleplugin', {}, []),
178 mg_globals
.app_config
= cfg
['mediagoblin']
179 mg_globals
.global_config
= cfg
181 pman
= pluginapi
.PluginManager()
184 # Make sure we didn't load the plugin
185 assert len(pman
.plugins
) == 0
188 CONFIG_ALL_CALLABLES
= [
189 ('mediagoblin', {}, []),
191 ('mediagoblin.tests.testplugins.callables1', {}, []),
192 ('mediagoblin.tests.testplugins.callables2', {}, []),
193 ('mediagoblin.tests.testplugins.callables3', {}, []),
199 def test_hook_handle():
201 Test the hook_handle method
203 cfg
= build_config(CONFIG_ALL_CALLABLES
)
205 mg_globals
.app_config
= cfg
['mediagoblin']
206 mg_globals
.global_config
= cfg
210 # Just one hook provided
212 assert pluginapi
.hook_handle(
213 "just_one", call_log
) == "Called just once"
214 assert call_log
== ["expect this one call"]
216 # Nothing provided and unhandled not okay
218 pluginapi
.hook_handle(
219 "nothing_handling", call_log
) == None
220 assert call_log
== []
222 # Nothing provided and unhandled okay
224 assert pluginapi
.hook_handle(
225 "nothing_handling", call_log
, unhandled_okay
=True) is None
226 assert call_log
== []
228 # Multiple provided, go with the first!
230 assert pluginapi
.hook_handle(
231 "multi_handle", call_log
) == "the first returns"
232 assert call_log
== ["Hi, I'm the first"]
234 # Multiple provided, one has CantHandleIt
236 assert pluginapi
.hook_handle(
237 "multi_handle_with_canthandle",
238 call_log
) == "the second returns"
239 assert call_log
== ["Hi, I'm the second"]
243 def test_hook_runall():
245 Test the hook_runall method
247 cfg
= build_config(CONFIG_ALL_CALLABLES
)
249 mg_globals
.app_config
= cfg
['mediagoblin']
250 mg_globals
.global_config
= cfg
254 # Just one hook, check results
256 assert pluginapi
.hook_runall(
257 "just_one", call_log
) == ["Called just once"]
258 assert call_log
== ["expect this one call"]
260 # None provided, check results
262 assert pluginapi
.hook_runall(
263 "nothing_handling", call_log
) == []
264 assert call_log
== []
266 # Multiple provided, check results
268 assert pluginapi
.hook_runall(
269 "multi_handle", call_log
) == [
271 "the second returns",
276 "Hi, I'm the second",
279 # Multiple provided, one has CantHandleIt, check results
281 assert pluginapi
.hook_runall(
282 "multi_handle_with_canthandle", call_log
) == [
283 "the second returns",
287 "Hi, I'm the second",
292 def test_hook_transform():
294 Test the hook_transform method
296 cfg
= build_config(CONFIG_ALL_CALLABLES
)
298 mg_globals
.app_config
= cfg
['mediagoblin']
299 mg_globals
.global_config
= cfg
303 assert pluginapi
.hook_transform(
304 "expand_tuple", (-1, 0)) == (-1, 0, 1, 2, 3)
307 def test_plugin_config():
309 Make sure plugins can set up their own config
311 config
, validation_result
= read_mediagoblin_config(
312 pkg_resources
.resource_filename(
313 'mediagoblin.tests', 'appconfig_plugin_specs.ini'))
315 pluginspec_section
= config
['plugins'][
316 'mediagoblin.tests.testplugins.pluginspec']
317 assert pluginspec_section
['some_string'] == 'not blork'
318 assert pluginspec_section
['dont_change_me'] == 'still the default'
320 # Make sure validation works... this should be an error
324 'mediagoblin.tests.testplugins.pluginspec'][
328 # the callables thing shouldn't really have anything though.
329 assert len(config
['plugins'][
330 'mediagoblin.tests.testplugins.callables1']) == 0
334 def context_modified_app(request
):
336 Get a MediaGoblin app fixture using appconfig_context_modified.ini
340 mgoblin_config
=pkg_resources
.resource_filename(
341 'mediagoblin.tests', 'appconfig_context_modified.ini'))
344 def test_modify_context(context_modified_app
):
346 Test that we can modify both the view/template specific and
347 global contexts for templates.
349 # Specific thing passed into a page
350 result
= context_modified_app
.get("/modify_context/specific/")
351 assert result
.body
.strip() == b
"""Specific page!
353 specific thing: in yer specificpage
354 global thing: globally appended!
356 doubleme: happyhappy"""
358 # General test, should have global context variable only
359 result
= context_modified_app
.get("/modify_context/")
360 assert result
.body
.strip() == b
"""General page!
362 global thing: globally appended!
368 def static_plugin_app(request
):
370 Get a MediaGoblin app fixture using appconfig_static_plugin.ini
374 mgoblin_config
=pkg_resources
.resource_filename(
375 'mediagoblin.tests', 'appconfig_static_plugin.ini'))
378 def test_plugin_assetlink(static_plugin_app
):
380 Test that the assetlink command works correctly
382 linked_assets_dir
= mg_globals
.app_config
['plugin_linked_assets_dir']
383 plugin_link_dir
= os
.path
.join(
384 linked_assets_dir
.rstrip(os
.path
.sep
),
387 plugin_statics
= pluginapi
.hook_runall("static_setup")
388 assert len(plugin_statics
) == 1
389 plugin_static
= plugin_statics
[0]
392 printer
= CollectingPrinter()
395 plugin_static
, linked_assets_dir
, printer
)
399 # it shouldn't exist yet
400 assert not os
.path
.lexists(plugin_link_dir
)
402 # link dir doesn't exist, link it
403 result
= run_assetlink().collection
[0]
405 'Linked asset directory for plugin "staticstuff":\n %s\nto:\n %s\n' % (
406 plugin_static
.file_path
.rstrip(os
.path
.sep
),
408 assert os
.path
.lexists(plugin_link_dir
)
409 assert os
.path
.islink(plugin_link_dir
)
410 assert os
.path
.realpath(plugin_link_dir
) == plugin_static
.file_path
412 # link dir exists, leave it alone
413 # (and it should exist still since we just ran it..)
414 result
= run_assetlink().collection
[0]
415 assert result
== 'Skipping "staticstuff"; already set up.\n'
416 assert os
.path
.lexists(plugin_link_dir
)
417 assert os
.path
.islink(plugin_link_dir
)
418 assert os
.path
.realpath(plugin_link_dir
) == plugin_static
.file_path
420 # link dir exists, is a symlink to somewhere else (re-link)
421 junk_file_path
= os
.path
.join(
422 linked_assets_dir
.rstrip(os
.path
.sep
),
424 with
open(junk_file_path
, 'w') as junk_file
:
425 junk_file
.write('barf')
427 os
.unlink(plugin_link_dir
)
428 os
.symlink(junk_file_path
, plugin_link_dir
)
430 result
= run_assetlink().combined_string
431 assert result
== """Old link found for "staticstuff"; removing.
432 Linked asset directory for plugin "staticstuff":
436 """ % (plugin_static
.file_path
.rstrip(os
.path
.sep
), plugin_link_dir
)
437 assert os
.path
.lexists(plugin_link_dir
)
438 assert os
.path
.islink(plugin_link_dir
)
439 assert os
.path
.realpath(plugin_link_dir
) == plugin_static
.file_path
441 # link dir exists, but is a non-symlink
442 os
.unlink(plugin_link_dir
)
443 with
open(plugin_link_dir
, 'w') as clobber_file
:
444 clobber_file
.write('clobbered!')
446 result
= run_assetlink().collection
[0]
447 assert result
== 'Could not link "staticstuff": %s exists and is not a symlink\n' % (
450 with
open(plugin_link_dir
, 'r') as clobber_file
:
451 assert clobber_file
.read() == 'clobbered!'
454 def test_plugin_staticdirect(static_plugin_app
):
456 Test that the staticdirect utilities pull up the right things
459 static_plugin_app
.get('/staticstuff/').body
.decode())
461 assert len(result
) == 2
463 assert result
['mgoblin_bunny_pic'] == '/test_static/images/bunny_pic.png'
464 assert result
['plugin_bunny_css'] == \
465 '/plugin_static/staticstuff/css/bunnify.css'