1#! /usr/bin/env python2.2 2 3__doc__ = """test case for samba.tdbpack functions 4 5tdbpack provides a means of pickling values into binary formats 6compatible with that used by the samba tdbpack()/tdbunpack() 7functions. 8 9Numbers are always stored in little-endian format; strings are stored 10in either DOS or Unix codepage as appropriate. 11 12The format for any particular element is encoded as a short ASCII 13string, with one character per field.""" 14 15# Copyright (C) 2002 Hewlett-Packard. 16 17__author__ = 'Martin Pool <mbp@sourcefrog.net>' 18 19import unittest 20import oldtdbutil 21import samba.tdbpack 22 23both_unpackers = (samba.tdbpack.unpack, oldtdbutil.unpack) 24both_packers = (samba.tdbpack.pack, oldtdbutil.pack) 25 26 27 28# # ('B', [10, 'hello'], '\x0a\0\0\0hello'), 29# ('BB', [11, 'hello\0world', 3, 'now'], 30# '\x0b\0\0\0hello\0world\x03\0\0\0now'), 31# ('pd', [1, 10], '\x01\0\0\0\x0a\0\0\0'), 32# ('BBB', [5, 'hello', 0, '', 5, 'world'], 33# '\x05\0\0\0hello\0\0\0\0\x05\0\0\0world'), 34 35 # strings are sequences in Python, there's no getting away 36 # from it 37# ('ffff', 'evil', 'e\0v\0i\0l\0'), 38# ('BBBB', 'evil', 39# '\x01\0\0\0e' 40# '\x01\0\0\0v' 41# '\x01\0\0\0i' 42# '\x01\0\0\0l'), 43 44# ('', [], ''), 45 46# # exercise some long strings 47# ('PP', ['hello' * 255, 'world' * 255], 48# 'hello' * 255 + '\0' + 'world' * 255 + '\0'), 49# ('PP', ['hello' * 40000, 'world' * 50000], 50# 'hello' * 40000 + '\0' + 'world' * 50000 + '\0'), 51# ('B', [(5*51), 'hello' * 51], '\xff\0\0\0' + 'hello' * 51), 52# ('BB', [(5 * 40000), 'hello' * 40000, 53# (5 * 50000), 'world' * 50000], 54# '\x40\x0d\x03\0' + 'hello' * 40000 + '\x90\xd0\x03\x00' + 'world' * 50000), 55 56 57class PackTests(unittest.TestCase): 58 symm_cases = [ 59 ('w', [42], '\x2a\0'), 60 ('www', [42, 2, 69], '\x2a\0\x02\0\x45\0'), 61 ('wd', [42, 256], '\x2a\0\0\x01\0\0'), 62 ('w', [0], '\0\0'), 63 ('w', [255], '\xff\0'), 64 ('w', [256], '\0\x01'), 65 ('w', [0xdead], '\xad\xde'), 66 ('w', [0xffff], '\xff\xff'), 67 ('p', [0], '\0\0\0\0'), 68 ('p', [1], '\x01\0\0\0'), 69 ('d', [0x01020304], '\x04\x03\x02\x01'), 70 ('d', [0x7fffffff], '\xff\xff\xff\x7f'), 71 ('d', [0x80000000L], '\x00\x00\x00\x80'), 72 ('d', [0x80000069L], '\x69\x00\x00\x80'), 73 ('d', [0xffffffffL], '\xff\xff\xff\xff'), 74 ('d', [0xffffff00L], '\x00\xff\xff\xff'), 75 ('ddd', [1, 10, 50], '\x01\0\0\0\x0a\0\0\0\x32\0\0\0'), 76 ('ff', ['hello', 'world'], 'hello\0world\0'), 77 ('fP', ['hello', 'world'], 'hello\0world\0'), 78 ('PP', ['hello', 'world'], 'hello\0world\0'), 79 ('B', [0, ''], '\0\0\0\0'), 80# old implementation is wierd when string is not the right length 81# ('B', [2, 'hello'], '\x0a\0\0\0hello'), 82 ('B', [5, 'hello'], '\x05\0\0\0hello'), 83 ] 84 85 def test_symmetric(self): 86 """Cookbook of symmetric pack/unpack tests 87 """ 88 for packer in [samba.tdbpack.pack]: # both_packers: 89 for unpacker in both_unpackers: 90 for format, values, expected in self.symm_cases: 91 out_packed = packer(format, values) 92 self.assertEquals(out_packed, expected) 93 out, rest = unpacker(format, expected) 94 self.assertEquals(rest, '') 95 self.assertEquals(list(values), list(out)) 96 97 def test_large(self): 98 """Test large pack/unpack strings""" 99 large_cases = [('w' * 1000, xrange(1000)), ] 100 for packer in both_packers: 101 for unpacker in both_unpackers: 102 for format, values in large_cases: 103 packed = packer(format, values) 104 out, rest = unpacker(format, packed) 105 self.assertEquals(rest, '') 106 self.assertEquals(list(values), list(out)) 107 108 109 def test_pack(self): 110 """Cookbook of expected pack values 111 112 These can't be used for the symmetric test because the unpacked value is 113 not "canonical". 114 """ 115 cases = [('w', (42,), '\x2a\0'), 116 ] 117 118 for packer in both_packers: 119 for format, values, expected in cases: 120 self.assertEquals(packer(format, values), expected) 121 122 def test_unpack_extra(self): 123 # Test leftover data 124 for unpacker in both_unpackers: 125 for format, values, packed in self.symm_cases: 126 out, rest = unpacker(format, packed + 'hello sailor!') 127 self.assertEquals(rest, 'hello sailor!') 128 self.assertEquals(list(values), list(out)) 129 130 131 def test_pack_extra(self): 132 """Leftover values when packing""" 133 cases = [ 134 ('d', [10, 20], [10]), 135 ('d', [10, 'hello'], [10]), 136 ('ff', ['hello', 'world', 'sailor'], ['hello', 'world']), 137 ] 138 for unpacker in both_unpackers: 139 for packer in both_packers: 140 for format, values, chopped in cases: 141 bin = packer(format, values) 142 out, rest = unpacker(format, bin) 143 self.assertEquals(list(out), list(chopped)) 144 self.assertEquals(rest, '') 145 146 147 def test_unpack(self): 148 """Cookbook of tricky unpack tests""" 149 cases = [ 150 # Apparently I couldn't think of any tests that weren't 151 # symmetric :-/ 152 ] 153 for unpacker in both_unpackers: 154 for format, values, expected in cases: 155 out, rest = unpacker(format, expected) 156 self.assertEquals(rest, '') 157 self.assertEquals(list(values), list(out)) 158 159 160 def test_pack_failures(self): 161 """Expected errors for incorrect packing""" 162 cases = [('w', []), 163# ('w', ()), 164# ('w', {}), 165 ('ww', [2]), 166 ('w', 2), 167# ('w', None), 168 ('wwwwwwwwwwww', []), 169# ('w', [0x60A15EC5L]), 170# ('w', [None]), 171 ('d', []), 172 ('p', []), 173 ('f', [2]), 174 ('P', [None]), 175 ('P', ()), 176 ('f', [hex]), 177 ('fw', ['hello']), 178# ('f', [u'hello']), 179 ('B', [2]), 180 (None, [2, 3, 4]), 181 (ord('f'), [20]), 182 # old code doesn't distinguish string from seq-of-char 183# (['w', 'w'], [2, 2]), 184 # old code just ignores invalid characters 185# ('Q', [2]), 186# ('fQ', ['2', 3]), 187# ('fQ', ['2']), 188 (2, [2]), 189 # old code doesn't typecheck format 190# ({}, {}) 191 ] 192 for packer in both_packers: 193 for format, values in cases: 194 try: 195 packer(format, values) 196 except StandardError: 197 pass 198 else: 199 raise AssertionError("didn't get exception: format %s, values %s, packer %s" 200 % (`format`, `values`, `packer`)) 201 202 203 def test_unpack_failures(self): 204 """Expected errors for incorrect unpacking""" 205 cases = [ 206# This ought to be illegal, but the old code doesn't prohibit it 207# ('$', '', ValueError), 208# ('Q', '', ValueError), 209# ('Q$', '', ValueError), 210 ('f', '', IndexError), 211 ('d', '', IndexError), 212# This is an illegal packing, but the old code doesn't trap 213# ('d', '2', IndexError), 214# ('d', '22', IndexError), 215# ('d', '222', IndexError), 216# ('p', '\x01\0', IndexError), 217# ('w', '2', IndexError), 218# ('B', '\xff\0\0\0hello', IndexError), 219# ('B', '\xff\0', IndexError), 220 ('w', '', IndexError), 221 ('f', 'hello', IndexError), 222 ('f', '', IndexError), 223# ('B', '\x01\0\0\0', IndexError), 224# ('B', '\x05\0\0\0hell', IndexError), 225 ('B', '\xff\xff\xff\xff', ValueError), 226# ('B', 'foobar', IndexError), 227# ('BB', '\x01\0\0\0a\x01', IndexError), 228 ] 229 230 for unpacker in both_unpackers: 231 for format, values, throwable_class in cases: 232 try: 233 unpacker(format, values) 234 except StandardError: 235 pass 236 else: 237 raise AssertionError("didn't get exception: format %s, values %s, unpacker %s" 238 % (`format`, `values`, `unpacker`)) 239 240 def test_unpack_repeated(self): 241 cases = [(('df$', 242 '\x00\x00\x00\x00HP C LaserJet 4500-PS\x00Windows 4.0\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.HLP\x00\x00RAW\x00\\print$\\WIN40\\0\\readme.wri\x00\\print$\\WIN40\\0\\pscript.drv\x00\\print$\\WIN40\\0\\pscript.hlp\x00'), 243 ([0L, 'HP C LaserJet 4500-PS', 'Windows 4.0', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.HLP', '', 'RAW', '\\print$\\WIN40\\0\\readme.wri', '\\print$\\WIN40\\0\\pscript.drv', '\\print$\\WIN40\\0\\pscript.hlp'], ''))] 244 for unpacker in both_unpackers: 245 for input, expected in cases: 246 result = apply(unpacker, input) 247 if result != expected: 248 raise AssertionError("%s:\n input: %s\n output: %s\n expected: %s" % (`unpacker`, `input`, `result`, `expected`)) 249 250 251if __name__ == '__main__': 252 unittest.main() 253 254