Coverage for core\test_leoAtFile.py : 99%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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
14#@+others
15#@+node:ekr.20210901172446.1: ** class TestAtFile(LeoUnitTest)
16class TestAtFile(LeoUnitTest):
17 """Test cases for leoAtFile.py"""
19 def setUp(self):
20 # Create a pristine instance of the AtFile class.
21 super().setUp()
22 self.at = leoAtFile.AtFile(self.c)
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):
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):
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):
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'
104 s2 = textwrap.dedent('''\
105 # syntax error
106 def spam: # missing parens.
107 pass
108 ''')
110 assert not at.checkPythonSyntax(p, s2), 'fail2'
111 #@+node:ekr.20210905052021.19: *3* TestAtFile.test_directiveKind4
112 def test_directiveKind4(self):
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):
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):
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):
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):
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
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
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):
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):
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):
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):
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):
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):
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):
362 at, p = self.at, self.c.p
363 at.initWriteIvars(p)
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
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)
381 #@+node:ekr.20211104163122.1: *3* TestAtFile.test_putRefLine
382 def test_putRefLine(self):
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)
399 #@+node:ekr.20210905052021.24: *3* TestAtFile.test_remove
400 def test_remove(self):
402 at = self.at
403 exists = g.os_path_exists
405 path = g.os_path_join(g.app.testDir, 'xyzzy')
406 if exists(path):
407 os.remove(path)
409 assert not exists(path)
410 assert not at.remove(path)
412 f = open(path, 'w')
413 f.write('test')
414 f.close()
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):
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):
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):
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):
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):
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):
516 at, p = self.at, self.c.p
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."""
526 def setUp(self):
527 super().setUp()
528 self.x = leoAtFile.FastAtRead(self.c, gnx2vnode={})
530 #@+others
531 #@+node:ekr.20211104162514.1: *3* TestFast.test_afterref
532 def test_afterref(self):
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
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
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
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 ### g.printObj(s, tag='s')
589 ### g.printObj(expected_contents, tag='expected_contents')
590 self.assertEqual(s, expected_contents, msg='mismatch in contents')
592 #@+node:ekr.20211103093332.1: *3* TestFast.test_at_all
593 def test_at_all(self):
595 c, x = self.c, self.x
596 h = '@file /test/test_at_all.txt'
597 root = c.rootPosition()
598 root.h = h # To match contents.
599 #@+<< define contents >>
600 #@+node:ekr.20211103093424.1: *4* << define contents >> (test_at_all)
601 # Be careful: no line should look like a Leo sentinel!
602 contents = textwrap.dedent(f'''\
603 #AT+leo-ver=5-thin
604 #AT+node:{root.gnx}: * {h}
605 # This is Leo's final resting place for dead code.
606 # Much easier to access than a git repo.
608 #AT@language python
609 #AT@killbeautify
610 #AT+all
611 #AT+node:ekr.20211103093559.1: ** node 1
612 Section references can be undefined.
614 LB missing reference >>
615 #AT+node:ekr.20211103093633.1: ** node 2
616 # ATothers doesn't matter
618 ATothers
619 #AT-all
620 #AT@nosearch
621 #AT-leo
622 ''').replace('AT', '@').replace('LB', '<<')
623 #@-<< define contents >>
624 x.read_into_root(contents, path='test', root=root)
625 s = c.atFileCommands.atFileToString(root, sentinels=True)
626 self.assertEqual(contents, s)
627 #@+node:ekr.20211101085019.1: *3* TestFast.test_at_comment (and @first)
628 def test_at_comment(self):
630 c, x = self.c, self.x
631 h = '@file /test/test_at_comment.txt'
632 root = c.rootPosition()
633 root.h = h # To match contents.
634 #@+<< define contents >>
635 #@+node:ekr.20211101090447.1: *4* << define contents >> (test_at_comment)
636 # Be careful: no line should look like a Leo sentinel!
637 contents = textwrap.dedent(f'''\
638 !!! -*- coding: utf-8 -*-
639 !!!AT+leo-ver=5-thin
640 !!!AT+node:{root.gnx}: * {h}
641 !!!AT@first
643 """Classes to read and write @file nodes."""
645 !!!AT@comment !!!
647 !!!AT+LB test >>
648 !!!AT+node:ekr.20211101090015.2: ** LB test >>
649 print('in test section')
650 print('done')
651 !!!AT-LB test >>
653 !!!AT+others
654 !!!AT+node:ekr.20211101090015.3: ** spam
655 def spam():
656 pass
657 !!!AT+node:ekr.20211101090015.4: ** eggs
658 def eggs():
659 pass
660 !!!AT-others
662 !!!AT@language plain
663 !!!AT-leo
664 ''').replace('AT', '@').replace('LB', '<<')
665 #@-<< define contents >>
666 x.read_into_root(contents, path='test', root=root)
667 s = c.atFileCommands.atFileToString(root, sentinels=True)
668 self.assertEqual(contents, s)
669 child1 = root.firstChild()
670 child2 = child1.next()
671 child3 = child2.next()
672 table = (
673 (child1, g.angleBrackets(' test ')),
674 (child2, 'spam'),
675 (child3, 'eggs'),
676 )
677 for child, h in table:
678 self.assertEqual(child.h, h)
679 #@+node:ekr.20211101111636.1: *3* TestFast.test_at_delims
680 def test_at_delims(self):
681 c, x = self.c, self.x
682 h = '@file /test/test_at_delims.txt'
683 root = c.rootPosition()
684 root.h = h # To match contents.
685 #@+<< define contents >>
686 #@+node:ekr.20211101111652.1: *4* << define contents >> (test_at_delims)
687 # Be careful: no line should look like a Leo sentinel!
688 contents = textwrap.dedent(f'''\
689 !! -*- coding: utf-8 -*-
690 #AT+leo-ver=5-thin
691 #AT+node:{root.gnx}: * {h}
692 #AT@first
694 #ATdelims !!
696 !!AT+LB test >>
697 !!AT+node:ekr.20211101111409.2: ** LB test >>
698 print('in test section')
699 print('done')
700 !!AT-LB test >>
702 !!AT+others
703 !!AT+node:ekr.20211101111409.3: ** spam
704 def spam():
705 pass
706 !!AT+node:ekr.20211101111409.4: ** eggs
707 def eggs():
708 pass
709 !!AT-others
711 !!AT@language python
712 !!AT-leo
713 ''').replace('AT', '@').replace('LB', '<<')
714 #@-<< define contents >>
715 x.read_into_root(contents, path='test', root=root)
716 s = c.atFileCommands.atFileToString(root, sentinels=True)
717 self.assertEqual(contents, s)
718 child1 = root.firstChild()
719 child2 = child1.next()
720 child3 = child2.next()
721 table = (
722 (child1, g.angleBrackets(' test ')),
723 (child2, 'spam'),
724 (child3, 'eggs'),
725 )
726 for child, h in table:
727 self.assertEqual(child.h, h)
728 #@+node:ekr.20211103095616.1: *3* TestFast.test_at_last
729 def test_at_last(self):
731 c, x = self.c, self.x
732 h = '@file /test/test_at_last.py'
733 root = c.rootPosition()
734 root.h = h # To match contents.
735 #@+<< define contents >>
736 #@+node:ekr.20211103095959.1: *4* << define contents >> (test_at_last)
737 # Be careful: no line should look like a Leo sentinel!
738 contents = textwrap.dedent(f'''\
739 #AT+leo-ver=5-thin
740 #AT+node:{root.gnx}: * {h}
741 # Test of ATlast
742 #AT+others
743 #AT+node:ekr.20211103095810.1: ** spam
744 def spam():
745 pass
746 #AT-others
747 #AT@language python
748 #AT@last
749 #AT-leo
750 # last line
751 ''').replace('AT', '@')
752 #@-<< define contents >>
753 #@+<< define expected_body >>
754 #@+node:ekr.20211104052937.1: *4* << define expected_body >> (test_at_last)
755 expected_body = textwrap.dedent('''\
756 # Test of ATlast
757 ATothers
758 ATlanguage python
759 ATlast # last line
760 ''').replace('AT', '@')
761 #@-<< define expected_body >>
762 x.read_into_root(contents, path='test', root=root)
763 self.assertEqual(root.b, expected_body)
764 s = c.atFileCommands.atFileToString(root, sentinels=True)
765 self.assertEqual(contents, s)
766 #@+node:ekr.20211103092228.1: *3* TestFast.test_at_others
767 def test_at_others(self):
769 # In particular, we want to test indented @others.
770 c, x = self.c, self.x
771 h = '@file /test/test_at_others'
772 root = c.rootPosition()
773 root.h = h # To match contents.
774 #@+<< define contents >>
775 #@+node:ekr.20211103092228.2: *4* << define contents >> (test_at_others)
776 # Be careful: no line should look like a Leo sentinel!
777 contents = textwrap.dedent(f'''\
778 #AT+leo-ver=5-thin
779 #AT+node:{root.gnx}: * {h}
780 #AT@language python
782 class AtOthersTestClass:
783 #AT+others
784 #AT+node:ekr.20211103092443.1: ** method1
785 def method1(self):
786 pass
787 #AT-others
788 #AT-leo
789 ''').replace('AT', '@').replace('LB', '<<')
790 #@-<< define contents >>
791 x.read_into_root(contents, path='test', root=root)
792 s = c.atFileCommands.atFileToString(root, sentinels=True)
793 self.assertEqual(contents, s)
794 #@+node:ekr.20211031093209.1: *3* TestFast.test_at_section_delim
795 def test_at_section_delim(self):
797 import sys
798 if sys.version_info < (3, 9, 0):
799 self.skipTest('Requires Python 3.9')
801 c, x = self.c, self.x
802 h = '@file /test/at_section_delim.py'
803 root = c.rootPosition()
804 root.h = h # To match contents.
805 #@+<< define contents >>
806 #@+node:ekr.20211101050923.1: *4* << define contents >> (test_at_section_delim)
807 # The contents of a personal test file, slightly altered.
808 contents = textwrap.dedent(f'''\
809 # -*- coding: utf-8 -*-
810 #AT+leo-ver=5-thin
811 #AT+node:{root.gnx}: * {h}
812 #AT@first
814 """Classes to read and write @file nodes."""
816 #AT@section-delims <!< >!>
818 #AT+<!< test >!>
819 #AT+node:ekr.20211029054238.1: ** <!< test >!>
820 print('in test section')
821 print('done')
822 #AT-<!< test >!>
824 #AT+others
825 #AT+node:ekr.20211030052810.1: ** spam
826 def spam():
827 pass
828 #AT+node:ekr.20211030053502.1: ** eggs
829 def eggs():
830 pass
831 #AT-others
833 #AT@language python
834 #AT-leo
835 ''').replace('#AT', '#@')
836 #@-<< define contents >>
837 x.read_into_root(contents, path='test', root=root)
838 s = c.atFileCommands.atFileToString(root, sentinels=True)
839 self.assertEqual(contents, s)
840 child1 = root.firstChild()
841 child2 = child1.next()
842 child3 = child2.next()
843 table = (
844 (child1, '<!< test >!>'),
845 (child2, 'spam'),
846 (child3, 'eggs'),
847 )
848 for child, h in table:
849 self.assertEqual(child.h, h)
850 #@+node:ekr.20211101155930.1: *3* TestFast.test_clones
851 def test_clones(self):
853 c, x = self.c, self.x
854 h = '@file /test/test_clones.py'
855 root = c.rootPosition()
856 root.h = h # To match contents.
857 #@+<< define contents >>
858 #@+node:ekr.20211101155930.2: *4* << define contents >> (test_clones)
859 # Be careful: no line should look like a Leo sentinel!
860 contents = textwrap.dedent(f'''\
861 #AT+leo-ver=5-thin
862 #AT+node:{root.gnx}: * {h}
863 #AT@language python
865 a = 1
867 #AT+others
868 #AT+node:ekr.20211101152631.1: ** cloned node
869 a = 2
870 #AT+node:ekr.20211101153300.1: *3* child
871 a = 3
872 #AT+node:ekr.20211101152631.1: ** cloned node
873 a = 2
874 #AT+node:ekr.20211101153300.1: *3* child
875 a = 3
876 #AT-others
877 #AT-leo
878 ''').replace('AT', '@').replace('LB', '<<')
879 #@-<< define contents >>
880 x.read_into_root(contents, path='test', root=root)
881 s = c.atFileCommands.atFileToString(root, sentinels=True)
882 self.assertEqual(contents, s)
883 child1 = root.firstChild()
884 child2 = child1.next()
885 grand_child1 = child1.firstChild()
886 grand_child2 = child2.firstChild()
887 table = (
888 (child1, 'cloned node'),
889 (child2, 'cloned node'),
890 (grand_child1, 'child'),
891 (grand_child2, 'child'),
892 )
893 for child, h in table:
894 self.assertEqual(child.h, h)
895 self.assertTrue(child1.isCloned())
896 self.assertTrue(child2.isCloned())
897 self.assertEqual(child1.v, child2.v)
898 self.assertFalse(grand_child1.isCloned())
899 self.assertFalse(grand_child2.isCloned())
900 #@+node:ekr.20211103080718.1: *3* TestFast.test_cweb
901 #@@language python
903 def test_cweb(self):
905 c, x = self.c, self.x
906 h = '@file /test/test_cweb.w'
907 root = c.rootPosition()
908 root.h = h # To match contents.
909 #@+<< define contents >>
910 #@+node:ekr.20211103080718.2: *4* << define contents >> (test_cweb)
911 # pylint: disable=anomalous-backslash-in-string
912 contents = textwrap.dedent(f'''\
913 ATq@@+leo-ver=5-thin@>
914 ATq@@+node:{root.gnx}: * @{h}@>
915 ATq@@@@language cweb@>
916 ATq@@@@comment @@q@@ @@>@>
918 % This is limbo in cweb mode... It should be in BSLaTeX mode, not BSc mode.
919 % The following should not be colorized: class,if,else.
921 @* this is a _cweb_ comment. Code is written in BSc.
922 "strings" should not be colorized.
923 It should be colored in BSLaTeX mode.
924 The following are not keywords in latex mode: if, else, etc.
925 Section references are _valid_ in cweb comments!
926 ATq@@+LB section ref 1 >>@>
927 ATq@@+node:ekr.20211103082104.1: ** LB section ref 1 >>@>
928 This is section 1.
929 ATq@@-LB section ref 1 >>@>
930 @c
932 and this is C code. // It is colored in BSLaTeX mode by default.
933 /* This is a C block comment. It may also be colored in restricted BSLaTeX mode. */
935 // Section refs are valid in code too, of course.
936 ATq@@+LB section ref 2 >>@>
937 ATq@@+node:ekr.20211103083538.1: ** LB section ref 2 >>@>
938 This is section 2.
939 ATq@@-LB section ref 2 >>@>
941 BSLaTeX and BSc should not be colored.
942 if else, while, do // C keywords.
943 ATq@@-leo@>
944 ''').replace('AT', '@').replace('LB', '<<').replace('BS', '\\')
945 #@-<< define contents >>
946 x.read_into_root(contents, path='test', root=root)
947 s = c.atFileCommands.atFileToString(root, sentinels=True)
948 self.assertEqual(contents, s)
949 #@+node:ekr.20211101152817.1: *3* TestFast.test_doc_parts
950 def test_doc_parts(self):
952 c, x = self.c, self.x
953 h = '@file /test/test_directives.py'
954 root = c.rootPosition()
955 root.h = h # To match contents.
956 #@+<< define contents >>
957 #@+node:ekr.20211101152843.1: *4* << define contents >> (test_doc_parts)
958 # Be careful: no line should look like a Leo sentinel!
959 contents = textwrap.dedent(f'''\
960 #AT+leo-ver=5-thin
961 #AT+node:{root.gnx}: * {h}
962 #AT@language python
964 a = 1
966 #AT+at A doc part
967 # Line 2.
968 #AT@c
970 #AT+doc
971 # Line 2
972 #
973 # Line 3
974 #AT@c
976 #AT-leo
977 ''').replace('AT', '@').replace('LB', '<<')
978 #@-<< define contents >>
979 x.read_into_root(contents, path='test', root=root)
980 s = c.atFileCommands.atFileToString(root, sentinels=True)
981 self.assertEqual(contents, s)
982 #@+node:ekr.20211101154632.1: *3* TestFast.test_html_doc_part
983 def test_html_doc_part(self):
985 c, x = self.c, self.x
986 h = '@file /test/test_html_doc_part.py'
987 root = c.rootPosition()
988 root.h = h # To match contents.
989 #@+<< define contents >>
990 #@+node:ekr.20211101154651.1: *4* << define contents >> (test_html_doc_part)
991 # Be careful: no line should look like a Leo sentinel!
992 contents = textwrap.dedent(f'''\
993 <!--AT+leo-ver=5-thin-->
994 <!--AT+node:{root.gnx}: * {h}-->
995 <!--AT@language html-->
997 <!--AT+at-->
998 <!--
999 Line 1.
1001 Line 2.
1002 -->
1003 <!--AT@c-->
1004 <!--AT-leo-->
1005 ''').replace('AT', '@').replace('LB', '<<')
1006 #@-<< define contents >>
1007 x.read_into_root(contents, path='test', root=root)
1008 s = c.atFileCommands.atFileToString(root, sentinels=True)
1009 self.assertEqual(contents, s)
1010 #@+node:ekr.20211101180354.1: *3* TestFast.test_verbatim
1011 def test_verbatim(self):
1013 c, x = self.c, self.x
1014 h = '@file /test/test_verbatim.py'
1015 root = c.rootPosition()
1016 root.h = h # To match contents.
1017 #@+<< define contents >>
1018 #@+node:ekr.20211101180404.1: *4* << define contents >> (test_verbatim)
1019 # Be careful: no line should look like a Leo sentinel!
1020 contents = textwrap.dedent(f'''\
1021 #AT+leo-ver=5-thin
1022 #AT+node:{root.gnx}: * {h}
1023 #AT@language python
1024 # Test of @verbatim
1025 print('hi')
1026 #ATverbatim
1027 #AT+node (should be protected by verbatim)
1028 #AT-leo
1029 ''').replace('AT', '@').replace('LB', '<<')
1030 #@-<< define contents >>
1031 #@+<< define expected_body >>
1032 #@+node:ekr.20211106070035.1: *4* << define expected_body >> (test_verbatim)
1033 expected_body = textwrap.dedent('''\
1034 ATlanguage python
1035 # Test of @verbatim
1036 print('hi')
1037 #AT+node (should be protected by verbatim)
1038 ''').replace('AT', '@')
1039 #@-<< define expected_body >>
1040 x.read_into_root(contents, path='test', root=root)
1041 self.assertEqual(root.b, expected_body)
1042 s = c.atFileCommands.atFileToString(root, sentinels=True)
1043 self.assertEqual(contents, s)
1044 #@-others
1045#@-others
1046#@-leo