Coverage for core\test_leoNodes.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

828 statements  

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""" 

6 

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 

12 

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.""" 

17 

18 test_outline = None # Set by create_test_outline. 

19 

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()) 

72 

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) 

81 

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): 

233 

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): 

242 

243 c = self.c 

244 

245 def p_true(p): 

246 return True 

247 

248 def p_false(p): 

249 return False 

250 

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): 

267 

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): 

273 

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): 

277 

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): 

654 

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): 

677 

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): 

684 

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() 

739 

740 #@+node:ekr.20220307043449.1: *5* TestNodes.test_p_getters 

741 def test_p_getters(self): 

742 

743 p = self.c.p 

744 

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): 

875 

876 p = self.c.p 

877 p.b = textwrap.dedent("""\ 

878 

879 def not_a_sentinel(x): 

880 pass 

881 

882 @not_a_sentinel 

883 def spam(): 

884 pass 

885  

886 """) 

887 self.assertEqual(p.b, p.nosentinels) 

888 #@+node:ekr.20210830095545.22: *5* TestNodes.test_p_relinkAsCloneOf 

889 def test_p_relinkAsCloneOf(self): 

890 

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()) 

924 

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') 

950 

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): 

1008 

1009 v = self.c.p.v 

1010 

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.""" 

1055 

1056 test_outline = None # Set by create_test_outline. 

1057 

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): 

1071 

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): 

1077 

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): 

1083 

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): 

1096 

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): 

1107 

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 

1119 

1120#@-leo