1 2# Copyright 2009-2017 Jaap Karssenberg <jaap.karssenberg@gmail.com> 3 4import tests 5 6from gi.repository import Gtk 7from gi.repository import Pango 8 9from zim.notebook import Path 10from zim.notebook.index.pages import MyTreeIter, IndexNotFoundError 11from zim.formats import ParseTree 12from zim.gui.clipboard import Clipboard 13 14from zim.plugins import find_extension, PluginManager 15from zim.plugins.pageindex import * 16 17 18from tests.mainwindow import setUpMainWindow 19 20 21class TestPageIndexPlugin(tests.TestCase): 22 23 def setUp(self): 24 self.plugin = PluginManager.load_plugin('pageindex') 25 self.window = setUpMainWindow(self.setUpNotebook(content=tests.FULL_NOTEBOOK)) 26 self.extension = find_extension(self.window.pageview, PageIndexNotebookViewExtension) 27 assert self.extension is not None 28 29 def do_expandcollapse(self, autoexpand, autocollapse): 30 self.plugin.preferences.update({'autoexpand': autoexpand, 'autocollapse': autocollapse}) 31 treeview = self.extension.treeview 32 treepath = treeview.get_model().find(Path('Test:foo')) 33 34 treeview.collapse_all() 35 self.assertFalse(treeview.row_expanded(treepath)) 36 37 self.window.open_page(Path('Test:foo:bar')) 38 if autoexpand: 39 self.assertTrue(treeview.row_expanded(treepath)) 40 else: 41 self.assertFalse(treeview.row_expanded(treepath)) 42 43 self.window.open_page(Path('Test')) 44 if autoexpand and not autocollapse: 45 self.assertTrue(treeview.row_expanded(treepath)) 46 else: 47 self.assertFalse(treeview.row_expanded(treepath)) 48 49 def testAutoExpandAndCollapse(self): 50 self.do_expandcollapse(True, True) 51 52 def testNoAutoExpand(self): 53 self.do_expandcollapse(True, False) 54 55 def testAutoExpandNoCollapse(self): 56 self.do_expandcollapse(False, False) 57 58 59def init_model_validator_wrapper(test, model): 60 61 def validate_path_iter(model, path, iter): 62 assert isinstance(path, Gtk.TreePath) 63 assert model.iter_is_valid(iter) 64 65 test.assertEqual(model.get_path(iter).get_indices(), path.get_indices()) 66 67 if model.iter_has_child(iter): 68 test.assertTrue(model.iter_n_children(iter) > 0) 69 child = model.iter_children(iter) 70 test.assertIsNotNone(child) 71 childpath = model.get_path(child) 72 test.assertEqual(childpath.get_indices(), path.get_indices() + [0]) 73 else: 74 test.assertTrue(model.iter_n_children(iter) == 0) 75 76 def validate_parent_path_iter(model, path): 77 assert isinstance(path, Gtk.TreePath) 78 if path.get_depth() > 1: 79 parent = Gtk.TreePath(path.get_indices()[:-1]) 80 iter = model.get_iter(parent) 81 validate_path_iter(model, parent, iter) 82 83 for signal in ('row-inserted', 'row-changed', 'row-has-child-toggled'): 84 model.connect(signal, validate_path_iter) 85 86 for signal in ('row-deleted',): 87 model.connect(signal, validate_parent_path_iter) 88 89 90class TestPageTreeStore(tests.TestCase): 91 92 def runTest(self): 93 notebook = self.setUpNotebook(content=tests.FULL_NOTEBOOK) 94 95 treestore = PageTreeStore(notebook.index) 96 init_model_validator_wrapper(self, treestore) 97 self.assertEqual(treestore.get_flags(), 0) 98 self.assertEqual(treestore.get_n_columns(), 7) 99 for i in range(treestore.get_n_columns()): 100 self.assertTrue(not treestore.get_column_type(i) is None) 101 102 n = treestore.on_iter_n_children(None) 103 self.assertTrue(n > 0) 104 n = treestore.iter_n_children(None) 105 self.assertTrue(n > 0) 106 107 # Quick check for basic methods 108 iter = treestore.on_get_iter((0,)) 109 self.assertTrue(isinstance(iter, MyTreeIter)) 110 self.assertFalse(iter.row['name'] == '') 111 self.assertEqual(iter.treepath, Gtk.TreePath((0,))) 112 self.assertEqual(treestore.on_get_path(iter), Gtk.TreePath((0,))) 113 self.assertEqual(treestore.find(Path(iter.row['name'])), Gtk.TreePath((0,))) 114 basename = treestore.on_get_value(iter, 0) 115 self.assertTrue(len(basename) > 0) 116 117 iter2 = treestore.on_iter_children(None) 118 self.assertIs(iter2, iter) 119 120 self.assertIsNone(treestore.on_get_iter((20, 20, 20, 20, 20))) 121 self.assertRaises(IndexNotFoundError, treestore.find, Path('nonexisting')) 122 self.assertRaises(ValueError, treestore.find, Path(':')) 123 124 # Now walk through the whole notebook testing the API 125 # with nested pages and stuff 126 npages = 0 127 path = [] 128 for page in notebook.pages.walk(): 129 #~ print('>>', page) 130 npages += 1 131 names = page.name.split(':') 132 if len(names) > len(path): 133 path.append(0) # always increment by one 134 elif len(names) < len(path): 135 while len(names) < len(path): 136 path.pop() 137 path[-1] += 1 138 else: 139 path[-1] += 1 140 #~ print('>>', page, path) 141 iter = treestore.get_iter(tuple(path)) 142 indexpath = treestore.get_indexpath(iter) 143 #~ print('>>>', indexpath) 144 self.assertEqual(indexpath, page) 145 self.assertEqual(treestore.get_value(iter, NAME_COL), page.basename) 146 self.assertEqual(treestore.get_value(iter, PATH_COL), page) 147 if not treestore.get_value(iter, EXISTS_COL): 148 self.assertEqual(treestore.get_value(iter, STYLE_COL), Pango.Style.ITALIC) 149 else: 150 self.assertEqual(treestore.get_value(iter, STYLE_COL), Pango.Style.NORMAL) 151 self.assertEqual(treestore.get_path(iter), Gtk.TreePath(path)) 152 153 if indexpath.haschildren: 154 self.assertTrue(treestore.iter_has_child(iter)) 155 child = treestore.iter_children(iter) 156 self.assertTrue(not child is None) 157 child = treestore.iter_nth_child(iter, 0) 158 self.assertTrue(not child is None) 159 parent = treestore.iter_parent(child) 160 self.assertEqual( 161 treestore.get_indexpath(parent), page) 162 childpath = treestore.get_path(child) 163 self.assertEqual( 164 childpath, Gtk.TreePath(tuple(path) + (0,))) 165 n = treestore.iter_n_children(iter) 166 for i in range(1, n): 167 child = treestore.iter_next(child) 168 childpath = treestore.get_path(child) 169 self.assertEqual( 170 childpath, Gtk.TreePath(tuple(path) + (i,))) 171 child = treestore.iter_next(child) 172 self.assertIsNone(child) # children exhausted 173 174 else: 175 self.assertTrue(not treestore.iter_has_child(iter)) 176 child = treestore.iter_children(iter) 177 self.assertTrue(child is None) 178 child = treestore.iter_nth_child(iter, 0) 179 self.assertTrue(child is None) 180 181 self.assertTrue(npages > 10) # double check sanity of walk() method 182 183 184class TestSignals(tests.TestCase): 185 186 PAGES = ('a', 'a:a', 'a:b', 'b', 'c') 187 188 def runTest(self): 189 notebook = self.setUpNotebook() 190 navigation = tests.MockObject(methods=('open_page',)) 191 model = PageTreeStore(notebook.index) 192 init_model_validator_wrapper(self, model) 193 treeview = PageTreeView(notebook, navigation, model=model) 194 195 signals = [] 196 def signal_logger(o, *a): 197 path = a[0].to_string() 198 signal = a[-1] 199 signals.append((signal, path)) 200 #print(">>>", signal, path) 201 202 for signal in ('row-inserted', 'row-changed', 'row-deleted', 'row-has-child-toggled'): 203 model.connect(signal, signal_logger, signal) 204 205 for path in map(Path, self.PAGES): 206 page = notebook.get_page(path) 207 page.parse('plain', 'Test 123\n') 208 notebook.store_page(page) 209 210 expect_add = [ 211 ('row-inserted', '0'), 212 ('row-changed', '0'), 213 ('row-inserted', '0:0'), 214 ('row-has-child-toggled', '0'), 215 ('row-changed', '0'), 216 ('row-changed', '0:0'), 217 ('row-inserted', '0:1'), 218 ('row-changed', '0'), 219 ('row-changed', '0:1'), 220 ('row-inserted', '1'), 221 ('row-changed', '1'), 222 ('row-inserted', '2'), 223 ('row-changed', '2') 224 ] 225 self.assertEqual(signals, expect_add) 226 signals[:] = [] 227 228 for path in reversed(self.PAGES): 229 notebook.delete_page(Path(path)) 230 231 expect_del = [ 232 ('row-deleted', '2'), 233 ('row-deleted', '1'), 234 ('row-deleted', '0:1'), 235 ('row-changed', '0'), 236 ('row-deleted', '0:0'), 237 ('row-has-child-toggled', '0'), 238 ('row-changed', '0'), 239 ('row-deleted', '0') 240 ] 241 self.assertEqual(signals, expect_del) 242 243 244class TestPageTreeView(tests.TestCase): 245 246 def setUp(self): 247 self.notebook = self.setUpNotebook(content=tests.FULL_NOTEBOOK) 248 navigation = tests.MockObject(methods=('open_page',)) 249 self.model = PageTreeStore(self.notebook.index) 250 init_model_validator_wrapper(self, self.model) 251 self.treeview = PageTreeView(self.notebook, navigation, model=self.model) 252 treepath = self.treeview.set_current_page(Path('Test')) 253 assert treepath is not None 254 self.treeview.select_treepath(treepath) 255 256 def testView(self): 257 path = Path('Test:foo') 258 self.treeview.set_current_page(path) 259 # TODO assert something 260 treepath = self.treeview.get_model().find(path) 261 self.assertTrue(not treepath is None) 262 col = self.treeview.get_column(0) 263 self.treeview.row_activated(treepath, col) 264 265 #~ self.treeview.emit('popup-menu') 266 self.treeview.emit('insert-link', path) 267 #self.treeview.emit('copy') 268 269 def testContextMenuExpandCollapse(self): 270 menu = self.treeview.get_popup() 271 272 # Check these do not cause errors - how to verify state ? 273 tests.gtk_activate_menu_item(menu, _("Expand _All")) 274 tests.gtk_activate_menu_item(menu, _("_Collapse All")) 275 276 #@tests.expectedFailure 277 #def testContextMenuCopyLocation(self): 278 # menu = self.treeview.get_popup() 279 # 280 # # Copy item 281 # tests.gtk_activate_menu_item(menu, 'gtk-copy') 282 # self.assertEqual(Clipboard.get_text(), 'Test') 283 284 def testSignals(self): 285 pages = [] 286 287 self.assertGreater(self.model.on_iter_n_children(None), 0) 288 289 # delete all pages 290 for path in list(self.notebook.pages.walk_bottomup()): 291 page = self.notebook.get_page(path) 292 pages.append((page.name, page.dump('wiki'))) 293 self.notebook.delete_page(page) 294 295 self.assertEqual(self.notebook.pages.n_list_pages(), 0) 296 self.assertEqual(self.model.on_iter_n_children(None), 0) 297 # TODO: assert something on the view ? 298 299 # and add them again 300 for name, content in reversed(pages): 301 page = self.notebook.get_page(Path(name)) 302 page.parse('wiki', content) 303 self.notebook.store_page(page) 304 305 self.assertGreater(self.model.on_iter_n_children(None), 0) 306 # TODO: assert something on the view ? 307 308 def testRestoreExpand(self): 309 treeview = self.treeview 310 treepath = treeview.get_model().find(Path('Test:foo')) 311 parent = treepath.copy() 312 parent.up() 313 treeview.collapse_all() 314 315 self.assertIsNone(treeview.get_expanded_path(treepath)) 316 self.assertFalse(treeview.row_expanded(parent)) 317 318 treeview.expand_to_path(parent) 319 self.assertEqual(treeview.get_expanded_path(treepath).to_string(), parent.to_string()) 320 self.assertTrue(treeview.row_expanded(parent)) 321 self.assertFalse(treeview.row_expanded(treepath)) 322 323 treeview.expand_to_path(treepath) 324 self.assertEqual(treeview.get_expanded_path(treepath).to_string(), treepath.to_string()) 325 self.assertTrue(treeview.row_expanded(treepath)) 326 327 treeview._restore_expanded_path(treepath, treepath) 328 self.assertTrue(treeview.row_expanded(treepath)) 329 330 treeview._restore_expanded_path(treepath, parent) 331 self.assertTrue(treeview.row_expanded(parent)) 332 self.assertFalse(treeview.row_expanded(treepath)) 333 334 treeview._restore_expanded_path(treepath, None) 335 self.assertFalse(treeview.row_expanded(parent)) 336 337 def testDragAndDropCallbacks(self): 338 # Don't know how to test real drag and drop, but at least test the callbacks 339 self._testDragAndDropCallbacks(workaround=False) 340 341 def testDragAndDropCallbacksWithWorkaround(self): 342 # Testing work around for issue #390 343 self._testDragAndDropCallbacks(workaround=True) 344 345 def _testDragAndDropCallbacks(self, workaround): 346 treeview = self.treeview 347 348 mocktarget = tests.MockObject(return_values={'name': PAGELIST_TARGET_NAME}) 349 mockselectiondata = tests.MockObject(return_values={'get_target': mocktarget, 'set': None}) 350 351 treeview.do_drag_data_get(None, mockselectiondata, None, None) 352 self.assertEqual(mockselectiondata.lastMethodCall, ('set', mocktarget, 8, b'testnotebook?Test\r\n')) 353 354 if workaround: 355 self.assertEqual(zim.gui.clipboard._internal_selection_data, b'testnotebook?Test\r\n') 356 mockselectiondata.addMockMethod('get_data', None) 357 else: 358 zim.gui.clipboard._internal_selection_data = None 359 mockselectiondata.addMockMethod('get_data', b'testnotebook?Test\r\n') 360 361 treepath = treeview.get_model().find(Path('Foo')) 362 position = Gtk.TreeViewDropPosition.INTO_OR_BEFORE 363 treeview.get_dest_row_at_pos = lambda x, y: (treepath, position) # MOCK method 364 365 with tests.LoggingFilter('zim.notebook', message='Number of links after move'): 366 context = tests.MockObject(methods=('finish',)) 367 treeview.do_drag_data_received(context, None, None, mockselectiondata, None, None) 368