From 73a32f7d9c48255f77a42f389b7c0dbfd3f88464 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 4 Oct 2020 09:54:36 -0400 Subject: [PATCH] Add initial subactor debug tests --- examples/debugging/subactor_breakpoint.py | 25 ++++++ examples/debugging/subactor_error.py | 16 ++++ tests/test_debugger.py | 100 +++++++++++++++++++--- 3 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 examples/debugging/subactor_breakpoint.py create mode 100644 examples/debugging/subactor_error.py diff --git a/examples/debugging/subactor_breakpoint.py b/examples/debugging/subactor_breakpoint.py new file mode 100644 index 0000000..35db6cf --- /dev/null +++ b/examples/debugging/subactor_breakpoint.py @@ -0,0 +1,25 @@ +import trio +import tractor + + +async def breakpoint_forever(): + """Indefinitely re-enter debugger in child actor. + """ + while True: + await trio.sleep(0.1) + await tractor.breakpoint() + + +async def main(): + + async with tractor.open_nursery() as n: + + portal = await n.run_in_actor( + 'breakpoint_forever', + breakpoint_forever, + ) + await portal.result() + + +if __name__ == '__main__': + tractor.run(main, debug_mode=True) diff --git a/examples/debugging/subactor_error.py b/examples/debugging/subactor_error.py new file mode 100644 index 0000000..67ec1b6 --- /dev/null +++ b/examples/debugging/subactor_error.py @@ -0,0 +1,16 @@ +import tractor + + +async def name_error(): + getattr(doggypants) + + +async def main(): + async with tractor.open_nursery() as n: + + portal = await n.run_in_actor('name_error', name_error) + await portal.result() + + +if __name__ == '__main__': + tractor.run(main, debug_mode=True) diff --git a/tests/test_debugger.py b/tests/test_debugger.py index 9d746c6..a26f9ce 100644 --- a/tests/test_debugger.py +++ b/tests/test_debugger.py @@ -24,14 +24,19 @@ def mk_cmd(ex_name: str) -> str: ) +@pytest.fixture def spawn( - cmd: str, testdir, + arb_addr, ) -> pexpect.spawn: - return testdir.spawn( - cmd=mk_cmd(cmd), - expect_timeout=3, - ) + + def _spawn(cmd): + return testdir.spawn( + cmd=mk_cmd(cmd), + expect_timeout=3, + ) + + return _spawn @pytest.mark.parametrize( @@ -42,12 +47,12 @@ def spawn( ], ids=lambda item: item[1], ) -def test_root_actor_error(testdir, user_in_out): +def test_root_actor_error(spawn, user_in_out): """Demonstrate crash handler entering pdbpp from basic error in root actor. """ user_input, expect_err_str = user_in_out - child = spawn('root_actor_error', testdir) + child = spawn('root_actor_error') # scan for the pdbpp prompt child.expect("\(Pdb\+\+\)") @@ -74,11 +79,11 @@ def test_root_actor_error(testdir, user_in_out): ], ids=lambda item: f'{item[0]} -> {item[1]}', ) -def test_root_actor_bp(testdir, user_in_out): +def test_root_actor_bp(spawn, user_in_out): """Demonstrate breakpoint from in root actor. """ user_input, expect_err_str = user_in_out - child = spawn('root_actor_breakpoint', testdir) + child = spawn('root_actor_breakpoint') # scan for the pdbpp prompt child.expect("\(Pdb\+\+\)") @@ -98,9 +103,78 @@ def test_root_actor_bp(testdir, user_in_out): assert expect_err_str in str(child.before) -def test_subactor_error(testdir): - ... +def test_subactor_error(spawn): + child = spawn('subactor_error') + + # scan for the pdbpp prompt + child.expect("\(Pdb\+\+\)") + + before = str(child.before.decode()) + assert "Attaching to pdb in crashed actor: ('name_error'" in before + + # send user command + # (in this case it's the same for 'continue' vs. 'quit') + child.sendline('continue') + child.expect('\r\n') + + # the debugger should enter a second time in the nursery + # creating actor + + child.expect("\(Pdb\+\+\)") + + before = str(child.before.decode()) + + # root actor gets debugger engaged + assert "Attaching to pdb in crashed actor: ('arbiter'" in before + + # error is a remote error propagated from the subactor + assert "RemoteActorError: ('name_error'" in before + + child.sendline('c') + child.expect('\r\n') + + # process should exit + child.expect(pexpect.EOF) -def test_subactor_breakpoint(testdir): - ... +def test_subactor_breakpoint(spawn): + child = spawn('subactor_breakpoint') + + # scan for the pdbpp prompt + child.expect("\(Pdb\+\+\)") + + before = str(child.before.decode()) + assert "Attaching pdb to actor: ('breakpoint_forever'" in before + + # do some "next" commands to demonstrate recurrent breakpoint + # entries + for _ in range(10): + child.sendline('next') + child.expect("\(Pdb\+\+\)") + + # now run some "continues" to show re-entries + for _ in range(5): + child.sendline('continue') + child.expect("\(Pdb\+\+\)") + before = str(child.before.decode()) + assert "Attaching pdb to actor: ('breakpoint_forever'" in before + + # finally quit the loop + child.sendline('q') + + # child process should exit but parent will capture pdb.BdbQuit + child.expect("\(Pdb\+\+\)") + + before = str(child.before.decode()) + assert "RemoteActorError: ('breakpoint_forever'" in before + assert 'bdb.BdbQuit' in before + + # quit the parent + child.sendline('c') + + # process should exit + child.expect(pexpect.EOF) + + before = str(child.before.decode()) + assert "RemoteActorError: ('breakpoint_forever'" in before + assert 'bdb.BdbQuit' in before