Back to index

enigmail  1.4.3
unit-expandlibs.py
Go to the documentation of this file.
00001 from __future__ import with_statement
00002 import subprocess
00003 import unittest
00004 import sys
00005 import os
00006 import imp
00007 from tempfile import mkdtemp
00008 from shutil import rmtree
00009 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
00010 from mozunit import MozTestRunner
00011 
00012 from UserString import UserString
00013 # Create a controlled configuration for use by expandlibs
00014 config_win = {
00015     'AR_EXTRACT': '',
00016     'DLL_PREFIX': '',
00017     'LIB_PREFIX': '',
00018     'OBJ_SUFFIX': '.obj',
00019     'LIB_SUFFIX': '.lib',
00020     'DLL_SUFFIX': '.dll',
00021     'IMPORT_LIB_SUFFIX': '.lib',
00022     'LIBS_DESC_SUFFIX': '.desc',
00023     'EXPAND_LIBS_LIST_STYLE': 'list',
00024 }
00025 config_unix = {
00026     'AR_EXTRACT': 'ar -x',
00027     'DLL_PREFIX': 'lib',
00028     'LIB_PREFIX': 'lib',
00029     'OBJ_SUFFIX': '.o',
00030     'LIB_SUFFIX': '.a',
00031     'DLL_SUFFIX': '.so',
00032     'IMPORT_LIB_SUFFIX': '',
00033     'LIBS_DESC_SUFFIX': '.desc',
00034     'EXPAND_LIBS_LIST_STYLE': 'linkerscript',
00035 }
00036 
00037 config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config')
00038 
00039 from expandlibs import LibDescriptor, ExpandArgs, relativize
00040 from expandlibs_gen import generate
00041 from expandlibs_exec import ExpandArgsMore, SectionFinder
00042 
00043 def Lib(name):
00044     return config.LIB_PREFIX + name + config.LIB_SUFFIX
00045 
00046 def Obj(name):
00047     return name + config.OBJ_SUFFIX
00048 
00049 def Dll(name):
00050     return config.DLL_PREFIX + name + config.DLL_SUFFIX
00051 
00052 def ImportLib(name):
00053     if not len(config.IMPORT_LIB_SUFFIX): return Dll(name)
00054     return config.LIB_PREFIX + name + config.IMPORT_LIB_SUFFIX
00055 
00056 class TestRelativize(unittest.TestCase):
00057     def test_relativize(self):
00058         '''Test relativize()'''
00059         os_path_exists = os.path.exists
00060         def exists(path):
00061             return True
00062         os.path.exists = exists
00063         self.assertEqual(relativize(os.path.abspath(os.curdir)), os.curdir)
00064         self.assertEqual(relativize(os.path.abspath(os.pardir)), os.pardir)
00065         self.assertEqual(relativize(os.path.join(os.curdir, 'a')), 'a')
00066         self.assertEqual(relativize(os.path.join(os.path.abspath(os.curdir), 'a')), 'a')
00067         # relativize is expected to return the absolute path if it is shorter
00068         self.assertEqual(relativize(os.sep), os.sep)
00069         os.path.exists = os.path.exists
00070 
00071 class TestLibDescriptor(unittest.TestCase):
00072     def test_serialize(self):
00073         '''Test LibDescriptor's serialization'''
00074         desc = LibDescriptor()
00075         desc[LibDescriptor.KEYS[0]] = ['a', 'b']
00076         self.assertEqual(str(desc), "%s = a b" % LibDescriptor.KEYS[0])
00077         desc['unsupported-key'] = ['a']
00078         self.assertEqual(str(desc), "%s = a b" % LibDescriptor.KEYS[0])
00079         desc[LibDescriptor.KEYS[1]] = ['c', 'd', 'e']
00080         self.assertEqual(str(desc), "%s = a b\n%s = c d e" % (LibDescriptor.KEYS[0], LibDescriptor.KEYS[1]))
00081         desc[LibDescriptor.KEYS[0]] = []
00082         self.assertEqual(str(desc), "%s = c d e" % (LibDescriptor.KEYS[1]))
00083 
00084     def test_read(self):
00085         '''Test LibDescriptor's initialization'''
00086         desc_list = ["# Comment",
00087                      "%s = a b" % LibDescriptor.KEYS[1],
00088                      "", # Empty line
00089                      "foo = bar", # Should be discarded
00090                      "%s = c d e" % LibDescriptor.KEYS[0]]
00091         desc = LibDescriptor(desc_list)
00092         self.assertEqual(desc[LibDescriptor.KEYS[1]], ['a', 'b'])
00093         self.assertEqual(desc[LibDescriptor.KEYS[0]], ['c', 'd', 'e'])
00094         self.assertEqual(False, 'foo' in desc)
00095 
00096 def wrap_method(conf, wrapped_method):
00097     '''Wrapper used to call a test with a specific configuration'''
00098     def _method(self):
00099         for key in conf:
00100             setattr(config, key, conf[key])
00101         self.init()
00102         try:
00103             wrapped_method(self)
00104         except:
00105             raise
00106         finally:
00107             self.cleanup()
00108     return _method
00109 
00110 class ReplicateTests(type):
00111     '''Replicates tests for unix and windows variants'''
00112     def __new__(cls, clsName, bases, dict):
00113         for name in [key for key in dict if key.startswith('test_')]:
00114             dict[name + '_unix'] = wrap_method(config_unix, dict[name])
00115             dict[name + '_unix'].__doc__ = dict[name].__doc__ + ' (unix)'
00116             dict[name + '_win'] = wrap_method(config_win, dict[name])
00117             dict[name + '_win'].__doc__ = dict[name].__doc__ + ' (win)'
00118             del dict[name]
00119         return type.__new__(cls, clsName, bases, dict)
00120 
00121 class TestCaseWithTmpDir(unittest.TestCase):
00122     __metaclass__ = ReplicateTests
00123     def init(self):
00124         self.tmpdir = os.path.abspath(mkdtemp(dir=os.curdir))
00125 
00126     def cleanup(self):
00127         rmtree(self.tmpdir)
00128 
00129     def touch(self, files):
00130         for f in files:
00131             open(f, 'w').close()
00132 
00133     def tmpfile(self, *args):
00134         return os.path.join(self.tmpdir, *args)
00135 
00136 class TestExpandLibsGen(TestCaseWithTmpDir):
00137     def test_generate(self):
00138         '''Test library descriptor generation'''
00139         files = [self.tmpfile(f) for f in
00140                  [Lib('a'), Obj('b'), Lib('c'), Obj('d'), Obj('e'), Lib('f')]]
00141         self.touch(files[:-1])
00142         self.touch([files[-1] + config.LIBS_DESC_SUFFIX])
00143 
00144         desc = generate(files)
00145         self.assertEqual(desc['OBJS'], [self.tmpfile(Obj(s)) for s in ['b', 'd', 'e']])
00146         self.assertEqual(desc['LIBS'], [self.tmpfile(Lib(s)) for s in ['a', 'c', 'f']])
00147 
00148 class TestExpandInit(TestCaseWithTmpDir):
00149     def init(self):
00150         ''' Initializes test environment for library expansion tests'''
00151         super(TestExpandInit, self).init()
00152         # Create 2 fake libraries, each containing 3 objects, and the second
00153         # including the first one and another library.
00154         os.mkdir(self.tmpfile('libx'))
00155         os.mkdir(self.tmpfile('liby'))
00156         self.libx_files = [self.tmpfile('libx', Obj(f)) for f in ['g', 'h', 'i']]
00157         self.liby_files = [self.tmpfile('liby', Obj(f)) for f in ['j', 'k', 'l']] + [self.tmpfile('liby', Lib('z'))]
00158         self.touch(self.libx_files + self.liby_files)
00159         with open(self.tmpfile('libx', Lib('x') + config.LIBS_DESC_SUFFIX), 'w') as f:
00160             f.write(str(generate(self.libx_files)))
00161         with open(self.tmpfile('liby', Lib('y') + config.LIBS_DESC_SUFFIX), 'w') as f:
00162             f.write(str(generate(self.liby_files + [self.tmpfile('libx', Lib('x'))])))
00163 
00164         # Create various objects and libraries 
00165         self.arg_files = [self.tmpfile(f) for f in [Lib('a'), Obj('b'), Obj('c'), Lib('d'), Obj('e')]]
00166         # We always give library names (LIB_PREFIX/SUFFIX), even for
00167         # dynamic/import libraries
00168         self.files = self.arg_files + [self.tmpfile(ImportLib('f'))]
00169         self.arg_files += [self.tmpfile(Lib('f'))]
00170         self.touch(self.files)
00171 
00172     def assertRelEqual(self, args1, args2):
00173         self.assertEqual(args1, [relativize(a) for a in args2])
00174 
00175 class TestExpandArgs(TestExpandInit):
00176     def test_expand(self):
00177         '''Test library expansion'''
00178         # Expanding arguments means libraries with a descriptor are expanded
00179         # with the descriptor content, and import libraries are used when
00180         # a library doesn't exist
00181         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
00182         self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
00183 
00184         # When a library exists at the same time as a descriptor, we just use
00185         # the library
00186         self.touch([self.tmpfile('libx', Lib('x'))])
00187         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
00188         self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + [self.tmpfile('libx', Lib('x'))]) 
00189 
00190         self.touch([self.tmpfile('liby', Lib('y'))])
00191         args = ExpandArgs(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))])
00192         self.assertRelEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
00193 
00194 class TestExpandArgsMore(TestExpandInit):
00195     def test_makelist(self):
00196         '''Test grouping object files in lists'''
00197         # ExpandArgsMore does the same as ExpandArgs
00198         with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
00199             self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
00200 
00201             # But also has an extra method replacing object files with a list
00202             args.makelist()
00203             # self.files has objects at #1, #2, #4
00204             self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1])
00205             self.assertRelEqual(args[4:], [self.files[3]] + self.files[5:] + [self.tmpfile('liby', Lib('z'))])
00206 
00207             # Check the list file content
00208             objs = [f for f in self.files + self.liby_files + self.libx_files if f.endswith(config.OBJ_SUFFIX)]
00209             if config.EXPAND_LIBS_LIST_STYLE == "linkerscript":
00210                 self.assertNotEqual(args[3][0], '@')
00211                 filename = args[3]
00212                 content = ["INPUT(%s)" % relativize(f) for f in objs]
00213                 with open(filename, 'r') as f:
00214                     self.assertEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
00215             elif config.EXPAND_LIBS_LIST_STYLE == "list":
00216                 self.assertEqual(args[3][0], '@')
00217                 filename = args[3][1:]
00218                 content = objs
00219                 with open(filename, 'r') as f:
00220                     self.assertRelEqual([l.strip() for l in f.readlines() if len(l.strip())], content)
00221 
00222             tmp = args.tmp
00223         # Check that all temporary files are properly removed
00224         self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
00225 
00226     def test_extract(self):
00227         '''Test library extraction'''
00228         # Divert subprocess.call
00229         subprocess_call = subprocess.call
00230         extracted = {}
00231         def call(args, **kargs):
00232             # The command called is always AR_EXTRACT
00233             ar_extract = config.AR_EXTRACT.split()
00234             self.assertRelEqual(args[:len(ar_extract)], ar_extract)
00235             # Remaining argument is always one library
00236             self.assertRelEqual([os.path.splitext(arg)[1] for arg in args[len(ar_extract):]], [config.LIB_SUFFIX])
00237             # Simulate AR_EXTRACT extracting one object file for the library
00238             lib = os.path.splitext(os.path.basename(args[len(ar_extract)]))[0]
00239             extracted[lib] = os.path.join(kargs['cwd'], "%s" % Obj(lib))
00240             self.touch([extracted[lib]])
00241         subprocess.call = call
00242 
00243         # ExpandArgsMore does the same as ExpandArgs
00244         self.touch([self.tmpfile('liby', Lib('y'))])
00245         with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
00246             self.assertRelEqual(args, ['foo', '-bar'] + self.files + [self.tmpfile('liby', Lib('y'))])
00247 
00248             # ExpandArgsMore also has an extra method extracting static libraries
00249             # when possible
00250             args.extract()
00251 
00252             files = self.files + self.liby_files + self.libx_files
00253             if not len(config.AR_EXTRACT):
00254                 # If we don't have an AR_EXTRACT, extract() expands libraries with a
00255                 # descriptor when the corresponding library exists (which ExpandArgs
00256                 # alone doesn't)
00257                 self.assertRelEqual(args, ['foo', '-bar'] + files)
00258             else:
00259                 # With AR_EXTRACT, it uses the descriptors when there are, and actually
00260                 # extracts the remaining libraries
00261                 self.assertRelEqual(args, ['foo', '-bar'] + [extracted[os.path.splitext(os.path.basename(f))[0]] if f.endswith(config.LIB_SUFFIX) else f for f in files])
00262 
00263             tmp = args.tmp
00264         # Check that all temporary files are properly removed
00265         self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
00266 
00267         # Restore subprocess.call
00268         subprocess.call = subprocess_call
00269 
00270 class FakeProcess(object):
00271     def __init__(self, out, err = ''):
00272         self.out = out
00273         self.err = err
00274 
00275     def communicate(self):
00276         return (self.out, self.err)
00277 
00278 OBJDUMPS = {
00279 'foo.o': '''
00280 00000000 g     F .text\t00000001 foo
00281 00000000 g     F .text._Z6foobarv\t00000001 _Z6foobarv
00282 00000000 g     F .text.hello\t00000001 hello
00283 00000000 g     F .text._ZThn4_6foobarv\t00000001 _ZThn4_6foobarv
00284 ''',
00285 'bar.o': '''
00286 00000000 g     F .text.hi\t00000001 hi
00287 00000000 g     F .text.hot._Z6barbazv\t00000001 .hidden _Z6barbazv
00288 ''',
00289 }
00290 
00291 PRINT_ICF = '''
00292 ld: ICF folding section '.text.hello' in file 'foo.o'into '.text.hi' in file 'bar.o'
00293 ld: ICF folding section '.foo' in file 'foo.o'into '.foo' in file 'bar.o'
00294 '''
00295 
00296 class SubprocessPopen(object):
00297     def __init__(self, test):
00298         self.test = test
00299 
00300     def __call__(self, args, stdout = None, stderr = None):
00301         self.test.assertEqual(stdout, subprocess.PIPE)
00302         self.test.assertEqual(stderr, subprocess.PIPE)
00303         if args[0] == 'objdump':
00304             self.test.assertEqual(args[1], '-t')
00305             self.test.assertTrue(args[2] in OBJDUMPS)
00306             return FakeProcess(OBJDUMPS[args[2]])
00307         else:
00308             return FakeProcess('', PRINT_ICF)
00309 
00310 class TestSectionFinder(unittest.TestCase):
00311     def test_getSections(self):
00312         '''Test SectionFinder'''
00313         # Divert subprocess.Popen
00314         subprocess_popen = subprocess.Popen
00315         subprocess.Popen = SubprocessPopen(self)
00316         config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
00317         config.OBJ_SUFFIX = '.o'
00318         config.LIB_SUFFIX = '.a'
00319         finder = SectionFinder(['foo.o', 'bar.o'])
00320         self.assertEqual(finder.getSections('foobar'), [])
00321         self.assertEqual(finder.getSections('_Z6barbazv'), ['.text.hot._Z6barbazv'])
00322         self.assertEqual(finder.getSections('_Z6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
00323         self.assertEqual(finder.getSections('_ZThn4_6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
00324         subprocess.Popen = subprocess_popen
00325 
00326 class TestSymbolOrder(unittest.TestCase):
00327     def test_getOrderedSections(self):
00328         '''Test ExpandMoreArgs' _getOrderedSections'''
00329         # Divert subprocess.Popen
00330         subprocess_popen = subprocess.Popen
00331         subprocess.Popen = SubprocessPopen(self)
00332         config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
00333         config.OBJ_SUFFIX = '.o'
00334         config.LIB_SUFFIX = '.a'
00335         config.LD_PRINT_ICF_SECTIONS = ''
00336         args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
00337         self.assertEqual(args._getOrderedSections(['_Z6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
00338         self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
00339         subprocess.Popen = subprocess_popen
00340 
00341     def test_getFoldedSections(self):
00342         '''Test ExpandMoreArgs' _getFoldedSections'''
00343         # Divert subprocess.Popen
00344         subprocess_popen = subprocess.Popen
00345         subprocess.Popen = SubprocessPopen(self)
00346         config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
00347         args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
00348         self.assertEqual(args._getFoldedSections(), {'.text.hello': '.text.hi', '.text.hi': ['.text.hello']})
00349         subprocess.Popen = subprocess_popen
00350 
00351     def test_getOrderedSectionsWithICF(self):
00352         '''Test ExpandMoreArgs' _getOrderedSections, with ICF'''
00353         # Divert subprocess.Popen
00354         subprocess_popen = subprocess.Popen
00355         subprocess.Popen = SubprocessPopen(self)
00356         config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
00357         config.OBJ_SUFFIX = '.o'
00358         config.LIB_SUFFIX = '.a'
00359         config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
00360         args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
00361         self.assertEqual(args._getOrderedSections(['hello', '_Z6barbazv']), ['.text.hi', '.text.hello', '.text.hot._Z6barbazv'])
00362         self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', 'hi', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hi', '.text.hello', '.text.hot._Z6barbazv'])
00363         subprocess.Popen = subprocess_popen
00364 
00365 
00366 if __name__ == '__main__':
00367     unittest.main(testRunner=MozTestRunner())