#!/usr/bin/python # Copyright 2016 Steven Watanabe # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) # Test for the debugger import BoostBuild import TestCmd import re def split_stdin_stdout(text): """stdin is all text after the prompt up to and including the next newline. Everything else is stdout. stdout may contain regular expressions enclosed in {{}}.""" prompt = re.escape('(b2db) ') pattern = re.compile('(?<=%s)(.*\n)' % prompt) text = text.replace("{{bjam}}", "{{.*}}b2{{(?:\\.exe)?}}") stdin = ''.join(re.findall(pattern, text)) stdout = re.sub(pattern, '', text) outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)') def escape_line(line): line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line) return re.sub(r'\{\{|\}\}', '', line) stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')]) return (stdin,stdout) def run(tester, io): (input,output) = split_stdin_stdout(io) tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re) def make_tester(): return BoostBuild.Tester(["-dconsole"], pass_toolset=False, pass_d0=False, use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re) def test_run(): t = make_tester() t.write("test.jam", """\ UPDATE ; """) run(t, """\ (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Child {{\d+}} exited with status 0 (b2db) quit """) t.cleanup() def test_exit_status(): t = make_tester() t.write("test.jam", """\ EXIT : 1 ; """) run(t, """\ (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Child {{\d+}} exited with status 1 (b2db) quit """) t.cleanup() def test_step(): t = make_tester() t.write("test.jam", """\ rule g ( ) { a = 1 ; b = 2 ; } rule f ( ) { g ; c = 3 ; } f ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( ) at test.jam:8 8 g ; (b2db) step 3 a = 1 ; (b2db) step 4 b = 2 ; (b2db) step 9 c = 3 ; (b2db) quit """) t.cleanup() # Note: step doesn't need to worry about breakpoints, # as it always stops at the next line executed. def test_next(): t = make_tester() t.write("test.jam", """\ rule g ( ) { a = 1 ; } rule f ( ) { g ; b = 2 ; c = 3 ; } rule h ( ) { f ; g ; } h ; d = 4 ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( ) at test.jam:7 7 g ; (b2db) next 8 b = 2 ; (b2db) next 9 c = 3 ; (b2db) next 14 g ; (b2db) next 17 d = 4 ; (b2db) quit """) t.cleanup() def test_next_breakpoint(): """next should stop if it encounters a breakpoint. If the normal end point happens to be a breakpoint, then it should be reported as normal stepping.""" t = make_tester() t.write("test.jam", """\ rule f ( recurse ? ) { if $(recurse) { f ; } a = 1 ; } rule g ( ) { b = 2 ; } f true ; g ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) break g Breakpoint 2 set at g (b2db) break test.jam:4 Breakpoint 3 set at test.jam:4 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( true ) at test.jam:3 3 if $(recurse) { f ; } (b2db) next Breakpoint 1, f ( ) at test.jam:3 3 if $(recurse) { f ; } (b2db) next 4 a = 1 ; (b2db) next 4 a = 1 ; (b2db) next 11 g ; (b2db) next Breakpoint 2, g ( ) at test.jam:8 8 b = 2 ; (b2db) quit """) t.cleanup() def test_finish(): t = make_tester() t.write("test.jam", """\ rule f ( ) { a = 1 ; } rule g ( ) { f ; b = 2 ; i ; } rule h ( ) { g ; i ; } rule i ( ) { c = 3 ; } h ; d = 4 ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( ) at test.jam:3 3 a = 1 ; (b2db) finish 8 b = 2 ; (b2db) finish 14 i ; (b2db) finish 21 d = 4 ; (b2db) quit """) t.cleanup() def test_finish_breakpoints(): """finish should stop when it reaches a breakpoint.""" t = make_tester() t.write("test.jam", """\ rule f ( recurse * ) { if $(recurse) { a = [ f $(recurse[2-]) ] ; } } rule g ( list * ) { for local v in $(list) { x = $(v) ; } } f 1 2 ; g 1 2 ; """) run(t, """\ (b2db) break test.jam:5 Breakpoint 1 set at test.jam:5 (b2db) break test.jam:12 Breakpoint 2 set at test.jam:12 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( 1 2 ) at test.jam:5 5 a = [ f $(recurse[2-]) ] ; (b2db) finish Breakpoint 1, f ( 2 ) at test.jam:5 5 a = [ f $(recurse[2-]) ] ; (b2db) finish 5 a = [ f $(recurse[2-]) ] ; (b2db) finish 16 g 1 2 ; (b2db) finish Breakpoint 2, g ( 1 2 ) at test.jam:12 12 x = $(v) ; (b2db) finish Breakpoint 2, g ( 1 2 ) at test.jam:12 12 x = $(v) ; (b2db) quit """) t.cleanup() def test_continue_breakpoints(): """continue should stop when it reaches a breakpoint""" t = make_tester() t.write("test.jam", """\ rule f ( recurse * ) { if $(recurse) { a = [ f $(recurse[2-]) ] ; } } rule g ( list * ) { for local v in $(list) { x = $(v) ; } } f 1 2 ; g 1 2 ; """) run(t, """\ (b2db) break test.jam:5 Breakpoint 1 set at test.jam:5 (b2db) break test.jam:12 Breakpoint 2 set at test.jam:12 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( 1 2 ) at test.jam:5 5 a = [ f $(recurse[2-]) ] ; (b2db) continue Breakpoint 1, f ( 2 ) at test.jam:5 5 a = [ f $(recurse[2-]) ] ; (b2db) continue Breakpoint 1, f ( 1 2 ) at test.jam:5 5 a = [ f $(recurse[2-]) ] ; (b2db) continue Breakpoint 2, g ( 1 2 ) at test.jam:12 12 x = $(v) ; (b2db) continue Breakpoint 2, g ( 1 2 ) at test.jam:12 12 x = $(v) ; (b2db) quit """) t.cleanup() def test_breakpoints(): """Tests the interaction between the following commands: break, clear, delete, disable, enable""" t = make_tester() t.write("test.jam", """\ rule f ( ) { a = 1 ; } rule g ( ) { b = 2 ; } rule h ( ) { c = 3 ; d = 4 ; } f ; g ; h ; UPDATE ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( ) at test.jam:3 3 a = 1 ; (b2db) kill (b2db) break g Breakpoint 2 set at g (b2db) disable 1 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 2, g ( ) at test.jam:7 7 b = 2 ; (b2db) kill (b2db) enable 1 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( ) at test.jam:3 3 a = 1 ; (b2db) kill (b2db) delete 1 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 2, g ( ) at test.jam:7 7 b = 2 ; (b2db) kill (b2db) break test.jam:12 Breakpoint 3 set at test.jam:12 (b2db) clear g Deleted breakpoint 2 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 3, h ( ) at test.jam:12 12 d = 4 ; (b2db) kill (b2db) clear test.jam:12 Deleted breakpoint 3 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Child {{\d+}} exited with status 0 (b2db) quit """) t.cleanup() def test_breakpoints_running(): """Tests that breakpoints can be added and modified while the program is running.""" t = make_tester() t.write("test.jam", """\ rule f ( ) { a = 1 ; } rule g ( ) { b = 2 ; } rule h ( ) { c = 3 ; d = 4 ; } f ; g ; h ; UPDATE ; """) run(t, """\ (b2db) break test.jam:14 Breakpoint 1 set at test.jam:14 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) break f Breakpoint 2 set at f (b2db) continue Breakpoint 2, f ( ) at test.jam:3 3 a = 1 ; (b2db) kill (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) break g Breakpoint 3 set at g (b2db) disable 2 (b2db) continue Breakpoint 3, g ( ) at test.jam:7 7 b = 2 ; (b2db) kill (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) enable 2 (b2db) continue Breakpoint 2, f ( ) at test.jam:3 3 a = 1 ; (b2db) kill (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) delete 2 (b2db) continue Breakpoint 3, g ( ) at test.jam:7 7 b = 2 ; (b2db) kill (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) break test.jam:12 Breakpoint 4 set at test.jam:12 (b2db) clear g Deleted breakpoint 3 (b2db) continue Breakpoint 4, h ( ) at test.jam:12 12 d = 4 ; (b2db) kill (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:14 14 f ; (b2db) clear test.jam:12 Deleted breakpoint 4 (b2db) continue Child {{\d+}} exited with status 0 (b2db) quit """) t.cleanup() def test_backtrace(): t = make_tester() t.write("test.jam", """\ rule f ( x * : y * : z * ) { return $(x) ; } rule g ( x * : y * : z * ) { return [ f $(x) : $(y) : $(z) ] ; } g 1 : 2 : 3 ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( 1 : 2 : 3 ) at test.jam:3 3 return $(x) ; (b2db) backtrace #0 in f ( 1 : 2 : 3 ) at test.jam:3 #1 in g ( 1 : 2 : 3 ) at test.jam:7 #2 in module scope at test.jam:9 (b2db) quit """) t.cleanup() def test_print(): t = make_tester() t.write("test.jam", """\ rule f ( args * ) { return $(args) ; } f x ; f x y ; """) run(t, """\ (b2db) break f Breakpoint 1 set at f (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, f ( x ) at test.jam:3 3 return $(args) ; (b2db) print $(args) x (b2db) continue Breakpoint 1, f ( x y ) at test.jam:3 3 return $(args) ; (b2db) print $(args) x y (b2db) disable 1 (b2db) print [ f z ] z (b2db) quit """) t.cleanup() def test_run_running(): t = make_tester() t.write("test.jam", """\ UPDATE ; """) run(t, """\ (b2db) break test.jam:1 Breakpoint 1 set at test.jam:1 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:1 1 UPDATE ; (b2db) run -ftest.jam Child {{\d+}} exited with status 0 Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:1 1 UPDATE ; (b2db) quit """) t.cleanup() def test_error_not_running(): t = make_tester() run(t, """\ (b2db) continue The program is not being run. (b2db) step The program is not being run. (b2db) next The program is not being run. (b2db) finish The program is not being run. (b2db) kill The program is not being run. (b2db) backtrace The program is not being run. (b2db) print 1 The program is not being run. (b2db) quit """) t.cleanup() def test_bad_arguments(): t = make_tester() t.write("test.jam", """\ UPDATE ; """) run(t, """\ (b2db) break test.jam:1 Breakpoint 1 set at test.jam:1 (b2db) run -ftest.jam Starting program: {{bjam}} -ftest.jam Breakpoint 1, module scope at test.jam:1 1 UPDATE ; (b2db) continue 1 Too many arguments to continue. (b2db) step 1 Too many arguments to step. (b2db) next 1 Too many arguments to next. (b2db) finish 1 Too many arguments to finish. (b2db) break Missing argument to break. (b2db) break x y Too many arguments to break. (b2db) disable Missing argument to disable. (b2db) disable 1 2 Too many arguments to disable. (b2db) disable x Invalid breakpoint number x. (b2db) disable 2 Unknown breakpoint 2. (b2db) enable Missing argument to enable. (b2db) enable 1 2 Too many arguments to enable. (b2db) enable x Invalid breakpoint number x. (b2db) enable 2 Unknown breakpoint 2. (b2db) delete Missing argument to delete. (b2db) delete 1 2 Too many arguments to delete. (b2db) delete x Invalid breakpoint number x. (b2db) delete 2 Unknown breakpoint 2. (b2db) clear Missing argument to clear. (b2db) clear test.jam:1 test.jam:1 Too many arguments to clear. (b2db) clear test.jam:2 No breakpoint at test.jam:2. (b2db) quit """) t.cleanup() def test_unknown_command(): t = make_tester() run(t, """\ (b2db) xyzzy Unknown command: xyzzy (b2db) gnusto rezrov Unknown command: gnusto (b2db) quit """) t.cleanup() test_run() test_exit_status() test_step() test_next() test_next_breakpoint() test_finish() test_finish_breakpoints() test_continue_breakpoints() test_breakpoints() test_breakpoints_running() test_backtrace() test_print() test_run_running() test_error_not_running() test_bad_arguments() test_unknown_command()