Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: third_party/cython/src/Cython/Debugger/Tests/test_libcython_in_gdb.py

Issue 385073004: Adding cython v0.20.2 in third-party. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reference cython dev list thread. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 """
2 Tests that run inside GDB.
3
4 Note: debug information is already imported by the file generated by
5 Cython.Debugger.Cygdb.make_command_file()
6 """
7
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
21
22 import gdb
23
24 from Cython.Debugger import libcython
25 from Cython.Debugger import libpython
26 from Cython.Debugger.Tests import TestLibCython as test_libcython
27
28 # for some reason sys.argv is missing in gdb
29 sys.argv = ['gdb']
30
31
32 def print_on_call_decorator(func):
33 @functools.wraps(func)
34 def wrapper(self, *args, **kwargs):
35 _debug(type(self).__name__, func.__name__)
36
37 try:
38 return func(self, *args, **kwargs)
39 except Exception, e:
40 _debug("An exception occurred:", traceback.format_exc(e))
41 raise
42
43 return wrapper
44
45 class TraceMethodCallMeta(type):
46
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))
51
52
53 class DebugTestCase(unittest.TestCase):
54 """
55 Base class for test cases. On teardown it kills the inferior and unsets
56 all breakpoints.
57 """
58
59 __metaclass__ = TraceMethodCallMeta
60
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']
70
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)
75
76 return result
77
78 def local_info(self):
79 return gdb.execute('info locals', to_string=True)
80
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)
86
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)
91
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
98
99 gdb.execute('set args -c "import codefile"')
100
101
102 class TestDebugInformationClasses(DebugTestCase):
103
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)
110
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')
118
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)
124
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
129
130 self.assertEqual(self.spam_func.type, libcython.CObject)
131 self.assertEqual(self.ham_func.type, libcython.CObject)
132
133 self.assertEqual(self.spam_func.arguments, ['a'])
134 self.assertEqual(self.spam_func.step_into_functions,
135 set(['puts', 'some_c_function']))
136
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'))
140
141
142 class TestParameters(unittest.TestCase):
143
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
149
150
151 class TestBreak(DebugTestCase):
152
153 def test_break(self):
154 breakpoint_amount = len(gdb.breakpoints() or ())
155 gdb.execute('cy break codefile.spam')
156
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
162
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)
166
167 def test_break_lineno(self):
168 beginline = 'import os'
169 nextline = 'cdef int c_var = 12'
170
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)
176
177
178 class TestKilled(DebugTestCase):
179
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()
184
185
186 class DebugStepperTestCase(DebugTestCase):
187
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())
192
193 self.lineno_equals(source_line, lineno)
194
195
196 class TestStep(DebugStepperTestCase):
197 """
198 Test stepping. Stepping happens in the code found in
199 Cython/Debugger/Tests/codefile.
200 """
201
202 def test_cython_step(self):
203 gdb.execute('cy break codefile.spam')
204
205 gdb.execute('run', to_string=True)
206 self.lineno_equals('def spam(a=0):')
207
208 gdb.execute('cy step', to_string=True)
209 self.lineno_equals('b = c = d = 0')
210
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")')
216
217 gdb.execute('cont', to_string=True)
218 self.assertEqual(len(gdb.inferiors()), 1)
219 self.assertEqual(gdb.inferiors()[0].pid, 0)
220
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')
225
226 def test_python_step(self):
227 self.break_and_run('os.path.join("foo", "bar")')
228
229 result = gdb.execute('cy step', to_string=True)
230
231 curframe = gdb.selected_frame()
232 self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
233
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
240
241
242 class TestNext(DebugStepperTestCase):
243
244 def test_cython_next(self):
245 self.break_and_run('c = 2')
246
247 lines = (
248 'int(10)',
249 'puts("spam")',
250 'os.path.join("foo", "bar")',
251 'some_c_function()',
252 )
253
254 for line in lines:
255 gdb.execute('cy next')
256 self.lineno_equals(line)
257
258
259 class TestLocalsGlobals(DebugTestCase):
260
261 def test_locals(self):
262 self.break_and_run('int(10)')
263
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)
268
269 def test_globals(self):
270 self.break_and_run('int(10)')
271
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)
278
279
280 class TestBacktrace(DebugTestCase):
281
282 def test_backtrace(self):
283 libcython.parameters.colorize_code.value = False
284
285 self.break_and_run('os.path.join("foo", "bar")')
286
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
291
292 result = gdb.execute('cy bt', to_string=True)
293 match_backtrace_output(result)
294
295 result = gdb.execute('cy bt -a', to_string=True)
296 match_backtrace_output(result)
297
298 # Apparently not everyone has main()
299 # assert re.search(r'\#0 *0x.* in main\(\)', result), result
300
301
302 class TestFunctions(DebugTestCase):
303
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
308
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)
312
313 result = gdb.execute('print $cy_cvalue("b")', to_string=True)
314 assert '= 1' in result
315
316
317 class TestPrint(DebugTestCase):
318
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)
323
324
325 class TestUpDown(DebugTestCase):
326
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')
331
332 result = gdb.execute('cy up', to_string=True)
333 assert 'spam()' in result
334 assert 'os.path.join("foo", "bar")' in result
335
336
337 class TestExec(DebugTestCase):
338
339 def setUp(self):
340 super(TestExec, self).setUp()
341 self.fd, self.tmpfilename = tempfile.mkstemp()
342 self.tmpfile = os.fdopen(self.fd, 'r+')
343
344 def tearDown(self):
345 super(TestExec, self).tearDown()
346
347 try:
348 self.tmpfile.close()
349 finally:
350 os.remove(self.tmpfilename)
351
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()
356
357 def test_cython_exec(self):
358 self.break_and_run('os.path.join("foo", "bar")')
359
360 # test normal behaviour
361 self.assertEqual("[0]", self.eval_command('[a]'))
362
363 # test multiline code
364 result = gdb.execute(textwrap.dedent('''\
365 cy exec
366 pass
367
368 "nothing"
369 end
370 '''))
371 result = self.tmpfile.read().rstrip()
372 self.assertEqual('', result)
373
374 def test_python_exec(self):
375 self.break_and_run('os.path.join("foo", "bar")')
376 gdb.execute('cy step')
377
378 gdb.execute('cy exec some_random_var = 14')
379 self.assertEqual('14', self.eval_command('some_random_var'))
380
381
382 class CySet(DebugTestCase):
383
384 def test_cyset(self):
385 self.break_and_run('os.path.join("foo", "bar")')
386
387 gdb.execute('cy set a = $cy_eval("{None: []}")')
388 stringvalue = self.read_var("a", cast_to=str)
389 self.assertEqual(stringvalue, "{None: []}")
390
391
392 class TestCyEval(DebugTestCase):
393 "Test the $cy_eval() gdb function."
394
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")')
399
400 result = gdb.execute('print $cy_eval("None")', to_string=True)
401 assert re.match(r'\$\d+ = None\n', result), result
402
403 result = gdb.execute('print $cy_eval("[a]")', to_string=True)
404 assert re.match(r'\$\d+ = \[0\]', result), result
405
406
407 class TestClosure(DebugTestCase):
408
409 def break_and_run_func(self, funcname):
410 gdb.execute('cy break ' + funcname)
411 gdb.execute('cy run')
412
413 def test_inner(self):
414 self.break_and_run_func('inner')
415 self.assertEqual('', gdb.execute('cy locals', to_string=True))
416
417 # Allow the Cython-generated code to initialize the scope variable
418 gdb.execute('cy step')
419
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'")
423
424 def test_outer(self):
425 self.break_and_run_func('outer')
426 self.assertEqual('', gdb.execute('cy locals', to_string=True))
427
428 # Initialize scope with 'a' uninitialized
429 gdb.execute('cy step')
430 self.assertEqual('', gdb.execute('cy locals', to_string=True))
431
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'")
436
437
438 _do_debug = os.environ.get('GDB_DEBUG')
439 if _do_debug:
440 _debug_file = open('/dev/tty', 'w')
441
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')
447
448
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)
461
462 # test_support.run_unittest(tests)
463
464 test_loader = unittest.TestLoader()
465 suite = unittest.TestSuite(
466 [test_loader.loadTestsFromTestCase(cls) for name, cls in tests])
467
468 result = unittest.TextTestRunner(verbosity=1).run(suite)
469 return result.wasSuccessful()
470
471 def runtests():
472 """
473 Run the libcython and libpython tests. Ensure that an appropriate status is
474 returned to the parent test process.
475 """
476 from Cython.Debugger.Tests import test_libpython_in_gdb
477
478 success_libcython = run_unittest_in_module(__name__)
479 success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__)
480
481 if not success_libcython or not success_libpython:
482 sys.exit(2)
483
484 def main(version, trace_code=False):
485 global inferior_python_version
486
487 inferior_python_version = version
488
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()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698