Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / cython / src / Cython / Debugger / Tests / test_libcython_in_gdb.py
blob683f4c7c24f1447c6b7d1b6ed3061a053e0f1dd1
1 """
2 Tests that run inside GDB.
4 Note: debug information is already imported by the file generated by
5 Cython.Debugger.Cygdb.make_command_file()
6 """
8 import os
9 import re
10 import sys
11 import trace
12 import inspect
13 import warnings
14 import unittest
15 import textwrap
16 import tempfile
17 import functools
18 import traceback
19 import itertools
20 from test import test_support
22 import gdb
24 from Cython.Debugger import libcython
25 from Cython.Debugger import libpython
26 from Cython.Debugger.Tests import TestLibCython as test_libcython
28 # for some reason sys.argv is missing in gdb
29 sys.argv = ['gdb']
32 def print_on_call_decorator(func):
33 @functools.wraps(func)
34 def wrapper(self, *args, **kwargs):
35 _debug(type(self).__name__, func.__name__)
37 try:
38 return func(self, *args, **kwargs)
39 except Exception, e:
40 _debug("An exception occurred:", traceback.format_exc(e))
41 raise
43 return wrapper
45 class TraceMethodCallMeta(type):
47 def __init__(self, name, bases, dict):
48 for func_name, func in dict.iteritems():
49 if inspect.isfunction(func):
50 setattr(self, func_name, print_on_call_decorator(func))
53 class DebugTestCase(unittest.TestCase):
54 """
55 Base class for test cases. On teardown it kills the inferior and unsets
56 all breakpoints.
57 """
59 __metaclass__ = TraceMethodCallMeta
61 def __init__(self, name):
62 super(DebugTestCase, self).__init__(name)
63 self.cy = libcython.cy
64 self.module = libcython.cy.cython_namespace['codefile']
65 self.spam_func, self.spam_meth = libcython.cy.functions_by_name['spam']
66 self.ham_func = libcython.cy.functions_by_qualified_name[
67 'codefile.ham']
68 self.eggs_func = libcython.cy.functions_by_qualified_name[
69 'codefile.eggs']
71 def read_var(self, varname, cast_to=None):
72 result = gdb.parse_and_eval('$cy_cvalue("%s")' % varname)
73 if cast_to:
74 result = cast_to(result)
76 return result
78 def local_info(self):
79 return gdb.execute('info locals', to_string=True)
81 def lineno_equals(self, source_line=None, lineno=None):
82 if source_line is not None:
83 lineno = test_libcython.source_to_lineno[source_line]
84 frame = gdb.selected_frame()
85 self.assertEqual(libcython.cython_info.lineno(frame), lineno)
87 def break_and_run(self, source_line):
88 break_lineno = test_libcython.source_to_lineno[source_line]
89 gdb.execute('cy break codefile:%d' % break_lineno, to_string=True)
90 gdb.execute('run', to_string=True)
92 def tearDown(self):
93 gdb.execute('delete breakpoints', to_string=True)
94 try:
95 gdb.execute('kill inferior 1', to_string=True)
96 except RuntimeError:
97 pass
99 gdb.execute('set args -c "import codefile"')
102 class TestDebugInformationClasses(DebugTestCase):
104 def test_CythonModule(self):
105 "test that debug information was parsed properly into data structures"
106 self.assertEqual(self.module.name, 'codefile')
107 global_vars = ('c_var', 'python_var', '__name__',
108 '__builtins__', '__doc__', '__file__')
109 assert set(global_vars).issubset(self.module.globals)
111 def test_CythonVariable(self):
112 module_globals = self.module.globals
113 c_var = module_globals['c_var']
114 python_var = module_globals['python_var']
115 self.assertEqual(c_var.type, libcython.CObject)
116 self.assertEqual(python_var.type, libcython.PythonObject)
117 self.assertEqual(c_var.qualified_name, 'codefile.c_var')
119 def test_CythonFunction(self):
120 self.assertEqual(self.spam_func.qualified_name, 'codefile.spam')
121 self.assertEqual(self.spam_meth.qualified_name,
122 'codefile.SomeClass.spam')
123 self.assertEqual(self.spam_func.module, self.module)
125 assert self.eggs_func.pf_cname, (self.eggs_func, self.eggs_func.pf_cname)
126 assert not self.ham_func.pf_cname
127 assert not self.spam_func.pf_cname
128 assert not self.spam_meth.pf_cname
130 self.assertEqual(self.spam_func.type, libcython.CObject)
131 self.assertEqual(self.ham_func.type, libcython.CObject)
133 self.assertEqual(self.spam_func.arguments, ['a'])
134 self.assertEqual(self.spam_func.step_into_functions,
135 set(['puts', 'some_c_function']))
137 expected_lineno = test_libcython.source_to_lineno['def spam(a=0):']
138 self.assertEqual(self.spam_func.lineno, expected_lineno)
139 self.assertEqual(sorted(self.spam_func.locals), list('abcd'))
142 class TestParameters(unittest.TestCase):
144 def test_parameters(self):
145 gdb.execute('set cy_colorize_code on')
146 assert libcython.parameters.colorize_code
147 gdb.execute('set cy_colorize_code off')
148 assert not libcython.parameters.colorize_code
151 class TestBreak(DebugTestCase):
153 def test_break(self):
154 breakpoint_amount = len(gdb.breakpoints() or ())
155 gdb.execute('cy break codefile.spam')
157 self.assertEqual(len(gdb.breakpoints()), breakpoint_amount + 1)
158 bp = gdb.breakpoints()[-1]
159 self.assertEqual(bp.type, gdb.BP_BREAKPOINT)
160 assert self.spam_func.cname in bp.location
161 assert bp.enabled
163 def test_python_break(self):
164 gdb.execute('cy break -p join')
165 assert 'def join(' in gdb.execute('cy run', to_string=True)
167 def test_break_lineno(self):
168 beginline = 'import os'
169 nextline = 'cdef int c_var = 12'
171 self.break_and_run(beginline)
172 self.lineno_equals(beginline)
173 step_result = gdb.execute('cy step', to_string=True)
174 self.lineno_equals(nextline)
175 assert step_result.rstrip().endswith(nextline)
178 class TestKilled(DebugTestCase):
180 def test_abort(self):
181 gdb.execute("set args -c 'import os; os.abort()'")
182 output = gdb.execute('cy run', to_string=True)
183 assert 'abort' in output.lower()
186 class DebugStepperTestCase(DebugTestCase):
188 def step(self, varnames_and_values, source_line=None, lineno=None):
189 gdb.execute(self.command)
190 for varname, value in varnames_and_values:
191 self.assertEqual(self.read_var(varname), value, self.local_info())
193 self.lineno_equals(source_line, lineno)
196 class TestStep(DebugStepperTestCase):
198 Test stepping. Stepping happens in the code found in
199 Cython/Debugger/Tests/codefile.
202 def test_cython_step(self):
203 gdb.execute('cy break codefile.spam')
205 gdb.execute('run', to_string=True)
206 self.lineno_equals('def spam(a=0):')
208 gdb.execute('cy step', to_string=True)
209 self.lineno_equals('b = c = d = 0')
211 self.command = 'cy step'
212 self.step([('b', 0)], source_line='b = 1')
213 self.step([('b', 1), ('c', 0)], source_line='c = 2')
214 self.step([('c', 2)], source_line='int(10)')
215 self.step([], source_line='puts("spam")')
217 gdb.execute('cont', to_string=True)
218 self.assertEqual(len(gdb.inferiors()), 1)
219 self.assertEqual(gdb.inferiors()[0].pid, 0)
221 def test_c_step(self):
222 self.break_and_run('some_c_function()')
223 gdb.execute('cy step', to_string=True)
224 self.assertEqual(gdb.selected_frame().name(), 'some_c_function')
226 def test_python_step(self):
227 self.break_and_run('os.path.join("foo", "bar")')
229 result = gdb.execute('cy step', to_string=True)
231 curframe = gdb.selected_frame()
232 self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
234 pyframe = libpython.Frame(curframe).get_pyop()
235 # With Python 3 inferiors, pyframe.co_name will return a PyUnicodePtr,
236 # be compatible
237 frame_name = pyframe.co_name.proxyval(set())
238 self.assertEqual(frame_name, 'join')
239 assert re.match(r'\d+ def join\(', result), result
242 class TestNext(DebugStepperTestCase):
244 def test_cython_next(self):
245 self.break_and_run('c = 2')
247 lines = (
248 'int(10)',
249 'puts("spam")',
250 'os.path.join("foo", "bar")',
251 'some_c_function()',
254 for line in lines:
255 gdb.execute('cy next')
256 self.lineno_equals(line)
259 class TestLocalsGlobals(DebugTestCase):
261 def test_locals(self):
262 self.break_and_run('int(10)')
264 result = gdb.execute('cy locals', to_string=True)
265 assert 'a = 0', repr(result)
266 assert 'b = (int) 1', result
267 assert 'c = (int) 2' in result, repr(result)
269 def test_globals(self):
270 self.break_and_run('int(10)')
272 result = gdb.execute('cy globals', to_string=True)
273 assert '__name__ ' in result, repr(result)
274 assert '__doc__ ' in result, repr(result)
275 assert 'os ' in result, repr(result)
276 assert 'c_var ' in result, repr(result)
277 assert 'python_var ' in result, repr(result)
280 class TestBacktrace(DebugTestCase):
282 def test_backtrace(self):
283 libcython.parameters.colorize_code.value = False
285 self.break_and_run('os.path.join("foo", "bar")')
287 def match_backtrace_output(result):
288 assert re.search(r'\#\d+ *0x.* in spam\(\) at .*codefile\.pyx:22',
289 result), result
290 assert 'os.path.join("foo", "bar")' in result, result
292 result = gdb.execute('cy bt', to_string=True)
293 match_backtrace_output(result)
295 result = gdb.execute('cy bt -a', to_string=True)
296 match_backtrace_output(result)
298 # Apparently not everyone has main()
299 # assert re.search(r'\#0 *0x.* in main\(\)', result), result
302 class TestFunctions(DebugTestCase):
304 def test_functions(self):
305 self.break_and_run('c = 2')
306 result = gdb.execute('print $cy_cname("b")', to_string=True)
307 assert re.search('__pyx_.*b', result), result
309 result = gdb.execute('print $cy_lineno()', to_string=True)
310 supposed_lineno = test_libcython.source_to_lineno['c = 2']
311 assert str(supposed_lineno) in result, (supposed_lineno, result)
313 result = gdb.execute('print $cy_cvalue("b")', to_string=True)
314 assert '= 1' in result
317 class TestPrint(DebugTestCase):
319 def test_print(self):
320 self.break_and_run('c = 2')
321 result = gdb.execute('cy print b', to_string=True)
322 self.assertEqual('b = (int) 1\n', result)
325 class TestUpDown(DebugTestCase):
327 def test_updown(self):
328 self.break_and_run('os.path.join("foo", "bar")')
329 gdb.execute('cy step')
330 self.assertRaises(RuntimeError, gdb.execute, 'cy down')
332 result = gdb.execute('cy up', to_string=True)
333 assert 'spam()' in result
334 assert 'os.path.join("foo", "bar")' in result
337 class TestExec(DebugTestCase):
339 def setUp(self):
340 super(TestExec, self).setUp()
341 self.fd, self.tmpfilename = tempfile.mkstemp()
342 self.tmpfile = os.fdopen(self.fd, 'r+')
344 def tearDown(self):
345 super(TestExec, self).tearDown()
347 try:
348 self.tmpfile.close()
349 finally:
350 os.remove(self.tmpfilename)
352 def eval_command(self, command):
353 gdb.execute('cy exec open(%r, "w").write(str(%s))' %
354 (self.tmpfilename, command))
355 return self.tmpfile.read().strip()
357 def test_cython_exec(self):
358 self.break_and_run('os.path.join("foo", "bar")')
360 # test normal behaviour
361 self.assertEqual("[0]", self.eval_command('[a]'))
363 # test multiline code
364 result = gdb.execute(textwrap.dedent('''\
365 cy exec
366 pass
368 "nothing"
370 '''))
371 result = self.tmpfile.read().rstrip()
372 self.assertEqual('', result)
374 def test_python_exec(self):
375 self.break_and_run('os.path.join("foo", "bar")')
376 gdb.execute('cy step')
378 gdb.execute('cy exec some_random_var = 14')
379 self.assertEqual('14', self.eval_command('some_random_var'))
382 class CySet(DebugTestCase):
384 def test_cyset(self):
385 self.break_and_run('os.path.join("foo", "bar")')
387 gdb.execute('cy set a = $cy_eval("{None: []}")')
388 stringvalue = self.read_var("a", cast_to=str)
389 self.assertEqual(stringvalue, "{None: []}")
392 class TestCyEval(DebugTestCase):
393 "Test the $cy_eval() gdb function."
395 def test_cy_eval(self):
396 # This function leaks a few objects in the GDB python process. This
397 # is no biggie
398 self.break_and_run('os.path.join("foo", "bar")')
400 result = gdb.execute('print $cy_eval("None")', to_string=True)
401 assert re.match(r'\$\d+ = None\n', result), result
403 result = gdb.execute('print $cy_eval("[a]")', to_string=True)
404 assert re.match(r'\$\d+ = \[0\]', result), result
407 class TestClosure(DebugTestCase):
409 def break_and_run_func(self, funcname):
410 gdb.execute('cy break ' + funcname)
411 gdb.execute('cy run')
413 def test_inner(self):
414 self.break_and_run_func('inner')
415 self.assertEqual('', gdb.execute('cy locals', to_string=True))
417 # Allow the Cython-generated code to initialize the scope variable
418 gdb.execute('cy step')
420 self.assertEqual(str(self.read_var('a')), "'an object'")
421 print_result = gdb.execute('cy print a', to_string=True).strip()
422 self.assertEqual(print_result, "a = 'an object'")
424 def test_outer(self):
425 self.break_and_run_func('outer')
426 self.assertEqual('', gdb.execute('cy locals', to_string=True))
428 # Initialize scope with 'a' uninitialized
429 gdb.execute('cy step')
430 self.assertEqual('', gdb.execute('cy locals', to_string=True))
432 # Initialize 'a' to 1
433 gdb.execute('cy step')
434 print_result = gdb.execute('cy print a', to_string=True).strip()
435 self.assertEqual(print_result, "a = 'an object'")
438 _do_debug = os.environ.get('GDB_DEBUG')
439 if _do_debug:
440 _debug_file = open('/dev/tty', 'w')
442 def _debug(*messages):
443 if _do_debug:
444 messages = itertools.chain([sys._getframe(1).f_code.co_name, ':'],
445 messages)
446 _debug_file.write(' '.join(str(msg) for msg in messages) + '\n')
449 def run_unittest_in_module(modulename):
450 try:
451 gdb.lookup_type('PyModuleObject')
452 except RuntimeError:
453 msg = ("Unable to run tests, Python was not compiled with "
454 "debugging information. Either compile python with "
455 "-g or get a debug build (configure with --with-pydebug).")
456 warnings.warn(msg)
457 os._exit(1)
458 else:
459 m = __import__(modulename, fromlist=[''])
460 tests = inspect.getmembers(m, inspect.isclass)
462 # test_support.run_unittest(tests)
464 test_loader = unittest.TestLoader()
465 suite = unittest.TestSuite(
466 [test_loader.loadTestsFromTestCase(cls) for name, cls in tests])
468 result = unittest.TextTestRunner(verbosity=1).run(suite)
469 return result.wasSuccessful()
471 def runtests():
473 Run the libcython and libpython tests. Ensure that an appropriate status is
474 returned to the parent test process.
476 from Cython.Debugger.Tests import test_libpython_in_gdb
478 success_libcython = run_unittest_in_module(__name__)
479 success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__)
481 if not success_libcython or not success_libpython:
482 sys.exit(2)
484 def main(version, trace_code=False):
485 global inferior_python_version
487 inferior_python_version = version
489 if trace_code:
490 tracer = trace.Trace(count=False, trace=True, outfile=sys.stderr,
491 ignoredirs=[sys.prefix, sys.exec_prefix])
492 tracer.runfunc(runtests)
493 else:
494 runtests()