Coverage for core\test_leoNodes.py: 100%
828 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
1# -*- coding: utf-8 -*-
2#@+leo-ver=5-thin
3#@+node:ekr.20201203042030.1: * @file ../unittests/core/test_leoNodes.py
4#@@first
5"""Tests for leo.core.leoNodes"""
7# pylint has troubles finding Commands methods.
8# pylint: disable=no-member
9import textwrap
10from leo.core import leoGlobals as g
11from leo.core.leoTest2 import LeoUnitTest
13#@+others
14#@+node:ekr.20210828112210.1: ** class TestNodes(LeoUnitTest)
15class TestNodes(LeoUnitTest):
16 """Unit tests for Position and Vnode classes leo/core/leoNodes.py."""
18 test_outline = None # Set by create_test_outline.
20 #@+others
21 #@+node:ekr.20201203042409.3: *3* TestNodes.setUp
22 def setUp(self):
23 """Create the nodes in the commander."""
24 super().setUp()
25 c = self.c
26 self.create_test_outline()
27 c.selectPosition(c.rootPosition())
28 #@+node:ekr.20210902022909.1: *3* TestNodes.tests...
29 #@+node:ekr.20220306073015.1: *4* TestNodes: Commander methods
30 #@+node:ekr.20210830095545.6: *5* TestNodes.test_c_positionExists
31 def test_c_positionExists(self):
32 c, p = self.c, self.c.p
33 child = p.insertAsLastChild()
34 self.assertTrue(c.positionExists(child))
35 child.doDelete()
36 self.assertFalse(c.positionExists(child))
37 # also check the same on root level
38 child = c.rootPosition().insertAfter()
39 self.assertTrue(c.positionExists(child))
40 child.doDelete()
41 self.assertFalse(c.positionExists(child))
42 #@+node:ekr.20210830095545.7: *5* TestNodes.test_c_positionExists_for_all_nodes
43 def test_c_positionExists_for_all_nodes(self):
44 c, p = self.c, self.c.p
45 for p in c.all_positions():
46 self.assertTrue(c.positionExists(p))
47 # 2012/03/08: If a root is given, the search is confined to that root only.
48 #@+node:ekr.20210830095545.8: *5* TestNodes.test_c_safe_all_positions
49 def test_c_safe_all_positions(self):
50 c = self.c
51 aList1 = list(c.all_positions())
52 aList2 = list(c.safe_all_positions())
53 self.assertEqual(len(aList1), len(aList2))
54 #@+node:ekr.20220306073547.1: *4* TestNodes: File operations
55 #@+node:ekr.20210830095545.58: *5* TestNodes.test_at_others_directive
56 def test_at_others_directive(self):
57 p = self.c.p
58 p1 = p.insertAsLastChild()
59 p1.setHeadString('@file zzz')
60 body = ''' %s
61 ''' % (chr(64) + 'others') # ugly hack
62 p1.setBodyString(body)
63 p2 = p1.insertAsLastChild()
64 self.assertEqual(p1.textOffset(), 0)
65 self.assertEqual(p2.textOffset(), 5)
66 #@+node:ekr.20210830095545.54: *5* TestNodes.test_insert_node_that_does_not_belong_to_a_derived_file
67 def test_insert_node_that_does_not_belong_to_a_derived_file(self):
68 # Change @file activeUnitTests.txt to @@file activeUnitTests.txt
69 p = self.c.p
70 p1 = p.insertAsLastChild()
71 self.assertFalse(p1.textOffset())
73 #@+node:ekr.20210830095545.56: *5* TestNodes.test_organizer_node
74 def test_organizer_node(self):
75 p = self.c.p
76 p1 = p.insertAsLastChild()
77 p1.setHeadString('@file zzz')
78 p2 = p1.insertAsLastChild()
79 self.assertEqual(p1.textOffset(), 0)
80 self.assertEqual(p2.textOffset(), 0)
82 #@+node:ekr.20210830095545.55: *5* TestNodes.test_root_of_a_derived_file
83 def test_root_of_a_derived_file(self):
84 p = self.c.p
85 p1 = p.insertAsLastChild()
86 p1.setHeadString('@file zzz')
87 self.assertEqual(p1.textOffset(), 0)
88 #@+node:ekr.20210830095545.57: *5* TestNodes.test_section_node
89 def test_section_node(self):
90 p = self.c.p
91 p1 = p.insertAsLastChild()
92 p1.setHeadString('@file zzz')
93 body = ''' %s
94 ''' % (g.angleBrackets(' section '))
95 p1.setBodyString(body)
96 p2 = p1.insertAsLastChild()
97 head = g.angleBrackets(' section ')
98 p2.setHeadString(head)
99 self.assertEqual(p1.textOffset(), 0)
100 self.assertEqual(p2.textOffset(), 3)
101 # Section nodes can appear in with @others nodes,
102 # so they don't get special treatment.
103 #@+node:ekr.20220307042702.1: *4* TestNodes: Generators
104 #@+node:ekr.20210830095545.3: *5* TestNodes.test_all_generators_return_unique_positions
105 def test_all_generators_return_unique_positions(self):
106 # This tests a major bug in *all* generators returning positions.
107 c, p = self.c, self.c.p
108 root = p.next()
109 table = (
110 ('all_positions', c.all_positions),
111 ('all_unique_positions', c.all_unique_positions),
112 ('children', root.children),
113 ('self_and_siblings', root.self_and_siblings),
114 ('self_and_parents', root.firstChild().self_and_parents),
115 ('self_and_subtree', root.self_and_subtree),
116 ('following_siblings', root.following_siblings),
117 ('parents', root.firstChild().firstChild().parents),
118 ('unique_subtree', root.unique_subtree),
119 )
120 for kind, generator in table:
121 aList = []
122 for p in generator():
123 self.assertFalse(p in aList, msg=f"{kind} {p.gnx} {p.h}")
124 aList.append(p)
125 #@+node:ekr.20210828075915.1: *5* TestNodes.test_all_nodes_coverage
126 def test_all_nodes_coverage(self):
127 # @test c iters: <coverage tests>
128 c = self.c
129 v1 = [p.v for p in c.all_positions()]
130 v2 = [v for v in c.all_nodes()]
131 for v in v2:
132 self.assertTrue(v in v1)
133 for v in v1:
134 self.assertTrue(v in v2)
135 #@+node:ekr.20210830095545.9: *5* TestNodes.test_check_all_gnx_s_exist_and_are_unique
136 def test_check_all_gnx_s_exist_and_are_unique(self):
137 c, p = self.c, self.c.p
138 d = {} # Keys are gnx's, values are lists of vnodes with that gnx.
139 for p in c.all_positions():
140 gnx = p.v.fileIndex
141 self.assertTrue(gnx)
142 aSet = d.get(gnx, set())
143 aSet.add(p.v)
144 d[gnx] = aSet
145 for gnx in sorted(d.keys()):
146 aList = sorted(d.get(gnx))
147 self.assertTrue(len(aList) == 1)
148 #@+node:ekr.20210830095545.2: *5* TestNodes.test_consistency_between_parents_iter_and_v_parents
149 def test_consistency_between_parents_iter_and_v_parents(self):
150 c, p = self.c, self.c.p
151 for p in c.all_positions():
152 parents1 = p.v.parents
153 parents2 = p.v.directParents()
154 self.assertEqual(len(parents1), len(parents2), msg=p.h)
155 for parent in parents1:
156 self.assertTrue(parent in parents2)
157 for parent in parents2:
158 self.assertTrue(parent in parents1)
159 #@+node:ekr.20210830095545.10: *5* TestNodes.test_consistency_of_back_next_links
160 def test_consistency_of_back_next_links(self):
161 c, p = self.c, self.c.p
162 for p in c.all_positions():
163 back = p.back()
164 next = p.next()
165 if back:
166 self.assertEqual(back.getNext(), p)
167 if next:
168 self.assertEqual(next.getBack(), p)
169 #@+node:ekr.20210830095545.11: *5* TestNodes.test_consistency_of_c_all_positions__and_p_ThreadNext_
170 def test_consistency_of_c_all_positions__and_p_ThreadNext_(self):
171 c, p = self.c, self.c.p
172 p2 = c.rootPosition()
173 for p in c.all_positions():
174 self.assertEqual(p, p2)
175 p2.moveToThreadNext()
176 self.assertFalse(p2)
177 #@+node:ekr.20210830095545.12: *5* TestNodes.test_consistency_of_firstChild__children_iter_
178 def test_consistency_of_firstChild__children_iter_(self):
179 c, p = self.c, self.c.p
180 for p in c.all_positions():
181 p2 = p.firstChild()
182 for p3 in p.children_iter():
183 self.assertEqual(p3, p2)
184 p2.moveToNext()
185 self.assertFalse(p2)
186 #@+node:ekr.20210830095545.13: *5* TestNodes.test_consistency_of_level
187 def test_consistency_of_level(self):
188 c, p = self.c, self.c.p
189 for p in c.all_positions():
190 if p.hasParent():
191 self.assertEqual(p.parent().level(), p.level() - 1)
192 if p.hasChildren():
193 self.assertEqual(p.firstChild().level(), p.level() + 1)
194 if p.hasNext():
195 self.assertEqual(p.next().level(), p.level())
196 if p.hasBack():
197 self.assertEqual(p.back().level(), p.level())
198 #@+node:ekr.20210830095545.14: *5* TestNodes.test_consistency_of_parent__parents_iter_
199 def test_consistency_of_parent__parents_iter_(self):
200 c, p = self.c, self.c.p
201 for p in c.all_positions():
202 p2 = p.parent()
203 for p3 in p.parents_iter():
204 self.assertEqual(p3, p2)
205 p2.moveToParent()
206 self.assertFalse(p2)
207 #@+node:ekr.20210830095545.15: *5* TestNodes.test_consistency_of_parent_child_links
208 def test_consistency_of_parent_child_links(self):
209 # Test consistency of p.parent, p.next, p.back and p.firstChild.
210 c, p = self.c, self.c.p
211 for p in c.all_positions():
212 if p.hasParent():
213 n = p.childIndex()
214 self.assertEqual(p, p.parent().moveToNthChild(n))
215 for child in p.children_iter():
216 self.assertEqual(p, child.parent())
217 if p.hasNext():
218 self.assertEqual(p.next().parent(), p.parent())
219 if p.hasBack():
220 self.assertEqual(p.back().parent(), p.parent())
221 #@+node:ekr.20210830095545.16: *5* TestNodes.test_consistency_of_threadBack_Next_links
222 def test_consistency_of_threadBack_Next_links(self):
223 c, p = self.c, self.c.p
224 for p in c.all_positions():
225 threadBack = p.threadBack()
226 threadNext = p.threadNext()
227 if threadBack:
228 self.assertEqual(p, threadBack.getThreadNext())
229 if threadNext:
230 self.assertEqual(p, threadNext.getThreadBack())
231 #@+node:ekr.20220306100004.1: *5* TestNodes.test_p_following_siblings
232 def test_p_following_siblings(self):
234 p = self.c.rootPosition()
235 while p:
236 for sib in p.following_siblings():
237 self.assertTrue(p != sib)
238 self.assertTrue(p < sib)
239 p.moveToThreadNext()
240 #@+node:ekr.20220306101100.1: *5* TestNodes.test_p_nearest
241 def test_p_nearest(self):
243 c = self.c
245 def p_true(p):
246 return True
248 def p_false(p):
249 return False
251 # Create top-level @file node..
252 root1 = c.rootPosition().insertAfter()
253 root1.h = '@file root1.py'
254 # Create a third-level @file node, *not* a child of the top-level node.
255 child = root1.next().insertAsLastChild()
256 child.h = 'target child'
257 root2 = child.insertAsLastChild()
258 root2.h = '@file root2.py'
259 for pred in (p_true, p_false, None):
260 for p in c.all_positions():
261 for root in p.nearest_roots(predicate=pred):
262 pass
263 for root in p.nearest_unique_roots(predicate=pred):
264 pass
265 #@+node:ekr.20220307042327.1: *5* TestNodes.test_p_nodes
266 def test_p_nodes(self):
268 c = self.c
269 for p in c.p.nodes():
270 pass
271 #@+node:ekr.20210830095545.38: *5* TestNodes.test_p_unique_nodes
272 def test_p_unique_nodes(self):
274 self.assertEqual(len(list(self.root_p.unique_nodes())), 5)
275 #@+node:ekr.20220306100527.1: *5* TestNodes.test_p_unique_subtree
276 def test_p_unique_subtree(self):
278 p = self.c.rootPosition()
279 while p:
280 for descendant in p.unique_subtree():
281 self.assertTrue(p <= descendant)
282 p.moveToThreadNext()
283 #@+node:ekr.20220306072631.1: *4* TestNodes: Outline operations
284 #@+node:ekr.20210830095545.42: *5* TestNodes.test_clone_and_move_the_clone_to_the_root
285 def test_clone_and_move_the_clone_to_the_root(self):
286 c, p = self.c, self.c.p
287 child = p.insertAsNthChild(0)
288 c.setHeadString(child, 'child') # Force the headline to update.
289 self.assertTrue(child)
290 c.selectPosition(child)
291 clone = c.clone()
292 self.assertEqual(clone, c.p)
293 self.assertEqual(clone.h, 'child')
294 assert child.isCloned(), 'fail 1'
295 assert clone.isCloned(), 'fail 2'
296 assert child.isCloned(), 'fail 3'
297 assert clone.isCloned(), 'fail 4'
298 c.undoer.undo()
299 assert not child.isCloned(), 'fail 1-a'
300 c.undoer.redo()
301 assert child.isCloned(), 'fail 1-b'
302 c.undoer.undo()
303 assert not child.isCloned(), 'fail 1-c'
304 c.undoer.redo()
305 assert child.isCloned(), 'fail 1-d'
306 clone.moveToRoot() # Does not change child position.
307 assert child.isCloned(), 'fail 3-2'
308 assert clone.isCloned(), 'fail 4-2'
309 assert not clone.parent(), 'fail 5'
310 assert not clone.back(), 'fail 6'
311 clone.doDelete()
312 assert not child.isCloned(), 'fail 7'
313 #@+node:ekr.20210830095545.43: *5* TestNodes.test_delete_node
314 def test_delete_node(self):
315 # This test requires @bool select-next-after-delete = False
316 c, p = self.c, self.c.p
317 p2 = p.insertAsNthChild(0)
318 p2.setHeadString('A')
319 p3 = p.insertAsNthChild(1)
320 p3.setHeadString('B')
321 p4 = p.insertAsNthChild(2)
322 p4.setHeadString('C')
323 p.expand()
324 c.selectPosition(p3)
325 c.deleteOutline()
326 c.redraw_now()
327 p = c.p
328 self.assertEqual(p.h, 'A')
329 self.assertEqual(p.next().h, 'C')
330 c.undoer.undo()
331 c.outerUpdate()
332 p = c.p
333 self.assertEqual(p.back(), p2)
334 self.assertEqual(p.next(), p4)
335 c.undoer.redo()
336 c.outerUpdate()
337 p = c.p
338 self.assertEqual(p.h, 'A')
339 self.assertEqual(p.next().h, 'C')
340 c.undoer.undo()
341 c.outerUpdate()
342 p = c.p
343 self.assertEqual(p.back(), p2)
344 self.assertEqual(p.next(), p4)
345 c.undoer.redo()
346 c.outerUpdate()
347 p = c.p
348 self.assertEqual(p.h, 'A')
349 self.assertEqual(p.next().h, 'C')
350 #@+node:ekr.20210830095545.44: *5* TestNodes.test_deleting_the_root_should_select_another_node
351 def test_deleting_the_root_should_select_another_node(self):
352 c, p = self.c, self.c.p
353 root_h = p.h
354 child = p.next()
355 child.moveToRoot() # Does not change child position.
356 c.setRootPosition(child)
357 self.assertTrue(c.positionExists(child))
358 self.assertEqual(c.rootPosition().h, child.h)
359 next = c.rootPosition().next()
360 self.assertEqual(next.h, root_h)
361 c.rootPosition().doDelete(newNode=next)
362 c.setRootPosition(next)
363 #@+node:ekr.20210830095545.45: *5* TestNodes.test_demote
364 def test_demote(self):
365 c, p = self.c, self.c.p
366 p2 = p.insertAsNthChild(0)
367 p2.setHeadString('A')
368 p3 = p.insertAsNthChild(1)
369 p3.setHeadString('B')
370 p4 = p.insertAsNthChild(2)
371 p4.setHeadString('C')
372 p5 = p.insertAsNthChild(3)
373 p5.setHeadString('D')
374 p.expand()
375 c.setCurrentPosition(p3)
376 c.demote()
377 p = c.p
378 self.assertEqual(p, p3)
379 self.assertEqual(p.h, 'B')
380 assert not p.next()
381 self.assertEqual(p.firstChild().h, 'C')
382 self.assertEqual(p.firstChild().next().h, 'D')
383 c.undoer.undo()
384 p = c.p
385 self.assertEqual(p, p3)
386 self.assertEqual(p.back(), p2)
387 self.assertEqual(p.next(), p4)
388 c.undoer.redo()
389 self.assertEqual(p, p3)
390 self.assertEqual(p.h, 'B')
391 assert not p.next()
392 self.assertEqual(p.firstChild().h, 'C')
393 self.assertEqual(p.firstChild().next().h, 'D')
394 c.undoer.undo()
395 p = c.p
396 self.assertEqual(p.back(), p2)
397 self.assertEqual(p.next(), p4)
398 c.undoer.redo()
399 self.assertEqual(p, p3)
400 self.assertEqual(p.h, 'B')
401 assert not p.next()
402 self.assertEqual(p.firstChild().h, 'C')
403 self.assertEqual(p.firstChild().next().h, 'D')
404 #@+node:ekr.20210830095545.46: *5* TestNodes.test_insert_node
405 def test_insert_node(self):
406 c, p = self.c, self.c.p
407 self.assertEqual(p.h, 'root')
408 p2 = p.insertAsNthChild(0)
409 p2.setHeadString('A')
410 p3 = p.insertAsNthChild(1)
411 p3.setHeadString('B')
412 p.expand()
413 c.setCurrentPosition(p2)
414 p4 = c.insertHeadline()
415 self.assertEqual(p4, c.p)
416 p = c.p
417 self.assertTrue(p)
418 p.setHeadString('inserted')
419 self.assertTrue(p.back())
420 self.assertEqual(p.back().h, 'A')
421 self.assertEqual(p.next().h, 'B')
422 # With the new undo logic, it takes 2 undoes.
423 # The first undo undoes the headline changes,
424 # the second undo undoes the insert node.
425 c.undoer.undo()
426 c.undoer.undo()
427 p = c.p
428 self.assertEqual(p, p2)
429 self.assertEqual(p.next(), p3)
430 c.undoer.redo()
431 p = c.p
432 self.assertTrue(p.back())
433 self.assertEqual(p.back().h, 'A')
434 self.assertEqual(p.next().h, 'B')
435 c.undoer.undo()
436 p = c.p
437 self.assertEqual(p, p2)
438 self.assertEqual(p.next(), p3)
439 c.undoer.redo()
440 p = c.p
441 self.assertEqual(p.back().h, 'A')
442 self.assertEqual(p.next().h, 'B')
443 #@+node:ekr.20210830095545.47: *5* TestNodes.test_move_outline_down__undo_redo
444 def test_move_outline_down__undo_redo(self):
445 c, p = self.c, self.c.p
446 p2 = p.insertAsNthChild(0)
447 p2.setHeadString('A')
448 p3 = p.insertAsNthChild(1)
449 p3.setHeadString('B')
450 p4 = p.insertAsNthChild(2)
451 p4.setHeadString('C')
452 p5 = p.insertAsNthChild(3)
453 p5.setHeadString('D')
454 p.expand()
455 c.setCurrentPosition(p3)
456 c.moveOutlineDown()
457 moved = c.p
458 self.assertEqual(moved.h, 'B')
459 self.assertEqual(moved.back().h, 'C')
460 self.assertEqual(moved.next().h, 'D')
461 self.assertEqual(moved.next(), p5)
462 c.undoer.undo()
463 moved = c.p
464 self.assertEqual(moved.back(), p2)
465 self.assertEqual(moved.next(), p4)
466 c.undoer.redo()
467 moved = c.p
468 self.assertEqual(moved.h, 'B')
469 self.assertEqual(moved.back().h, 'C')
470 self.assertEqual(moved.next().h, 'D')
471 c.undoer.undo()
472 moved = c.p
473 self.assertEqual(moved.back(), p2)
474 self.assertEqual(moved.next(), p4)
475 c.undoer.redo()
476 moved = c.p
477 self.assertEqual(moved.h, 'B')
478 self.assertEqual(moved.back().h, 'C')
479 self.assertEqual(moved.next().h, 'D')
480 #@+node:ekr.20210830095545.48: *5* TestNodes.test_move_outline_left
481 def test_move_outline_left(self):
482 c, p = self.c, self.c.p
483 p2 = p.insertAsNthChild(0)
484 p2.setHeadString('A')
485 p.expand()
486 c.setCurrentPosition(p2)
487 c.moveOutlineLeft()
488 moved = c.p
489 self.assertEqual(moved.h, 'A')
490 self.assertEqual(moved.back(), p)
491 c.undoer.undo()
492 c.undoer.redo()
493 c.undoer.undo()
494 c.undoer.redo()
495 moved.doDelete(newNode=p)
496 #@+node:ekr.20210830095545.49: *5* TestNodes.test_move_outline_right
497 def test_move_outline_right(self):
498 c, p = self.c, self.c.p
499 p2 = p.insertAsNthChild(0)
500 p2.setHeadString('A')
501 p3 = p.insertAsNthChild(1)
502 p3.setHeadString('B')
503 p4 = p.insertAsNthChild(2)
504 p4.setHeadString('C')
505 p.expand()
506 c.setCurrentPosition(p3)
507 c.moveOutlineRight()
508 moved = c.p
509 self.assertEqual(moved.h, 'B')
510 self.assertEqual(moved.parent(), p2)
511 c.undoer.undo()
512 c.undoer.redo()
513 c.undoer.undo()
514 c.undoer.redo()
515 #@+node:ekr.20210830095545.50: *5* TestNodes.test_move_outline_up
516 def test_move_outline_up(self):
517 c, p = self.c, self.c.p
518 p2 = p.insertAsNthChild(0)
519 p2.setHeadString('A')
520 p3 = p.insertAsNthChild(1)
521 p3.setHeadString('B')
522 p4 = p.insertAsNthChild(2)
523 p4.setHeadString('C')
524 p5 = p.insertAsNthChild(3)
525 p5.setHeadString('D')
526 p.expand()
527 c.setCurrentPosition(p4)
528 c.moveOutlineUp()
529 moved = c.p
530 self.assertEqual(moved.h, 'C')
531 self.assertEqual(moved.back().h, 'A')
532 self.assertEqual(moved.next().h, 'B')
533 self.assertEqual(moved.back(), p2)
534 c.undoer.undo()
535 c.undoer.redo()
536 c.undoer.undo()
537 c.undoer.redo()
538 #@+node:ekr.20210830095545.51: *5* TestNodes.test_paste_node
539 def test_paste_node(self):
540 c, p = self.c, self.c.p
541 child = p.insertAsNthChild(0)
542 child.setHeadString('child')
543 child2 = p.insertAsNthChild(1)
544 child2.setHeadString('child2')
545 grandChild = child.insertAsNthChild(0)
546 grandChild.setHeadString('grand child')
547 c.selectPosition(grandChild)
548 c.clone()
549 c.selectPosition(child)
550 p.expand()
551 c.selectPosition(child)
552 self.assertEqual(c.p.h, 'child')
553 c.copyOutline()
554 oldVnodes = [p2.v for p2 in child.self_and_subtree()]
555 c.selectPosition(child)
556 c.p.contract() # Essential
557 c.pasteOutline()
558 assert c.p != child
559 self.assertEqual(c.p.h, 'child')
560 newVnodes = [p2.v for p2 in c.p.self_and_subtree()]
561 for v in newVnodes:
562 assert v not in oldVnodes
563 c.undoer.undo()
564 c.undoer.redo()
565 c.undoer.undo()
566 c.undoer.redo()
567 #@+node:ekr.20210830095545.52: *5* TestNodes.test_paste_retaining_clones
568 def test_paste_retaining_clones(self):
569 c, p = self.c, self.c.p
570 child = p.insertAsNthChild(0)
571 child.setHeadString('child')
572 self.assertTrue(child)
573 grandChild = child.insertAsNthChild(0)
574 grandChild.setHeadString('grand child')
575 c.selectPosition(child)
576 c.copyOutline()
577 oldVnodes = [p2.v for p2 in child.self_and_subtree()]
578 c.p.contract() # Essential
579 c.pasteOutlineRetainingClones()
580 self.assertNotEqual(c.p, child)
581 newVnodes = [p2.v for p2 in c.p.self_and_subtree()]
582 for v in newVnodes:
583 self.assertTrue(v in oldVnodes)
584 #@+node:ekr.20210830095545.53: *5* TestNodes.test_promote
585 def test_promote(self):
586 c, p = self.c, self.c.p
587 p2 = p.insertAsNthChild(0)
588 p2.setHeadString('A')
589 p3 = p.insertAsNthChild(1)
590 p3.setHeadString('B')
591 p4 = p3.insertAsNthChild(0)
592 p4.setHeadString('child 1')
593 p5 = p3.insertAsNthChild(1)
594 p5.setHeadString('child 2')
595 p.expand()
596 p6 = p.insertAsNthChild(2)
597 p6.setHeadString('C')
598 c.setCurrentPosition(p3)
599 c.promote()
600 p = c.p
601 self.assertEqual(p, p3)
602 self.assertEqual(p.h, 'B')
603 self.assertEqual(p.next().h, 'child 1')
604 self.assertEqual(p.next().next().h, 'child 2')
605 self.assertEqual(p.next().next().next().h, 'C')
606 c.undoer.undo()
607 p = c.p
608 self.assertEqual(p, p3)
609 self.assertEqual(p.back(), p2)
610 self.assertEqual(p.next(), p6)
611 self.assertEqual(p.firstChild().h, 'child 1')
612 self.assertEqual(p.firstChild().next().h, 'child 2')
613 c.undoer.redo()
614 p = c.p
615 self.assertEqual(p, p3)
616 self.assertEqual(p.h, 'B')
617 self.assertEqual(p.next().h, 'child 1')
618 self.assertEqual(p.next().next().h, 'child 2')
619 self.assertEqual(p.next().next().next().h, 'C')
620 c.undoer.undo()
621 p = c.p
622 self.assertEqual(p, p3)
623 self.assertEqual(p.back(), p2)
624 self.assertEqual(p.next(), p6)
625 self.assertEqual(p.firstChild().h, 'child 1')
626 self.assertEqual(p.firstChild().next().h, 'child 2')
627 c.undoer.redo()
628 p = c.p
629 self.assertEqual(p, p3)
630 self.assertEqual(p.h, 'B')
631 self.assertEqual(p.next().h, 'child 1')
632 self.assertEqual(p.next().next().h, 'child 2')
633 self.assertEqual(p.next().next().next().h, 'C')
634 #@+node:ekr.20220306072850.1: *4* TestNodes: Positions methods
635 #@+node:ekr.20210830095545.17: *5* TestNodes.test_p_convertTreeToString_and_allies
636 def test_convertTreeToString_and_allies(self):
637 p = self.c.p
638 sib = p.next()
639 self.assertTrue(sib)
640 s = sib.convertTreeToString()
641 for p2 in sib.self_and_subtree():
642 self.assertTrue(p2.h in s)
643 #@+node:ekr.20210830095545.21: *5* TestNodes.test_p__eq_
644 def test_p__eq_(self):
645 c, p = self.c, self.c.p
646 # These must not return NotImplemented!
647 root = c.rootPosition()
648 self.assertFalse(p.__eq__(None))
649 self.assertTrue(p.__ne__(None))
650 self.assertTrue(p.__eq__(root))
651 self.assertFalse(p.__ne__(root))
652 #@+node:ekr.20220306092728.1: *5* TestNodes.test_p__gt__
653 def test_p__gt__(self):
655 # p.__gt__ is the foundation for >, <, >=, <=.
656 p = self.c.rootPosition()
657 n = 0
658 # Test all possible comparisons.
659 while p:
660 prev = p.threadBack() # Make a copy.
661 next = p.threadNext() # Make a copy.
662 while prev:
663 n += 1
664 self.assertTrue(p != prev)
665 self.assertTrue(prev < p)
666 self.assertTrue(p > prev)
667 prev.moveToThreadBack()
668 while next:
669 n += 1
670 self.assertTrue(p != prev)
671 self.assertTrue(next > p)
672 self.assertTrue(p < next)
673 next.moveToThreadNext()
674 p.moveToThreadNext()
675 #@+node:ekr.20220307045746.1: *5* TestNodes.test_p_key
676 def test_p__key__(self):
678 c = self.c
679 child = c.p.firstChild()
680 child.key()
681 child.sort_key(child)
682 #@+node:ekr.20210830095545.24: *5* TestNodes.test_p_comparisons
683 def test_p_comparisons(self):
685 c, p = self.c, self.c.p
686 root = c.rootPosition()
687 self.assertEqual(root, p)
688 child = p.firstChild()
689 self.assertTrue(child)
690 grandChild = child.firstChild()
691 self.assertTrue(grandChild)
692 copy = p.copy()
693 self.assertEqual(p, copy)
694 self.assertNotEqual(p, p.threadNext())
695 self.assertTrue(p.__eq__(p))
696 self.assertFalse(p.__ne__(copy))
697 self.assertTrue(p.__eq__(root))
698 self.assertFalse(p.__ne__(root))
699 self.assertTrue(p.threadNext().__gt__(p))
700 self.assertTrue(p.threadNext() > p)
701 self.assertTrue(p.threadNext().__ge__(p))
702 self.assertFalse(p.threadNext().__lt__(p))
703 self.assertFalse(p.threadNext().__le__(p))
704 self.assertTrue(child.__gt__(p))
705 self.assertTrue(child > p)
706 self.assertTrue(grandChild > child)
707 #@+node:ekr.20210830095545.25: *5* TestNodes.test_p_deletePositionsInList
708 def test_p_deletePositionsInList(self):
709 c, p = self.c, self.c.p
710 root = p.insertAsLastChild()
711 root.h = 'root'
712 # Top level
713 a1 = root.insertAsLastChild()
714 a1.h = 'a'
715 a1.clone()
716 d1 = a1.insertAfter()
717 d1.h = 'd'
718 b1 = root.insertAsLastChild()
719 b1.h = 'b'
720 # Children of a.
721 b11 = b1.clone()
722 b11.moveToLastChildOf(a1)
723 b11.clone()
724 c2 = b11.insertAfter()
725 c2.h = 'c'
726 # Children of d
727 b11 = b1.clone()
728 b11.moveToLastChildOf(d1)
729 # Count number of 'b' nodes.
730 aList = []
731 nodes = 0
732 for p in root.subtree():
733 nodes += 1
734 if p.h == 'b':
735 aList.append(p.copy())
736 self.assertEqual(len(aList), 6)
737 c.deletePositionsInList(aList)
738 c.redraw()
740 #@+node:ekr.20220307043449.1: *5* TestNodes.test_p_getters
741 def test_p_getters(self):
743 p = self.c.p
745 table1 = (
746 p.anyAtFileNodeName,
747 p.atAutoNodeName,
748 p.atCleanNodeName,
749 p.atEditNodeName,
750 p.atFileNodeName,
751 p.atNoSentinelsFileNodeName,
752 p.atShadowFileNodeName,
753 p.atSilentFileNodeName,
754 p.atThinFileNodeName,
755 p.isAnyAtFileNode,
756 p.isAtAllNode,
757 p.isAtAutoNode,
758 p.isAtAutoRstNode,
759 p.isAtCleanNode,
760 p.isAtEditNode,
761 p.isAtFileNode,
762 p.isAtIgnoreNode,
763 p.isAtNoSentinelsFileNode,
764 p.isAtOthersNode,
765 p.isAtRstFileNode,
766 p.isAtShadowFileNode,
767 p.isAtSilentFileNode,
768 p.isAtThinFileNode,
769 p.isMarked,
770 p.isOrphan,
771 p.isTopBitSet,
772 p.isVisited,
773 )
774 for func in table1:
775 self.assertFalse(func(), msg=func.__name__)
776 table2 = (
777 p.bodyString,
778 p.headString,
779 p.isDirty,
780 p.isSelected,
781 p.status,
782 )
783 for func in table2:
784 func() # Don't care about result.
785 #@+node:ekr.20210830095545.26: *5* TestNodes.test_p_hasNextBack
786 def test_p_hasNextBack(self):
787 c, p = self.c, self.c.p
788 for p in c.all_positions():
789 back = p.back()
790 next = p.next()
791 assert(
792 (back and p.hasBack()) or
793 (not back and not p.hasBack()))
794 assert(
795 (next and p.hasNext()) or
796 (not next and not p.hasNext()))
797 #@+node:ekr.20210830095545.27: *5* TestNodes.test_p_hasParentChild
798 def test_p_hasParentChild(self):
799 c, p = self.c, self.c.p
800 for p in c.all_positions():
801 child = p.firstChild()
802 parent = p.parent()
803 assert(
804 (child and p.hasFirstChild()) or
805 (not child and not p.hasFirstChild()))
806 assert(
807 (parent and p.hasParent()) or
808 (not parent and not p.hasParent()))
809 #@+node:ekr.20210830095545.28: *5* TestNodes.test_p_hasThreadNextBack
810 def test_p_hasThreadNextBack(self):
811 c, p = self.c, self.c.p
812 for p in c.all_positions():
813 threadBack = p.getThreadBack()
814 threadNext = p.getThreadNext()
815 assert(
816 (threadBack and p.hasThreadBack()) or
817 (not threadBack and not p.hasThreadBack()))
818 assert(
819 (threadNext and p.hasThreadNext()) or
820 (not threadNext and not p.hasThreadNext()))
821 #@+node:ekr.20210830095545.29: *5* TestNodes.test_p_isAncestorOf
822 def test_p_isAncestorOf(self):
823 c, p = self.c, self.c.p
824 for p in c.all_positions():
825 child = p.firstChild()
826 while child:
827 for parent in p.self_and_parents_iter():
828 assert parent.isAncestorOf(child)
829 child.moveToNext()
830 next = p.next()
831 self.assertFalse(p.isAncestorOf(next))
832 #@+node:ekr.20210830095545.30: *5* TestNodes.test_p_isCurrentPosition
833 def test_p_isCurrentPosition(self):
834 c, p = self.c, self.c.p
835 self.assertFalse(c.isCurrentPosition(None))
836 self.assertTrue(c.isCurrentPosition(p))
837 #@+node:ekr.20210830095545.31: *5* TestNodes.test_p_isRootPosition
838 def test_p_isRootPosition(self):
839 c, p = self.c, self.c.p
840 self.assertFalse(c.isRootPosition(None))
841 self.assertTrue(c.isRootPosition(p))
842 #@+node:ekr.20210830095545.33: *5* TestNodes.test_p_moveToFirst_LastChild
843 def test_p_moveToFirst_LastChild(self):
844 c, p = self.c, self.c.p
845 root2 = p.next()
846 self.assertTrue(root2)
847 p2 = root2.insertAfter()
848 p2.h = "test"
849 self.assertTrue(c.positionExists(p2))
850 p2.moveToFirstChildOf(root2)
851 self.assertTrue(c.positionExists(p2))
852 p2.moveToLastChildOf(root2)
853 self.assertTrue(c.positionExists(p2))
854 #@+node:ekr.20210830095545.34: *5* TestNodes.test_p_moveToVisBack_in_a_chapter
855 def test_p_moveToVisBack_in_a_chapter(self):
856 # Verify a fix for bug https://bugs.launchpad.net/leo-editor/+bug/1264350
857 import leo.core.leoChapters as leoChapters
858 c, p = self.c, self.c.p
859 cc = c.chapterController
860 settings_p = p.insertAsNthChild(0)
861 settings_p.h = '@settings'
862 chapter_p = settings_p.insertAsLastChild()
863 chapter_p.h = '@chapter aaa'
864 node_p = chapter_p.insertAsNthChild(0)
865 node_p.h = 'aaa node 1'
866 # Hack the chaptersDict.
867 cc.chaptersDict['aaa'] = leoChapters.Chapter(c, cc, 'aaa')
868 # Select the chapter.
869 cc.selectChapterByName('aaa')
870 self.assertEqual(c.p.h, 'aaa node 1')
871 p2 = c.p.moveToVisBack(c)
872 self.assertEqual(p2, None)
873 #@+node:ekr.20210830095545.35: *5* TestNodes.test_p_nosentinels
874 def test_p_nosentinels(self):
876 p = self.c.p
877 p.b = textwrap.dedent("""\
879 def not_a_sentinel(x):
880 pass
882 @not_a_sentinel
883 def spam():
884 pass
886 """)
887 self.assertEqual(p.b, p.nosentinels)
888 #@+node:ekr.20210830095545.22: *5* TestNodes.test_p_relinkAsCloneOf
889 def test_p_relinkAsCloneOf(self):
891 # test-outline: root
892 # child clone a
893 # node clone 1
894 # child b
895 # child clone a
896 # node clone 1
897 # child c
898 # node clone 1
899 # child clone a
900 # node clone 1
901 # child b
902 # child clone a
903 # node clone 1
904 c, u = self.c, self.c.undoer
905 p = c.p.next()
906 child_b = g.findNodeAnywhere(c, 'child b')
907 self.assertTrue(child_b)
908 self.assertTrue(child_b.isCloned())
909 #
910 # child_c must *not* be a clone at first.
911 child_c = g.findNodeAnywhere(c, 'child c')
912 self.assertTrue(child_c)
913 self.assertFalse(child_c.isCloned())
914 #
915 # Change the tree.
916 bunch = u.beforeChangeTree(p)
917 child_c._relinkAsCloneOf(child_b)
918 u.afterChangeTree(p, 'relink-clone', bunch)
919 # self.dump_tree('Before...')
920 u.undo()
921 # self.dump_tree('After...')
922 self.assertTrue(child_b.isCloned())
923 self.assertFalse(child_c.isCloned())
925 #@+node:ekr.20210830095545.36: *5* TestNodes.test_p_setBodyString
926 def test_p_setBodyString(self):
927 # Test that c.setBodyString works immediately.
928 c, w = self.c, self.c.frame.body.wrapper
929 next = self.root_p.next()
930 c.setBodyString(next, "after")
931 c.selectPosition(next)
932 s = w.get("1.0", "end")
933 self.assertEqual(s.rstrip(), "after")
934 #@+node:ekr.20210830095545.4: *5* TestNodes.test_position_not_hashable
935 def test_position_not_hashable(self):
936 p = self.c.p
937 try:
938 a = set()
939 a.add(p)
940 assert False, 'Adding position to set should throw exception' # pragma: no cover
941 except TypeError:
942 pass
943 #@+node:ekr.20220307043258.1: *4* TestNodes: Position properties
944 #@+node:ekr.20210830095545.20: *5* TestNodes.test_p_h_with_newlines
945 def test_p_h_with_newlines(self):
946 # Bug https://bugs.launchpad.net/leo-editor/+bug/1245535
947 p = self.c.p
948 p.h = '\nab\nxy\n'
949 self.assertEqual(p.h, 'abxy')
951 #@+node:ekr.20210830095545.18: *5* TestNodes.test_p_properties
952 def test_leoNodes_properties(self):
953 c, p = self.c, self.c.p
954 v = p.v
955 b = p.b
956 p.b = b
957 self.assertEqual(p.b, b)
958 v.b = b
959 self.assertEqual(v.b, b)
960 h = p.h
961 p.h = h
962 self.assertEqual(p.h, h)
963 v.h = h
964 self.assertEqual(v.h, h)
965 for p in c.all_positions():
966 self.assertEqual(p.b, p.bodyString())
967 self.assertEqual(p.v.b, p.v.bodyString())
968 self.assertEqual(p.h, p.headString())
969 self.assertEqual(p.v.h, p.v.headString())
970 #@+node:ekr.20210830095545.37: *5* TestNodes.test_p_u
971 def test_p_u(self):
972 p = self.c.p
973 self.assertEqual(p.u, p.v.u)
974 p.v.u = None
975 self.assertEqual(p.u, {})
976 self.assertEqual(p.v.u, {})
977 d = {'my_plugin': 'val'}
978 p.u = d
979 self.assertEqual(p.u, d)
980 self.assertEqual(p.v.u, d)
981 #@+node:ekr.20220306073301.1: *4* TestNodes: VNode methods
982 #@+node:ekr.20210830095545.39: *5* TestNodes.test_v_atAutoNodeName_and_v_atAutoRstNodeName
983 def test_v_atAutoNodeName_and_v_atAutoRstNodeName(self):
984 p = self.c.p
985 table = (
986 ('@auto-rst rst-file', 'rst-file', 'rst-file'),
987 ('@auto x', 'x', ''),
988 ('xyz', '', ''),
989 )
990 for s, expected1, expected2 in table:
991 result1 = p.v.atAutoNodeName(h=s)
992 result2 = p.v.atAutoRstNodeName(h=s)
993 self.assertEqual(result1, expected1, msg=s)
994 self.assertEqual(result2, expected2, msg=s)
995 #@+node:ekr.20210830095545.19: *5* TestNodes.test_new_vnodes_methods
996 def test_new_vnodes_methods(self):
997 c, p = self.c, self.c.p
998 parent_v = p.parent().v or c.hiddenRootNode
999 p.v.cloneAsNthChild(parent_v, p.childIndex())
1000 v2 = p.v.insertAsFirstChild()
1001 v2.h = 'insertAsFirstChild'
1002 v2 = p.v.insertAsLastChild()
1003 v2.h = 'insertAsLastChild'
1004 v2 = p.v.insertAsNthChild(1)
1005 v2.h = 'insertAsNthChild(1)'
1006 #@+node:ekr.20220307051855.1: *5* TestNodes.test_v_getters
1007 def test_v_getters(self):
1009 v = self.c.p.v
1011 table1 = (
1012 v.anyAtFileNodeName,
1013 v.atAutoNodeName,
1014 v.atCleanNodeName,
1015 v.atEditNodeName,
1016 v.atFileNodeName,
1017 v.atNoSentinelsFileNodeName,
1018 v.atShadowFileNodeName,
1019 v.atSilentFileNodeName,
1020 v.atThinFileNodeName,
1021 v.isAnyAtFileNode,
1022 v.isAtAllNode,
1023 v.isAtAutoNode,
1024 v.isAtAutoRstNode,
1025 v.isAtCleanNode,
1026 v.isAtEditNode,
1027 v.isAtFileNode,
1028 v.isAtIgnoreNode,
1029 v.isAtNoSentinelsFileNode,
1030 v.isAtOthersNode,
1031 v.isAtRstFileNode,
1032 v.isAtShadowFileNode,
1033 v.isAtSilentFileNode,
1034 v.isAtThinFileNode,
1035 v.isMarked,
1036 v.isOrphan,
1037 v.isTopBitSet,
1038 v.isVisited,
1039 )
1040 for func in table1:
1041 self.assertFalse(func(), msg=func.__name__)
1042 table2 = (
1043 v.bodyString,
1044 v.headString,
1045 v.isDirty,
1046 v.isSelected,
1047 v.status,
1048 )
1049 for func in table2:
1050 func() # Don't care about result.
1051 #@-others
1052#@+node:ekr.20220306054624.1: ** class TestNodeIndices(LeoUnitTest)
1053class TestNodeIndices(LeoUnitTest):
1054 """Unit tests for NodeIndices class in leo/core/leoNodes.py."""
1056 test_outline = None # Set by create_test_outline.
1058 #@+others
1059 #@+node:ekr.20220306054659.1: *3* TestNodeIndices.setUp
1060 def setUp(self):
1061 """Create the nodes in the commander."""
1062 super().setUp()
1063 c = self.c
1064 self.create_test_outline()
1065 # Make sure all indices in the test outline have the proper id, set in create_app.
1066 for v in c.all_nodes():
1067 self.assertTrue(v.fileIndex.startswith(g.app.leoID), msg=repr(v.fileIndex))
1068 c.selectPosition(c.rootPosition())
1069 #@+node:ekr.20220306055432.1: *3* TestNodeIndices.test_compute_last_index
1070 def test_compute_last_index(self):
1072 ni = g.app.nodeIndices
1073 ni.compute_last_index(self.c)
1074 self.assertTrue(isinstance(ni.lastIndex, int))
1075 #@+node:ekr.20220306055505.1: *3* TestNodeIndices.test_computeNewIndex
1076 def test_computeNewIndex(self):
1078 ni = g.app.nodeIndices
1079 gnx = ni.computeNewIndex()
1080 self.assertTrue(isinstance(gnx, str))
1081 #@+node:ekr.20220306055506.1: *3* TestNodeIndices.test_scanGnx
1082 def test_scanGnx(self):
1084 ni = g.app.nodeIndices
1085 for s, id1, t1, n1 in (
1086 ('ekr.123', 'ekr', '123', None),
1087 ('ekr.456.2', 'ekr', '456', '2'),
1088 ('', g.app.leoID, None, None),
1089 ):
1090 id2, t2, n2 = ni.scanGnx(s)
1091 self.assertEqual(id1, id2)
1092 self.assertEqual(t1, t2)
1093 self.assertEqual(n1, n2)
1094 #@+node:ekr.20220306055507.1: *3* TestNodeIndices.test_tupleToString
1095 def test_tupleToString(self):
1097 ni = g.app.nodeIndices
1098 for s1, id1, t1, n1 in (
1099 ('ekr.123', 'ekr', '123', None),
1100 ('ekr.456.2', 'ekr', '456', '2'),
1101 (f"{g.app.leoID}.1", g.app.leoID, '1', None),
1102 ):
1103 s = ni.tupleToString((id1, t1, n1))
1104 self.assertEqual(s, s1)
1105 #@+node:ekr.20220306070213.1: *3* TestNodeIndices.test_updateLastIndex
1106 def test_updateLastIndex(self):
1108 ni = g.app.nodeIndices
1109 old_last = ni.lastIndex
1110 for gnx, new_last in (
1111 ('', old_last), # For error logic: no change.
1112 (f"{g.app.leoID}.{ni.timeString}.1000", 1000),
1113 ):
1114 ni.lastIndex = old_last
1115 ni.updateLastIndex(gnx)
1116 self.assertEqual(ni.lastIndex, new_last)
1117 #@-others
1118#@-others
1120#@-leo