1
2# Copyright 2008-2013 Jaap Karssenberg <jaap.karssenberg@gmail.com>
3
4import tests
5
6import os
7
8from zim.plugins import *
9
10from tests.mainwindow import setUpMainWindow
11
12import zim.plugins
13assert len(zim.plugins.__path__) > 1 # test __path__ magic
14zim.plugins.__path__ = [os.path.abspath('./zim/plugins')] # set back default search path
15
16
17class TestPluginClasses(tests.TestCase):
18	'''Test case to check coding and documentation of plugin classes'''
19
20	def runTest(self):
21		plugins = PluginManager.list_installed_plugins()
22		self.assertTrue(len(plugins) > 10)
23		self.assertTrue('spell' in plugins)
24		self.assertTrue('linkmap' in plugins)
25
26		pluginindex = tests.ZIM_DATA_FOLDER.file('manual/Plugins.txt').read()
27
28		seen = {
29			'name': set(),
30			'description': set(),
31			'help': set(),
32		}
33		for name in plugins:
34			#~ print('>>', name)
35			klass = PluginManager.get_plugin_class(name)
36
37			# test plugin info
38			for key in ('name', 'description', 'author'):
39				self.assertTrue(
40					klass.plugin_info.get(key),
41					'Plugin %s misses info field \'%s\'' % (name, key)
42				)
43
44			for key in ('name', 'description', 'help'):
45				self.assertIn(key, klass.plugin_info, 'Plugin %s missing "%s"' % (name, key))
46				value = klass.plugin_info[key]
47				self.assertFalse(
48					value in seen[key],
49					'Value for \'%s\' in %s seen before - copy-paste error ?' % (key, name)
50				)
51				seen[key].add(value)
52
53			# test manual page present and at least documents preferences
54			page = klass.plugin_info['help']
55			self.assertTrue(page.startswith('Plugins:'), 'Help page for %s not valid' % name)
56
57			rellink = "+%s" % page[8:]
58			self.assertIn(rellink, pluginindex, 'Missing links "%s" in manual/Plugins.txt' % rellink)
59
60			file = tests.ZIM_DATA_FOLDER.file('manual/' + page.replace(':', '/').replace(' ', '_') + '.txt')
61			self.assertTrue(file.exists(), 'Missing file: %s' % file)
62
63			manual = file.read()
64			ignore = getattr(klass, 'hide_preferences', [])
65			prefs = [p for p in klass.plugin_preferences if not p[0] in ignore]
66			props = [p for p in klass.plugin_notebook_properties]
67			for pref in prefs + props:
68				label = pref[2]
69				if '\n' in label:
70					label, x = label.split('\n', 1)
71					label = label.rstrip(',')
72				self.assertTrue(label in manual, 'Preference or property "%s" for %s plugin not documented in manual page' % (label, name))
73
74			# test dependencies data
75			dep = klass.check_dependencies()
76			self.assertTrue(isinstance(dep, tuple))
77			check, dep = dep
78			self.assertTrue(isinstance(check, bool))
79			self.assertTrue(isinstance(dep, list))
80			for i in range(len(dep)):
81				self.assertTrue(isinstance(dep[i], tuple))
82				self.assertTrue(isinstance(dep[i][0], str))
83				self.assertTrue(isinstance(dep[i][1], bool))
84				self.assertTrue(isinstance(dep[i][2], bool))
85
86
87class TestPluginManager(tests.TestCase):
88	'''Test case for TestManager infrastructure'''
89
90	def testLoadAndRemovePlugin(self):
91		manager = PluginManager()
92		self.assertEqual(len(manager), 0)
93		self.assertEqual(list(manager), [])
94
95		obj = manager.load_plugin('journal')
96		self.assertEqual(len(manager), 1)
97		self.assertEqual(list(manager), ['journal'])
98		self.assertEqual(manager['journal'], obj)
99
100		obj1 = manager.load_plugin('journal') # redundant call
101		self.assertEqual(obj1, obj)
102		self.assertEqual(len(manager), 1)
103
104		manager.remove_plugin('journal')
105		self.assertEqual(len(manager), 0)
106		self.assertEqual(list(manager), [])
107		self.assertRaises(KeyError, manager.__getitem__, 'journal')
108
109		manager.remove_plugin('journal') # redundant call
110
111	def testLoadNonExistingPlugin(self):
112		manager = PluginManager()
113		self.assertRaises(ImportError, manager.load_plugin, 'nonexistingplugin')
114
115
116class TestPlugins(tests.TestCase):
117	'''Test case to initiate all (loadable) plugins and load some extensions'''
118
119	def runTest(self):
120		preferences = ConfigManager.get_config_dict('preferences.conf')
121		manager = PluginManager()
122		self.assertFalse(preferences.modified)
123		for name in PluginManager.list_installed_plugins():
124			klass = PluginManager.get_plugin_class(name)
125			if klass.check_dependencies_ok():
126				manager.load_plugin(name)
127				self.assertIn(name, manager)
128
129				self.assertFalse(preferences.modified,
130					'Plugin "%s" modified the preferences while loading' % name)
131
132		self.assertTrue(len(manager) > 3)
133
134		for i, name in enumerate(manager):
135			manager[name].preferences.emit('changed')
136				# Checking for exceptions and infinite recursion
137
138		self.assertTrue(i > 0)
139		#~ self.assertTrue(preferences.modified)
140			# If "False" the check while loading the plugins is not valid
141			# FIXME this detection is broken due to autosave in ConfigManager ...
142
143		notebook = self.setUpNotebook(content=tests.FULL_NOTEBOOK)
144		self.assertGreaterEqual(len(notebook.__zim_extension_objects__), 2)
145			# At least journal and tasklist should load
146
147		mainwindow = setUpMainWindow(notebook)
148		self.assertGreaterEqual(len(mainwindow.pageview.__zim_extension_objects__), 3)
149			# enough plugins without dependencies here
150
151		for i, name in enumerate(manager):
152			manager[name].preferences.emit('changed')
153				# Checking for exceptions and infinite recursion
154
155		loaded = list(manager)
156		for name in manager:
157			# print("REMOVE:", name)
158			self.assertIsInstance(manager[name], PluginClass)
159			manager.remove_plugin(name)
160			self.assertNotIn(name, manager)
161
162		self.assertEqual(len(manager), 0)
163
164		for name in sorted(loaded):
165			# print("LOAD AGAIN:", name)
166			manager.load_plugin(name)
167			self.assertIsInstance(manager[name], PluginClass)
168
169		self.assertEqual(len(manager), len(loaded))
170
171
172class TestFunctions(tests.TestCase):
173
174	def test_find_extension(self):
175
176		class Extension(ExtensionBase):
177			pass
178
179		@extendable(Extension)
180		class Extendable(object):
181			pass
182
183		obj = Extendable()
184		ext = Extension(None, obj)
185
186		self.assertIs(find_extension(obj, Extension), ext)
187		with self.assertRaises(ValueError):
188			self.assertIs(find_extension(obj, Extendable), ext)
189
190	def test_find_action(self):
191		from zim.actions import action
192
193		class Extension(ExtensionBase):
194
195			@action('Bar')
196			def bar(self):
197				pass
198
199		@extendable(Extension)
200		class Extendable(object):
201
202			@action('Foo')
203			def foo(self):
204				pass
205
206		obj = Extendable()
207		ext = Extension(None, obj)
208
209		self.assertTrue(hasaction(obj, 'foo'))
210		self.assertTrue(hasaction(ext, 'bar'))
211
212		self.assertIsNotNone(find_action(obj, 'foo'))
213		self.assertIsNotNone(find_action(obj, 'bar'))
214		with self.assertRaises(ValueError):
215			find_action(obj, 'dus')
216