Skip to content

Commit 8c9bfb1

Browse files
authored
Merge pull request #50 from rocky/add-api-test
Add api test
2 parents 6e3aae2 + c50b2a1 commit 8c9bfb1

7 files changed

Lines changed: 205 additions & 62 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/python
2+
"""Greatest Common Divisor
3+
4+
Some characterstics of this program used for testing check_args() does
5+
not have a 'return' statement.
6+
7+
check_args() raises an uncaught exception when given the wrong number
8+
of parameters.
9+
10+
"""
11+
import sys
12+
13+
from trepan.api import debug
14+
15+
16+
def check_args():
17+
if len(sys.argv) != 3:
18+
# Rather than use sys.exit let's just raise an error
19+
raise Exception("Need to give two numbers")
20+
for i in range(2):
21+
try:
22+
sys.argv[i + 1] = int(sys.argv[i + 1])
23+
except ValueError:
24+
print("** Expecting an integer, got: %s" % repr(sys.argv[i]))
25+
sys.exit(2)
26+
27+
28+
def gcd(a, b):
29+
"""GCD. We assume positive numbers"""
30+
31+
# Make: a <= b
32+
if a > b:
33+
(a, b) = (b, a)
34+
35+
if a <= 0:
36+
debug(step_ignore=0)
37+
return None
38+
if a == 1 or b - a == 0:
39+
debug(start_opts={"startup-profile": True})
40+
return a
41+
return gcd(b - a, a)
42+
43+
44+
if __name__ == "__main__":
45+
check_args()
46+
47+
(a, b) = sys.argv[1:3]
48+
print("The GCD of %d and %d is %d" % (a, b, gcd(a, b)))
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/python
2+
"""Greatest Common Divisor
3+
4+
Some characterstics of this program used for testing check_args() does
5+
not have a 'return' statement.
6+
7+
check_args() raises an uncaught exception when given the wrong number
8+
of parameters.
9+
10+
"""
11+
import sys
12+
13+
from trepan.api import debug
14+
15+
16+
def check_args():
17+
if len(sys.argv) != 3:
18+
# Rather than use sys.exit let's just raise an error
19+
raise Exception("Need to give two numbers")
20+
for i in range(2):
21+
try:
22+
sys.argv[i + 1] = int(sys.argv[i + 1])
23+
except ValueError:
24+
print("** Expecting an integer, got: %s" % repr(sys.argv[i]))
25+
sys.exit(2)
26+
27+
28+
def gcd(a, b):
29+
"""GCD. We assume positive numbers"""
30+
31+
# Make: a <= b
32+
if a > b:
33+
(a, b) = (b, a)
34+
35+
if a <= 0:
36+
debug(step_ignore=0)
37+
return None
38+
if a == 1 or b - a == 0:
39+
debug(start_opts={"startup-profile": False})
40+
return a
41+
return gcd(b - a, a)
42+
43+
44+
if __name__ == "__main__":
45+
check_args()
46+
47+
(a, b) = sys.argv[1:3]
48+
print("The GCD of %d and %d is %d" % (a, b, gcd(a, b)))

test/example/gcd-dbgcall.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,39 @@
99
1010
"""
1111
import sys
12+
1213
from trepan.api import debug
1314

15+
1416
def check_args():
1517
if len(sys.argv) != 3:
1618
# Rather than use sys.exit let's just raise an error
1719
raise Exception("Need to give two numbers")
1820
for i in range(2):
1921
try:
20-
sys.argv[i+1] = int(sys.argv[i+1])
22+
sys.argv[i + 1] = int(sys.argv[i + 1])
2123
except ValueError:
2224
print("** Expecting an integer, got: %s" % repr(sys.argv[i]))
2325
sys.exit(2)
2426

25-
def gcd(a,b):
26-
""" GCD. We assume positive numbers"""
27+
28+
def gcd(a, b):
29+
"""GCD. We assume positive numbers"""
2730

2831
# Make: a <= b
2932
if a > b:
30-
(a, b) = (b, a)
33+
(a, b) = (b, a)
3134

3235
if a <= 0:
3336
debug(step_ignore=0)
3437
return None
35-
if a == 1 or b-a == 0:
36-
debug(start_opts={'startup-profile': True})
38+
if a == 1 or b - a == 0:
39+
debug()
3740
return a
38-
return gcd(b-a, a)
41+
return gcd(b - a, a)
42+
3943

40-
if __name__=='__main__':
44+
if __name__ == "__main__":
4145
check_args()
4246

4347
(a, b) = sys.argv[1:3]

test/unit/lib/test_lib_thread.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import sys
44
import threading
55

6+
import pytest
7+
68
from trepan.lib.thred import (
79
current_thread_name,
810
find_debugged_frame,
@@ -21,8 +23,6 @@ def run(self):
2123
self.id_name_checker()
2224
return
2325

24-
pass
25-
2626

2727
def id_name_checker():
2828
"""Helper for testing map_thread_names and id2thread"""
@@ -35,17 +35,17 @@ def id_name_checker():
3535

3636

3737
def test_current_thread_name():
38-
assert "MainThread", current_thread_name()
39-
return
38+
"""Test trepan.lib.thred.current_thread_name"""
39+
assert "MainThread" == current_thread_name()
4040

4141

42+
@pytest.mark.skip(reason="Fix have an intermittent failure")
4243
def test_id2thread_name():
43-
"""Test ``map_thread_names`` and ``id2thread``. """
44+
"""Test ``map_thread_names`` and ``id2thread``."""
4445
thread_id = _thread.get_ident()
4546
assert "MainThread" == id2thread_name(thread_id)
4647
id_name_checker()
4748

4849
background = BgThread(id_name_checker)
4950
background.start()
5051
background.join() # Wait for the background task to finish
51-
return

trepan/api.py

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
# we see below. So for now, we'll live with the code duplication.
3636

3737
import sys
38+
from typing import Callable, Optional
3839

3940
from trepan.debugger import Trepan, debugger_obj
4041
from trepan.post_mortem import post_mortem_excepthook, uncaught_exception
@@ -48,11 +49,11 @@ def debugger_on_post_mortem():
4849

4950
def run_eval(
5051
expression,
51-
debug_opts=None,
52-
start_opts=None,
53-
globals_=None,
54-
locals_=None,
55-
tb_fn=None,
52+
debug_opts: Optional[dict] = None,
53+
start_opts: Optional[dict] = None,
54+
globals_: Optional[dict] = None,
55+
locals_: Optional[dict] = None,
56+
tb_fn: Optional[Callable] = None,
5657
):
5758
"""Evaluate the expression (given as a string) under debugger
5859
control starting with the statement after the place that
@@ -79,7 +80,13 @@ def run_eval(
7980
return
8081

8182

82-
def run_call(func, *args, **kwds):
83+
def run_call(
84+
func: Callable,
85+
*args,
86+
debug_opts: Optional[dict] = None,
87+
start_opts: Optional[dict] = None,
88+
**kwds,
89+
):
8390
"""Call the function (a function or method object, not a string)
8491
with the given arguments starting with the statement after
8592
the place that this appears in your program.
@@ -88,7 +95,7 @@ def run_call(func, *args, **kwds):
8895
returned. The debugger prompt appears as soon as the function is
8996
entered."""
9097

91-
dbg = Trepan()
98+
dbg = Trepan(opts=debug_opts)
9299
try:
93100
return dbg.run_call(func, *args, **kwds)
94101
except Exception:
@@ -211,21 +218,43 @@ def debug(
211218
`dbg_opts' is an optional "options" dictionary that gets fed
212219
trepan.Debugger(); `start_opts' are the optional "options"
213220
dictionary that gets fed to trepan.Debugger.core.start()."""
221+
222+
# We have a global debugger_obj that we reuse
214223
global debugger_obj
224+
225+
# A list of debugger profiles we might run
226+
dbg_initfiles = []
227+
215228
if debugger_obj is None:
229+
# If debugger_obj has not been set this is the first time
230+
# we are entering the debugger.
231+
# create the object, and set to run the user's
232+
# debugger initialization profile
233+
216234
debugger_obj = Trepan(dbg_opts)
217235
debugger_obj.core.add_ignore(debug, stop)
236+
237+
# Run user profile if first time and we haven't
238+
# explicit set to ignore profile loading.
239+
if not start_opts or start_opts.get("startup-profile", True):
240+
from trepan.options import add_startup_file
241+
242+
add_startup_file(dbg_initfiles)
243+
218244
pass
245+
219246
core = debugger_obj.core
220247
frame = sys._getframe(0 + level)
221248
core.set_next(frame)
222-
if start_opts and "startup-profile" in start_opts and start_opts["startup-profile"]:
223-
dbg_initfiles = start_opts["startup-profile"]
224-
from trepan import options
225249

226-
options.add_startup_file(dbg_initfiles)
227-
for init_cmdfile in dbg_initfiles:
228-
core.processor.queue_startfile(init_cmdfile)
250+
# If we've specified profile loading, add that
251+
if start_opts and start_opts.get("startup-profile", False):
252+
from trepan.options import add_startup_file
253+
254+
add_startup_file(dbg_initfiles)
255+
256+
for init_cmdfile in dbg_initfiles:
257+
core.processor.queue_startfile(init_cmdfile)
229258

230259
if not core.is_started():
231260
core.start(start_opts)
@@ -256,26 +285,41 @@ def stop(opts=None):
256285
if __name__ == "__main__":
257286
import tracer
258287

259-
def foo(n):
260-
y = n
261-
for i in range(n):
262-
print(i)
263-
pass
264-
return y
288+
def plus5(n: int) -> int:
289+
return n + 5
265290

291+
from trepan.inout.stringarray import StringArrayInput, StringArrayOutput
266292
from trepan.lib.default import DEBUGGER_SETTINGS
267293

268294
settings = dict(DEBUGGER_SETTINGS)
269295
settings.update({"trace": True, "printset": tracer.ALL_EVENTS})
270-
debug_opts = {"step_ignore": -1, "settings": settings}
296+
debugger_input = StringArrayInput()
297+
debugger_output = StringArrayOutput()
298+
debug_opts = {
299+
"step_ignore": -1,
300+
"settings": settings,
301+
"input": debugger_input,
302+
"output": debugger_output,
303+
}
271304
print('Issuing: run_eval("1+2")')
272-
print(run_eval("1+2", debug_opts=debug_opts))
273-
print('Issuing: run_exec("x=1; y=2")')
274-
run_exec("x=1; y=2", debug_opts=debug_opts)
275-
print("Issuing: run_call(foo, debug_opts, None, 2)")
305+
run_eval("1+2", debug_opts=debug_opts)
306+
print(debugger_output.output)
307+
# print('Issuing: run_exec("x=1; y=2")')
308+
# run_exec("x=1; y=2", debug_opts=debug_opts)
309+
debugger_input = StringArrayInput(["step", "list", "continue"])
310+
debugger_output = StringArrayOutput()
311+
debug_opts = {
312+
"step_ignore": -1,
313+
"settings": settings,
314+
"input": debugger_input,
315+
"output": debugger_output,
316+
}
276317
if len(sys.argv) > 1:
277-
print(f"Issuing interactive: run_call(foo, {debug_opts}, None, {sys.argv[1]})")
278-
run_call(foo, debug_opts, int(sys.argv[1]))
318+
print(
319+
f"Issuing interactive: run_call(plus5, {int(sys.argv[1])}, debug_opts={debug_opts})"
320+
)
321+
run_call(plus5, int(sys.argv[1]), debug_opts=debug_opts)
279322
else:
280-
run_call(foo, debug_opts, 2)
323+
print(f"Issuing interactive: run_call(plus5, 2, debug_opts={debug_opts})")
324+
run_call(plus5, 2, debug_opts=debug_opts)
281325
pass

0 commit comments

Comments
 (0)