--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqltrans.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,321 @@
+// 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>
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+#define UNUSED_VAR(a) (a) = (a)
+
+RTest TheTest(_L("t_sqltrans test"));
+
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KTestDbName, "c:\\test\\t_sqltrans.db");
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void DeleteTestFiles()
+ {
+ RSqlDatabase::Delete(KTestDbName);
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//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)
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void CreateTestDir()
+ {
+ RFs fs;
+ TInt err = fs.Connect();
+ TEST2(err, KErrNone);
+
+ err = fs.MkDir(KTestDir);
+ TEST(err == KErrNone || err == KErrAlreadyExists);
+
+ fs.Close();
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+_LIT8(KTestSql1, "INSERT INTO A(Id) VALUES(1); INSERT INTO A(Id) VALUES(2);");
+
+const TPtrC8 KSqls[] = {KTestSql1()};
+
+static RCriticalSection ThreadCritSect;
+static RCriticalSection MainCritSect;
+
+static TInt TheSqlIdx = 0;
+
+_LIT(KPanicCategory, "TransFail");
+const TInt KPanicCode = 0x1234;
+
+//Test thread function
+TInt ThreadFunc1(void*)
+ {
+ __UHEAP_MARK;
+
+ CTrapCleanup* tc = CTrapCleanup::New();
+ TTEST(tc != NULL);
+
+ __ASSERT_ALWAYS(TheSqlIdx >= 0 && TheSqlIdx < (TInt)(sizeof(KSqls) / sizeof(KSqls[0])), User::Invariant());
+ const TPtrC8 sql = KSqls[TheSqlIdx];
+
+ //Open test database
+ RSqlDatabase db;
+ TInt err = db.Open(KTestDbName);
+ TTEST2(err, KErrNone);
+
+ RDebug::Print(_L("---:WorkThread: Begin transaction. Exec SQL...\r\n"));
+
+ //Begin a transaction
+ _LIT8(KBeginTrans, "BEGIN");
+ err = db.Exec(KBeginTrans);
+ TTEST(err >= 0);
+
+ //Execute the SQL statement(s)
+ err = db.Exec(sql);
+ 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
+ RDebug::Print(_L("---:WorkThread: Panic!\r\n"));
+ User::Panic(KPanicCategory, KPanicCode);
+
+ delete tc;
+
+ __UHEAP_MARKEND;
+
+ return KErrNone;
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1623
+@SYMTestCaseDesc Transaction atomicity test.
+ Create a test database with a table.
+ Create a worker thread and make some "insert record" operations in a transaction from
+ that thread. Before commiting the transaction notify the main thread that the
+ insert operation completed and wait for a notification from the main thread.
+ The main thread notifies the worker thread to panic and checks the test table
+ content. No records should be found there.
+@SYMTestPriority High
+@SYMTestActions Transaction atomicity test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void TransactionTest1()
+ {
+ RDebug::Print(_L("+++:MainThread: Create critical sections\r\n"));
+ TEST2(ThreadCritSect.CreateLocal(), KErrNone);
+ ThreadCritSect.Wait();
+ TEST2(MainCritSect.CreateLocal(), KErrNone);
+ MainCritSect.Wait();
+
+ RDebug::Print(_L("+++:MainThread: Create test database\r\n"));
+ (void)RSqlDatabase::Delete(KTestDbName);
+ RSqlDatabase db;
+ TInt err = db.Create(KTestDbName);
+ TEST2(err, KErrNone);
+
+ RDebug::Print(_L("+++:MainThread: Create a table in the test database\r\n"));
+ _LIT8(KCreateSql, "CREATE TABLE A(Id INTEGER)");
+ err = db.Exec(KCreateSql);
+ TEST(err >= 0);
+
+ db.Close();
+
+ RDebug::Print(_L("+++:MainThread: Create the worker thread\r\n"));
+ _LIT(KThreadName, "WorkThrd");
+ RThread thread;
+ TheSqlIdx = 0;
+ 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 panic...\r\n"));
+ ThreadCritSect.Signal();
+
+ User::WaitForRequest(status);
+ User::SetJustInTime(ETrue); // enable debugger panic handling
+
+ TEST2(thread.ExitType(), EExitPanic);
+ TEST2(thread.ExitReason(), KPanicCode);
+
+ thread.Close();
+
+ RDebug::Print(_L("+++:MainThread: Check the database content...\r\n"));
+ err = db.Open(KTestDbName);
+ TEST2(err, KErrNone);
+ _LIT8(KSelectSql, "SELECT COUNT(*) AS CNT FROM A");
+ RSqlStatement stmt;
+ err = stmt.Prepare(db, KSelectSql);
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TInt val = stmt.ColumnInt(0);
+ TEST(val == 0);
+ stmt.Close();
+ db.Close();
+
+ err = RSqlDatabase::Delete(KTestDbName);
+ TEST2(err, KErrNone);
+ }
+
+/**
+@SYMTestCaseID SYSLIB-SQL-CT-1624
+@SYMTestCaseDesc Transaction consistency test.
+ Create a test database with a table with a field with a CHECK constraint.
+ Try to insert some records in a transaction violating the CHECK constraint.
+ The transaction should fail.
+ No records should be found in the test table.
+@SYMTestPriority High
+@SYMTestActions Transaction atomicity test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ REQ5792
+ REQ5793
+*/
+void TransactionTest2()
+ {
+ //Create a test database
+ (void)RSqlDatabase::Delete(KTestDbName);
+ RSqlDatabase db;
+ TInt err = db.Create(KTestDbName);
+ TEST2(err, KErrNone);
+
+ //Create a test table
+ _LIT8(KCreateSql, "CREATE TABLE A(Id INTEGER, CHECK(Id > 10 AND Id <= 20))");
+ err = db.Exec(KCreateSql);
+ TEST(err >= 0);
+
+ //Begin a transaction
+ _LIT8(KBeginTrans, "BEGIN");
+ err = db.Exec(KBeginTrans);
+ TEST(err >= 0);
+
+ //Exec SQL, viloate constraint.
+ _LIT8(KInsertSql, "INSERT INTO A(Id) VALUES(15); INSERT INTO A(Id) VALUES(38);");
+ err = db.Exec(KInsertSql);
+ TEST2(err, KSqlErrConstraint);
+
+ //Rollback transaction
+ _LIT8(KRollbackTrans, "ROLLBACK");
+ err = db.Exec(KRollbackTrans);
+ TEST(err >= 0);
+
+ //Check the database content
+ _LIT8(KSelectSql, "SELECT COUNT(*) AS CNT FROM A");
+ RSqlStatement stmt;
+ err = stmt.Prepare(db, KSelectSql);
+ TEST2(err, KErrNone);
+ err = stmt.Next();
+ TEST2(err, KSqlAtRow);
+ TInt val = stmt.ColumnInt(0);
+ TEST2(val, 0);
+ stmt.Close();
+
+ db.Close();
+
+ err = RSqlDatabase::Delete(KTestDbName);
+ TEST2(err, KErrNone);
+ }
+
+void DoTests()
+ {
+ TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1623 Transaction test 1 "));
+ TransactionTest1();
+
+ TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1624 Transaction test 2 "));
+ TransactionTest2();
+ }
+
+TInt E32Main()
+ {
+ TheTest.Title();
+
+ CTrapCleanup* tc = CTrapCleanup::New();
+
+ __UHEAP_MARK;
+
+ CreateTestDir();
+ DeleteTestFiles();
+ DoTests();
+ DeleteTestFiles();
+
+ __UHEAP_MARKEND;
+
+ TheTest.End();
+ TheTest.Close();
+
+ delete tc;
+
+ User::Heap().Check();
+ return KErrNone;
+ }