// Copyright (c) 1998-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 <d32dbms.h>
#include <s32file.h>
#include <e32test.h>
#include <e32math.h>
#include <hal.h>
#include "D32DBAS.H"

LOCAL_D RTest test(_L("t_dbsrv: Test DBMS server"));
LOCAL_D CTrapCleanup* TheTrapCleanup;
LOCAL_D RFs TheFs;
LOCAL_D RDbs TheDbs;
LOCAL_D RDbNamedDatabase TheDatabase;
LOCAL_D RDbTable TheTable;
LOCAL_D RDbView TheView;

const TInt KTestCleanupStack=0x40;
const TPtrC KTestDatabase(_S("c:\\dbms-tst\\share.db"));
const TPtrC KTestFormat(_S(""));
const TPtrC KTable1(_S("Table1"));
const TPtrC KTable2(_S("Table2"));
const TInt KRowCount=1000;
const TUint KExitDelay=6*0x100000;	// ~6 seconds

const TPtrC KCreateTable1=_S("CREATE TABLE Table1 (Id INTEGER,Name CHAR(200),Memo LONG VARCHAR,Memo2 LONG VARBINARY)");
const TPtrC KCreateTable2=_S("CREATE TABLE Table2 (Id INTEGER,Name CHAR(200),Memo LONG VARCHAR)");
const TPtrC KDropTable1=_S("DROP TABLE Table1");
const TPtrC KDropTable2=_S("DROP TABLE Table2");

_LIT(KTestSpdFile1, "c:\\dbms-tst\\11335577.spd");
_LIT(KTestSpdFile2, "c:\\dbms-tst\\11335578.spd");
_LIT(KTestSpdFile3, "c:\\dbms-tst\\11335579.spd");
_LIT(KTestSpdFile4, "c:\\dbms-tst\\1133557A.spd");

const TInt KReturnValueForCompare = 10;
const TInt KReturnValueForFind = 20;

class TTimer
	{
public:
	void Start(const TDesC& aDes);
	void Stop();
private:
	TUint iTicks;
	};

static TTimer TheTimer;

void TTimer::Start(const TDesC& aDes)
	{
	test.Printf(_L("  %S: "),&aDes);
	iTicks=User::FastCounter();
	}

void TTimer::Stop()
	{
	TUint ticks = User::FastCounter() - iTicks;
	TInt freq = 0;
	test(HAL::Get(HAL::EFastCounterFrequency, freq) == KErrNone);
	const TInt KMicroSecIn1Sec = 1000000;
	const TInt KMsIn1Sec = 1000;
	double v = ((double)ticks * KMicroSecIn1Sec) / (double)freq; TInt v2 = (TInt)v;
	test.Printf(_L("%d ms\r\n"),v2/KMsIn1Sec);
	}

void DeleteTestFiles()
	{
	(void)TheFs.Delete(KTestDatabase);
	(void)TheFs.Delete(KTestSpdFile1); 
	(void)TheFs.Delete(KTestSpdFile2); 
	(void)TheFs.Delete(KTestSpdFile3); 
	(void)TheFs.Delete(KTestSpdFile4); 
	}

void Check(TInt aValue,TInt aExpected,TInt aLine)
	{
	if (aValue!=aExpected)
		{
		DeleteTestFiles();
		test.Printf(_L("*** Expected %d: got %d\r\n"),aExpected,aValue);
		test.operator()(EFalse,aLine);
		}
	}
#define test2(a,b) Check(a,b,__LINE__)

LOCAL_C void Connect()
	{
	test2(TheDbs.Connect(),KErrNone);
	TheDbs.ResourceMark();
	}

LOCAL_C void Disconnect()
	{
	TheDbs.ResourceCheck();
	TheDbs.Close();
	}

LOCAL_C void Execute(const TDesC& aSql)
	{
	test2 (TheDatabase.Execute(aSql),KErrNone);
	}

LOCAL_C void WriteRecordsL(TInt aCount)
	{
	TheTimer.Start(_L("writing"));
	TheDatabase.Begin();
	TBuf<10> text;
	TInt jj=0;
	for (TInt ii=0;ii<aCount;++ii)
		{
		TheTable.InsertL();
		jj=(jj+23);
		if (jj>=aCount)
			jj-=aCount;
		TheTable.SetColL(1,jj);
		text.Num(jj);
		TheTable.SetColL(2,text);
		TheTable.PutL();
		}
	test2 (TheDatabase.Commit(),KErrNone);
	TheTimer.Stop();
	}

LOCAL_C void Ipc(TInt aCount)
	{
	TheTimer.Start(_L("ipc"));
	while (--aCount>=0)
		TheDatabase.InTransaction();
	TheTimer.Stop();
	}

LOCAL_C void LoopL(RDbTable::TPosition aDirection)
	{
	TheTimer.Start(_L("loop"));
	TInt cc=0;
	while (TheTable.GotoL(aDirection))
		++cc;
	test2 (cc,TheTable.CountL());
	TheTimer.Stop();
	}

LOCAL_C void IterateL(RDbTable::TPosition aDirection)
	{
	TheTimer.Start(_L("iterate"));
	TInt cc=0;
	while (TheTable.GotoL(aDirection))
		{
		++cc;
		TheTable.GetL();
		TBuf<10> text;
		text.Num(TheTable.ColInt(1));
		test (text==TheTable.ColDes(2));
		}
	test2 (cc,TheTable.CountL());
	TheTimer.Stop();
	}

LOCAL_C void LocateL(const TDesC& aSql)
	{
	test2 (TheView.Prepare(TheDatabase,aSql),KErrNone);
	test2 (TheView.EvaluateAll(),KErrNone);
	TheTimer.Start(_L("locate"));
	test (!TheView.NextL());
	TheTimer.Stop();
	TheView.Close();
	}

LOCAL_C void Locate1L()
	{
	LocateL(_L("select * from Table1 where id is null"));
	}

LOCAL_C void Locate2L()
	{
	LocateL(_L("select * from Table1 where id = -12345"));
	}

LOCAL_C TInt DeleteL()
	{
	TheTimer.Start(_L("deleting"));
	TheDatabase.Begin();
	TInt cc=0;
	TheTable.BeginningL();
	while (TheTable.NextL())
		{
		++cc;
		TheTable.DeleteL();
		}
	test (TheTable.CountL()==0);
	test (TheDatabase.Commit()==KErrNone);
	TheTimer.Stop();
	return cc;
	}

LOCAL_C TInt DeleteSQL()
	{
	TheTimer.Start(_L("deleting [SQL]"));
	TInt cc=TheDatabase.Execute(_L("delete from Table1"));
	test (cc>=KErrNone);
	test (TheTable.CountL()==0);
	TheTimer.Stop();
	return cc;
	}

LOCAL_C void BenchmarkL()
	{
	TInt r=TheTable.Open(TheDatabase,KTable1);
	TheTable.LastL();
	test (r==KErrNone);
	Ipc(KRowCount);
	TheTable.Reset();
	LoopL(TheTable.ENext);
	TheTable.Reset();
	IterateL(TheTable.ENext);
	Locate1L();
	Locate2L();
	TheTable.Close();
	}

const TPtrC KSemaphore(_S("Connect-test"));

LOCAL_C TInt ConnectThread(TAny*)
	{
	RSemaphore s;
	s.OpenGlobal(KSemaphore,EOwnerThread);
	RDbs dbs;
	dbs.Connect();
	s.Signal();
	User::After(0x100000);
	s.Wait();
	dbs.Close();
	s.Signal();
	s.Close();
	return KErrNone;
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0605
@SYMTestCaseDesc        Single and multiple database connection tests
@SYMTestPriority        Medium
@SYMTestActions         Setup a DBMS server session Search the server for pattern matching
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestConnect()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0605 Single connection "));
	test (TheDbs.Connect()==KErrNone);
	TheDbs.Close();
	test.Next(_L("Deferred exit"));
	TFindServer find(_L("!DBMS server"));
	TFullName result;
	test2 (find.Next(result),KErrNone);
	test.Next(_L("Multiple connection"));
	Connect();
	RDbs dbs;
	test2 (dbs.Connect(),KErrNone);
	Disconnect();
	dbs.Close();
	test.Next(_L("Deferred exit"));
	find.Find(_L("!DBMS server"));
	test2 (find.Next(result),KErrNone);
	User::After(KExitDelay);
	find.Find(_L("!DBMS server"));
	test2 (find.Next(result),KErrNotFound);
	test.Next(_L("multiple thread connections"));
	RSemaphore s;
	test2 (s.CreateGlobal(KSemaphore,0,EOwnerThread),KErrNone);
	RThread t;
	test2 (t.Create(KSemaphore,ConnectThread,0x2000,0x1000,0x10000,0,EOwnerThread),KErrNone);
	t.Resume();
	TRequestStatus st;
	t.Logon(st);
	t.Close();
	s.Wait();
	Connect();
	s.Signal();
	s.Close();
	User::WaitForRequest(st);
	test2 (TheDbs.ResourceCount(),0);
	Disconnect();
	test.Next(_L("Deferred exit"));
	find.Find(_L("!DBMS server"));
	test2 (find.Next(result),KErrNone);
	User::After(KExitDelay);
	find.Find(_L("!DBMS server"));
	test2 (find.Next(result),KErrNotFound);
	test.End();
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0606
@SYMTestCaseDesc        Tests for benchmark
@SYMTestPriority        Medium
@SYMTestActions         Tests for creating database and writing a table.Test for no error conditions.
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestBenchL()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0606 Create database "));
	TInt r=TheDatabase.Replace(TheFs,KTestDatabase,KTestFormat);
	test (r==KErrNone);
	Execute(KCreateTable1);
	test.Next(_L("write table"));
	r=TheTable.Open(TheDatabase,KTable1);
	test (r==KErrNone);
	WriteRecordsL(KRowCount);
	TheTable.Close();
	test.Next(_L("BenchmarkL"));
	TRAPD(errCode, BenchmarkL());
	test(errCode == KErrNone);
	test.Next(_L("Open server-side"));
	TheDatabase.Close();
	Connect();
	r=TheDatabase.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	test.Next(_L("BenchmarkL"));
	TRAP(errCode, BenchmarkL());
	test(errCode == KErrNone);
	test.Next(_L("Wrap up"));
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0607
@SYMTestCaseDesc        Tests for opening a shared database
@SYMTestPriority        Medium
@SYMTestActions         Tests for database open,column type,write and drop table
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestOpenL()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0607 Create database "));
	test2 (TheDatabase.Replace(TheFs,KTestDatabase,KTestFormat),KErrNone);
	TheDatabase.Close();
	test.Next(_L("Open as shared"));
	Connect();
	test2 (TheDatabase.Open(TheDbs,KTestDatabase),KErrNone);
	Execute(KCreateTable1);
	test2 (TheTable.Open(TheDatabase,KTable1),KErrNone);
	test2 (TheTable.ColCount(),4);
	test2 (TheTable.ColType(1),EDbColInt32);
	test2 (TheTable.ColType(2),EDbColText);
	test2 (TheTable.ColType(3),EDbColLongText);
	test2 (TheTable.ColType(4),EDbColLongBinary);
	delete TheTable.ColSetL();
	test.Next(_L("write table"));
	WriteRecordsL(KRowCount);
	test.Next(_L("Check table"));
	TheTable.Reset();
	test (TheTable.AtBeginning());
	test (!TheTable.AtRow());
	test (!TheTable.AtEnd());
	test2 (TheTable.CountL(),KRowCount);
	IterateL(TheTable.ENext);
	test (!TheTable.AtBeginning());
	test (!TheTable.AtRow());
	test (TheTable.AtEnd());
	test.Next(_L("Delete rows"));
	test2 (DeleteL(),KRowCount);
	TheTable.Close();
	test.Next(_L("Drop table"));
	Execute(KDropTable1);
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0608
@SYMTestCaseDesc        Tests for database locking
@SYMTestPriority        Medium
@SYMTestActions         Tests for shared,exclusive locks on database
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestLocking()
	{
	TInt r;
	RDbNamedDatabase db;
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0608 Open databases "));
	Connect();
	test2 (TheDatabase.Open(TheDbs,KTestDatabase),KErrNone);
	test2 (db.Open(TheDbs,KTestDatabase,KTestFormat),KErrNone);
	RDbNotifier ob;
	test2 (ob.Open(TheDatabase),KErrNone);
//
	test.Next(_L("Shared locks"));
	TRequestStatus stat;
	ob.NotifyUnlock(stat);
	test2 (TheDatabase.Begin(),KErrNone);
	test2 (db.Begin(),KErrNone);
	test2 (TheDatabase.Commit(),KErrNone);
	User::After(1);
	test2 (stat.Int(),KRequestPending);		// should not report yet
	test2 (db.Commit(),KErrNone);
	User::WaitForRequest(stat);
	test2 (stat.Int(),ob.EUnlock);
//
	test.Next(_L("Exclusive locks"));
	ob.NotifyUnlock(stat);
	test2 (TheDatabase.Begin(),KErrNone);
	test2 (db.Begin(),KErrNone);
	test2 (TheDatabase.Execute(KCreateTable1),KErrLocked);	// cannot acquire
	test2 (db.Commit(),KErrNone);	// release lock
	test2 (TheDatabase.Execute(KCreateTable1),KErrNone);	// upgrade to X-lock
	test2 (db.Begin(),KErrLocked);	// cannot share
	test2 (TheTable.Open(db,KTable1),KErrNone);
	TRAP(r,TheTable.InsertL());		// cannot lock
	test2 (r,KErrLocked);
	TheTable.Close();
	test2 (db.Execute(KDropTable1),KErrLocked);	// cannot lock
	test2 (TheTable.Open(TheDatabase,KTable1),KErrNone);
	TRAP(r,TheTable.InsertL());		// we own the lock
	test2 (r,KErrNone);
	TheTable.Cancel();
	TheTable.Close();
	test2 (TheDatabase.Execute(KDropTable1),KErrNone);
	test2 (stat.Int(),KRequestPending);	// no NotifyUnlock yet
	test2 (TheDatabase.Commit(),KErrNone);
	User::WaitForRequest(stat);
	test2 (stat.Int(),ob.ECommit);
//
	ob.Close();
	db.Close();
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0609
@SYMTestCaseDesc        Tests for sharing a database
@SYMTestPriority        Medium
@SYMTestActions         Tests for opening a database more than once.
                        Tests the integrity of the table data
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestShareL()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0609 Open database "));
	Connect();
	TInt r=TheDatabase.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	Execute(KCreateTable1);
	test.Next(_L("Open it again"));
	RDbNamedDatabase db;
	test (db.Open(TheDbs,KTestDatabase,KTestFormat)==KErrNone);
	test (TheTable.Open(db,KTable1)==KErrNone);
	test2 (TheTable.ColCount(),4);
	test2 (TheTable.ColType(1),EDbColInt32);
	test2 (TheTable.ColType(2),EDbColText);
	test2 (TheTable.ColType(3),EDbColLongText);
	test2 (TheTable.ColType(4),EDbColLongBinary);
	delete TheTable.ColSetL();
	TheTable.Close();
//
	test.Next(_L("write table"));
	test (TheTable.Open(TheDatabase,KTable1)==KErrNone);
	WriteRecordsL(KRowCount);
	TheTable.Close();
	test.Next(_L("Check table"));
	TheDatabase.Close();
	TheDatabase=db;
	test (TheTable.Open(TheDatabase,KTable1)==KErrNone);
	test (TheTable.AtBeginning());
	test (!TheTable.AtRow());
	test (!TheTable.AtEnd());
	test (TheTable.CountL()==KRowCount);
	IterateL(TheTable.ENext);
	test (!TheTable.AtBeginning());
	test (!TheTable.AtRow());
	test (TheTable.AtEnd());
	test.Next(_L("Delete rows"));
	test (DeleteSQL()==KRowCount);
	TheTable.Close();
	test.Next(_L("Drop table"));
	Execute(KDropTable1);
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

LOCAL_C void TestScenario1L()
	{
	test.Start(_L("Take stream-lock on client 2"));
	test (TheView.FirstL());
	TheView.GetL();
	RDbColReadStream rcol;
	rcol.OpenLC(TheView,3);
	test2 (rcol.ReadUint16L(),0);
	test.Next(_L("Take a [shared] transaction-lock on client 1"));
	test2 (TheDatabase.Begin(),KErrNone);
	test.Next(_L("Attempt a write-lock on client 1"));
	TRAPD(r,TheTable.InsertL());
	test2 (r,KErrLocked);
	test2 (rcol.ReadUint16L(),1);
	test.Next(_L("Release locks on client 1 and then 2"));
	TheDatabase.Rollback();
	CleanupStack::PopAndDestroy();	// rcol
	test.End();
	}

LOCAL_C void TestScenario2L()
	{
	test.Start(_L("Take stream-lock on client 2"));
	test (TheView.FirstL());
	TheView.GetL();
	RDbColReadStream rcol;
	rcol.OpenLC(TheView,3);
	test2 (rcol.ReadUint16L(),0);
	test.Next(_L("Take a [shared] transaction-lock on client 1"));
	test2 (TheDatabase.Begin(),KErrNone);
	test.Next(_L("Attempt a write-lock on client 1"));
	TRAPD(r,TheTable.InsertL());
	test2 (r,KErrLocked);
	test2 (rcol.ReadUint16L(),1);
	test.Next(_L("Release client 2 stream-lock"));
	CleanupStack::PopAndDestroy();	// rcol
	test.Next(_L("Take write-lock on client 1"));
	TheTable.InsertL();				// read-lock removed
	test.Next(_L("release client 1 locks"));
	TheTable.Cancel();
	TheDatabase.Rollback();
	test.End();
	}

LOCAL_C void TestScenario3L(RDbDatabase& aClient2)
	{
	test.Start(_L("Take transaction-lock on client 1"));
	test2 (TheDatabase.Begin(),KErrNone);
	test.Next(_L("Take stream-lock on client 2"));
	test (TheView.FirstL());
	TheView.GetL();
	RDbColReadStream rcol;
	rcol.OpenLC(TheView,3);
	test2 (rcol.ReadUint16L(),0);
	test.Next(_L("Release client 1 lock"));
	TheDatabase.Rollback();
	test.Next(_L("Take write-lock on client 2"));
	RDbTable table2;			// need a second cursor (cannot do insert with open streams)
	test2 (table2.Open(aClient2,KTable1,table2.EInsertOnly),KErrNone);
	TRAPD(r,table2.InsertL());
	test2 (r,KErrNone);
	test.Next(_L("Release stream-lock"));
	CleanupStack::PopAndDestroy();
	test.Next(_L("Release write-lock"));
	table2.PutL();
	table2.Close();
	test.Next(_L("check the system is unlocked"));
	test2 (TheDatabase.Begin(),KErrNone);
	test2 (TheDatabase.Commit(),KErrNone);
	test.End();
	}

LOCAL_C void TestScenario4L()
	{
	test.Start(_L("Take transaction-lock on client 1"));
	test2 (TheDatabase.Begin(),KErrNone);
	test.Next(_L("Take stream-lock on client 1"));
	test (TheTable.FirstL());
	TheTable.GetL();
	RDbColReadStream rcol;
	rcol.OpenLC(TheTable,3);
	test2 (rcol.ReadUint16L(),0);
	test.Next(_L("Take write-lock on client 1"));
	RDbTable table2;			// need a second cursor (cannot do insert with open streams)
	test2 (table2.Open(TheDatabase,KTable1,table2.EInsertOnly),KErrNone);
	TRAPD(r,table2.InsertL());
	test2 (r,KErrNone);
	test.Next(_L("Release write-lock"));
	table2.PutL();
	table2.Close();
	test.Next(_L("Release stream-lock"));
	CleanupStack::PopAndDestroy();
	test.Next(_L("release transaction-lock"));
	test2 (TheDatabase.Commit(),KErrNone);
	test.End();
	}

LOCAL_C void TestScenario5L()
	{
	test.Start(_L("Begin compaction on client 1"));
	RDbIncremental inc;
	TInt s;
	test2 (inc.Compact(TheDatabase,s),KErrNone);
	test.Next(_L("Attempt a stream-lock on client 2"));
	test (TheView.FirstL());
	TheView.GetL();
	RDbColReadStream rcol;
	TRAPD(r,rcol.OpenL(TheView,3));
	test (r==KErrLocked);
	test.Next(_L("Attempt a stream-lock on client 1"));
	TRAP(r,rcol.OpenL(TheTable,3));
	test (r==KErrAccessDenied);
	test.Next(_L("Release compaction lock"));
	inc.Close();
	test.End();
	}

LOCAL_C void TestScenario6L()
	{
	test.Start(_L("Begin compaction on client 1 - open and read columns"));
	TheTable.Close();

	RDbIncremental inc;
	TInt s;
	test2 (inc.Compact(TheDatabase,s),KErrNone);

	test.Next(_L("Attempt to open a table on client 1"));
	TInt r=TheTable.Open(TheDatabase,KTable1);
	test (r==KErrNone);

	//read short column data
	TheTable.FirstL();
	TheTable.GetL();
	TheTable.ColInt32(1);

	// We cant write to tables
	TRAP(r,TheTable.InsertL());
	test (r==KErrAccessDenied);

	RDbColReadStream rcol;
	// We can't read long columns
	TheTable.FirstL();
	TheTable.GetL();
	TRAP(r, rcol.OpenL(TheTable,3));
	test2 (r,KErrAccessDenied);

	// can read other columns
	TRAP(r, rcol.OpenL(TheTable,4));
	test2 (r,KErrNone);
	rcol.Close();

	test.Next(_L("Release compaction lock"));
	inc.Close();
	test.End();
	}

LOCAL_C void TestScenario7L()
	{
	test.Start(_L("Open tables on client 1 - Begin compaction on client 1"));

	TheTable.Close();
	test.Next(_L("Attempt to open a table on client 1"));
	TInt r=TheTable.Open(TheDatabase,KTable1);
	test (r==KErrNone);

	test.Next(_L("Begin compaction on client 1"));
	RDbIncremental inc;
	TInt s;
	test2 (inc.Compact(TheDatabase,s),KErrNone);

	//read short column data
	TheTable.FirstL();
	TheTable.GetL();
	TheTable.ColInt32(1);

	// We cant write to tables
	TRAP(r,TheTable.InsertL());
	test (r==KErrAccessDenied);

	RDbColReadStream rcol;
	// cant read 4K text
	TheTable.FirstL();
	TheTable.GetL();
	TRAP(r, rcol.OpenL(TheTable,3));
	test2 (r,KErrAccessDenied);

	// can read 1K blob
	TRAP(r, rcol.OpenL(TheTable,4));
	test2 (r,KErrNone);
	rcol.Close();

	test.Next(_L("Release compaction lock"));
	inc.Close();
	test.End();
	}

LOCAL_C void TestScenario8L()
	{
	test.Start(_L("Take update-lock on client 1"));
	test2 (TheDatabase.Begin(),KErrNone);
	TheTable.InsertL();
	test.Next(_L("Read blob without locking on client 2"));
	test (TheView.FirstL());
	TheView.GetL();
	RDbColReadStream rcol;
	rcol.OpenLC(TheView,4);			// 1K blob
	test2 (rcol.ReadInt32L(),0);
	CleanupStack::PopAndDestroy();
	TRAPD(r,rcol.OpenL(TheView,3));			// 4K text blob
	test2 (r,KErrLocked);
	test.Next(_L("Release client 1 lock"));
	TheTable.Cancel();
	TheDatabase.Rollback();
	test.End();
	}

/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0610
@SYMTestCaseDesc        Tests for a defect on database
@SYMTestPriority        Medium
@SYMTestActions         Tests for database connection
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestDefectL()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0610 Build test database "));
	Connect();
	TInt r=TheDatabase.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	Execute(KCreateTable1);
	test2 (TheTable.Open(TheDatabase,KTable1),KErrNone);
	TheTable.InsertL();
	RDbColWriteStream wcol;
	wcol.OpenLC(TheTable,3);
	TInt ii;
	for (ii=0;ii<2048;++ii)
		wcol.WriteUint16L(ii);
	wcol.CommitL();
	CleanupStack::PopAndDestroy();
	wcol.OpenLC(TheTable,4);
	for (ii=0;ii<256;++ii)
		wcol.WriteInt32L(ii);
	wcol.CommitL();
	CleanupStack::PopAndDestroy();
	TheTable.PutL();
//
	test.Next(_L("open client 2 connection"));
	RDbNamedDatabase db;
	test2 (db.Open(TheDbs,KTestDatabase,KTestFormat),KErrNone);
	test2 (TheView.Prepare(db,_L("select * from Table1")),KErrNone);
	test2 (TheView.EvaluateAll(),KErrNone);
//
	test.Next(_L("Scenario 1"));
	TestScenario1L();
//
	test.Next(_L("Scenario 2"));
	TestScenario2L();
//
	test.Next(_L("Scenario 3"));
	TestScenario3L(db);
//
	test.Next(_L("Scenario 4"));
	TestScenario4L();
//
	test.Next(_L("Scenario 5"));
	TestScenario5L();
//
	test.Next(_L("Scenario 6"));
	TestScenario6L();
//
	test.Next(_L("Scenario 7"));
	TestScenario7L();
//
	test.Next(_L("Scenario 8"));
	TestScenario8L();
//
	
	test.Next(_L("tidy up"));
	test2 (TheDatabase.Begin(),KErrNone);
	TheView.Close();
	TheTable.Close();
	db.Close();
	Execute(KDropTable1);
	test2 (TheDatabase.Commit(),KErrNone);
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

/**
@SYMTestCaseID          PDS-DBMS-CT-4002
@SYMTestCaseDesc	    Testing RDbIncremental API
@SYMTestPriority 	    High
@SYMTestActions  	    Executing SQL using RDbIncremental, Altering tables, creating and droping indexes
@SYMTestExpectedResults After execution Table should exist in DB, after altering, table colums should change,
						creation of index should create index column in table, dropping index should remove
						it from table
@SYMDEF                 DEF135710
*/
LOCAL_C void TestRDbIncrementalAPIL()
	{
	test.Start(_L(" @SYMTestCaseID:PDS-DBMS-CT-4002 Testing RDbIncremental API"));
	
	Connect();
	TInt err = TheDatabase.Open(TheDbs,KTestDatabase);
	test2(err, KErrNone);
	
	test.Next(_L("RDbIncremental API"));
	RDbIncremental inc;
	TInt step;
	
	test2(TheDatabase.Begin(), KErrNone);
	err = inc.Execute(TheDatabase, KCreateTable2, step);
	test2(err, KErrNone);	
	while(step > 0)
		{
		err = inc.Next(step);
		test2(err, KErrNone);
		}
	inc.Close();
	
	test.Next(_L("AlterTable"));
	CDbColSet* colSet = CDbColSet::NewLC();
	colSet->AddL(TDbCol(_L("Id2"), EDbColUint32));
	colSet->AddL(TDbCol(_L("Memo2"), EDbColText));
	test2(colSet->Count(), 2);
	err = inc.AlterTable(TheDatabase, KTable2, *colSet, step);
	test2(err, KErrNone);
	while(step > 0)
		{
		err = inc.Next(step);
		test2(err, KErrNone);
		}
	inc.Close();
	err = TheDatabase.Commit();
	test2(err, KErrNone);
	
	test.Next(_L("CreateIndex"));
	TDbKeyCol kcol(_L("Id2"), TDbKeyCol::EAsc);
	CDbKey* key = CDbKey::NewLC();
	test2(TheDatabase.Begin(), KErrNone);
	key->AddL(kcol);
	err = inc.CreateIndex(TheDatabase, _L("Id2"), KTable2, *key, step);
	test2(err, KErrNone);
	while(step > 0)
		{
		err = inc.Next(step);
		test2(err, KErrNone);
		}
	inc.Close();
	
	test.Next(_L("DropIndex"));
	err = inc.DropIndex(TheDatabase, _L("Id2"), KTable2, step);
	test2(err, KErrNone);
	while(step > 0)
		{
		err = inc.Next(step);
		test2(err, KErrNone);
		} 
	inc.Close();
	err = inc.Execute(TheDatabase, KDropTable2, step);
	test2(err, KErrNone);
	while(step > 0)
		{
		err = inc.Next(step);
		test2(err, KErrNone);
		}
	err = TheDatabase.Commit();
	test2(err, KErrNone);
	inc.Close();
	
	CleanupStack::PopAndDestroy(2);
	
	TheDatabase.Close();
	Disconnect();
	test.End();
	}

/**
@SYMTestCaseID          PDS-DBMS-CT-4003
@SYMTestCaseDesc	    Testing RDbUpdate API
@SYMTestPriority 	    High
@SYMTestActions  	    Executing DML command using RDbUpdate, checking if rowCount working properly
@SYMTestExpectedResults After DML execution, rowCount should return proper number of afected rows. 
@SYMDEF                 DEF135710
*/
LOCAL_C void TestRDbUpdate()
{
	test.Start(_L(" @SYMTestCaseID:PDS-DBMS-CT-4003 Testing RDbUpdate API"));
	_LIT(DMLinsert, "INSERT INTO Table2 VALUES (2,'Mietek', 'Mietek ma kota')");
	
	Connect();
	TInt err = TheDatabase.Open(TheDbs,KTestDatabase);
	test2(err, KErrNone);
	
	err = TheDatabase.Begin();
	test2(err, KErrNone);
	
	err = TheDatabase.Execute(KCreateTable2, EDbCompareNormal);
	test(err >= KErrNone);
	
	RDbUpdate update;
	err = update.Execute(TheDatabase, DMLinsert, EDbCompareNormal );
	if(err != KErrNone)
		RDebug::Printf("Error on Execute %d",err);
	test2(err, KErrNone);	
	
	TInt rows = update.RowCount();
	RDebug::Printf("Afected rows %d",rows);
	test2(rows, 1);	
	update.Close();
	
	err = TheDatabase.Commit();
	test2(err, KErrNone);
	
	TheDatabase.Close();
	Disconnect();
	test.End();
}

/**
@SYMTestCaseID          PDS-DBMS-CT-4004 
@SYMTestCaseDesc	    Testing RDbRow API
@SYMTestPriority 	    High
@SYMTestActions  	    Test for construction functions for RDbRow
@SYMTestExpectedResults After Creation/Opening row should be a valid object.
@SYMDEF                 DEF135710
*/
LOCAL_C void TestRDbRowL()
{
	test.Start(_L(" @SYMTestCaseID:PDS-DBMS-CT-4004 Testing RDbRow API"));
	const TInt KRowSize = 300;
	const TInt KRealRowSize = 512;

	RDbRow row;
	row.CreateL(KRowSize);
	RDebug::Printf("Rows %d %d",row.Size(), row.MaxSize() );
	test2(row.MaxSize(), KRealRowSize);
	TAny* rptr = row.First();
	
	RDbRow row2;
	row2.Open(rptr, KRowSize, 2*KRowSize);
	RDebug::Printf("Row2s %d %d", KRowSize, row2.MaxSize());
	test2(row2.Size(), KRowSize);
	test2(row2.MaxSize(), 2*KRowSize);
	
	row.Close();
	row2.Close();
	test.End();
}
/*
 * Helper function for SYSLIB-DBMS-CT-4005 Testing TTextOps API
 */
LOCAL_C TInt HelperCompareForTestTTextOps(const TText8*,TInt,const TText8*,TInt)
	{
	return KReturnValueForCompare;
	}
/*
 * Helper function for SYSLIB-DBMS-CT-4005 Testing TTextOps API
 */
LOCAL_C TInt HelperFindForTestTTextOps(const TDesC8&,const TText8*,TInt)
	{
	return KReturnValueForFind;
	}
/**
@SYMTestCaseID          PDS-DBMS-CT-4005
@SYMTestCaseDesc	    Testing TTextOps API
@SYMTestPriority 	    High
@SYMTestActions  	    Test for Compare and Find functions for TTextOps
@SYMTestExpectedResults Test if those functions really calling proper targets.
@SYMDEF                 DEF135710
*/
LOCAL_C void TestTTextOps()
{
	test.Start(_L(" @SYMTestCaseID:PDS-DBMS-CT-4005 Testing TTextOps API"));
	TTextOps text;
	
	text.iCompare8 = HelperCompareForTestTTextOps;
	text.iFind8 = HelperFindForTestTTextOps;
	TInt ret = text.Compare(_L8("Ala ma kota"), _L8("Ala ma konia"));
	test2(ret, KReturnValueForCompare);
	ret = text.Find(_L8("Ala ma kota"), _L8("ma ko"));
	test2(ret, KReturnValueForFind);
	
	test.End();
}

#if defined __WINS__ ||	defined __WINSCW__

LOCAL_C TInt ExecuteRemoteL(const TDesC& aCommand, const TDesC& aCommandLineArg)
	{
	RProcess process;
	User::LeaveIfError(process.Create(aCommand, aCommandLineArg));
	
	TRequestStatus status;
	process.Logon(status);
	process.Resume();

	User::WaitForRequest(status);
	TInt exitReason = process.ExitReason();
	
	process.Close();
	User::LeaveIfError(exitReason);
	
	return exitReason;
	}

#endif //defined __WINS__ ||	defined __WINSCW__

/**
@SYMTestCaseID          PDS-DBMS-CT-4007
@SYMTestCaseDesc	    Testing SPConv - This test case uses the Edbsconv tool 
						so therefore only runs on WINSCW builds
@SYMTestPriority 	    High
@SYMTestActions  	    Launch the external tool to generate SPD files
@SYMTestExpectedResults Should produce SPD files and report no errors
@SYMDEF                 DEF135710
*/
LOCAL_C void TestSPConvL()
	{
	test.Start(_L(" @SYMTestCaseID:PDS-DBMS-CT-4007 Testing SPConv"));
	
#if defined __WINS__ ||	defined __WINSCW__
	
	_LIT(KRomTxtFile1, "z:\\test\\11335577.txt");
	_LIT(KRomTxtFile2, "z:\\test\\11335578.txt");
	_LIT(KRomTxtFile3, "z:\\test\\11335579.txt");
	_LIT(KRomTxtFile4, "z:\\test\\1133557A.txt");
	_LIT(KCommand,"Edbspconv");
	_LIT(KCommandParameter, " /f=%S /b=%S /s");
	
	TBuf<200> commandParameter;
	
	test.Next(_L("Create SPD File from \"11335577.txt\""));
	commandParameter.Format(KCommandParameter, &KRomTxtFile1, &KTestSpdFile1);
	ExecuteRemoteL(KCommand, commandParameter);
		
	test.Next(_L("Create SPD File from \"11335578.txt\""));
	commandParameter.Format(KCommandParameter, &KRomTxtFile2, &KTestSpdFile2);
	ExecuteRemoteL(KCommand, commandParameter);
	
	test.Next(_L("Create SPD File from \"11335579.txt\""));
	commandParameter.Format(KCommandParameter, &KRomTxtFile3, &KTestSpdFile3);
	ExecuteRemoteL(KCommand, commandParameter);
	
	test.Next(_L("Create SPD File from \"1133557A.txt\""));
	commandParameter.Format(KCommandParameter, &KRomTxtFile4, &KTestSpdFile4);
	ExecuteRemoteL(KCommand, commandParameter);
	
#else
	RDebug::Print(_L("Testing SPConv - This test case cannot run on hardware"));
#endif	
	
	test.Next(_L("End"));
	test.End();
	}


/**
@SYMTestCaseID          SYSLIB-DBMS-CT-0611
@SYMTestCaseDesc        Tests the behaviour of observers
@SYMTestPriority        Medium
@SYMTestActions         Tests for DDL,DML
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestObserverL()
	{
	test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0611 Single connection/observer "));
	Connect();
	TInt r=TheDatabase.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	RDbNotifier ob;
	r=ob.Open(TheDatabase);
	test (r==KErrNone);
//
	test.Next(_L("Cancel"));
	TRequestStatus stat;
	ob.NotifyUnlock(stat);
	test (stat==KRequestPending);
	ob.Cancel();
	User::WaitForRequest(stat);
	test (stat==KErrCancel);
//
	test.Next(_L("Close"));
	ob.NotifyUnlock(stat);
	test (stat==KRequestPending);
	ob.Close();
	User::WaitForRequest(stat);
	test (stat==KErrCancel);
//
	test.Next(_L("DDL"));
	r=ob.Open(TheDatabase);
	test (r==KErrNone);
	ob.NotifyUnlock(stat);
	test (stat==KRequestPending);
	Execute(KCreateTable1);
	User::WaitForRequest(stat);
	test (stat==ob.ECommit);
//
	test.Next(_L("DML"));
	ob.NotifyUnlock(stat);
	test (stat==KRequestPending);
	r=TheTable.Open(TheDatabase,KTable1);
	test (r==KErrNone);
	WriteRecordsL(50);
	User::WaitForRequest(stat);
	test (stat==ob.ECommit);
//
	test.Next(_L("Locked read"));
	ob.NotifyUnlock(stat);
	TheDatabase.Begin();
	test (stat==KRequestPending);
	TheDatabase.Commit();
	User::WaitForRequest(stat);
	test (stat==ob.EUnlock);
//
	test.Next(_L("Database closed"));
	ob.NotifyUnlock(stat);
	TheTable.Close();
	TheDatabase.Close();
	User::WaitForRequest(stat);
	test (stat==ob.EClose);
	ob.NotifyUnlock(stat);
	User::WaitForRequest(stat);
	test (stat==ob.EClose);
//
	test.Next(_L("Re-open database"));
	r=TheDatabase.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	ob.NotifyUnlock(stat);
	User::WaitForRequest(stat);
	test (stat==ob.EClose);
	ob.Close();
//
	test.Next(_L("Multiple connections and observers"));
	r=ob.Open(TheDatabase);
	test (r==KErrNone);
	RDbNamedDatabase db2;
	r=db2.Open(TheDbs,KTestDatabase);
	test (r==KErrNone);
	RDbNotifier ob2;
	r=ob2.Open(db2);
	test (r==KErrNone);
//
	test.Next(_L("Cancel"));
	ob.NotifyUnlock(stat);
	TRequestStatus stat2;
	ob2.NotifyUnlock(stat2);
	test (stat==KRequestPending);
	ob.Cancel();
	User::WaitForRequest(stat);
	test (stat==KErrCancel);
	test (stat2==KRequestPending);
	ob2.Cancel();
	User::WaitForRequest(stat2);
	test (stat2==KErrCancel);
//
	test.Next(_L("Close"));
	ob.NotifyUnlock(stat);
	ob2.NotifyUnlock(stat2);
	test (stat2==KRequestPending);
	ob2.Close();
	User::WaitForRequest(stat2);
	test (stat2==KErrCancel);
	test (stat==KRequestPending);
	ob.Close();
	User::WaitForRequest(stat);
	test (stat==KErrCancel);
//
	test.Next(_L("NotifyChange"));
	r=ob.Open(TheDatabase);
	test (r==KErrNone);
	r=ob2.Open(db2);
	test (r==KErrNone);
	ob.NotifyUnlock(stat);
	ob2.NotifyChange(stat2);
	TheDatabase.Begin();
	r=TheDatabase.Commit();
	test (r==KErrNone);
	User::WaitForRequest(stat);
	test (stat==ob.EUnlock);
	test (stat2==KRequestPending);
	ob.NotifyUnlock(stat);
	db2.Begin();
	r=db2.Commit();
	test (r==KErrNone);
	User::WaitForRequest(stat);
	test (stat==ob.EUnlock);
	test (stat2==KRequestPending);
	Execute(KDropTable1);
	User::WaitForRequest(stat2);
	test (stat2==ob.ECommit);
//
	test.Next(_L("missed event"));
	ob.NotifyUnlock(stat);
	User::WaitForRequest(stat);
	test (stat==ob.ECommit);
//
	test.Next(_L("database close"));
	ob.NotifyUnlock(stat);
	test (stat==KRequestPending);
	ob2.NotifyUnlock(stat2);
	test (stat2==KRequestPending);
	TheDatabase.Close();
	User::After(0x20000);	// ~.1s
	test (stat==KRequestPending);
	test (stat2==KRequestPending);
	db2.Close();
	User::WaitForRequest(stat);
	test (stat==ob.EClose);
	User::WaitForRequest(stat2);
	test (stat2==ob.EClose);
	ob.NotifyUnlock(stat);
	User::WaitForRequest(stat);
	test (stat==ob.EClose);
	ob.Cancel();
	ob.Close();
	ob2.NotifyUnlock(stat2);
	User::WaitForRequest(stat2);
	test (stat2==ob.EClose);
	ob2.Cancel();
	ob2.Close();
//
	Disconnect();
	test.End();
	}

LOCAL_C void setupTestDirectory()
//
// Prepare the test directory.
//
    {
	TInt r=TheFs.Connect();
	test(r==KErrNone);
//
	r=TheFs.MkDir(KTestDatabase);
	test(r==KErrNone || r==KErrAlreadyExists);
	}

LOCAL_C void setupCleanup()
//
// Initialise the cleanup stack.
//
    {
	TheTrapCleanup=CTrapCleanup::New();
	test(TheTrapCleanup!=NULL);
	TRAPD(r,\
		{\
		for (TInt i=KTestCleanupStack;i>0;i--)\
			CleanupStack::PushL((TAny*)0);\
		CleanupStack::Pop(KTestCleanupStack);\
		});
	test(r==KErrNone);
	}

void DoTests()
	{
	TVersionName n=RDbs::Version().Name();
	test.Printf(_L("DBMS server v%S\n"),&n);
	TInt r;
	test.Start(_L("Connection"));
	TestConnect();
	
	test.Next(_L("Open Database"));
	TRAP(r,TestOpenL());
	test2 (r,KErrNone);
	
	test.Next(_L("test defect"));
	TRAP(r,TestDefectL());
	test2 (r,KErrNone);
	
	test.Next(_L("Share Database"));
	TRAP(r,TestShareL());
	test2 (r,KErrNone);
	
	test.Next(_L("Locking"));
	TRAP(r,TestLocking());
	test2 (r,KErrNone);
	
	test.Next(_L("Observers"));
	TRAP(r,TestObserverL());
	test2 (r,KErrNone);
	
	test.Next(_L("Benchmarks"));
	TRAP(r,TestBenchL());
	test2 (r,KErrNone);
	
	test.Next(_L("RDbIncremental"));
	TRAP(r, TestRDbIncrementalAPIL() );
	test2 (r,KErrNone);
	
	test.Next(_L("RDbUpdate"));
	TestRDbUpdate();

	test.Next(_L("RDbRow"));
	TRAP(r, TestRDbRowL() );
	test2 (r,KErrNone);
	
	test.Next(_L("TTextOpsL"));
	TestTTextOps();
	
	test.Next(_L("TestSPConvL"));
	TRAP(r, TestSPConvL() );
	test2 (r, KErrNone);
	
	test.Next(_L("Waiting for server exit"));
	}


GLDEF_C TInt E32Main()
//
// Test streaming conversions.
//
    {
	__UHEAP_MARK;
	
	test.Title();
	setupTestDirectory();
	DeleteTestFiles();
	setupCleanup();
	DoTests();
	delete TheTrapCleanup;
	DeleteTestFiles();
	
	test.End();
	TheFs.Close();
	test.Close();
	__UHEAP_MARKEND;
	return 0;
    }
