persistentstorage/dbms/tdbms/t_dbstrcmp.cpp
changeset 0 08ec8eefde2f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/dbms/tdbms/t_dbstrcmp.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,485 @@
+// Copyright (c) 2005-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:
+// Testing DBMS ordering and searching functionality when the key field is unicode string.
+// The idea is to verify that collation level 0 is used when doing string based searching
+// and collation level > 0 is used when doing string based ordering.
+// 
+//
+
+#include <e32test.h>
+#include <f32file.h>
+#include <d32dbms.h>
+
+/////////////////////////////////////////////////////////////////
+//Globals
+
+_LIT(					KTestDatabase, "C:\\DBMS-TST\\T_DbmsStrComp.DB");
+
+static RTest			TheTest(_L("t_dbstrcmp"));
+static RFs				TheFs;
+static RDbNamedDatabase TheDb;
+static RDbs				TheDbSession;
+
+//Test table defs
+_LIT(KTestTableName1, "TABLE1");//EDbColText16 key field
+_LIT(KTestTableName2, "TABLE2");//EDbColLongText16 key field
+
+struct TColDef
+	{
+	const TText*	iName;
+	TDbColType		iType;
+	TInt			iAttributes;
+	};
+static TColDef const KColDefs1[]=
+	{
+		{_S("ID"), EDbColText16, 0},
+		{_S("DATA"), EDbColUint32, 0},
+		{0}
+	};
+static TColDef const KColDefs2[]=
+	{
+		{_S("ID"), EDbColLongText16, 0},
+		{_S("DATA"), EDbColUint32, 0},
+		{0}
+	};
+
+//Test strings
+const TInt KTestStrLen = 3; //The length of test strings
+typedef TBuf16<KTestStrLen> TNameBuf;
+//Test strings array - using upper and lower case - which will force the DBMS server to make
+//different decisions depending on what is the current case: ordering or searching.
+const TNameBuf KTestStr[] =
+	{
+	_L16("aaa"),
+	_L16("aAa"),
+	_L16("bbB"),
+	_L16("BbB")
+	};
+const TInt KTestStrCnt = sizeof(KTestStr) / sizeof(KTestStr[0]);
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Destroy test environment - global functions
+
+//Deletes "aFullName" file.
+static TInt DeleteDataFile(const TDesC& aFullName)
+	{
+	RFs fsSession;
+	TInt err = fsSession.Connect();
+	if(err == KErrNone)
+		{
+		TEntry entry;
+		err = fsSession.Entry(aFullName, entry);
+		if(err == KErrNone)
+			{
+			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
+			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
+			if(err != KErrNone)
+				{
+				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
+				}
+			err = fsSession.Delete(aFullName);
+			if(err != KErrNone)
+				{
+				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
+				}
+			}
+		fsSession.Close();
+		}
+	else
+		{
+		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
+		}
+	return err;
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Tests macros and functions.
+//If (!aValue) then the test will be panicked, the test data files will be deleted.
+static void Check(TInt aValue, TInt aLine)
+	{
+	if(!aValue)
+		{
+		::DeleteDataFile(KTestDatabase);
+		TheTest(EFalse, aLine);
+		}
+	}
+//If (aValue != aExpected) then the test will be panicked, the test data files will be deleted.
+static void Check(TInt aValue, TInt aExpected, TInt aLine)
+	{
+	if(aValue != aExpected)
+		{
+		RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue);
+		::DeleteDataFile(KTestDatabase);
+		TheTest(EFalse, aLine);
+		}
+	}
+//Use these to test conditions.
+#define TEST(arg) ::Check((arg), __LINE__)
+#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__)
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Global functions
+
+//Prepares the test directory.
+//TheFs.Connect() has to be called already.
+static void SetupTestDirectory()
+    {
+	TInt err = TheFs.MkDir(KTestDatabase);
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+	}
+
+//Leaves with info message printed out
+static void LeaveL(TInt aError, TInt aLine)
+	{
+	RDebug::Print(_L("*** Leave. Error: %d, Line: %d\r\n"), aError, aLine);
+	User::Leave(aError);
+	}
+
+//Leaves if aError < 0 with info message printed out
+static void LeaveIfErrorL(TInt aError, TInt aLine)
+	{
+	if(aError < KErrNone)
+		{
+		LeaveL(aError, aLine);
+		}
+	}
+
+//Use LEAVE() macro instead of User::Leave() and LEAVE_IF_ERROR() macro instead of
+//User::LeaveIfError(). They will print the line number, where the "leave" was called.
+#define LEAVE(aError) ::LeaveL(aError, __LINE__)
+#define LEAVE_IF_ERROR(aError) ::LeaveIfErrorL(aError, __LINE__)
+
+//Creates the test DBMS session
+static void CreateTestDbSession()
+	{
+	RDebug::Print(_L("Create DBMS session\n"));
+	TInt err = TheDbSession.Connect();
+	TEST2(err, KErrNone);
+	}
+
+
+//Creates the test database
+//TheDbSession instance has to be connected already.
+//TheFs.Connect() has to be called already.
+static void CreateTestDatabase(RDbs& aDbs, RDbNamedDatabase& aDb)
+	{
+	RDebug::Print(_L("Create test database\n"));
+	TInt err = aDb.Replace(TheFs, KTestDatabase);
+	TEST2(err, KErrNone);
+	TheDb.Close();
+	err = aDb.Open(aDbs, KTestDatabase);
+	TEST2(err, KErrNone);
+	}
+
+//Creates test table
+static void DoCreateTestTableL(RDbNamedDatabase& aDb, const TDesC& aTblName, const TColDef aColDefs[])
+	{
+	CDbColSet* colSet = CDbColSet::NewLC();
+	for(const TColDef* colDef=aColDefs;colDef->iName;++colDef)
+		{
+		TDbCol col(TPtrC(colDef->iName), colDef->iType);
+		col.iAttributes = colDef->iAttributes;
+		colSet->AddL(col);
+		}
+	TEST2(aDb.CreateTable(aTblName, *colSet), KErrNone);
+	CleanupStack::PopAndDestroy(colSet);
+	}
+
+//Creates test tables
+static void CreateTestTablesL(RDbNamedDatabase& aDb)
+	{
+	RDebug::Print(_L("Create test tables\n"));
+	::DoCreateTestTableL(aDb, KTestTableName1, KColDefs1);
+	::DoCreateTestTableL(aDb, KTestTableName2, KColDefs2);
+	}
+
+//Gets the value of the string field, which type may be EDbColText16 or EDbColLongText16
+void GetStrFieldValueL(RDbRowSet& aTbl, const TDesC& aTblName, TDes& aStrFldVal)
+	{
+	if(aTblName.CompareF(KTestTableName1) == 0)
+		{
+		aStrFldVal = aTbl.ColDes16(1);
+		}
+	else
+		{
+		RDbColReadStream blob;
+		blob.OpenLC(aTbl, 1);
+		blob.ReadL(aStrFldVal, aTbl.ColLength(1));
+		CleanupStack::PopAndDestroy();
+		}
+	}
+
+//Prints all table records
+static TInt PrintRecordsL(RDbRowSet& aTbl, const TDesC& aTblName)
+	{
+	RDebug::Print(_L("Table: %S\n"), &aTblName);
+    aTbl.FirstL();
+	TInt rec = 0;
+    while(aTbl.AtRow())
+        {
+        aTbl.GetL();
+		TNameBuf strFldVal;
+		GetStrFieldValueL(aTbl, aTblName, strFldVal);
+		TUint32 v = aTbl.ColUint32(2);
+		RDebug::Print(_L("   Record %d, Str: %S, Val: %d\n"), ++rec, &strFldVal, v);
+        aTbl.NextL();
+        }
+	return rec;
+	}
+
+//Checks if the records order (based on a string key field comparison) matches the order of the
+//strings in aTestStrArray
+static void AssertRecordsOrderL(RDbRowSet& aTbl, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray)
+	{
+    aTbl.FirstL();
+	TInt rec = 0;
+    while(aTbl.AtRow())
+        {
+        aTbl.GetL();
+		TNameBuf strFldVal;
+		GetStrFieldValueL(aTbl, aTblName, strFldVal);
+		TEST(aTestStrArray[rec] == strFldVal);
+		++rec;
+        aTbl.NextL();
+        }
+	}
+
+//Adds test data to the specified table. Make sure that the records are not in
+//order (assuming that the first field will be the key).
+static void AddTestDataL(RDbNamedDatabase& aDb, const TDesC& aTblName)
+	{
+	RDbTable tbl;
+	CleanupClosePushL(tbl);
+	TEST2(tbl.Open(aDb, aTblName, RDbRowSet::EUpdatable), KErrNone);
+	for(TInt i=0;i<KTestStrCnt;++i)
+		{
+		tbl.InsertL();
+		tbl.SetColL(1, KTestStr[KTestStrCnt - i - 1]);
+		tbl.SetColL(2, i + 1);
+		tbl.PutL();
+		}
+	TEST(tbl.CountL() == KTestStrCnt);
+	(void)::PrintRecordsL(tbl, aTblName);
+	CleanupStack::PopAndDestroy(&tbl);
+	}
+
+//Adds the test data to test tables
+static void AddTestDataL(RDbNamedDatabase& aDb)
+	{
+	RDebug::Print(_L("Add data to test tables\n"));
+	::AddTestDataL(aDb, KTestTableName1);
+	::AddTestDataL(aDb, KTestTableName2);
+	}
+
+//Init test environment
+static void InitEnvL()
+	{
+	::CreateTestDbSession();
+    //Create test database and tables. Add some test data to them.
+	::CreateTestDatabase(TheDbSession, TheDb);
+	::CreateTestTablesL(TheDb);
+	::AddTestDataL(TheDb);
+	}
+
+//String comparison function, used in FillStrArraySorted() function.
+static TInt CompareC(const TNameBuf& aName1, const TNameBuf& aName2)
+    {
+    return aName1.CompareC(aName2);
+    }
+
+//Inserts all test string into an ordered array - aTestStrArray
+static void FillStrArraySortedL(RArray<TNameBuf>& aTestStrArray)
+	{
+	for(TInt i=0;i<KTestStrCnt;++i)
+		{
+    	User::LeaveIfError(aTestStrArray.InsertInOrder(KTestStr[i], TLinearOrder<TNameBuf>(CompareC)));
+		}
+	}
+
+static void CreateIndexL(RDbNamedDatabase& aDb, const TDesC& aTblName, const TDesC& aColumnName)
+	{
+	RDebug::Print(_L("Create index. Table: %S, column: %S\n"), &aTblName, &aColumnName);
+	CDbKey* key = CDbKey::NewLC();
+	key->AddL(aColumnName);
+	key->MakeUnique();
+	key->SetComparison(EDbCompareCollated);
+	LEAVE_IF_ERROR(aDb.CreateIndex(aColumnName, aTblName, *key));
+	CleanupStack::PopAndDestroy(key);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Test cases
+
+//Test case 1. Check SELECT statement with ORDER BY clause when the key field is a string.
+static void OrderByTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray)
+	{
+    RDbView view;
+    CleanupClosePushL(view);
+
+    TBuf<128> sqlStmt;
+    sqlStmt.Append(_L("SELECT ID, DATA FROM "));
+    sqlStmt.Append(aTblName);
+    sqlStmt.Append(_L(" ORDER BY ID"));
+    User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited));
+    User::LeaveIfError(view.EvaluateAll());
+
+	(void)::PrintRecordsL(view, aTblName);
+	AssertRecordsOrderL(view, aTblName, aTestStrArray);
+
+    CleanupStack::PopAndDestroy(&view);
+	}
+
+//Test case 2. Check SELECT statement with LIKE keyword when the key field is a string.
+static void LikeTestL(RDbNamedDatabase& aDb, const TDesC& aTblName)
+	{
+    RDbView view;
+    CleanupClosePushL(view);
+
+    TBuf<128> sqlStmt;
+    sqlStmt.Append(_L("SELECT ID, DATA FROM "));
+    sqlStmt.Append(aTblName);
+    sqlStmt.Append(_L(" WHERE ID LIKE 'B*'"));
+    User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited));
+    User::LeaveIfError(view.EvaluateAll());
+
+	TInt cnt = ::PrintRecordsL(view, aTblName);
+	TEST(cnt == 2);
+
+    CleanupStack::PopAndDestroy(&view);
+	}
+
+//Test case 3. Check SELECT statement with LIKE & ORDER BY keywords when the key field is a string.
+static void LikeOrderTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray)
+	{
+    RDbView view;
+    CleanupClosePushL(view);
+
+    TBuf<128> sqlStmt;
+    sqlStmt.Append(_L("SELECT ID, DATA FROM "));
+    sqlStmt.Append(aTblName);
+    sqlStmt.Append(_L(" WHERE ID LIKE 'B*' ORDER BY ID"));
+    User::LeaveIfError(view.Prepare(aDb, TDbQuery(sqlStmt, EDbCompareCollated), TDbWindow::EUnlimited));
+    User::LeaveIfError(view.EvaluateAll());
+
+	TInt cnt = ::PrintRecordsL(view, aTblName);
+	TEST(cnt == 2);
+	AssertRecordsOrderL(view, aTblName, aTestStrArray);
+
+    CleanupStack::PopAndDestroy(&view);
+	}
+
+//Test case 4. Indexed table. The index is a string field.
+static void IndexTestL(RDbNamedDatabase& aDb, const TDesC& aTblName, const RArray<TNameBuf>& aTestStrArray)
+	{
+	_LIT(KIdxName, "ID");
+	::CreateIndexL(aDb, aTblName, KIdxName);
+
+	RDbTable tbl;
+	CleanupClosePushL(tbl);
+	TEST2(tbl.Open(aDb, aTblName, RDbRowSet::EReadOnly), KErrNone);
+	TEST2(tbl.SetIndex(KIdxName), KErrNone);
+
+	(void)::PrintRecordsL(tbl, aTblName);
+	AssertRecordsOrderL(tbl, aTblName, aTestStrArray);
+
+    CleanupStack::PopAndDestroy(&tbl);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//The main test function.
+//Call your new test functions from here
+static void RunTestsL()
+	{
+	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-LEGACY-DBMSSTRCOMP-0001 Init test environment "));
+	::InitEnvL();
+
+    RArray<TNameBuf> testStrArray;
+    CleanupClosePushL(testStrArray);
+	::FillStrArraySortedL(testStrArray);
+
+	TheTest.Next(_L("SELECT, ORDER BY, EDbColText16"));
+	::OrderByTestL(TheDb, KTestTableName1, testStrArray);
+
+	TheTest.Next(_L("SELECT, ORDER BY, EDbColLongText16"));
+	::OrderByTestL(TheDb, KTestTableName2, testStrArray);
+
+	TheTest.Next(_L("SELECT, LIKE, EDbColText16"));
+	::LikeTestL(TheDb, KTestTableName1);
+
+	TheTest.Next(_L("SELECT, LIKE, EDbColLongText16"));
+	::LikeTestL(TheDb, KTestTableName2);
+
+    RArray<TNameBuf> testStrArray2;
+    CleanupClosePushL(testStrArray2);
+    testStrArray2.AppendL(testStrArray[2]);//"bbB"
+    testStrArray2.AppendL(testStrArray[3]);//"BbB"
+
+	TheTest.Next(_L("SELECT, LIKE, ORDER BY, EDbColText16"));
+	::LikeOrderTestL(TheDb, KTestTableName1, testStrArray2);
+
+	TheTest.Next(_L("SELECT, LIKE, ORDER BY, EDbColLongText16"));
+	::LikeOrderTestL(TheDb, KTestTableName2, testStrArray2);
+
+	TheTest.Next(_L("Index, EDbColText16"));
+	::IndexTestL(TheDb, KTestTableName1, testStrArray);
+
+// Not possible to create a key with EDbColLongText16
+//	TheTest.Next(_L("Index, EDbColLongText16"));
+//	::IndexTestL(TheDb, KTestTableName2, testStrArray);
+
+	//Add tests here!
+
+    CleanupStack::PopAndDestroy(&testStrArray2);
+    CleanupStack::PopAndDestroy(&testStrArray);
+	}
+
+TInt E32Main()
+	{
+	TheTest.Title();
+
+	__UHEAP_MARK;
+
+	CTrapCleanup* trapCleanup = CTrapCleanup::New();
+	TEST(trapCleanup != NULL);
+
+	TInt err = TheFs.Connect();
+	TEST2(err, KErrNone);
+	::SetupTestDirectory();
+
+	::DeleteDataFile(KTestDatabase);
+
+	TRAP(err, ::RunTestsL());
+	TheDb.Close();
+	TheDbSession.Close();
+	TheFs.Close();
+	TEST2(err, KErrNone);
+
+	::DeleteDataFile(KTestDatabase);
+
+	TheTest.End();
+	TheTest.Close();
+
+	delete trapCleanup;
+
+	__UHEAP_MARKEND;
+
+	return 0;
+	}
+
+