--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines_old/contactsmodel/tsrc/T_RemoteView.cpp Tue Aug 31 15:05:21 2010 +0300
@@ -0,0 +1,720 @@
+// Copyright (c) 2003-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:
+// T_RemoteView creates a database in the main thread. Another thread is created containing
+// a remote view to the same database. A number of contacts are added, then some deleted.
+// Meanwhile whenever the view is updated and available, the second thread prints all the
+// contact IDs to the console using an active object, one at a time (a nicely time-consuming
+// process intended to cause a backlog of messages between the thread).
+// After the contacts are added, a number of them are deleted at once.
+// Before the changes for INC037352 , this would Panic. Now it executes without any errors.
+// Note: This overwrites the default contacts database: c:\system\data\Contacts.cdb
+// This is necessary if this is to be used with phonebook server which requires this file
+//
+//
+
+// System includes
+#include <cntdb.h>
+#include <cntdef.h>
+#include <cntitem.h>
+#include <cntfield.h>
+#include <cntfldst.h>
+
+// System includes
+#include <e32std.h>
+#include <e32twin.h>
+#include <e32test.h>
+#include <cntdbobs.h>
+
+// User includes
+#include "t_utils2.h"
+
+// Constants
+
+_LIT(KThreadNameOne, "OneThread");
+
+_LIT(KViewNameOne, "T_RemoteView-ViewOne");
+
+const TInt KNumberOfContacts = 300;
+const TInt KNumberOfContactsForTest2 = 300;
+const TInt KNumberToDeleteAtOneTime = 45;
+
+static RTest TheTest(_L("T_RemoteView"));
+
+//
+//#pragma mark ==== Class definitions ====
+//
+
+/**
+This class recieves view events, prints them, and then prints all the
+contacts in the view. If a view changes while printing, it will stop,
+starting again from the top when the view becomes available again
+*/
+class CViewObsever : public CActive, public MContactViewObserver
+ {
+ public:
+ CViewObsever(RTest& aTest);
+ void HandleContactViewEvent(const CContactViewBase& aView,const TContactViewEvent& aEvent);
+
+ protected:
+ void RunL();
+ void DoCancel();
+ TInt RunError(TInt aError);
+
+ private:
+ void StartPrintingAllContacts(const CContactViewBase& aView);
+ TBool iReady;
+ RTest& iTest;
+ TInt iCount;
+ TInt iIndex;
+ const CContactViewBase* iView;
+ };
+
+/** This class is used to create a named remote view in its own thread, displaying the contents
+ in a console */
+class CViewer : public CBase
+ {
+ public:
+ static TThreadId Create(const TDesC& aName, const TDesC& aViewName);
+ static TInt LaunchThread(TAny* aAny);
+ static CViewer* NewL(const TDesC& aViewName);
+ static TInt CheckFinished(TAny* self);
+ ~CViewer();
+
+ private:
+ void ConstructL(const TDesC& aViewName);
+ TBool IsDone();
+
+ private:
+ RTest* iTest;
+ CContactDatabase* iDatabase;
+ RContactViewSortOrder iViewSortOrder;
+ CViewObsever* iEventObserver;
+ CContactNamedRemoteView* iView;
+ RSemaphore iSemaphore;
+ CIdle* iSemaphoreWatcher;
+ };
+
+
+//#pragma mark -
+
+CViewObsever::CViewObsever(RTest& aTest) : CActive(EPriorityStandard+4), iTest(aTest)
+ {
+ }
+
+/** Tell to print all contacts in the view (using RunL) */
+void CViewObsever::StartPrintingAllContacts(const CContactViewBase& aView)
+ {
+ ASSERT(iReady);
+
+ if(IsActive())
+ {
+ Cancel();
+ }
+
+ iView = &aView;
+ TRAPD(err, iCount = aView.CountL() );
+ if(err!=KErrNone)
+ {
+ RunError(err);
+ return;
+ }
+ iIndex=iCount;
+ TRequestStatus* stat = &iStatus;
+ User::RequestComplete(stat,KErrNone);
+ SetActive();
+ }
+
+
+/** Print any error. If it's simply not ready, ignore it (but end the printing cycle) */
+/** And, if it's not found ... do the same ... this happens when the test system is slow
+either due to other processes running or the speed of the processor itself. If the test
+in question is Test2L, the attempt to gain a count may have indicated the view cannot be
+found. */
+TInt CViewObsever::RunError(TInt aError)
+ {
+ iTest.Printf(_L("Error: %i\n"),aError);
+ if(aError == KErrNotReady | aError == KErrNotFound)
+ // view is not ready or count returned not found
+ {
+ /* Can only get here by a Leave in AtL() (or by being called manually an equvalent Leaver in CountL() in StartPrintingAllContacts() )
+ This means the object thinks the state is ready, but it really is not .
+ This can *only* ever happen if an EUnavailable event got sent,
+ but it's not been recieved yet.
+ So, to save the trouble set the readiness to false and just sit and wait
+ for the EUnavailable and (eventually) the EReady events
+ */
+ iReady=EFalse;
+ return KErrNone;
+ }
+ else if(aError == KErrNotFound)
+ {
+ return KErrNone;
+ }
+ return aError;
+ }
+
+/** Print the next item in the view*/
+void CViewObsever::RunL()
+ {
+ if(iReady && --iIndex > (iCount-10))
+ {
+ iView->AtL(iIndex);
+
+ TRequestStatus* stat = &iStatus;
+ User::RequestComplete(stat,KErrNone);
+ SetActive();
+ }
+ iTest.Printf(_L("\n"));
+ }
+
+/** Stop printing */
+void CViewObsever::DoCancel()
+ {
+ iIndex = iCount;
+ iTest.Printf(_L("Cancelling view observer\n"));
+ }
+
+/** Print the current readiness state and the incoming event. If the view is ready, start printing the list of contacts*/
+void CViewObsever::HandleContactViewEvent(const CContactViewBase& aView,const TContactViewEvent& aEvent)
+ {
+ TPtrC name(RThread().Name());
+ if(iReady)
+ {
+ iTest.Printf(_L("%S Ready: "), &name);
+ }
+ else
+ {
+ iTest.Printf(_L("%S Not ready: "), &name);
+ }
+ switch(aEvent.iEventType)
+ {
+ case TContactViewEvent::EUnavailable:
+ iReady = EFalse;
+ iTest.Printf(_L("EUnavailable\n"));
+ break;
+ case TContactViewEvent::EReady:
+ iReady = ETrue;
+ iTest.Printf(_L("EReady\n"));
+ break;
+ case TContactViewEvent::ESortOrderChanged:
+ iTest.Printf(_L("ESortOrderChanged\n"));
+ break;
+ case TContactViewEvent::ESortError:
+ case TContactViewEvent::EServerError:
+ case TContactViewEvent::EIndexingError:
+ iTest.Printf(_L("Error: %x %i\n"),aEvent.iEventType, aEvent.iInt);
+ iReady = EFalse;
+ break;
+ case TContactViewEvent::EItemAdded:
+ iTest.Printf(_L("EItemAdded %i (%i)\n"), aEvent.iContactId, aEvent.iInt);
+ break;
+ case TContactViewEvent::EItemRemoved:
+ iTest.Printf(_L("EItemRemoved\n"));
+ break;
+ case TContactViewEvent::EGroupChanged:
+ iTest.Printf(_L("EGroupChanged\n"));
+ break;
+ default:
+ return;
+ }
+
+ if(iReady)
+ {
+ StartPrintingAllContacts(aView);
+ }
+ }
+
+//#pragma mark -
+
+CViewer* CViewer::NewL(const TDesC& aViewName)
+ {
+ CViewer* self = new(ELeave) CViewer;
+ CleanupStack::PushL(self);
+ self->ConstructL(aViewName);
+ CleanupStack::Pop(self);
+ return(self);
+ }
+
+CViewer::~CViewer()
+ {
+ TPtrC name(RThread().Name());
+ iTest->Printf(_L("Deleting viewer: %S\n"), &name);
+ iView->Close(*iEventObserver);
+ delete iEventObserver;
+ iViewSortOrder.Close();
+
+ delete iDatabase;
+ iSemaphore.Close();
+ iTest->End();
+ iTest->Close();
+ delete iTest;
+ delete iSemaphoreWatcher;
+ CActiveScheduler::Stop();
+ }
+
+void CViewer::ConstructL( const TDesC& aViewName)
+ {
+
+ TPtrC name(RThread().Name());
+
+ // Create and name an RTest
+ iTest = new(ELeave) RTest(name);
+ CConsoleBase* console = iTest->Console();
+ // Position our console window
+ if(console)
+ {
+ TSize size = console->ScreenSize();
+ size.iWidth = size.iWidth /2 -3;
+ console = Console::NewL(name, size);
+ }
+ else
+ {
+ TSize size(40,10);
+ console = Console::NewL(name, size);
+ }
+ delete const_cast<RTest*>(iTest)->Console();
+ iTest->SetConsole(console);
+ console->SetTitle(name);
+ iTest->Start(_L("Thread: Starting Viewer"));
+
+ iTest->Next(_L("Thread: Creating view sort order"));
+ iViewSortOrder.AppendL(KUidContactFieldFamilyName);
+ iViewSortOrder.AppendL(KUidContactFieldGivenName);
+ iViewSortOrder.AppendL(KUidContactFieldCompanyName);
+
+ iTest->Next(_L("Thread: Creating view observer"));
+ iEventObserver = new(ELeave) CViewObsever(*iTest);
+
+ User::LeaveIfError(iSemaphore.OpenGlobal(aViewName));
+
+ User::After(2000000); // give the other thread a chance to do stuff
+ TInt err=KErrNotFound;
+ TInt count=0;
+ while(err!=KErrNone)
+ {
+ if(count)
+ {
+ iTest->Printf(_L(" retry %i\n"),count);
+ }
+ else
+ {
+ iTest->Next(_L("Thread: Starting Database"));
+ }
+ TRAP(err, iDatabase = CContactDatabase::OpenL());
+ count++;
+ if(count>30)
+ {
+ User::Leave(err);
+ }
+ }
+
+ CActiveScheduler::Add(iEventObserver);
+
+ iTest->Next(_L("Thread: Starting View"));
+ iView = CContactNamedRemoteView::NewL(*iEventObserver, aViewName, *iDatabase, iViewSortOrder, EContactsOnly);
+
+ iSemaphore.Signal(); // main thread can start doing things
+ iSemaphoreWatcher = CIdle::NewL(CActive::EPriorityIdle-100); // really very idle
+ TCallBack callback(CViewer::CheckFinished,this);
+ iSemaphoreWatcher->Start(callback);
+ }
+
+TBool CViewer::IsDone()
+ {
+ return ETrue;
+ }
+
+TInt CViewer::CheckFinished(TAny* self)
+ {
+ if( ((CViewer*) self)->IsDone() )
+ {
+ delete ((CViewer*) self);
+ return false;
+ }
+ return true;
+ }
+
+static CViewer* InitL(TDesC& aViewName)
+ {
+ CActiveScheduler *scheduler=new(ELeave) CActiveScheduler;
+ CActiveScheduler::Install(scheduler);
+ return CViewer::NewL(aViewName);
+ }
+
+TInt CViewer::LaunchThread(TAny* aAny)
+ {
+ __UHEAP_MARK;
+ CTrapCleanup* cleanUpStack=CTrapCleanup::New();
+ if (!cleanUpStack)
+ {
+ return KErrNoMemory;
+ }
+ TRAPD(r,InitL(*((TDesC*) aAny)));
+ if (r==KErrNone)
+ {
+ User::After(2000000); // give the other thread a chance to do stuff
+ CActiveScheduler::Start();
+ User::After(2000000); // give the other thread a chance to do stuff
+ }
+ delete CActiveScheduler::Current();
+ delete cleanUpStack;
+
+ __UHEAP_MARKEND;
+ return r;
+ }
+
+/* This is the only thing one needs to call in order to set the view in motion. */
+/** Creates a remote view in its own thread.
+@param aName the name of the thread
+@param aViewName the name of the view*/
+TThreadId CViewer::Create(const TDesC& aName, const TDesC& aViewName)
+ {
+ RThread thread;
+ TInt err = thread.Create( aName, CViewer::LaunchThread, KDefaultStackSize, 0x2000, 0x20000, (void*) &aViewName, EOwnerThread );
+ if(err!=KErrNone)
+ {
+ return KNullUidValue;
+ }
+ thread.Resume();
+ thread.SetPriority(EPriorityNormal);
+ TThreadId id = thread.Id();
+ thread.Close();
+ return id;
+ }
+
+
+
+//
+//#pragma mark -
+//
+
+/** This object creates and closes a thread containing a view to the contacts database.
+ The view will output all changes and the current state of the database using an RDebug object.
+ This thread acts completely independently until it is told to close.
+ @see CViewObsever
+
+ Call Open() to create initailise the other thread. WaitUntilDbIsReady() or Close() must be called eventually.
+ if CheckIfDbIsReady() returns true, WaitUntilDbIsReady() will return immediately.
+ Close() is synchronous and will not return until the other thread dies.
+*/
+class RRemoteViewThread
+ {
+ public:
+ TInt Open(const TDesC& aViewName, const TDesC& aThreadName);
+ void WaitUntilDbIsReady();
+ TBool CheckIfDbIsReady();
+ TInt Close();
+
+ private:
+ RSemaphore iSemaphore;
+ TBool iDbReady;
+ TRequestStatus iStatus;
+ };
+
+/** Create and initalise the view thread.
+ There is no reason why the view name and thread name cannot be the same.
+ This call is more or less asynchronous, and the thread will not be immediately ready.
+ Call CheckIfDbIsReady() or WaitUntilDbIsReady() to find out if the thread is ready.
+ This can be closed at any time, even if the thread is not ready.
+@param aViewName The name of the view
+@param aThreadName The name of the thread.
+@return the error state
+*/
+
+TInt RRemoteViewThread::Open(const TDesC& aViewName, const TDesC& aThreadName)
+ {
+ // Thread1: create semaphone for communicating w/Thread 2
+ iDbReady = EFalse;
+
+ //create semphore for communicating between threads:
+ TInt err = iSemaphore.CreateGlobal(aViewName, 0);
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ // this thread: Start Other thread
+ TheTest.Printf(_L("Starting view thread %S"), &aThreadName);
+ TThreadId id = CViewer::Create(aThreadName, aViewName);
+
+ RThread thread;
+ err = thread.Open(id);
+ if(err==KErrNone)
+ { // tell kernel that this thread wants to know when the other thread dies
+ TRequestStatus* stat = &iStatus;
+ User::RequestComplete(stat,KRequestPending);
+ thread.Logon(iStatus);
+ thread.Close();
+ }
+
+ return err;
+ }
+
+/** Sit and wait until the thread is ready*/
+void RRemoteViewThread::WaitUntilDbIsReady()
+ {
+ if(iDbReady)
+ {
+ return;
+ }
+ iSemaphore.Wait();
+ iDbReady = ETrue;
+ }
+
+/** Always call WaitUntilDbIsReady after this returns a true result
+@return true if the thread is ready, false otherwise*/
+TBool RRemoteViewThread::CheckIfDbIsReady()
+ {
+ if(iDbReady)
+ {
+ return ETrue;
+ }
+ iDbReady = ETrue;
+ return iDbReady;
+ }
+
+/** Signal to the other thread that it should shut down and then wait for it to signal that is has closed
+*/
+TInt RRemoteViewThread::Close()
+ {
+ if(!iDbReady)
+ {
+ WaitUntilDbIsReady();
+ }
+ iSemaphore.Signal(); // tell thread one it can die now
+ TheTest.Printf(_L("Waiting for other thread to die"));
+ // iStatus is set when the other thread dies
+ User::WaitForRequest(iStatus);
+ iSemaphore.Close();
+ return iStatus.Int();
+ }
+
+//
+//#pragma mark ==== Test Function Prototypes ====
+//
+void PopulateTestDatabaseL(CContactDatabase& aDatabase);
+void TestL();
+void doMainL();
+
+//
+//#pragma mark ==== Test Function Implementations ====
+//
+const TInt KPrintFreq=10;
+
+/** add aCount random contacts to the open database. The 1st item will always be the own card */
+CContactIdArray* PopulateTestDatabaseLC(CContactDatabase& aDatabase, TInt aCount )
+ {
+ __ASSERT_ALWAYS(aCount>0, User::Invariant());
+ CContactIdArray* contacts = CContactIdArray::NewLC();
+ CRandomContactGenerator* generator = CRandomContactGenerator::NewL();
+ CleanupStack::PushL( generator );
+ generator->SetDbL(aDatabase);
+
+ TInt bit = CContactDatabase::ESmsable;
+
+ TContactItemId itemId = generator->AddTypicalContactForFilterL(bit);
+ TInt counter = 0;
+ TheTest.Printf(_L("Adding own card %d\n"), counter++);
+ CContactItem* ownerCardItem = aDatabase.ReadContactL( itemId );
+ CleanupStack::PushL( ownerCardItem );
+ aDatabase.SetOwnCardL( *ownerCardItem );
+ contacts->AddL(itemId);
+ for(;counter<aCount;counter++)
+ {
+ itemId = generator->AddTypicalContactForFilterL(bit);
+ if(counter%KPrintFreq==0)
+ {
+ TheTest.Printf(_L("Added contact %d: %i\n"), counter, itemId);
+ }
+ contacts->AddL(itemId);
+ }
+ CleanupStack::PopAndDestroy( ownerCardItem );
+ CleanupStack::PopAndDestroy( generator );
+ return contacts;
+ }
+
+
+/** Test that the view thread always correctly knows weather or not the view is ready for use*/
+void Test1L()
+ {
+ // Thread 1: Create db
+ CContactDatabase* database = CContactDatabase::ReplaceL();
+ CleanupStack::PushL( database );
+
+ // Thread1: create and start a thread with a remote view
+ RRemoteViewThread viewer;
+ User::LeaveIfError(viewer.Open(KViewNameOne, KThreadNameOne));
+ CleanupClosePushL(viewer);
+
+ // Thread 2: Open database
+ // Thread 2: Create named view
+
+ viewer.WaitUntilDbIsReady(); // wait until we've at least got the Db open (can cause file sharing trouble otherwise)
+
+ // Thread 1: add a whole lot of contacts
+ CContactIdArray& contacts = *PopulateTestDatabaseLC(*database, KNumberOfContacts );
+ // Thread 2: print all the contacts in the view
+
+ TInt64 seed=85204;
+ CContactIdArray* toDelete = CContactIdArray::NewLC();
+ TInt i;
+ TInt end = contacts.Count();
+ TInt start = Math::Rand(seed)%end;
+ if(start+KNumberToDeleteAtOneTime<end)
+ {
+ end = start+KNumberToDeleteAtOneTime;
+ }
+ TheTest.Printf(_L("Deleting contatcs %i -- %i\n"), contacts[start],contacts[end-1]);
+ for(i=start;i<end;i++)
+ {
+ toDelete->AddL(contacts[i]);
+ }
+ contacts.Remove(start,end-start);
+ database->DeleteContactsL(*toDelete);
+ toDelete->Reset();
+
+ CleanupStack::PopAndDestroy(toDelete );
+ CleanupStack::PopAndDestroy(&contacts );
+ CleanupStack::Pop( );
+ TheTest( viewer.Close() == KErrNone );
+
+ CleanupStack::PopAndDestroy(database );
+
+ }
+
+/** Test to make sure another thread can open the db when this one is performing many operations on it */
+void Test2L()
+ {
+ // Thread 1: Create db
+ CContactDatabase* database = CContactDatabase::ReplaceL();
+ CleanupStack::PushL( database );
+
+ // Thread 1: add a whole lot of contacts
+ CContactIdArray& contacts = *PopulateTestDatabaseLC(*database, KNumberOfContactsForTest2 );
+
+
+ // Thread 1: create a group
+ CContactItem* group = database->CreateContactGroupL(_L("My Group"));
+ CleanupStack::PushL(group);
+ TContactItemId groupId = group->Id();
+
+ // Thread1: create and start a thread with a remote view
+ RRemoteViewThread viewer;
+ User::LeaveIfError(viewer.Open(KViewNameOne, KThreadNameOne));
+ CleanupClosePushL(viewer);
+
+ // Thread 1: start hammering the database
+ TInt i;
+ TInt err;
+ for(i=-1/*contacts.Count()-1*/;i>=0;i--)
+ {
+ TRAP(err,database->AddContactToGroupL(contacts[i],groupId));
+ if(err==KErrNotReady || err==KErrLocked)
+ {
+ i++; // try again
+ }
+ else
+ {
+ User::LeaveIfError(err);
+ if(i%KPrintFreq==0)
+ {
+ TheTest.Printf(_L("Added contact %d to group\n"),contacts[i]);
+ viewer.CheckIfDbIsReady(); // acknowledge db-is-ready signal
+ }
+ }
+ }
+
+ // Thread 2: Open database
+ // Thread 2: Create named view
+
+ viewer.WaitUntilDbIsReady(); // just in case is not ready yet -- acknowledge db-is-ready signal
+
+ // Thread 2: print all the contacts in the view
+
+ CContactIdArray* toDelete = CContactIdArray::NewLC();
+ TInt64 seed=85204;
+ TInt end = contacts.Count();
+ TInt start = Math::Rand(seed)%end;
+ if(start+KNumberToDeleteAtOneTime<end)
+ {
+ end = start+KNumberToDeleteAtOneTime;
+ }
+ TheTest.Printf(_L("\nDeleting contacts %i -- %i\n"), contacts[start],contacts[end-1]);
+ for(i=start;i<end;i++)
+ {
+ toDelete->AddL(contacts[i]);
+ }
+ contacts.Remove(start,end-start);
+ database->DeleteContactsL(*toDelete);
+ toDelete->Reset();
+
+ CleanupStack::PopAndDestroy(toDelete );
+ CleanupStack::Pop( );
+ TheTest( viewer.Close() == KErrNone );
+
+ CleanupStack::PopAndDestroy(group );
+ CleanupStack::PopAndDestroy(&contacts );
+ CleanupStack::PopAndDestroy(database );
+ }
+
+
+//
+// -------> Static global functions (source)
+//
+void doMainL()
+ {
+ CActiveScheduler* scheduler = new (ELeave) CActiveScheduler;
+ CleanupStack::PushL(scheduler);
+ CActiveScheduler::Install(scheduler);
+
+ // This call is just included to get rid of a linker warning LNK4089
+ // Probably the reason for the warning is that the MVC++ environment
+ // sometimes does not realise when a library is actually used.
+ // We do not care about if the server is hung or not, we just want to
+ // make the call.
+
+ // Delete any existing ini file so that we can be sure this test is ok
+ Test1L(); // Test read-when-not-ready Panic defect
+
+ Test2L();
+ CleanupStack::PopAndDestroy( scheduler );
+ // delete db file here
+ }
+
+
+/**
+
+@SYMTestCaseID PIM-T-REMOTEVIEW-0001
+
+*/
+
+GLDEF_C TInt E32Main()
+ {
+ RDebug::Print(_L("t_bench started"));
+
+ CTrapCleanup* cleanupStack = NULL;
+ __UHEAP_MARK;
+ TheTest.Start(_L("@SYMTESTCaseID:PIM-T-REMOTEVIEW-0001 Remote View testing"));
+
+ TheTest.Title();
+ cleanupStack = CTrapCleanup::New();
+ TRAPD(ret, doMainL());
+ TheTest(ret == KErrNone);
+ delete cleanupStack;
+
+ TheTest.End();
+ TheTest.Close();
+ __UHEAP_MARKEND;
+ return(KErrNone);
+ }