--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqlprivcage.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,569 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <e32test.h>
+#include <bautils.h>
+#include <sqldb.h>
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+static RFs TheFs;
+RTest TheTest(_L("t_sqlprivcage test"));
+
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KTestDb1, "c:\\private\\21212124\\t_sqlprivcage_1.db");
+_LIT(KTestDb2, "c:\\private\\21212124\\t_sqlprivcage_2.db");
+_LIT(KTestDbZ, "z:\\private\\21212124\\t_sqldb1.db");//Created outside this test
+_LIT(KTestDb, "\\private\\21212124\\t_sqlprivcage_3.db");
+TParse TheFileNameParse;
+
+static RCriticalSection ThreadCritSect;
+static RCriticalSection MainCritSect;
+
+_LIT(KPanicCategory, "TransFail");
+const TInt KPanicCode = 1111;
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+//Deletes all created test files.
+void DeleteTestFiles()
+ {
+ if(TheFileNameParse.FullName().Length() > 0)
+ {
+ (void)RSqlDatabase::Delete(TheFileNameParse.FullName());
+ }
+ (void)RSqlDatabase::Delete(KTestDb2);
+ (void)RSqlDatabase::Delete(KTestDb1);
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Test macros and functions
+void Check1(TInt aValue, TInt aLine, TBool aPrintThreadName = EFalse)
+ {
+ if(!aValue)
+ {
+ DeleteTestFiles();
+ if(aPrintThreadName)
+ {
+ RThread th;
+ TName name = th.Name();
+ RDebug::Print(_L("*** Thread %S, Line %d\r\n"), &name, aLine);
+ }
+ else
+ {
+ RDebug::Print(_L("*** Line %d\r\n"), aLine);
+ }
+ TheTest(EFalse, aLine);
+ }
+ }
+void Check2(TInt aValue, TInt aExpected, TInt aLine, TBool aPrintThreadName = EFalse)
+ {
+ if(aValue != aExpected)
+ {
+ DeleteTestFiles();
+ if(aPrintThreadName)
+ {
+ RThread th;
+ TName name = th.Name();
+ RDebug::Print(_L("*** Thread %S, Line %d Expected error: %d, got: %d\r\n"), &name, aLine, aExpected, aValue);
+ }
+ else
+ {
+ RDebug::Print(_L("*** Line %d, Expected error: %d, got: %d\r\n"), aLine, aExpected, aValue);
+ }
+ TheTest(EFalse, aLine);
+ }
+ }
+#define TEST(arg) ::Check1((arg), __LINE__)
+#define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__)
+#define TTEST(arg) ::Check1((arg), __LINE__, ETrue)
+#define TTEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__, ETrue)
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+//Creates file session instance and the test directory
+void CreateTestEnv()
+ {
+ TInt err = TheFs.Connect();
+ TEST2(err, KErrNone);
+
+ err = TheFs.CreatePrivatePath(EDriveC);
+ TEST(err == KErrNone || err == KErrAlreadyExists);
+
+ err = TheFs.MkDir(KTestDir);
+ TEST(err == KErrNone || err == KErrAlreadyExists);
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1764
+@SYMTestCaseDesc The test creates a database in the test application's private data cage.
+ Then the test does some operations with the created private database:
+ create table, insert records, select records, transactions, delete database.
+ The tests verifies that the SQL server can create a database in the application's
+ private data cage and can operate with the database.
+@SYMTestPriority High
+@SYMTestActions SQL, Private database test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void SimpleDbOpTest()
+ {
+ //Create private database
+ RSqlDatabase db;
+ TInt err = db.Create(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Execute some operations with the private database
+ err = db.Exec(_L8("CREATE TABLE A(ID INTEGER)"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("INSERT INTO A(ID) VALUES(1);INSERT INTO A(ID) VALUES(2);INSERT INTO A(ID) VALUES(3);"));
+ TEST(err >= 0);
+
+ //Check written records
+ RSqlStatement stmt;
+ err = stmt.Prepare(db, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 2);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 3);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+
+ db.Close();
+ //Open private database
+ err = db.Open(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Execute a DELETE transaction
+ err = db.Exec(_L8("BEGIN IMMEDIATE TRANSACTION"));
+ TEST(err >= 0);
+
+ err = db.Exec(_L8("DELETE FROM A WHERE ID > 1"));
+ TEST(err >= 0);
+
+ err = db.Exec(_L8("COMMIT TRANSACTION"));
+ TEST(err >= 0);
+
+ //Check records left
+ err = stmt.Prepare(db, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+
+ //Open private database which is on drive Z and with the same name
+ RSqlDatabase db2;
+ err = db2.Open(KTestDbZ);
+ TEST2(err, KErrNone);
+ //An attempt to write to a read-only database
+ err = db2.Exec(_L("INSERT INTO A VALUES(6)"));
+ TheTest.Printf(_L(" === Read-only private database. RSqlDatabase::Exec() returned err=%d\r\n"), err);
+ TEST(err != KErrNone);
+ //Check records
+ err = stmt.Prepare(db2, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+ db2.Close();
+
+ db.Close();
+ err = RSqlDatabase::Delete(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Create private database on drive different than C:
+ for(TInt drvNum=EDriveD;drvNum<=EDriveZ;++drvNum)
+ {
+ TDriveUnit drvUnit(drvNum);
+ TPtrC drvName = drvUnit.Name();
+ TheFileNameParse.Set(KTestDb, &drvName, 0);
+ //Check if it is possible to create application's private data cage on drvNum drive.
+ err = TheFs.CreatePrivatePath(drvNum);
+ if(err == KErrNone || err == KErrAlreadyExists)
+ {
+ (void)RSqlDatabase::Delete(TheFileNameParse.FullName());
+ err = db.Create(TheFileNameParse.FullName());
+ if(err == KErrNone)
+ {
+ //Execute some operations with the private database
+ err = db.Exec(_L8("BEGIN IMMEDIATE TRANSACTION"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("CREATE TABLE A(ID INTEGER)"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("INSERT INTO A(ID) VALUES(1);INSERT INTO A(ID) VALUES(2);INSERT INTO A(ID) VALUES(3);"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("COMMIT TRANSACTION"));
+ TEST(err >= 0);
+ db.Close();
+ err = RSqlDatabase::Delete(TheFileNameParse.FullName());
+ TEST2(err, KErrNone);
+ break;
+ }
+ }
+ TheFileNameParse.Set(KNullDesC, 0, 0);
+ }
+
+ //An attempt to create/open "C:[21212122]BBDb2.db" - this test has no enough rights to do that.
+ //...open as a non-secure database
+ err = db.Open(_L("C:[21212122]BBDb2.db"));
+ TEST2(err, KErrPermissionDenied);
+ //...create as a non-secure database
+ err = db.Create(_L("C:[21212122]BBDb2.db"));
+ TEST2(err, KErrArgument);//secure database name, no security policy
+ //...create as a secure database
+ RSqlSecurityPolicy dbSecurity;
+ TSecurityPolicy policy(TSecurityPolicy::EAlwaysPass);
+ err = dbSecurity.Create(policy);
+ TEST2(err, KErrNone);
+ err = db.Create(_L("C:[21212122]BBDb2.db"), dbSecurity);
+ TEST2(err, KErrPermissionDenied);
+ dbSecurity.Close();
+
+ //An attempt to delete "C:[21212122]BBDb2.db" - this test has no enough rights to do that.
+ err = RSqlDatabase::Delete(_L("C:[21212122]BBDb2.db"));
+ TEST2(err, KErrPermissionDenied);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1765
+@SYMTestCaseDesc The test creates two databases in the test application's private data cage.
+ Then the test inserts some records in both databases using separate RSqlDatabase objects.
+ The test closes both databases, then reopens the first one and attaches th second one.
+ Again, the test inserts some records in both databases, using single RSqlDatabase object.
+ The test reads the inserted records and verifies their column values.
+ The tests verifies that the SQL server can create a database in the application's
+ private data cage, can operate with the database and can attach private databases.
+@SYMTestPriority High
+@SYMTestActions SQL, Attach private database test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void AttachDbOpTest()
+ {
+ //Create private database 1
+ RSqlDatabase db1;
+ TInt err = db1.Create(KTestDb1);
+ TEST2(err, KErrNone);
+ //Execute some operations with private database 1
+ err = db1.Exec(_L8("CREATE TABLE A(ID INTEGER)"));
+ TEST(err >= 0);
+ err = db1.Exec(_L8("INSERT INTO A(ID) VALUES(1)"));
+ TEST2(err, 1);
+ db1.Close();
+
+ //Create private database 2
+ RSqlDatabase db2;
+ err = db2.Create(KTestDb2);
+ TEST2(err, KErrNone);
+ //Execute some operations with private database 2
+ err = db2.Exec(_L8("CREATE TABLE A(ID INTEGER, T TEXT)"));
+ TEST(err >= 0);
+ err = db2.Exec(_L8("INSERT INTO A(ID, T) VALUES(1, 'NAME-NAME-NAME')"));
+ TEST2(err, 1);
+ db2.Close();
+
+ //Open database 1, attach database 2
+ RSqlDatabase db;
+ err = db.Open(KTestDb1);
+ TEST2(err, KErrNone);
+ err = db.Attach(KTestDb2, _L("Db2"));
+ TEST2(err, KErrNone);
+
+ //Insert some records
+ err = db.Exec(_L8("BEGIN IMMEDIATE TRANSACTION"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("INSERT INTO Main.A(ID) VALUES(2);INSERT INTO Db2.A(ID, T) VALUES(2, 'AAA');"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("COMMIT TRANSACTION"));
+ TEST(err >= 0);
+
+ err = db.Detach(_L("Db2"));
+ TEST2(err, KErrNone);
+ db.Close();
+
+ //Verify inserted data in database 2
+ err = db.Open(KTestDb2);
+ TEST2(err, KErrNone);
+
+ RSqlStatement stmt;
+ err = stmt.Prepare(db, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ TPtrC text;
+ err = stmt.ColumnText(1, text);
+ TEST2(err, KErrNone);
+ TEST(text == _L("NAME-NAME-NAME"));
+
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 2);
+ err = stmt.ColumnText(1, text);
+ TEST2(err, KErrNone);
+ TEST(text == _L("AAA"));
+
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+ db.Close();
+
+ err = RSqlDatabase::Delete(KTestDb2);
+ TEST2(err, KErrNone);
+ err = RSqlDatabase::Delete(KTestDb1);
+ TEST2(err, KErrNone);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1766
+@SYMTestCaseDesc The test creates a database in the test application's private data cage.
+ Then the test creates two RSqlDatabase obejcts connecting them to the same
+ private database. The test inserts some records using both connections.
+ The test verifies that the inserted records are in the database file and
+ verifies the column values.
+ The test verifies that it is possible to make more than one connection to the
+ same private database and operate with the database using the database connections.
+@SYMTestPriority High
+@SYMTestActions SQL, Two database connections to the same private database test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void TwoDbOpTest()
+ {
+ //Create private database
+ RSqlDatabase db1;
+ TInt err = db1.Create(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Make a second connection with the test database
+ RSqlDatabase db2;
+ err = db2.Open(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Execute some operations with the private database
+ err = db1.Exec(_L8("CREATE TABLE A(ID INTEGER)"));
+ TEST(err >= 0);
+ err = db1.Exec(_L8("INSERT INTO A(ID) VALUES(1);"));
+ TEST2(err, 1);
+ err = db2.Exec(_L8("INSERT INTO A(ID) VALUES(2);"));
+ TEST2(err, 1);
+
+ //Verify inserted data
+ RSqlStatement stmt;
+ err = stmt.Prepare(db2, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 2);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+
+ db2.Close();
+ db1.Close();
+ err = RSqlDatabase::Delete(KTestDb1);
+ TEST2(err, KErrNone);
+ }
+
+//Test thread function.
+//The test thread opens a database, begins a transaction and then simulates a crash within the transaction.
+TInt ThreadFunc1(void*)
+ {
+ __UHEAP_MARK;
+
+ CTrapCleanup* tc = CTrapCleanup::New();
+ TTEST(tc != NULL);
+
+ //Open test database
+ RSqlDatabase db;
+ TInt err = db.Open(KTestDb1);
+ TTEST2(err, KErrNone);
+
+ RDebug::Print(_L("---:WorkThread: Begin transaction. Exec SQL...\r\n"));
+
+ //Begin a transaction
+ err = db.Exec(_L8("BEGIN IMMEDIATE TRANSACTION"));
+ TTEST(err >= 0);
+
+ //Execute INSERT sql statements
+ err = db.Exec(_L8("INSERT INTO A(ID) VALUES(2);INSERT INTO A(ID) VALUES(3);"));
+ TTEST(err >= 0);
+
+ RDebug::Print(_L("---:WorkThread: Notify the main thread about the SQL statement execution\r\n"));
+ MainCritSect.Signal();
+
+ RDebug::Print(_L("---:WorkThread: Wait for permisson to continue...\r\n"));
+ ThreadCritSect.Wait();
+
+ User::SetJustInTime(EFalse); // disable debugger panic handling
+
+ //Panic current thread without commiting the transaction (crash simulation)
+ RDebug::Print(_L("---:WorkThread: Panic!\r\n"));
+ User::Panic(KPanicCategory, KPanicCode);
+
+ delete tc;
+
+ __UHEAP_MARKEND;
+
+ return KErrNone;
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1767
+@SYMTestCaseDesc The test creates a database in the test application's private data cage.
+ The test does some operations with the database leaving it in state A.
+ Then the test creates a test thread and runs the thread. The test thread
+ opens a connection to the database, begins a transaction, inserts some data
+ and then simulates a crash within the transaction (kills the thread).
+ When the main thread takes the execution control, it reopens the database connection
+ and verifies that the database is in the same state A as it was before.
+@SYMTestPriority High
+@SYMTestActions SQL, Private database - transaction recovery test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void TransDbOpTest()
+ {
+ //Create private database
+ RSqlDatabase db;
+ TInt err = db.Create(KTestDb1);
+ TEST2(err, KErrNone);
+
+ //Execute some operations with the private database
+ err = db.Exec(_L8("CREATE TABLE A(ID INTEGER)"));
+ TEST(err >= 0);
+ err = db.Exec(_L8("INSERT INTO A(ID) VALUES(1)"));
+ TEST2(err, 1);
+
+ //Check written records
+ RSqlStatement stmt;
+ err = stmt.Prepare(db, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+
+ db.Close();
+
+ //Run a test thread which will begin a transaction and then simulate a crash within the transaction
+ TEST2(ThreadCritSect.CreateLocal(), KErrNone);
+ ThreadCritSect.Wait();
+ TEST2(MainCritSect.CreateLocal(), KErrNone);
+ MainCritSect.Wait();
+ RDebug::Print(_L("+++:MainThread: Create the worker thread\r\n"));
+ _LIT(KThreadName, "WorkThrd");
+ RThread thread;
+ TEST2(thread.Create(KThreadName, &ThreadFunc1, 0x2000, 0x1000, 0x10000, NULL, EOwnerProcess), KErrNone);
+ TRequestStatus status;
+ thread.Logon(status);
+ TEST2(status.Int(), KRequestPending);
+ thread.Resume();
+ RDebug::Print(_L("+++:MainThread: Wait SQL statement(s) to be executed...\r\n"));
+ MainCritSect.Wait();
+ RDebug::Print(_L("+++:MainThread: Notify the worker thread to simulate a crash...\r\n"));
+ ThreadCritSect.Signal();
+ User::WaitForRequest(status);
+ User::SetJustInTime(ETrue); // enable debugger panic handling
+ TEST2(thread.ExitType(), EExitPanic);
+ TEST2(thread.ExitReason(), KPanicCode);
+ thread.Close();
+ MainCritSect.Close();
+ ThreadCritSect.Close();
+
+ //Reopen the test database. The failed transaction must be rolled back.
+ err = db.Open(KTestDb1);
+ TEST2(err, KErrNone);
+ //Verify that the database content is the same as before the failed transaction
+ err = stmt.Prepare(db, _L8("SELECT * FROM A"));
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TEST2(stmt.ColumnInt(0), 1);
+ err = stmt.Next();
+ TEST2(err, KSqlAtEnd);
+ stmt.Close();
+ db.Close();
+
+ err = RSqlDatabase::Delete(KTestDb1);
+ TEST2(err, KErrNone);
+ }
+
+void DoTests()
+ {
+ TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1764 Simple private db operations "));
+ SimpleDbOpTest();
+
+ TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1765 Private db operations - attach database "));
+ AttachDbOpTest();
+
+ TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1766 Private db operations - 2 database connections "));
+ TwoDbOpTest();
+
+ TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1767 Private db operations - transaction recovery "));
+ TransDbOpTest();
+ }
+
+TInt E32Main()
+ {
+ TheTest.Title();
+
+ CTrapCleanup* tc = CTrapCleanup::New();
+
+ __UHEAP_MARK;
+
+ CreateTestEnv();
+ DeleteTestFiles();
+ DoTests();
+ DeleteTestFiles();
+ TheFs.Close();
+
+ __UHEAP_MARKEND;
+
+ TheTest.End();
+ TheTest.Close();
+
+ delete tc;
+
+ User::Heap().Check();
+ return KErrNone;
+ }