/**
 *  Copyright 2010 Wallace Wadge
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.jolbox.bonecp;

import static org.easymock.EasyMock.anyLong;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.makeThreadSafe;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.lang.Thread.State;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;

import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.sql.DataSource;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;

import com.jolbox.bonecp.hooks.AcquireFailConfig;
import com.jolbox.bonecp.hooks.ConnectionHook;
import com.jolbox.bonecp.hooks.ConnectionState;
import com.jolbox.bonecp.hooks.CustomHook;

/**
 * @author wwadge
 *
 */
@SuppressWarnings("resource")
public class TestBoneCP {
	/** Class under test. */
	private BoneCP testClass;
	/** Mock handle. */
	private BoneCPConfig mockConfig;
	/** Mock handle. */
	private ConnectionPartition mockPartition;
	/** Mock handle. */
	private ScheduledExecutorService mockKeepAliveScheduler;
	/** Mock handle. */
	private ExecutorService mockConnectionsScheduler;
	/** Mock handle. */
	private LinkedBlockingQueue<ConnectionHandle> mockConnectionHandles;
	/** Mock handle. */
	private ConnectionHandle mockConnection;
	/** Mock handle. */
	private Lock mockLock;
	/** Mock handle. */
	private Logger mockLogger;
	/** Mock handle. */
	private DatabaseMetaData mockDatabaseMetadata;
	/** Mock handle. */
	private MockResultSet mockResultSet;
	/** Fake database driver. */
	private MockJDBCDriver driver;
	private BoneCP mockPool;

	/**
	 * Reset the mocks.
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws SQLException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws CloneNotSupportedException 
	 */
	@Before
	@SuppressWarnings({ "unchecked", "deprecation" })
	public void before() throws IllegalArgumentException, IllegalAccessException, SQLException, SecurityException, NoSuchFieldException, CloneNotSupportedException{
		driver = new MockJDBCDriver(new MockJDBCAnswer() {

			public Connection answer() throws SQLException {
				return new MockConnection();
			}
		});
		mockConfig = EasyMock.createNiceMock(BoneCPConfig.class);
		expect(mockConfig.clone()).andReturn(mockConfig).anyTimes();
		expect(mockConfig.getPartitionCount()).andReturn(2).anyTimes();
		expect(mockConfig.getMaxConnectionsPerPartition()).andReturn(1).anyTimes();
		expect(mockConfig.getMinConnectionsPerPartition()).andReturn(1).anyTimes();
		expect(mockConfig.getIdleConnectionTestPeriodInMinutes()).andReturn(0L).anyTimes();
		expect(mockConfig.getIdleMaxAgeInMinutes()).andReturn(1000L).anyTimes();
		expect(mockConfig.getUsername()).andReturn(CommonTestUtils.username).anyTimes();
		expect(mockConfig.getPassword()).andReturn(CommonTestUtils.password).anyTimes();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(mockConfig.getReleaseHelperThreads()).andReturn(1).once().andReturn(0).anyTimes();
		expect(mockConfig.getStatementReleaseHelperThreads()).andReturn(1).once().andReturn(0).anyTimes();
		expect(mockConfig.getInitSQL()).andReturn(CommonTestUtils.TEST_QUERY).anyTimes();
		expect(mockConfig.isCloseConnectionWatch()).andReturn(true).anyTimes();
		expect(mockConfig.isLogStatementsEnabled()).andReturn(true).anyTimes();
		expect(mockConfig.getConnectionTimeoutInMs()).andReturn(Long.MAX_VALUE).anyTimes();
		expect(mockConfig.getServiceOrder()).andReturn("LIFO").anyTimes();
		mockConfig.sanitize();
		expectLastCall().anyTimes();

		expect(mockConfig.getAcquireRetryDelayInMs()).andReturn(1000L).anyTimes();
		expect(mockConfig.getPoolName()).andReturn("poolName").anyTimes();
		expect(mockConfig.getPoolAvailabilityThreshold()).andReturn(20).anyTimes();
		
		replay(mockConfig);

		// once for no {statement, connection} release threads, once with release threads....
		testClass = new BoneCP(mockConfig);
		testClass = new BoneCP(mockConfig);

		Field field = testClass.getClass().getDeclaredField("partitions");
		field.setAccessible(true);
		ConnectionPartition[] partitions = (ConnectionPartition[]) field.get(testClass);


		// if all ok 
		assertEquals(2, partitions.length);
		// switch to our mock version now
		mockPartition = EasyMock.createNiceMock(ConnectionPartition.class);
		Array.set(field.get(testClass), 0, mockPartition);
		Array.set(field.get(testClass), 1, mockPartition);

		mockKeepAliveScheduler = EasyMock.createNiceMock(ScheduledExecutorService.class); 
		field = testClass.getClass().getDeclaredField("keepAliveScheduler");
		field.setAccessible(true);
		field.set(testClass, mockKeepAliveScheduler);

		field = testClass.getClass().getDeclaredField("connectionsScheduler");
		field.setAccessible(true);
		mockConnectionsScheduler = EasyMock.createNiceMock(ExecutorService.class);
		field.set(testClass, mockConnectionsScheduler);

		mockConnectionHandles = EasyMock.createNiceMock(LinkedBlockingQueue.class);
		mockConnection = EasyMock.createNiceMock(ConnectionHandle.class);
		mockLock = EasyMock.createNiceMock(Lock.class);
		mockLogger = TestUtils.mockLogger(testClass.getClass());
		makeThreadSafe(mockLogger, true);
		mockDatabaseMetadata = EasyMock.createNiceMock(DatabaseMetaData.class);
		mockResultSet = EasyMock.createNiceMock(MockResultSet.class);

		mockLogger.error((String)anyObject(), anyObject());
		expectLastCall().anyTimes();

		mockPool = EasyMock.createNiceMock(BoneCP.class);
		reset(mockConfig, mockKeepAliveScheduler, mockConnectionsScheduler, mockPartition, 
				mockConnectionHandles, mockConnection, mockLock);
	}

	/**
	 * @throws SQLException
	 */
	@After
	public void teardown() throws SQLException{
		driver.disable();
	}
	
	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#shutdown()}.
	 * @throws IllegalAccessException 
	 * @throws NoSuchFieldException 
	 * @throws IllegalArgumentException 
	 * @throws SecurityException 
	 */
	@Test
	public void testShutdown() throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
		testShutdownClose(true);
	}
	
	
	

	/** Tests obtaining internal connection.
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @throws InterruptedException 
	 */
	@Test
	public void testObtainInternalConnection() throws SQLException, ClassNotFoundException, InterruptedException{
		expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
		DataSource mockDataSourceBean = EasyMock.createNiceMock(DataSource.class);
		expect(this.mockConfig.getDatasourceBean()).andReturn(mockDataSourceBean).anyTimes();

		expect(mockConfig.getJdbcUrl()).andReturn("jdbc:mock:driver").anyTimes();
		mockConfig.setAcquireRetryDelayInMs(1);
		CustomHook testHook = new CustomHook();
		expect(mockConfig.getConnectionHook()).andReturn(testHook).anyTimes();
		// make it fail the first time and succeed the second time
		expect(this.mockPool.getDbIsDown()).andReturn(new AtomicBoolean()).anyTimes();
		expect(mockDataSourceBean.getConnection()).andThrow(new SQLException()).once().andReturn(null).once(); // a call in obtainRaw
		expect(mockConnection.getInternalConnection()).andReturn(new MockConnection()).anyTimes(); // a call in obtainRaw
		 
		replay(this.mockPool, mockConfig, mockDataSourceBean, mockConnection);
		this.testClass.obtainInternalConnection(mockConnection);
		// get counts on our hooks
		
		assertEquals(1, testHook.fail);
		assertEquals(1, testHook.acquire);
		
	
		// Test 2: Same thing but without the hooks
		reset(this.mockPool, mockDataSourceBean);
		expect(this.mockPool.getDbIsDown()).andReturn(new AtomicBoolean()).anyTimes();
		expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
		expect(mockDataSourceBean.getConnection()).andThrow(new SQLException()).once().andReturn(this.mockConnection).once();
		int count=1;
		mockConfig.setConnectionHook(null);
		
		replay(this.mockPool, mockDataSourceBean);
		assertEquals(this.mockConnection, this.testClass.obtainInternalConnection(mockConnection));
		
		
		// Test 3: Keep failing
		reset(this.mockPool, mockDataSourceBean);
		expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
		expect(mockDataSourceBean.getConnection()).andThrow(new SQLException()).anyTimes();
		replay(this.mockPool, mockDataSourceBean);
		count=99;
		mockConfig.setAcquireRetryAttempts(2);
		try{
			this.testClass.obtainInternalConnection(mockConnection);
			fail("Should have thrown an exception");
		} catch (SQLException e){
			// expected behaviour
		}
		 
		
		//	Test 4: Get signalled to interrupt fail delay
		count=99;
		mockConfig.setAcquireRetryAttempts(2);
		mockConfig.setAcquireRetryDelayInMs(7000);
		final Thread currentThread = Thread.currentThread();

		try{
			new Thread(new Runnable() {
				
//				@Override
				public void run() {
					while (!currentThread.getState().equals(State.TIMED_WAITING)){
						try {
							Thread.sleep(50);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					currentThread.interrupt();
				}
			}).start();
			this.testClass.obtainInternalConnection(mockConnection);
			fail("Should have thrown an exception");
		} catch (SQLException e){
			// expected behaviour
		}
		
		
		// Test: No acquire delay
				reset(this.mockPool, mockDataSourceBean, mockConfig);
				expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
				expect(mockDataSourceBean.getConnection()).andThrow(new SQLException()).anyTimes();
				mockConfig.setAcquireRetryAttempts(0);
				replay(this.mockPool, mockDataSourceBean, mockConfig);
				count=99;
				try{
					this.testClass.obtainInternalConnection(mockConnection);
					fail("Should have thrown an exception");
				} catch (SQLException e){
					// expected behaviour
				}
				
				
		mockConfig.setAcquireRetryDelayInMs(10);
		
		// Test: coverage
	/*	   PowerMock.mockStatic(Thread.class);

		  Thread.sleep(anyLong());
		  expectLastCall().andThrow(new InterruptedException()).anyTimes();
	        PowerMock.replay(Thread.class);
				reset(this.mockPool, mockDataSourceBean);
				expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
				expect(mockDataSourceBean.getConnection()).andThrow(new SQLException()).anyTimes();
				replay(this.mockPool, mockDataSourceBean);
				count=99;
				mockConfig.setAcquireRetryAttempts(2);
				try{
					this.testClass.obtainInternalConnection(mockConnection);
					fail("Should have thrown an exception");
				} catch (SQLException e){
					// expected behaviour
				}
				*/
		
		
	}
	

	/**
	 * @throws SQLException
	 */
	@Test
	public void testObtainInternalConnectionWithFailingInitSQL() throws SQLException {
		DataSource mockDataSourceBean = EasyMock.createNiceMock(DataSource.class);
		
		// Test: Fail in sending out init connection
		reset(mockConfig, mockDataSourceBean);
		reset(mockConnection);
		expect(mockConnection.getInternalConnection()).andReturn(new MockConnection()).anyTimes(); // a call in obtainRaw
		expect(this.mockConfig.getDatasourceBean()).andReturn(mockDataSourceBean).anyTimes();
		expect(mockConfig.getJdbcUrl()).andReturn("jdbc:mock:driver").anyTimes();
		expect(mockDataSourceBean.getConnection()).andReturn(mockConnection).once(); // a call in obtainRaw
		expect(mockConfig.getInitSQL()).andReturn("Foo").anyTimes();
		Statement mockStatement = EasyMock.createNiceMock(Statement.class);
		expect(mockConnection.createStatement()).andReturn(mockStatement).anyTimes();
		expect(mockStatement.execute((String)anyObject())).andThrow(new SQLException());
		mockConnection.close();
		expectLastCall().once(); // this is what we're testing
		replay(mockConfig, mockDataSourceBean, mockConnection, mockStatement);
		try {
			this.testClass.obtainInternalConnection(mockConnection);
			fail("Should have thrown an exception");
		} catch (SQLException e) {
			//normal case
			verify(mockConnection);
		}

	}

	/**
	 * Tests shutdown/close method.
	 * @param doShutdown call shutdown or call close
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	private void testShutdownClose(boolean doShutdown) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
		expect(mockConfig.isDeregisterDriverOnClose()).andReturn(false).anyTimes();
		expect(mockConfig.isDisableJMX()).andReturn(false).anyTimes();
		expect(mockConfig.getPoolName()).andReturn("foo").anyTimes();
		
		expect(mockKeepAliveScheduler.shutdownNow()).andReturn(null).once();
		expect(mockConnectionsScheduler.shutdownNow()).andReturn(null).once();

//		expect(mockConnectionHandles.poll()).andReturn(null).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
	

		ExecutorService mockReleaseHelper = EasyMock.createNiceMock(ExecutorService.class);


		replay(mockConfig, mockConnectionsScheduler,  mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockReleaseHelper);
		
		if (doShutdown){
			testClass.shutdown();
		} else {
			testClass.close();
		}
		verify(mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles);
	}


	/**
	 * Tests shutdown/close method coverage.
	 * @throws InterruptedException 
	 */
	@Test
	public void testShutdownCoverageClose() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InterruptedException {


		expect(mockConnectionsScheduler.awaitTermination(5, TimeUnit.SECONDS)).andThrow(new InterruptedException()).once();

		replay(mockConnectionsScheduler);
		try{
			testClass.shutdown();
		} catch(Exception e){
			// do nothing
		}
	}


	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#close()}.
	 * @throws IllegalAccessException 
	 * @throws NoSuchFieldException 
	 * @throws IllegalArgumentException 
	 * @throws SecurityException 
	 */ 
	@Test
	public void testClose() throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
		testClass.poolShuttingDown=false;
		testShutdownClose(false);
	}

//	/**
//	 * Test method.
//	 * @throws SQLException 
//	 */
//	@Test
//	public void testTerminateAllConnections() throws SQLException {
//	//	expect(mockConnectionHandles.poll()).andReturn(mockConnection).times(2).andReturn(null).once();
//		mockConnection.destroyConnection();
//		expectLastCall().once().andThrow(new SQLException()).once();
//		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
//		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
//		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
//		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
//		replay(mockRealConnection, mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockConnection);
//
//		// test.
//		testClass.connectionStrategy.terminateAllConnections();
//		verify(mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockConnection);
//	}


	/**
	 * Test method for.
	 * @throws SQLException 
	 */
	@Test
	public void testTerminateAllConnections2() throws SQLException {
		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);

		// same test but to cover the finally section
		reset(mockRealConnection, mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.getFreeConnections()).andThrow(new RuntimeException()).once();
		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		replay(mockRealConnection, mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockConnection);

		// test.
		try{
			testClass.connectionStrategy.terminateAllConnections();
			fail("Should throw exception");
		} catch (RuntimeException e){
			// do nothing
		}
		verify(mockConnectionsScheduler, mockKeepAliveScheduler, mockPartition, mockConnectionHandles, mockConnection);
	}
	/**
	 * Mostly for coverage.
	 */
	@Test
	public void testPostDestroyConnection(){
		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);

		reset(mockConnection);
		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		mockPartition.updateCreatedConnections(-1);
		expectLastCall().once();
		mockPartition.setUnableToCreateMoreTransactions(false);
		expectLastCall().once();
		ConnectionHook mockConnectionHook = EasyMock.createNiceMock(ConnectionHook.class);
		expect(mockConnection.getConnectionHook()).andReturn(mockConnectionHook).anyTimes();
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();

		mockConnectionHook.onDestroy(mockConnection);
		expectLastCall().once();
		replay(mockRealConnection, mockConnectionHook, mockConnection);
		testClass.postDestroyConnection(mockConnection);
		verify(mockConnectionHook, mockConnection);
	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getConnection()}.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetConnection() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		// Test 1: Get connection - normal state

		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		expect(mockConnectionHandles.poll()).andReturn(mockConnection).once();
		mockConnection.renewConnection();
		expectLastCall().once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		assertEquals(mockConnection, testClass.getConnection());
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getConnection()}.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetConnectionWithTimeout() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {

		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.poll()).andReturn(null).anyTimes();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		try{
			testClass.getConnection();
			fail("Should have thrown an exception");
		} catch (SQLException e){
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}


	/**
	 * Test method.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetRawConnectionViaDataSourceBean() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		DataSource mockDataSource = EasyMock.createNiceMock(DataSource.class);
		expect(mockDataSource.getConnection()).andReturn(mockConnection).once();
		expect(mockConfig.getJdbcUrl()).andReturn("").once().andReturn("jdbc:mock:foo").times(2);
		expect(mockConfig.getUsername()).andReturn(null).anyTimes();
		expect(mockConfig.getDatasourceBean()).andReturn(mockDataSource).once().andReturn(null).times(2);
		testClass.clientInfo = new Properties(); // coverage
	
		replay(mockConfig, mockDataSource);
		testClass.obtainRawInternalConnection();
	
		// 2nd path
		testClass.jvmMajorVersion=5;
		testClass.obtainRawInternalConnection();

		testClass.clientInfo = null;
		testClass.obtainRawInternalConnection(); // coverage
 
		verify(mockConfig, mockDataSource);
	}

	
	/**
	 * Test method.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetRawConnectionWithExternalAuth() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		testClass.config = mockConfig;
		expect(mockConfig.isExternalAuth()).andReturn(true).once();
		expect(mockConfig.getDatasourceBean()).andReturn(null).once();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).once();
		
		replay(mockConfig);
		assertNotNull(testClass.obtainRawInternalConnection());
		verify(mockConfig);

		// coverage
		reset(mockConfig);
		expect(mockConfig.isExternalAuth()).andReturn(false).once();
		expect(mockConfig.getDriverProperties()).andReturn(new Properties()).once();
		expect(mockConfig.getDatasourceBean()).andReturn(null).once();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).once();
		replay(mockConfig);
		assertNotNull(testClass.obtainRawInternalConnection());
		verify(mockConfig);
		
	}

	@Test
	public void testGetRawConnectionCoverage() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		testClass.config = mockConfig;
		testClass.driverInitialized = false;
		// coverage
		reset(mockConfig);
		expect(mockConfig.isExternalAuth()).andReturn(false).once();
		expect(mockConfig.getDriverProperties()).andReturn(new Properties()).once();
		expect(mockConfig.getDatasourceBean()).andReturn(null).once();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).once();
		replay(mockConfig);
		assertNotNull(testClass.obtainRawInternalConnection());
		verify(mockConfig);

		//  coverage of exception
		driver.setMockJDBCAnswer(new MockJDBCAnswer() {
			
			public Connection answer() throws SQLException {
				throw new SQLException();
			}
		});
		
		reset(mockConfig);
		expect(mockConfig.isExternalAuth()).andReturn(false).once();
		expect(mockConfig.getDriverProperties()).andReturn(new Properties()).once();
		expect(mockConfig.getDatasourceBean()).andReturn(null).once();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).once();
		replay(mockConfig);
		try{
			testClass.obtainRawInternalConnection();
		} catch (SQLException e){
			// nothing
		}
		driver.unregister();
		
	}


	/**
	 * Test method.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetConnectionViaDriverProperties() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		//		DataSource mockDataSource = EasyMock.createNiceMock(DataSource.class);
		//		expect(mockDataSource.getConnection()).andReturn(mockConnection).once();
		expect(mockConfig.getDriverProperties()).andReturn(new Properties()).once().andReturn(null).once();
		expect(mockConfig.getDatasourceBean()).andReturn(null).anyTimes();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(mockConfig.isExternalAuth()).andReturn(true).once().andReturn(false).once();
		replay(mockConfig);
		testClass.obtainRawInternalConnection();
		testClass.obtainRawInternalConnection(); // coverage
		verify(mockConfig);
		
	}

	/**
	 * Test method.
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 */
	@Test
	public void testGetConnectionViaDataSourceBeanWithPass() throws SQLException, InterruptedException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
		DataSource mockDataSource = EasyMock.createNiceMock(DataSource.class);
		expect(mockDataSource.getConnection((String)anyObject(), (String)anyObject())).andReturn(mockConnection).once();
		expect(mockConfig.getJdbcUrl()).andReturn("").once();
		expect(mockConfig.getUsername()).andReturn("username").once();
		expect(mockConfig.getPassword()).andReturn(null).once();
		expect(mockConfig.getDatasourceBean()).andReturn(mockDataSource).once();
		replay(mockConfig, mockDataSource);
		testClass.obtainRawInternalConnection();
		verify(mockConfig, mockDataSource);
	}
	/**
	 * Attempting to fetch a connection from a pool that is marked as being shut down should throw an exception
	 */
	@Test
	public void testGetConnectionOnShutdownPool() {
		// Test #8: 
		testClass.poolShuttingDown = true;
		try{
			testClass.getConnection();
			fail("Should have thrown an exception");
		} catch (SQLException e){
			// do nothing
		}
	}


	/** Like test 6, except we fake an unchecked exception to make sure our locks are released.
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	@Test
	public void testGetConnectionUncheckedExceptionTriggeredWhileWaiting()
	throws NoSuchFieldException, IllegalAccessException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(false).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getMaxConnections()).andReturn(0).anyTimes(); // cause a division by zero error
		replay(mockPartition, mockConnectionHandles, mockConnection, mockLock);
		try{
			testClass.getConnection();
			fail("Should have thrown an exception");
		} catch (Throwable t){
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection, mockLock);
	}


	/** If we hit our limit, we should signal for more connections to be created on the fly
	 * @throws SQLException
	 */
	@Test
	public void testGetConnectionLimitsHit() throws SQLException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockConfig.getPoolAvailabilityThreshold()).andReturn(0).anyTimes();
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(false).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getMaxConnections()).andReturn(10).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		BlockingQueue<Object> bq = new ArrayBlockingQueue<Object>(1);
		bq.add(new Object());
		expect(mockPartition.getPoolWatchThreadSignalQueue()).andReturn(bq);


		//		mockPartition.almostFullSignal();
		//		expectLastCall().once();

		expect(mockConnectionHandles.poll()).andReturn(mockConnection).once();
		mockConnection.renewConnection();
		expectLastCall().once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		testClass.getConnection();
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}


	/** Connection queues are starved of free connections. Should block and wait on one without spin-locking.
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws InterruptedException
	 * @throws SQLException
	 */
	@Test
	public void testGetConnectionConnectionQueueStarved()
	throws NoSuchFieldException, IllegalAccessException,
	InterruptedException, SQLException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andReturn(mockConnection).once();

		mockConnection.renewConnection();
		expectLastCall().once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		assertEquals(mockConnection, testClass.getConnection());
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}


	/** Simulate an interrupted exception elsewhere.
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws InterruptedException
	 */
	@Test
	public void testGetConnectionSimulateInterruptedException2()
	throws NoSuchFieldException, IllegalAccessException,
	InterruptedException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.poll()).andReturn(null).once();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andThrow(new InterruptedException()).once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		try{
			testClass.getConnection();
			fail("Should have throw an SQL Exception");
		} catch (SQLException e){
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}

	/** Simulate an interrupted exception elsewhere.
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws InterruptedException
	 */
	@Test
	public void testGetConnectionSimulateInterruptedExceptionWithNullReturn()
	throws NoSuchFieldException, IllegalAccessException,
	InterruptedException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		testClass.nullOnConnectionTimeout = true;
		expect(mockConnectionHandles.poll()).andReturn(null).once();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andThrow(new InterruptedException()).once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		try{
			assertNull(testClass.getConnection());
		} catch (SQLException e){
			fail("Should not have throw an SQL Exception");
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}
	

	/**	Test #3: Like test #2 but simulate an interrupted exception
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws InterruptedException
	 */
	@Test
	public void testGetConnectionSimulateInterruptedException()
	throws NoSuchFieldException, IllegalAccessException,
	InterruptedException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.getMaxConnections()).andReturn(100).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andThrow(new InterruptedException()).once();
		BlockingQueue<Object> bq = new ArrayBlockingQueue<Object>(1);
		bq.add(new Object());
		expect(mockPartition.getPoolWatchThreadSignalQueue()).andReturn(bq);
		replay(mockPartition, mockConnectionHandles, mockConnection);
		try{ 
			testClass.getConnection();
			fail("Should have throw an SQL Exception");
		} catch (SQLException e){
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}

	/**	Test #3: Like test #2 but simulate an interrupted exception
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws InterruptedException
	 */
	@Test
	public void testGetConnectionWithNullReturn()
	throws NoSuchFieldException, IllegalAccessException,
	InterruptedException {
		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.getMaxConnections()).andReturn(100).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andReturn(null).once();
		BlockingQueue<Object> bq = new ArrayBlockingQueue<Object>(1);
		bq.add(new Object());
		testClass.nullOnConnectionTimeout = true;
		expect(mockPartition.getPoolWatchThreadSignalQueue()).andReturn(bq);
		replay(mockPartition, mockConnectionHandles, mockConnection);
		try{ 
			assertNull(testClass.getConnection());
		} catch (SQLException e){
			fail("Should have throw an SQL Exception");
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}

	
	/** Get connection, not finding any available block to wait for one
	 * @throws InterruptedException
	 * @throws SQLException
	 */
	@Test
	public void testGetConnectionBlockOnUnavailable()
	throws InterruptedException, SQLException {

		reset(mockPartition, mockConnectionHandles, mockConnection);
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		expect(mockConnectionHandles.poll()).andReturn(null).once();
		expect(mockConnectionHandles.poll(Long.MAX_VALUE, TimeUnit.MILLISECONDS)).andReturn(mockConnection).once();

		mockConnection.renewConnection();
		expectLastCall().once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		assertEquals(mockConnection, testClass.getConnection());
		verify(mockPartition, mockConnectionHandles, mockConnection);
	}

	/** Test obtaining a connection asynchronously.
	 * @throws InterruptedException
	 * @throws ExecutionException
	 */
	@Test
	public void testGetAsyncConnection() throws InterruptedException, ExecutionException{
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		expect(mockConnectionHandles.poll()).andReturn(mockConnection).once();
		mockConnection.renewConnection();
		expectLastCall().once();

		replay(mockPartition, mockConnectionHandles, mockConnection);
		assertEquals(mockConnection, testClass.getAsyncConnection().get());
		verify(mockPartition, mockConnectionHandles, mockConnection);
 
	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#releaseConnection(java.sql.Connection)}.
	 * @throws SQLException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws InterruptedException 
	 */
	@Test
	public void testReleaseConnection() throws SQLException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InterruptedException {
		// Test #1: Test releasing connections directly
		expect(mockConnection.isPossiblyBroken()).andReturn(false).once();
		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();

		//		expect(mockConnectionHandles.offer(mockConnection)).andReturn(true).once();

		// should reset last connection use
		mockConnection.setConnectionLastUsedInMs(anyLong());
		expectLastCall().once();
		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
	
		replay(mockRealConnection, mockConnection, mockPartition, mockConnectionHandles);
		testClass.releaseConnection(mockConnection);
		verify(mockConnection, mockPartition, mockConnectionHandles);

		reset(mockConnection, mockPartition, mockConnectionHandles);
	}


	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#internalReleaseConnection(ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testInternalReleaseConnection() throws InterruptedException, SQLException {
		// Test #1: Test normal case where connection is considered to be not broken
		// should reset last connection use
		mockConnection.setConnectionLastUsedInMs(anyLong());
		expectLastCall().once();
		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
	
		//		expect(mockConnectionHandles.offer(mockConnection)).andReturn(false).anyTimes();
		expect(mockConnectionHandles.offer(mockConnection)).andReturn(true).once();

		replay(mockRealConnection, mockConnection,mockPartition, mockConnectionHandles);
		testClass.internalReleaseConnection(mockConnection);
		verify(mockConnection,mockPartition, mockConnectionHandles);
	}


	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#internalReleaseConnection(ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testInternalReleaseConnectionWhereConnectionIsBroken() throws InterruptedException, SQLException {
		// Test case where connection is broken
		mockConnection.logicallyClosed = new AtomicBoolean(false);
		reset(mockConnection,mockPartition, mockConnectionHandles);
		expect(mockConnection.isPossiblyBroken()).andReturn(true).once();
		expect(mockConfig.getConnectionTestStatement()).andReturn(null).once();
		// make it fail to return false
		expect(mockConnection.getMetaData()).andThrow(new SQLException()).once();

		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		// we're about to destroy this connection, so we can create new ones.
		mockPartition.setUnableToCreateMoreTransactions(false);
		expectLastCall().once();

		// break out from the next method, we're not interested in it
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();

		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();

		replay(mockPartition, mockConnection);
		testClass.internalReleaseConnection(mockConnection);
		verify(mockPartition, mockConnection);
	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#internalReleaseConnection(ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testInternalReleaseConnectionWhereConnectionIsMarkedBrokenButConnectionTestSucceeds() throws InterruptedException, SQLException {
		// Test case where connection is broken
		mockConnection.logicallyClosed = new AtomicBoolean(false);
		reset(mockConnection,mockPartition, mockConnectionHandles);
		expect(mockConnection.isPossiblyBroken()).andReturn(true).once();
		expect(mockConfig.getConnectionTestStatement()).andReturn(null).once();
		expect(mockConnection.getMetaData()).andReturn(EasyMock.createNiceMock(DatabaseMetaData.class)).once();

		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();


		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();

				Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();

		replay(mockPartition, mockConnection);
		testClass.internalReleaseConnection(mockConnection);
		verify(mockPartition, mockConnection);
	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#internalReleaseConnection(ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testInternalReleaseConnectionWherePoolisShuttingDown() throws InterruptedException, SQLException {
		mockConnection.logicallyClosed = new AtomicBoolean(false);
		reset(mockConnection,mockPartition, mockConnectionHandles);

		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();

		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
		mockConnection.internalClose();
		expectLastCall().once();

		replay(mockPartition, mockConnection);
		testClass.poolShuttingDown = true;
		testClass.internalReleaseConnection(mockConnection);
		testClass.poolShuttingDown = false;
		verify(mockPartition, mockConnection);
	}

    /**
     * Test method for {@link com.jolbox.bonecp.BoneCP#internalReleaseConnection(ConnectionHandle)}.
     *
     * @throws InterruptedException
     * @throws SQLException
     */
    @Test
    public void testInternalReleaseConnectionWhereConnectionIsExpired() throws InterruptedException, SQLException {
        // Test case where connection is expired
        reset(mockConnection, mockPartition, mockConnectionHandles);

        Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
        expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();

        // return a partition
        expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
        // break out from this method, we're not interested in it
        expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(true).once();

        expect(mockConnection.isExpired()).andReturn(true).anyTimes();
        mockConnection.internalClose();
        expectLastCall();

        replay(mockPartition, mockConnection, mockRealConnection);
        testClass.internalReleaseConnection(mockConnection);
        verify(mockPartition, mockConnection, mockRealConnection);
    }

    /**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#putConnectionBackInPartition(com.jolbox.bonecp.ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testPutConnectionBackInPartition() throws InterruptedException, SQLException {
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		Connection mockRealConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockConnection.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
	
		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		expect(mockConnectionHandles.offer(mockConnection)).andReturn(true).once();
		replay(mockRealConnection, mockPartition, mockConnectionHandles, mockConnection);
		testClass.putConnectionBackInPartition(mockConnection);
		// FIXME
		//		assertEquals(2, ai.get());
		verify(mockPartition, mockConnectionHandles);

	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#putConnectionBackInPartition(com.jolbox.bonecp.ConnectionHandle)}.
	 * @throws InterruptedException 
	 * @throws SQLException 
	 */
	@Test
	public void testPutConnectionBackInPartitionWithResetConnectionOnClose() throws InterruptedException, SQLException {
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();

		expect(mockConnection.getOriginatingPartition()).andReturn(mockPartition).anyTimes();
		expect(mockConnectionHandles.offer(mockConnection)).andReturn(true).once();
		expect(mockConnection.isTxResolved()).andReturn(false).once();
		Connection mockInternalConnection = EasyMock.createNiceMock(Connection.class);
		expect(mockInternalConnection.getAutoCommit()).andReturn(false).once();
		expect(mockConnection.getInternalConnection()).andReturn(mockInternalConnection).anyTimes();
		mockInternalConnection.rollback();
		mockInternalConnection.setAutoCommit(true);
		replay(mockInternalConnection, mockPartition, mockConnectionHandles, mockConnection);
		testClass.resetConnectionOnClose = true;
		testClass.putConnectionBackInPartition(mockConnection);
		verify(mockPartition, mockConnectionHandles);

	}

	
	/**
	 * Test method for com.jolbox.bonecp.BoneCP isConnectionHandleAlive.
	 * @throws SQLException 
	 */
	@Test
	public void testIsConnectionHandleAlive() throws SQLException {
		// Test 1: Normal case (+ without connection test statement)
		expect(mockConfig.getConnectionTestStatement()).andReturn(null).once();
		mockConnection.logicallyClosed=new AtomicBoolean();
		expect(mockConnection.getMetaData()).andReturn(mockDatabaseMetadata).once();
		expect(mockDatabaseMetadata.getTables((String)anyObject(), (String)anyObject(), (String)anyObject(), (String[])anyObject())).andReturn(mockResultSet).once();
		mockResultSet.close();
		expectLastCall().once();

		replay(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);
		assertTrue(testClass.isConnectionHandleAlive(mockConnection));
		verify(mockConfig, mockConnection, mockResultSet,mockDatabaseMetadata);
	}
	
	/**
	 * Test method for com.jolbox.bonecp.BoneCP isConnectionHandleAlive.
	 * @throws SQLException 
	 */
	@Test
	public void testIsConnectionHandleAliveTriggerException() throws SQLException {
	
		// Test 2: Same test as testIsConnectionHandleAlive but triggers an exception
		reset(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);
		expect(mockConfig.getConnectionTestStatement()).andReturn(null).once();
		expect(mockConnection.getMetaData()).andThrow(new SQLException()).once();
		mockConnection.logicallyClosed = new AtomicBoolean(false);

		replay(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);
		assertFalse(testClass.isConnectionHandleAlive(mockConnection));
		verify(mockConfig, mockConnection, mockResultSet,mockDatabaseMetadata);

	}
	
	/**
	 * Test method for com.jolbox.bonecp.BoneCP isConnectionHandleAlive.
	 * @throws SQLException 
	 */
	@Test
	public void testIsConnectionHandleAliveNormalCaseWithConnectionTestStatement() throws SQLException {
	
		// Test 3: Normal case (+ with connection test statement)
		reset(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);

		Statement mockStatement = EasyMock.createNiceMock(Statement.class);
		expect(mockConfig.getConnectionTestStatement()).andReturn("whatever").once();
		expect(mockConnection.createStatement()).andReturn(mockStatement).once();
		expect(mockStatement.execute((String)anyObject())).andReturn(true).once();
		//		mockResultSet.close();
		//		expectLastCall().once();

		mockConnection.logicallyClosed = new AtomicBoolean(true);
		replay(mockConfig, mockConnection, mockDatabaseMetadata,mockStatement);
		assertTrue(testClass.isConnectionHandleAlive(mockConnection));
		verify(mockConfig, mockConnection,mockDatabaseMetadata, mockStatement);
	}
	/**
	 * Test method for com.jolbox.bonecp.BoneCP isConnectionHandleAlive.
	 * @throws SQLException 
	 */
	@Test
	public void testIsConnectionHandleAliveNormalCaseWithConnectionTestTriggerException() throws SQLException {
		Statement mockStatement = EasyMock.createNiceMock(Statement.class);
		// Test 4: Same like test testIsConnectionHandleAliveNormalCaseWithConnectionTestStatement but triggers exception
		reset(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);

		expect(mockConfig.getConnectionTestStatement()).andReturn("whatever").once();
		expect(mockConnection.createStatement()).andReturn(mockStatement).once();
		expect(mockStatement.execute((String)anyObject())).andThrow(new RuntimeException()).once();
		mockStatement.close();
		expectLastCall().once();

		replay(mockConfig, mockConnection, mockDatabaseMetadata,mockStatement, mockResultSet);
		//		assertFalse(testClass.isConnectionHandleAlive(mockConnection));
		try{
			mockConnection.logicallyClosed = new AtomicBoolean(true);// (code coverage)
			testClass.isConnectionHandleAlive(mockConnection);
			fail("Should have thrown an exception");
		} catch (RuntimeException e){
			// do nothing 
		}
		verify(mockConfig, mockConnection, mockResultSet,mockDatabaseMetadata, mockStatement);
	}
	
	/**
	 * Test method for com.jolbox.bonecp.BoneCP isConnectionHandleAlive.
	 * @throws SQLException 
	 */
	@Test
	public void testIsConnectionHandleAliveNormalCaseWithConnectionTestTriggerExceptionInFinally() throws SQLException {
		Statement mockStatement = EasyMock.createNiceMock(Statement.class);
		
		// Test 5: Same like test testIsConnectionHandleAliveNormalCaseWithConnectionTestTriggerException
		// but triggers exception in finally block
		reset(mockConfig, mockConnection, mockDatabaseMetadata, mockResultSet);

		expect(mockConfig.getConnectionTestStatement()).andReturn("whatever").once();
		expect(mockConnection.createStatement()).andReturn(mockStatement).once();
		expect(mockStatement.execute((String)anyObject())).andThrow(new RuntimeException()).once();
		mockStatement.close();
		expectLastCall().andThrow(new SQLException()).once();

		mockConnection.logicallyClosed = new AtomicBoolean(false);
		replay(mockConfig, mockConnection, mockDatabaseMetadata,mockStatement, mockResultSet);
		try{
			testClass.isConnectionHandleAlive(mockConnection);
			fail("Should have thrown an exception");
		} catch (RuntimeException e){
			// do nothing
		}
		verify(mockConfig, mockConnection, mockResultSet,mockDatabaseMetadata, mockStatement);

	}

	/**
	 * Test method for maybeSignalForMoreConnections(com.jolbox.bonecp.ConnectionPartition)}.
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 * @throws InvocationTargetException 
	 */
	@Test
	public void testMaybeSignalForMoreConnections() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(false).once();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		//		expect(mockConnectionHandles.size()).andReturn(1).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(1).anyTimes();
		expect(mockPartition.getMaxConnections()).andReturn(10).anyTimes();
		BlockingQueue<Object> bq = new ArrayBlockingQueue<Object>(1);
		expect(mockPartition.getPoolWatchThreadSignalQueue()).andReturn(bq).anyTimes();
		//		mockPartition.lockAlmostFullLock();
		//		expectLastCall().once();
		//		mockPartition.almostFullSignal();
		//		expectLastCall().once();
		//		mockPartition.unlockAlmostFullLock();
		//		expectLastCall().once();
		replay(mockPartition, mockConnectionHandles);
		Method method = testClass.getClass().getDeclaredMethod("maybeSignalForMoreConnections", ConnectionPartition.class);
		method.setAccessible(true);
		method.invoke(testClass, new Object[]{mockPartition});
		verify(mockPartition, mockConnectionHandles);
	}
	
	/**
	 * Test method for maybeSignalForMoreConnections(com.jolbox.bonecp.ConnectionPartition)}.
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 * @throws InvocationTargetException 
	 */
	@Test
	public void testMaybeSignalForMoreConnectionsWithException() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		BlockingQueue<Object> bq = new ArrayBlockingQueue<Object>(1);
		Method method = testClass.getClass().getDeclaredMethod("maybeSignalForMoreConnections", ConnectionPartition.class);
		method.setAccessible(true);
		
		// Test 2, same test but fake an exception
		reset(mockPartition, mockConnectionHandles);
		expect(mockPartition.getPoolWatchThreadSignalQueue()).andReturn(bq).anyTimes();
		expect(mockPartition.isUnableToCreateMoreTransactions()).andReturn(false).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockConnectionHandles.size()).andReturn(1).anyTimes();
		expect(mockPartition.getMaxConnections()).andReturn(10).anyTimes();
		//		mockPartition.lockAlmostFullLock();
		//		expectLastCall().once();
		//		mockPartition.almostFullSignal();
		//		expectLastCall().andThrow(new RuntimeException()).once();
		//		mockPartition.unlockAlmostFullLock();
		//		expectLastCall().once(); 
		replay(mockPartition, mockConnectionHandles);
		try{
			method.invoke(testClass, new Object[]{mockPartition});
			fail("Should have thrown an exception");
		} catch (Throwable t){
			// do nothing
		}
		verify(mockPartition, mockConnectionHandles);

	}
	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getTotalLeased()}.
	 */
	@Test
	public void testGetTotalLeased() {
		expect(mockPartition.getCreatedConnections()).andReturn(5).anyTimes();
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(3).anyTimes();
		replay(mockPartition, mockConnectionHandles);
		ConnectionPartition cp = testClass.partitions[1]; 
		assertEquals(4, testClass.getTotalLeased());
		testClass.partitions[1] = null; // test safety
		assertEquals(2, testClass.getTotalLeased());
		verify(mockPartition, mockConnectionHandles);

	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getTotalFree()}.
	 */
	@Test
	public void testGetTotalFree() {
		expect(mockPartition.getFreeConnections()).andReturn(mockConnectionHandles).anyTimes();
		expect(mockPartition.getAvailableConnections()).andReturn(3).anyTimes();

		// expect(mockConnectionHandles.size()).andReturn(3).anyTimes();
		replay(mockPartition, mockConnectionHandles);
		
		assertEquals(6, testClass.getTotalFree());
		testClass.partitions[1] = null; // test safety
		assertEquals(3, testClass.getTotalFree());
		verify(mockPartition, mockConnectionHandles);

	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getTotalCreatedConnections()}.
	 */
	@Test
	public void testGetTotalCreatedConnections() {
		expect(mockPartition.getCreatedConnections()).andReturn(5).anyTimes();
		replay(mockPartition);
		assertEquals(10, testClass.getTotalCreatedConnections());
		testClass.partitions[1] = null; // test safety
		assertEquals(5, testClass.getTotalCreatedConnections());
		
		verify(mockPartition);

	}

	/**
	 * Test method for {@link com.jolbox.bonecp.BoneCP#getConfig()}.
	 */
	@Test
	public void testGetConfig() {
		assertEquals(mockConfig, testClass.getConfig());
	}

	/**
	 * Coverage.
	 * @throws SQLException 
	 * @throws CloneNotSupportedException 
	 * @throws ClassNotFoundException 
	 */
	@SuppressWarnings("resource")
	@Test
	public void testCoverage() throws SQLException, CloneNotSupportedException, ClassNotFoundException{
		BoneCPConfig config = EasyMock.createNiceMock(BoneCPConfig.class);
		expect(config.isLazyInit()).andReturn(true).anyTimes();
		expect(config.isDisableJMX()).andReturn(true).anyTimes();

		expect(config.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(config.getMinConnectionsPerPartition()).andReturn(2).anyTimes();
		expect(config.getMaxConnectionsPerPartition()).andReturn(20).anyTimes();
		expect(config.getPartitionCount()).andReturn(1).anyTimes();
		expect(config.getServiceOrder()).andReturn("LIFO").anyTimes();
		expect(config.getMaxConnectionAgeInSeconds()).andReturn(100000L).anyTimes();
		expect(config.clone()).andThrow(new CloneNotSupportedException()).once().andReturn(config).once();
		replay(config);
		try{
			new BoneCP(config);
			fail("Should throw exception because clone throws a fake exception");
		} catch(SQLException e){
			// nothing
		}
		

		new BoneCP(config);


	}

	@Test
	public void testIdleThreadsStartCoverage() throws SQLException, CloneNotSupportedException, ClassNotFoundException{
		BoneCPConfig config = EasyMock.createNiceMock(BoneCPConfig.class);
		expect(config.isLazyInit()).andReturn(true).anyTimes();
		expect(config.isDisableJMX()).andReturn(true).anyTimes();

		expect(config.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(config.getMinConnectionsPerPartition()).andReturn(2).anyTimes();
		expect(config.getMaxConnectionsPerPartition()).andReturn(20).anyTimes();
		expect(config.getPartitionCount()).andReturn(1).anyTimes();
		expect(config.getServiceOrder()).andReturn("LIFO").anyTimes();
		expect(config.getMaxConnectionAgeInSeconds()).andReturn(100000L).anyTimes();
		expect(config.clone()).andReturn(config).anyTimes();
		expect(config.getIdleConnectionTestPeriod((TimeUnit)anyObject())).andReturn(5L).times(2).andReturn(0L).times(2);
		expect(config.getIdleMaxAge(TimeUnit.SECONDS)).andReturn(0L).times(2).andReturn(4L).times(2);
		reset(mockKeepAliveScheduler);
		replay(config, mockKeepAliveScheduler);
		
		BoneCP pool = new BoneCP(config); // test idleConnectionTestPeriod > 0. max age = 0
//		assertEquals(1, pool.keepAliveScheduler.shutdownNow().size());
	
		pool = new BoneCP(config); // test idleConnectionTestPeriod = 0. max age > 0
//		assertEquals(1, pool.keepAliveScheduler.shutdownNow().size());
		
		
		verify(config, mockKeepAliveScheduler);
	}


	@Test
	public void testIdleThreadsStart2Coverage() throws SQLException, CloneNotSupportedException, ClassNotFoundException{
		BoneCPConfig config = EasyMock.createNiceMock(BoneCPConfig.class);
		expect(config.isLazyInit()).andReturn(true).anyTimes();
		expect(config.isDisableJMX()).andReturn(true).anyTimes();

		expect(config.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(config.getMinConnectionsPerPartition()).andReturn(2).anyTimes();
		expect(config.getMaxConnectionsPerPartition()).andReturn(20).anyTimes();
		expect(config.getPartitionCount()).andReturn(1).anyTimes();
		expect(config.getServiceOrder()).andReturn("LIFO").anyTimes();
		expect(config.getMaxConnectionAgeInSeconds()).andReturn(100000L).anyTimes();
		expect(config.clone()).andReturn(config).anyTimes();
		expect(config.getIdleConnectionTestPeriod((TimeUnit)anyObject())).andReturn(5L).anyTimes();
		expect(config.getIdleMaxAge(TimeUnit.SECONDS)).andReturn(4L).anyTimes();
		reset(mockKeepAliveScheduler);
		replay(config, mockKeepAliveScheduler);
		
		
		BoneCP pool = new BoneCP(config); // test idleConnectionTestPeriod > 0. max age > 0
		assertEquals(1, pool.keepAliveScheduler.shutdownNow().size());

		verify(config, mockKeepAliveScheduler);
	}

	
	@Test
	public void testDetectJVMVersion() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, CloneNotSupportedException, SQLException{
	//	AtomicInteger ai = new AtomicInteger();
		
//		final Connection connection = EasyMock.createNiceMock(Connection.class);
		
		expect(mockConfig.clone()).andReturn(mockConfig).anyTimes();
		expect(mockConfig.getPartitionCount()).andReturn(2).anyTimes();
		expect(mockConfig.getMaxConnectionsPerPartition()).andReturn(1).anyTimes();
		expect(mockConfig.getMinConnectionsPerPartition()).andReturn(1).anyTimes();
		expect(mockConfig.getIdleConnectionTestPeriodInMinutes()).andReturn(0L).anyTimes();
		expect(mockConfig.getIdleMaxAgeInMinutes()).andReturn(1000L).anyTimes();
		expect(mockConfig.getUsername()).andReturn(CommonTestUtils.username).anyTimes();
		expect(mockConfig.getPassword()).andReturn(CommonTestUtils.password).anyTimes();
		expect(mockConfig.getJdbcUrl()).andReturn(CommonTestUtils.url).anyTimes();
		expect(mockConfig.getInitSQL()).andReturn(CommonTestUtils.TEST_QUERY).anyTimes();
		expect(mockConfig.isCloseConnectionWatch()).andReturn(true).anyTimes();
		expect(mockConfig.isLogStatementsEnabled()).andReturn(true).anyTimes();
		expect(mockConfig.getConnectionTimeoutInMs()).andReturn(Long.MAX_VALUE).anyTimes();
		expect(mockConfig.getServiceOrder()).andReturn("LIFO").anyTimes();
		expect(mockConfig.getClassLoader()).andReturn(this.getClass().getClassLoader()).anyTimes();
		mockConfig.sanitize();
		expectLastCall().anyTimes();

		expect(mockConfig.getAcquireRetryDelayInMs()).andReturn(1000L).anyTimes();
		expect(mockConfig.getPoolName()).andReturn("poolName").anyTimes();
		expect(mockConfig.getPoolAvailabilityThreshold()).andReturn(20).anyTimes();
		
		replay(mockConfig);

		BoneCP.connectionClass = FakeTestClassJDK5.class.getCanonicalName();
		BoneCP pool = new BoneCP(mockConfig);
		assertEquals(5, pool.jvmMajorVersion);
		
		BoneCP.connectionClass = FakeTestClassJDK6.class.getCanonicalName();
		pool = new BoneCP(mockConfig);
		assertEquals(6, pool.jvmMajorVersion);

		BoneCP.connectionClass = FakeTestClassJDK7.class.getCanonicalName();
		pool = new BoneCP(mockConfig);
		assertEquals(7, pool.jvmMajorVersion);

	}
	/** JMX setup test
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanRegistrationException
	 * @throws NotCompliantMBeanException
	 * @throws InstanceNotFoundException 
	 */
	@Test
	public void testJMXRegister() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InstanceNotFoundException{
		MBeanServer mockMbs = EasyMock.createNiceMock(MBeanServer.class);
		Field field = testClass.getClass().getDeclaredField("mbs");
		field.setAccessible(true);
		field.set(testClass, mockMbs);
		expect(mockConfig.getPoolName()).andReturn(null).anyTimes(); 
		ObjectInstance mockInstance = EasyMock.createNiceMock(ObjectInstance.class);
		expect(mockMbs.isRegistered((ObjectName)anyObject())).andReturn(false).anyTimes();
		expect(mockMbs.registerMBean(anyObject(), (ObjectName)anyObject())).andReturn(mockInstance).once().andThrow(new InstanceAlreadyExistsException()).once();
		replay(mockMbs, mockInstance, mockConfig);
		testClass.registerUnregisterJMX(true);
		verify(mockMbs);
	}
	
	@Test
	public void testJMXUnregisterNoName() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InstanceNotFoundException{
		MBeanServer mockMbs = EasyMock.createNiceMock(MBeanServer.class);
		Field field = testClass.getClass().getDeclaredField("mbs");
		field.setAccessible(true);
		field.set(testClass, mockMbs);
		expect(mockConfig.getPoolName()).andReturn(null).anyTimes(); 
		ObjectInstance mockInstance = EasyMock.createNiceMock(ObjectInstance.class);
	
		reset(mockMbs, mockInstance, mockConfig);
		expect(mockMbs.isRegistered((ObjectName)anyObject())).andReturn(true).anyTimes();
		mockMbs.unregisterMBean((ObjectName)anyObject());
		replay(mockMbs, mockInstance, mockConfig);
		testClass.registerUnregisterJMX(false);
		verify(mockMbs);
	}

	/** Test for different pool names.
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanRegistrationException
	 * @throws NotCompliantMBeanException
	 */
	@Test
	public void testJMXRegisterWithName() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException{
		MBeanServer mockMbs = EasyMock.createNiceMock(MBeanServer.class);
		Field field = testClass.getClass().getDeclaredField("mbs");
		field.setAccessible(true);
		field.set(testClass, mockMbs);
		expect(mockConfig.getPoolName()).andReturn("poolName").anyTimes();
		ObjectInstance mockInstance = EasyMock.createNiceMock(ObjectInstance.class);
		expect(mockMbs.isRegistered((ObjectName)anyObject())).andReturn(false).anyTimes();
		expect(mockMbs.registerMBean(anyObject(), (ObjectName)anyObject())).andReturn(mockInstance).once().andThrow(new InstanceAlreadyExistsException()).once();
		replay(mockMbs, mockInstance, mockConfig);
		testClass.registerUnregisterJMX(true);
		verify(mockMbs);
	}

	/** Test for different pool names.
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanRegistrationException
	 * @throws NotCompliantMBeanException
	 * @throws InstanceNotFoundException 
	 */
	@Test
	public void testJMXUnRegisterWithName() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InstanceNotFoundException{
		MBeanServer mockMbs = EasyMock.createNiceMock(MBeanServer.class);
		Field field = testClass.getClass().getDeclaredField("mbs");
		field.setAccessible(true);
		field.set(testClass, mockMbs);
	
		field = testClass.getClass().getDeclaredField("config");
		field.setAccessible(true);
		field.set(testClass, mockConfig);
	
		expect(mockConfig.getPoolName()).andReturn("poolName").anyTimes();
		ObjectInstance mockInstance = EasyMock.createNiceMock(ObjectInstance.class);
		expect(mockMbs.isRegistered((ObjectName)anyObject())).andReturn(true).anyTimes();
		mockMbs.unregisterMBean((ObjectName)anyObject());
		expectLastCall().times(2).andThrow(new InstanceNotFoundException()).once();
		replay(mockMbs, mockInstance, mockConfig);
		testClass.registerUnregisterJMX(false);
		testClass.registerUnregisterJMX(false); // should trigger an exception

		verify(mockMbs);
	}

	/**
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	@Test
	public void testCaptureException() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		Method method = testClass.getClass().getDeclaredMethod("captureStackTrace",  String.class);
		method.setAccessible(true);
		try{
			method.invoke(testClass);
			fail("Should throw an exception");
		} catch (Exception e){
			// do nothing
		}
	}

	/** Tests for watch connection.
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchFieldException
	 */
	@Test
	public void testWatchConnection() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchFieldException{
		Field field = testClass.getClass().getDeclaredField("closeConnectionExecutor");
		field.setAccessible(true);

		ExecutorService mockExecutor = EasyMock.createNiceMock(ExecutorService.class);
		field.set(testClass, mockExecutor);


		Method method = testClass.getClass().getDeclaredMethod("watchConnection", ConnectionHandle.class);
		method.setAccessible(true);
		expect(mockExecutor.submit((CloseThreadMonitor) anyObject())).andReturn(null).once();
		replay(mockExecutor);
		method.invoke(testClass, mockConnection);
		verify(mockExecutor);

		// Test #2: Code coverage
		method.invoke(testClass, new Object[]{null});
	}
	
	/**
	 * 
	 */
	@Test
	public void testUnregisterDriver() {
		expect(mockConfig.isDeregisterDriverOnClose()).andReturn(true).anyTimes();
		expect(mockConfig.getJdbcUrl()).andReturn("jdbc:mock").anyTimes();

		replay(mockConfig);
		try {
			Assert.assertNotNull(DriverManager.getDriver("jdbc:mock"));
		} catch (SQLException e1) {
			fail("SQLException thrown");
		}
		testClass.unregisterDriver();
		
		testClass.unregisterDriver(); // should log a message (nothing to unregister)
	}


}