lots of tests
authorMark Florisson <markflorisson88@gmail.com>
Sun, 31 Oct 2010 21:35:18 +0000 (22:35 +0100)
committerMark Florisson <markflorisson88@gmail.com>
Sun, 31 Oct 2010 21:35:18 +0000 (22:35 +0100)
Cython/Compiler/Tests/TestParseTreeTransforms.py
Cython/Debugger/Tests/TestLibCython.py
Cython/Debugger/Tests/cfuncs.c [new file with mode: 0644]
Cython/Debugger/Tests/codefile [new file with mode: 0644]
Cython/Debugger/Tests/test_libcython_in_gdb.py
Cython/Tests/TestStringIOTree.py [new file with mode: 0644]

index 7857986c6818146261019aa0dcf500e7511d37e1..6be5f42317c74fc4bc8d6d7b8cc9c56cab68467f 100644 (file)
@@ -197,10 +197,12 @@ class TestDebugTransform(TestLibCython.DebuggerTestCase):
             self.assertEqual(1, len(list(spam_arguments)))
             
             # test step-into functions
-            spam_stepinto = list(spam.find('StepIntoFunctions'))
+            step_into = spam.find('StepIntoFunctions')
+            spam_stepinto = [x.attrib['name'] for x in step_into]
             assert spam_stepinto
-            self.assertEqual(1, len(list(spam_stepinto)))
-            self.assertEqual('puts', list(spam_stepinto)[0].attrib['name'])
+            self.assertEqual(2, len(spam_stepinto))
+            assert 'puts' in spam_stepinto
+            assert 'some_c_function' in spam_stepinto
         except:
             print open(self.debug_dest).read()
             raise
index f83b1f84e5d98192091615af015cca0ccc4221cf..25941d4be0409fb83d5b939c35fd3abc60bab90b 100644 (file)
@@ -9,10 +9,16 @@ import tempfile
 import subprocess
 import distutils.core
 from distutils import sysconfig
+from distutils import ccompiler
 
 import Cython.Distutils.extension
 from Cython.Debugger import Cygdb as cygdb
 
+root = os.path.dirname(os.path.abspath(__file__))
+codefile = os.path.join(root, 'codefile')
+cfuncs_file = os.path.join(root, 'cfuncs.c')
+with open(codefile) as f:
+    source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f))
 
 class DebuggerTestCase(unittest.TestCase):
     
@@ -26,52 +32,26 @@ class DebuggerTestCase(unittest.TestCase):
         self.debug_dest = os.path.join(self.tempdir, 
                                       'cython_debug', 
                                       'cython_debug_info_codefile')
-        
-        code = textwrap.dedent("""
-            cdef extern from "stdio.h":
-                int puts(char *s)
-                
-            cdef int c_var = 0
-            python_var = 0
-            
-            def spam(a=0):
-                cdef:
-                    int b, c, d
-                
-                b = c = d = 0
-                
-                b = 1
-                c = 2
-                d = 3
-                int(10)
-                puts("spam")
-                
-            cdef ham():
-                pass
-                
-            cpdef eggs():
-                pass
-            
-            cdef class SomeClass(object):
-                def spam(self):
-                    pass
-            
-            spam()
-        """)
+        self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs')
         
         self.cwd = os.getcwd()
         os.chdir(self.tempdir)
         
-        open(self.destfile, 'w').write(code)
+        shutil.copy(codefile, self.destfile)
+        shutil.copy(cfuncs_file, self.cfuncs_destfile + '.c')
+        
+        compiler = ccompiler.new_compiler()
+        compiler.compile(['cfuncs.c'], debug=True)
         
         ext = Cython.Distutils.extension.Extension(
             'codefile',
             ['codefile.pyx'], 
-            pyrex_debug=True)
+            pyrex_debug=True,
+            extra_objects=['cfuncs.o'])
             
         distutils.core.setup(
             script_args=['build_ext', '--inplace'],
-            ext_modules=[ext], 
+            ext_modules=[ext],
             cmdclass=dict(build_ext=Cython.Distutils.build_ext)
         )
     
@@ -84,12 +64,39 @@ class GdbDebuggerTestCase(DebuggerTestCase):
     def setUp(self):
         super(GdbDebuggerTestCase, self).setUp()
         
-        self.gdb_command_file = cygdb.make_command_file(self.tempdir)
-        with open(self.gdb_command_file, 'a') as f:
-            f.write('python '
-                'from Cython.Debugger.Tests import test_libcython_in_gdb;'
-                'test_libcython_in_gdb.main()\n')
+        prefix_code = textwrap.dedent('''\
+            python
                 
+            import os
+            import sys
+            import traceback
+            
+            def excepthook(type, value, tb):
+                traceback.print_exception(type, value, tb)
+                os._exit(1)
+            
+            sys.excepthook = excepthook
+            
+            # Have tracebacks end up on sys.stderr (gdb replaces sys.stderr
+            # with an object that calls gdb.write())
+            sys.stderr = sys.__stderr__
+            
+            end
+            ''')
+        
+        code = textwrap.dedent('''\
+            python
+            
+            from Cython.Debugger.Tests import test_libcython_in_gdb
+            test_libcython_in_gdb.main()
+            
+            end
+            ''')
+        
+        self.gdb_command_file = cygdb.make_command_file(self.tempdir, 
+                                                        prefix_code)
+        open(self.gdb_command_file, 'a').write(code)
+        
         args = ['gdb', '-batch', '-x', self.gdb_command_file, '-n', '--args',
                 sys.executable, '-c', 'import codefile']
         
@@ -100,6 +107,7 @@ class GdbDebuggerTestCase(DebuggerTestCase):
         paths.append(os.path.dirname(os.path.dirname(
             os.path.abspath(Cython.__file__))))
         env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths))
+
         self.p = subprocess.Popen(
             args,
             stdout=open(os.devnull, 'w'),
diff --git a/Cython/Debugger/Tests/cfuncs.c b/Cython/Debugger/Tests/cfuncs.c
new file mode 100644 (file)
index 0000000..35fb029
--- /dev/null
@@ -0,0 +1,8 @@
+void
+some_c_function(void)
+{
+    int a, b, c;
+    
+    a = 1;
+    b = 2;
+}
\ No newline at end of file
diff --git a/Cython/Debugger/Tests/codefile b/Cython/Debugger/Tests/codefile
new file mode 100644 (file)
index 0000000..e9c77c7
--- /dev/null
@@ -0,0 +1,36 @@
+cdef extern from "stdio.h":
+    int puts(char *s)
+
+cdef extern:
+    void some_c_function()
+
+import os
+
+cdef int c_var = 0
+python_var = 0
+
+def spam(a=0):
+    cdef:
+        int b, c
+    
+    b = c = d = 0
+    
+    b = 1
+    c = 2
+    int(10)
+    puts("spam")
+    os.path.join("foo", "bar")
+    some_c_function()
+    
+cdef ham():
+    pass
+    
+cpdef eggs():
+    pass
+
+cdef class SomeClass(object):
+    def spam(self):
+        pass
+
+spam()
+print "bye!"
index 5a065b681a517746f4f3190298e3e26b12bf1e73..c926b526e47ffee2c8346f0351ed605b695109fa 100644 (file)
@@ -5,19 +5,9 @@ Note: debug information is already imported by the file generated by
 Cython.Debugger.Cygdb.make_command_file()
 """
 
-import sys
-
-# First, fix gdb's python. Make sure to do this before importing modules
-# that bind output streams as default parameters
-
-# for some reason sys.argv is missing in gdb
-sys.argv = ['gdb']
-
-# Allow gdb to capture output, but have errors end up on stderr
-# sys.stdout = sys.__stdout__
-sys.stderr = sys.__stderr__
-
 import os
+import sys
+import trace
 import warnings
 import unittest
 import traceback
@@ -26,6 +16,12 @@ from test import test_support
 import gdb
 
 from Cython.Debugger import libcython
+from Cython.Debugger import libpython
+from Cython.Debugger.Tests import TestLibCython as test_libcython
+
+# for some reason sys.argv is missing in gdb
+sys.argv = ['gdb']
+
 
 class DebugTestCase(unittest.TestCase):
     
@@ -39,14 +35,35 @@ class DebugTestCase(unittest.TestCase):
         self.eggs_func = libcython.cy.functions_by_qualified_name[
             'codefile.eggs']
     
-    def read_var(self, varname):
-        return gdb.parse_and_eval('$cy_cname("%s")' % varname)
+    def read_var(self, varname, cast_to=None):
+        result = gdb.parse_and_eval('$cy_cname("%s")' % varname)
+        if cast_to:
+            result = cast_to(result)
+        
+        return result
     
     def local_info(self):
         return gdb.execute('info locals', to_string=True)
     
-class TestDebugInformationClasses(DebugTestCase):
+    def lineno_equals(self, source_line=None, lineno=None):
+        if source_line is not None:
+            lineno = test_libcython.source_to_lineno[source_line]
+        frame = gdb.selected_frame()
+        self.assertEqual(libcython.cy.step.lineno(frame), lineno)
     
+    def tearDown(self):
+        gdb.execute('delete breakpoints', to_string=True)
+        try:
+            gdb.execute('kill inferior 1', to_string=True)
+        except RuntimeError:
+            pass
+
+    def break_and_run(self, source_line):
+        break_lineno = test_libcython.source_to_lineno[source_line]
+        gdb.execute('cy break codefile:%d' % break_lineno, to_string=True)
+        gdb.execute('run', to_string=True)
+
+class TestDebugInformationClasses(DebugTestCase):
     
     def test_CythonModule(self):
         "test that debug information was parsed properly into data structures"
@@ -78,9 +95,11 @@ class TestDebugInformationClasses(DebugTestCase):
         self.assertEqual(self.ham_func.type, libcython.CObject)
         
         self.assertEqual(self.spam_func.arguments, ['a'])
-        self.assertEqual(self.spam_func.step_into_functions, set(['puts']))
+        self.assertEqual(self.spam_func.step_into_functions, 
+                         set(['puts', 'some_c_function']))
         
-        self.assertEqual(self.spam_func.lineno, 8)
+        expected_lineno = test_libcython.source_to_lineno['def spam(a=0):']
+        self.assertEqual(self.spam_func.lineno, expected_lineno)
         self.assertEqual(sorted(self.spam_func.locals), list('abcd'))
 
 
@@ -95,7 +114,8 @@ class TestParameters(unittest.TestCase):
 class TestBreak(DebugTestCase):
 
     def test_break(self):
-        result = gdb.execute('cy break codefile.spam', to_string=True)
+        result = libpython._execute('cy break codefile.spam', to_string=True)
+        print >>sys.stderr, repr(result)
         assert self.spam_func.cname in result
         
         self.assertEqual(len(gdb.breakpoints()), 1)
@@ -104,48 +124,109 @@ class TestBreak(DebugTestCase):
         self.assertEqual(bp.location, self.spam_func.cname)
         assert bp.enabled
 
-
-class TestStep(DebugTestCase):
+        
+class DebugStepperTestCase(DebugTestCase):
     
-    def test_step(self):
-        # Note: breakpoint for spam is still set
-        gdb.execute('run')
+    def step(self, varnames_and_values, source_line=None, lineno=None):
+        gdb.execute(self.command, to_string=True)
+        for varname, value in varnames_and_values:
+            self.assertEqual(self.read_var(varname), value, self.local_info())
         
-        gdb.execute('cy step', to_string=True) # b = c = d = 0
-        gdb.execute('cy step', to_string=True) # b = 1
-        self.assertEqual(self.read_var('b'), 1, self.local_info())
-        self.assertRaises(RuntimeError, self.read_var('b'))
-        gdb.execute('cont')
+        self.lineno_equals(source_line, lineno)
 
 
-class TestNext(DebugTestCase):
+class TestStep(DebugStepperTestCase):
+    """
+    Test stepping. Stepping happens in the code found in 
+    Cython/Debugger/Tests/codefile.
+    """
+    def test_cython_step(self):
+        gdb.execute('cy break codefile.spam')
+        libcython.parameters.step_into_c_code.value = False
+        
+        gdb.execute('run', to_string=True)
+        self.lineno_equals('def spam(a=0):')
+        
+        gdb.execute('cy step', to_string=True)
+        self.lineno_equals('b = c = d = 0')
+        
+        self.command = 'cy step'
+        self.step([('b', 0)], source_line='b = 1')
+        self.step([('b', 1), ('c', 0)], source_line='c = 2')
+        self.step([('c', 2)], source_line='int(10)')
+        self.step([], source_line='puts("spam")')
+        self.step([], source_line='os.path.join("foo", "bar")')
+        
+        gdb.execute('cont', to_string=True)
+        self.assertEqual(len(gdb.inferiors()), 1)
+        self.assertEqual(gdb.inferiors()[0].pid, 0)
     
-    def test_next(self):
-        pass
+    def test_c_step(self):
+        libcython.parameters.step_into_c_code.value = True
+        self.break_and_run('some_c_function()')
+        gdb.execute('cy step', to_string=True)
+        self.assertEqual(gdb.selected_frame().name(), 'some_c_function')
     
-def main():
+    def test_python_step(self):
+        self.break_and_run('os.path.join("foo", "bar")')
+        
+        gdb.execute('cy step', to_string=True)
+        
+        curframe = gdb.selected_frame()
+        self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
+        
+        pyframe = libpython.Frame(curframe).get_pyop()
+        self.assertEqual(str(pyframe.co_name), 'join')
+
+
+class TestNext(DebugStepperTestCase):
+    
+    def test_cython_next(self):
+        libcython.parameters.step_into_c_code.value = True
+        self.break_and_run('c = 2')
+
+        lines = (
+            'int(10)',
+            'puts("spam")',
+            'os.path.join("foo", "bar")',
+            'some_c_function()',
+        )
+
+        for line in lines:
+            gdb.execute('cy next')
+            self.lineno_equals(line)
+
+
+def _main():
+    # unittest.main(module=__import__(__name__, fromlist=['']))
     try:
-        # unittest.main(module=__import__(__name__, fromlist=['']))
-        try:
-            gdb.lookup_type('PyModuleObject')
-        except RuntimeError:
-            msg = ("Unable to run tests, Python was not compiled with "
-                   "debugging information. Either compile python with "
-                   "-g or get a debug build (configure with --with-pydebug).")
-            warnings.warn(msg)
-        else:
-            tests = (
-                TestDebugInformationClasses,
-                TestParameters,
-                TestBreak,
-                TestStep
-            )
-            # test_support.run_unittest(tests)
-            test_loader = unittest.TestLoader()
-            suite = unittest.TestSuite(
-                [test_loader.loadTestsFromTestCase(cls) for cls in tests])
-                
-            unittest.TextTestRunner(verbosity=1).run(suite)
-    except Exception:
-        traceback.print_exc()
-        os._exit(1)
\ No newline at end of file
+        gdb.lookup_type('PyModuleObject')
+    except RuntimeError:
+        msg = ("Unable to run tests, Python was not compiled with "
+                "debugging information. Either compile python with "
+                "-g or get a debug build (configure with --with-pydebug).")
+        warnings.warn(msg)
+    else:
+        tests = (
+            TestDebugInformationClasses,
+            TestParameters,
+            TestBreak,
+            TestStep,
+            TestNext,
+        )
+        # test_support.run_unittest(tests)
+        test_loader = unittest.TestLoader()
+        suite = unittest.TestSuite(
+            [test_loader.loadTestsFromTestCase(cls) for cls in tests])
+        
+        result = unittest.TextTestRunner(verbosity=1).run(suite)
+        if not result.wasSuccessful():
+            os._exit(1)
+
+def main(trace_code=False):
+    if trace_code:
+        tracer = trace.Trace(count=False, trace=True, outfile=sys.stderr,
+                            ignoredirs=[sys.prefix, sys.exec_prefix])
+        tracer.runfunc(_main)
+    else:
+        _main()
\ No newline at end of file
diff --git a/Cython/Tests/TestStringIOTree.py b/Cython/Tests/TestStringIOTree.py
new file mode 100644 (file)
index 0000000..41162ac
--- /dev/null
@@ -0,0 +1,67 @@
+import unittest
+
+from Cython import StringIOTree as stringtree
+
+code = """
+cdef int spam                   # line 1
+
+cdef ham():
+    a = 1
+    b = 2
+    c = 3
+    d = 4
+
+def eggs():
+    pass
+
+cpdef bacon():
+    print spam
+    print 'scotch'
+    print 'tea?'
+    print 'or coffee?'          # line 16
+"""
+
+linemap = dict(enumerate(code.splitlines()))
+
+class TestStringIOTree(unittest.TestCase):
+    
+    def setUp(self):
+        self.tree = stringtree.StringIOTree()
+    
+    def test_markers(self):
+        assert not self.tree.allmarkers()
+    
+    def test_insertion(self):
+        self.write_lines((1, 2, 3))
+        line_4_to_6_insertion_point = self.tree.insertion_point()
+        self.write_lines((7, 8))
+        line_9_to_13_insertion_point = self.tree.insertion_point()
+        self.write_lines((14, 15, 16))
+    
+        line_4_insertion_point = line_4_to_6_insertion_point.insertion_point()
+        self.write_lines((5, 6), tree=line_4_to_6_insertion_point)
+        
+        line_9_to_12_insertion_point = (
+            line_9_to_13_insertion_point.insertion_point())
+        self.write_line(13, tree=line_9_to_13_insertion_point)
+        
+        self.write_line(4, tree=line_4_insertion_point)
+        self.write_line(9, tree=line_9_to_12_insertion_point)
+        line_10_insertion_point = line_9_to_12_insertion_point.insertion_point()
+        self.write_line(11, tree=line_9_to_12_insertion_point)
+        self.write_line(10, tree=line_10_insertion_point)
+        self.write_line(12, tree=line_9_to_12_insertion_point)
+        
+        self.assertEqual(self.tree.allmarkers(), range(1, 17))
+        self.assertEqual(code.strip(), self.tree.getvalue().strip())
+        
+        
+    def write_lines(self, linenos, tree=None):
+        for lineno in linenos:
+            self.write_line(lineno, tree=tree)
+    
+    def write_line(self, lineno, tree=None):
+        if tree is None:
+            tree = self.tree
+        tree.markers.append(lineno)
+        tree.write(linemap[lineno] + '\n')
\ No newline at end of file