Coverage for core\test_leoAtFile.py: 100%

437 statements  

« 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.20210901172411.1: * @file ../unittests/core/test_leoAtFile.py 

4#@@first 

5"""Tests of leoAtFile.py""" 

6import os 

7import tempfile 

8import textwrap 

9from leo.core import leoGlobals as g 

10from leo.core import leoAtFile 

11from leo.core import leoBridge 

12from leo.core.leoTest2 import LeoUnitTest 

13 

14#@+others 

15#@+node:ekr.20210901172446.1: ** class TestAtFile(LeoUnitTest) 

16class TestAtFile(LeoUnitTest): 

17 """Test cases for leoAtFile.py""" 

18 

19 def setUp(self): 

20 # Create a pristine instance of the AtFile class. 

21 super().setUp() 

22 self.at = leoAtFile.AtFile(self.c) 

23 

24 #@+others 

25 #@+node:ekr.20200204095726.1: *3* TestAtFile.bridge 

26 def bridge(self): 

27 """Return an instance of Leo's bridge.""" 

28 return leoBridge.controller(gui='nullGui', 

29 loadPlugins=False, 

30 readSettings=False, 

31 silent=True, 

32 verbose=False, 

33 ) 

34 #@+node:ekr.20210905052021.28: *3* TestAtFile.test_at_scanAllDirectives 

35 def test_at_scanAllDirectives(self): 

36 

37 at, c = self.at, self.c 

38 d = at.scanAllDirectives(c.p) 

39 # These are the commander defaults, without any settings. 

40 self.assertEqual(d.get('language'), 'python') 

41 self.assertEqual(d.get('tabwidth'), -4) 

42 self.assertEqual(d.get('pagewidth'), 132) 

43 #@+node:ekr.20210905052021.29: *3* TestAtFile.test_at_scanAllDirectives_minimal_ 

44 def test_at_scanAllDirectives_minimal_(self): 

45 

46 at, c = self.at, self.c 

47 d = at.scanAllDirectives(c.p) 

48 d = c.atFileCommands.scanAllDirectives(c.p) 

49 assert d 

50 #@+node:ekr.20200204094139.1: *3* TestAtFile.test_bug_1469 

51 def test_bug_1469(self): 

52 # Test #1469: saves renaming an external file 

53 # Create a new outline with @file node and save it 

54 bridge = self.bridge() 

55 with tempfile.TemporaryDirectory() as temp_dir: 

56 filename = f"{temp_dir}{os.sep}test_file.leo" 

57 c = bridge.openLeoFile(filename) 

58 p = c.rootPosition() 

59 p.h = '@file 1' 

60 p.b = 'b1' 

61 c.save() 

62 # Rename the @file node and save 

63 p1 = c.rootPosition() 

64 p1.h = "@file 1_renamed" 

65 c.save() 

66 # Remove the original "@file 1" from the disk 

67 external_filename = f"{temp_dir}{os.sep}1" 

68 assert os.path.exists(external_filename) 

69 os.remove(external_filename) 

70 assert not os.path.exists(external_filename) 

71 # Change the @file contents, save and reopen the outline 

72 p1.b = "b_1_changed" 

73 c.save() 

74 c.close() 

75 c = bridge.openLeoFile(c.fileName()) 

76 p1 = c.rootPosition() 

77 self.assertEqual(p1.h, "@file 1_renamed") 

78 #@+node:ekr.20210421035527.1: *3* TestAtFile.test_bug_1889 

79 def test_bug_1889(self): 

80 # Test #1889: Honor ~ in ancestor @path nodes. 

81 # Create a new outline with @file node and save it 

82 bridge = self.bridge() 

83 with tempfile.TemporaryDirectory() as temp_dir: 

84 filename = f"{temp_dir}{os.sep}test_file.leo" 

85 c = bridge.openLeoFile(filename) 

86 root = c.rootPosition() 

87 root.h = '@path ~/sub-directory/' 

88 child = root.insertAsLastChild() 

89 child.h = '@file test_bug_1889.py' 

90 child.b = '@language python\n# test #1889' 

91 path = g.fullPath(c, child) 

92 assert '~' not in path, repr(path) 

93 #@+node:ekr.20210901140645.13: *3* TestAtFile.test_checkPythonSyntax 

94 def test_checkPythonSyntax(self): 

95 

96 at, p = self.at, self.c.p 

97 s = textwrap.dedent('''\ 

98 # no error 

99 def spam(): 

100 pass 

101 ''') 

102 assert at.checkPythonSyntax(p, s), 'fail 1' 

103 

104 s2 = textwrap.dedent('''\ 

105 # syntax error 

106 def spam: # missing parens. 

107 pass 

108 ''') 

109 

110 assert not at.checkPythonSyntax(p, s2), 'fail2' 

111 #@+node:ekr.20210905052021.19: *3* TestAtFile.test_directiveKind4 

112 def test_directiveKind4(self): 

113 

114 at = self.at 

115 at.language = 'python' # Usually set by atFile read/write logic. 

116 table = [ 

117 ('@=', 0, at.noDirective), 

118 ('@', 0, at.atDirective), 

119 ('@ ', 0, at.atDirective), 

120 ('@\t', 0, at.atDirective), 

121 ('@\n', 0, at.atDirective), 

122 ('@all', 0, at.allDirective), 

123 (' @all', 4, at.allDirective), 

124 (' @all', 0, at.allDirective), # 2021/11/04 

125 ("@c", 0, at.cDirective), 

126 ("@code", 0, at.codeDirective), 

127 ("@doc", 0, at.docDirective), 

128 ('@others', 0, at.othersDirective), 

129 (' @others', 4, at.othersDirective), 

130 # ("@end_raw", 0, at.endRawDirective), # #2276. 

131 # ("@raw", 0, at.rawDirective), # #2276. 

132 ] 

133 for name in g.globalDirectiveList: 

134 # Note: entries in g.globalDirectiveList do not start with '@' 

135 if name not in ('all', 'c', 'code', 'doc', 'end_raw', 'others', 'raw',): 

136 table.append(('@' + name, 0, at.miscDirective),) 

137 for s, i, expected in table: 

138 result = at.directiveKind4(s, i) 

139 self.assertEqual(result, expected, msg=f"i: {i}, s: {s!r}") 

140 #@+node:ekr.20210905052021.20: *3* TestAtFile.test_directiveKind4_2 

141 def test_directiveKind4_2(self): 

142 

143 at = self.at 

144 at.language = 'python' # Usually set by atFile read/write logic. 

145 table = ( 

146 (at.othersDirective, '@others'), 

147 (at.othersDirective, '@others\n'), 

148 (at.othersDirective, ' @others'), 

149 (at.miscDirective, '@tabwidth -4'), 

150 (at.miscDirective, '@tabwidth -4\n'), 

151 (at.miscDirective, '@encoding'), 

152 (at.noDirective, '@encoding.setter'), 

153 (at.noDirective, '@encoding("abc")'), 

154 (at.noDirective, 'encoding = "abc"'), 

155 (at.noDirective, '@directive'), # A crucial new test. 

156 (at.noDirective, '@raw'), # 2021/11/04. 

157 ) 

158 for expected, s in table: 

159 result = at.directiveKind4(s, 0) 

160 self.assertEqual(expected, result, msg=repr(s)) 

161 #@+node:ekr.20211106034202.1: *3* TsetAtFile.test_findSectionName 

162 def test_findSectionName(self): 

163 # Test code per #2303. 

164 at, p = self.at, self.c.p 

165 at.initWriteIvars(p) 

166 ref = g.angleBrackets(' abc ') 

167 table = ( 

168 (True, f"{ref}\n"), 

169 (True, f"{ref}"), 

170 (True, f" {ref} \n"), 

171 (False, f"if {ref}:\n"), 

172 (False, f"{ref} # comment\n"), 

173 (False, f"# {ref}\n"), 

174 ) 

175 for valid, s in table: 

176 name, n1, n2 = at.findSectionName(s, 0, p) 

177 self.assertEqual(valid, bool(name), msg=repr(s)) 

178 #@+node:ekr.20210905052021.23: *3* TestAtFile.test_parseLeoSentinel 

179 def test_parseLeoSentinel(self): 

180 

181 at = self.at 

182 table = ( 

183 # start, end, new_df, isThin, encoding 

184 # pre 4.2 formats... 

185 ('#', '', False, True, 'utf-8', '#@+leo-thin-encoding=utf-8.'), 

186 ('#', '', False, False, 'utf-8', '#@+leo-encoding=utf-8.'), 

187 # 4.2 formats... 

188 ('#', '', True, True, 'utf-8', '#@+leo-ver=4-thin-encoding=utf-8,.'), 

189 ('/*', '*/', True, True, 'utf-8', r'\*@+leo-ver=5-thin-encoding=utf-8,.*/'), 

190 ('#', '', True, True, 'utf-8', '#@+leo-ver=5-thin'), 

191 ('#', '', True, True, 'utf-16', '#@+leo-ver=5-thin-encoding=utf-16,.'), 

192 ) 

193 try: 

194 for start, end, new_df, isThin, encoding, s in table: 

195 valid, new_df2, start2, end2, isThin2 = at.parseLeoSentinel(s) 

196 # g.trace('start',start,'end',repr(end),'len(s)',len(s)) 

197 assert valid, s 

198 self.assertEqual(new_df, new_df2, msg=repr(s)) 

199 self.assertEqual(isThin, isThin2, msg=repr(s)) 

200 self.assertEqual(end, end2, msg=repr(s)) 

201 self.assertEqual(at.encoding, encoding, msg=repr(s)) 

202 finally: 

203 at.encoding = 'utf-8' 

204 #@+node:ekr.20211102110237.1: *3* TestAtFile.test_putBody_adjacent_at_doc_part 

205 def test_putBody_adjacent_at_doc_part(self): 

206 

207 at, c = self.at, self.c 

208 root = c.rootPosition() 

209 root.h = '@file test.html' 

210 contents = textwrap.dedent('''\ 

211 @doc 

212 First @doc part 

213 @doc 

214 Second @doc part 

215 ''') 

216 expected = textwrap.dedent('''\ 

217 <!--@+doc--> 

218 <!-- 

219 First @doc part 

220 --> 

221 <!--@+doc--> 

222 <!-- 

223 Second @doc part 

224 --> 

225 ''') 

226 root.b = contents 

227 at.initWriteIvars(root) 

228 at.putBody(root) 

229 result = ''.join(at.outputList) 

230 self.assertEqual(result, expected) 

231 #@+node:ekr.20211102110833.1: *3* TestAtFile.test_putBody_at_all 

232 def test_putBody_at_all(self): 

233 

234 at, c = self.at, self.c 

235 root = c.rootPosition() 

236 root.h = '@file test.py' 

237 child = root.insertAsLastChild() 

238 child.h = 'child' 

239 child.b = textwrap.dedent('''\ 

240 def spam(): 

241 pass 

242 

243 @ A single-line doc part.''') 

244 child.v.fileIndex = '<GNX>' 

245 contents = textwrap.dedent('''\ 

246 ATall 

247 ''').replace('AT', '@') 

248 expected = textwrap.dedent('''\ 

249 #AT+all 

250 #AT+node:<GNX>: ** child 

251 def spam(): 

252 pass 

253 

254 @ A single-line doc part. 

255 #AT-all 

256 ''').replace('AT', '@') 

257 root.b = contents 

258 at.initWriteIvars(root) 

259 at.putBody(root) 

260 result = ''.join(at.outputList) 

261 self.assertEqual(result, expected) 

262 #@+node:ekr.20211102111413.1: *3* TestAtFile.test_putBody_at_all_after_at_doc 

263 def test_putBody_at_all_after_at_doc(self): 

264 

265 at, c = self.at, self.c 

266 root = c.rootPosition() 

267 root.h = '@file test.py' 

268 contents = textwrap.dedent('''\ 

269 ATdoc 

270 doc line 1 

271 ATall 

272 ''').replace('AT', '@') 

273 expected = textwrap.dedent('''\ 

274 #AT+doc 

275 # doc line 1 

276 # ATall 

277 ''').replace('AT', '@') 

278 root.b = contents 

279 at.initWriteIvars(root) 

280 at.putBody(root) 

281 result = ''.join(at.outputList) 

282 self.assertEqual(result, expected) 

283 #@+node:ekr.20211102150707.1: *3* TestAtFile.test_putBody_at_others 

284 def test_putBody_at_others(self): 

285 

286 at, c = self.at, self.c 

287 root = c.rootPosition() 

288 root.h = '@file test_putBody_at_others.py' 

289 child = root.insertAsLastChild() 

290 child.h = 'child' 

291 child.b = '@others\n' 

292 child.v.fileIndex = '<GNX>' 

293 contents = textwrap.dedent('''\ 

294 ATothers 

295 ''').replace('AT', '@') 

296 expected = textwrap.dedent('''\ 

297 #AT+others 

298 #AT+node:<GNX>: ** child 

299 #AT+others 

300 #AT-others 

301 #AT-others 

302 ''').replace('AT', '@') 

303 root.b = contents 

304 at.initWriteIvars(root) 

305 at.putBody(root) 

306 result = ''.join(at.outputList) 

307 self.assertEqual(result, expected) 

308 #@+node:ekr.20211102102024.1: *3* TestAtFile.test_putBody_unterminated_at_doc_part 

309 def test_putBody_unterminated_at_doc_part(self): 

310 

311 at, c = self.at, self.c 

312 root = c.rootPosition() 

313 root.h = '@file test.html' 

314 contents = textwrap.dedent('''\ 

315 @doc 

316 Unterminated @doc parts (not an error) 

317 ''') 

318 expected = textwrap.dedent('''\ 

319 <!--@+doc--> 

320 <!-- 

321 Unterminated @doc parts (not an error) 

322 --> 

323 ''') 

324 root.b = contents 

325 at.initWriteIvars(root) 

326 at.putBody(root) 

327 result = ''.join(at.outputList) 

328 self.assertEqual(result, expected) 

329 #@+node:ekr.20211104154501.1: *3* TestAtFile.test_putCodeLine 

330 def test_putCodeLine(self): 

331 

332 at, p = self.at, self.c.p 

333 at.initWriteIvars(p) 

334 at.startSentinelComment = '#' 

335 table = ( 

336 'Line without newline', 

337 'Line with newline', 

338 ' ', 

339 ) 

340 for line in table: 

341 at.putCodeLine(line, 0) 

342 #@+node:ekr.20211104161927.1: *3* TestAtFile.test_putDelims 

343 def test_putDelims(self): 

344 

345 at, p = self.at, self.c.p 

346 at.initWriteIvars(p) 

347 # Cover the missing code. 

348 directive = '@delims' 

349 s = ' @delims <! !>\n' 

350 at.putDelims(directive, s, 0) 

351 #@+node:ekr.20211104155139.1: *3* TestAtFile.test_putLeadInSentinel 

352 def test_putLeadInSentinel(self): 

353 

354 at, p = self.at, self.c.p 

355 at.initWriteIvars(p) 

356 # Cover the special case code. 

357 s = ' @others\n' 

358 at.putLeadInSentinel(s, 0, 2) 

359 #@+node:ekr.20211104142459.1: *3* TestAtFile.test_putLine 

360 def test_putLine(self): 

361 

362 at, p = self.at, self.c.p 

363 at.initWriteIvars(p) 

364 

365 class Status: # at.putBody defines the status class. 

366 at_comment_seen = False 

367 at_delims_seen = False 

368 at_warning_given = True # Always suppress warning messages. 

369 has_at_others = False 

370 in_code = True 

371 

372 # For now, test only the case that hasn't been covered: 

373 # kind == at.othersDirective and not status.in_code 

374 status = Status() 

375 status.in_code = False 

376 i, kind = 0, at.othersDirective 

377 s = 'A doc line\n' 

378 at.putLine(i, kind, p, s, status) 

379 

380 

381 #@+node:ekr.20211104163122.1: *3* TestAtFile.test_putRefLine 

382 def test_putRefLine(self): 

383 

384 at, p = self.at, self.c.p 

385 at.initWriteIvars(p) 

386 # Create one section definition node. 

387 name1 = g.angleBrackets('section 1') 

388 child1 = p.insertAsLastChild() 

389 child1.h = name1 

390 child1.b = "print('test_putRefLine')\n" 

391 # Create the valid section reference. 

392 s = f" {name1}\n" 

393 # Careful: init n2 and n2. 

394 name, n1, n2 = at.findSectionName(s, 0, p) 

395 self.assertTrue(name) 

396 at.putRefLine(s, 0, n1, n2, name, p) 

397 

398 

399 #@+node:ekr.20210905052021.24: *3* TestAtFile.test_remove 

400 def test_remove(self): 

401 

402 at = self.at 

403 exists = g.os_path_exists 

404 

405 path = g.os_path_join(g.app.testDir, 'xyzzy') 

406 if exists(path): 

407 os.remove(path) # pragma: no cover 

408 

409 assert not exists(path) 

410 assert not at.remove(path) 

411 

412 f = open(path, 'w') 

413 f.write('test') 

414 f.close() 

415 

416 assert exists(path) 

417 assert at.remove(path) 

418 assert not exists(path) 

419 #@+node:ekr.20210905052021.25: *3* TestAtFile.test_replaceFile_different_contents 

420 def test_replaceFile_different_contents(self): 

421 

422 at, c = self.at, self.c 

423 # Duplicate init logic... 

424 at.initCommonIvars() 

425 at.scanAllDirectives(c.p) 

426 encoding = 'utf-8' 

427 try: 

428 # https://stackoverflow.com/questions/23212435 

429 f = tempfile.NamedTemporaryFile(delete=False, encoding=encoding, mode='w') 

430 fn = f.name 

431 contents = 'test contents' 

432 val = at.replaceFile(contents, encoding, fn, at.root) 

433 assert val, val 

434 finally: 

435 f.close() 

436 os.unlink(f.name) 

437 #@+node:ekr.20210905052021.26: *3* TestAtFile.test_replaceFile_no_target_file 

438 def test_replaceFile_no_target_file(self): 

439 

440 at, c = self.at, self.c 

441 # Duplicate init logic... 

442 at.initCommonIvars() 

443 at.scanAllDirectives(c.p) 

444 encoding = 'utf-8' 

445 at.outputFileName = None # The point of this test, but I'm not sure it matters. 

446 try: 

447 # https://stackoverflow.com/questions/23212435 

448 f = tempfile.NamedTemporaryFile(delete=False, encoding=encoding, mode='w') 

449 fn = f.name 

450 contents = 'test contents' 

451 val = at.replaceFile(contents, encoding, fn, at.root) 

452 assert val, val 

453 finally: 

454 f.close() 

455 os.unlink(f.name) 

456 #@+node:ekr.20210905052021.27: *3* TestAtFile.test_replaceFile_same_contents 

457 def test_replaceFile_same_contents(self): 

458 

459 at, c = self.at, self.c 

460 # Duplicate init logic... 

461 at.initCommonIvars() 

462 at.scanAllDirectives(c.p) 

463 encoding = 'utf-8' 

464 try: 

465 # https://stackoverflow.com/questions/23212435 

466 f = tempfile.NamedTemporaryFile(delete=False, encoding=encoding, mode='w') 

467 fn = f.name 

468 contents = 'test contents' 

469 f.write(contents) 

470 f.flush() 

471 val = at.replaceFile(contents, encoding, fn, at.root) 

472 assert not val, val 

473 finally: 

474 f.close() 

475 os.unlink(f.name) 

476 #@+node:ekr.20210905052021.21: *3* TestAtFile.test_setPathUa 

477 def test_setPathUa(self): 

478 

479 at, p = self.at, self.c.p 

480 at.setPathUa(p, 'abc') 

481 d = p.v.tempAttributes 

482 d2 = d.get('read-path') 

483 val1 = d2.get('path') 

484 val2 = at.getPathUa(p) 

485 table = ( 

486 ('d2.get', val1), 

487 ('at.getPathUa', val2), 

488 ) 

489 for kind, val in table: 

490 self.assertEqual(val, 'abc', msg=kind) 

491 #@+node:ekr.20210901140645.14: *3* TestAtFile.test_tabNannyNode 

492 def test_tabNannyNode(self): 

493 

494 at, p = self.at, self.c.p 

495 # Test 1. 

496 s = textwrap.dedent("""\ 

497 # no error 

498 def spam(): 

499 pass 

500 """) 

501 at.tabNannyNode(p, body=s) 

502 # Test 2. 

503 s2 = textwrap.dedent("""\ 

504 # syntax error 

505 def spam: 

506 pass 

507 a = 2 

508 """) 

509 try: 

510 at.tabNannyNode(p, body=s2) 

511 except IndentationError: 

512 pass 

513 #@+node:ekr.20211104154115.1: *3* TestAtFile.test_validInAtOthers 

514 def test_validInAtOthers(self): 

515 

516 at, p = self.at, self.c.p 

517 

518 # Just test the last line. 

519 at.sentinels = False 

520 at.validInAtOthers(p) 

521 #@-others 

522#@+node:ekr.20211031085414.1: ** class TestFastAtRead(LeoUnitTest) 

523class TestFastAtRead(LeoUnitTest): 

524 """Test the FastAtRead class.""" 

525 

526 def setUp(self): 

527 super().setUp() 

528 self.x = leoAtFile.FastAtRead(self.c, gnx2vnode={}) 

529 

530 #@+others 

531 #@+node:ekr.20211104162514.1: *3* TestFast.test_afterref 

532 def test_afterref(self): 

533 

534 c, x = self.c, self.x 

535 h = '@file /test/test_afterLastRef.py' 

536 root = c.rootPosition() 

537 root.h = h # To match contents. 

538 #@+<< define contents >> 

539 #@+node:ekr.20211106112233.1: *4* << define contents >> 

540 # Be careful: no line should look like a Leo sentinel! 

541 contents = textwrap.dedent(f'''\ 

542 #AT+leo-ver=5-thin 

543 #AT+node:{root.gnx}: * {h} 

544 #AT@language python 

545 

546 a = 1 

547 if ( 

548 #AT+LB test >> 

549 #AT+node:ekr.20211107051401.1: ** LB test >> 

550 a == 2 

551 #AT-LB test >> 

552 #ATafterref 

553 ): 

554 a = 2 

555 #AT-leo 

556 ''').replace('AT', '@').replace('LB', '<<') 

557 #@-<< define contents >> 

558 #@+<< define expected_body >> 

559 #@+node:ekr.20211106115654.1: *4* << define expected_body >> 

560 expected_body = textwrap.dedent('''\ 

561 ATlanguage python 

562 

563 a = 1 

564 if ( 

565 LB test >> ): 

566 a = 2 

567 ''').replace('AT', '@').replace('LB', '<<') 

568 #@-<< define expected_body >> 

569 #@+<< define expected_contents >> 

570 #@+node:ekr.20211107053133.1: *4* << define expected_contents >> 

571 # Be careful: no line should look like a Leo sentinel! 

572 expected_contents = textwrap.dedent(f'''\ 

573 #AT+leo-ver=5-thin 

574 #AT+node:{root.gnx}: * {h} 

575 #AT@language python 

576 

577 a = 1 

578 if ( 

579 LB test >> ): 

580 a = 2 

581 #AT-leo 

582 ''').replace('AT', '@').replace('LB', '<<') 

583 #@-<< define expected_contents >> 

584 x.read_into_root(contents, path='test', root=root) 

585 self.assertEqual(root.b, expected_body, msg='mismatch in body') 

586 s = c.atFileCommands.atFileToString(root, sentinels=True) 

587 # Leo has *never* round-tripped the contents without change! 

588 self.assertEqual(s, expected_contents, msg='mismatch in contents') 

589 

590 #@+node:ekr.20211103093332.1: *3* TestFast.test_at_all 

591 def test_at_all(self): 

592 

593 c, x = self.c, self.x 

594 h = '@file /test/test_at_all.txt' 

595 root = c.rootPosition() 

596 root.h = h # To match contents. 

597 #@+<< define contents >> 

598 #@+node:ekr.20211103093424.1: *4* << define contents >> (test_at_all) 

599 # Be careful: no line should look like a Leo sentinel! 

600 contents = textwrap.dedent(f'''\ 

601 #AT+leo-ver=5-thin 

602 #AT+node:{root.gnx}: * {h} 

603 # This is Leo's final resting place for dead code. 

604 # Much easier to access than a git repo. 

605 

606 #AT@language python 

607 #AT@killbeautify 

608 #AT+all 

609 #AT+node:ekr.20211103093559.1: ** node 1 

610 Section references can be undefined. 

611 

612 LB missing reference >> 

613 #AT+node:ekr.20211103093633.1: ** node 2 

614 # ATothers doesn't matter 

615 

616 ATothers 

617 #AT-all 

618 #AT@nosearch 

619 #AT-leo 

620 ''').replace('AT', '@').replace('LB', '<<') 

621 #@-<< define contents >> 

622 x.read_into_root(contents, path='test', root=root) 

623 s = c.atFileCommands.atFileToString(root, sentinels=True) 

624 self.assertEqual(contents, s) 

625 #@+node:ekr.20211101085019.1: *3* TestFast.test_at_comment (and @first) 

626 def test_at_comment(self): 

627 

628 c, x = self.c, self.x 

629 h = '@file /test/test_at_comment.txt' 

630 root = c.rootPosition() 

631 root.h = h # To match contents. 

632 #@+<< define contents >> 

633 #@+node:ekr.20211101090447.1: *4* << define contents >> (test_at_comment) 

634 # Be careful: no line should look like a Leo sentinel! 

635 contents = textwrap.dedent(f'''\ 

636 !!! -*- coding: utf-8 -*- 

637 !!!AT+leo-ver=5-thin 

638 !!!AT+node:{root.gnx}: * {h} 

639 !!!AT@first 

640 

641 """Classes to read and write @file nodes.""" 

642 

643 !!!AT@comment !!! 

644 

645 !!!AT+LB test >> 

646 !!!AT+node:ekr.20211101090015.2: ** LB test >> 

647 print('in test section') 

648 print('done') 

649 !!!AT-LB test >> 

650 

651 !!!AT+others 

652 !!!AT+node:ekr.20211101090015.3: ** spam 

653 def spam(): 

654 pass 

655 !!!AT+node:ekr.20211101090015.4: ** eggs 

656 def eggs(): 

657 pass 

658 !!!AT-others 

659 

660 !!!AT@language plain 

661 !!!AT-leo 

662 ''').replace('AT', '@').replace('LB', '<<') 

663 #@-<< define contents >> 

664 x.read_into_root(contents, path='test', root=root) 

665 s = c.atFileCommands.atFileToString(root, sentinels=True) 

666 self.assertEqual(contents, s) 

667 child1 = root.firstChild() 

668 child2 = child1.next() 

669 child3 = child2.next() 

670 table = ( 

671 (child1, g.angleBrackets(' test ')), 

672 (child2, 'spam'), 

673 (child3, 'eggs'), 

674 ) 

675 for child, h in table: 

676 self.assertEqual(child.h, h) 

677 #@+node:ekr.20211101111636.1: *3* TestFast.test_at_delims 

678 def test_at_delims(self): 

679 c, x = self.c, self.x 

680 h = '@file /test/test_at_delims.txt' 

681 root = c.rootPosition() 

682 root.h = h # To match contents. 

683 #@+<< define contents >> 

684 #@+node:ekr.20211101111652.1: *4* << define contents >> (test_at_delims) 

685 # Be careful: no line should look like a Leo sentinel! 

686 contents = textwrap.dedent(f'''\ 

687 !! -*- coding: utf-8 -*- 

688 #AT+leo-ver=5-thin 

689 #AT+node:{root.gnx}: * {h} 

690 #AT@first 

691 

692 #ATdelims !!SPACE 

693 

694 !!AT+LB test >> 

695 !!AT+node:ekr.20211101111409.2: ** LB test >> 

696 print('in test section') 

697 print('done') 

698 !!AT-LB test >> 

699 

700 !!AT+others 

701 !!AT+node:ekr.20211101111409.3: ** spam 

702 def spam(): 

703 pass 

704 !!AT+node:ekr.20211101111409.4: ** eggs 

705 def eggs(): 

706 pass 

707 !!AT-others 

708 

709 !!AT@language python 

710 !!AT-leo 

711 ''').replace('AT', '@').replace('LB', '<<').replace('SPACE', ' ') 

712 #@-<< define contents >> 

713 x.read_into_root(contents, path='test', root=root) 

714 s = c.atFileCommands.atFileToString(root, sentinels=True) 

715 self.assertEqual(contents, s) 

716 child1 = root.firstChild() 

717 child2 = child1.next() 

718 child3 = child2.next() 

719 table = ( 

720 (child1, g.angleBrackets(' test ')), 

721 (child2, 'spam'), 

722 (child3, 'eggs'), 

723 ) 

724 for child, h in table: 

725 self.assertEqual(child.h, h) 

726 #@+node:ekr.20211103095616.1: *3* TestFast.test_at_last 

727 def test_at_last(self): 

728 

729 c, x = self.c, self.x 

730 h = '@file /test/test_at_last.py' 

731 root = c.rootPosition() 

732 root.h = h # To match contents. 

733 #@+<< define contents >> 

734 #@+node:ekr.20211103095959.1: *4* << define contents >> (test_at_last) 

735 # Be careful: no line should look like a Leo sentinel! 

736 contents = textwrap.dedent(f'''\ 

737 #AT+leo-ver=5-thin 

738 #AT+node:{root.gnx}: * {h} 

739 # Test of ATlast 

740 #AT+others 

741 #AT+node:ekr.20211103095810.1: ** spam 

742 def spam(): 

743 pass 

744 #AT-others 

745 #AT@language python 

746 #AT@last 

747 #AT-leo 

748 # last line 

749 ''').replace('AT', '@') 

750 #@-<< define contents >> 

751 #@+<< define expected_body >> 

752 #@+node:ekr.20211104052937.1: *4* << define expected_body >> (test_at_last) 

753 expected_body = textwrap.dedent('''\ 

754 # Test of ATlast 

755 ATothers 

756 ATlanguage python 

757 ATlast # last line 

758 ''').replace('AT', '@') 

759 #@-<< define expected_body >> 

760 x.read_into_root(contents, path='test', root=root) 

761 self.assertEqual(root.b, expected_body) 

762 s = c.atFileCommands.atFileToString(root, sentinels=True) 

763 self.assertEqual(contents, s) 

764 #@+node:ekr.20211103092228.1: *3* TestFast.test_at_others 

765 def test_at_others(self): 

766 

767 # In particular, we want to test indented @others. 

768 c, x = self.c, self.x 

769 h = '@file /test/test_at_others' 

770 root = c.rootPosition() 

771 root.h = h # To match contents. 

772 #@+<< define contents >> 

773 #@+node:ekr.20211103092228.2: *4* << define contents >> (test_at_others) 

774 # Be careful: no line should look like a Leo sentinel! 

775 contents = textwrap.dedent(f'''\ 

776 #AT+leo-ver=5-thin 

777 #AT+node:{root.gnx}: * {h} 

778 #AT@language python 

779 

780 class AtOthersTestClass: 

781 #AT+others 

782 #AT+node:ekr.20211103092443.1: ** method1 

783 def method1(self): 

784 pass 

785 #AT-others 

786 #AT-leo 

787 ''').replace('AT', '@').replace('LB', '<<') 

788 #@-<< define contents >> 

789 x.read_into_root(contents, path='test', root=root) 

790 s = c.atFileCommands.atFileToString(root, sentinels=True) 

791 self.assertEqual(contents, s) 

792 #@+node:ekr.20211031093209.1: *3* TestFast.test_at_section_delim 

793 def test_at_section_delim(self): 

794 

795 import sys 

796 if sys.version_info < (3, 9, 0): 

797 self.skipTest('Requires Python 3.9') # pragma: no cover 

798 

799 c, x = self.c, self.x 

800 h = '@file /test/at_section_delim.py' 

801 root = c.rootPosition() 

802 root.h = h # To match contents. 

803 #@+<< define contents >> 

804 #@+node:ekr.20211101050923.1: *4* << define contents >> (test_at_section_delim) 

805 # The contents of a personal test file, slightly altered. 

806 contents = textwrap.dedent(f'''\ 

807 # -*- coding: utf-8 -*- 

808 #AT+leo-ver=5-thin 

809 #AT+node:{root.gnx}: * {h} 

810 #AT@first 

811 

812 """Classes to read and write @file nodes.""" 

813 

814 #AT@section-delims <!< >!> 

815 

816 #AT+<!< test >!> 

817 #AT+node:ekr.20211029054238.1: ** <!< test >!> 

818 print('in test section') 

819 print('done') 

820 #AT-<!< test >!> 

821 

822 #AT+others 

823 #AT+node:ekr.20211030052810.1: ** spam 

824 def spam(): 

825 pass 

826 #AT+node:ekr.20211030053502.1: ** eggs 

827 def eggs(): 

828 pass 

829 #AT-others 

830 

831 #AT@language python 

832 #AT-leo 

833 ''').replace('#AT', '#@') 

834 #@-<< define contents >> 

835 x.read_into_root(contents, path='test', root=root) 

836 s = c.atFileCommands.atFileToString(root, sentinels=True) 

837 self.assertEqual(contents, s) 

838 child1 = root.firstChild() 

839 child2 = child1.next() 

840 child3 = child2.next() 

841 table = ( 

842 (child1, '<!< test >!>'), 

843 (child2, 'spam'), 

844 (child3, 'eggs'), 

845 ) 

846 for child, h in table: 

847 self.assertEqual(child.h, h) 

848 #@+node:ekr.20211101155930.1: *3* TestFast.test_clones 

849 def test_clones(self): 

850 

851 c, x = self.c, self.x 

852 h = '@file /test/test_clones.py' 

853 root = c.rootPosition() 

854 root.h = h # To match contents. 

855 #@+<< define contents >> 

856 #@+node:ekr.20211101155930.2: *4* << define contents >> (test_clones) 

857 # Be careful: no line should look like a Leo sentinel! 

858 contents = textwrap.dedent(f'''\ 

859 #AT+leo-ver=5-thin 

860 #AT+node:{root.gnx}: * {h} 

861 #AT@language python 

862 

863 a = 1 

864 

865 #AT+others 

866 #AT+node:ekr.20211101152631.1: ** cloned node 

867 a = 2 

868 #AT+node:ekr.20211101153300.1: *3* child 

869 a = 3 

870 #AT+node:ekr.20211101152631.1: ** cloned node 

871 a = 2 

872 #AT+node:ekr.20211101153300.1: *3* child 

873 a = 3 

874 #AT-others 

875 #AT-leo 

876 ''').replace('AT', '@').replace('LB', '<<') 

877 #@-<< define contents >> 

878 x.read_into_root(contents, path='test', root=root) 

879 s = c.atFileCommands.atFileToString(root, sentinels=True) 

880 self.assertEqual(contents, s) 

881 child1 = root.firstChild() 

882 child2 = child1.next() 

883 grand_child1 = child1.firstChild() 

884 grand_child2 = child2.firstChild() 

885 table = ( 

886 (child1, 'cloned node'), 

887 (child2, 'cloned node'), 

888 (grand_child1, 'child'), 

889 (grand_child2, 'child'), 

890 ) 

891 for child, h in table: 

892 self.assertEqual(child.h, h) 

893 self.assertTrue(child1.isCloned()) 

894 self.assertTrue(child2.isCloned()) 

895 self.assertEqual(child1.v, child2.v) 

896 self.assertFalse(grand_child1.isCloned()) 

897 self.assertFalse(grand_child2.isCloned()) 

898 #@+node:ekr.20211103080718.1: *3* TestFast.test_cweb 

899 #@@language python 

900 

901 def test_cweb(self): 

902 

903 c, x = self.c, self.x 

904 h = '@file /test/test_cweb.w' 

905 root = c.rootPosition() 

906 root.h = h # To match contents. 

907 #@+<< define contents >> 

908 #@+node:ekr.20211103080718.2: *4* << define contents >> (test_cweb) 

909 # pylint: disable=anomalous-backslash-in-string 

910 contents = textwrap.dedent(f'''\ 

911 ATq@@+leo-ver=5-thin@> 

912 ATq@@+node:{root.gnx}: * @{h}@> 

913 ATq@@@@language cweb@> 

914 ATq@@@@comment @@q@@ @@>@> 

915 

916 % This is limbo in cweb mode... It should be in BSLaTeX mode, not BSc mode. 

917 % The following should not be colorized: class,if,else. 

918 

919 @* this is a _cweb_ comment. Code is written in BSc. 

920 "strings" should not be colorized. 

921 It should be colored in BSLaTeX mode. 

922 The following are not keywords in latex mode: if, else, etc. 

923 Section references are _valid_ in cweb comments! 

924 ATq@@+LB section ref 1 >>@> 

925 ATq@@+node:ekr.20211103082104.1: ** LB section ref 1 >>@> 

926 This is section 1. 

927 ATq@@-LB section ref 1 >>@> 

928 @c 

929 

930 and this is C code. // It is colored in BSLaTeX mode by default. 

931 /* This is a C block comment. It may also be colored in restricted BSLaTeX mode. */ 

932 

933 // Section refs are valid in code too, of course. 

934 ATq@@+LB section ref 2 >>@> 

935 ATq@@+node:ekr.20211103083538.1: ** LB section ref 2 >>@> 

936 This is section 2. 

937 ATq@@-LB section ref 2 >>@> 

938 

939 BSLaTeX and BSc should not be colored. 

940 if else, while, do // C keywords. 

941 ATq@@-leo@> 

942 ''').replace('AT', '@').replace('LB', '<<').replace('BS', '\\') 

943 #@-<< define contents >> 

944 x.read_into_root(contents, path='test', root=root) 

945 s = c.atFileCommands.atFileToString(root, sentinels=True) 

946 self.assertEqual(contents, s) 

947 #@+node:ekr.20211101152817.1: *3* TestFast.test_doc_parts 

948 def test_doc_parts(self): 

949 

950 c, x = self.c, self.x 

951 h = '@file /test/test_directives.py' 

952 root = c.rootPosition() 

953 root.h = h # To match contents. 

954 #@+<< define contents >> 

955 #@+node:ekr.20211101152843.1: *4* << define contents >> (test_doc_parts) 

956 # Be careful: no line should look like a Leo sentinel! 

957 contents = textwrap.dedent(f'''\ 

958 #AT+leo-ver=5-thin 

959 #AT+node:{root.gnx}: * {h} 

960 #AT@language python 

961 

962 a = 1 

963 

964 #AT+at A doc part 

965 # Line 2. 

966 #AT@c 

967 

968 #AT+doc 

969 # Line 2 

970 # 

971 # Line 3 

972 #AT@c 

973 

974 #AT-leo 

975 ''').replace('AT', '@').replace('LB', '<<') 

976 #@-<< define contents >> 

977 x.read_into_root(contents, path='test', root=root) 

978 s = c.atFileCommands.atFileToString(root, sentinels=True) 

979 self.assertEqual(contents, s) 

980 #@+node:ekr.20211101154632.1: *3* TestFast.test_html_doc_part 

981 def test_html_doc_part(self): 

982 

983 c, x = self.c, self.x 

984 h = '@file /test/test_html_doc_part.py' 

985 root = c.rootPosition() 

986 root.h = h # To match contents. 

987 #@+<< define contents >> 

988 #@+node:ekr.20211101154651.1: *4* << define contents >> (test_html_doc_part) 

989 # Be careful: no line should look like a Leo sentinel! 

990 contents = textwrap.dedent(f'''\ 

991 <!--AT+leo-ver=5-thin--> 

992 <!--AT+node:{root.gnx}: * {h}--> 

993 <!--AT@language html--> 

994 

995 <!--AT+at--> 

996 <!-- 

997 Line 1. 

998 

999 Line 2. 

1000 --> 

1001 <!--AT@c--> 

1002 <!--AT-leo--> 

1003 ''').replace('AT', '@').replace('LB', '<<') 

1004 #@-<< define contents >> 

1005 x.read_into_root(contents, path='test', root=root) 

1006 s = c.atFileCommands.atFileToString(root, sentinels=True) 

1007 self.assertEqual(contents, s) 

1008 #@+node:ekr.20211101180354.1: *3* TestFast.test_verbatim 

1009 def test_verbatim(self): 

1010 

1011 c, x = self.c, self.x 

1012 h = '@file /test/test_verbatim.py' 

1013 root = c.rootPosition() 

1014 root.h = h # To match contents. 

1015 #@+<< define contents >> 

1016 #@+node:ekr.20211101180404.1: *4* << define contents >> (test_verbatim) 

1017 # Be careful: no line should look like a Leo sentinel! 

1018 contents = textwrap.dedent(f'''\ 

1019 #AT+leo-ver=5-thin 

1020 #AT+node:{root.gnx}: * {h} 

1021 #AT@language python 

1022 # Test of @verbatim 

1023 print('hi') 

1024 #ATverbatim 

1025 #AT+node (should be protected by verbatim) 

1026 #AT-leo 

1027 ''').replace('AT', '@').replace('LB', '<<') 

1028 #@-<< define contents >> 

1029 #@+<< define expected_body >> 

1030 #@+node:ekr.20211106070035.1: *4* << define expected_body >> (test_verbatim) 

1031 expected_body = textwrap.dedent('''\ 

1032 ATlanguage python 

1033 # Test of @verbatim 

1034 print('hi') 

1035 #AT+node (should be protected by verbatim) 

1036 ''').replace('AT', '@') 

1037 #@-<< define expected_body >> 

1038 x.read_into_root(contents, path='test', root=root) 

1039 self.assertEqual(root.b, expected_body) 

1040 s = c.atFileCommands.atFileToString(root, sentinels=True) 

1041 self.assertEqual(contents, s) 

1042 #@-others 

1043#@-others 

1044#@-leo