merged in Vitja's tab removals
[cython.git] / runtests.py
index 4d2c1bb201842d4d436252a6daf94f4692536366..d565aed1c9497128c3df274b2fedc183b8cb3a21 100644 (file)
@@ -69,20 +69,18 @@ if sys.platform == 'win32':
     distutils_distro.parse_config_files(cfgfiles)
 
 EXT_DEP_MODULES = {
-    'numpy' : 'tag:numpy',
-    'pstats' : 'tag:pstats',
-    'posix' : 'tag:posix',
+    'tag:numpy' : 'numpy',
+    'tag:pstats': 'pstats',
+    'tag:posix' : 'posix',
 }
 
-def get_numpy_include_dirs():
+def update_numpy_extension(ext):
     import numpy
-    return [numpy.get_include()]
+    ext.include_dirs.append(numpy.get_include())
 
-# TODO: use tags
-EXT_DEP_INCLUDES = [
-    # test name matcher , callable returning list
-    (re.compile('numpy_.*').match, get_numpy_include_dirs),
-]
+EXT_EXTRAS = {
+    'tag:numpy' : update_numpy_extension,
+}
 
 # TODO: use tags
 VER_DEP_MODULES = {
@@ -107,7 +105,9 @@ VER_DEP_MODULES = {
     # we can only have one (3,) key.  Since 2.7 is supposed to be the
     # last 2.x release, things would have to change drastically for this
     # to be unsafe...
-    (2,999): (operator.lt, lambda x: x in ['run.special_methods_T561_py3']),
+    (2,999): (operator.lt, lambda x: x in ['run.special_methods_T561_py3',
+                                           'run.test_raisefrom',
+                                           ]),
     (3,): (operator.ge, lambda x: x in ['run.non_future_division',
                                         'compile.extsetslice',
                                         'compile.extdelslice',
@@ -157,6 +157,8 @@ def parse_tags(filepath):
 
 parse_tags = memoize(parse_tags)
 
+list_unchanging_dir = memoize(lambda x: os.listdir(x))
+
 
 class build_ext(_build_ext):
     def build_extension(self, ext):
@@ -243,7 +245,7 @@ class TestBuilder(object):
             os.makedirs(workdir)
 
         suite = unittest.TestSuite()
-        filenames = os.listdir(path)
+        filenames = list_unchanging_dir(path)
         filenames.sort()
         for filename in filenames:
             filepath = os.path.join(path, filename)
@@ -295,7 +297,7 @@ class TestBuilder(object):
         return suite
 
     def build_tests(self, test_class, path, workdir, module, expect_errors, tags):
-        if 'werror' in tags['tags']:
+        if 'werror' in tags['tag']:
             warning_errors = True
         else:
             warning_errors = False
@@ -412,19 +414,21 @@ class CythonCompileTestCase(unittest.TestCase):
     def build_target_filename(self, module_name):
         target = '%s.%s' % (module_name, self.language)
         return target
-
-    def copy_related_files(self, test_directory, target_directory, module_name):
+    
+    def related_files(self, test_directory, module_name):
         is_related = re.compile('%s_.*[.].*' % module_name).match
-        for filename in os.listdir(test_directory):
-            if is_related(filename):
-                shutil.copy(os.path.join(test_directory, filename),
-                            target_directory)
+        return [filename for filename in list_unchanging_dir(test_directory)
+            if is_related(filename)]
+
+    def copy_files(self, test_directory, target_directory, file_list):
+        for filename in file_list:
+            shutil.copy(os.path.join(test_directory, filename),
+                        target_directory)
 
-    def find_source_files(self, workdir, module_name):
-        is_related = re.compile('%s_.*[.]%s' % (module_name, self.language)).match
-        return [self.build_target_filename(module_name)] + [
-            filename for filename in os.listdir(workdir)
-            if is_related(filename) and os.path.isfile(os.path.join(workdir, filename)) ]
+    def source_files(self, workdir, module_name, file_list):
+        return ([self.build_target_filename(module_name)] +
+            [filename for filename in file_list
+                if not os.path.isfile(os.path.join(workdir, filename))])
 
     def split_source_and_output(self, test_directory, module, workdir):
         source_file = self.find_module_source_file(os.path.join(test_directory, module) + '.pyx')
@@ -485,6 +489,12 @@ class CythonCompileTestCase(unittest.TestCase):
 
     def run_distutils(self, test_directory, module, workdir, incdir,
                       extra_extension_args=None):
+        original_source = self.find_module_source_file(
+            os.path.join(test_directory, module + '.pyx'))
+        try:
+            tags = parse_tags(original_source)
+        except IOError:
+            tags = {}
         cwd = os.getcwd()
         os.chdir(workdir)
         try:
@@ -495,24 +505,27 @@ class CythonCompileTestCase(unittest.TestCase):
             build_extension.finalize_options()
             if COMPILER:
                 build_extension.compiler = COMPILER
-            ext_include_dirs = []
-            for match, get_additional_include_dirs in EXT_DEP_INCLUDES:
-                if match(module):
-                    ext_include_dirs += get_additional_include_dirs()
             ext_compile_flags = CFLAGS[:]
             if  build_extension.compiler == 'mingw32':
                 ext_compile_flags.append('-Wno-format')
             if extra_extension_args is None:
                 extra_extension_args = {}
 
-            self.copy_related_files(test_directory, workdir, module)
+            related_files = self.related_files(test_directory, module)
+            self.copy_files(test_directory, workdir, related_files)
             extension = Extension(
                 module,
-                sources = self.find_source_files(workdir, module),
-                include_dirs = ext_include_dirs,
+                sources = self.source_files(workdir, module, related_files),
                 extra_compile_args = ext_compile_flags,
                 **extra_extension_args
                 )
+            for matcher, fixer in EXT_EXTRAS.items():
+                if isinstance(matcher, str):
+                    del EXT_EXTRAS[matcher]
+                    matcher = string_selector(matcher)
+                    EXT_EXTRAS[matcher] = fixer
+                if matcher(module, tags):
+                    extension = fixer(extension) or extension
             if self.language == 'cpp':
                 extension.language = 'c++'
             build_extension.extensions = [extension]
@@ -595,8 +608,8 @@ class CythonRunTestCase(CythonCompileTestCase):
 
 
 def run_forked_test(result, run_func, test_name, fork=True):
-    if sys.version_info[0] >= 3 or not hasattr(os, 'fork') or not fork:
-        run_test(result)
+    if not fork or sys.version_info[0] >= 3 or not hasattr(os, 'fork'):
+        run_func(result)
         gc.collect()
         return
 
@@ -998,9 +1011,9 @@ class EmbedTest(unittest.TestCase):
 
 class MissingDependencyExcluder:
     def __init__(self, deps):
-        # deps: { module name : matcher func }
+        # deps: { matcher func : module name }
         self.exclude_matchers = []
-        for mod, matcher in deps.items():
+        for matcher, mod in deps.items():
             try:
                 __import__(mod)
             except ImportError:
@@ -1187,6 +1200,9 @@ def main():
     parser.add_option("--coverage-xml", dest="coverage_xml",
                       action="store_true", default=False,
                       help="collect source coverage data for the Compiler in XML format")
+    parser.add_option("--coverage-html", dest="coverage_html",
+                      action="store_true", default=False,
+                      help="collect source coverage data for the Compiler in HTML format")
     parser.add_option("-A", "--annotate", dest="annotate_source",
                       action="store_true", default=True,
                       help="generate annotated HTML versions of the test source files")
@@ -1242,9 +1258,9 @@ def main():
 
     WITH_CYTHON = options.with_cython
 
-    if options.coverage or options.coverage_xml:
+    if options.coverage or options.coverage_xml or options.coverage_html:
         if not WITH_CYTHON:
-            options.coverage = options.coverage_xml = False
+            options.coverage = options.coverage_xml = options.coverage_html = False
         else:
             from coverage import coverage as _coverage
             coverage = _coverage(branch=True)
@@ -1377,7 +1393,7 @@ def main():
 
     result = test_runner.run(test_suite)
 
-    if options.coverage or options.coverage_xml:
+    if options.coverage or options.coverage_xml or options.coverage_html:
         coverage.stop()
         ignored_modules = ('Options', 'Version', 'DebugFlags', 'CmdLine')
         modules = [ module for name, module in sys.modules.items()
@@ -1388,6 +1404,8 @@ def main():
             coverage.report(modules, show_missing=0)
         if options.coverage_xml:
             coverage.xml_report(modules, outfile="coverage-report.xml")
+        if options.coverage_html:
+            coverage.html_report(modules, directory="coverage-report-html")
 
     if missing_dep_excluder.tests_missing_deps:
         sys.stderr.write("Following tests excluded because of missing dependencies on your system:\n")