1#!/usr/local/bin/python3.8
2
3# Copyright 2016 Steven Watanabe
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
6
7# Test for the debugger
8
9import BoostBuild
10import TestCmd
11import re
12
13def split_stdin_stdout(text):
14    """stdin is all text after the prompt up to and including
15    the next newline.  Everything else is stdout.  stdout
16    may contain regular expressions enclosed in {{}}."""
17    prompt = re.escape('(b2db) ')
18    pattern = re.compile('(?<=%s)(.*\n)' % prompt)
19    text = text.replace("{{bjam}}", "{{.*}}b2{{(?:\\.exe)?}}")
20    stdin = ''.join(re.findall(pattern, text))
21    stdout = re.sub(pattern, '', text)
22    outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)')
23
24    def escape_line(line):
25        line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line)
26        return re.sub(r'\{\{|\}\}', '', line)
27
28    stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')])
29    return (stdin,stdout)
30
31def run(tester, io):
32    (input,output) = split_stdin_stdout(io)
33    tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re)
34
35def make_tester():
36    return BoostBuild.Tester(["-dconsole"], pass_toolset=False, pass_d0=False,
37        use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re)
38
39def test_run():
40    t = make_tester()
41    t.write("test.jam", """\
42        UPDATE ;
43    """)
44
45    run(t, """\
46(b2db) run -ftest.jam
47Starting program: {{bjam}} -ftest.jam
48Child {{\d+}} exited with status 0
49(b2db) quit
50""")
51
52    t.cleanup()
53
54def test_exit_status():
55    t = make_tester()
56    t.write("test.jam", """\
57        EXIT : 1 ;
58    """)
59    run(t, """\
60(b2db) run -ftest.jam
61Starting program: {{bjam}} -ftest.jam
62
63Child {{\d+}} exited with status 1
64(b2db) quit
65""")
66    t.cleanup()
67
68def test_step():
69    t = make_tester()
70    t.write("test.jam", """\
71        rule g ( )
72        {
73            a = 1 ;
74            b = 2 ;
75        }
76        rule f ( )
77        {
78            g ;
79            c = 3 ;
80        }
81        f ;
82    """)
83    run(t, """\
84(b2db) break f
85Breakpoint 1 set at f
86(b2db) run -ftest.jam
87Starting program: {{bjam}} -ftest.jam
88Breakpoint 1, f ( ) at test.jam:8
898	            g ;
90(b2db) step
913	            a = 1 ;
92(b2db) step
934	            b = 2 ;
94(b2db) step
959	            c = 3 ;
96(b2db) quit
97""")
98    t.cleanup()
99
100# Note: step doesn't need to worry about breakpoints,
101# as it always stops at the next line executed.
102
103def test_next():
104    t = make_tester()
105    t.write("test.jam", """\
106        rule g ( )
107        {
108            a = 1 ;
109        }
110        rule f ( )
111        {
112            g ;
113            b = 2 ;
114            c = 3 ;
115        }
116        rule h ( )
117        {
118            f ;
119            g ;
120        }
121        h ;
122        d = 4 ;
123    """)
124    run(t, """\
125(b2db) break f
126Breakpoint 1 set at f
127(b2db) run -ftest.jam
128Starting program: {{bjam}} -ftest.jam
129Breakpoint 1, f ( ) at test.jam:7
1307	            g ;
131(b2db) next
1328	            b = 2 ;
133(b2db) next
1349	            c = 3 ;
135(b2db) next
13614	            g ;
137(b2db) next
13817	        d = 4 ;
139(b2db) quit
140""")
141    t.cleanup()
142
143def test_next_breakpoint():
144    """next should stop if it encounters a breakpoint.
145    If the normal end point happens to be a breakpoint,
146    then it should be reported as normal stepping."""
147    t = make_tester()
148    t.write("test.jam", """\
149        rule f ( recurse ? )
150        {
151            if $(recurse) { f ; }
152            a = 1 ;
153        }
154        rule g ( )
155        {
156            b = 2 ;
157        }
158        f true ;
159        g ;
160    """)
161    run(t, """\
162(b2db) break f
163Breakpoint 1 set at f
164(b2db) break g
165Breakpoint 2 set at g
166(b2db) break test.jam:4
167Breakpoint 3 set at test.jam:4
168(b2db) run -ftest.jam
169Starting program: {{bjam}} -ftest.jam
170Breakpoint 1, f ( true ) at test.jam:3
1713	            if $(recurse) { f ; }
172(b2db) next
173Breakpoint 1, f ( ) at test.jam:3
1743	            if $(recurse) { f ; }
175(b2db) next
1764	            a = 1 ;
177(b2db) next
1784	            a = 1 ;
179(b2db) next
18011	        g ;
181(b2db) next
182Breakpoint 2, g ( ) at test.jam:8
1838	            b = 2 ;
184(b2db) quit
185""")
186    t.cleanup()
187
188def test_finish():
189    t = make_tester()
190    t.write("test.jam", """\
191        rule f ( )
192        {
193            a = 1 ;
194        }
195        rule g ( )
196        {
197            f ;
198            b = 2 ;
199            i ;
200        }
201        rule h ( )
202        {
203            g ;
204            i ;
205        }
206        rule i ( )
207        {
208            c = 3 ;
209        }
210        h ;
211        d = 4 ;
212    """)
213    run(t, """\
214(b2db) break f
215Breakpoint 1 set at f
216(b2db) run -ftest.jam
217Starting program: {{bjam}} -ftest.jam
218Breakpoint 1, f ( ) at test.jam:3
2193	            a = 1 ;
220(b2db) finish
2218	            b = 2 ;
222(b2db) finish
22314	            i ;
224(b2db) finish
22521	        d = 4 ;
226(b2db) quit
227""")
228    t.cleanup()
229
230def test_finish_breakpoints():
231    """finish should stop when it reaches a breakpoint."""
232    t = make_tester()
233    t.write("test.jam", """\
234        rule f ( recurse * )
235        {
236            if $(recurse)
237            {
238                a = [ f $(recurse[2-]) ] ;
239            }
240        }
241        rule g ( list * )
242        {
243            for local v in $(list)
244            {
245                x = $(v) ;
246            }
247        }
248        f 1 2 ;
249        g 1 2 ;
250    """)
251    run(t, """\
252(b2db) break test.jam:5
253Breakpoint 1 set at test.jam:5
254(b2db) break test.jam:12
255Breakpoint 2 set at test.jam:12
256(b2db) run -ftest.jam
257Starting program: {{bjam}} -ftest.jam
258Breakpoint 1, f ( 1 2 ) at test.jam:5
2595	                a = [ f $(recurse[2-]) ] ;
260(b2db) finish
261Breakpoint 1, f ( 2 ) at test.jam:5
2625	                a = [ f $(recurse[2-]) ] ;
263(b2db) finish
2645	                a = [ f $(recurse[2-]) ] ;
265(b2db) finish
26616	        g 1 2 ;
267(b2db) finish
268Breakpoint 2, g ( 1 2 ) at test.jam:12
26912	                x = $(v) ;
270(b2db) finish
271Breakpoint 2, g ( 1 2 ) at test.jam:12
27212	                x = $(v) ;
273(b2db) quit
274""")
275    t.cleanup()
276
277def test_continue_breakpoints():
278    """continue should stop when it reaches a breakpoint"""
279    t = make_tester()
280    t.write("test.jam", """\
281        rule f ( recurse * )
282        {
283            if $(recurse)
284            {
285                a = [ f $(recurse[2-]) ] ;
286            }
287        }
288        rule g ( list * )
289        {
290            for local v in $(list)
291            {
292                x = $(v) ;
293            }
294        }
295        f 1 2 ;
296        g 1 2 ;
297    """)
298    run(t, """\
299(b2db) break test.jam:5
300Breakpoint 1 set at test.jam:5
301(b2db) break test.jam:12
302Breakpoint 2 set at test.jam:12
303(b2db) run -ftest.jam
304Starting program: {{bjam}} -ftest.jam
305Breakpoint 1, f ( 1 2 ) at test.jam:5
3065	                a = [ f $(recurse[2-]) ] ;
307(b2db) continue
308Breakpoint 1, f ( 2 ) at test.jam:5
3095	                a = [ f $(recurse[2-]) ] ;
310(b2db) continue
311Breakpoint 1, f ( 1 2 ) at test.jam:5
3125	                a = [ f $(recurse[2-]) ] ;
313(b2db) continue
314Breakpoint 2, g ( 1 2 ) at test.jam:12
31512	                x = $(v) ;
316(b2db) continue
317Breakpoint 2, g ( 1 2 ) at test.jam:12
31812	                x = $(v) ;
319(b2db) quit
320""")
321    t.cleanup()
322
323def test_breakpoints():
324    """Tests the interaction between the following commands:
325    break, clear, delete, disable, enable"""
326    t = make_tester()
327    t.write("test.jam", """\
328        rule f ( )
329        {
330            a = 1 ;
331        }
332        rule g ( )
333        {
334            b = 2 ;
335        }
336        rule h ( )
337        {
338            c = 3 ;
339            d = 4 ;
340        }
341        f ;
342        g ;
343        h ;
344        UPDATE ;
345    """)
346    run(t, """\
347(b2db) break f
348Breakpoint 1 set at f
349(b2db) run -ftest.jam
350Starting program: {{bjam}} -ftest.jam
351Breakpoint 1, f ( ) at test.jam:3
3523	            a = 1 ;
353(b2db) kill
354(b2db) break g
355Breakpoint 2 set at g
356(b2db) disable 1
357(b2db) run -ftest.jam
358Starting program: {{bjam}} -ftest.jam
359Breakpoint 2, g ( ) at test.jam:7
3607	            b = 2 ;
361(b2db) kill
362(b2db) enable 1
363(b2db) run -ftest.jam
364Starting program: {{bjam}} -ftest.jam
365Breakpoint 1, f ( ) at test.jam:3
3663	            a = 1 ;
367(b2db) kill
368(b2db) delete 1
369(b2db) run -ftest.jam
370Starting program: {{bjam}} -ftest.jam
371Breakpoint 2, g ( ) at test.jam:7
3727	            b = 2 ;
373(b2db) kill
374(b2db) break test.jam:12
375Breakpoint 3 set at test.jam:12
376(b2db) clear g
377Deleted breakpoint 2
378(b2db) run -ftest.jam
379Starting program: {{bjam}} -ftest.jam
380Breakpoint 3, h ( ) at test.jam:12
38112	            d = 4 ;
382(b2db) kill
383(b2db) clear test.jam:12
384Deleted breakpoint 3
385(b2db) run -ftest.jam
386Starting program: {{bjam}} -ftest.jam
387Child {{\d+}} exited with status 0
388(b2db) quit
389""")
390    t.cleanup()
391
392def test_breakpoints_running():
393    """Tests that breakpoints can be added and modified
394    while the program is running."""
395    t = make_tester()
396    t.write("test.jam", """\
397        rule f ( )
398        {
399            a = 1 ;
400        }
401        rule g ( )
402        {
403            b = 2 ;
404        }
405        rule h ( )
406        {
407            c = 3 ;
408            d = 4 ;
409        }
410        f ;
411        g ;
412        h ;
413        UPDATE ;
414    """)
415    run(t, """\
416(b2db) break test.jam:14
417Breakpoint 1 set at test.jam:14
418(b2db) run -ftest.jam
419Starting program: {{bjam}} -ftest.jam
420Breakpoint 1, module scope at test.jam:14
42114	        f ;
422(b2db) break f
423Breakpoint 2 set at f
424(b2db) continue
425Breakpoint 2, f ( ) at test.jam:3
4263	            a = 1 ;
427(b2db) kill
428(b2db) run -ftest.jam
429Starting program: {{bjam}} -ftest.jam
430Breakpoint 1, module scope at test.jam:14
43114	        f ;
432(b2db) break g
433Breakpoint 3 set at g
434(b2db) disable 2
435(b2db) continue
436Breakpoint 3, g ( ) at test.jam:7
4377	            b = 2 ;
438(b2db) kill
439(b2db) run -ftest.jam
440Starting program: {{bjam}} -ftest.jam
441Breakpoint 1, module scope at test.jam:14
44214	        f ;
443(b2db) enable 2
444(b2db) continue
445Breakpoint 2, f ( ) at test.jam:3
4463	            a = 1 ;
447(b2db) kill
448(b2db) run -ftest.jam
449Starting program: {{bjam}} -ftest.jam
450Breakpoint 1, module scope at test.jam:14
45114	        f ;
452(b2db) delete 2
453(b2db) continue
454Breakpoint 3, g ( ) at test.jam:7
4557	            b = 2 ;
456(b2db) kill
457(b2db) run -ftest.jam
458Starting program: {{bjam}} -ftest.jam
459Breakpoint 1, module scope at test.jam:14
46014	        f ;
461(b2db) break test.jam:12
462Breakpoint 4 set at test.jam:12
463(b2db) clear g
464Deleted breakpoint 3
465(b2db) continue
466Breakpoint 4, h ( ) at test.jam:12
46712	            d = 4 ;
468(b2db) kill
469(b2db) run -ftest.jam
470Starting program: {{bjam}} -ftest.jam
471Breakpoint 1, module scope at test.jam:14
47214	        f ;
473(b2db) clear test.jam:12
474Deleted breakpoint 4
475(b2db) continue
476Child {{\d+}} exited with status 0
477(b2db) quit
478""")
479    t.cleanup()
480
481def test_backtrace():
482    t = make_tester()
483    t.write("test.jam", """\
484        rule f ( x * : y * : z * )
485        {
486            return $(x) ;
487        }
488        rule g ( x * : y * : z * )
489        {
490            return [ f $(x) : $(y) : $(z) ] ;
491        }
492        g 1 : 2 : 3 ;
493    """)
494    run(t, """\
495(b2db) break f
496Breakpoint 1 set at f
497(b2db) run -ftest.jam
498Starting program: {{bjam}} -ftest.jam
499Breakpoint 1, f ( 1 : 2 : 3 ) at test.jam:3
5003	            return $(x) ;
501(b2db) backtrace
502#0  in f ( 1 : 2 : 3 ) at test.jam:3
503#1  in g ( 1 : 2 : 3 ) at test.jam:7
504#2  in module scope at test.jam:9
505(b2db) quit
506""")
507    t.cleanup()
508
509def test_print():
510    t = make_tester()
511    t.write("test.jam", """\
512        rule f ( args * )
513        {
514            return $(args) ;
515        }
516        f x ;
517        f x y ;
518    """)
519
520    run(t, """\
521(b2db) break f
522Breakpoint 1 set at f
523(b2db) run -ftest.jam
524Starting program: {{bjam}} -ftest.jam
525Breakpoint 1, f ( x ) at test.jam:3
5263	            return $(args) ;
527(b2db) print $(args)
528x
529(b2db) continue
530Breakpoint 1, f ( x y ) at test.jam:3
5313	            return $(args) ;
532(b2db) print $(args)
533x y
534(b2db) disable 1
535(b2db) print [ f z ]
536z
537(b2db) quit
538""")
539
540    t.cleanup()
541
542def test_run_running():
543    t = make_tester()
544    t.write("test.jam", """\
545        UPDATE ;
546    """)
547
548    run(t, """\
549(b2db) break test.jam:1
550Breakpoint 1 set at test.jam:1
551(b2db) run -ftest.jam
552Starting program: {{bjam}} -ftest.jam
553Breakpoint 1, module scope at test.jam:1
5541	        UPDATE ;
555(b2db) run -ftest.jam
556Child {{\d+}} exited with status 0
557Starting program: {{bjam}} -ftest.jam
558Breakpoint 1, module scope at test.jam:1
5591	        UPDATE ;
560(b2db) quit
561""")
562
563    t.cleanup()
564
565def test_error_not_running():
566    t = make_tester()
567    run(t, """\
568(b2db) continue
569The program is not being run.
570(b2db) step
571The program is not being run.
572(b2db) next
573The program is not being run.
574(b2db) finish
575The program is not being run.
576(b2db) kill
577The program is not being run.
578(b2db) backtrace
579The program is not being run.
580(b2db) print 1
581The program is not being run.
582(b2db) quit
583""")
584
585    t.cleanup()
586
587def test_bad_arguments():
588    t = make_tester()
589    t.write("test.jam", """\
590        UPDATE ;
591    """)
592
593    run(t, """\
594(b2db) break test.jam:1
595Breakpoint 1 set at test.jam:1
596(b2db) run -ftest.jam
597Starting program: {{bjam}} -ftest.jam
598Breakpoint 1, module scope at test.jam:1
5991	        UPDATE ;
600(b2db) continue 1
601Too many arguments to continue.
602(b2db) step 1
603Too many arguments to step.
604(b2db) next 1
605Too many arguments to next.
606(b2db) finish 1
607Too many arguments to finish.
608(b2db) break
609Missing argument to break.
610(b2db) break x y
611Too many arguments to break.
612(b2db) disable
613Missing argument to disable.
614(b2db) disable 1 2
615Too many arguments to disable.
616(b2db) disable x
617Invalid breakpoint number x.
618(b2db) disable 2
619Unknown breakpoint 2.
620(b2db) enable
621Missing argument to enable.
622(b2db) enable 1 2
623Too many arguments to enable.
624(b2db) enable x
625Invalid breakpoint number x.
626(b2db) enable 2
627Unknown breakpoint 2.
628(b2db) delete
629Missing argument to delete.
630(b2db) delete 1 2
631Too many arguments to delete.
632(b2db) delete x
633Invalid breakpoint number x.
634(b2db) delete 2
635Unknown breakpoint 2.
636(b2db) clear
637Missing argument to clear.
638(b2db) clear test.jam:1 test.jam:1
639Too many arguments to clear.
640(b2db) clear test.jam:2
641No breakpoint at test.jam:2.
642(b2db) quit
643""")
644
645    t.cleanup()
646
647def test_unknown_command():
648    t = make_tester()
649    run(t, """\
650(b2db) xyzzy
651Unknown command: xyzzy
652(b2db) gnusto rezrov
653Unknown command: gnusto
654(b2db) quit
655""")
656
657    t.cleanup()
658
659test_run()
660test_exit_status()
661test_step()
662test_next()
663test_next_breakpoint()
664test_finish()
665test_finish_breakpoints()
666test_continue_breakpoints()
667test_breakpoints()
668test_breakpoints_running()
669test_backtrace()
670test_print()
671test_run_running()
672test_error_not_running()
673test_bad_arguments()
674test_unknown_command()
675