Add a multi-subactor test with nesting

debug_tests
Tyler Goodlet 2020-10-04 17:31:02 -04:00
parent a2151cdd4d
commit e387e8b322
3 changed files with 137 additions and 48 deletions

View File

@ -1,39 +0,0 @@
import tractor
import trio
async def bubble():
print('IN BUBBLE')
while True:
await trio.sleep(.1)
await tractor.breakpoint()
async def name_error():
getattr(doggy)
async def main():
"""The main ``tractor`` routine.
"""
async with tractor.open_nursery() as n:
portal1 = await n.run_in_actor('bubble', bubble)
portal = await n.run_in_actor('name_error', name_error)
await portal1.result()
await portal.result()
# The ``async with`` will unblock here since the 'some_linguist'
# actor has completed its main task ``cellar_door``.
# TODO:
# - recurrent entry from single actor
# - recurrent entry to breakpoint() from single actor *after* and an
# error
# - root error alongside child errors
# - recurrent root errors
if __name__ == '__main__':
tractor.run(main, loglevel='info', debug_mode=True)

View File

@ -0,0 +1,51 @@
import tractor
import trio
async def breakpoint_forever():
"Indefinitely re-enter debugger in child actor."
while True:
await trio.sleep(0.1)
await tractor.breakpoint()
async def name_error():
"Raise a ``NameError``"
getattr(doggypants)
async def spawn_error():
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as n:
portal = await n.run_in_actor('name_error_1', name_error)
return await portal.result()
async def main():
"""The main ``tractor`` routine.
The process tree should look as approximately as follows:
-python examples/debugging/multi_subactors.py
|-python -m tractor._child --uid ('name_error', 'a7caf490 ...)
|-python -m tractor._child --uid ('bp_forever', '1f787a7e ...)
`-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
`-python -m tractor._child --uid ('name_error', '3391222c ...)
"""
async with tractor.open_nursery() as n:
# spawn both actors
portal1 = await n.run_in_actor('bp_forever', breakpoint_forever)
portal = await n.run_in_actor('name_error', name_error)
portal2 = await n.run_in_actor('spawn_error', spawn_error)
# attempt to collect results (which raises error in parent)
# still has some issues where the parent seems to get stuck
# await portal.result()
# await portal1.result()
# await portal2.result()
if __name__ == '__main__':
tractor.run(main, debug_mode=True)

View File

@ -9,6 +9,17 @@ import pexpect
from .test_docs_examples import repodir
# TODO:
# - recurrent entry from single actor
# - recurrent entry to breakpoint() from single actor *after* and an
# error
# - root error before child errors
# - root error after child errors
# - root error before child breakpoint
# - root error after child breakpoint
# - recurrent root errors
def examples_dir():
"""Return the abspath to the examples directory.
"""
@ -45,7 +56,7 @@ def spawn(
('c', 'AssertionError'),
('q', 'AssertionError'),
],
ids=lambda item: item[1],
ids=lambda item: f'{item[0]} -> {item[1]}',
)
def test_root_actor_error(spawn, user_in_out):
"""Demonstrate crash handler entering pdbpp from basic error in root actor.
@ -55,7 +66,7 @@ def test_root_actor_error(spawn, user_in_out):
child = spawn('root_actor_error')
# scan for the pdbpp prompt
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
# make sure expected logging and error arrives
assert 'TTY lock acquired' in str(child.before)
@ -86,7 +97,7 @@ def test_root_actor_bp(spawn, user_in_out):
child = spawn('root_actor_breakpoint')
# scan for the pdbpp prompt
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
assert 'Error' not in str(child.before)
@ -104,10 +115,12 @@ def test_root_actor_bp(spawn, user_in_out):
def test_subactor_error(spawn):
"Single subactor raising an error"
child = spawn('subactor_error')
# scan for the pdbpp prompt
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching to pdb in crashed actor: ('name_error'" in before
@ -120,7 +133,7 @@ def test_subactor_error(spawn):
# the debugger should enter a second time in the nursery
# creating actor
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
@ -138,10 +151,12 @@ def test_subactor_error(spawn):
def test_subactor_breakpoint(spawn):
"Single subactor with an infinite breakpoint loop"
child = spawn('subactor_breakpoint')
# scan for the pdbpp prompt
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
@ -150,12 +165,12 @@ def test_subactor_breakpoint(spawn):
# entries
for _ in range(10):
child.sendline('next')
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
# now run some "continues" to show re-entries
for _ in range(5):
child.sendline('continue')
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
@ -163,7 +178,7 @@ def test_subactor_breakpoint(spawn):
child.sendline('q')
# child process should exit but parent will capture pdb.BdbQuit
child.expect("\(Pdb\+\+\)")
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "RemoteActorError: ('breakpoint_forever'" in before
@ -178,3 +193,65 @@ def test_subactor_breakpoint(spawn):
before = str(child.before.decode())
assert "RemoteActorError: ('breakpoint_forever'" in before
assert 'bdb.BdbQuit' in before
def test_multi_subactors(spawn):
"""Multiple subactors, both erroring and breakpointing as well as
a nested subactor erroring.
"""
child = spawn(r'multi_subactors')
# scan for the pdbpp prompt
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching pdb to actor: ('bp_forever'" in before
# do some "next" commands to demonstrate recurrent breakpoint
# entries
for _ in range(10):
child.sendline('next')
child.expect(r"\(Pdb\+\+\)")
# continue to next error
child.sendline('c')
# first name_error failure
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "NameError" in before
# continue again
child.sendline('c')
# 2nd name_error failure
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "NameError" in before
# breakpoint loop should re-engage
child.sendline('c')
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching pdb to actor: ('bp_forever'" in before
# now run some "continues" to show re-entries
for _ in range(5):
child.sendline('c')
child.expect(r"\(Pdb\+\+\)")
# quit the loop and expect parent to attach
child.sendline('q')
child.expect(r"\(Pdb\+\+\)")
before = str(child.before.decode())
assert "Attaching to pdb in crashed actor: ('arbiter'" in before
assert "RemoteActorError: ('bp_forever'" in before
assert 'bdb.BdbQuit' in before
# process should exit
child.sendline('c')
child.expect(pexpect.EOF)
before = str(child.before.decode())
assert "RemoteActorError: ('bp_forever'" in before
assert 'bdb.BdbQuit' in before