blob: a8dfeb9b2d69330092b0634903f2687d9a72b295 [file] [log] [blame]
Petri Lehtinenf8547992012-02-02 17:17:36 +02001#-*- coding: iso-8859-1 -*-
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00002# pysqlite2/test/dbapi.py: tests for DB-API compliance
3#
Gerhard Häringf9cee222010-03-05 15:20:03 +00004# Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de>
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00005#
6# This file is part of pysqlite.
7#
8# This software is provided 'as-is', without any express or implied
9# warranty. In no event will the authors be held liable for any damages
10# arising from the use of this software.
11#
12# Permission is granted to anyone to use this software for any purpose,
13# including commercial applications, and to alter it and redistribute it
14# freely, subject to the following restrictions:
15#
16# 1. The origin of this software must not be misrepresented; you must not
17# claim that you wrote the original software. If you use this software
18# in a product, an acknowledgment in the product documentation would be
19# appreciated but is not required.
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22# 3. This notice may not be removed or altered from any source distribution.
23
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020024import threading
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000025import unittest
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000026import sqlite3 as sqlite
27
Hai Shifcce8c62020-08-08 05:55:35 +080028from test.support.os_helper import TESTFN, unlink
Antoine Pitrou902fc8b2013-02-10 00:02:44 +010029
30
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000031class ModuleTests(unittest.TestCase):
32 def CheckAPILevel(self):
33 self.assertEqual(sqlite.apilevel, "2.0",
34 "apilevel is %s, should be 2.0" % sqlite.apilevel)
35
36 def CheckThreadSafety(self):
37 self.assertEqual(sqlite.threadsafety, 1,
38 "threadsafety is %d, should be 1" % sqlite.threadsafety)
39
40 def CheckParamStyle(self):
41 self.assertEqual(sqlite.paramstyle, "qmark",
42 "paramstyle is '%s', should be 'qmark'" %
43 sqlite.paramstyle)
44
45 def CheckWarning(self):
Ezio Melottib3aedd42010-11-20 19:04:17 +000046 self.assertTrue(issubclass(sqlite.Warning, Exception),
Guido van Rossumcd16bf62007-06-13 18:07:49 +000047 "Warning is not a subclass of Exception")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000048
49 def CheckError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000050 self.assertTrue(issubclass(sqlite.Error, Exception),
Guido van Rossumcd16bf62007-06-13 18:07:49 +000051 "Error is not a subclass of Exception")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000052
53 def CheckInterfaceError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000054 self.assertTrue(issubclass(sqlite.InterfaceError, sqlite.Error),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000055 "InterfaceError is not a subclass of Error")
56
57 def CheckDatabaseError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000058 self.assertTrue(issubclass(sqlite.DatabaseError, sqlite.Error),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000059 "DatabaseError is not a subclass of Error")
60
61 def CheckDataError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000062 self.assertTrue(issubclass(sqlite.DataError, sqlite.DatabaseError),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000063 "DataError is not a subclass of DatabaseError")
64
65 def CheckOperationalError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000066 self.assertTrue(issubclass(sqlite.OperationalError, sqlite.DatabaseError),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000067 "OperationalError is not a subclass of DatabaseError")
68
69 def CheckIntegrityError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000070 self.assertTrue(issubclass(sqlite.IntegrityError, sqlite.DatabaseError),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000071 "IntegrityError is not a subclass of DatabaseError")
72
73 def CheckInternalError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000074 self.assertTrue(issubclass(sqlite.InternalError, sqlite.DatabaseError),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000075 "InternalError is not a subclass of DatabaseError")
76
77 def CheckProgrammingError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000078 self.assertTrue(issubclass(sqlite.ProgrammingError, sqlite.DatabaseError),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000079 "ProgrammingError is not a subclass of DatabaseError")
80
81 def CheckNotSupportedError(self):
Gregory P. Smith04cecaf2009-07-04 08:32:15 +000082 self.assertTrue(issubclass(sqlite.NotSupportedError,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000083 sqlite.DatabaseError),
84 "NotSupportedError is not a subclass of DatabaseError")
85
86class ConnectionTests(unittest.TestCase):
R. David Murrayd35251d2010-06-01 01:32:12 +000087
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000088 def setUp(self):
89 self.cx = sqlite.connect(":memory:")
90 cu = self.cx.cursor()
91 cu.execute("create table test(id integer primary key, name text)")
92 cu.execute("insert into test(name) values (?)", ("foo",))
93
94 def tearDown(self):
95 self.cx.close()
96
97 def CheckCommit(self):
98 self.cx.commit()
99
100 def CheckCommitAfterNoChanges(self):
101 """
102 A commit should also work when no changes were made to the database.
103 """
104 self.cx.commit()
105 self.cx.commit()
106
107 def CheckRollback(self):
108 self.cx.rollback()
109
110 def CheckRollbackAfterNoChanges(self):
111 """
112 A rollback should also work when no changes were made to the database.
113 """
114 self.cx.rollback()
115 self.cx.rollback()
116
117 def CheckCursor(self):
118 cu = self.cx.cursor()
119
120 def CheckFailedOpen(self):
121 YOU_CANNOT_OPEN_THIS = "/foo/bar/bla/23534/mydb.db"
Berker Peksag1003b342016-06-12 22:34:49 +0300122 with self.assertRaises(sqlite.OperationalError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000123 con = sqlite.connect(YOU_CANNOT_OPEN_THIS)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000124
125 def CheckClose(self):
126 self.cx.close()
127
128 def CheckExceptions(self):
129 # Optional DB-API extension.
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000130 self.assertEqual(self.cx.Warning, sqlite.Warning)
131 self.assertEqual(self.cx.Error, sqlite.Error)
132 self.assertEqual(self.cx.InterfaceError, sqlite.InterfaceError)
133 self.assertEqual(self.cx.DatabaseError, sqlite.DatabaseError)
134 self.assertEqual(self.cx.DataError, sqlite.DataError)
135 self.assertEqual(self.cx.OperationalError, sqlite.OperationalError)
136 self.assertEqual(self.cx.IntegrityError, sqlite.IntegrityError)
137 self.assertEqual(self.cx.InternalError, sqlite.InternalError)
138 self.assertEqual(self.cx.ProgrammingError, sqlite.ProgrammingError)
139 self.assertEqual(self.cx.NotSupportedError, sqlite.NotSupportedError)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000140
R. David Murrayd35251d2010-06-01 01:32:12 +0000141 def CheckInTransaction(self):
142 # Can't use db from setUp because we want to test initial state.
143 cx = sqlite.connect(":memory:")
144 cu = cx.cursor()
145 self.assertEqual(cx.in_transaction, False)
146 cu.execute("create table transactiontest(id integer primary key, name text)")
147 self.assertEqual(cx.in_transaction, False)
148 cu.execute("insert into transactiontest(name) values (?)", ("foo",))
149 self.assertEqual(cx.in_transaction, True)
150 cu.execute("select name from transactiontest where name=?", ["foo"])
151 row = cu.fetchone()
152 self.assertEqual(cx.in_transaction, True)
153 cx.commit()
154 self.assertEqual(cx.in_transaction, False)
155 cu.execute("select name from transactiontest where name=?", ["foo"])
156 row = cu.fetchone()
157 self.assertEqual(cx.in_transaction, False)
158
159 def CheckInTransactionRO(self):
160 with self.assertRaises(AttributeError):
161 self.cx.in_transaction = True
162
Anders Lorentsena22a1272017-11-07 01:47:43 +0100163 def CheckOpenWithPathLikeObject(self):
Ville Skyttä61f82e02018-04-20 23:08:45 +0300164 """ Checks that we can successfully connect to a database using an object that
Anders Lorentsena22a1272017-11-07 01:47:43 +0100165 is PathLike, i.e. has __fspath__(). """
166 self.addCleanup(unlink, TESTFN)
167 class Path:
168 def __fspath__(self):
169 return TESTFN
170 path = Path()
171 with sqlite.connect(path) as cx:
172 cx.execute('create table test(id integer)')
173
Antoine Pitrou902fc8b2013-02-10 00:02:44 +0100174 def CheckOpenUri(self):
175 if sqlite.sqlite_version_info < (3, 7, 7):
176 with self.assertRaises(sqlite.NotSupportedError):
177 sqlite.connect(':memory:', uri=True)
178 return
179 self.addCleanup(unlink, TESTFN)
180 with sqlite.connect(TESTFN) as cx:
181 cx.execute('create table test(id integer)')
182 with sqlite.connect('file:' + TESTFN, uri=True) as cx:
183 cx.execute('insert into test(id) values(0)')
184 with sqlite.connect('file:' + TESTFN + '?mode=ro', uri=True) as cx:
185 with self.assertRaises(sqlite.OperationalError):
186 cx.execute('insert into test(id) values(1)')
187
188
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000189class CursorTests(unittest.TestCase):
190 def setUp(self):
191 self.cx = sqlite.connect(":memory:")
192 self.cu = self.cx.cursor()
Berker Peksage0b70cd2016-06-14 15:25:36 +0300193 self.cu.execute(
194 "create table test(id integer primary key, name text, "
195 "income number, unique_test text unique)"
196 )
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000197 self.cu.execute("insert into test(name) values (?)", ("foo",))
198
199 def tearDown(self):
200 self.cu.close()
201 self.cx.close()
202
203 def CheckExecuteNoArgs(self):
204 self.cu.execute("delete from test")
205
206 def CheckExecuteIllegalSql(self):
Berker Peksag1003b342016-06-12 22:34:49 +0300207 with self.assertRaises(sqlite.OperationalError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000208 self.cu.execute("select asdf")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000209
210 def CheckExecuteTooMuchSql(self):
Berker Peksag1003b342016-06-12 22:34:49 +0300211 with self.assertRaises(sqlite.Warning):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000212 self.cu.execute("select 5+4; select 4+5")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000213
214 def CheckExecuteTooMuchSql2(self):
215 self.cu.execute("select 5+4; -- foo bar")
216
217 def CheckExecuteTooMuchSql3(self):
218 self.cu.execute("""
219 select 5+4;
220
221 /*
222 foo
223 */
224 """)
225
226 def CheckExecuteWrongSqlArg(self):
Victor Stinnerc6a23202019-06-26 03:16:24 +0200227 with self.assertRaises(TypeError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000228 self.cu.execute(42)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000229
230 def CheckExecuteArgInt(self):
231 self.cu.execute("insert into test(id) values (?)", (42,))
232
233 def CheckExecuteArgFloat(self):
234 self.cu.execute("insert into test(income) values (?)", (2500.32,))
235
236 def CheckExecuteArgString(self):
237 self.cu.execute("insert into test(name) values (?)", ("Hugo",))
238
Petri Lehtinen023fe332012-02-01 22:18:19 +0200239 def CheckExecuteArgStringWithZeroByte(self):
240 self.cu.execute("insert into test(name) values (?)", ("Hu\x00go",))
241
242 self.cu.execute("select name from test where id=?", (self.cu.lastrowid,))
243 row = self.cu.fetchone()
244 self.assertEqual(row[0], "Hu\x00go")
245
Berker Peksagc4154402016-06-12 13:41:47 +0300246 def CheckExecuteNonIterable(self):
247 with self.assertRaises(ValueError) as cm:
248 self.cu.execute("insert into test(id) values (?)", 42)
249 self.assertEqual(str(cm.exception), 'parameters are of unsupported type')
250
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000251 def CheckExecuteWrongNoOfArgs1(self):
252 # too many parameters
Berker Peksag1003b342016-06-12 22:34:49 +0300253 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000254 self.cu.execute("insert into test(id) values (?)", (17, "Egon"))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000255
256 def CheckExecuteWrongNoOfArgs2(self):
257 # too little parameters
Berker Peksag1003b342016-06-12 22:34:49 +0300258 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000259 self.cu.execute("insert into test(id) values (?)")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000260
261 def CheckExecuteWrongNoOfArgs3(self):
262 # no parameters, parameters are needed
Berker Peksag1003b342016-06-12 22:34:49 +0300263 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000264 self.cu.execute("insert into test(id) values (?)")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000265
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000266 def CheckExecuteParamList(self):
267 self.cu.execute("insert into test(name) values ('foo')")
268 self.cu.execute("select name from test where name=?", ["foo"])
269 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000270 self.assertEqual(row[0], "foo")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000271
272 def CheckExecuteParamSequence(self):
273 class L(object):
274 def __len__(self):
275 return 1
276 def __getitem__(self, x):
277 assert x == 0
278 return "foo"
279
280 self.cu.execute("insert into test(name) values ('foo')")
281 self.cu.execute("select name from test where name=?", L())
282 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000283 self.assertEqual(row[0], "foo")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000284
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000285 def CheckExecuteDictMapping(self):
286 self.cu.execute("insert into test(name) values ('foo')")
287 self.cu.execute("select name from test where name=:name", {"name": "foo"})
288 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000289 self.assertEqual(row[0], "foo")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000290
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000291 def CheckExecuteDictMapping_Mapping(self):
292 class D(dict):
293 def __missing__(self, key):
294 return "foo"
295
296 self.cu.execute("insert into test(name) values ('foo')")
297 self.cu.execute("select name from test where name=:name", D())
298 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000299 self.assertEqual(row[0], "foo")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000300
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000301 def CheckExecuteDictMappingTooLittleArgs(self):
302 self.cu.execute("insert into test(name) values ('foo')")
Berker Peksag1003b342016-06-12 22:34:49 +0300303 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000304 self.cu.execute("select name from test where name=:name and id=:id", {"name": "foo"})
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000305
306 def CheckExecuteDictMappingNoArgs(self):
307 self.cu.execute("insert into test(name) values ('foo')")
Berker Peksag1003b342016-06-12 22:34:49 +0300308 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000309 self.cu.execute("select name from test where name=:name")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000310
311 def CheckExecuteDictMappingUnnamed(self):
312 self.cu.execute("insert into test(name) values ('foo')")
Berker Peksag1003b342016-06-12 22:34:49 +0300313 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000314 self.cu.execute("select name from test where name=?", {"name": "foo"})
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000315
316 def CheckClose(self):
317 self.cu.close()
318
319 def CheckRowcountExecute(self):
320 self.cu.execute("delete from test")
321 self.cu.execute("insert into test(name) values ('foo')")
322 self.cu.execute("insert into test(name) values ('foo')")
323 self.cu.execute("update test set name='bar'")
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000324 self.assertEqual(self.cu.rowcount, 2)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000325
Georg Brandlf78e02b2008-06-10 17:40:04 +0000326 def CheckRowcountSelect(self):
327 """
328 pysqlite does not know the rowcount of SELECT statements, because we
329 don't fetch all rows after executing the select statement. The rowcount
330 has thus to be -1.
331 """
332 self.cu.execute("select 5 union select 6")
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000333 self.assertEqual(self.cu.rowcount, -1)
Georg Brandlf78e02b2008-06-10 17:40:04 +0000334
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000335 def CheckRowcountExecutemany(self):
336 self.cu.execute("delete from test")
337 self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)])
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000338 self.assertEqual(self.cu.rowcount, 3)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000339
340 def CheckTotalChanges(self):
341 self.cu.execute("insert into test(name) values ('foo')")
342 self.cu.execute("insert into test(name) values ('foo')")
Berker Peksag48b5c982016-06-14 00:42:50 +0300343 self.assertLess(2, self.cx.total_changes, msg='total changes reported wrong value')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000344
345 # Checks for executemany:
346 # Sequences are required by the DB-API, iterators
347 # enhancements in pysqlite.
348
349 def CheckExecuteManySequence(self):
350 self.cu.executemany("insert into test(income) values (?)", [(x,) for x in range(100, 110)])
351
352 def CheckExecuteManyIterator(self):
353 class MyIter:
354 def __init__(self):
355 self.value = 5
356
Georg Brandla18af4e2007-04-21 15:47:16 +0000357 def __next__(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000358 if self.value == 10:
359 raise StopIteration
360 else:
361 self.value += 1
362 return (self.value,)
363
364 self.cu.executemany("insert into test(income) values (?)", MyIter())
365
366 def CheckExecuteManyGenerator(self):
367 def mygen():
368 for i in range(5):
369 yield (i,)
370
371 self.cu.executemany("insert into test(income) values (?)", mygen())
372
373 def CheckExecuteManyWrongSqlArg(self):
Victor Stinnerc6a23202019-06-26 03:16:24 +0200374 with self.assertRaises(TypeError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000375 self.cu.executemany(42, [(3,)])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000376
377 def CheckExecuteManySelect(self):
Berker Peksag1003b342016-06-12 22:34:49 +0300378 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000379 self.cu.executemany("select ?", [(3,)])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000380
381 def CheckExecuteManyNotIterable(self):
Berker Peksag1003b342016-06-12 22:34:49 +0300382 with self.assertRaises(TypeError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000383 self.cu.executemany("insert into test(income) values (?)", 42)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000384
385 def CheckFetchIter(self):
386 # Optional DB-API extension.
387 self.cu.execute("delete from test")
388 self.cu.execute("insert into test(id) values (?)", (5,))
389 self.cu.execute("insert into test(id) values (?)", (6,))
390 self.cu.execute("select id from test order by id")
391 lst = []
392 for row in self.cu:
393 lst.append(row[0])
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000394 self.assertEqual(lst[0], 5)
395 self.assertEqual(lst[1], 6)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000396
397 def CheckFetchone(self):
398 self.cu.execute("select name from test")
399 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000400 self.assertEqual(row[0], "foo")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000401 row = self.cu.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000402 self.assertEqual(row, None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000403
404 def CheckFetchoneNoStatement(self):
405 cur = self.cx.cursor()
406 row = cur.fetchone()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000407 self.assertEqual(row, None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000408
409 def CheckArraySize(self):
410 # must default ot 1
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000411 self.assertEqual(self.cu.arraysize, 1)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000412
413 # now set to 2
414 self.cu.arraysize = 2
415
416 # now make the query return 3 rows
417 self.cu.execute("delete from test")
418 self.cu.execute("insert into test(name) values ('A')")
419 self.cu.execute("insert into test(name) values ('B')")
420 self.cu.execute("insert into test(name) values ('C')")
421 self.cu.execute("select name from test")
422 res = self.cu.fetchmany()
423
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000424 self.assertEqual(len(res), 2)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000425
426 def CheckFetchmany(self):
427 self.cu.execute("select name from test")
428 res = self.cu.fetchmany(100)
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000429 self.assertEqual(len(res), 1)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000430 res = self.cu.fetchmany(100)
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000431 self.assertEqual(res, [])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000432
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000433 def CheckFetchmanyKwArg(self):
434 """Checks if fetchmany works with keyword arguments"""
435 self.cu.execute("select name from test")
436 res = self.cu.fetchmany(size=100)
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000437 self.assertEqual(len(res), 1)
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000438
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000439 def CheckFetchall(self):
440 self.cu.execute("select name from test")
441 res = self.cu.fetchall()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000442 self.assertEqual(len(res), 1)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000443 res = self.cu.fetchall()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000444 self.assertEqual(res, [])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000445
446 def CheckSetinputsizes(self):
447 self.cu.setinputsizes([3, 4, 5])
448
449 def CheckSetoutputsize(self):
450 self.cu.setoutputsize(5, 0)
451
452 def CheckSetoutputsizeNoColumn(self):
453 self.cu.setoutputsize(42)
454
455 def CheckCursorConnection(self):
456 # Optional DB-API extension.
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000457 self.assertEqual(self.cu.connection, self.cx)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000458
459 def CheckWrongCursorCallable(self):
Berker Peksag1003b342016-06-12 22:34:49 +0300460 with self.assertRaises(TypeError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000461 def f(): pass
462 cur = self.cx.cursor(f)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000463
464 def CheckCursorWrongClass(self):
465 class Foo: pass
466 foo = Foo()
Berker Peksag1003b342016-06-12 22:34:49 +0300467 with self.assertRaises(TypeError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000468 cur = sqlite.Cursor(foo)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000469
Berker Peksage0b70cd2016-06-14 15:25:36 +0300470 def CheckLastRowIDOnReplace(self):
471 """
472 INSERT OR REPLACE and REPLACE INTO should produce the same behavior.
473 """
474 sql = '{} INTO test(id, unique_test) VALUES (?, ?)'
475 for statement in ('INSERT OR REPLACE', 'REPLACE'):
476 with self.subTest(statement=statement):
477 self.cu.execute(sql.format(statement), (1, 'foo'))
478 self.assertEqual(self.cu.lastrowid, 1)
479
480 def CheckLastRowIDOnIgnore(self):
481 self.cu.execute(
482 "insert or ignore into test(unique_test) values (?)",
483 ('test',))
484 self.assertEqual(self.cu.lastrowid, 2)
485 self.cu.execute(
486 "insert or ignore into test(unique_test) values (?)",
487 ('test',))
488 self.assertEqual(self.cu.lastrowid, 2)
489
490 def CheckLastRowIDInsertOR(self):
491 results = []
492 for statement in ('FAIL', 'ABORT', 'ROLLBACK'):
493 sql = 'INSERT OR {} INTO test(unique_test) VALUES (?)'
494 with self.subTest(statement='INSERT OR {}'.format(statement)):
495 self.cu.execute(sql.format(statement), (statement,))
496 results.append((statement, self.cu.lastrowid))
497 with self.assertRaises(sqlite.IntegrityError):
498 self.cu.execute(sql.format(statement), (statement,))
499 results.append((statement, self.cu.lastrowid))
500 expected = [
501 ('FAIL', 2), ('FAIL', 2),
502 ('ABORT', 3), ('ABORT', 3),
503 ('ROLLBACK', 4), ('ROLLBACK', 4),
504 ]
505 self.assertEqual(results, expected)
506
507
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000508class ThreadTests(unittest.TestCase):
509 def setUp(self):
510 self.con = sqlite.connect(":memory:")
511 self.cur = self.con.cursor()
512 self.cur.execute("create table test(id integer primary key, name text, bin binary, ratio number, ts timestamp)")
513
514 def tearDown(self):
515 self.cur.close()
516 self.con.close()
517
518 def CheckConCursor(self):
519 def run(con, errors):
520 try:
521 cur = con.cursor()
522 errors.append("did not raise ProgrammingError")
523 return
524 except sqlite.ProgrammingError:
525 return
526 except:
527 errors.append("raised wrong exception")
528
529 errors = []
530 t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
531 t.start()
532 t.join()
533 if len(errors) > 0:
534 self.fail("\n".join(errors))
535
536 def CheckConCommit(self):
537 def run(con, errors):
538 try:
539 con.commit()
540 errors.append("did not raise ProgrammingError")
541 return
542 except sqlite.ProgrammingError:
543 return
544 except:
545 errors.append("raised wrong exception")
546
547 errors = []
548 t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
549 t.start()
550 t.join()
551 if len(errors) > 0:
552 self.fail("\n".join(errors))
553
554 def CheckConRollback(self):
555 def run(con, errors):
556 try:
557 con.rollback()
558 errors.append("did not raise ProgrammingError")
559 return
560 except sqlite.ProgrammingError:
561 return
562 except:
563 errors.append("raised wrong exception")
564
565 errors = []
566 t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
567 t.start()
568 t.join()
569 if len(errors) > 0:
570 self.fail("\n".join(errors))
571
572 def CheckConClose(self):
573 def run(con, errors):
574 try:
575 con.close()
576 errors.append("did not raise ProgrammingError")
577 return
578 except sqlite.ProgrammingError:
579 return
580 except:
581 errors.append("raised wrong exception")
582
583 errors = []
584 t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors})
585 t.start()
586 t.join()
587 if len(errors) > 0:
588 self.fail("\n".join(errors))
589
590 def CheckCurImplicitBegin(self):
591 def run(cur, errors):
592 try:
593 cur.execute("insert into test(name) values ('a')")
594 errors.append("did not raise ProgrammingError")
595 return
596 except sqlite.ProgrammingError:
597 return
598 except:
599 errors.append("raised wrong exception")
600
601 errors = []
602 t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
603 t.start()
604 t.join()
605 if len(errors) > 0:
606 self.fail("\n".join(errors))
607
608 def CheckCurClose(self):
609 def run(cur, errors):
610 try:
611 cur.close()
612 errors.append("did not raise ProgrammingError")
613 return
614 except sqlite.ProgrammingError:
615 return
616 except:
617 errors.append("raised wrong exception")
618
619 errors = []
620 t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
621 t.start()
622 t.join()
623 if len(errors) > 0:
624 self.fail("\n".join(errors))
625
626 def CheckCurExecute(self):
627 def run(cur, errors):
628 try:
629 cur.execute("select name from test")
630 errors.append("did not raise ProgrammingError")
631 return
632 except sqlite.ProgrammingError:
633 return
634 except:
635 errors.append("raised wrong exception")
636
637 errors = []
638 self.cur.execute("insert into test(name) values ('a')")
639 t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
640 t.start()
641 t.join()
642 if len(errors) > 0:
643 self.fail("\n".join(errors))
644
645 def CheckCurIterNext(self):
646 def run(cur, errors):
647 try:
648 row = cur.fetchone()
649 errors.append("did not raise ProgrammingError")
650 return
651 except sqlite.ProgrammingError:
652 return
653 except:
654 errors.append("raised wrong exception")
655
656 errors = []
657 self.cur.execute("insert into test(name) values ('a')")
658 self.cur.execute("select name from test")
659 t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors})
660 t.start()
661 t.join()
662 if len(errors) > 0:
663 self.fail("\n".join(errors))
664
665class ConstructorTests(unittest.TestCase):
666 def CheckDate(self):
667 d = sqlite.Date(2004, 10, 28)
668
669 def CheckTime(self):
670 t = sqlite.Time(12, 39, 35)
671
672 def CheckTimestamp(self):
673 ts = sqlite.Timestamp(2004, 10, 28, 12, 39, 35)
674
675 def CheckDateFromTicks(self):
676 d = sqlite.DateFromTicks(42)
677
678 def CheckTimeFromTicks(self):
679 t = sqlite.TimeFromTicks(42)
680
681 def CheckTimestampFromTicks(self):
682 ts = sqlite.TimestampFromTicks(42)
683
684 def CheckBinary(self):
Guido van Rossumbae07c92007-10-08 02:46:15 +0000685 b = sqlite.Binary(b"\0'")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000686
687class ExtensionTests(unittest.TestCase):
688 def CheckScriptStringSql(self):
689 con = sqlite.connect(":memory:")
690 cur = con.cursor()
691 cur.executescript("""
692 -- bla bla
693 /* a stupid comment */
694 create table a(i);
695 insert into a(i) values (5);
696 """)
697 cur.execute("select i from a")
698 res = cur.fetchone()[0]
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000699 self.assertEqual(res, 5)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000700
Gerhard Häringf9cee222010-03-05 15:20:03 +0000701 def CheckScriptSyntaxError(self):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000702 con = sqlite.connect(":memory:")
703 cur = con.cursor()
Berker Peksag1003b342016-06-12 22:34:49 +0300704 with self.assertRaises(sqlite.OperationalError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000705 cur.executescript("create table test(x); asdf; create table test2(x)")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000706
707 def CheckScriptErrorNormal(self):
708 con = sqlite.connect(":memory:")
709 cur = con.cursor()
Berker Peksag1003b342016-06-12 22:34:49 +0300710 with self.assertRaises(sqlite.OperationalError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000711 cur.executescript("create table test(sadfsadfdsa); select foo from hurz;")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000712
Berker Peksagc4154402016-06-12 13:41:47 +0300713 def CheckCursorExecutescriptAsBytes(self):
714 con = sqlite.connect(":memory:")
715 cur = con.cursor()
716 with self.assertRaises(ValueError) as cm:
717 cur.executescript(b"create table test(foo); insert into test(foo) values (5);")
718 self.assertEqual(str(cm.exception), 'script argument must be unicode.')
719
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000720 def CheckConnectionExecute(self):
721 con = sqlite.connect(":memory:")
722 result = con.execute("select 5").fetchone()[0]
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000723 self.assertEqual(result, 5, "Basic test of Connection.execute")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000724
725 def CheckConnectionExecutemany(self):
726 con = sqlite.connect(":memory:")
727 con.execute("create table test(foo)")
728 con.executemany("insert into test(foo) values (?)", [(3,), (4,)])
729 result = con.execute("select foo from test order by foo").fetchall()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000730 self.assertEqual(result[0][0], 3, "Basic test of Connection.executemany")
731 self.assertEqual(result[1][0], 4, "Basic test of Connection.executemany")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000732
733 def CheckConnectionExecutescript(self):
734 con = sqlite.connect(":memory:")
735 con.executescript("create table test(foo); insert into test(foo) values (5);")
736 result = con.execute("select foo from test").fetchone()[0]
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000737 self.assertEqual(result, 5, "Basic test of Connection.executescript")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000738
Gerhard Häringf9cee222010-03-05 15:20:03 +0000739class ClosedConTests(unittest.TestCase):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000740 def CheckClosedConCursor(self):
741 con = sqlite.connect(":memory:")
742 con.close()
Berker Peksag1003b342016-06-12 22:34:49 +0300743 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000744 cur = con.cursor()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000745
746 def CheckClosedConCommit(self):
747 con = sqlite.connect(":memory:")
748 con.close()
Berker Peksag1003b342016-06-12 22:34:49 +0300749 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000750 con.commit()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000751
752 def CheckClosedConRollback(self):
753 con = sqlite.connect(":memory:")
754 con.close()
Berker Peksag1003b342016-06-12 22:34:49 +0300755 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000756 con.rollback()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000757
758 def CheckClosedCurExecute(self):
759 con = sqlite.connect(":memory:")
760 cur = con.cursor()
761 con.close()
Berker Peksag1003b342016-06-12 22:34:49 +0300762 with self.assertRaises(sqlite.ProgrammingError):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000763 cur.execute("select 4")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000764
Gerhard Häringf9cee222010-03-05 15:20:03 +0000765 def CheckClosedCreateFunction(self):
766 con = sqlite.connect(":memory:")
767 con.close()
768 def f(x): return 17
Berker Peksag1003b342016-06-12 22:34:49 +0300769 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000770 con.create_function("foo", 1, f)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000771
772 def CheckClosedCreateAggregate(self):
773 con = sqlite.connect(":memory:")
774 con.close()
775 class Agg:
776 def __init__(self):
777 pass
778 def step(self, x):
779 pass
780 def finalize(self):
781 return 17
Berker Peksag1003b342016-06-12 22:34:49 +0300782 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000783 con.create_aggregate("foo", 1, Agg)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000784
785 def CheckClosedSetAuthorizer(self):
786 con = sqlite.connect(":memory:")
787 con.close()
788 def authorizer(*args):
789 return sqlite.DENY
Berker Peksag1003b342016-06-12 22:34:49 +0300790 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000791 con.set_authorizer(authorizer)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000792
793 def CheckClosedSetProgressCallback(self):
794 con = sqlite.connect(":memory:")
795 con.close()
796 def progress(): pass
Berker Peksag1003b342016-06-12 22:34:49 +0300797 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000798 con.set_progress_handler(progress, 100)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000799
800 def CheckClosedCall(self):
801 con = sqlite.connect(":memory:")
802 con.close()
Berker Peksag1003b342016-06-12 22:34:49 +0300803 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000804 con()
Gerhard Häringf9cee222010-03-05 15:20:03 +0000805
806class ClosedCurTests(unittest.TestCase):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000807 def CheckClosed(self):
808 con = sqlite.connect(":memory:")
809 cur = con.cursor()
810 cur.close()
811
812 for method_name in ("execute", "executemany", "executescript", "fetchall", "fetchmany", "fetchone"):
813 if method_name in ("execute", "executescript"):
814 params = ("select 4 union select 5",)
815 elif method_name == "executemany":
816 params = ("insert into foo(bar) values (?)", [(3,), (4,)])
817 else:
818 params = []
819
Berker Peksag1003b342016-06-12 22:34:49 +0300820 with self.assertRaises(sqlite.ProgrammingError):
Gerhard Häringf9cee222010-03-05 15:20:03 +0000821 method = getattr(cur, method_name)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000822 method(*params)
Gerhard Häringf9cee222010-03-05 15:20:03 +0000823
Berker Peksag4bf580d2016-09-07 02:04:34 +0300824
825class SqliteOnConflictTests(unittest.TestCase):
826 """
827 Tests for SQLite's "insert on conflict" feature.
828
829 See https://www.sqlite.org/lang_conflict.html for details.
830 """
831
832 def setUp(self):
833 self.cx = sqlite.connect(":memory:")
834 self.cu = self.cx.cursor()
835 self.cu.execute("""
836 CREATE TABLE test(
837 id INTEGER PRIMARY KEY, name TEXT, unique_name TEXT UNIQUE
838 );
839 """)
840
841 def tearDown(self):
842 self.cu.close()
843 self.cx.close()
844
845 def CheckOnConflictRollbackWithExplicitTransaction(self):
846 self.cx.isolation_level = None # autocommit mode
847 self.cu = self.cx.cursor()
848 # Start an explicit transaction.
849 self.cu.execute("BEGIN")
850 self.cu.execute("INSERT INTO test(name) VALUES ('abort_test')")
851 self.cu.execute("INSERT OR ROLLBACK INTO test(unique_name) VALUES ('foo')")
852 with self.assertRaises(sqlite.IntegrityError):
853 self.cu.execute("INSERT OR ROLLBACK INTO test(unique_name) VALUES ('foo')")
854 # Use connection to commit.
855 self.cx.commit()
856 self.cu.execute("SELECT name, unique_name from test")
857 # Transaction should have rolled back and nothing should be in table.
858 self.assertEqual(self.cu.fetchall(), [])
859
860 def CheckOnConflictAbortRaisesWithExplicitTransactions(self):
861 # Abort cancels the current sql statement but doesn't change anything
862 # about the current transaction.
863 self.cx.isolation_level = None # autocommit mode
864 self.cu = self.cx.cursor()
865 # Start an explicit transaction.
866 self.cu.execute("BEGIN")
867 self.cu.execute("INSERT INTO test(name) VALUES ('abort_test')")
868 self.cu.execute("INSERT OR ABORT INTO test(unique_name) VALUES ('foo')")
869 with self.assertRaises(sqlite.IntegrityError):
870 self.cu.execute("INSERT OR ABORT INTO test(unique_name) VALUES ('foo')")
871 self.cx.commit()
872 self.cu.execute("SELECT name, unique_name FROM test")
873 # Expect the first two inserts to work, third to do nothing.
874 self.assertEqual(self.cu.fetchall(), [('abort_test', None), (None, 'foo',)])
875
876 def CheckOnConflictRollbackWithoutTransaction(self):
877 # Start of implicit transaction
878 self.cu.execute("INSERT INTO test(name) VALUES ('abort_test')")
879 self.cu.execute("INSERT OR ROLLBACK INTO test(unique_name) VALUES ('foo')")
880 with self.assertRaises(sqlite.IntegrityError):
881 self.cu.execute("INSERT OR ROLLBACK INTO test(unique_name) VALUES ('foo')")
882 self.cu.execute("SELECT name, unique_name FROM test")
883 # Implicit transaction is rolled back on error.
884 self.assertEqual(self.cu.fetchall(), [])
885
886 def CheckOnConflictAbortRaisesWithoutTransactions(self):
887 # Abort cancels the current sql statement but doesn't change anything
888 # about the current transaction.
889 self.cu.execute("INSERT INTO test(name) VALUES ('abort_test')")
890 self.cu.execute("INSERT OR ABORT INTO test(unique_name) VALUES ('foo')")
891 with self.assertRaises(sqlite.IntegrityError):
892 self.cu.execute("INSERT OR ABORT INTO test(unique_name) VALUES ('foo')")
893 # Make sure all other values were inserted.
894 self.cu.execute("SELECT name, unique_name FROM test")
895 self.assertEqual(self.cu.fetchall(), [('abort_test', None), (None, 'foo',)])
896
897 def CheckOnConflictFail(self):
898 self.cu.execute("INSERT OR FAIL INTO test(unique_name) VALUES ('foo')")
899 with self.assertRaises(sqlite.IntegrityError):
900 self.cu.execute("INSERT OR FAIL INTO test(unique_name) VALUES ('foo')")
901 self.assertEqual(self.cu.fetchall(), [])
902
903 def CheckOnConflictIgnore(self):
904 self.cu.execute("INSERT OR IGNORE INTO test(unique_name) VALUES ('foo')")
905 # Nothing should happen.
906 self.cu.execute("INSERT OR IGNORE INTO test(unique_name) VALUES ('foo')")
907 self.cu.execute("SELECT unique_name FROM test")
908 self.assertEqual(self.cu.fetchall(), [('foo',)])
909
910 def CheckOnConflictReplace(self):
911 self.cu.execute("INSERT OR REPLACE INTO test(name, unique_name) VALUES ('Data!', 'foo')")
912 # There shouldn't be an IntegrityError exception.
913 self.cu.execute("INSERT OR REPLACE INTO test(name, unique_name) VALUES ('Very different data!', 'foo')")
914 self.cu.execute("SELECT name, unique_name FROM test")
915 self.assertEqual(self.cu.fetchall(), [('Very different data!', 'foo')])
916
917
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000918def suite():
919 module_suite = unittest.makeSuite(ModuleTests, "Check")
920 connection_suite = unittest.makeSuite(ConnectionTests, "Check")
921 cursor_suite = unittest.makeSuite(CursorTests, "Check")
922 thread_suite = unittest.makeSuite(ThreadTests, "Check")
923 constructor_suite = unittest.makeSuite(ConstructorTests, "Check")
924 ext_suite = unittest.makeSuite(ExtensionTests, "Check")
Gerhard Häringf9cee222010-03-05 15:20:03 +0000925 closed_con_suite = unittest.makeSuite(ClosedConTests, "Check")
926 closed_cur_suite = unittest.makeSuite(ClosedCurTests, "Check")
Berker Peksag4bf580d2016-09-07 02:04:34 +0300927 on_conflict_suite = unittest.makeSuite(SqliteOnConflictTests, "Check")
928 return unittest.TestSuite((
929 module_suite, connection_suite, cursor_suite, thread_suite,
930 constructor_suite, ext_suite, closed_con_suite, closed_cur_suite,
931 on_conflict_suite,
932 ))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000933
934def test():
935 runner = unittest.TextTestRunner()
936 runner.run(suite())
937
938if __name__ == "__main__":
939 test()