/*
* Copyright (c) 2007-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 <e32std.h>
#include <e32test.h>
#include <e32const.h>
#include <collate.h>
#include <cntdb.h>
#include <cntitem.h>
#include <cntfldst.h>
#include "t_utils2.h"
#include "t_utils.h"
#include <badesca.h>
LOCAL_D TInt ThreadFunction(TAny*);
_LIT(KDatabaseFileName, "C:T_OwnCardDelete");
_LIT(KOwnCardMutex, "OwnCardMutex");
_LIT(KOwnCard, "OwnCard");
_LIT(KThreadFunc, "ThreadFunc");
const TInt KTotalContacts = 4;
const TInt KTimeout = 2000000;
LOCAL_D TInt gEventCounter = 0;
LOCAL_D TInt gUnknownEventCounter = 0;
// Add*KTotalContacts + 1*OwnCardChanged + 1*UnknownChanges + OwnCardDeleted
const TInt KMaxNumOfEvents = KTotalContacts + 3;
const TInt KMaxUnknownEvents = 1;
class RTestPtr
{
public:
RTestPtr(RTest* aPtr) : iPtr(aPtr)
{
}
~RTestPtr()
{
iPtr->Close();
delete iPtr;
}
RTest* operator->()
{
return iPtr;
}
RTest& Ref()
{
return *iPtr;
}
private:
RTest* iPtr;
};
class CMyObserver : public CActive, public MContactDbObserver
{
public:
CMyObserver() : CActive(EPriorityIdle)
{
iStart.UniversalTime();
}
void Activate();
private: // From CActive.
void RunL();
void DoCancel();
TInt RunError(TInt aError);
private: // From MContactDbObserver
void HandleDatabaseEventL(TContactDbObserverEvent aEvent);
private:
TTime iStart;
TTime iEnd;
};
void CMyObserver::RunL()
{
iEnd.UniversalTime();
// Timeout has been excedeed, stop waiting
if( iEnd.MicroSecondsFrom(iStart.Int64()) > KTimeout )
{
CActiveScheduler::Stop();
}
else // Waiting for next event
{
Activate();
}
}
void CMyObserver::DoCancel()
{
}
TInt CMyObserver::RunError(TInt aError)
{
return aError;
}
void CMyObserver::Activate()
{
if(!IsActive())
{
TRequestStatus* pS = &iStatus;
User::RequestComplete(pS, KErrNone);
SetActive();
}
}
void CMyObserver::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
{
switch( aEvent.iType )
{
case EContactDbObserverEventUnknownChanges:
{
gUnknownEventCounter++;
}break;
default:
{
Activate();
break;
}
}
gEventCounter++;
}
LOCAL_C void WaitForNotificationsL(CContactDatabase* aDb)
{
CMyObserver* observer = new (ELeave) CMyObserver();
CleanupStack::PushL(observer);
CContactChangeNotifier* notifier = CContactChangeNotifier::NewL(*aDb, observer);
CleanupStack::PushL(notifier);
CActiveScheduler::Add(observer);
observer->Activate();
CActiveScheduler::Start();
CleanupStack::PopAndDestroy(notifier);
CleanupStack::PopAndDestroy(observer);
}
LOCAL_C void DeleteMultipleContactsL(CContactDatabase* aDb)
{
CContactIdArray* idArray = aDb->ContactsChangedSinceL(TTime(0));
CleanupStack::PushL(idArray);
aDb->DeleteContactsL(*idArray);
CleanupStack::PopAndDestroy(idArray);
}
LOCAL_C void DeleteOwnContactL(CContactDatabase* aDb)
{
TContactItemId contactId = aDb->OwnCardId();
aDb->DeleteContactL(contactId);
}
LOCAL_C void TestSetOwnCardL(CContactDatabase* aDb)
{
CContactIdArray* idArray = aDb->ContactsChangedSinceL(TTime(0));
CleanupStack::PushL(idArray);
TInt count = idArray->Count();
if (count > 0)
{
TInt someIndex(0); //Make the first contact own contact
TContactItemId id = (*idArray)[someIndex];
CContactItem* item = aDb->ReadContactLC(id); // take ownership of item
aDb->SetOwnCardL(*item);
CleanupStack::PopAndDestroy(item);
}
CleanupStack::PopAndDestroy(idArray);
}
LOCAL_C void PopulateDatabaseL(CContactDatabase* aDb, TInt aCntCount)
{
CRandomContactGenerator* randomCnt = CRandomContactGenerator::NewL();
CleanupStack::PushL(randomCnt);
randomCnt->SetDbL(*aDb);
for (TInt i = 0; i < aCntCount; ++i)
{
randomCnt->AddTypicalRandomContactL();
}
CleanupStack::PopAndDestroy(randomCnt);
}
LOCAL_D void SwitchToMainThread(RMutex& aMutex)
{
RThread::Rendezvous(KErrNone);
aMutex.Wait();
aMutex.Signal();
User::After(10);
}
LOCAL_D void SwitchToWorkerThread(RMutex& aMutex, RThread& aThread, TBool aSignal, TBool aWait)
{
if (aSignal)
{
aMutex.Signal();
User::After(10);
}
if (aWait)
{
aMutex.Wait();
}
TRequestStatus status;
aThread.Rendezvous(status);
User::WaitForRequest(status);
}
LOCAL_D void ThreadFirstTestL(RTest& aTest)
{
aTest.Next(_L("-- Running worker thread --"));
RMutex mutex;
TInt errMutex = mutex.OpenGlobal(KOwnCardMutex);
User::LeaveIfError(errMutex);
CleanupClosePushL(mutex);
aTest.Printf(_L("Replacing the database \n"));
CContactDatabase* db = CContactDatabase::ReplaceL(KDatabaseFileName);
CleanupStack::PushL(db);
// get a second database object
CContactDatabase* dbAnother = CContactDatabase::OpenL(KDatabaseFileName);
CleanupStack::PushL(dbAnother);
SwitchToMainThread(mutex); // 1) Back to MT-a
///////////////////////////////////////////////
// Main thread has now opened the database
///////////////////////////////////////////////
aTest.Printf(_L("Populating the database \n"));
PopulateDatabaseL(db, KTotalContacts);
WaitForNotificationsL(db);
aTest.Printf(_L("Set the Own card id \n"));
TestSetOwnCardL(db);
WaitForNotificationsL(db);
aTest.Printf(_L("Checking count value \n"));
TInt count = db->CountL();
TInt countAnother = dbAnother->CountL();
aTest.Printf(_L("Count: %d \n"), count );
aTest(count == KTotalContacts && countAnother == KTotalContacts, __LINE__);
aTest.Printf(_L("Checking the id \n"));
TContactItemId id = db->OwnCardId();
TContactItemId idCopy = dbAnother->OwnCardId();
aTest.Printf(_L("Id: %d \n"), id);
aTest(id != KNullContactId && idCopy != KNullContactId, __LINE__);
SwitchToMainThread(mutex); // 2) Back to MT-b
///////////////////////////////////////////////
// Main thread has now checked the added values
///////////////////////////////////////////////
DeleteMultipleContactsL(db);
WaitForNotificationsL(db);
aTest.Printf(_L("Checking deleted count value \n"));
count = db->CountL();
countAnother = dbAnother->CountL();
aTest.Printf(_L("Count: %d \n"), count );
aTest(count == 0 && countAnother == 0, __LINE__);
aTest.Printf(_L("Checking the deleted id \n"));
id = db->OwnCardId();
idCopy = dbAnother->OwnCardId();
aTest.Printf(_L("Id: %d \n"), id);
aTest(id == KNullContactId && idCopy == KNullContactId, __LINE__);
SwitchToMainThread(mutex); // 3) Back to MT-c
///////////////////////////////////////////////
// Main thread has now checked the deleted values
///////////////////////////////////////////////
CleanupStack::PopAndDestroy(dbAnother);
CleanupStack::PopAndDestroy(db);
CleanupStack::PopAndDestroy(); // close mutex handle
RThread::Rendezvous(KErrNone); // Finish) back to MT-d
}
LOCAL_D void TestSecondL(RTest& aTest)
{
aTest.Next(_L("-- Second Owncard Test --"));
RMutex mtx;
TInt errMutex = mtx.CreateGlobal(KOwnCardMutex);
User::LeaveIfError(errMutex);
CleanupClosePushL(mtx);
RThread thd;
aTest.Printf(_L("Creating worker thread \n"));
TInt errThread = thd.Create(KThreadFunc, ThreadFunction, KDefaultStackSize, KDefaultStackSize*20, KDefaultStackSize*40, NULL);
User::LeaveIfError(errThread);
CleanupClosePushL(thd);
thd.Resume();
SwitchToWorkerThread(mtx, thd, EFalse, ETrue); // a) Run: WT-start to WT-1
///////////////////////////////////////////////
// Worker thread has now replaced the database
///////////////////////////////////////////////
aTest.Printf(_L("Open existing database \n"));
CContactDatabase* db = CContactDatabase::OpenL(KDatabaseFileName);
CleanupStack::PushL(db);
SwitchToWorkerThread(mtx, thd, ETrue, ETrue); // b) Run: WT-1 to WT-2
///////////////////////////////////////////////
// Worker thread has now added the contacts
///////////////////////////////////////////////
WaitForNotificationsL(db);
aTest.Printf(_L("Checking db count \n"));
TInt count = db->CountL();
aTest.Printf(_L("Count: %d \n"), count);
aTest(count == KTotalContacts, __LINE__);
aTest.Printf(_L("Checking owncard id \n"));
TContactItemId id = db->OwnCardId();
aTest.Printf(_L("id: %d \n"), id);
aTest(id != KNullContactId, __LINE__);
SwitchToWorkerThread(mtx, thd, ETrue, ETrue); // c) Run: WT-2 to WT-3
///////////////////////////////////////////////
// Worker thread has now deleted the contacts
///////////////////////////////////////////////
aTest.Printf(_L("Checking deleted db count \n"));
WaitForNotificationsL(db);
count = db->CountL();
aTest.Printf(_L("Count: %d \n"), count);
aTest(count == 0, __LINE__);
aTest.Printf(_L("Checking deleted owncard id \n"));
id = db->OwnCardId();
aTest.Printf(_L("id: %d \n"), id);
aTest(id == KNullContactId, __LINE__);
CleanupStack::PopAndDestroy(db);
SwitchToWorkerThread(mtx, thd, ETrue, EFalse); // d) Run: WT-3 to end
CleanupStack::PopAndDestroy(); // close thd handle
CleanupStack::PopAndDestroy(); // close mtx handle
}
LOCAL_D void TestFirstL(RTest& aTest)
{
aTest.Next(_L("@SYMTestCaseID:PIM-T-OWNCARDDELETE-0001 -- First Owncard Test --"));
aTest.Printf(_L("Replacing database \n"));
CContactDatabase* db = CContactDatabase::ReplaceL(KDatabaseFileName);
CleanupStack::PushL(db);
CContactDatabase* dbAnother = CContactDatabase::OpenL(KDatabaseFileName);
CleanupStack::PushL(dbAnother);
PopulateDatabaseL(db, KTotalContacts);
WaitForNotificationsL(db);
TestSetOwnCardL(db);
WaitForNotificationsL(db);
aTest.Printf(_L("Checking the count value \n"));
TInt count = dbAnother->CountL();
aTest.Printf(_L("Count: %d \n"), count);
aTest(count == KTotalContacts, __LINE__);
aTest.Printf(_L("Checking the id \n"));
TContactItemId id = dbAnother->OwnCardId();
aTest.Printf(_L("id: %d \n"), id);
aTest(id != KNullContactId, __LINE__);
TContactIter iter(*db); //Test that the iterator is updated
TContactItemId firstContactId;
firstContactId = iter.FirstL(); //should be own contactId
DeleteOwnContactL(dbAnother);
WaitForNotificationsL(dbAnother);
TContactItemId secondContactId;
TRAPD (err, secondContactId = iter.FirstL());
aTest(err == KErrNone && firstContactId != secondContactId, __LINE__);
DeleteMultipleContactsL(db);
WaitForNotificationsL(db);
aTest.Printf(_L("Checking the deleted count value \n"));
count = dbAnother->CountL();
aTest.Printf(_L("Count: %d \n"), count);
aTest(count == 0, __LINE__);
aTest.Printf(_L("Checking the deleted id \n"));
id = dbAnother->OwnCardId();
aTest.Printf(_L("id: %d \n"), id);
aTest(id == KNullContactId, __LINE__);
aTest(gEventCounter == KMaxNumOfEvents, __LINE__);
aTest(gUnknownEventCounter == KMaxUnknownEvents, __LINE__);
CleanupStack::PopAndDestroy(2,db);
}
LOCAL_D void ThreadDoTestsL()
{
RTestPtr myTest(new(ELeave) RTest(KThreadFunc));
myTest->Start(KThreadFunc);
ThreadFirstTestL(myTest.Ref());
myTest->End();
}
/**
@SYMTestCaseID PIM-T-OWNCARDDELETE-0001
@SYMTestType UT
@SYMTestPriority Medium
@SYMDEF PDEF107246
@SYMTestCaseDependencies CntModel CoreAppsTest
@SYMTestCaseDesc Check that updates to one CContactDatabase-instance will propagate to a second instance
@SYMTestActions Add some contacts. Set one as own card. Check that the count is the same for all instances.
Check that the own card id is the same for all instances. Delete all contacts and check that
the count is zero and own card id is KNullContactId (-1) for all instances.
@SYMTestExpectedResults see above.
*/
LOCAL_D void DoTestsL()
{
RTestPtr myTest(new(ELeave) RTest(KOwnCard));
myTest->Start(KOwnCard);
TestFirstL(myTest.Ref());
TestSecondL(myTest.Ref());
CContactDatabase::DeleteDatabaseL(KDatabaseFileName);
myTest->Printf(_L("Completed OK \n"));
myTest->End();
}
LOCAL_D TInt ThreadFunction(TAny*)
{
__UHEAP_MARK;
CActiveScheduler* scheduler = new CActiveScheduler;
if (scheduler)
{
CActiveScheduler::Install(scheduler);
CTrapCleanup* cleanup = CTrapCleanup::New();
if (cleanup)
{
TRAPD(err, ThreadDoTestsL());
__ASSERT_ALWAYS(err == KErrNone, User::Panic(_L("Failure in Worker Thread"),err));
delete cleanup;
}
delete scheduler;
}
__UHEAP_MARKEND;
return KErrNone;
}
GLDEF_C TInt E32Main()
{
__UHEAP_MARK;
CActiveScheduler* scheduler = new CActiveScheduler;
if (scheduler)
{
CActiveScheduler::Install(scheduler);
CTrapCleanup* cleanup = CTrapCleanup::New();
if (cleanup)
{
TRAPD(err, DoTestsL());
__ASSERT_ALWAYS(err == KErrNone, User::Panic(_L("Failure in Main Thread"),err));
delete cleanup;
}
delete scheduler;
}
__UHEAP_MARKEND;
return KErrNone;
}