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"""
2# Tests for the literate programming (LP) feature.
4We turn code tags with lp as argument after the language into evaluated result blocks incl Ansi Escape sequences, which are processed by xterm on the client, possibly remote fetched.
6"""
7import json pytest
8import os pytest
9import shutil pytest
10import sys pytest
11import time pytest
12import unittest pytest
14import pytest pytest
16from lcdoc.mkdocs.lp import LP, LPPlugin, split_off_fenced_blocks pytest
17from lcdoc.mkdocs.markdown import deindent pytest
19from lcdoc.tools import dirname, exists, project, read_file, write_file pytest
21# initializing it, otherwise init_page fails:
22LP.fn_lp = '/tmp/not_set/not_set.md' pytest
23# import lcdoc.call_flow_logging as cfl
25# just for reference, the unwrapped original:
26# orig_gen_markdown = make_badges.gen_markdown
27# first_test_was_run = []
29now = time.time pytest
31# set (and cache) hard, we are in <root>/tests:
32r = dirname(dirname(__file__)) pytest
33project.root(root=r) pytest
34LP.config = {'docs_dir': r + '/docs'} pytest
37def d_test(): pytest
38 return project.root() + '/tmp/lp_tests'
41fn_test = lambda: d_test() + '/test.md' 41 ↛ exitline 41 didn't run the lambda on line 41pytest
42test_content = '\n'.join(['line0', ' \x1b[38;5;124mline1\x1b[0m', 'line2']) pytest
45plugin = LPPlugin() pytest
48def run_md_page(md, fn, raise_on_errs): pytest
49 rmd = plugin.on_page_markdown(md, page=mock_page(fn), config={}, files=[])
50 return rmd
53def run_lp(md, raise_on_errs=None): pytest
54 old = LP.on_err_keep_running
55 LP.on_err_keep_running = not (raise_on_errs)
56 try:
57 dw, fn = d_test(), fn_test()
58 LP.fn_lp = fn
59 assert '/tmp/' in dw # safety measure
60 shutil.rmtree(dw, ignore_errors=True)
61 os.makedirs(dw, exist_ok=True)
62 write_file(dw + '/test_content', test_content)
63 return run_md_page(md, fn, raise_on_errs=raise_on_errs)
64 finally:
65 LP.on_err_keep_running = old
68def err_msg(l, res): pytest
69 # just to not have too much lines in breakpoint below
70 msg = 'Expected line in produced markdown not in result!\nline: %s\nres: %s'
71 msg = os.environ['PYTEST_CURRENT_TEST'] + '\n' + msg
72 msg = msg % (l, res)
73 print(msg)
74 return msg
77def check_lines_in(res, *blocks): pytest
78 """Are all non empty lines of the blocks in the result?
79 More strict we don't go, for changes in newlines would kill all tests
80 Plus "the dot, which we'll remove some day ..."
81 """
82 res = [l.rstrip() for l in res.splitlines()]
83 # print('check_lines')
84 # print(res)
85 # print('????????')
86 for b in blocks:
87 for l in b.splitlines():
89 if l.strip():
90 kk = l.rstrip()
91 # print('kk')
92 # print(kk)
93 assert kk in res
94 # try:
95 # # res is a LIST - whole line must match
96 # assert l.rstrip() in res
97 # except Exception as ex:
98 # if not sys.stdin.isatty():
99 # raise
100 # err_msg(l, res)
101 # breakpoint()
102 # sys.exit(1)
105class Page: pytest
106 url = 'tests/' pytest
107 title = 'test_lp' pytest
109 class file: pytest
110 abs_src_path = None pytest
113def mock_page(fn): pytest
114 """mock for the mkdocs page object. We need only this in prod code:"""
115 p = Page
116 # being set by the lcd hook decorator:
117 p.stats = {}
118 p.file.abs_src_path = fn
119 p.file.src_path = r + '/' + fn
120 LP.page = p
121 LP.init_page()
122 return p
125class extract(unittest.TestCase): pytest
126 'Detecting LP blocks in Markdown'
128 def extract(md): pytest
129 mock_page(__file__)
130 r = split_off_fenced_blocks(
131 deindent(md), fc_crit=LP.is_lp_block, fc_process=LP.parse_lp_block
132 )
133 return r
135 # the test md, where we play with the header of the first LP block:
136 md = '''
137 hi
138 ```js lp %s
139 foo = bar
140 ```
141 there
143 no lp code:
144 ```py k lp
146 # also not since in other one:
147 ```py lp
148 outer non lp closes here:
149 ```
150 second lp one, indented, ok:
152 ```foo lp
153 second lp code
154 ```
155 '''
157 gen_md = lambda header: extract.md % header 157 ↛ exitline 157 didn't run the lambda on line 157pytest
159 @classmethod pytest
160 def check_norm(cls, header):
162 h = cls.gen_md(header)
163 mds, specs = cls.extract(h)
164 assert mds == [
165 ['hi'],
166 [
167 'there',
168 '',
169 'no lp code:',
170 '```py k lp',
171 '',
172 '# also not since in other one:',
173 '```py lp',
174 'outer non lp closes here:',
175 '```',
176 'second lp one, indented, ok:',
177 '',
178 ],
179 [],
180 ]
182 # all blocks
183 assert isinstance(specs, list)
184 # would indent the result like the original lp block:
185 assert specs[1]['indent'] == ' '
186 assert specs[1]['code'] == [' second lp code']
187 assert specs[1]['lang'] == 'foo'
188 assert specs[1]['nr'] == 1
190 spec = specs[0]
191 assert spec['code'] == ['foo = bar']
192 assert spec['indent'] == ''
193 assert spec['lang'] == 'js'
194 assert spec['nr'] == 0
195 return spec
197 def test_find_block_without_attrs(self): pytest
198 spec = extract.check_norm('')
199 assert spec['args'] == ()
200 assert spec['kwargs'] == {}
202 def test_find_block_with_easy_attrs(self): pytest
203 spec = extract.check_norm('foo=bar i=42 b=true f=1.2')
204 assert spec['args'] == ()
205 assert spec['kwargs'] == {'b': True, 'f': 1.2, 'foo': 'bar', 'i': 42}
207 def test_find_block_with_py_sig_attrs(self): pytest
208 spec = extract.check_norm("'foo', 'bar', a='b', c='23', d={'foo': 'bar'}")
209 assert spec['args'] == ('foo', 'bar')
210 assert spec['kwargs'] == {'a': 'b', 'c': '23', 'd': {'foo': 'bar'}}
212 def test_header_parse_error(self): pytest
213 spec = extract.check_norm("'foo 'bar', a='b', c='23', d={'foo': 'bar'}")
214 assert spec['args'] == LP.header_parse_err
215 assert 'SyntaxError' in repr(spec['kwargs'][LP.py_err])
216 assert spec['kwargs'][LP.easy_args_err]
219class embedded_no_sessions(unittest.TestCase): pytest
220 'Embedded Blocks, not remote fetched'
222 def test_no_session_cmd_out(self): pytest
223 """First a test w/o sessions"""
224 run = 'head %s/test_content |grep --color=never line' % d_test()
225 md = '''
226 ```bash lp
227 %s
228 ```
229 '''
230 cmd = '''
231 === "Cmd"
232 ```console
233 $ %s
234 ```
235 '''
236 out = '''
237 === "Output"
238 <xterm />
239 line0
240 \x1b[38;5;124mline1\x1b[0m
241 line2
242 '''
243 res = run_lp(md % run)
244 check_lines_in(res, cmd % run, out)
246 def test_asserts_work(self): pytest
247 run = 'head %s/test_content |grep --color=never line' % d_test()
248 md = '''
249 ```bash lp asserts=line1
250 %s # lp: expect=line
251 ```
252 '''
253 cmd = '''
254 === "Cmd"
255 ```console
256 $ %s
257 ```
258 '''
259 out = '''
260 === "Output"
261 <xterm />
262 line0
263 \x1b[38;5;124mline1\x1b[0m
264 line2
265 '''
266 res = run_lp(md % run)
267 check_lines_in(res, cmd % run, out)
269 def test_asserts_fail(self): pytest
270 run = 'head %s/test_content |grep --color=never line' % d_test()
271 md = '''
272 ```bash lp asserts=XXXX
273 %s
274 ```
275 '''
276 cmd = '''
277 === "Cmd"
278 ```console
279 $ %s
280 ```
281 '''
282 out = '''
283 === "Output"
284 <xterm />
285 line0
286 \x1b[38;5;124mline1\x1b[0m
287 line2
288 '''
289 with pytest.raises(Exception, match='XXX'):
290 res = run_lp(md % run, raise_on_errs=True)
292 def test_escape(self): pytest
293 """Single Escapes Working?"""
294 md = '''
295 ```bash lp
296 echo -e "With \x1b[1;38;5;124mAnsi\x1b[0m"
297 ```
298 '''
299 cmd = '''
300 === "Cmd"
301 ```console
302 $ echo -e "With \x1b[1;38;5;124mAnsi\x1b[0m"\n
303 ```
304 '''
305 out = '''
306 === "Output"
307 <xterm />
308 With \x1b[1;38;5;124mAnsi\x1b[0m\n
309 '''
310 res = run_lp(md)
311 # print('have res')
312 # print(res)
313 check_lines_in(res, cmd, out)
316class embedded_sessions(unittest.TestCase): pytest
317 """Format mk_cmd_out (cmd and output tabs)
319 This is the default format.
320 """
322 def test_with_paths(self): pytest
323 for k in 'PATH', 'PYTHONPATH':
324 os.environ[k] = os.environ.get(k, '') + ':/bin/baz/foobar' + k
325 md = '''
326 ```bash lp new_session=test with_paths
327 echo $PATH
328 echo $PYTHONPATH
329 ```
330 '''
331 res = run_lp(md)
332 assert '/bin/baz/foobarPATH' in res
333 assert '/bin/baz/foobarPYTHONPATH' in res
335 def test_session_escape(self): pytest
336 """Single Escapes Working?
337 Note: Tmux breaks appart the escape sequences, e.g. bold and color,
338 so we only send one esc - a color:
340 """
341 md = '''
342 ```bash lp new_session=test
343 echo -e "With \x1b[38;5;124mAnsi"
344 ```
345 '''
346 cmd = '''
347 === "Cmd"
348 ```console
349 $ echo -e "With \x1b[38;5;124mAnsi"
350 ```
351 '''
352 out = '''
353 === "Output"
354 <xterm />
355 With \x1b[38;5;124mAnsi"
356 '''
357 res = run_lp(md)
358 check_lines_in(res, cmd)
360 def test_multiline(self): pytest
361 """ With the '> ' at the beginning, we send those blocks as one command
362 the other lines are run one by one, for results per command.
363 """
364 md = '''
365 ```bash lp new_session=test asserts=foobarbaz and line2
367 ip () { echo bar; }
368 cat << FOO > test.pyc
369 > foo$(ip)baz
370 > line2
371 > FOO
372 cat test.pyc
373 ```
374 '''
375 res = run_lp(md, raise_on_errs=True)
376 with open('test.pyc') as fd:
377 assert fd.read().strip() == 'foobarbaz\nline2'
378 os.unlink('test.pyc')
380 def test_multiline_struct(self): pytest
381 """ No '> ' required here"""
382 md = '''
383 ```bash lp new_session=test expect= asserts=foobarbaz and line2
384 [{'cmd': 'ip () { echo bar; }'},
385 {'cmd': """cat << FOO > test.pyc
386 foo$(ip)baz
387 line2
388 FOO"""},
389 {'cmd': 'cat test.pyc'}]
391 ```
392 '''
393 res = run_lp(md, raise_on_errs=True)
394 with open('test.pyc') as fd:
395 assert fd.read().strip() == 'foobarbaz\nline2'
396 os.unlink('test.pyc')
398 def test_cmd_out(self): pytest
399 md = '''
400 ```bash lp new_session=test
401 %s
402 ```
403 '''
404 cmd = '''
405 === "Cmd"
406 ```console
407 $ %s
408 ```
409 '''
410 out = '''
411 === "Output"
412 <xterm />
413 line0
414 line2
415 '''
416 run = 'head %s/test_content |grep --color=never line' % d_test()
417 res = run_lp(md % run)
418 check_lines_in(res, cmd % run, out)
419 # tmux changes the ansi codes slightly and the cmd is
420 # repeated in the output, with prompt:
421 assert '[38;5;124mline1' in res
422 assert run in res.split('Output', 1)[1]
424 def test_session_reuse(self): pytest
425 md1 = '''
426 ```bash lp new_session=test1
427 i=23
428 ```
429 '''
430 res = run_lp(md1)
431 md2 = '''
432 ```bash lp session=test1
433 echo $i
434 ```
435 '''
436 res = run_lp(md2)
437 o = res.split('Output', 1)[1]
438 ind = o.split('<xterm', 1)[0].rsplit('\n', 1)[1]
439 assert '\n' + ind + ' $ echo $i' in o
440 assert '\n' + ind + ' 23' in o
442 def test_custom_expect_and_kill(self): pytest
443 """expect=... will include the expected string in the output.
444 kill terminates the session after last command
445 """
446 md1 = '''
447 ```bash lp new_session=test_foo kill_session=true
448 ['echo bar', {'cmd': 'echo foo', 'expect': 'foo'}]
449 ```
450 '''
451 res = run_lp(md1)
452 out = '''
454 === "Output"
455 <xterm />
456 $ echo bar
457 bar
458 $ echo foo
459 foo
460 '''
461 check_lines_in(res, out)
462 assert 'test_foo' not in os.popen('tmux ls').read()
464 def test_empty_expect_and_ctrl_c(self): pytest
465 """This way you start e.g. a service in foreground, then kill it"""
466 md1 = '''
468 ```bash lp session=test1 fmt=mk_console expect=false
469 [{'cmd': 'sleep 5', 'timeout': 0.5}, 'send-keys: C-c']
470 ```
472 '''
473 t0 = now()
474 res = run_lp(md1)
475 assert now() - t0 > 0.5
476 out = '''
477 $ sleep 5
478 '''
479 print('res--------------')
480 print(res)
481 print('res--------------')
482 check_lines_in(res, out)
483 # cmd output was skipped since result had it anyway:
484 assert len(res.split('$ sleep 5')) == 2
486 def test_asserts_pycond(self): pytest
488 md1 = '''
490 ```bash lp session='test1', asserts='[bar and not foo]'
491 ['echo foo', {'cmd': 'echo bar'}]
492 ```
493 '''
495 with pytest.raises(Exception, match='foo'):
496 res = run_lp(md1, raise_on_errs=True)
498 md1 = '''
500 ```bash lp session='test1', asserts='bar and foo'
501 ['echo foo', {'cmd': 'echo bar'}]
502 ```
503 '''
505 res = run_lp(md1)
506 out = '''
507 === "Output"
508 <xterm />
509 $ echo foo
510 foo
511 $ echo bar
512 bar
513 '''
514 check_lines_in(res, out)
515 md1 = '''
517 ```bash lp session='test1', asserts='[bar and not foo] or echo'
518 ['echo foo', {'cmd': 'echo bar'}]
519 ```
520 '''
521 res = run_lp(md1)
522 check_lines_in(res, out)
524 def test_asserts_inline(self): pytest
525 """Use the documentation tool as a little test framework"""
526 md1 = '''
528 ```bash lp session=test1 asserts=foo
529 ['echo foo', {'cmd': 'echo bar', 'asserts': 'bar'}]
530 ```
531 '''
532 res = run_lp(md1)
533 out = '''
534 === "Output"
535 <xterm />
536 $ echo foo
537 foo
538 $ echo bar
539 bar
540 '''
541 check_lines_in(res, out)
543 md1 = '''
544 ```bash lp session=test1 asserts=foo
545 ['echo foo', {'cmd': 'echo bar', 'asserts': 'XXX'}]
546 ```
547 '''
548 res = run_lp(md1)
549 assert (
550 '!!! error "LP error: Assertion failed: Expected "[\'XXX\']" not found in result'
551 in res
552 )
554 md1 = '''
555 ```bash lp session=test1 asserts=foo
556 ['echo foo', {'cmd': 'echo bar', 'asserts': ['XXX', 'bar']}]
557 ```
558 '''
560 with pytest.raises(Exception, match='XXX'):
561 res = run_lp(md1, raise_on_errs=True)
563 md1 = '''
564 ```bash lp session=test1 asserts=foo
565 ['echo foo', {'cmd': 'echo bar', 'asserts': ['b', 'bar']}]
566 ```
567 '''
568 res = run_lp(md1, raise_on_errs=False)
571class embedded_multiline(unittest.TestCase): pytest
572 def test_simple_multiline_cmd(self): pytest
573 """w/o and w/ tmux"""
574 for k in '', ' session=test':
575 md = '''
576 ```bash lp %s
577 echo foo
578 echo bar
579 ```
580 '''
581 md = md % k
582 cmd = '''
583 === "Cmd"
585 ```console
586 $ echo foo
587 $ echo bar
588 ```
589 '''
590 out = '''
591 === "Output"
592 <xterm />
593 $ echo foo
594 foo
595 $ echo bar
596 bar
597 '''
598 res = run_lp(md)
599 check_lines_in(res, cmd, out)
601 def test_simple_ansi_multiline_cmd(self): pytest
602 """w/o and w/ tmux"""
603 for k in ('',):
604 md = '''
605 ```bash lp %s
606 echo -e '\x1b[32mfoo'
607 echo bar
608 ```
609 '''
610 md = md % k
611 cmd = '''
612 === "Cmd"
614 ```console
615 $ echo -e '\x1b[32mfoo'\n
616 $ echo bar
617 ```
618 '''
619 out = '''
620 === "Output"
621 <xterm />
622 $ echo -e '\x1b[32mfoo'
623 \x1b[32mfoo
624 $ echo bar
625 bar
626 '''
627 res = run_lp(md)
628 check_lines_in(res, cmd, out)
630 def test_simple_multiline_cmd_flat(self): pytest
631 for k in '', ' session=test':
632 md = '''
633 ```bash lp fmt=xt_flat %s
634 echo foo
635 echo bar
636 ```
637 '''
638 md = md % k
639 cmd = '''
640 <xterm />
641 $ echo foo
642 foo
643 $ echo bar
644 bar
645 '''
646 res = run_lp(md)
647 check_lines_in(res, cmd)
650class multi(unittest.TestCase): pytest
651 def test_multicmd_with_one_silent(self): pytest
652 md = '''
653 ```bash lp new_session=test
654 ['n=foo', {"cmd": "echo fubar", "silent": True}, 'echo $n']
655 ```
656 '''
657 cmd = '''
659 === "Cmd"
660 ```console
661 $ n=foo
662 $ echo $n
663 ```
664 '''
665 out = '''
666 === "Output"
667 <xterm />
668 $ n=foo
669 $ echo $n
670 foo
671 '''
673 res = run_lp(md)
674 assert not 'fubar' in res
675 check_lines_in(res, cmd, out)
678def strip_id(s): pytest
679 r, l = [], s.splitlines()
680 while l:
681 line = l.pop(0)
682 if not '<!-- id: ' in line:
683 r.append(line)
684 return '\n'.join(line)
687class python_mode(unittest.TestCase): pytest
688 def test_mode_python_1(self): pytest
689 md = '''
690 ```python lp:python fmt=mk_console
691 assert ctx.get('hide_cmd') == None
692 print('foo')
693 print('bar')
694 ```
695 '''
696 out = '''
697 ```python
698 print('foo')
699 foo
700 '''
701 res = run_lp(md)
702 check_lines_in(res, out)
704 def test_mode_python_cmd_hidden_and_ctx_availability(self): pytest
705 md = '''
706 ```python lp mode=python hide_cmd=True fmt=mk_console
707 assert ctx['hide_cmd']
708 assert ctx['fmt'] == 'mk_console'
709 print('foo')
710 ```
711 '''
712 out = '''
713 ```python
714 foo
715 '''
716 res = run_lp(md)
717 check_lines_in(res, out)
718 assert not 'print' in res
720 def test_mode_python_header_arg_silent(self): pytest
721 md = '''
722 ```python lp mode=python silent=True
723 assert ctx.get('hide_cmd') == None
724 print('foo')
725 ```
726 '''
727 res = run_lp(md)
728 assert len(res.split('<!-- id: ')) == 3
729 assert not strip_id(res).strip()
731 def test_python_new_session(self): pytest
732 """new(!) session same name 2 times:"""
733 md = '''
734 ```python lp:python new_session=pytest1
735 pytest1var='foo'
736 ```
737 '''
738 res = run_lp(md)
739 md = '''
740 ```python lp:python new_session=pytest1
741 print(pytest1var)
742 ```
743 '''
744 res = run_lp(md)
745 assert 'is not defined' in str(res)
747 def test_python_session_reuse(self): pytest
748 md = '''
749 ```python lp:python new_session=pytest1
750 pytest1var='foo'
751 ```
752 '''
753 res = run_lp(md)
754 # other session:
755 md = '''
756 ```python lp:python session=pytest2
757 print(pytest1var)
758 ```
759 '''
760 res = run_lp(md)
761 assert 'is not defined' in str(res)
763 # firstsession:
764 md = '''
765 ```python lp:python session=pytest1
766 print(pytest1var)
767 ```
768 '''
769 res = run_lp(md)
770 assert 'foo' in str(res) and not 'is not defined' in str(res)
773class test_other_modes(unittest.TestCase): pytest
774 def test_mode_make_file(self): pytest
775 fn = '/tmp/test_lp_file_%s' % os.environ['USER']
776 md = '''
777 ```python lp fn=%s mode=make_file
778 foo = bar
779 ```
780 '''
781 md = md % fn
782 res = run_lp(md)
783 out = '''
784 ```python
785 $ cat %s
786 foo = bar
787 ```
788 '''
789 check_lines_in(res, out % fn)
791 def test_mode_show_file(self): pytest
792 fn = '/tmp/test_lp_file_%s' % os.environ['USER']
793 with open(fn, 'w') as fd:
794 fd.write('foo = bar')
795 md = '''
796 ```console lp fn=%s mode=show_file
797 ```
798 '''
799 md = md % fn
800 res = run_lp(md)
801 out = '''
802 ```console
803 $ cat %s
804 foo = bar
805 ```
806 '''
807 check_lines_in(res, out % fn)
810class header_args(unittest.TestCase): pytest
811 def test_presets(self): pytest
812 md = '''
813 ```bash lp foo=dir_repo bar=dir_project
814 stuff
815 ```
816 '''
817 d = project.root()
818 fn = d + '/docs/foo.md'
819 mds, lps = extract.extract(md)
820 mock_page(fn)
821 assert lps[0]['kwargs'] == {'bar': d, 'foo': d}
824class state(unittest.TestCase): pytest
825 def test_assign(self): pytest
826 md = '''
827 ```bash lp
828 name=joe; echo $name
829 ```
830 '''
831 res = run_lp(md)
832 assert ' joe' in res.split('Output', 1)[1]
834 def test__assign_with_session_state(self): pytest
835 """Tmux keeps the state. No NEW session in the second call"""
836 md = '''
837 ```bash lp new_session=test
838 name=joe; echo $name
839 ```
840 '''
841 res = run_lp(md)
842 assert ' joe' in res.split('Output', 1)[1]
844 md = '''
845 ```bash lp session=test
846 echo "hi $name"
847 ```
848 '''
849 res = run_lp(md)
850 assert ' hi joe' in res.split('Output', 1)[1]
853# class lang:
854# def xxtest_lang():
855# md = '''
856# ```js lp session=test timeout=10000
857# echo '{\"json\": true}'
858# ```
859# '''
860# res = run_lp(md)
861# breakpoint() # FIXME BREAKPOINT
862# assert ' joe' in res.split('Output', 1)[1]
865class fetched_mk_cmd_out(unittest.TestCase): pytest
866 """Format mk_cmd_out (cmd and output tabs)
867 'Blocks now fetched from the server'
869 This is the default format.
870 """
872 def test_with_and_without_session_cmd_fetched_out(self): pytest
873 run = 'head %s/test_content |grep --color=never line' % d_test()
874 for k in '', 'new_session=test':
875 md = '''
876 ```bash lp fetch=usecase _k_
877 %s
878 ```
879 '''
880 md = md.replace('_k_', k)
881 cmd = '''
882 === "Cmd"
883 ```console
884 $ %s
885 ```
886 '''
887 out = '''
888 === "Output"
889 <xterm />
890 remote_content
891 ![](./media/test.md_usecase.ansi)
892 '''
893 res = run_lp(md % run)
894 check_lines_in(res, cmd % run, out)
895 # done by the js when live:
896 s = read_file(d_test() + '/media/test.md_usecase.ansi')
897 if k:
898 # tmux we have prompt and it changes the ansi slightly:
899 s = s.split(run, 1)[1]
900 assert '124mline1' in s
901 else:
902 # prompt is in s but rest is ident:
903 s.endswith(test_content)
906extr_head = lambda h: mock_page('x') and LP.extract_header_args(h)[1] 906 ↛ exitline 906 didn't run the lambda on line 906pytest
909class headers_easy(unittest.TestCase): pytest
910 def test_headers_easy_1(self): pytest
911 res = extr_head('```bash lp foo=bar')
912 assert res == {'foo': 'bar'}
914 def test_headers_easy_2(self): pytest
915 res = extr_head('```bash lp foo=bar bar')
916 assert res == {'foo': 'bar', 'bar': True}
918 def test_headers_easy_3(self): pytest
919 res = extr_head('```bash lp foo=bar, bar')
920 assert LP.easy_args_err in str(res)
922 def test_headers_easy_4(self): pytest
923 res = extr_head('```bash lp foo="bar, " bar')
924 assert res == {'bar': True, 'foo': 'bar, '}
926 def test_headers_easy_5(self): pytest
927 res = extr_head("```bash lp foo='=,bar, ' bar")
928 assert res == {'bar': True, 'foo': '=,bar, '}
931class headers_py(unittest.TestCase): pytest
932 def test_headers_py_1(self): pytest
933 res = extr_head('```bash lp foo="bar", bar')
934 assert LP.easy_args_err in str(res)
935 assert 'positional argument follows keyword argument' in str(res)
937 def test_headers_py_2(self): pytest
938 res = extr_head('```bash lp bar, foo="bar"')
939 assert LP.easy_args_err in str(res)
940 assert "name 'bar' is not defined"
943class features(unittest.TestCase): pytest
944 def test_wait(self): pytest
946 md1 = '''
947 ```bash lp new_session=test1
948 ['echo foo', 'wait 0.1', 'echo bar' ]
949 ```
950 '''
951 t = now()
952 res = run_lp(md1)
953 assert now() - t > 0.1
954 out = '''
955 === "Output"
956 <xterm />
957 $ echo foo
958 foo
959 $ echo bar
960 bar
961 '''
962 check_lines_in(res, out)