http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / Scanner / CTests.py
index 3a5d3082e7985ba52dc8f47d9cf2c3ef31ba5678..2869d3be145f4471d8f3897a845ce7d3fd36b241 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-import TestCmd
-import SCons.Scanner.C
-import unittest
-import sys
 import os
 import os.path
+import sys
+import TestCmd
+import unittest
+import UserDict
+
 import SCons.Node.FS
 import SCons.Warnings
 
+import SCons.Scanner.C
+
 test = TestCmd.TestCmd(workdir = '')
 
 os.chdir(test.workpath(''))
@@ -167,92 +170,103 @@ test.write("f5b.h", "\n")
 
 # define some helpers:
 
-class DummyEnvironment:
-    def __init__(self, listCppPath):
-        self.path = listCppPath
-        
+class DummyEnvironment(UserDict.UserDict):
+    def __init__(self, **kw):
+        UserDict.UserDict.__init__(self)
+        self.data.update(kw)
+        self.fs = SCons.Node.FS.FS(test.workpath(''))
+
     def Dictionary(self, *args):
-        if not args:
-            return { 'CPPPATH': self.path }
-        elif len(args) == 1 and args[0] == 'CPPPATH':
-            return self.path
-        else:
-            raise KeyError, "Dummy environment only has CPPPATH attribute."
+        return self.data
 
-    def subst(self, arg):
-        return arg
+    def subst(self, strSubst, target=None, source=None, conv=None):
+        if strSubst[0] == '$':
+            return self.data[strSubst[1:]]
+        return strSubst
 
-    def has_key(self, key):
-        return self.Dictionary().has_key(key)
+    def subst_list(self, strSubst, target=None, source=None, conv=None):
+        if strSubst[0] == '$':
+            return [self.data[strSubst[1:]]]
+        return [[strSubst]]
 
-    def __getitem__(self,key):
-        return self.Dictionary()[key]
+    def subst_path(self, path, target=None, source=None, conv=None):
+        if not isinstance(path, list):
+            path = [path]
+        return list(map(self.subst, path))
 
-    def __setitem__(self,key,value):
-        self.Dictionary()[key] = value
+    def get_calculator(self):
+        return None
 
-    def __delitem__(self,key):
-        del self.Dictionary()[key]
+    def get_factory(self, factory):
+        return factory or self.fs.File
+
+    def Dir(self, filename):
+        return self.fs.Dir(filename)
+
+    def File(self, filename):
+        return self.fs.File(filename)
 
-global my_normpath
-my_normpath = os.path.normpath
 if os.path.normcase('foo') == os.path.normcase('FOO'):
-    global my_normpath
     my_normpath = os.path.normcase
+else:
+    my_normpath = os.path.normpath
 
 def deps_match(self, deps, headers):
-    scanned = map(my_normpath, map(str, deps))
-    expect = map(my_normpath, headers)
+    global my_normpath
+    scanned = list(map(my_normpath, list(map(str, deps))))
+    expect = list(map(my_normpath, headers))
     self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
 
-def make_node(filename, fs=SCons.Node.FS.default_fs):
-    return fs.File(test.workpath(filename))
-
 # define some tests:
 
 class CScannerTestCase1(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan()
+        """Find local files with no CPPPATH"""
+        env = DummyEnvironment(CPPPATH=[])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f1.cpp'), env, path)
+        deps = s(env.File('f1.cpp'), env, path)
         headers = ['f1.h', 'f2.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        deps_match(self, deps, headers)
 
 class CScannerTestCase2(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([test.workpath("d1")])
-        s = SCons.Scanner.C.CScan()
+        """Find a file in a CPPPATH directory"""
+        env = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f1.cpp'), env, path)
-        headers = ['d1/f2.h', 'f1.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        deps = s(env.File('f1.cpp'), env, path)
+        headers = ['f1.h', 'd1/f2.h']
+        deps_match(self, deps, headers)
 
 class CScannerTestCase3(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([test.workpath("d1")])
-        s = SCons.Scanner.C.CScan()
+        """Find files in explicit subdirectories, ignore missing file"""
+        env = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f2.cpp'), env, path)
-        headers = ['d1/d2/f1.h', 'd1/f1.h', 'f1.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        deps = s(env.File('f2.cpp'), env, path)
+        headers = ['d1/f1.h', 'f1.h', 'd1/d2/f1.h']
+        deps_match(self, deps, headers)
 
 class CScannerTestCase4(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
-        s = SCons.Scanner.C.CScan()
+        """Find files in explicit subdirectories"""
+        env = DummyEnvironment(CPPPATH=[test.workpath("d1"), test.workpath("d1/d2")])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f2.cpp'), env, path)
-        headers =  ['d1/d2/f1.h', 'd1/d2/f4.h', 'd1/f1.h', 'f1.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        deps = s(env.File('f2.cpp'), env, path)
+        headers =  ['d1/f1.h', 'f1.h', 'd1/d2/f1.h', 'd1/d2/f4.h']
+        deps_match(self, deps, headers)
         
 class CScannerTestCase5(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan()
+        """Make sure files in repositories will get scanned"""
+        env = DummyEnvironment(CPPPATH=[])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
 
-        n = make_node('f3.cpp')
+        n = env.File('f3.cpp')
         def my_rexists(s=n):
             s.rexists_called = 1
             return s.old_rexists()
@@ -262,46 +276,48 @@ class CScannerTestCase5(unittest.TestCase):
         deps = s(n, env, path)
 
         # Make sure rexists() got called on the file node being
-        # scanned, essential for cooperation with BuildDir functionality.
+        # scanned, essential for cooperation with VariantDir functionality.
         assert n.rexists_called
         
-        headers =  ['d1/f1.h', 'd1/f2.h', 'd1/f3-test.h',
-                    'f1.h', 'f2.h', 'f3-test.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        headers =  ['f1.h', 'f2.h', 'f3-test.h',
+                    'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h']
+        deps_match(self, deps, headers)
 
 class CScannerTestCase6(unittest.TestCase):
     def runTest(self):
-        env1 = DummyEnvironment([test.workpath("d1")])
-        env2 = DummyEnvironment([test.workpath("d1/d2")])
-        s = SCons.Scanner.C.CScan()
+        """Find a same-named file in different directories when CPPPATH changes"""
+        env1 = DummyEnvironment(CPPPATH=[test.workpath("d1")])
+        env2 = DummyEnvironment(CPPPATH=[test.workpath("d1/d2")])
+        s = SCons.Scanner.C.CScanner()
         path1 = s.path(env1)
         path2 = s.path(env2)
-        deps1 = s(make_node('f1.cpp'), env1, path1)
-        deps2 = s(make_node('f1.cpp'), env2, path2)
-        headers1 =  ['d1/f2.h', 'f1.h']
-        headers2 =  ['d1/d2/f2.h', 'f1.h']
-        deps_match(self, deps1, map(test.workpath, headers1))
-        deps_match(self, deps2, map(test.workpath, headers2))
+        deps1 = s(env1.File('f1.cpp'), env1, path1)
+        deps2 = s(env2.File('f1.cpp'), env2, path2)
+        headers1 =  ['f1.h', 'd1/f2.h']
+        headers2 =  ['f1.h', 'd1/d2/f2.h']
+        deps_match(self, deps1, headers1)
+        deps_match(self, deps2, headers2)
 
 class CScannerTestCase8(unittest.TestCase):
     def runTest(self):
-        fs = SCons.Node.FS.FS(test.workpath(''))
-        env = DummyEnvironment(["include"])
-        s = SCons.Scanner.C.CScan(fs = fs)
+        """Find files in a subdirectory relative to the current directory"""
+        env = DummyEnvironment(CPPPATH=["include"])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps1 = s(fs.File('fa.cpp'), env, path)
-        fs.chdir(fs.Dir('subdir'))
-        dir = fs.getcwd()
-        fs.chdir(fs.Dir('..'))
+        deps1 = s(env.File('fa.cpp'), env, path)
+        env.fs.chdir(env.Dir('subdir'))
+        dir = env.fs.getcwd()
+        env.fs.chdir(env.Dir(''))
         path = s.path(env, dir)
-        deps2 = s(fs.File('#fa.cpp'), env, path)
-        headers1 =  ['include/fa.h', 'include/fb.h']
-        headers2 =  ['subdir/include/fa.h', 'subdir/include/fb.h']
+        deps2 = s(env.File('#fa.cpp'), env, path)
+        headers1 =  list(map(test.workpath, ['include/fa.h', 'include/fb.h']))
+        headers2 =  ['include/fa.h', 'include/fb.h']
         deps_match(self, deps1, headers1)
         deps_match(self, deps2, headers2)
 
 class CScannerTestCase9(unittest.TestCase):
     def runTest(self):
+        """Generate a warning when we can't find a #included file"""
         SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
         class TestOut:
             def __call__(self, x):
@@ -312,8 +328,9 @@ class CScannerTestCase9(unittest.TestCase):
         SCons.Warnings._warningOut = to
         test.write('fa.h','\n')
         fs = SCons.Node.FS.FS(test.workpath(''))
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan(fs=fs)
+        env = DummyEnvironment(CPPPATH=[])
+        env.fs = fs
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
         deps = s(fs.File('fa.cpp'), env, path)
 
@@ -325,19 +342,22 @@ class CScannerTestCase9(unittest.TestCase):
 
 class CScannerTestCase10(unittest.TestCase):
     def runTest(self):
+        """Find files in the local directory when the scanned file is elsewhere"""
         fs = SCons.Node.FS.FS(test.workpath(''))
         fs.chdir(fs.Dir('include'))
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan(fs=fs)
+        env = DummyEnvironment(CPPPATH=[])
+        env.fs = fs
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
         test.write('include/fa.cpp', test.read('fa.cpp'))
-        deps = s(fs.File('#include/fa.cpp'), env, path)
         fs.chdir(fs.Dir('..'))
+        deps = s(fs.File('#include/fa.cpp'), env, path)
         deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
         test.unlink('include/fa.cpp')
 
 class CScannerTestCase11(unittest.TestCase):
     def runTest(self):
+        """Handle dependencies on a derived .h file in a non-existent directory"""
         os.chdir(test.workpath('work'))
         fs = SCons.Node.FS.FS(test.workpath('work'))
         fs.Repository(test.workpath('repository'))
@@ -346,22 +366,26 @@ class CScannerTestCase11(unittest.TestCase):
         # This was a bug at one time.
         f1=fs.File('include2/jjj.h')
         f1.builder=1
-        env = DummyEnvironment(['include', 'include2'])
-        s = SCons.Scanner.C.CScan(fs=fs)
+        env = DummyEnvironment(CPPPATH=['include', 'include2'])
+        env.fs = fs
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
         deps = s(fs.File('src/fff.c'), env, path)
-        deps_match(self, deps, [ test.workpath('repository/include/iii.h'), 'include2/jjj.h' ])
+        deps_match(self, deps, [ test.workpath('repository/include/iii.h'),
+                                 'include2/jjj.h' ])
         os.chdir(test.workpath(''))
 
 class CScannerTestCase12(unittest.TestCase):
     def runTest(self):
+        """Find files in VariantDir() directories"""
         os.chdir(test.workpath('work'))
         fs = SCons.Node.FS.FS(test.workpath('work'))
-        fs.BuildDir('build1', 'src', 1)
-        fs.BuildDir('build2', 'src', 0)
+        fs.VariantDir('build1', 'src', 1)
+        fs.VariantDir('build2', 'src', 0)
         fs.Repository(test.workpath('repository'))
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan(fs = fs)
+        env = DummyEnvironment(CPPPATH=[])
+        env.fs = fs
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
         deps1 = s(fs.File('build1/aaa.c'), env, path)
         deps_match(self, deps1, [ 'build1/bbb.h' ])
@@ -375,25 +399,43 @@ class CScannerTestCase12(unittest.TestCase):
 
 class CScannerTestCase13(unittest.TestCase):
     def runTest(self):
+        """Find files in directories named in a substituted environment variable"""
         class SubstEnvironment(DummyEnvironment):
-            def subst(self, arg, test=test):
-                return test.workpath("d1")
-        env = SubstEnvironment(["blah"])
-        s = SCons.Scanner.C.CScan()
+            def subst(self, arg, target=None, source=None, conv=None, test=test):
+                if arg == "$blah":
+                    return test.workpath("d1")
+                else:
+                    return arg
+        env = SubstEnvironment(CPPPATH=["$blah"])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f1.cpp'), env, path)
-        headers = ['d1/f2.h', 'f1.h']
-        deps_match(self, deps, map(test.workpath, headers))
+        deps = s(env.File('f1.cpp'), env, path)
+        headers = ['f1.h', 'd1/f2.h']
+        deps_match(self, deps, headers)
 
 class CScannerTestCase14(unittest.TestCase):
     def runTest(self):
-        env = DummyEnvironment([])
-        s = SCons.Scanner.C.CScan()
+        """Find files when there's no space between "#include" and the name"""
+        env = DummyEnvironment(CPPPATH=[])
+        s = SCons.Scanner.C.CScanner()
         path = s.path(env)
-        deps = s(make_node('f5.c'), env, path)
+        deps = s(env.File('f5.c'), env, path)
         headers = ['f5a.h', 'f5b.h']
-        deps_match(self, deps, map(test.workpath, headers))
-        
+        deps_match(self, deps, headers)
+
+class CScannerTestCase15(unittest.TestCase):
+    def runTest(self):
+        """Verify scanner initialization with the suffixes in $CPPSUFFIXES"""
+        suffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+                    ".h", ".H", ".hxx", ".hpp", ".hh",
+                    ".F", ".fpp", ".FPP",
+                    ".S", ".spp", ".SPP"]
+        env = DummyEnvironment(CPPSUFFIXES = suffixes)
+        s = SCons.Scanner.C.CScanner()
+        for suffix in suffixes:
+            assert suffix in s.get_skeys(env), "%s not in skeys" % suffix
+
+
 
 def suite():
     suite = unittest.TestSuite()
@@ -410,6 +452,7 @@ def suite():
     suite.addTest(CScannerTestCase12())
     suite.addTest(CScannerTestCase13())
     suite.addTest(CScannerTestCase14())
+    suite.addTest(CScannerTestCase15())
     return suite
 
 if __name__ == "__main__":
@@ -417,3 +460,9 @@ if __name__ == "__main__":
     result = runner.run(suite())
     if not result.wasSuccessful():
         sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: