config/tests/unitMozZipFile.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 import unittest
michael@0 6
michael@0 7 import shutil
michael@0 8 import os
michael@0 9 import re
michael@0 10 import sys
michael@0 11 import random
michael@0 12 import copy
michael@0 13 from string import letters
michael@0 14
michael@0 15 '''
michael@0 16 Test case infrastructure for MozZipFile.
michael@0 17
michael@0 18 This isn't really a unit test, but a test case generator and runner.
michael@0 19 For a given set of files, lengths, and number of writes, we create
michael@0 20 a testcase for every combination of the three. There are some
michael@0 21 symmetries used to reduce the number of test cases, the first file
michael@0 22 written is always the first file, the second is either the first or
michael@0 23 the second, the third is one of the first three. That is, if we
michael@0 24 had 4 files, but only three writes, the fourth file would never even
michael@0 25 get tried.
michael@0 26
michael@0 27 The content written to the jars is pseudorandom with a fixed seed.
michael@0 28 '''
michael@0 29
michael@0 30 if not __file__:
michael@0 31 __file__ = sys.argv[0]
michael@0 32 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
michael@0 33
michael@0 34 from MozZipFile import ZipFile
michael@0 35 import zipfile
michael@0 36
michael@0 37 leafs = (
michael@0 38 'firstdir/oneleaf',
michael@0 39 'seconddir/twoleaf',
michael@0 40 'thirddir/with/sub/threeleaf')
michael@0 41 _lengths = map(lambda n: n * 64, [16, 64, 80])
michael@0 42 lengths = 3
michael@0 43 writes = 5
michael@0 44
michael@0 45 def givenlength(i):
michael@0 46 '''Return a length given in the _lengths array to allow manual
michael@0 47 tuning of which lengths of zip entries to use.
michael@0 48 '''
michael@0 49 return _lengths[i]
michael@0 50
michael@0 51
michael@0 52 def prod(*iterables):
michael@0 53 ''''Tensor product of a list of iterables.
michael@0 54
michael@0 55 This generator returns lists of items, one of each given
michael@0 56 iterable. It iterates over all possible combinations.
michael@0 57 '''
michael@0 58 for item in iterables[0]:
michael@0 59 if len(iterables) == 1:
michael@0 60 yield [item]
michael@0 61 else:
michael@0 62 for others in prod(*iterables[1:]):
michael@0 63 yield [item] + others
michael@0 64
michael@0 65
michael@0 66 def getid(descs):
michael@0 67 'Convert a list of ints to a string.'
michael@0 68 return reduce(lambda x,y: x+'{0}{1}'.format(*tuple(y)), descs,'')
michael@0 69
michael@0 70
michael@0 71 def getContent(length):
michael@0 72 'Get pseudo random content of given length.'
michael@0 73 rv = [None] * length
michael@0 74 for i in xrange(length):
michael@0 75 rv[i] = random.choice(letters)
michael@0 76 return ''.join(rv)
michael@0 77
michael@0 78
michael@0 79 def createWriter(sizer, *items):
michael@0 80 'Helper method to fill in tests, one set of writes, one for each item'
michael@0 81 locitems = copy.deepcopy(items)
michael@0 82 for item in locitems:
michael@0 83 item['length'] = sizer(item.pop('length', 0))
michael@0 84 def helper(self):
michael@0 85 mode = 'w'
michael@0 86 if os.path.isfile(self.f):
michael@0 87 mode = 'a'
michael@0 88 zf = ZipFile(self.f, mode, self.compression)
michael@0 89 for item in locitems:
michael@0 90 self._write(zf, **item)
michael@0 91 zf = None
michael@0 92 pass
michael@0 93 return helper
michael@0 94
michael@0 95 def createTester(name, *writes):
michael@0 96 '''Helper method to fill in tests, calls into a list of write
michael@0 97 helper methods.
michael@0 98 '''
michael@0 99 _writes = copy.copy(writes)
michael@0 100 def tester(self):
michael@0 101 for w in _writes:
michael@0 102 getattr(self, w)()
michael@0 103 self._verifyZip()
michael@0 104 pass
michael@0 105 # unit tests get confused if the method name isn't test...
michael@0 106 tester.__name__ = name
michael@0 107 return tester
michael@0 108
michael@0 109 class TestExtensiveStored(unittest.TestCase):
michael@0 110 '''Unit tests for MozZipFile
michael@0 111
michael@0 112 The testcase are actually populated by code following the class
michael@0 113 definition.
michael@0 114 '''
michael@0 115
michael@0 116 stage = "mozzipfilestage"
michael@0 117 compression = zipfile.ZIP_STORED
michael@0 118
michael@0 119 def leaf(self, *leafs):
michael@0 120 return os.path.join(self.stage, *leafs)
michael@0 121 def setUp(self):
michael@0 122 if os.path.exists(self.stage):
michael@0 123 shutil.rmtree(self.stage)
michael@0 124 os.mkdir(self.stage)
michael@0 125 self.f = self.leaf('test.jar')
michael@0 126 self.ref = {}
michael@0 127 self.seed = 0
michael@0 128
michael@0 129 def tearDown(self):
michael@0 130 self.f = None
michael@0 131 self.ref = None
michael@0 132
michael@0 133 def _verifyZip(self):
michael@0 134 zf = zipfile.ZipFile(self.f)
michael@0 135 badEntry = zf.testzip()
michael@0 136 self.failIf(badEntry, badEntry)
michael@0 137 zlist = zf.namelist()
michael@0 138 zlist.sort()
michael@0 139 vlist = self.ref.keys()
michael@0 140 vlist.sort()
michael@0 141 self.assertEqual(zlist, vlist)
michael@0 142 for leaf, content in self.ref.iteritems():
michael@0 143 zcontent = zf.read(leaf)
michael@0 144 self.assertEqual(content, zcontent)
michael@0 145
michael@0 146 def _write(self, zf, seed=None, leaf=0, length=0):
michael@0 147 if seed is None:
michael@0 148 seed = self.seed
michael@0 149 self.seed += 1
michael@0 150 random.seed(seed)
michael@0 151 leaf = leafs[leaf]
michael@0 152 content = getContent(length)
michael@0 153 self.ref[leaf] = content
michael@0 154 zf.writestr(leaf, content)
michael@0 155 dir = os.path.dirname(self.leaf('stage', leaf))
michael@0 156 if not os.path.isdir(dir):
michael@0 157 os.makedirs(dir)
michael@0 158 open(self.leaf('stage', leaf), 'w').write(content)
michael@0 159
michael@0 160 # all leafs in all lengths
michael@0 161 atomics = list(prod(xrange(len(leafs)), xrange(lengths)))
michael@0 162
michael@0 163 # populate TestExtensiveStore with testcases
michael@0 164 for w in xrange(writes):
michael@0 165 # Don't iterate over all files for the the first n passes,
michael@0 166 # those are redundant as long as w < lengths.
michael@0 167 # There are symmetries in the trailing end, too, but I don't know
michael@0 168 # how to reduce those out right now.
michael@0 169 nonatomics = [list(prod(range(min(i,len(leafs))), xrange(lengths)))
michael@0 170 for i in xrange(1, w+1)] + [atomics]
michael@0 171 for descs in prod(*nonatomics):
michael@0 172 suffix = getid(descs)
michael@0 173 dicts = [dict(leaf=leaf, length=length) for leaf, length in descs]
michael@0 174 setattr(TestExtensiveStored, '_write' + suffix,
michael@0 175 createWriter(givenlength, *dicts))
michael@0 176 setattr(TestExtensiveStored, 'test' + suffix,
michael@0 177 createTester('test' + suffix, '_write' + suffix))
michael@0 178
michael@0 179 # now create another round of tests, with two writing passes
michael@0 180 # first, write all file combinations into the jar, close it,
michael@0 181 # and then write all atomics again.
michael@0 182 # This should catch more or less all artifacts generated
michael@0 183 # by the final ordering step when closing the jar.
michael@0 184 files = [list(prod([i], xrange(lengths))) for i in xrange(len(leafs))]
michael@0 185 allfiles = reduce(lambda l,r:l+r,
michael@0 186 [list(prod(*files[:(i+1)])) for i in xrange(len(leafs))])
michael@0 187
michael@0 188 for first in allfiles:
michael@0 189 testbasename = 'test{0}_'.format(getid(first))
michael@0 190 test = [None, '_write' + getid(first), None]
michael@0 191 for second in atomics:
michael@0 192 test[0] = testbasename + getid([second])
michael@0 193 test[2] = '_write' + getid([second])
michael@0 194 setattr(TestExtensiveStored, test[0], createTester(*test))
michael@0 195
michael@0 196 class TestExtensiveDeflated(TestExtensiveStored):
michael@0 197 'Test all that has been tested with ZIP_STORED with DEFLATED, too.'
michael@0 198 compression = zipfile.ZIP_DEFLATED
michael@0 199
michael@0 200 if __name__ == '__main__':
michael@0 201 unittest.main()

mercurial