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