""" Unit tests version 2.6.1.0 for adodbapi""" """ adodbapi - A python DB API 2.0 interface to Microsoft ADO Copyright (C) 2002 Henrik Ekelund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Updates by Vernon Cole """ import copy import datetime import decimal import random import string import sys import unittest try: import win32com.client win32 = True except ImportError: win32 = False # run the configuration module. import adodbapitestconfig as config # will set sys.path to find correct version of adodbapi # in our code below, all our switches are from config.whatever import tryconnection import adodbapi import adodbapi.apibase as api try: import adodbapi.ado_consts as ado_consts except ImportError: # we are doing a shortcut import as a module -- so try: import ado_consts except ImportError: from adodbapi import ado_consts def str2bytes(sval): return sval.encode("latin1") long = int def randomstring(length): return "".join([random.choice(string.ascii_letters) for n in range(32)]) class CommonDBTests(unittest.TestCase): "Self contained super-simple tests in easy syntax, should work on everything between mySQL and Oracle" def setUp(self): self.engine = "unknown" def getEngine(self): return self.engine def getConnection(self): raise NotImplementedError # "This method must be overriden by a subclass" def getCursor(self): return self.getConnection().cursor() def testConnection(self): crsr = self.getCursor() assert crsr.__class__.__name__ == "Cursor" def testErrorHandlerInherits(self): if not self.remote: conn = self.getConnection() mycallable = lambda connection, cursor, errorclass, errorvalue: 1 conn.errorhandler = mycallable crsr = conn.cursor() assert ( crsr.errorhandler == mycallable ), "Error handler on crsr should be same as on connection" def testDefaultErrorHandlerConnection(self): if not self.remote: conn = self.getConnection() del conn.messages[:] try: conn.close() conn.commit() # Should not be able to use connection after it is closed except: assert len(conn.messages) == 1 assert len(conn.messages[0]) == 2 assert conn.messages[0][0] == api.ProgrammingError def testOwnErrorHandlerConnection(self): if self.remote: # ToDo: use "skip" return mycallable = ( lambda connection, cursor, errorclass, errorvalue: 1 ) # does not raise anything conn = self.getConnection() conn.errorhandler = mycallable conn.close() conn.commit() # Should not be able to use connection after it is closed assert len(conn.messages) == 0 conn.errorhandler = None # This should bring back the standard error handler try: conn.close() conn.commit() # Should not be able to use connection after it is closed except: pass # The Standard errorhandler appends error to messages attribute assert ( len(conn.messages) > 0 ), "Setting errorhandler to none should bring back the standard error handler" def testDefaultErrorHandlerCursor(self): crsr = self.getConnection().cursor() if not self.remote: del crsr.messages[:] try: crsr.execute("SELECT abbtytddrf FROM dasdasd") except: assert len(crsr.messages) == 1 assert len(crsr.messages[0]) == 2 assert crsr.messages[0][0] == api.DatabaseError def testOwnErrorHandlerCursor(self): if self.remote: # ToDo: should be a "skip" return mycallable = ( lambda connection, cursor, errorclass, errorvalue: 1 ) # does not raise anything crsr = self.getConnection().cursor() crsr.errorhandler = mycallable crsr.execute("SELECT abbtytddrf FROM dasdasd") assert len(crsr.messages) == 0 crsr.errorhandler = None # This should bring back the standard error handler try: crsr.execute("SELECT abbtytddrf FROM dasdasd") except: pass # The Standard errorhandler appends error to messages attribute assert ( len(crsr.messages) > 0 ), "Setting errorhandler to none should bring back the standard error handler" def testUserDefinedConversions(self): if self.remote: ## Todo: should be a "skip" return try: duplicatingConverter = lambda aStringField: aStringField * 2 assert duplicatingConverter("gabba") == "gabbagabba" self.helpForceDropOnTblTemp() conn = self.getConnection() # the variantConversions attribute should not exist on a normal connection object self.assertRaises(AttributeError, lambda x: conn.variantConversions[x], [2]) if not self.remote: # create a variantConversions attribute on the connection conn.variantConversions = copy.copy(api.variantConversions) crsr = conn.cursor() tabdef = ( "CREATE TABLE xx_%s (fldData VARCHAR(100) NOT NULL, fld2 VARCHAR(20))" % config.tmp ) crsr.execute(tabdef) crsr.execute( "INSERT INTO xx_%s(fldData,fld2) VALUES('gabba','booga')" % config.tmp ) crsr.execute( "INSERT INTO xx_%s(fldData,fld2) VALUES('hey','yo')" % config.tmp ) # change converter for ALL adoStringTypes columns conn.variantConversions[api.adoStringTypes] = duplicatingConverter crsr.execute( "SELECT fldData,fld2 FROM xx_%s ORDER BY fldData" % config.tmp ) rows = crsr.fetchall() row = rows[0] self.assertEqual(row[0], "gabbagabba") row = rows[1] self.assertEqual(row[0], "heyhey") self.assertEqual(row[1], "yoyo") upcaseConverter = lambda aStringField: aStringField.upper() assert upcaseConverter("upThis") == "UPTHIS" # now use a single column converter rows.converters[1] = upcaseConverter # convert second column self.assertEqual(row[0], "heyhey") # first will be unchanged self.assertEqual(row[1], "YO") # second will convert to upper case finally: try: del conn.variantConversions # Restore the default except: pass self.helpRollbackTblTemp() def testUserDefinedConversionForExactNumericTypes(self): # variantConversions is a dictionary of conversion functions # held internally in adodbapi.apibase # # !!! this test intentionally alters the value of what should be constant in the module # !!! no new code should use this example, to is only a test to see that the # !!! deprecated way of doing this still works. (use connection.variantConversions) # if not self.remote and sys.version_info < (3, 0): ### Py3 need different test oldconverter = adodbapi.variantConversions[ ado_consts.adNumeric ] # keep old function to restore later # By default decimal and "numbers" are returned as decimals. # Instead, make numbers return as floats try: adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtFloat self.helpTestDataType( "decimal(18,2)", "NUMBER", 3.45, compareAlmostEqual=1 ) self.helpTestDataType( "numeric(18,2)", "NUMBER", 3.45, compareAlmostEqual=1 ) # now return strings adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtString self.helpTestDataType("numeric(18,2)", "NUMBER", "3.45") # now a completly weird user defined convertion adodbapi.variantConversions[ado_consts.adNumeric] = ( lambda x: "!!This function returns a funny unicode string %s!!" % x ) self.helpTestDataType( "numeric(18,2)", "NUMBER", "3.45", allowedReturnValues=[ "!!This function returns a funny unicode string 3.45!!" ], ) finally: # now reset the converter to its original function adodbapi.variantConversions[ ado_consts.adNumeric ] = oldconverter # Restore the original convertion function def helpTestDataType( self, sqlDataTypeString, DBAPIDataTypeString, pyData, pyDataInputAlternatives=None, compareAlmostEqual=None, allowedReturnValues=None, ): self.helpForceDropOnTblTemp() conn = self.getConnection() crsr = conn.cursor() tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldData """ % config.tmp + sqlDataTypeString + ")\n" ) crsr.execute(tabdef) # Test Null values mapped to None crsr.execute("INSERT INTO xx_%s (fldId) VALUES (1)" % config.tmp) crsr.execute("SELECT fldId,fldData FROM xx_%s" % config.tmp) rs = crsr.fetchone() self.assertEqual(rs[1], None) # Null should be mapped to None assert rs[0] == 1 # Test description related descTuple = crsr.description[1] assert descTuple[0] in ["fldData", "flddata"], 'was "%s" expected "%s"' % ( descTuple[0], "fldData", ) if DBAPIDataTypeString == "STRING": assert descTuple[1] == api.STRING, 'was "%s" expected "%s"' % ( descTuple[1], api.STRING.values, ) elif DBAPIDataTypeString == "NUMBER": assert descTuple[1] == api.NUMBER, 'was "%s" expected "%s"' % ( descTuple[1], api.NUMBER.values, ) elif DBAPIDataTypeString == "BINARY": assert descTuple[1] == api.BINARY, 'was "%s" expected "%s"' % ( descTuple[1], api.BINARY.values, ) elif DBAPIDataTypeString == "DATETIME": assert descTuple[1] == api.DATETIME, 'was "%s" expected "%s"' % ( descTuple[1], api.DATETIME.values, ) elif DBAPIDataTypeString == "ROWID": assert descTuple[1] == api.ROWID, 'was "%s" expected "%s"' % ( descTuple[1], api.ROWID.values, ) elif DBAPIDataTypeString == "UUID": assert descTuple[1] == api.OTHER, 'was "%s" expected "%s"' % ( descTuple[1], api.OTHER.values, ) else: raise NotImplementedError # "DBAPIDataTypeString not provided" # Test data binding inputs = [pyData] if pyDataInputAlternatives: inputs.extend(pyDataInputAlternatives) inputs = set(inputs) # removes redundant string==unicode tests fldId = 1 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_%s (fldId,fldData) VALUES (?,?)" % config.tmp, (fldId, inParam), ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldData FROM xx_%s WHERE ?=fldID" % config.tmp, [fldId] ) rs = crsr.fetchone() if allowedReturnValues: allowedTypes = tuple([type(aRV) for aRV in allowedReturnValues]) assert isinstance( rs[0], allowedTypes ), 'result type "%s" must be one of %s' % (type(rs[0]), allowedTypes) else: assert isinstance( rs[0], type(pyData) ), 'result type "%s" must be instance of %s' % ( type(rs[0]), type(pyData), ) if compareAlmostEqual and DBAPIDataTypeString == "DATETIME": iso1 = adodbapi.dateconverter.DateObjectToIsoFormatString(rs[0]) iso2 = adodbapi.dateconverter.DateObjectToIsoFormatString(pyData) self.assertEqual(iso1, iso2) elif compareAlmostEqual: s = float(pyData) v = float(rs[0]) assert ( abs(v - s) / s < 0.00001 ), "Values not almost equal recvd=%s, expected=%f" % (rs[0], s) else: if allowedReturnValues: ok = False self.assertTrue( rs[0] in allowedReturnValues, 'Value "%s" not in %s' % (repr(rs[0]), allowedReturnValues), ) else: self.assertEqual( rs[0], pyData, 'Values are not equal recvd="%s", expected="%s"' % (rs[0], pyData), ) def testDataTypeFloat(self): self.helpTestDataType("real", "NUMBER", 3.45, compareAlmostEqual=True) self.helpTestDataType("float", "NUMBER", 1.79e37, compareAlmostEqual=True) def testDataTypeDecmal(self): self.helpTestDataType( "decimal(18,2)", "NUMBER", 3.45, allowedReturnValues=["3.45", "3,45", decimal.Decimal("3.45")], ) self.helpTestDataType( "numeric(18,2)", "NUMBER", 3.45, allowedReturnValues=["3.45", "3,45", decimal.Decimal("3.45")], ) self.helpTestDataType( "decimal(20,2)", "NUMBER", 444444444444444444, allowedReturnValues=[ "444444444444444444.00", "444444444444444444,00", decimal.Decimal("444444444444444444"), ], ) if self.getEngine() == "MSSQL": self.helpTestDataType( "uniqueidentifier", "UUID", "{71A4F49E-39F3-42B1-A41E-48FF154996E6}", allowedReturnValues=["{71A4F49E-39F3-42B1-A41E-48FF154996E6}"], ) def testDataTypeMoney(self): # v2.1 Cole -- use decimal for money if self.getEngine() == "MySQL": self.helpTestDataType( "DECIMAL(20,4)", "NUMBER", decimal.Decimal("-922337203685477.5808") ) elif self.getEngine() == "PostgreSQL": self.helpTestDataType( "money", "NUMBER", decimal.Decimal("-922337203685477.5808"), compareAlmostEqual=True, allowedReturnValues=[ -922337203685477.5808, decimal.Decimal("-922337203685477.5808"), ], ) else: self.helpTestDataType("smallmoney", "NUMBER", decimal.Decimal("214748.02")) self.helpTestDataType( "money", "NUMBER", decimal.Decimal("-922337203685477.5808") ) def testDataTypeInt(self): if self.getEngine() != "PostgreSQL": self.helpTestDataType("tinyint", "NUMBER", 115) self.helpTestDataType("smallint", "NUMBER", -32768) if self.getEngine() not in ["ACCESS", "PostgreSQL"]: self.helpTestDataType( "bit", "NUMBER", 1 ) # Does not work correctly with access if self.getEngine() in ["MSSQL", "PostgreSQL"]: self.helpTestDataType( "bigint", "NUMBER", 3000000000, allowedReturnValues=[3000000000, int(3000000000)], ) self.helpTestDataType("int", "NUMBER", 2147483647) def testDataTypeChar(self): for sqlDataType in ("char(6)", "nchar(6)"): self.helpTestDataType( sqlDataType, "STRING", "spam ", allowedReturnValues=["spam", "spam", "spam ", "spam "], ) def testDataTypeVarChar(self): if self.getEngine() == "MySQL": stringKinds = ["varchar(10)", "text"] elif self.getEngine() == "PostgreSQL": stringKinds = ["varchar(10)", "text", "character varying"] else: stringKinds = [ "varchar(10)", "nvarchar(10)", "text", "ntext", ] # ,"varchar(max)"] for sqlDataType in stringKinds: self.helpTestDataType(sqlDataType, "STRING", "spam", ["spam"]) def testDataTypeDate(self): if self.getEngine() == "PostgreSQL": dt = "timestamp" else: dt = "datetime" self.helpTestDataType( dt, "DATETIME", adodbapi.Date(2002, 10, 28), compareAlmostEqual=True ) if self.getEngine() not in ["MySQL", "PostgreSQL"]: self.helpTestDataType( "smalldatetime", "DATETIME", adodbapi.Date(2002, 10, 28), compareAlmostEqual=True, ) if tag != "pythontime" and self.getEngine() not in [ "MySQL", "PostgreSQL", ]: # fails when using pythonTime self.helpTestDataType( dt, "DATETIME", adodbapi.Timestamp(2002, 10, 28, 12, 15, 1), compareAlmostEqual=True, ) def testDataTypeBinary(self): binfld = str2bytes("\x07\x00\xE2\x40*") arv = [binfld, adodbapi.Binary(binfld), bytes(binfld)] if self.getEngine() == "PostgreSQL": self.helpTestDataType( "bytea", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv ) else: self.helpTestDataType( "binary(5)", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv ) self.helpTestDataType( "varbinary(100)", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv, ) if self.getEngine() != "MySQL": self.helpTestDataType( "image", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv ) def helpRollbackTblTemp(self): self.helpForceDropOnTblTemp() def helpForceDropOnTblTemp(self): conn = self.getConnection() with conn.cursor() as crsr: try: crsr.execute("DROP TABLE xx_%s" % config.tmp) if not conn.autocommit: conn.commit() except: pass def helpCreateAndPopulateTableTemp(self, crsr): tabdef = ( """ CREATE TABLE xx_%s ( fldData INTEGER ) """ % config.tmp ) try: # EAFP crsr.execute(tabdef) except api.DatabaseError: # was not dropped before self.helpForceDropOnTblTemp() # so drop it now crsr.execute(tabdef) for i in range(9): # note: this poor SQL code, but a valid test crsr.execute("INSERT INTO xx_%s (fldData) VALUES (%i)" % (config.tmp, i)) # NOTE: building the test table without using parameter substitution def testFetchAll(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) rs = crsr.fetchall() assert len(rs) == 9 # test slice of rows i = 3 for row in rs[3:-2]: # should have rowid 3..6 assert row[0] == i i += 1 self.helpRollbackTblTemp() def testPreparedStatement(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.prepare("SELECT fldData FROM xx_%s" % config.tmp) crsr.execute(crsr.command) # remember the one that was prepared rs = crsr.fetchall() assert len(rs) == 9 assert rs[2][0] == 2 self.helpRollbackTblTemp() def testWrongPreparedStatement(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.prepare("SELECT * FROM nowhere") crsr.execute( "SELECT fldData FROM xx_%s" % config.tmp ) # should execute this one, not the prepared one rs = crsr.fetchall() assert len(rs) == 9 assert rs[2][0] == 2 self.helpRollbackTblTemp() def testIterator(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) for i, row in enumerate( crsr ): # using cursor as an iterator, rather than fetchxxx assert row[0] == i self.helpRollbackTblTemp() def testExecuteMany(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) seq_of_values = [(111,), (222,)] crsr.executemany( "INSERT INTO xx_%s (fldData) VALUES (?)" % config.tmp, seq_of_values ) if crsr.rowcount == -1: print( self.getEngine() + " Provider does not support rowcount (on .executemany())" ) else: self.assertEqual(crsr.rowcount, 2) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) rs = crsr.fetchall() assert len(rs) == 11 self.helpRollbackTblTemp() def testRowCount(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) if crsr.rowcount == -1: # print("provider does not support rowcount on select") pass else: self.assertEqual(crsr.rowcount, 9) self.helpRollbackTblTemp() def testRowCountNoRecordset(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("DELETE FROM xx_%s WHERE fldData >= 5" % config.tmp) if crsr.rowcount == -1: print(self.getEngine() + " Provider does not support rowcount (on DELETE)") else: self.assertEqual(crsr.rowcount, 4) self.helpRollbackTblTemp() def testFetchMany(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) rs = crsr.fetchmany(3) assert len(rs) == 3 rs = crsr.fetchmany(5) assert len(rs) == 5 rs = crsr.fetchmany(5) assert len(rs) == 1 # Asked for five, but there is only one left self.helpRollbackTblTemp() def testFetchManyWithArraySize(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("SELECT fldData FROM xx_%s" % config.tmp) rs = crsr.fetchmany() assert len(rs) == 1 # arraysize Defaults to one crsr.arraysize = 4 rs = crsr.fetchmany() assert len(rs) == 4 rs = crsr.fetchmany() assert len(rs) == 4 rs = crsr.fetchmany() assert len(rs) == 0 self.helpRollbackTblTemp() def testErrorConnect(self): conn = self.getConnection() kw = {} if "proxy_host" in conn.kwargs: kw["proxy_host"] = conn.kwargs["proxy_host"] conn.close() self.assertRaises(api.DatabaseError, self.db, "not a valid connect string", kw) def testRowIterator(self): self.helpForceDropOnTblTemp() conn = self.getConnection() crsr = conn.cursor() tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldTwo integer, fldThree integer, fldFour integer) """ % config.tmp ) crsr.execute(tabdef) inputs = [(2, 3, 4), (102, 103, 104)] fldId = 1 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_%s (fldId,fldTwo,fldThree,fldFour) VALUES (?,?,?,?)" % config.tmp, (fldId, inParam[0], inParam[1], inParam[2]), ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldTwo,fldThree,fldFour FROM xx_%s WHERE ?=fldID" % config.tmp, [fldId], ) rec = crsr.fetchone() # check that stepping through an emulated row works for j in range(len(inParam)): assert ( rec[j] == inParam[j] ), 'returned value:"%s" != test value:"%s"' % (rec[j], inParam[j]) # check that we can get a complete tuple from a row assert tuple(rec) == inParam, 'returned value:"%s" != test value:"%s"' % ( repr(rec), repr(inParam), ) # test that slices of rows work slice1 = tuple(rec[:-1]) slice2 = tuple(inParam[0:2]) assert slice1 == slice2, 'returned value:"%s" != test value:"%s"' % ( repr(slice1), repr(slice2), ) # now test named column retrieval assert rec["fldTwo"] == inParam[0] assert rec.fldThree == inParam[1] assert rec.fldFour == inParam[2] # test array operation # note that the fields vv vv vv are out of order crsr.execute("select fldThree,fldFour,fldTwo from xx_%s" % config.tmp) recs = crsr.fetchall() assert recs[1][0] == 103 assert recs[0][1] == 4 assert recs[1]["fldFour"] == 104 assert recs[0, 0] == 3 assert recs[0, "fldTwo"] == 2 assert recs[1, 2] == 102 for i in range(1): for j in range(2): assert recs[i][j] == recs[i, j] def testFormatParamstyle(self): self.helpForceDropOnTblTemp() conn = self.getConnection() conn.paramstyle = "format" # test nonstandard use of paramstyle crsr = conn.cursor() tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldData varchar(10), fldConst varchar(30)) """ % config.tmp ) crsr.execute(tabdef) inputs = ["one", "two", "three"] fldId = 2 for inParam in inputs: fldId += 1 sql = ( "INSERT INTO xx_" + config.tmp + " (fldId,fldConst,fldData) VALUES (%s,'thi%s :may cause? trouble', %s)" ) try: crsr.execute(sql, (fldId, inParam)) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE %s=fldID", [fldId], ) rec = crsr.fetchone() self.assertEqual( rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0], inParam), ) self.assertEqual(rec[1], "thi%s :may cause? trouble") # now try an operation with a "%s" as part of a literal sel = ( "insert into xx_" + config.tmp + " (fldId,fldData) VALUES (%s,'four%sfive')" ) params = (20,) crsr.execute(sel, params) # test the .query implementation assert "(?," in crsr.query, 'expected:"%s" in "%s"' % ("(?,", crsr.query) # test the .command attribute assert crsr.command == sel, 'expected:"%s" but found "%s"' % (sel, crsr.command) # test the .parameters attribute if not self.remote: # parameter list will be altered in transit self.assertEqual(crsr.parameters, params) # now make sure the data made it crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=20" % config.tmp) rec = crsr.fetchone() self.assertEqual(rec[0], "four%sfive") def testNamedParamstyle(self): self.helpForceDropOnTblTemp() conn = self.getConnection() crsr = conn.cursor() crsr.paramstyle = "named" # test nonstandard use of paramstyle tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldData varchar(10)) """ % config.tmp ) crsr.execute(tabdef) inputs = ["four", "five", "six"] fldId = 10 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)" % config.tmp, {"f_Val": inParam, "Id": fldId}, ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldData FROM xx_%s WHERE fldID=:Id" % config.tmp, {"Id": fldId} ) rec = crsr.fetchone() self.assertEqual( rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0], inParam), ) # now a test with a ":" as part of a literal crsr.execute( "insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp, {"xyz": 30}, ) crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp) rec = crsr.fetchone() self.assertEqual(rec[0], "six:five") def testPyformatParamstyle(self): self.helpForceDropOnTblTemp() conn = self.getConnection() crsr = conn.cursor() crsr.paramstyle = "pyformat" # test nonstandard use of paramstyle tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldData varchar(10)) """ % config.tmp ) crsr.execute(tabdef) inputs = ["four", "five", "six"] fldId = 10 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_%s (fldId,fldData) VALUES (%%(Id)s,%%(f_Val)s)" % config.tmp, {"f_Val": inParam, "Id": fldId}, ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldData FROM xx_%s WHERE fldID=%%(Id)s" % config.tmp, {"Id": fldId}, ) rec = crsr.fetchone() self.assertEqual( rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0], inParam), ) # now a test with a "%" as part of a literal crsr.execute( "insert into xx_%s (fldId,fldData) VALUES (%%(xyz)s,'six%%five')" % config.tmp, {"xyz": 30}, ) crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp) rec = crsr.fetchone() self.assertEqual(rec[0], "six%five") def testAutomaticParamstyle(self): self.helpForceDropOnTblTemp() conn = self.getConnection() conn.paramstyle = "dynamic" # test nonstandard use of paramstyle crsr = conn.cursor() tabdef = ( """ CREATE TABLE xx_%s ( fldId integer NOT NULL, fldData varchar(10), fldConst varchar(30)) """ % config.tmp ) crsr.execute(tabdef) inputs = ["one", "two", "three"] fldId = 2 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_" + config.tmp + " (fldId,fldConst,fldData) VALUES (?,'thi%s :may cause? troub:1e', ?)", (fldId, inParam), ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise trouble = "thi%s :may cause? troub:1e" crsr.execute( "SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE ?=fldID", [fldId], ) rec = crsr.fetchone() self.assertEqual( rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0], inParam), ) self.assertEqual(rec[1], trouble) # inputs = [u'four',u'five',u'six'] fldId = 10 for inParam in inputs: fldId += 1 try: crsr.execute( "INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)" % config.tmp, {"f_Val": inParam, "Id": fldId}, ) except: if self.remote: for message in crsr.messages: print(message) else: conn.printADOerrors() raise crsr.execute( "SELECT fldData FROM xx_%s WHERE :Id=fldID" % config.tmp, {"Id": fldId} ) rec = crsr.fetchone() self.assertEqual( rec[0], inParam, 'returned value:"%s" != test value:"%s"' % (rec[0], inParam), ) # now a test with a ":" as part of a literal -- and use a prepared query ppdcmd = ( "insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp ) crsr.prepare(ppdcmd) crsr.execute(ppdcmd, {"xyz": 30}) crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp) rec = crsr.fetchone() self.assertEqual(rec[0], "six:five") def testRollBack(self): conn = self.getConnection() crsr = conn.cursor() assert not crsr.connection.autocommit, "Unexpected beginning condition" self.helpCreateAndPopulateTableTemp(crsr) crsr.connection.commit() # commit the first bunch crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp) selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp crsr.execute(selectSql) rs = crsr.fetchall() assert len(rs) == 1 self.conn.rollback() crsr.execute(selectSql) assert ( crsr.fetchone() == None ), "cursor.fetchone should return None if a query retrieves no rows" crsr.execute("SELECT fldData from xx_%s" % config.tmp) rs = crsr.fetchall() assert len(rs) == 9, "the original records should still be present" self.helpRollbackTblTemp() def testCommit(self): try: con2 = self.getAnotherConnection() except NotImplementedError: return # should be "SKIP" for ACCESS assert not con2.autocommit, "default should be manual commit" crsr = con2.cursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp) con2.commit() selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp crsr.execute(selectSql) rs = crsr.fetchall() assert len(rs) == 1 crsr.close() con2.close() conn = self.getConnection() crsr = self.getCursor() with conn.cursor() as crsr: crsr.execute(selectSql) rs = crsr.fetchall() assert len(rs) == 1 assert rs[0][0] == 100 self.helpRollbackTblTemp() def testAutoRollback(self): try: con2 = self.getAnotherConnection() except NotImplementedError: return # should be "SKIP" for ACCESS assert not con2.autocommit, "unexpected beginning condition" crsr = con2.cursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp) selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp crsr.execute(selectSql) rs = crsr.fetchall() assert len(rs) == 1 crsr.close() con2.close() crsr = self.getCursor() try: crsr.execute( selectSql ) # closing the connection should have forced rollback row = crsr.fetchone() except api.DatabaseError: row = None # if the entire table disappeared the rollback was perfect and the test passed assert row == None, ( "cursor.fetchone should return None if a query retrieves no rows. Got %s" % repr(row) ) self.helpRollbackTblTemp() def testAutoCommit(self): try: ac_conn = self.getAnotherConnection({"autocommit": True}) except NotImplementedError: return # should be "SKIP" for ACCESS crsr = ac_conn.cursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp) crsr.close() with self.getCursor() as crsr: selectSql = "SELECT fldData from xx_%s" % config.tmp crsr.execute( selectSql ) # closing the connection should _not_ have forced rollback rs = crsr.fetchall() assert len(rs) == 10, "all records should still be present" ac_conn.close() self.helpRollbackTblTemp() def testSwitchedAutoCommit(self): try: ac_conn = self.getAnotherConnection() except NotImplementedError: return # should be "SKIP" for ACCESS ac_conn.autocommit = True crsr = ac_conn.cursor() self.helpCreateAndPopulateTableTemp(crsr) crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp) crsr.close() conn = self.getConnection() ac_conn.close() with self.getCursor() as crsr: selectSql = "SELECT fldData from xx_%s" % config.tmp crsr.execute( selectSql ) # closing the connection should _not_ have forced rollback rs = crsr.fetchall() assert len(rs) == 10, "all records should still be present" self.helpRollbackTblTemp() def testExtendedTypeHandling(self): class XtendString(str): pass class XtendInt(int): pass class XtendFloat(float): pass xs = XtendString(randomstring(30)) xi = XtendInt(random.randint(-100, 500)) xf = XtendFloat(random.random()) self.helpForceDropOnTblTemp() conn = self.getConnection() crsr = conn.cursor() tabdef = ( """ CREATE TABLE xx_%s ( s VARCHAR(40) NOT NULL, i INTEGER NOT NULL, f REAL NOT NULL)""" % config.tmp ) crsr.execute(tabdef) crsr.execute( "INSERT INTO xx_%s (s, i, f) VALUES (?, ?, ?)" % config.tmp, (xs, xi, xf) ) crsr.close() conn = self.getConnection() with self.getCursor() as crsr: selectSql = "SELECT s, i, f from xx_%s" % config.tmp crsr.execute( selectSql ) # closing the connection should _not_ have forced rollback row = crsr.fetchone() self.assertEqual(row.s, xs) self.assertEqual(row.i, xi) self.assertAlmostEqual(row.f, xf) self.helpRollbackTblTemp() class TestADOwithSQLServer(CommonDBTests): def setUp(self): self.conn = config.dbSqlServerconnect( *config.connStrSQLServer[0], **config.connStrSQLServer[1] ) self.conn.timeout = 30 # turn timeout back up self.engine = "MSSQL" self.db = config.dbSqlServerconnect self.remote = config.connStrSQLServer[2] def tearDown(self): try: self.conn.rollback() except: pass try: self.conn.close() except: pass self.conn = None def getConnection(self): return self.conn def getAnotherConnection(self, addkeys=None): keys = dict(config.connStrSQLServer[1]) if addkeys: keys.update(addkeys) return config.dbSqlServerconnect(*config.connStrSQLServer[0], **keys) def testVariableReturningStoredProcedure(self): crsr = self.conn.cursor() spdef = """ CREATE PROCEDURE sp_DeleteMeOnlyForTesting @theInput varchar(50), @theOtherInput varchar(50), @theOutput varchar(100) OUTPUT AS SET @theOutput=@theInput+@theOtherInput """ try: crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting") self.conn.commit() except: # Make sure it is empty pass crsr.execute(spdef) retvalues = crsr.callproc( "sp_DeleteMeOnlyForTesting", ("Dodsworth", "Anne", " ") ) assert retvalues[0] == "Dodsworth", '%s is not "Dodsworth"' % repr(retvalues[0]) assert retvalues[1] == "Anne", '%s is not "Anne"' % repr(retvalues[1]) assert retvalues[2] == "DodsworthAnne", '%s is not "DodsworthAnne"' % repr( retvalues[2] ) self.conn.rollback() def testMultipleSetReturn(self): crsr = self.getCursor() self.helpCreateAndPopulateTableTemp(crsr) spdef = """ CREATE PROCEDURE sp_DeleteMe_OnlyForTesting AS SELECT fldData FROM xx_%s ORDER BY fldData ASC SELECT fldData From xx_%s where fldData = -9999 SELECT fldData FROM xx_%s ORDER BY fldData DESC """ % ( config.tmp, config.tmp, config.tmp, ) try: crsr.execute("DROP PROCEDURE sp_DeleteMe_OnlyForTesting") self.conn.commit() except: # Make sure it is empty pass crsr.execute(spdef) retvalues = crsr.callproc("sp_DeleteMe_OnlyForTesting") row = crsr.fetchone() self.assertEqual(row[0], 0) assert crsr.nextset() == True, "Operation should succeed" assert not crsr.fetchall(), "Should be an empty second set" assert crsr.nextset() == True, "third set should be present" rowdesc = crsr.fetchall() self.assertEqual(rowdesc[0][0], 8) assert crsr.nextset() == None, "No more return sets, should return None" self.helpRollbackTblTemp() def testDatetimeProcedureParameter(self): crsr = self.conn.cursor() spdef = """ CREATE PROCEDURE sp_DeleteMeOnlyForTesting @theInput DATETIME, @theOtherInput varchar(50), @theOutput varchar(100) OUTPUT AS SET @theOutput = CONVERT(CHARACTER(20), @theInput, 0) + @theOtherInput """ try: crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting") self.conn.commit() except: # Make sure it is empty pass crsr.execute(spdef) result = crsr.callproc( "sp_DeleteMeOnlyForTesting", [adodbapi.Timestamp(2014, 12, 25, 0, 1, 0), "Beep", " " * 30], ) assert result[2] == "Dec 25 2014 12:01AM Beep", 'value was="%s"' % result[2] self.conn.rollback() def testIncorrectStoredProcedureParameter(self): crsr = self.conn.cursor() spdef = """ CREATE PROCEDURE sp_DeleteMeOnlyForTesting @theInput DATETIME, @theOtherInput varchar(50), @theOutput varchar(100) OUTPUT AS SET @theOutput = CONVERT(CHARACTER(20), @theInput) + @theOtherInput """ try: crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting") self.conn.commit() except: # Make sure it is empty pass crsr.execute(spdef) # calling the sproc with a string for the first parameter where a DateTime is expected result = tryconnection.try_operation_with_expected_exception( (api.DataError, api.DatabaseError), crsr.callproc, ["sp_DeleteMeOnlyForTesting"], {"parameters": ["this is wrong", "Anne", "not Alice"]}, ) if result[0]: # the expected exception was raised assert "@theInput" in str(result[1]) or "DatabaseError" in str( result ), "Identifies the wrong erroneous parameter" else: assert result[0], result[1] # incorrect or no exception self.conn.rollback() class TestADOwithAccessDB(CommonDBTests): def setUp(self): self.conn = config.dbAccessconnect( *config.connStrAccess[0], **config.connStrAccess[1] ) self.conn.timeout = 30 # turn timeout back up self.engine = "ACCESS" self.db = config.dbAccessconnect self.remote = config.connStrAccess[2] def tearDown(self): try: self.conn.rollback() except: pass try: self.conn.close() except: pass self.conn = None def getConnection(self): return self.conn def getAnotherConnection(self, addkeys=None): raise NotImplementedError("Jet cannot use a second connection to the database") def testOkConnect(self): c = self.db(*config.connStrAccess[0], **config.connStrAccess[1]) assert c != None c.close() class TestADOwithMySql(CommonDBTests): def setUp(self): self.conn = config.dbMySqlconnect( *config.connStrMySql[0], **config.connStrMySql[1] ) self.conn.timeout = 30 # turn timeout back up self.engine = "MySQL" self.db = config.dbMySqlconnect self.remote = config.connStrMySql[2] def tearDown(self): try: self.conn.rollback() except: pass try: self.conn.close() except: pass self.conn = None def getConnection(self): return self.conn def getAnotherConnection(self, addkeys=None): keys = dict(config.connStrMySql[1]) if addkeys: keys.update(addkeys) return config.dbMySqlconnect(*config.connStrMySql[0], **keys) def testOkConnect(self): c = self.db(*config.connStrMySql[0], **config.connStrMySql[1]) assert c != None # def testStoredProcedure(self): # crsr=self.conn.cursor() # try: # crsr.execute("DROP PROCEDURE DeleteMeOnlyForTesting") # self.conn.commit() # except: #Make sure it is empty # pass # spdef= """ # DELIMITER $$ # CREATE PROCEDURE DeleteMeOnlyForTesting (onein CHAR(10), twoin CHAR(10), OUT theout CHAR(20)) # DETERMINISTIC # BEGIN # SET theout = onein //|| twoin; # /* (SELECT 'a small string' as result; */ # END $$ # """ # # crsr.execute(spdef) # # retvalues=crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' ')) # print 'return value (mysql)=',repr(crsr.returnValue) ### # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0]) # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1]) # assert retvalues[2]=='DodsworthAnne','%s is not "DodsworthAnne"'%repr(retvalues[2]) # # try: # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting") # self.conn.commit() # except: #Make sure it is empty # pass class TestADOwithPostgres(CommonDBTests): def setUp(self): self.conn = config.dbPostgresConnect( *config.connStrPostgres[0], **config.connStrPostgres[1] ) self.conn.timeout = 30 # turn timeout back up self.engine = "PostgreSQL" self.db = config.dbPostgresConnect self.remote = config.connStrPostgres[2] def tearDown(self): try: self.conn.rollback() except: pass try: self.conn.close() except: pass self.conn = None def getConnection(self): return self.conn def getAnotherConnection(self, addkeys=None): keys = dict(config.connStrPostgres[1]) if addkeys: keys.update(addkeys) return config.dbPostgresConnect(*config.connStrPostgres[0], **keys) def testOkConnect(self): c = self.db(*config.connStrPostgres[0], **config.connStrPostgres[1]) assert c != None # def testStoredProcedure(self): # crsr=self.conn.cursor() # spdef= """ # CREATE OR REPLACE FUNCTION DeleteMeOnlyForTesting (text, text) # RETURNS text AS $funk$ # BEGIN # RETURN $1 || $2; # END; # $funk$ # LANGUAGE SQL; # """ # # crsr.execute(spdef) # retvalues = crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' ')) # ### print 'return value (pg)=',repr(crsr.returnValue) ### # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0]) # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1]) # assert retvalues[2]=='Dodsworth Anne','%s is not "Dodsworth Anne"'%repr(retvalues[2]) # self.conn.rollback() # try: # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting") # self.conn.commit() # except: #Make sure it is empty # pass class TimeConverterInterfaceTest(unittest.TestCase): def testIDate(self): assert self.tc.Date(1990, 2, 2) def testITime(self): assert self.tc.Time(13, 2, 2) def testITimestamp(self): assert self.tc.Timestamp(1990, 2, 2, 13, 2, 1) def testIDateObjectFromCOMDate(self): assert self.tc.DateObjectFromCOMDate(37435.7604282) def testICOMDate(self): assert hasattr(self.tc, "COMDate") def testExactDate(self): d = self.tc.Date(1994, 11, 15) comDate = self.tc.COMDate(d) correct = 34653.0 assert comDate == correct, comDate def testExactTimestamp(self): d = self.tc.Timestamp(1994, 11, 15, 12, 0, 0) comDate = self.tc.COMDate(d) correct = 34653.5 self.assertEqual(comDate, correct) d = self.tc.Timestamp(2003, 5, 6, 14, 15, 17) comDate = self.tc.COMDate(d) correct = 37747.593946759262 self.assertEqual(comDate, correct) def testIsoFormat(self): d = self.tc.Timestamp(1994, 11, 15, 12, 3, 10) iso = self.tc.DateObjectToIsoFormatString(d) self.assertEqual(str(iso[:19]), "1994-11-15 12:03:10") dt = self.tc.Date(2003, 5, 2) iso = self.tc.DateObjectToIsoFormatString(dt) self.assertEqual(str(iso[:10]), "2003-05-02") if config.doMxDateTimeTest: import mx.DateTime class TestMXDateTimeConverter(TimeConverterInterfaceTest): def setUp(self): self.tc = api.mxDateTimeConverter() def testCOMDate(self): t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 2) cmd = self.tc.COMDate(t) assert cmd == t.COMDate() def testDateObjectFromCOMDate(self): cmd = self.tc.DateObjectFromCOMDate(37435.7604282) t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 0) t2 = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 2) assert t2 > cmd > t def testDate(self): assert mx.DateTime.Date(1980, 11, 4) == self.tc.Date(1980, 11, 4) def testTime(self): assert mx.DateTime.Time(13, 11, 4) == self.tc.Time(13, 11, 4) def testTimestamp(self): t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 1) obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 1) assert t == obj import time class TestPythonTimeConverter(TimeConverterInterfaceTest): def setUp(self): self.tc = api.pythonTimeConverter() def testCOMDate(self): mk = time.mktime((2002, 6, 28, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) t = time.localtime(mk) # Fri, 28 Jun 2002 18:15:01 +0000 cmd = self.tc.COMDate(t) assert abs(cmd - 37435.7604282) < 1.0 / 24, "%f more than an hour wrong" % cmd def testDateObjectFromCOMDate(self): cmd = self.tc.DateObjectFromCOMDate(37435.7604282) t1 = time.gmtime( time.mktime((2002, 6, 28, 0, 14, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) ) # there are errors in the implementation of gmtime which we ignore t2 = time.gmtime( time.mktime((2002, 6, 29, 12, 14, 2, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) ) assert t1 < cmd < t2, '"%s" should be about 2002-6-28 12:15:01' % repr(cmd) def testDate(self): t1 = time.mktime((2002, 6, 28, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 30, 0)) t2 = time.mktime((2002, 6, 30, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, 0)) obj = self.tc.Date(2002, 6, 29) assert t1 < time.mktime(obj) < t2, obj def testTime(self): self.assertEqual( self.tc.Time(18, 15, 2), time.gmtime(18 * 60 * 60 + 15 * 60 + 2) ) def testTimestamp(self): t1 = time.localtime( time.mktime((2002, 6, 28, 18, 14, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) ) t2 = time.localtime( time.mktime((2002, 6, 28, 18, 16, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1)) ) obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2) assert t1 < obj < t2, obj class TestPythonDateTimeConverter(TimeConverterInterfaceTest): def setUp(self): self.tc = api.pythonDateTimeConverter() def testCOMDate(self): t = datetime.datetime(2002, 6, 28, 18, 15, 1) # Fri, 28 Jun 2002 18:15:01 +0000 cmd = self.tc.COMDate(t) assert abs(cmd - 37435.7604282) < 1.0 / 24, "more than an hour wrong" def testDateObjectFromCOMDate(self): cmd = self.tc.DateObjectFromCOMDate(37435.7604282) t1 = datetime.datetime(2002, 6, 28, 18, 14, 1) t2 = datetime.datetime(2002, 6, 28, 18, 16, 1) assert t1 < cmd < t2, cmd tx = datetime.datetime( 2002, 6, 28, 18, 14, 1, 900000 ) # testing that microseconds don't become milliseconds c1 = self.tc.DateObjectFromCOMDate(self.tc.COMDate(tx)) assert t1 < c1 < t2, c1 def testDate(self): t1 = datetime.date(2002, 6, 28) t2 = datetime.date(2002, 6, 30) obj = self.tc.Date(2002, 6, 29) assert t1 < obj < t2, obj def testTime(self): self.assertEqual(self.tc.Time(18, 15, 2).isoformat()[:8], "18:15:02") def testTimestamp(self): t1 = datetime.datetime(2002, 6, 28, 18, 14, 1) t2 = datetime.datetime(2002, 6, 28, 18, 16, 1) obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2) assert t1 < obj < t2, obj suites = [] suites.append(unittest.makeSuite(TestPythonDateTimeConverter, "test")) if config.doMxDateTimeTest: suites.append(unittest.makeSuite(TestMXDateTimeConverter, "test")) if config.doTimeTest: suites.append(unittest.makeSuite(TestPythonTimeConverter, "test")) if config.doAccessTest: suites.append(unittest.makeSuite(TestADOwithAccessDB, "test")) if config.doSqlServerTest: suites.append(unittest.makeSuite(TestADOwithSQLServer, "test")) if config.doMySqlTest: suites.append(unittest.makeSuite(TestADOwithMySql, "test")) if config.doPostgresTest: suites.append(unittest.makeSuite(TestADOwithPostgres, "test")) class cleanup_manager(object): def __enter__(self): pass def __exit__(self, exc_type, exc_val, exc_tb): config.cleanup(config.testfolder, config.mdb_name) suite = unittest.TestSuite(suites) if __name__ == "__main__": mysuite = copy.deepcopy(suite) with cleanup_manager(): defaultDateConverter = adodbapi.dateconverter print(__doc__) print("Default Date Converter is %s" % (defaultDateConverter,)) dateconverter = defaultDateConverter tag = "datetime" unittest.TextTestRunner().run(mysuite) if config.iterateOverTimeTests: for test, dateconverter, tag in ( (config.doTimeTest, api.pythonTimeConverter, "pythontime"), (config.doMxDateTimeTest, api.mxDateTimeConverter, "mx"), ): if test: mysuite = copy.deepcopy( suite ) # work around a side effect of unittest.TextTestRunner adodbapi.adodbapi.dateconverter = dateconverter() print("Changed dateconverter to ") print(adodbapi.adodbapi.dateconverter) unittest.TextTestRunner().run(mysuite)