af2ba2b296dadf5ae9d3036c7d26caf650900107
[samba.git] / source / python / examples / tdbpack / test_tdbpack.py
1 #! /usr/bin/env python
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 <mbp@sourcefrog.net>'
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