/tests/tests.py

https://github.com/fvbock/gDBPool · Python · 260 lines · 192 code · 53 blank · 15 comment · 19 complexity · 838243c34c936141a5e982a5537c38b0 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2011-2012 Florian von Bock (f at vonbock dot info)
  3. #
  4. # gDBPool - Tests
  5. __author__ = "Florian von Bock"
  6. __email__ = "f at vonbock dot info"
  7. __version__ = "0.1.2"
  8. import gevent
  9. from gevent import monkey; monkey.patch_all()
  10. import os, sys
  11. sys.path.insert( 0, os.path.dirname( __file__ ).rpartition( '/' )[ 0 ] )
  12. import unittest
  13. import random
  14. import logging
  15. from gevent.select import select
  16. from gevent.queue import Queue
  17. from gevent.queue import Empty as QueueEmptyException
  18. from gdbpool.gdbpool_error import DBInteractionException, DBPoolConnectionException, PoolConnectionException, StreamEndException
  19. from gdbpool.interaction_pool import DBInteractionPool
  20. from gdbpool.connection_pool import DBConnectionPool
  21. from gdbpool.pool_connection import PoolConnection
  22. logging.basicConfig( level = logging.INFO, format = "%(asctime)s %(message)s" )
  23. logger = logging.getLogger()
  24. dsn = "host=127.0.0.1 port=5432 user=postgres dbname=gdbpool_test"
  25. #dsn_read = "host=127.0.0.1 port=5433 user=postgres dbname=gdbpool_test"
  26. class gDBPoolTests( unittest.TestCase ):
  27. def setUp( self ):
  28. print "\n================================================================================\nRunning: %s\n================================================================================" % ( self._testMethodName )
  29. self.ipool = DBInteractionPool( dsn, pool_size = 16, do_log = True )
  30. def tearDown( self ):
  31. self.ipool.__del__()
  32. def test_connect_nonexisting_host_port( self ):
  33. with self.assertRaises( DBPoolConnectionException ):
  34. dsn = "host=127.0.0.1 port=6432 user=postgres dbname=gdbpool_test"
  35. fail_ipool = DBInteractionPool( dsn, pool_size = 1, do_log = False )
  36. def test_connect_nonexisting_db( self ):
  37. with self.assertRaises( DBPoolConnectionException ):
  38. dsn = "host=127.0.0.1 port=5432 user=postgres dbname=gdbpool_test_not_here"
  39. fail_ipool = DBInteractionPool( dsn, pool_size = 1, do_log = False )
  40. def _test_connect_pool_size_too_big( self ):
  41. pass
  42. def test_invalid_query( self ):
  43. """Test running an invalid SQL interactions on the DBInteractionPool"""
  44. sql = """
  45. ESLECT val1, count(id) FROM test_values GROUP BY val1 order by val1;
  46. """
  47. with self.assertRaises( DBInteractionException ):
  48. res = self.ipool.run( sql )
  49. print res.get()
  50. def test_select_ip_query( self ):
  51. """
  52. Test running a bunch of random queries as SQL interactions on the
  53. DBInteractionPool
  54. """
  55. sql1 = """
  56. SELECT val1, count(id) FROM test_values WHERE val2 = %s GROUP BY val1 order by val1;
  57. """
  58. sql2 = """
  59. SELECT pg_sleep( %s );
  60. """
  61. sql3 = """
  62. SELECT val1, count(id) FROM test_values GROUP BY val1 order by val1;
  63. """
  64. import random
  65. from time import time
  66. greenlets = []
  67. def tests( val2 ):
  68. stt = time()
  69. ran = random.random()
  70. if ran <= 0.33:
  71. sql = sql1
  72. res = self.ipool.run( sql, [ val2 ] )
  73. elif ran <= 0.66:
  74. sql = sql2
  75. res = self.ipool.run( sql, [ 0.5 ] )
  76. else:
  77. sql = sql3
  78. res = self.ipool.run( sql )
  79. r = res.get()
  80. et = time()
  81. logger.info( r[0] )
  82. for i in xrange( 1, 10 ):
  83. greenlets.append( gevent.spawn( tests, i ) )
  84. gevent.joinall( greenlets, timeout = 10 )
  85. def test_select_ip_interaction( self ):
  86. def interaction( conn ):
  87. curs = conn.cursor()
  88. sql = """
  89. SELECT val1, val2, count(id) FROM test_values GROUP BY val1, val2 order by val1, val2;
  90. """
  91. curs.execute( sql )
  92. res = curs.fetchall()
  93. curs.close()
  94. return res
  95. res = self.ipool.run( interaction ).get()
  96. # logger.info( res )
  97. def test_select_ip_interactions( self ):
  98. def interaction( conn ):
  99. curs = conn.cursor()
  100. sql = """
  101. SELECT val1, count(id) FROM test_values GROUP BY val1 order by val1;
  102. """
  103. curs.execute( sql )
  104. res = curs.fetchall()
  105. curs.close()
  106. return res
  107. def print_res( res ):
  108. logger.info( "?" )
  109. while 1:
  110. try:
  111. r = res.get().get_nowait()
  112. logger.info( r )
  113. break
  114. except gevent.Timeout:
  115. gevent.sleep( 0.01 )
  116. greenlets = []
  117. for i in xrange( 5 ):
  118. greenlets.append( gevent.spawn( self.ipool.run, interaction ) )
  119. greenlets[ i ].link( print_res )
  120. gevent.joinall( greenlets, timeout = 10, raise_error = True )
  121. gevent.sleep( 1 )
  122. def test_listen_on( self ):
  123. def run_insert( wait ):
  124. gevent.sleep( 0.1 )
  125. sql = """
  126. INSERT INTO test_values ( val1, val2 ) VALUES ( %s, %s );
  127. SELECT pg_sleep( %s );
  128. """
  129. val1 = random.randint( 1, 10 )
  130. val2 = random.randint( 1, 100 )
  131. sleep_time = wait + random.random()
  132. res = self.ipool.run( sql, [ val1, val2, sleep_time ] )
  133. r = res.get()
  134. res.get()
  135. def run_update( wait ):
  136. gevent.sleep( 0.1 )
  137. sql = """
  138. UPDATE test_values SET val1 = 11, val2 = %s WHERE id = %s;
  139. SELECT pg_sleep( %s );
  140. """
  141. val2 = random.randint( 1, 100 )
  142. id = random.randint( 1, 1000000 )
  143. sleep_time = wait + random.random()
  144. res = self.ipool.run( sql, [ val2, id, sleep_time ] )
  145. r = res.get()
  146. def listen():
  147. runtime = 0.0
  148. rq = Queue( maxsize = None )
  149. stop_event = gevent.event.Event()
  150. gevent.spawn( self.ipool.listen_on, result_queue = rq, channel_name = 'notify_test_values', cancel_event = stop_event )
  151. import time
  152. while 1:
  153. st = time.time()
  154. if runtime > 5.0:
  155. break
  156. try:
  157. notify = rq.get_nowait()
  158. print "NOTIFY", notify
  159. except QueueEmptyException:
  160. gevent.sleep( 0.001 )
  161. tt = time.time() - st
  162. runtime += tt
  163. stop_event.set()
  164. gevent.sleep( 1 )
  165. for i in xrange( 5 ):
  166. gevent.spawn( listen )
  167. greenlets = []
  168. for i in xrange( 5 ):
  169. greenlets.append( gevent.spawn( run_insert, i ) )
  170. greenlets.append( gevent.spawn( run_update, i ) )
  171. gevent.joinall( greenlets )
  172. gevent.sleep( 1 )
  173. def test_partial_run( self ):
  174. def interaction_part1( conn, cursor ):
  175. # cursor = conn.cursor()
  176. sql = """
  177. SELECT * FROM test_values WHERE id = 20000 FOR UPDATE;
  178. """
  179. cursor.execute( sql )
  180. res = cursor.fetchone()
  181. return res
  182. # setting commit=false i want to get back not only the result, but also
  183. # the connection and cursor (that might hold any locks) as well
  184. txn_part1 = self.ipool.run( interaction_part1, partial_txn = True ).get()
  185. print "result from partial txn 1:", txn_part1
  186. data = txn_part1[ 'result' ]
  187. conn = txn_part1[ 'connection' ]
  188. cursor = txn_part1[ 'cursor' ]
  189. #TODO: do this inside the test - not manually
  190. print "try running:\nUPDATE test_values SET val2 = val2 + 100 WHERE id = %s;\nand check that the result will be %s. the current value for val2 is %s" % ( data[ 'id'], data[ 'val2' ] + 200, data[ 'val2'] )
  191. gevent.sleep( 5 )
  192. def interaction_part2( conn, cursor, pk, val2 ):
  193. try:
  194. sql = """
  195. UPDATE test_values SET val2 = %s WHERE id = %s;
  196. """
  197. cursor.execute( sql, [ val2, pk ] )
  198. res = cursor.fetchall()
  199. conn.commit()
  200. except Exception, e:
  201. res = e
  202. conn.rollback()
  203. return res
  204. txn_part2 = self.ipool.run( interaction_part2, conn = conn, cursor = cursor, pk = data[ 'id'], val2 = data[ 'val2'] + 100 ).get()
  205. print "result from partial txn 2:", txn_part2
  206. test_suite = unittest.TestLoader().loadTestsFromTestCase( gDBPoolTests )
  207. unittest.TextTestRunner( verbosity = 1 ).run( test_suite )