Back to index

enigmail  1.4.3
unit-JarMaker.py
Go to the documentation of this file.
00001 import unittest
00002 
00003 import os, sys, os.path, time, inspect
00004 from filecmp import dircmp
00005 from tempfile import mkdtemp
00006 from shutil import rmtree, copy2
00007 from zipfile import ZipFile
00008 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
00009 
00010 from mozunit import MozTestRunner
00011 from JarMaker import JarMaker
00012 
00013 if sys.platform == "win32":
00014     import ctypes
00015     from ctypes import POINTER, WinError
00016     DWORD = ctypes.c_ulong
00017     LPDWORD = POINTER(DWORD)
00018     HANDLE = ctypes.c_void_p
00019     GENERIC_READ = 0x80000000
00020     FILE_SHARE_READ = 0x00000001
00021     OPEN_EXISTING = 3
00022     MAX_PATH = 260
00023 
00024     class FILETIME(ctypes.Structure):
00025         _fields_ = [("dwLowDateTime", DWORD),
00026                     ("dwHighDateTime", DWORD)]
00027 
00028     class BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
00029         _fields_ = [("dwFileAttributes", DWORD),
00030                     ("ftCreationTime", FILETIME),
00031                     ("ftLastAccessTime", FILETIME),
00032                     ("ftLastWriteTime", FILETIME),
00033                     ("dwVolumeSerialNumber", DWORD),
00034                     ("nFileSizeHigh", DWORD),
00035                     ("nFileSizeLow", DWORD),
00036                     ("nNumberOfLinks", DWORD),
00037                     ("nFileIndexHigh", DWORD),
00038                     ("nFileIndexLow", DWORD)]
00039 
00040     # http://msdn.microsoft.com/en-us/library/aa363858
00041     CreateFile = ctypes.windll.kernel32.CreateFileA
00042     CreateFile.argtypes = [ctypes.c_char_p, DWORD, DWORD, ctypes.c_void_p,
00043                            DWORD, DWORD, HANDLE]
00044     CreateFile.restype = HANDLE
00045 
00046     # http://msdn.microsoft.com/en-us/library/aa364952
00047     GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle
00048     GetFileInformationByHandle.argtypes = [HANDLE, POINTER(BY_HANDLE_FILE_INFORMATION)]
00049     GetFileInformationByHandle.restype = ctypes.c_int
00050 
00051     # http://msdn.microsoft.com/en-us/library/aa364996
00052     GetVolumePathName = ctypes.windll.kernel32.GetVolumePathNameA
00053     GetVolumePathName.argtypes = [ctypes.c_char_p, ctypes.c_char_p, DWORD]
00054     GetVolumePathName.restype = ctypes.c_int
00055 
00056     # http://msdn.microsoft.com/en-us/library/aa364993
00057     GetVolumeInformation = ctypes.windll.kernel32.GetVolumeInformationA
00058     GetVolumeInformation.argtypes = [ctypes.c_char_p, ctypes.c_char_p, DWORD,
00059                                      LPDWORD, LPDWORD, LPDWORD, ctypes.c_char_p,
00060                                      DWORD]
00061     GetVolumeInformation.restype = ctypes.c_int
00062 
00063 def symlinks_supported(path):
00064     if sys.platform == "win32":
00065         # Add 1 for a trailing backslash if necessary, and 1 for the terminating
00066         # null character.
00067         volpath = ctypes.create_string_buffer(len(path) + 2)
00068         rv = GetVolumePathName(path, volpath, len(volpath))
00069         if rv == 0:
00070             raise WinError()
00071 
00072         fsname = ctypes.create_string_buffer(MAX_PATH + 1)
00073         rv = GetVolumeInformation(volpath, None, 0, None, None, None, fsname,
00074                                   len(fsname))
00075         if rv == 0:
00076             raise WinError()
00077 
00078         # Return true only if the fsname is NTFS
00079         return fsname.value == "NTFS"
00080     else:
00081         return True
00082 
00083 def _getfileinfo(path):
00084     """Return information for the given file. This only works on Windows."""
00085     fh = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, None, OPEN_EXISTING, 0, None)
00086     if fh is None:
00087         raise WinError()
00088     info = BY_HANDLE_FILE_INFORMATION()
00089     rv = GetFileInformationByHandle(fh, info)
00090     if rv == 0:
00091         raise WinError()
00092     return info
00093 
00094 def is_symlink_to(dest, src):
00095     if sys.platform == "win32":
00096         # Check if both are on the same volume and have the same file ID
00097         destinfo = _getfileinfo(dest)
00098         srcinfo = _getfileinfo(src)
00099         return (destinfo.dwVolumeSerialNumber == srcinfo.dwVolumeSerialNumber and
00100                 destinfo.nFileIndexHigh == srcinfo.nFileIndexHigh and
00101                 destinfo.nFileIndexLow == srcinfo.nFileIndexLow)
00102     else:
00103         # Read the link and check if it is correct
00104         if not os.path.islink(dest):
00105             return False
00106         target = os.path.abspath(os.readlink(dest))
00107         abssrc = os.path.abspath(src)
00108         return target == abssrc
00109 
00110 class _TreeDiff(dircmp):
00111     """Helper to report rich results on difference between two directories.
00112     """
00113     def _fillDiff(self, dc, rv, basepath="%s"):
00114         rv['right_only'] += map(lambda l: basepath % l, dc.right_only)
00115         rv['left_only'] += map(lambda l: basepath % l, dc.left_only)
00116         rv['diff_files'] += map(lambda l: basepath % l, dc.diff_files)
00117         rv['funny'] += map(lambda l: basepath % l, dc.common_funny)
00118         rv['funny'] += map(lambda l: basepath % l, dc.funny_files)
00119         for subdir, _dc in dc.subdirs.iteritems():
00120             self._fillDiff(_dc, rv, basepath % (subdir + "/%s"))
00121     def allResults(self, left, right):
00122         rv = {'right_only':[], 'left_only':[],
00123               'diff_files':[], 'funny': []}
00124         self._fillDiff(self, rv)
00125         chunks = []
00126         if rv['right_only']:
00127             chunks.append('%s only in %s' % (', '.join(rv['right_only']),
00128                                             right))
00129         if rv['left_only']:
00130             chunks.append('%s only in %s' % (', '.join(rv['left_only']),
00131                                             left))
00132         if rv['diff_files']:
00133             chunks.append('%s differ' % ', '.join(rv['diff_files']))
00134         if rv['funny']:
00135             chunks.append("%s don't compare" % ', '.join(rv['funny']))
00136         return '; '.join(chunks)
00137 
00138 class TestJarMaker(unittest.TestCase):
00139     """
00140     Unit tests for JarMaker.py
00141     """
00142     debug = True # set to True to debug failing tests on disk
00143     def setUp(self):
00144         self.tmpdir = mkdtemp()
00145         self.srcdir = os.path.join(self.tmpdir, 'src')
00146         os.mkdir(self.srcdir)
00147         self.builddir = os.path.join(self.tmpdir, 'build')
00148         os.mkdir(self.builddir)
00149         self.refdir = os.path.join(self.tmpdir, 'ref')
00150         os.mkdir(self.refdir)
00151         self.stagedir = os.path.join(self.tmpdir, 'stage')
00152         os.mkdir(self.stagedir)
00153 
00154     def tearDown(self):
00155         if self.debug:
00156             print self.tmpdir
00157         else:
00158             rmtree(self.tmpdir)
00159 
00160     def _jar_and_compare(self, *args, **kwargs):
00161         jm = JarMaker(outputFormat='jar')
00162         kwargs['jardir'] = os.path.join(self.builddir, 'chrome')
00163         if 'topsourcedir' not in kwargs:
00164             kwargs['topsourcedir'] = self.srcdir
00165         jm.makeJars(*args, **kwargs)
00166         cwd = os.getcwd()
00167         os.chdir(self.builddir)
00168         try:
00169             # expand build to stage
00170             for path, dirs, files in os.walk('.'):
00171                 stagedir = os.path.join(self.stagedir, path)
00172                 if not os.path.isdir(stagedir):
00173                     os.mkdir(stagedir)
00174                 for file in files:
00175                     if file.endswith('.jar'):
00176                         # expand jar
00177                         stagepath = os.path.join(stagedir, file)
00178                         os.mkdir(stagepath)
00179                         zf = ZipFile(os.path.join(path, file))
00180                         # extractall is only in 2.6, do this manually :-(
00181                         for entry_name in zf.namelist():
00182                             segs = entry_name.split('/')
00183                             fname = segs.pop()
00184                             dname = os.path.join(stagepath, *segs)
00185                             if not os.path.isdir(dname):
00186                                 os.makedirs(dname)
00187                             if not fname:
00188                                 # directory, we're done
00189                                 continue
00190                             _c = zf.read(entry_name)
00191                             open(os.path.join(dname, fname), 'wb').write(_c)
00192                         zf.close()
00193                     else:
00194                         copy2(os.path.join(path, file), stagedir)
00195             # compare both dirs
00196             os.chdir('..')
00197             td = _TreeDiff('ref', 'stage')
00198             return td.allResults('reference', 'build')
00199         finally:
00200             os.chdir(cwd)
00201 
00202     def _create_simple_setup(self):
00203         # create src content
00204         jarf = open(os.path.join(self.srcdir, 'jar.mn'), 'w')
00205         jarf.write('''test.jar:
00206  dir/foo (bar)
00207 ''')
00208         jarf.close()
00209         open(os.path.join(self.srcdir,'bar'),'w').write('content\n')
00210         # create reference
00211         refpath = os.path.join(self.refdir, 'chrome', 'test.jar', 'dir')
00212         os.makedirs(refpath)
00213         open(os.path.join(refpath, 'foo'), 'w').write('content\n')        
00214 
00215     def test_a_simple_jar(self):
00216         '''Test a simple jar.mn'''
00217         self._create_simple_setup()
00218         # call JarMaker
00219         rv = self._jar_and_compare((os.path.join(self.srcdir,'jar.mn'),),
00220                                    tuple(),
00221                                    sourcedirs = [self.srcdir])
00222         self.assertTrue(not rv, rv)
00223 
00224     def test_a_simple_symlink(self):
00225         '''Test a simple jar.mn with a symlink'''
00226         if not symlinks_supported(self.srcdir):
00227             return
00228 
00229         self._create_simple_setup()
00230         jm = JarMaker(outputFormat='symlink')
00231         kwargs = {
00232             'sourcedirs': [self.srcdir],
00233             'topsourcedir': self.srcdir,
00234             'jardir': os.path.join(self.builddir, 'chrome'),
00235         }
00236         jm.makeJars((os.path.join(self.srcdir,'jar.mn'),), tuple(), **kwargs)
00237         # All we do is check that srcdir/bar points to builddir/chrome/test/dir/foo
00238         srcbar = os.path.join(self.srcdir, 'bar')
00239         destfoo = os.path.join(self.builddir, 'chrome', 'test', 'dir', 'foo')
00240         self.assertTrue(is_symlink_to(destfoo, srcbar),
00241                         "%s is not a symlink to %s" % (destfoo, srcbar))
00242 
00243     def test_k_multi_relative_jar(self):
00244         '''Test the API for multiple l10n jars, with different relative paths'''
00245         # create app src content
00246         def _mangle(relpath):
00247             'method we use to map relpath to srcpaths'
00248             return os.path.join(self.srcdir, 'other-' + relpath)
00249         jars = []
00250         for relpath in ('foo', 'bar'):
00251             ldir = os.path.join(self.srcdir, relpath, 'locales')
00252             os.makedirs(ldir)
00253             jp = os.path.join(ldir, 'jar.mn')
00254             jars.append(jp)
00255             open(jp, 'w').write('''ab-CD.jar:
00256 % locale app ab-CD %app
00257   app/''' + relpath + ' (%' + relpath + ''')
00258 ''')
00259             ldir = _mangle(relpath)
00260             os.mkdir(ldir)
00261             open(os.path.join(ldir, relpath), 'w').write(relpath+" content\n")
00262         # create reference
00263         mf = open(os.path.join(self.refdir, 'chrome.manifest'), 'w')
00264         mf.write('manifest chrome/ab-CD.manifest\n')
00265         mf.close()
00266 
00267         chrome_ref = os.path.join(self.refdir, 'chrome')
00268         os.mkdir(chrome_ref)
00269         mf = open(os.path.join(chrome_ref, 'ab-CD.manifest'), 'wb')
00270         mf.write('locale app ab-CD jar:ab-CD.jar!/app\n')
00271         mf.close()
00272         ldir = os.path.join(chrome_ref, 'ab-CD.jar', 'app')
00273         os.makedirs(ldir)
00274         for relpath in ('foo', 'bar'):
00275             open(os.path.join(ldir, relpath), 'w').write(relpath+" content\n")
00276         # call JarMaker
00277         difference = self._jar_and_compare(jars,
00278                                            (_mangle,),
00279                                            sourcedirs = [])
00280         self.assertTrue(not difference, difference)
00281 
00282 if __name__ == '__main__':
00283     unittest.main(testRunner=MozTestRunner())