blob: 525dd2db0adca08e0dfe7b5bb13c49d754014fdf [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* 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 android.database.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
public class DatabaseConnectionPoolTest extends AndroidTestCase {
private static final String TAG = "DatabaseConnectionPoolTest";
private static final int MAX_CONN = 5;
private static final String TEST_SQL = "select * from test where i = ? AND j = 1";
private static final String[] TEST_SQLS = new String[] {
TEST_SQL, TEST_SQL + 1, TEST_SQL + 2, TEST_SQL + 3, TEST_SQL + 4
};
private SQLiteDatabase mDatabase;
private File mDatabaseFile;
private DatabaseConnectionPool mTestPool;
@Override
protected void setUp() throws Exception {
super.setUp();
File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
mDatabaseFile = new File(dbDir, "database_test.db");
if (mDatabaseFile.exists()) {
mDatabaseFile.delete();
}
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
assertNotNull(mDatabase);
mDatabase.execSQL("create table test (i int, j int);");
mTestPool = new DatabaseConnectionPool(mDatabase);
assertNotNull(mTestPool);
}
@Override
protected void tearDown() throws Exception {
mTestPool.close();
mDatabase.close();
mDatabaseFile.delete();
super.tearDown();
}
@SmallTest
public void testGetAndRelease() {
mTestPool.setMaxPoolSize(MAX_CONN);
// connections should be lazily created.
assertEquals(0, mTestPool.getSize());
// MAX pool size should be set to MAX_CONN
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// get a connection
SQLiteDatabase db = mTestPool.get(TEST_SQL);
// pool size should be one - since only one should be allocated for the above get()
assertEquals(1, mTestPool.getSize());
assertEquals(mDatabase, db.mParentConnObj);
// no free connections should be available
assertEquals(0, mTestPool.getFreePoolSize());
assertFalse(mTestPool.isDatabaseObjFree(db));
// release the connection
mTestPool.release(db);
assertEquals(1, mTestPool.getFreePoolSize());
assertEquals(1, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
assertTrue(mTestPool.isDatabaseObjFree(db));
// release the same object again and expect IllegalStateException
try {
mTestPool.release(db);
fail("illegalStateException expected");
} catch (IllegalStateException e ) {
// expected.
}
}
/**
* get all connections from the pool and ask for one more.
* should get one of the connections already got so far.
*/
@SmallTest
public void testGetAllConnAndOneMore() {
mTestPool.setMaxPoolSize(MAX_CONN);
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
ArrayList<SQLiteDatabase> dbObjs = new ArrayList<SQLiteDatabase>();
for (int i = 0; i < MAX_CONN; i++) {
SQLiteDatabase db = mTestPool.get(TEST_SQL);
assertFalse(dbObjs.contains(db));
dbObjs.add(db);
assertEquals(mDatabase, db.mParentConnObj);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// pool is maxed out and no free connections. ask for one more connection
SQLiteDatabase db1 = mTestPool.get(TEST_SQL);
// make sure db1 is one of the existing ones
assertTrue(dbObjs.contains(db1));
// pool size should remain at MAX_CONN
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release db1 but since it is allocated 2 times, it should still remain 'busy'
mTestPool.release(db1);
assertFalse(mTestPool.isDatabaseObjFree(db1));
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release all connections
for (int i = 0; i < MAX_CONN; i++) {
mTestPool.release(dbObjs.get(i));
}
// all objects in the pool should be freed now
assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
}
/**
* same as above except that each connection has different SQL statement associated with it.
*/
@SmallTest
public void testConnRetrievalForPreviouslySeenSql() {
mTestPool.setMaxPoolSize(MAX_CONN);
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
HashMap<String, SQLiteDatabase> dbObjs = new HashMap<String, SQLiteDatabase>();
for (int i = 0; i < MAX_CONN; i++) {
SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
assertFalse(dbObjs.values().contains(db));
dbObjs.put(TEST_SQLS[i], db);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// pool is maxed out and no free connections. ask for one more connection
// use a previously seen SQL statement
String testSql = TEST_SQLS[MAX_CONN - 1];
SQLiteDatabase db1 = mTestPool.get(testSql);
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// make sure db1 is one of the existing ones
assertTrue(dbObjs.values().contains(db1));
assertEquals(db1, dbObjs.get(testSql));
// do the same again
SQLiteDatabase db2 = mTestPool.get(testSql);
// make sure db1 is one of the existing ones
assertEquals(db2, dbObjs.get(testSql));
// pool size should remain at MAX_CONN
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release db1 but since the same connection is allocated 3 times,
// it should still remain 'busy'
mTestPool.release(db1);
assertFalse(mTestPool.isDatabaseObjFree(dbObjs.get(testSql)));
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release db2 but since the same connection is allocated 2 times,
// it should still remain 'busy'
mTestPool.release(db2);
assertFalse(mTestPool.isDatabaseObjFree(dbObjs.get(testSql)));
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release all connections
for (int i = 0; i < MAX_CONN; i++) {
mTestPool.release(dbObjs.get(TEST_SQLS[i]));
}
// all objects in the pool should be freed now
assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
}
private void executeSqlOnDatabaseConn(SQLiteDatabase db, String sql) {
// get the given sql be compiled on the given database connection.
// this will help DatabaseConenctionPool figure out if a given SQL statement
// is already cached by a database connection.
ClassToTestSqlCompilationAndCaching c =
ClassToTestSqlCompilationAndCaching.create(db, sql);
c.close();
}
/**
* get a connection for a SQL statement 'blah'. (connection_s)
* make sure the pool has at least one free connection even after this get().
* and get a connection for the same SQL again.
* this connection should be different from connection_s.
* even though there is a connection with the given SQL pre-compiled, since is it not free
* AND since the pool has free connections available, should get a new connection.
*/
@SmallTest
public void testGetConnForTheSameSql() {
mTestPool.setMaxPoolSize(MAX_CONN);
SQLiteDatabase db = mTestPool.get(TEST_SQL);
executeSqlOnDatabaseConn(db, TEST_SQL);
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(1, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
assertFalse(mTestPool.isDatabaseObjFree(db));
SQLiteDatabase db1 = mTestPool.get(TEST_SQL);
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(2, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
assertFalse(mTestPool.isDatabaseObjFree(db1));
assertFalse(db1.equals(db));
mTestPool.release(db);
assertEquals(1, mTestPool.getFreePoolSize());
assertEquals(2, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
mTestPool.release(db1);
assertEquals(2, mTestPool.getFreePoolSize());
assertEquals(2, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
}
/**
* get the same connection N times and release it N times.
* this tests DatabaseConnectionPool.PoolObj.mNumHolders
*/
@SmallTest
public void testGetSameConnNtimesAndReleaseItNtimes() {
mTestPool.setMaxPoolSize(MAX_CONN);
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
HashMap<String, SQLiteDatabase> dbObjs = new HashMap<String, SQLiteDatabase>();
for (int i = 0; i < MAX_CONN; i++) {
SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
assertFalse(dbObjs.values().contains(db));
dbObjs.put(TEST_SQLS[i], db);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// every connection in the pool should have numHolders = 1
for (int i = 0; i < MAX_CONN; i ++) {
assertEquals(1, mTestPool.getPool().get(i).getNumHolders());
}
// pool is maxed out and no free connections. ask for one more connection
// use a previously seen SQL statement
String testSql = TEST_SQLS[MAX_CONN - 1];
SQLiteDatabase db1 = mTestPool.get(testSql);
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// make sure db1 is one of the existing ones
assertTrue(dbObjs.values().contains(db1));
assertEquals(db1, dbObjs.get(testSql));
assertFalse(mTestPool.isDatabaseObjFree(db1));
DatabaseConnectionPool.PoolObj poolObj = mTestPool.getPool().get(db1.mConnectionNum - 1);
int numHolders = poolObj.getNumHolders();
assertEquals(2, numHolders);
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// get the same connection N times more
int N = 100;
for (int i = 0; i < N; i++) {
SQLiteDatabase db2 = mTestPool.get(testSql);
assertEquals(db1, db2);
assertFalse(mTestPool.isDatabaseObjFree(db2));
// numHolders for this object should be now up by 1
int prev = numHolders;
numHolders = poolObj.getNumHolders();
assertEquals(prev + 1, numHolders);
}
// release it N times
for (int i = 0; i < N; i++) {
mTestPool.release(db1);
int prev = numHolders;
numHolders = poolObj.getNumHolders();
assertEquals(prev - 1, numHolders);
assertFalse(mTestPool.isDatabaseObjFree(db1));
}
// the connection should still have 2 more holders
assertFalse(mTestPool.isDatabaseObjFree(db1));
assertEquals(2, poolObj.getNumHolders());
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// release 2 more times
mTestPool.release(db1);
mTestPool.release(db1);
assertEquals(0, poolObj.getNumHolders());
assertEquals(1, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
assertTrue(mTestPool.isDatabaseObjFree(db1));
}
@SmallTest
public void testStressTest() {
mTestPool.setMaxPoolSize(MAX_CONN);
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
HashMap<SQLiteDatabase, Integer> dbMap = new HashMap<SQLiteDatabase, Integer>();
for (int i = 0; i < MAX_CONN; i++) {
SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
assertFalse(dbMap.containsKey(db));
dbMap.put(db, 1);
executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// ask for lot more connections but since the pool is maxed out, we should start receiving
// connections that we already got so far
for (int i = MAX_CONN; i < 1000; i++) {
SQLiteDatabase db = mTestPool.get(TEST_SQL + i);
assertTrue(dbMap.containsKey(db));
int k = dbMap.get(db);
dbMap.put(db, ++k);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
// print the distribution of the database connection handles received, should be uniform.
for (SQLiteDatabase d : dbMap.keySet()) {
Log.i(TAG, "connection # " + d.mConnectionNum + ", numHolders: " + dbMap.get(d));
}
// print the pool info
Log.i(TAG, mTestPool.toString());
// release all
for (SQLiteDatabase d : dbMap.keySet()) {
int num = dbMap.get(d);
for (int i = 0; i < num; i++) {
mTestPool.release(d);
}
}
assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
}
}