# Copyright 2008-2013 Jaap Karssenberg import tests import os from zim.plugins import * from tests.mainwindow import setUpMainWindow import zim.plugins assert len(zim.plugins.__path__) > 1 # test __path__ magic zim.plugins.__path__ = [os.path.abspath('./zim/plugins')] # set back default search path class TestPluginClasses(tests.TestCase): '''Test case to check coding and documentation of plugin classes''' def runTest(self): plugins = PluginManager.list_installed_plugins() self.assertTrue(len(plugins) > 10) self.assertTrue('spell' in plugins) self.assertTrue('linkmap' in plugins) pluginindex = tests.ZIM_DATA_FOLDER.file('manual/Plugins.txt').read() seen = { 'name': set(), 'description': set(), 'help': set(), } for name in plugins: #~ print('>>', name) klass = PluginManager.get_plugin_class(name) # test plugin info for key in ('name', 'description', 'author'): self.assertTrue( klass.plugin_info.get(key), 'Plugin %s misses info field \'%s\'' % (name, key) ) for key in ('name', 'description', 'help'): self.assertIn(key, klass.plugin_info, 'Plugin %s missing "%s"' % (name, key)) value = klass.plugin_info[key] self.assertFalse( value in seen[key], 'Value for \'%s\' in %s seen before - copy-paste error ?' % (key, name) ) seen[key].add(value) # test manual page present and at least documents preferences page = klass.plugin_info['help'] self.assertTrue(page.startswith('Plugins:'), 'Help page for %s not valid' % name) rellink = "+%s" % page[8:] self.assertIn(rellink, pluginindex, 'Missing links "%s" in manual/Plugins.txt' % rellink) file = tests.ZIM_DATA_FOLDER.file('manual/' + page.replace(':', '/').replace(' ', '_') + '.txt') self.assertTrue(file.exists(), 'Missing file: %s' % file) manual = file.read() ignore = getattr(klass, 'hide_preferences', []) prefs = [p for p in klass.plugin_preferences if not p[0] in ignore] props = [p for p in klass.plugin_notebook_properties] for pref in prefs + props: label = pref[2] if '\n' in label: label, x = label.split('\n', 1) label = label.rstrip(',') self.assertTrue(label in manual, 'Preference or property "%s" for %s plugin not documented in manual page' % (label, name)) # test dependencies data dep = klass.check_dependencies() self.assertTrue(isinstance(dep, tuple)) check, dep = dep self.assertTrue(isinstance(check, bool)) self.assertTrue(isinstance(dep, list)) for i in range(len(dep)): self.assertTrue(isinstance(dep[i], tuple)) self.assertTrue(isinstance(dep[i][0], str)) self.assertTrue(isinstance(dep[i][1], bool)) self.assertTrue(isinstance(dep[i][2], bool)) class TestPluginManager(tests.TestCase): '''Test case for TestManager infrastructure''' def testLoadAndRemovePlugin(self): manager = PluginManager() self.assertEqual(len(manager), 0) self.assertEqual(list(manager), []) obj = manager.load_plugin('journal') self.assertEqual(len(manager), 1) self.assertEqual(list(manager), ['journal']) self.assertEqual(manager['journal'], obj) obj1 = manager.load_plugin('journal') # redundant call self.assertEqual(obj1, obj) self.assertEqual(len(manager), 1) manager.remove_plugin('journal') self.assertEqual(len(manager), 0) self.assertEqual(list(manager), []) self.assertRaises(KeyError, manager.__getitem__, 'journal') manager.remove_plugin('journal') # redundant call def testLoadNonExistingPlugin(self): manager = PluginManager() self.assertRaises(ImportError, manager.load_plugin, 'nonexistingplugin') class TestPlugins(tests.TestCase): '''Test case to initiate all (loadable) plugins and load some extensions''' def runTest(self): preferences = ConfigManager.get_config_dict('preferences.conf') manager = PluginManager() self.assertFalse(preferences.modified) for name in PluginManager.list_installed_plugins(): klass = PluginManager.get_plugin_class(name) if klass.check_dependencies_ok(): manager.load_plugin(name) self.assertIn(name, manager) self.assertFalse(preferences.modified, 'Plugin "%s" modified the preferences while loading' % name) self.assertTrue(len(manager) > 3) for i, name in enumerate(manager): manager[name].preferences.emit('changed') # Checking for exceptions and infinite recursion self.assertTrue(i > 0) #~ self.assertTrue(preferences.modified) # If "False" the check while loading the plugins is not valid # FIXME this detection is broken due to autosave in ConfigManager ... notebook = self.setUpNotebook(content=tests.FULL_NOTEBOOK) self.assertGreaterEqual(len(notebook.__zim_extension_objects__), 2) # At least journal and tasklist should load mainwindow = setUpMainWindow(notebook) self.assertGreaterEqual(len(mainwindow.pageview.__zim_extension_objects__), 3) # enough plugins without dependencies here for i, name in enumerate(manager): manager[name].preferences.emit('changed') # Checking for exceptions and infinite recursion loaded = list(manager) for name in manager: # print("REMOVE:", name) self.assertIsInstance(manager[name], PluginClass) manager.remove_plugin(name) self.assertNotIn(name, manager) self.assertEqual(len(manager), 0) for name in sorted(loaded): # print("LOAD AGAIN:", name) manager.load_plugin(name) self.assertIsInstance(manager[name], PluginClass) self.assertEqual(len(manager), len(loaded)) class TestFunctions(tests.TestCase): def test_find_extension(self): class Extension(ExtensionBase): pass @extendable(Extension) class Extendable(object): pass obj = Extendable() ext = Extension(None, obj) self.assertIs(find_extension(obj, Extension), ext) with self.assertRaises(ValueError): self.assertIs(find_extension(obj, Extendable), ext) def test_find_action(self): from zim.actions import action class Extension(ExtensionBase): @action('Bar') def bar(self): pass @extendable(Extension) class Extendable(object): @action('Foo') def foo(self): pass obj = Extendable() ext = Extension(None, obj) self.assertTrue(hasaction(obj, 'foo')) self.assertTrue(hasaction(ext, 'bar')) self.assertIsNotNone(find_action(obj, 'foo')) self.assertIsNotNone(find_action(obj, 'bar')) with self.assertRaises(ValueError): find_action(obj, 'dus')