Back to index

enigmail  1.4.3
unitMozZipFile.py
Go to the documentation of this file.
00001 # ***** BEGIN LICENSE BLOCK *****
00002 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003 #
00004 # The contents of this file are subject to the Mozilla Public License Version
00005 # 1.1 (the "License"); you may not use this file except in compliance with
00006 # the License. You may obtain a copy of the License at
00007 # http://www.mozilla.org/MPL/
00008 #
00009 # Software distributed under the License is distributed on an "AS IS" basis,
00010 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011 # for the specific language governing rights and limitations under the
00012 # License.
00013 #
00014 # The Original Code is Mozilla build system.
00015 #
00016 # The Initial Developer of the Original Code is
00017 # Mozilla Foundation.
00018 # Portions created by the Initial Developer are Copyright (C) 2007
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #  Axel Hecht <axel@pike.org>
00023 #
00024 # Alternatively, the contents of this file may be used under the terms of
00025 # either the GNU General Public License Version 2 or later (the "GPL"), or
00026 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027 # in which case the provisions of the GPL or the LGPL are applicable instead
00028 # of those above. If you wish to allow use of your version of this file only
00029 # under the terms of either the GPL or the LGPL, and not to allow others to
00030 # use your version of this file under the terms of the MPL, indicate your
00031 # decision by deleting the provisions above and replace them with the notice
00032 # and other provisions required by the GPL or the LGPL. If you do not delete
00033 # the provisions above, a recipient may use your version of this file under
00034 # the terms of any one of the MPL, the GPL or the LGPL.
00035 #
00036 # ***** END LICENSE BLOCK *****
00037 
00038 import unittest
00039 
00040 import shutil
00041 import os
00042 import re
00043 import sys
00044 import random
00045 import copy
00046 from string import letters
00047 
00048 '''
00049 Test case infrastructure for MozZipFile.
00050 
00051 This isn't really a unit test, but a test case generator and runner.
00052 For a given set of files, lengths, and number of writes, we create 
00053 a testcase for every combination of the three. There are some
00054 symmetries used to reduce the number of test cases, the first file
00055 written is always the first file, the second is either the first or
00056 the second, the third is one of the first three. That is, if we
00057 had 4 files, but only three writes, the fourth file would never even
00058 get tried.
00059 
00060 The content written to the jars is pseudorandom with a fixed seed.
00061 '''
00062 
00063 if not __file__:
00064   __file__ = sys.argv[0]
00065 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
00066 
00067 from MozZipFile import ZipFile
00068 import zipfile
00069 
00070 leafs = (
00071   'firstdir/oneleaf',
00072   'seconddir/twoleaf',
00073   'thirddir/with/sub/threeleaf')
00074 _lengths = map(lambda n: n * 64, [16, 64, 80])
00075 lengths = 3
00076 writes = 5
00077 
00078 def givenlength(i):
00079   '''Return a length given in the _lengths array to allow manual
00080   tuning of which lengths of zip entries to use.
00081   '''
00082   return _lengths[i]
00083 
00084 
00085 def prod(*iterables):
00086   ''''Tensor product of a list of iterables.
00087 
00088   This generator returns lists of items, one of each given
00089   iterable. It iterates over all possible combinations.
00090   '''
00091   for item in iterables[0]:
00092     if len(iterables) == 1:
00093       yield [item]
00094     else:
00095       for others in prod(*iterables[1:]):
00096         yield [item] + others
00097 
00098 
00099 def getid(descs):
00100   'Convert a list of ints to a string.'
00101   return reduce(lambda x,y: x+'%d%d'%tuple(y), descs,'')
00102 
00103 
00104 def getContent(length):
00105   'Get pseudo random content of given length.'
00106   rv = [None] * length
00107   for i in xrange(length):
00108     rv[i] = random.choice(letters)
00109   return ''.join(rv)
00110 
00111 
00112 def createWriter(sizer, *items):
00113   'Helper method to fill in tests, one set of writes, one for each item'
00114   locitems = copy.deepcopy(items)
00115   for item in locitems:
00116     item['length'] = sizer(item.pop('length', 0))
00117   def helper(self):
00118     mode  = 'w'
00119     if os.path.isfile(self.f):
00120       mode = 'a'
00121     zf = ZipFile(self.f, mode, self.compression)
00122     for item in locitems:
00123       self._write(zf, **item)
00124     zf = None
00125     pass
00126   return helper
00127 
00128 def createTester(name, *writes):
00129   '''Helper method to fill in tests, calls into a list of write
00130   helper methods.
00131   '''
00132   _writes = copy.copy(writes)
00133   def tester(self):
00134     for w in _writes:
00135       getattr(self, w)()
00136     self._verifyZip()
00137     pass
00138   # unit tests get confused if the method name isn't test...
00139   tester.__name__ = name
00140   return tester
00141 
00142 class TestExtensiveStored(unittest.TestCase):
00143   '''Unit tests for MozZipFile
00144 
00145   The testcase are actually populated by code following the class
00146   definition.
00147   '''
00148   
00149   stage = "mozzipfilestage"
00150   compression = zipfile.ZIP_STORED
00151 
00152   def leaf(self, *leafs):
00153     return os.path.join(self.stage, *leafs)
00154   def setUp(self):
00155     if os.path.exists(self.stage):
00156       shutil.rmtree(self.stage)
00157     os.mkdir(self.stage)
00158     self.f = self.leaf('test.jar')
00159     self.ref = {}
00160     self.seed = 0
00161   
00162   def tearDown(self):
00163     self.f = None
00164     self.ref = None
00165   
00166   def _verifyZip(self):
00167     zf = zipfile.ZipFile(self.f)
00168     badEntry = zf.testzip()
00169     self.failIf(badEntry, badEntry)
00170     zlist = zf.namelist()
00171     zlist.sort()
00172     vlist = self.ref.keys()
00173     vlist.sort()
00174     self.assertEqual(zlist, vlist)
00175     for leaf, content in self.ref.iteritems():
00176       zcontent = zf.read(leaf)
00177       self.assertEqual(content, zcontent)
00178   
00179   def _write(self, zf, seed=None, leaf=0, length=0):
00180     if seed is None:
00181       seed = self.seed
00182       self.seed += 1
00183     random.seed(seed)
00184     leaf = leafs[leaf]
00185     content = getContent(length)
00186     self.ref[leaf] = content
00187     zf.writestr(leaf, content)
00188     dir = os.path.dirname(self.leaf('stage', leaf))
00189     if not os.path.isdir(dir):
00190       os.makedirs(dir)
00191     open(self.leaf('stage', leaf), 'w').write(content)
00192 
00193 # all leafs in all lengths
00194 atomics = list(prod(xrange(len(leafs)), xrange(lengths)))
00195 
00196 # populate TestExtensiveStore with testcases
00197 for w in xrange(writes):
00198   # Don't iterate over all files for the the first n passes,
00199   # those are redundant as long as w < lengths.
00200   # There are symmetries in the trailing end, too, but I don't know
00201   # how to reduce those out right now.
00202   nonatomics = [list(prod(range(min(i,len(leafs))), xrange(lengths)))
00203                 for i in xrange(1, w+1)] + [atomics]
00204   for descs in prod(*nonatomics):
00205     suffix = getid(descs)
00206     dicts = [dict(leaf=leaf, length=length) for leaf, length in descs]
00207     setattr(TestExtensiveStored, '_write' + suffix,
00208             createWriter(givenlength, *dicts))
00209     setattr(TestExtensiveStored, 'test' + suffix,
00210             createTester('test' + suffix, '_write' + suffix))
00211 
00212 # now create another round of tests, with two writing passes
00213 # first, write all file combinations into the jar, close it,
00214 # and then write all atomics again.
00215 # This should catch more or less all artifacts generated
00216 # by the final ordering step when closing the jar.
00217 files = [list(prod([i], xrange(lengths))) for i in xrange(len(leafs))]
00218 allfiles = reduce(lambda l,r:l+r,
00219                   [list(prod(*files[:(i+1)])) for i in xrange(len(leafs))])
00220 
00221 for first in allfiles:
00222   testbasename = 'test%s_' % getid(first)
00223   test = [None, '_write' + getid(first), None]
00224   for second in atomics:
00225     test[0] = testbasename + getid([second])
00226     test[2] = '_write' + getid([second])
00227     setattr(TestExtensiveStored, test[0], createTester(*test))
00228 
00229 class TestExtensiveDeflated(TestExtensiveStored):
00230   'Test all that has been tested with ZIP_STORED with DEFLATED, too.'
00231   compression = zipfile.ZIP_DEFLATED
00232 
00233 if __name__ == '__main__':
00234   unittest.main()