| 1 | #! /usr/bin/env python2.2
|
|---|
| 2 |
|
|---|
| 3 | __doc__ = """test case for samba.tdbpack functions
|
|---|
| 4 |
|
|---|
| 5 | tdbpack provides a means of pickling values into binary formats
|
|---|
| 6 | compatible with that used by the samba tdbpack()/tdbunpack()
|
|---|
| 7 | functions.
|
|---|
| 8 |
|
|---|
| 9 | Numbers are always stored in little-endian format; strings are stored
|
|---|
| 10 | in either DOS or Unix codepage as appropriate.
|
|---|
| 11 |
|
|---|
| 12 | The format for any particular element is encoded as a short ASCII
|
|---|
| 13 | string, with one character per field."""
|
|---|
| 14 |
|
|---|
| 15 | # Copyright (C) 2002 Hewlett-Packard.
|
|---|
| 16 |
|
|---|
| 17 | __author__ = 'Martin Pool <[email protected]>'
|
|---|
| 18 |
|
|---|
| 19 | import unittest
|
|---|
| 20 | import oldtdbutil
|
|---|
| 21 | import samba.tdbpack
|
|---|
| 22 |
|
|---|
| 23 | both_unpackers = (samba.tdbpack.unpack, oldtdbutil.unpack)
|
|---|
| 24 | both_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 |
|
|---|
| 57 | class 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 |
|
|---|
| 251 | if __name__ == '__main__':
|
|---|
| 252 | unittest.main()
|
|---|
| 253 |
|
|---|