source: trunk/tests/test_classic_functions.py @ 798

Last change on this file since 798 was 793, checked in by cito, 4 years ago

Improve quoting and typecasting in the pg module

Larger refactoring of the code for adapting and typecasting in the pg module.
Things are now a lot cleaner and clearer.

The _Adapt class is responsible for all adapting of Python objects to their
PostgreSQL equivalents when sending data to the database. The typecasting
from PostgreSQL on output happens in the C module, except for the typecasting
of records which is new and provided by the _CastRecord class.

The classic module also did not work properly when regular type names were
switched on with use_regtypes(True), since the adapting of types relied on
the PyGreSQL type names. This has been solved by adding a new _PgType class
that is essentially the old type name, but augmented with all the necessary
information necessary to adapt types, particularly record types.

All tests in test_classic_dbwrapper now run twice, using opposite settings
for the various configuration settings like use_bool() or use_regtypes(),
in order to make sure that no internal functions rely on default settings.

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 28.8 KB
Line 
1#! /usr/bin/python
2# -*- coding: utf-8 -*-
3
4"""Test the classic PyGreSQL interface.
5
6Sub-tests for the module functions and constants.
7
8Contributed by Christoph Zwerschke.
9
10These tests do not need a database to test against.
11"""
12
13try:
14    import unittest2 as unittest  # for Python < 2.7
15except ImportError:
16    import unittest
17
18import json
19import re
20
21import pg  # the module under test
22
23try:
24    long
25except NameError:  # Python >= 3.0
26    long = int
27
28try:
29    unicode
30except NameError:  # Python >= 3.0
31    unicode = str
32
33
34class TestHasConnect(unittest.TestCase):
35    """Test existence of basic pg module functions."""
36
37    def testhasPgError(self):
38        self.assertTrue(issubclass(pg.Error, Exception))
39
40    def testhasPgWarning(self):
41        self.assertTrue(issubclass(pg.Warning, Exception))
42
43    def testhasPgInterfaceError(self):
44        self.assertTrue(issubclass(pg.InterfaceError, pg.Error))
45
46    def testhasPgDatabaseError(self):
47        self.assertTrue(issubclass(pg.DatabaseError, pg.Error))
48
49    def testhasPgInternalError(self):
50        self.assertTrue(issubclass(pg.InternalError, pg.DatabaseError))
51
52    def testhasPgOperationalError(self):
53        self.assertTrue(issubclass(pg.OperationalError, pg.DatabaseError))
54
55    def testhasPgProgrammingError(self):
56        self.assertTrue(issubclass(pg.ProgrammingError, pg.DatabaseError))
57
58    def testhasPgIntegrityError(self):
59        self.assertTrue(issubclass(pg.IntegrityError, pg.DatabaseError))
60
61    def testhasPgDataError(self):
62        self.assertTrue(issubclass(pg.DataError, pg.DatabaseError))
63
64    def testhasPgNotSupportedError(self):
65        self.assertTrue(issubclass(pg.NotSupportedError, pg.DatabaseError))
66
67    def testhasConnect(self):
68        self.assertTrue(callable(pg.connect))
69
70    def testhasEscapeString(self):
71        self.assertTrue(callable(pg.escape_string))
72
73    def testhasEscapeBytea(self):
74        self.assertTrue(callable(pg.escape_bytea))
75
76    def testhasUnescapeBytea(self):
77        self.assertTrue(callable(pg.unescape_bytea))
78
79    def testDefHost(self):
80        d0 = pg.get_defhost()
81        d1 = 'pgtesthost'
82        pg.set_defhost(d1)
83        self.assertEqual(pg.get_defhost(), d1)
84        pg.set_defhost(d0)
85        self.assertEqual(pg.get_defhost(), d0)
86
87    def testDefPort(self):
88        d0 = pg.get_defport()
89        d1 = 1234
90        pg.set_defport(d1)
91        self.assertEqual(pg.get_defport(), d1)
92        if d0 is None:
93            d0 = -1
94        pg.set_defport(d0)
95        if d0 == -1:
96            d0 = None
97        self.assertEqual(pg.get_defport(), d0)
98
99    def testDefOpt(self):
100        d0 = pg.get_defopt()
101        d1 = '-h pgtesthost -p 1234'
102        pg.set_defopt(d1)
103        self.assertEqual(pg.get_defopt(), d1)
104        pg.set_defopt(d0)
105        self.assertEqual(pg.get_defopt(), d0)
106
107    def testDefBase(self):
108        d0 = pg.get_defbase()
109        d1 = 'pgtestdb'
110        pg.set_defbase(d1)
111        self.assertEqual(pg.get_defbase(), d1)
112        pg.set_defbase(d0)
113        self.assertEqual(pg.get_defbase(), d0)
114
115
116class TestParseArray(unittest.TestCase):
117    """Test the array parser."""
118
119    test_strings = [
120        ('', str, ValueError),
121        ('{}', None, []),
122        ('{}', str, []),
123        ('   {   }   ', None, []),
124        ('{', str, ValueError),
125        ('{{}', str, ValueError),
126        ('{}{', str, ValueError),
127        ('[]', str, ValueError),
128        ('()', str, ValueError),
129        ('{[]}', str, ['[]']),
130        ('{hello}', int, ValueError),
131        ('{42}', int, [42]),
132        ('{ 42 }', int, [42]),
133        ('{42', int, ValueError),
134        ('{ 42 ', int, ValueError),
135        ('{hello}', str, ['hello']),
136        ('{ hello }', str, ['hello']),
137        ('{hi}   ', str, ['hi']),
138        ('{hi}   ?', str, ValueError),
139        ('{null}', str, [None]),
140        (' { NULL } ', str, [None]),
141        ('   {   NULL   }   ', str, [None]),
142        (' { not null } ', str, ['not null']),
143        (' { not NULL } ', str, ['not NULL']),
144        (' {"null"} ', str, ['null']),
145        (' {"NULL"} ', str, ['NULL']),
146        ('{Hi!}', str, ['Hi!']),
147        ('{"Hi!"}', str, ['Hi!']),
148        ('{" Hi! "}', str, [' Hi! ']),
149        ('{a"}', str, ValueError),
150        ('{"b}', str, ValueError),
151        ('{a"b}', str, ValueError),
152        (r'{a\"b}', str, ['a"b']),
153        (r'{a\,b}', str, ['a,b']),
154        (r'{a\bc}', str, ['abc']),
155        (r'{"a\bc"}', str, ['abc']),
156        (r'{\a\b\c}', str, ['abc']),
157        (r'{"\a\b\c"}', str, ['abc']),
158        (r'{"a"b"}', str, ValueError),
159        (r'{"a""b"}', str, ValueError),
160        (r'{"a\"b"}', str, ['a"b']),
161        ('{"{}"}', str, ['{}']),
162        (r'{\{\}}', str, ['{}']),
163        ('{"{a,b,c}"}', str, ['{a,b,c}']),
164        ("{'abc'}", str, ["'abc'"]),
165        ('{"abc"}', str, ['abc']),
166        (r'{\"abc\"}', str, ['"abc"']),
167        (r"{\'abc\'}", str, ["'abc'"]),
168        (r"{abc,d,efg}", str, ['abc', 'd', 'efg']),
169        ('{Hello World!}', str, ['Hello World!']),
170        ('{Hello, World!}', str, ['Hello', 'World!']),
171        ('{Hello,\ World!}', str, ['Hello', ' World!']),
172        ('{Hello\, World!}', str, ['Hello, World!']),
173        ('{"Hello World!"}', str, ['Hello World!']),
174        ('{this, should, be, null}', str, ['this', 'should', 'be', None]),
175        ('{This, should, be, NULL}', str, ['This', 'should', 'be', None]),
176        ('{3, 2, 1, null}', int, [3, 2, 1, None]),
177        ('{3, 2, 1, NULL}', int, [3, 2, 1, None]),
178        ('{3,17,51}', int, [3, 17, 51]),
179        (' { 3 , 17 , 51 } ', int, [3, 17, 51]),
180        ('{3,17,51}', str, ['3', '17', '51']),
181        (' { 3 , 17 , 51 } ', str, ['3', '17', '51']),
182        ('{1,"2",abc,"def"}', str, ['1', '2', 'abc', 'def']),
183        ('{{}}', int, [[]]),
184        ('{{},{}}', int, [[], []]),
185        ('{ {} , {} , {} }', int, [[], [], []]),
186        ('{ {} , {} , {} , }', int, ValueError),
187        ('{{{1,2,3},{4,5,6}}}', int, [[[1, 2, 3], [4, 5, 6]]]),
188        ('{{1,2,3},{4,5,6},{7,8,9}}', int, [[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
189        ('{20000, 25000, 25000, 25000}', int, [20000, 25000, 25000, 25000]),
190        ('{{{17,18,19},{14,15,16},{11,12,13}},'
191         '{{27,28,29},{24,25,26},{21,22,23}},'
192         '{{37,38,39},{34,35,36},{31,32,33}}}', int,
193            [[[17, 18, 19], [14, 15, 16], [11, 12, 13]],
194             [[27, 28, 29], [24, 25, 26], [21, 22, 23]],
195             [[37, 38, 39], [34, 35, 36], [31, 32, 33]]]),
196        ('{{"breakfast", "consulting"}, {"meeting", "lunch"}}', str,
197            [['breakfast', 'consulting'], ['meeting', 'lunch']]),
198        ('[1:3]={1,2,3}', int, [1, 2, 3]),
199        ('[-1:1]={1,2,3}', int, [1, 2, 3]),
200        ('[-1:+1]={1,2,3}', int, [1, 2, 3]),
201        ('[-3:-1]={1,2,3}', int, [1, 2, 3]),
202        ('[+1:+3]={1,2,3}', int, [1, 2, 3]),
203        ('[]={1,2,3}', int, ValueError),
204        ('[1:]={1,2,3}', int, ValueError),
205        ('[:3]={1,2,3}', int, ValueError),
206        ('[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}',
207            int, [[[1, 2, 3], [4, 5, 6]]]),
208        ('  [1:1]  [-2:-1]  [3:5]  =  { { { 1 , 2 , 3 }, {4 , 5 , 6 } } }',
209            int, [[[1, 2, 3], [4, 5, 6]]]),
210        ('[1:1][3:5]={{1,2,3},{4,5,6}}', int, [[1, 2, 3], [4, 5, 6]]),
211        ('[3:5]={{1,2,3},{4,5,6}}', int, ValueError),
212        ('[1:1][-2:-1][3:5]={{1,2,3},{4,5,6}}', int, ValueError)]
213
214    def testParserParams(self):
215        f = pg.cast_array
216        self.assertRaises(TypeError, f)
217        self.assertRaises(TypeError, f, None)
218        self.assertRaises(TypeError, f, '{}', 1)
219        self.assertRaises(TypeError, f, '{}', b',',)
220        self.assertRaises(TypeError, f, '{}', None, None)
221        self.assertRaises(TypeError, f, '{}', None, 1)
222        self.assertRaises(TypeError, f, '{}', None, b'')
223        self.assertRaises(ValueError, f, '{}', None, b'\\')
224        self.assertRaises(ValueError, f, '{}', None, b'{')
225        self.assertRaises(ValueError, f, '{}', None, b'}')
226        self.assertRaises(TypeError, f, '{}', None, b',;')
227        self.assertEqual(f('{}'), [])
228        self.assertEqual(f('{}', None), [])
229        self.assertEqual(f('{}', None, b';'), [])
230        self.assertEqual(f('{}', str), [])
231        self.assertEqual(f('{}', str, b';'), [])
232
233    def testParserSimple(self):
234        r = pg.cast_array('{a,b,c}')
235        self.assertIsInstance(r, list)
236        self.assertEqual(len(r), 3)
237        self.assertEqual(r, ['a', 'b', 'c'])
238
239    def testParserNested(self):
240        f = pg.cast_array
241        r = f('{{a,b,c}}')
242        self.assertIsInstance(r, list)
243        self.assertEqual(len(r), 1)
244        r = r[0]
245        self.assertIsInstance(r, list)
246        self.assertEqual(len(r), 3)
247        self.assertEqual(r, ['a', 'b', 'c'])
248        self.assertRaises(ValueError, f, '{a,{b,c}}')
249        r = f('{{a,b},{c,d}}')
250        self.assertIsInstance(r, list)
251        self.assertEqual(len(r), 2)
252        r = r[1]
253        self.assertIsInstance(r, list)
254        self.assertEqual(len(r), 2)
255        self.assertEqual(r, ['c', 'd'])
256        r = f('{{a},{b},{c}}')
257        self.assertIsInstance(r, list)
258        self.assertEqual(len(r), 3)
259        r = r[1]
260        self.assertIsInstance(r, list)
261        self.assertEqual(len(r), 1)
262        self.assertEqual(r[0], 'b')
263        r = f('{{{{{{{abc}}}}}}}')
264        for i in range(7):
265            self.assertIsInstance(r, list)
266            self.assertEqual(len(r), 1)
267            r = r[0]
268        self.assertEqual(r, 'abc')
269
270    def testParserTooDeeplyNested(self):
271        f = pg.cast_array
272        for n in 3, 5, 9, 12, 16, 32, 64, 256:
273            r = '%sa,b,c%s' % ('{' * n, '}' * n)
274            if n > 16:  # hard coded maximum depth
275                self.assertRaises(ValueError, f, r)
276            else:
277                r = f(r)
278                for i in range(n - 1):
279                    self.assertIsInstance(r, list)
280                    self.assertEqual(len(r), 1)
281                    r = r[0]
282                self.assertEqual(len(r), 3)
283                self.assertEqual(r, ['a', 'b', 'c'])
284
285    def testParserCast(self):
286        f = pg.cast_array
287        self.assertEqual(f('{1}'), ['1'])
288        self.assertEqual(f('{1}', None), ['1'])
289        self.assertEqual(f('{1}', int), [1])
290        self.assertEqual(f('{1}', str), ['1'])
291        self.assertEqual(f('{a}'), ['a'])
292        self.assertEqual(f('{a}', None), ['a'])
293        self.assertRaises(ValueError, f, '{a}', int)
294        self.assertEqual(f('{a}', str), ['a'])
295        cast = lambda s: '%s is ok' % s
296        self.assertEqual(f('{a}', cast), ['a is ok'])
297
298    def testParserDelim(self):
299        f = pg.cast_array
300        self.assertEqual(f('{1,2}'), ['1', '2'])
301        self.assertEqual(f('{1,2}', delim=b','), ['1', '2'])
302        self.assertEqual(f('{1;2}'), ['1;2'])
303        self.assertEqual(f('{1;2}', delim=b';'), ['1', '2'])
304        self.assertEqual(f('{1,2}', delim=b';'), ['1,2'])
305
306    def testParserWithData(self):
307        f = pg.cast_array
308        for string, cast, expected in self.test_strings:
309            if expected is ValueError:
310                self.assertRaises(ValueError, f, string, cast)
311            else:
312                self.assertEqual(f(string, cast), expected)
313
314    def testParserWithoutCast(self):
315        f = pg.cast_array
316
317        for string, cast, expected in self.test_strings:
318            if cast is not str:
319                continue
320            if expected is ValueError:
321                self.assertRaises(ValueError, f, string)
322            else:
323                self.assertEqual(f(string), expected)
324
325    def testParserWithDifferentDelimiter(self):
326        f = pg.cast_array
327
328        def replace_comma(value):
329            if isinstance(value, str):
330                return value.replace(',', ';')
331            elif isinstance(value, list):
332                return [replace_comma(v) for v in value]
333            else:
334                return value
335
336        for string, cast, expected in self.test_strings:
337            string = replace_comma(string)
338            if expected is ValueError:
339                self.assertRaises(ValueError, f, string, cast)
340            else:
341                expected = replace_comma(expected)
342                self.assertEqual(f(string, cast, b';'), expected)
343
344
345class TestParseRecord(unittest.TestCase):
346    """Test the record parser."""
347
348    test_strings = [
349        ('', None, ValueError),
350        ('', str, ValueError),
351        ('(', None, ValueError),
352        ('(', str, ValueError),
353        ('()', None, (None,)),
354        ('()', str, (None,)),
355        ('()', int, (None,)),
356        ('(,)', str, (None, None)),
357        ('( , )', str, (' ', ' ')),
358        ('(")', None, ValueError),
359        ('("")', None, ('',)),
360        ('("")', str, ('',)),
361        ('("")', int, ValueError),
362        ('("" )', None, (' ',)),
363        ('("" )', str, (' ',)),
364        ('("" )', int, ValueError),
365        ('    ()    ', None, (None,)),
366        ('   (   )   ', None, ('   ',)),
367        ('(', str, ValueError),
368        ('(()', str, ('(',)),
369        ('(())', str, ValueError),
370        ('()(', str, ValueError),
371        ('()()', str, ValueError),
372        ('[]', str, ValueError),
373        ('{}', str, ValueError),
374        ('([])', str, ('[]',)),
375        ('(hello)', int, ValueError),
376        ('(42)', int, (42,)),
377        ('( 42 )', int, (42,)),
378        ('(  42)', int, (42,)),
379        ('(42)', str, ('42',)),
380        ('( 42 )', str, (' 42 ',)),
381        ('(  42)', str, ('  42',)),
382        ('(42', int, ValueError),
383        ('( 42 ', int, ValueError),
384        ('(hello)', str, ('hello',)),
385        ('( hello )', str, (' hello ',)),
386        ('(hello))', str, ValueError),
387        ('   (hello)   ', str, ('hello',)),
388        ('   (hello)   )', str, ValueError),
389        ('(hello)?', str, ValueError),
390        ('(null)', str, ('null',)),
391        ('(null)', int, ValueError),
392        (' ( NULL ) ', str, (' NULL ',)),
393        ('   (   NULL   )   ', str, ('   NULL   ',)),
394        (' ( null null ) ', str, (' null null ',)),
395        (' ("null") ', str, ('null',)),
396        (' ("NULL") ', str, ('NULL',)),
397        ('(Hi!)', str, ('Hi!',)),
398        ('("Hi!")', str, ('Hi!',)),
399        ("('Hi!')", str, ("'Hi!'",)),
400        ('(" Hi! ")', str, (' Hi! ',)),
401        ('("Hi!" )', str, ('Hi! ',)),
402        ('( "Hi!")', str, (' Hi!',)),
403        ('( "Hi!" )', str, (' Hi! ',)),
404        ('( ""Hi!"" )', str, (' Hi! ',)),
405        ('( """Hi!""" )', str, (' "Hi!" ',)),
406        ('(a")', str, ValueError),
407        ('("b)', str, ValueError),
408        ('("a" "b)', str, ValueError),
409        ('("a" "b")', str, ('a b',)),
410        ('( "a" "b" "c" )', str, (' a b c ',)),
411        ('(  "a"  "b"  "c"  )', str, ('  a  b  c  ',)),
412        ('(  "a,b"  "c,d"  )', str, ('  a,b  c,d  ',)),
413        ('( "(a,b,c)" d, e, "f,g")', str, (' (a,b,c) d', ' e', ' f,g')),
414        ('(a",b,c",d,"e,f")', str, ('a,b,c', 'd', 'e,f')),
415        ('( """a,b""", ""c,d"", "e,f", "g", ""h"", """i""")', str,
416            (' "a,b"', ' c', 'd', ' e,f', ' g', ' h', ' "i"')),
417        ('(a",b)",c"),(d,e)",f,g)', str, ('a,b)', 'c),(d,e)', 'f', 'g')),
418        ('(a"b)', str, ValueError),
419        (r'(a\"b)', str, ('a"b',)),
420        ('(a""b)', str, ('ab',)),
421        ('("a""b")', str, ('a"b',)),
422        (r'(a\,b)', str, ('a,b',)),
423        (r'(a\bc)', str, ('abc',)),
424        (r'("a\bc")', str, ('abc',)),
425        (r'(\a\b\c)', str, ('abc',)),
426        (r'("\a\b\c")', str, ('abc',)),
427        ('("()")', str, ('()',)),
428        (r'(\,)', str, (',',)),
429        (r'(\(\))', str, ('()',)),
430        (r'(\)\()', str, (')(',)),
431        ('("(a,b,c)")', str, ('(a,b,c)',)),
432        ("('abc')", str, ("'abc'",)),
433        ('("abc")', str, ('abc',)),
434        (r'(\"abc\")', str, ('"abc"',)),
435        (r"(\'abc\')", str, ("'abc'",)),
436        ('(Hello World!)', str, ('Hello World!',)),
437        ('(Hello, World!)', str, ('Hello', ' World!',)),
438        ('(Hello,\ World!)', str, ('Hello', ' World!',)),
439        ('(Hello\, World!)', str, ('Hello, World!',)),
440        ('("Hello World!")', str, ('Hello World!',)),
441        ("(this,shouldn't,be,null)", str, ('this', "shouldn't", 'be', 'null')),
442        ('(null,should,be,)', str, ('null', 'should', 'be', None)),
443        ('(abcABC0123!?+-*/=&%$\\\\\'\\"{[]}"""":;\\,,)', str,
444            ('abcABC0123!?+-*/=&%$\\\'"{[]}":;,', None)),
445        ('(3, 2, 1,)', int, (3, 2, 1, None)),
446        ('(3, 2, 1, )', int, ValueError),
447        ('(, 1, 2, 3)', int, (None, 1, 2, 3)),
448        ('( , 1, 2, 3)', int, ValueError),
449        ('(,1,,2,,3,)', int, (None, 1, None, 2, None, 3, None)),
450        ('(3,17,51)', int, (3, 17, 51)),
451        (' ( 3 , 17 , 51 ) ', int, (3, 17, 51)),
452        ('(3,17,51)', str, ('3', '17', '51')),
453        (' ( 3 , 17 , 51 ) ', str, (' 3 ', ' 17 ', ' 51 ')),
454        ('(1,"2",abc,"def")', str, ('1', '2', 'abc', 'def')),
455        ('(())', str, ValueError),
456        ('()))', str, ValueError),
457        ('()()', str, ValueError),
458        ('((()', str, ('((',)),
459        ('(())', int, ValueError),
460        ('((),())', str, ValueError),
461        ('("()","()")', str, ('()', '()')),
462        ('( " () , () , () " )', str, ('  () , () , ()  ',)),
463        ('(20000, 25000, 25000, 25000)', int, (20000, 25000, 25000, 25000)),
464        ('("breakfast","consulting","meeting","lunch")', str,
465            ('breakfast', 'consulting', 'meeting', 'lunch')),
466        ('("breakfast","consulting","meeting","lunch")',
467            (str, str, str), ValueError),
468        ('("breakfast","consulting","meeting","lunch")', (str, str, str, str),
469            ('breakfast', 'consulting', 'meeting', 'lunch')),
470        ('("breakfast","consulting","meeting","lunch")',
471            (str, str, str, str, str), ValueError),
472        ('("fuzzy dice",42,1.9375)', None, ('fuzzy dice', '42', '1.9375')),
473        ('("fuzzy dice",42,1.9375)', str, ('fuzzy dice', '42', '1.9375')),
474        ('("fuzzy dice",42,1.9375)', int, ValueError),
475        ('("fuzzy dice",42,1.9375)', (str, int, float),
476            ('fuzzy dice', 42, 1.9375)),
477        ('("fuzzy dice",42,1.9375)', (str, int), ValueError),
478        ('("fuzzy dice",42,1.9375)', (str, int, float, str), ValueError),
479        ('("fuzzy dice",42,)', (str, int, float), ('fuzzy dice', 42, None)),
480        ('("fuzzy dice",42,)', (str, int), ValueError),
481        ('("",42,)', (str, int, float), ('', 42, None)),
482        ('("fuzzy dice","",1.9375)', (str, int, float), ValueError),
483        ('(fuzzy dice,"42","1.9375")', (str, int, float),
484            ('fuzzy dice', 42, 1.9375))]
485
486    def testParserParams(self):
487        f = pg.cast_record
488        self.assertRaises(TypeError, f)
489        self.assertRaises(TypeError, f, None)
490        self.assertRaises(TypeError, f, '()', 1)
491        self.assertRaises(TypeError, f, '()', b',',)
492        self.assertRaises(TypeError, f, '()', None, None)
493        self.assertRaises(TypeError, f, '()', None, 1)
494        self.assertRaises(TypeError, f, '()', None, b'')
495        self.assertRaises(ValueError, f, '()', None, b'\\')
496        self.assertRaises(ValueError, f, '()', None, b'(')
497        self.assertRaises(ValueError, f, '()', None, b')')
498        self.assertRaises(TypeError, f, '{}', None, b',;')
499        self.assertEqual(f('()'), (None,))
500        self.assertEqual(f('()', None), (None,))
501        self.assertEqual(f('()', None, b';'), (None,))
502        self.assertEqual(f('()', str), (None,))
503        self.assertEqual(f('()', str, b';'), (None,))
504
505    def testParserSimple(self):
506        r = pg.cast_record('(a,b,c)')
507        self.assertIsInstance(r, tuple)
508        self.assertEqual(len(r), 3)
509        self.assertEqual(r, ('a', 'b', 'c'))
510
511    def testParserNested(self):
512        f = pg.cast_record
513        self.assertRaises(ValueError, f, '((a,b,c))')
514        self.assertRaises(ValueError, f, '((a,b),(c,d))')
515        self.assertRaises(ValueError, f, '((a),(b),(c))')
516        self.assertRaises(ValueError, f, '(((((((abc)))))))')
517
518    def testParserManyElements(self):
519        f = pg.cast_record
520        for n in 3, 5, 9, 12, 16, 32, 64, 256:
521            r = '(%s)' % ','.join(map(str, range(n)))
522            r = f(r, int)
523            self.assertEqual(r, tuple(range(n)))
524
525    def testParserCastUniform(self):
526        f = pg.cast_record
527        self.assertEqual(f('(1)'), ('1',))
528        self.assertEqual(f('(1)', None), ('1',))
529        self.assertEqual(f('(1)', int), (1,))
530        self.assertEqual(f('(1)', str), ('1',))
531        self.assertEqual(f('(a)'), ('a',))
532        self.assertEqual(f('(a)', None), ('a',))
533        self.assertRaises(ValueError, f, '(a)', int)
534        self.assertEqual(f('(a)', str), ('a',))
535        cast = lambda s: '%s is ok' % s
536        self.assertEqual(f('(a)', cast), ('a is ok',))
537
538    def testParserCastNonUniform(self):
539        f = pg.cast_record
540        self.assertEqual(f('(1)', []), ('1',))
541        self.assertEqual(f('(1)', [None]), ('1',))
542        self.assertEqual(f('(1)', [str]), ('1',))
543        self.assertEqual(f('(1)', [int]), (1,))
544        self.assertRaises(ValueError, f, '(1)', [None, None])
545        self.assertRaises(ValueError, f, '(1)', [str, str])
546        self.assertRaises(ValueError, f, '(1)', [int, int])
547        self.assertEqual(f('(a)', [None]), ('a',))
548        self.assertEqual(f('(a)', [str]), ('a',))
549        self.assertRaises(ValueError, f, '(a)', [int])
550        self.assertEqual(f('(1,a)', [int, str]), (1, 'a'))
551        self.assertRaises(ValueError, f, '(1,a)', [str, int])
552        self.assertEqual(f('(a,1)', [str, int]), ('a', 1))
553        self.assertRaises(ValueError, f, '(a,1)', [int, str])
554        self.assertEqual(f('(1,a,2,b,3,c)',
555            [int, str, int, str, int, str]), (1, 'a', 2, 'b', 3, 'c'))
556        self.assertEqual(f('(1,a,2,b,3,c)',
557            (int, str, int, str, int, str)), (1, 'a', 2, 'b', 3, 'c'))
558        cast1 = lambda s: '%s is ok' % s
559        self.assertEqual(f('(a)', [cast1]), ('a is ok',))
560        cast2 = lambda s: 'and %s is ok, too' % s
561        self.assertEqual(f('(a,b)', [cast1, cast2]),
562            ('a is ok', 'and b is ok, too'))
563        self.assertRaises(ValueError, f, '(a)', [cast1, cast2])
564        self.assertRaises(ValueError, f, '(a,b,c)', [cast1, cast2])
565        self.assertEqual(f('(1,2,3,4,5,6)',
566            [int, float, str, None, cast1, cast2]),
567            (1, 2.0, '3', '4', '5 is ok', 'and 6 is ok, too'))
568
569    def testParserDelim(self):
570        f = pg.cast_record
571        self.assertEqual(f('(1,2)'), ('1', '2'))
572        self.assertEqual(f('(1,2)', delim=b','), ('1', '2'))
573        self.assertEqual(f('(1;2)'), ('1;2',))
574        self.assertEqual(f('(1;2)', delim=b';'), ('1', '2'))
575        self.assertEqual(f('(1,2)', delim=b';'), ('1,2',))
576
577    def testParserWithData(self):
578        f = pg.cast_record
579        for string, cast, expected in self.test_strings:
580            if expected is ValueError:
581                self.assertRaises(ValueError, f, string, cast)
582            else:
583                self.assertEqual(f(string, cast), expected)
584
585    def testParserWithoutCast(self):
586        f = pg.cast_record
587
588        for string, cast, expected in self.test_strings:
589            if cast is not str:
590                continue
591            if expected is ValueError:
592                self.assertRaises(ValueError, f, string)
593            else:
594                self.assertEqual(f(string), expected)
595
596    def testParserWithDifferentDelimiter(self):
597        f = pg.cast_record
598
599        def replace_comma(value):
600            if isinstance(value, str):
601                return value.replace(';', '@').replace(
602                    ',', ';').replace('@', ',')
603            elif isinstance(value, tuple):
604                return tuple(replace_comma(v) for v in value)
605            else:
606                return value
607
608        for string, cast, expected in self.test_strings:
609            string = replace_comma(string)
610            if expected is ValueError:
611                self.assertRaises(ValueError, f, string, cast)
612            else:
613                expected = replace_comma(expected)
614                self.assertEqual(f(string, cast, b';'), expected)
615
616
617class TestEscapeFunctions(unittest.TestCase):
618    """Test pg escape and unescape functions.
619
620    The libpq interface memorizes some parameters of the last opened
621    connection that influence the result of these functions.
622    Therefore we cannot do rigid tests of these functions here.
623    We leave this for the test module that runs with a database.
624
625    """
626
627    def testEscapeString(self):
628        f = pg.escape_string
629        r = f(b'plain')
630        self.assertIsInstance(r, bytes)
631        self.assertEqual(r, b'plain')
632        r = f(u'plain')
633        self.assertIsInstance(r, unicode)
634        self.assertEqual(r, u'plain')
635        r = f("that's cheese")
636        self.assertIsInstance(r, str)
637        self.assertEqual(r, "that''s cheese")
638
639    def testEscapeBytea(self):
640        f = pg.escape_bytea
641        r = f(b'plain')
642        self.assertIsInstance(r, bytes)
643        self.assertEqual(r, b'plain')
644        r = f(u'plain')
645        self.assertIsInstance(r, unicode)
646        self.assertEqual(r, u'plain')
647        r = f("that's cheese")
648        self.assertIsInstance(r, str)
649        self.assertEqual(r, "that''s cheese")
650
651    def testUnescapeBytea(self):
652        f = pg.unescape_bytea
653        r = f(b'plain')
654        self.assertIsInstance(r, bytes)
655        self.assertEqual(r, b'plain')
656        r = f(u'plain')
657        self.assertIsInstance(r, bytes)
658        self.assertEqual(r, b'plain')
659        r = f(b"das is' k\\303\\244se")
660        self.assertIsInstance(r, bytes)
661        self.assertEqual(r, u"das is' kÀse".encode('utf-8'))
662        r = f(u"das is' k\\303\\244se")
663        self.assertIsInstance(r, bytes)
664        self.assertEqual(r, u"das is' kÀse".encode('utf-8'))
665        r = f(b'O\\000ps\\377!')
666        self.assertEqual(r, b'O\x00ps\xff!')
667        r = f(u'O\\000ps\\377!')
668        self.assertEqual(r, b'O\x00ps\xff!')
669
670
671class TestConfigFunctions(unittest.TestCase):
672    """Test the functions for changing default settings.
673
674    The effect of most of these cannot be tested here, because that
675    needs a database connection.  So we merely test their existence here.
676
677    """
678
679    def testGetDecimalPoint(self):
680        r = pg.get_decimal_point()
681        self.assertIsInstance(r, str)
682        self.assertEqual(r, '.')
683
684    def testSetDecimalPoint(self):
685        point = pg.get_decimal_point()
686        try:
687            pg.set_decimal_point('*')
688            r = pg.get_decimal_point()
689            self.assertIsInstance(r, str)
690            self.assertEqual(r, '*')
691        finally:
692            pg.set_decimal_point(point)
693        r = pg.get_decimal_point()
694        self.assertIsInstance(r, str)
695        self.assertEqual(r, point)
696
697    def testGetDecimal(self):
698        r = pg.get_decimal()
699        self.assertIs(r, pg.Decimal)
700
701    def testSetDecimal(self):
702        decimal_class = pg.Decimal
703        try:
704            pg.set_decimal(int)
705            r = pg.get_decimal()
706            self.assertIs(r, int)
707        finally:
708            pg.set_decimal(decimal_class)
709        r = pg.get_decimal()
710        self.assertIs(r, decimal_class)
711
712    def testGetBool(self):
713        r = pg.get_bool()
714        self.assertIsInstance(r, bool)
715        self.assertIs(r, False)
716
717    def testSetBool(self):
718        use_bool = pg.get_bool()
719        try:
720            pg.set_bool(True)
721            r = pg.get_bool()
722            pg.set_bool(use_bool)
723            self.assertIsInstance(r, bool)
724            self.assertIs(r, True)
725            pg.set_bool(False)
726            r = pg.get_bool()
727            self.assertIsInstance(r, bool)
728            self.assertIs(r, False)
729        finally:
730            pg.set_bool(use_bool)
731        r = pg.get_bool()
732        self.assertIsInstance(r, bool)
733        self.assertIs(r, use_bool)
734
735    def testGetNamedresult(self):
736        r = pg.get_namedresult()
737        self.assertTrue(callable(r))
738        self.assertIs(r, pg._namedresult)
739
740    def testSetNamedresult(self):
741        namedresult = pg.get_namedresult()
742        try:
743            pg.set_namedresult(None)
744            r = pg.get_namedresult()
745            self.assertIsNone(r)
746            f = lambda q: q.getresult()
747            pg.set_namedresult(f)
748            r = pg.get_namedresult()
749            self.assertIs(r, f)
750            self.assertRaises(TypeError, pg.set_namedresult, 'invalid')
751        finally:
752            pg.set_namedresult(namedresult)
753        r = pg.get_namedresult()
754        self.assertIs(r, namedresult)
755
756    def testGetJsondecode(self):
757        r = pg.get_jsondecode()
758        self.assertTrue(callable(r))
759        self.assertIs(r, json.loads)
760
761    def testSetJsondecode(self):
762        jsondecode = pg.get_jsondecode()
763        try:
764            pg.set_jsondecode(None)
765            r = pg.get_jsondecode()
766            self.assertIsNone(r)
767            pg.set_jsondecode(str)
768            r = pg.get_jsondecode()
769            self.assertIs(r, str)
770            self.assertRaises(TypeError, pg.set_jsondecode, 'invalid')
771        finally:
772            pg.set_jsondecode(jsondecode)
773        r = pg.get_jsondecode()
774        self.assertIs(r, jsondecode)
775
776
777class TestModuleConstants(unittest.TestCase):
778    """Test the existence of the documented module constants."""
779
780    def testVersion(self):
781        v = pg.version
782        self.assertIsInstance(v, str)
783        # make sure the version conforms to PEP440
784        re_version = r"""^
785            (\d[\.\d]*(?<= \d))
786            ((?:[abc]|rc)\d+)?
787            (?:(\.post\d+))?
788            (?:(\.dev\d+))?
789            (?:(\+(?![.])[a-zA-Z0-9\.]*[a-zA-Z0-9]))?
790            $"""
791        match = re.match(re_version, v, re.X)
792        self.assertIsNotNone(match)
793
794
795if __name__ == '__main__':
796    unittest.main()
Note: See TracBrowser for help on using the repository browser.