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