Fix leak in try-break.
[cython.git] / runtests.py
index e90c6850b06af7921b5e14976603393a98a0a892..8e8925e1deefda6157df2d9afd19727ee401eda7 100644 (file)
@@ -6,11 +6,11 @@ WITH_CYTHON = True
 
 from distutils.dist import Distribution
 from distutils.core import Extension
-from distutils.command.build_ext import build_ext
+from distutils.command.build_ext import build_ext as _build_ext
 distutils_distro = Distribution()
 
 TEST_DIRS = ['compile', 'errors', 'run', 'pyregr']
-TEST_RUN_DIRS = ['run', 'pyregr']
+TEST_RUN_DIRS = ['run', 'pyregr', 'bugs']
 
 # Lists external modules, and a matcher matching tests
 # which should be excluded if the module is not present.
@@ -18,9 +18,22 @@ EXT_DEP_MODULES = {
     'numpy' : re.compile('.*\.numpy_.*').match
 }
 
+VER_DEP_MODULES = {
+# such as:
+#    (2,4) : lambda x: x in ['run.set']
+}
+
 INCLUDE_DIRS = [ d for d in os.getenv('INCLUDE', '').split(os.pathsep) if d ]
 CFLAGS = os.getenv('CFLAGS', '').split()
 
+class build_ext(_build_ext):
+    def build_extension(self, ext):
+        if ext.language == 'c++':
+            try:
+                self.compiler.compiler_so.remove('-Wstrict-prototypes')
+            except Exception:
+                pass
+        _build_ext.build_extension(self, ext)
 
 class ErrorWriter(object):
     match_error = re.compile('(warning:)?(?:.*:)?\s*([-0-9]+)\s*:\s*([-0-9]+)\s*:\s*(.*)').match
@@ -53,7 +66,7 @@ class ErrorWriter(object):
 class TestBuilder(object):
     def __init__(self, rootdir, workdir, selectors, exclude_selectors, annotate,
                  cleanup_workdir, cleanup_sharedlibs, with_pyregr, cython_only,
-                 languages):
+                 languages, test_bugs):
         self.rootdir = rootdir
         self.workdir = workdir
         self.selectors = selectors
@@ -64,9 +77,13 @@ class TestBuilder(object):
         self.with_pyregr = with_pyregr
         self.cython_only = cython_only
         self.languages = languages
+        self.test_bugs = test_bugs
 
     def build_suite(self):
         suite = unittest.TestSuite()
+        test_dirs = TEST_DIRS
+        if self.test_bugs and 'bugs' not in test_dirs:
+            test_dirs.append('bugs')
         filenames = os.listdir(self.rootdir)
         filenames.sort()
         for filename in filenames:
@@ -74,7 +91,7 @@ class TestBuilder(object):
                 # we won't get any errors without running Cython
                 continue
             path = os.path.join(self.rootdir, filename)
-            if os.path.isdir(path) and filename in TEST_DIRS:
+            if os.path.isdir(path) and filename in test_dirs:
                 if filename == 'pyregr' and not self.with_pyregr:
                     continue
                 suite.addTest(
@@ -254,12 +271,13 @@ class CythonCompileTestCase(unittest.TestCase):
             if incdir:
                 build_extension.include_dirs.append(incdir)
             build_extension.finalize_options()
-
             extension = Extension(
                 module,
                 sources = [self.build_target_filename(module)],
                 extra_compile_args = CFLAGS,
                 )
+            if self.language == 'cpp':
+                extension.language = 'c++'
             build_extension.extensions = [extension]
             build_extension.build_temp = workdir
             build_extension.build_lib  = workdir
@@ -309,7 +327,6 @@ class CythonRunTestCase(CythonCompileTestCase):
             self.setUp()
             self.runCompileTest()
             if not self.cython_only:
-                sys.stderr.write('running doctests in %s ...\n' % self.module)
                 doctest.DocTestSuite(self.module).run(result)
         except Exception:
             result.addError(self, sys.exc_info())
@@ -330,7 +347,6 @@ class CythonUnitTestCase(CythonCompileTestCase):
         try:
             self.setUp()
             self.runCompileTest()
-            sys.stderr.write('running tests in %s ...\n' % self.module)
             unittest.defaultTestLoader.loadTestsFromName(self.module).run(result)
         except Exception:
             result.addError(self, sys.exc_info())
@@ -417,6 +433,22 @@ class MissingDependencyExcluder:
                 return True
         return False
 
+class VersionDependencyExcluder:
+    def __init__(self, deps):
+        # deps: { version : matcher func }
+        from sys import version_info
+        self.exclude_matchers = []
+        for ver, matcher in deps.items():
+            if version_info < ver:
+                self.exclude_matchers.append(matcher)
+        self.tests_missing_deps = []
+    def __call__(self, testname):
+        for matcher in self.exclude_matchers:
+            if matcher(testname):
+                self.tests_missing_deps.append(testname)
+                return True
+        return False
+
 if __name__ == '__main__':
     from optparse import OptionParser
     parser = OptionParser()
@@ -450,6 +482,9 @@ if __name__ == '__main__':
     parser.add_option("--cython-only", dest="cython_only",
                       action="store_true", default=False,
                       help="only compile pyx to c, do not run C compiler or run the tests")
+    parser.add_option("--no-refnanny", dest="with_refnanny",
+                      action="store_false", default=True,
+                      help="do not regression test reference counting")
     parser.add_option("--sys-pyregr", dest="system_pyregr",
                       action="store_true", default=False,
                       help="run the regression tests of the CPython installation")
@@ -468,6 +503,9 @@ if __name__ == '__main__':
     parser.add_option("-v", "--verbose", dest="verbosity",
                       action="count", default=0,
                       help="display test progress, pass twice to print test names")
+    parser.add_option("-T", "--ticket", dest="tickets",
+                      action="append",
+                      help="a bug ticket number to run the respective test in 'tests/bugs'")
 
     options, cmd_args = parser.parse_args()
 
@@ -500,7 +538,9 @@ if __name__ == '__main__':
     UNITTEST_ROOT = os.path.join(os.getcwd(), UNITTEST_MODULE)
     if WITH_CYTHON:
         if os.path.exists(WORKDIR):
-            shutil.rmtree(WORKDIR, ignore_errors=True)
+            for path in os.listdir(WORKDIR):
+                if path in ("support",): continue
+                shutil.rmtree(os.path.join(WORKDIR, path), ignore_errors=True)
     if not os.path.exists(WORKDIR):
         os.makedirs(WORKDIR)
 
@@ -512,6 +552,24 @@ if __name__ == '__main__':
     sys.stderr.write("Python %s\n" % sys.version)
     sys.stderr.write("\n")
 
+    if options.with_refnanny:
+        from pyximport.pyxbuild import pyx_to_dll
+        libpath = pyx_to_dll(os.path.join("Cython", "Runtime", "refnanny.pyx"),
+                             build_in_temp=True,
+                             pyxbuild_dir=os.path.join(WORKDIR, "support"))
+        sys.path.insert(0, os.path.split(libpath)[0])
+        CFLAGS.append("-DCYTHON_REFNANNY")
+
+    test_bugs = False
+    if options.tickets:
+        for ticket_number in options.tickets:
+            test_bugs = True
+            cmd_args.append('bugs.*T%s$' % ticket_number)
+    if not test_bugs:
+        for selector in cmd_args:
+            if selector.startswith('bugs'):
+                test_bugs = True
+
     import re
     selectors = [ re.compile(r, re.I|re.U).search for r in cmd_args ]
     if not selectors:
@@ -521,7 +579,8 @@ if __name__ == '__main__':
     # which depends on them (by prefix)
 
     missing_dep_excluder = MissingDependencyExcluder(EXT_DEP_MODULES) 
-    exclude_selectors = [missing_dep_excluder] # want to pring msg at exit
+    version_dep_excluder = VersionDependencyExcluder(VER_DEP_MODULES) 
+    exclude_selectors = [missing_dep_excluder, version_dep_excluder] # want to pring msg at exit
 
     if options.exclude:
         exclude_selectors += [ re.compile(r, re.I|re.U).search for r in options.exclude ]
@@ -544,7 +603,7 @@ if __name__ == '__main__':
         filetests = TestBuilder(ROOTDIR, WORKDIR, selectors, exclude_selectors,
                                 options.annotate_source, options.cleanup_workdir,
                                 options.cleanup_sharedlibs, options.pyregr,
-                                options.cython_only, languages)
+                                options.cython_only, languages, test_bugs)
         test_suite.addTest(filetests.build_suite())
 
     if options.system_pyregr and languages:
@@ -561,7 +620,7 @@ if __name__ == '__main__':
 
     if options.coverage:
         coverage.stop()
-        ignored_modules = ('Options', 'Version', 'DebugFlags')
+        ignored_modules = ('Options', 'Version', 'DebugFlags', 'CmdLine')
         modules = [ module for name, module in sys.modules.items()
                     if module is not None and
                     name.startswith('Cython.Compiler.') and 
@@ -572,3 +631,7 @@ if __name__ == '__main__':
         sys.stderr.write("Following tests excluded because of missing dependencies on your system:\n")
         for test in missing_dep_excluder.tests_missing_deps:
             sys.stderr.write("   %s\n" % test)
+
+    if options.with_refnanny:
+        import refnanny
+        sys.stderr.write("\n".join([repr(x) for x in refnanny.reflog]))