// Copyright (c) 1997-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 <f32file.h>
#include <s32file.h>
#include <cntdb.h>
#include <cntitem.h>
#include <cntfield.h>
#include <cntfldst.h>
#include "t_utils.h"
#include "t_utils2.h"
RFs fs;
CCntTest* CntTest=NULL;
#define xtest(x) TestErr(x,__LINE__)
_LIT(KDataBaseName, "c:T_MultReq.cdb");
const TInt KDeleteGranularity=16; // Must be kept in step with KDeleteTransactionGranularity in CNTDB.CPP
struct TUpdatingResults
{
public:
TUpdatingResults();
void ProcessErrorL(TInt aError);
void ProcessErrorL(TInt aError, TUpdatingResults &aMoreResults);
void PrintResults(const TDesC &aTitle) const;
void AppendResults(TDes &aBuf) const;
public:
TInt iTotalCount;
TInt iLockedCount;
TInt iNotFoundCount;
TInt iModifiedErr;
};
struct SDbmsErrParams
{
inline SDbmsErrParams(TBool *aRemoteThreadFinished,TUpdatingResults *aResults) : iRemoteThreadFinished(aRemoteThreadFinished),iResults(aResults) {};
TBool *iRemoteThreadFinished;
TUpdatingResults *iResults;
};
LOCAL_D RTest test(_L("T_MULTS"));
LOCAL_D CContactDatabase* TheDb1;
LOCAL_D CContactDatabase* TheDb2;
LOCAL_D CArrayFix<TContactItemId>* TheIds;
void DoDEF022709TestL();
const TPtrC KDatabaseFileName=_L("C:T_MULTS");
// Note long names needed to push record size of 255 bytes to trigger problems with DBMS locking
const TPtrC KTestName1=_L("Test Name No%d, plus some padding to make the data over 255 bytes, 0123456789, 0123456789, 0123456789, 0123456789");
const TPtrC KTestName2=_L("Test Name 2 No%d, plus some padding to make the data over 255 bytes, 0123456789, 0123456789, 0123456789, 0123456789");
const TPtrC KTestName3=_L("Test Name 3 No%d, plus some padding to make the data over 255 bytes, 0123456789, 0123456789, 0123456789, 0123456789");
const TInt KTotalNumRecords=20;
struct TOpenBaseParams
{
TFileName iDbName;
TInt iParam1;
TInt iParam2;
};
enum TTestFuncs
{
EThreadFuncHoldOpenForAWhile,
EThreadFuncHoldOpenForever,
EThreadFuncHoldOpenRangeForAWhile,
EThreadFuncHoldOpenRangeForever,
EThreadFuncKeepUpdatingContacts,
};
enum TThreadFuncs
{
EFuncOpenContact,
EFuncDeleteContact,
EFuncKeepReadingContacts,
};
class CFred2 : public CBase
{
public:
~CFred2();
void ConstructL(TInt aFunc, const TDesC8 &aParams);
inline TInt Func() {return(iFunc);};
inline const HBufC8 *Params() {return(iParams);};
void WaitForLogon();
void Kill();
// Other thread funcs
void DoTestL();
protected:
void OpenDbEtcL();
void RunTestL();
void CloseDbEtcL();
void KeepUpdatingContactsL(TInt aNumTimesToLoop,SDbmsErrParams *aResults);
protected:
CContactDatabase* iDb;
TInt iFunc;
HBufC8* iParams;
RThread iThread;
TRequestStatus iLogonStatus;
};
class CNotificationRec : public CBase, public MContactDbObserver
{
public:
static CNotificationRec* NewLC();
~CNotificationRec();
void Wait(TContactItemId aTestId, TContactDbObserverEventType aEventType, const CContactDatabase* aDatabase);
static TInt TimerCallBackL(TAny* aSelf);
void Purge();
public: // from MContactDbObserver
void HandleDatabaseEventL(TContactDbObserverEvent aEvent);
private:
void ConstructL();
private:
CPeriodic* iTimer;
TBool iWaiting;
TContactItemId iTestId;
TUint iConnectionId;
TContactDbObserverEventType iEventType;
};
//
// TUpdatingResults
//
TUpdatingResults::TUpdatingResults()
{
iTotalCount=0;
iLockedCount=0;
iNotFoundCount=0;
iModifiedErr=0;
}
void TUpdatingResults::ProcessErrorL(TInt aError)
{
if (aError==KErrLocked)
iLockedCount++;
else if (aError==KErrNotFound)
iNotFoundCount++;
else if (aError==KErrCommsLineFail)
iModifiedErr++;
else
User::LeaveIfError(aError);
iTotalCount++;
}
void TUpdatingResults::ProcessErrorL(TInt aError, TUpdatingResults &aMoreResults)
{
ProcessErrorL(aError);
aMoreResults.ProcessErrorL(aError);
}
void TUpdatingResults::PrintResults(const TDesC &aTitle) const
{
test.Printf(aTitle);
test.Printf(_L("-Locked/Not Found/Debug/Total[1] %d/%d/%d/%d\n"),iLockedCount,iNotFoundCount,iModifiedErr,iTotalCount);
}
void TUpdatingResults::AppendResults(TDes &aBuf) const
{
aBuf.AppendFormat(_L("%d/%d/%d/%d"),iLockedCount,iNotFoundCount,iModifiedErr,iTotalCount);
}
//
LOCAL_C CFred2 *LaunchFred2L(TInt aFunc, const TDesC8 &aParams)
{
CFred2 *fred2=new(ELeave) CFred2;
fred2->ConstructL(aFunc, aParams);
return(fred2);
}
CNotificationRec* CNotificationRec::NewLC()
{ // static
CNotificationRec* self=new(ELeave) CNotificationRec;
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CNotificationRec::~CNotificationRec()
{
delete iTimer;
}
void CNotificationRec::ConstructL()
{
iTimer=CPeriodic::NewL(0);
}
void CNotificationRec::Wait(TContactItemId aTestId, TContactDbObserverEventType aEventType, const CContactDatabase* aDatabase)
{
iWaiting=ETrue;
iTestId=aTestId;
iEventType=aEventType;
iConnectionId=aDatabase->ConnectionId();
iTimer->Start(5000000,5000000,TCallBack(TimerCallBackL,NULL)); // wait for 5 seconds for notification
CActiveScheduler::Start();
}
void CNotificationRec::Purge()
{
iWaiting=EFalse;
iTimer->Start(2000000,2000000,TCallBack(TimerCallBackL,this)); // wait for 2 seconds for notification
CActiveScheduler::Start();
}
TInt CNotificationRec::TimerCallBackL(TAny* aSelf)
{ // static
// if this gets called, notification hasn't happened
if (((CNotificationRec*)aSelf)->iWaiting)
User::Leave(KErrGeneral);
else
{
((CNotificationRec*)aSelf)->iTimer->Cancel();
CActiveScheduler::Stop();
}
return 0;
}
void CNotificationRec::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
{
if (!iWaiting || aEvent.iType==EContactDbObserverEventRollback)
return;
if (iEventType!=EContactDbObserverEventUnknownChanges)
test(aEvent.iContactId==iTestId);
test(iEventType==aEvent.iType);
switch(iEventType)
{
case EContactDbObserverEventRecover:
case EContactDbObserverEventTablesClosed:
case EContactDbObserverEventTablesOpened:
test(aEvent.iConnectionId==0);
break;
default:
test(iConnectionId==aEvent.iConnectionId);
break;
}
iWaiting=EFalse;
iTimer->Cancel();
CActiveScheduler::Stop();
}
LOCAL_C void PopulateDatabaseL()
//
// Create and populate the database
//
{
TheIds=new(ELeave) CArrayFixFlat<TContactItemId>(5);
CContactDatabase* db=CntTest->CreateDatabaseL();
CntTest->DeleteAllTemplateFieldsL();
TContactItemId id;
for (TInt ii=0;ii<KTotalNumRecords;ii++)
{
CContactItem* item=CContactCard::NewLC();
TBuf<256> name;
name.Format(KTestName1,ii);
SetNameL(*item,KUidContactFieldCompanyName,KUidContactFieldVCardMapUnusedN,name,ETrue);
name.Format(KTestName2,ii);
SetNameL(*item,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,name,ETrue);
name.Format(KTestName3,ii);
SetNameL(*item,KUidContactFieldGivenName,KUidContactFieldVCardMapUnusedN,name,ETrue);
id=db->AddNewContactL(*item);
TheIds->AppendL(id);
CleanupStack::PopAndDestroy(); // item
}
CntTest->CloseDatabase();
}
LOCAL_C TPtrC Name(CContactItem& aItem,TUid aType)
{
CContactItemFieldSet& fieldSet=aItem.CardFields();
const TInt pos=fieldSet.Find(aType);
if (pos==KErrNotFound)
return _L("");
return fieldSet[pos].TextStorage()->Text();
}
LOCAL_C void ReadL()
//
// Access the database with two clients running in the same thread
//
{
CContactItem* item1=NULL;
CContactItem* item2=NULL;
for (TInt ii=0;ii<KTotalNumRecords;ii++)
{
TContactItemId id=(*TheIds)[ii];
item1=TheDb1->ReadContactLC(id);
item2=TheDb2->ReadContactLC(id);
test(Name(*item1,KUidContactFieldCompanyName)==Name(*item2,KUidContactFieldCompanyName));
TBuf<256> testName;
testName.Format(KTestName1,ii);
test(Name(*item1,KUidContactFieldCompanyName)==testName);
CleanupStack::PopAndDestroy(2); // item2, item1
}
}
LOCAL_C void EditL()
{
CContactItem* item1=NULL;
CContactItem* item2=NULL;
for (TInt ii=0;ii<KTotalNumRecords;ii++)
{
TContactItemId id=(*TheIds)[ii];
// edit items in db1
item1=TheDb1->OpenContactL(id);
CleanupStack::PushL(item1);
SetNameL(*item1,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,_L("Family"),EFalse);
TheDb1->CommitContactL(*item1);
CleanupStack::PopAndDestroy(); // item1
// test db2 picks up changes and re-edit items
item2=TheDb2->OpenContactL(id);
CleanupStack::PushL(item2);
test(Name(*item2,KUidContactFieldFamilyName)==_L("Family"));
SetNameL(*item2,KUidContactFieldGivenName,KUidContactFieldVCardMapUnusedN,_L(""),EFalse);
TheDb2->CommitContactL(*item2);
CleanupStack::PopAndDestroy(); // item2
// check db1 notices changes
item1=TheDb1->ReadContactL(id);
test(Name(*item1,KUidContactFieldGivenName)==_L(""));
delete item1;
}
// test each one can lock records
TContactItemId id=(*TheIds)[0];
item1=TheDb1->OpenContactL(id);
CleanupStack::PushL(item1);
TRAPD(err,TheDb2->OpenContactL(id));
test(err==KErrInUse);
TheDb1->CloseContactL(id);
CleanupStack::PopAndDestroy(); // item1
item2=TheDb2->OpenContactL(id);
CleanupStack::PushL(item2);
TRAP(err,TheDb1->OpenContactL(id));
test(err==KErrInUse);
TheDb2->CloseContactL(id);
CleanupStack::PopAndDestroy(); // item2
}
LOCAL_C void AddL()
{
CContactItem* item=CContactCard::NewLC();
TContactItemId id1=TheDb1->AddNewContactL(*item);
TContactItemId id2=TheDb2->AddNewContactL(*item);
CleanupStack::PopAndDestroy(); // item
delete TheDb1->ReadContactL(id2);
delete TheDb2->ReadContactL(id1);
TheDb1->DeleteContactL(id2);
TheDb2->DeleteContactL(id1);
}
LOCAL_C void TouchContactL(CContactDatabase* aDb, TContactItemId aId)
{
CContactItem* item=aDb->OpenContactL(aId);
SetNameL(*item,KUidContactFieldGivenName,KUidContactFieldVCardMapUnusedN,_L(""),EFalse);
aDb->CommitContactL(*item);
delete item;
}
LOCAL_C void AdjustContactAccessCountL(CContactDatabase* aDb, TContactItemId aItemId, TInt aCount)
{
CContactItem *incItem=aDb->OpenContactLX(aItemId);
CleanupStack::PushL(incItem);
while(aCount>0)
{
incItem->IncAccessCount();
aCount--;
}
while(aCount<0)
{
incItem->DecAccessCount();
aCount++;
}
aDb->CommitContactL(*incItem);
CleanupStack::PopAndDestroy(2); // incItem, Close(incItem)
}
LOCAL_C void WaitAndCheckEvent(CNotificationRec* aRec1,CNotificationRec* aRec2,TContactItemId aTestId, TContactDbObserverEventType aEventType, const CContactDatabase* aDatabase)
{
aRec1->Wait(aTestId,aEventType,aDatabase);
if (aRec2)
aRec2->Wait(aTestId,aEventType,aDatabase);
}
LOCAL_C void SetSortOrderL(CContactDatabase* aDb, TFieldType aFieldType)
{
CArrayFix<CContactDatabase::TSortPref>* sortOrder=new(ELeave) CArrayFixFlat<CContactDatabase::TSortPref>(3);
CleanupStack::PushL(sortOrder);
sortOrder->AppendL(CContactDatabase::TSortPref(aFieldType,CContactDatabase::TSortPref::EAsc));
aDb->SortL(sortOrder);
CleanupStack::Pop(); // sortOrder
}
LOCAL_C void TestDeleteContactsErrorL(CContactDatabase* aDb, CContactDatabase* aDb2, CNotificationRec* aRec, CNotificationRec* aRec2, TInt aErrorPos, TBool aChangedMessage)
{
aRec2->Purge();
SetSortOrderL(aDb,KUidContactFieldGivenName);
CContactIdArray *deleteIds=CContactIdArray::NewLC();
CContactItem *openItem=NULL;
TInt startCount=aDb->SortedItemsL()->Count();
TInt startCount2=aDb2->SortedItemsL()->Count();
for(TInt loop=0;loop<(aErrorPos+10);loop++)
{
TContactItemId add=AddContactL(aDb,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,_L("Add1"));
deleteIds->AddL(add);
aRec->Wait(add,EContactDbObserverEventContactAdded,aDb);
aRec2->Wait(add,EContactDbObserverEventContactAdded,aDb);
if (loop==aErrorPos)
{
openItem=aDb2->OpenContactLX(add);
CleanupStack::PushL(openItem);
}
TInt db2Count=aDb2->SortedItemsL()->Count();
test(db2Count==(startCount2+loop+1));
}
test(openItem!=NULL);
TRAPD(delErr,aDb->DeleteContactsL(*deleteIds));
test(delErr==KErrInUse);
User::After(500000); // Let rollback event arrive
if (aChangedMessage)
aRec->Wait(0,EContactDbObserverEventUnknownChanges,aDb);
//
CleanupStack::PopAndDestroy(3); // deleteIds, openItem, openItem->Close()
//
aDb2->RecoverL();
//
if (aChangedMessage)
{
aRec->Wait(0,EContactDbObserverEventTablesClosed,aDb);
// aRec->Wait(0,EContactDbObserverEventUnknownChanges,aDb);
}
//
aDb->OpenTablesL();
aRec->Purge();
aRec2->Purge();
SetSortOrderL(aDb,KUidContactFieldFamilyName);
TInt db1Count=aDb->SortedItemsL()->Count();
TInt db2Count=aDb2->SortedItemsL()->Count();
test(db1Count==db2Count);
if (aChangedMessage)
test(db1Count==(startCount+10+aErrorPos-KDeleteGranularity));
else
test(db1Count==(startCount+10+aErrorPos));
//
deleteIds=CContactIdArray::NewLC(aDb->SortedItemsL());
aDb->DeleteContactsL(*deleteIds);
aRec->Wait(0,EContactDbObserverEventUnknownChanges,aDb);
CleanupStack::PopAndDestroy(); // deleteIds
}
LOCAL_C void WaitForNotificationL()
{
CNotificationRec* rec1=CNotificationRec::NewLC();
CNotificationRec* rec2=CNotificationRec::NewLC();
CContactDatabase* db1=CContactDatabase::OpenL(CntTest->DatabaseName());
CleanupStack::PushL(db1);
CContactChangeNotifier *notify1=CContactChangeNotifier::NewL(*db1,rec1);
CleanupStack::PushL(notify1);
CContactDatabase* db2=CContactDatabase::OpenL(CntTest->DatabaseName());
CleanupStack::PushL(db2);
CContactChangeNotifier *notify2=CContactChangeNotifier::NewL(*db2,rec2);
CleanupStack::PushL(notify2);
TContactItemId touchId=(*TheIds)[0];
TouchContactL(db1,touchId);
WaitAndCheckEvent(rec1,rec2,touchId,EContactDbObserverEventContactChanged,db1); // shouldn't return until the change is notified
//
touchId=(*TheIds)[1];
TouchContactL(db2,touchId);
WaitAndCheckEvent(rec1,rec2,touchId,EContactDbObserverEventContactChanged,db2);
//
TouchContactL(db2,(*TheIds)[0]);
TouchContactL(db2,(*TheIds)[1]);
TouchContactL(db2,(*TheIds)[2]);
db2->DeleteContactL((*TheIds)[1]);
TouchContactL(db2,(*TheIds)[3]);
TouchContactL(db2,(*TheIds)[0]);
// zzz Beef up test code to have a queue of expected events and read rec & rec2 in parallel
WaitAndCheckEvent(rec1,NULL,(*TheIds)[0],EContactDbObserverEventContactChanged,db2);
WaitAndCheckEvent(rec1,NULL,(*TheIds)[1],EContactDbObserverEventContactChanged,db2);
WaitAndCheckEvent(rec1,NULL,(*TheIds)[2],EContactDbObserverEventContactChanged,db2);
WaitAndCheckEvent(rec1,NULL,(*TheIds)[1],EContactDbObserverEventContactDeleted,db2);
WaitAndCheckEvent(rec1,NULL,(*TheIds)[3],EContactDbObserverEventContactChanged,db2);
WaitAndCheckEvent(rec1,NULL,(*TheIds)[0],EContactDbObserverEventContactChanged,db2);
//
AdjustContactAccessCountL(db2,(*TheIds)[2],1);
rec1->Wait((*TheIds)[2],EContactDbObserverEventContactChanged,db2);
db2->DeleteContactL((*TheIds)[2]);
rec1->Wait((*TheIds)[2],EContactDbObserverEventContactDeleted,db2);
AdjustContactAccessCountL(db2,(*TheIds)[2],-1);
TouchContactL(db2,(*TheIds)[0]);
rec1->Wait((*TheIds)[0],EContactDbObserverEventContactChanged,db2);
//
TContactItemId add1=AddContactL(db1,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,_L("Add1"));
TContactItemId add2=AddContactL(db2,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,_L("Add2"));
TContactItemId add3=AddContactL(db1,KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN,_L("Add3"));
rec1->Wait(add1,EContactDbObserverEventContactAdded,db1);
rec1->Wait(add2,EContactDbObserverEventContactAdded,db2);
rec1->Wait(add3,EContactDbObserverEventContactAdded,db1);
//
CContactIdArray* deleteIds=CContactIdArray::NewLC();
deleteIds->AddL(add2);
deleteIds->AddL(add1);
deleteIds->AddL(add3);
db1->DeleteContactsL(*deleteIds);
rec1->Wait(0,EContactDbObserverEventUnknownChanges,db1);
CleanupStack::PopAndDestroy(); // deleteIds
//
TestDeleteContactsErrorL(db1,db2,rec1,rec2,KDeleteGranularity-4,EFalse); // Count lower than delete granularity
TestDeleteContactsErrorL(db1,db2,rec1,rec2,KDeleteGranularity+4,ETrue); // Count Higher than delete granularity
//
CleanupStack::PopAndDestroy(6); // db2, db1, notify2, notify1, rec2, rec1
}
void TestErr(TInt aErr,TInt aLineNum)
{
if (aErr!=KErrNone)
{
test.Printf(_L("Error %d, line %d\n"),aErr,aLineNum);
test(EFalse);
}
}
LOCAL_C void KeepReadingContactsL(TInt ,SDbmsErrParams *aParams)
{
const CContactIdArray* idarray=CContactIdArray::NewLC(TheDb1->SortedItemsL());
User::After(200000); // Synch with other thread
while(!(*aParams->iRemoteThreadFinished))
{
for(TInt idIndex=idarray->Count();idIndex>0;)
{
CContactItem *readItem=NULL;
TContactItemId id=(*idarray)[--idIndex];
TRAPD(err,readItem=TheDb1->ReadContactL(id));
CleanupStack::PushL(readItem);
aParams->iResults->ProcessErrorL(err);
if (err==KErrLocked)
test.Printf(_L("*"));
else if (err==KErrNotFound)
test.Printf(_L("#"));
else if (err==KErrCommsLineFail)
test.Printf(_L("%"));
else
test.Printf(_L("."));
CleanupStack::PopAndDestroy(); // readItem
}
}
test.Printf(_L("\n"));
CleanupStack::PopAndDestroy(); // idArray
}
LOCAL_C void MultiAccessTestL(TInt aFredFunc,TInt aFredParam1,TInt aFredParam2,TInt aFunc,TInt aParam1,TInt aParam2)
{
TOpenBaseParams params;
params.iDbName.Copy(CntTest->DatabaseName());
params.iParam1=aFredParam1;
params.iParam2=aFredParam2;
CFred2* fred2=LaunchFred2L(aFredFunc,TPtrC8((TUint8 *)¶ms,sizeof(params)));
User::After(600000); // Let Fred2 get going
//
switch(aFunc)
{
case EFuncOpenContact:
{
CContactItem *item=NULL;
TRAPD(openErr,item=TheDb1->OpenContactL((*TheIds)[aParam1]));
CleanupStack::PushL(item);
xtest(openErr);
TheDb1->CommitContactL(*item);
CleanupStack::PopAndDestroy();
}
break;
case EFuncDeleteContact:
TheDb1->DeleteContactL((*TheIds)[aParam1]);
break;
case EFuncKeepReadingContacts:
KeepReadingContactsL(aParam1,(SDbmsErrParams *)aParam2);
break;
}
//
if (aFredFunc==EThreadFuncHoldOpenForever || aFredFunc==EThreadFuncHoldOpenRangeForever)
fred2->Kill();
fred2->WaitForLogon();
delete fred2;
User::After(400000); // Let Fred2 shutdown properly
}
/**
@SYMTestCaseID PIM-T-MULTS-0001
*/
LOCAL_C void MultiAccessTestL()
{
test.Start(_L("@SYMTestCaseID:PIM-T-MULTS-0001 Multi 1"));
//
MultiAccessTestL(EThreadFuncHoldOpenForAWhile,1,0,EFuncOpenContact,1,0);
test.Next(_L("Multi 2"));
MultiAccessTestL(EThreadFuncHoldOpenForever,2,0,EFuncOpenContact,3,0);
//
test.Next(_L("Multi 3"));
MultiAccessTestL(EThreadFuncHoldOpenRangeForAWhile,8,9,EFuncOpenContact,5,0);
test.Next(_L("Multi 4"));
MultiAccessTestL(EThreadFuncHoldOpenRangeForever,2,16,EFuncOpenContact,18,0);
//
test.Next(_L("Multi 5"));
MultiAccessTestL(EThreadFuncHoldOpenForAWhile,1,0,EFuncDeleteContact,1,0);
test.Next(_L("Multi 6"));
MultiAccessTestL(EThreadFuncHoldOpenForever,0,0,EFuncDeleteContact,3,0);
//
test.Next(_L("Multi 7"));
TUpdatingResults updateResults1;
TUpdatingResults updateResults2[4];
TBool remoteThreadFinished=EFalse;
SDbmsErrParams params1(&remoteThreadFinished,&updateResults1);
SDbmsErrParams params2(&remoteThreadFinished,&updateResults2[0]);
MultiAccessTestL(EThreadFuncKeepUpdatingContacts,5,(TInt)¶ms2,EFuncKeepReadingContacts,0,(TInt)¶ms1);
updateResults1.PrintResults(_L("Reading"));
updateResults2[0].PrintResults(_L("Open/Commit"));
updateResults2[1].PrintResults(_L("Export"));
updateResults2[2].PrintResults(_L("Import"));
updateResults2[3].PrintResults(_L("Delete"));
//
test.End();
}
void InitialiseDatabaseEtcL()
{
TRAPD(err,PopulateDatabaseL());
xtest(err);
TheDb1=CContactDatabase::OpenL(CntTest->DatabaseName());
TheDb2=CContactDatabase::OpenL(CntTest->DatabaseName());
}
void DeleteDatabaseEtc()
{
delete TheDb1;
delete TheDb2;
delete TheIds;
TheDb1=NULL;
TheDb2=NULL;
TheIds=NULL;
}
class RTestSession : public RSessionBase
{
public:
TInt CreateSession();
};
TInt RTestSession::CreateSession()
{
TVersion version;
return(RSessionBase::CreateSession(_L("CntLockServer"),version,1));
}
void TestServerGoneL()
{
test(CntTest->LockServerProcessCount(ETrue,EFalse,ETrue)==0);
if (CntTest->LockServerSessionExists())
{
test.Printf(_L("Warning - Lock server still exists"));
test.Getch();
}
}
void DoTestsL()
{
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
CTestRegister* TempFiles = CTestRegister::NewLC();
TempFiles->RegisterL(KDataBaseName, EFileTypeCnt);
TempFiles->RegisterL(KDatabaseFileName, EFileTypeCnt);
CntTest->SelectDriveL();
test.Start(_L("Create new database"));
TInt err;
//
TRAP(err,InitialiseDatabaseEtcL());
test.Next(_L("Wait for change notification"));
TRAP(err,WaitForNotificationL());
xtest(err);
DeleteDatabaseEtc();
//
TRAP(err,InitialiseDatabaseEtcL());
xtest(err);
test.Next(_L("Lock tests"));
TRAP(err,MultiAccessTestL());
xtest(err);
DeleteDatabaseEtc();
//
TRAP(err,InitialiseDatabaseEtcL());
xtest(err);
test.Next(_L("Read items"));
TRAP(err,ReadL());
xtest(err);
test.Next(_L("Edit items"));
TRAP(err,EditL());
xtest(err);
test.Next(_L("Add / delete items"));
TRAP(err,AddL());
xtest(err);
test.Next(_L("Wait for change notification"));
TRAP(err,WaitForNotificationL());
xtest(err);
DeleteDatabaseEtc();
TestServerGoneL();
// Now do test for DEF022709 (Propagated) Contact DB Monitoring Error Messages
TRAP(err,DoDEF022709TestL());
xtest(err);
CleanupStack::PopAndDestroy(2); // TempFiles, close fs
}
LOCAL_C TInt FredIIFunc(TAny *aParam);
GLDEF_C TInt E32Main()
{
CntTest=new(ELeave) CCntTest;
CntTest->ConstructL(test,KDatabaseFileName);
TRAPD(err,DoTestsL());
CntTest->EndTestLib(err);
return KErrNone;
}
//
// CFred2
//
CFred2::~CFred2()
{
iThread.Close();
delete iParams;
}
void CFred2::ConstructL(TInt aFunc, const TDesC8 &aParams)
{
iFunc=aFunc;
iParams=aParams.AllocL();
User::LeaveIfError(iThread.Create(_L("FredII"),FredIIFunc,KDefaultStackSize,0x2000,0x20000,this,EOwnerThread));
iThread.Logon(iLogonStatus);
iThread.Resume();
}
void CFred2::Kill()
{
iThread.Kill(0);
}
void CFred2::WaitForLogon()
{
User::WaitForRequest(iLogonStatus);
xtest(iLogonStatus.Int());
}
//
// Fred II from here on
//
LOCAL_C TInt FredIIFunc(TAny *aParam)
{
CActiveScheduler::Install(new(ELeave) CActiveScheduler);
CTrapCleanup* cleanup=CTrapCleanup::New();
TRAPD(err,((CFred2 *)aParam)->DoTestL());
delete cleanup;
delete CActiveScheduler::Current();
return(err);
}
void CFred2::OpenDbEtcL()
{
switch(Func())
{
case EThreadFuncHoldOpenForAWhile:
case EThreadFuncHoldOpenForever:
case EThreadFuncHoldOpenRangeForAWhile:
case EThreadFuncHoldOpenRangeForever:
case EThreadFuncKeepUpdatingContacts:
iDb=CContactDatabase::OpenL(((TOpenBaseParams *)Params()->Ptr())->iDbName);
break;
}
}
void CFred2::CloseDbEtcL()
{
delete iDb;
}
void CFred2::KeepUpdatingContactsL(TInt aNumTimesToLoop,SDbmsErrParams *aParams)
{
TUpdatingResults *openResults=aParams->iResults;
TUpdatingResults *exportResults=aParams->iResults+1;
TUpdatingResults *importResults=aParams->iResults+2;
TUpdatingResults *deleteResults=aParams->iResults+3;
TUpdatingResults totalResults;
const CContactIdArray* idarray=CContactIdArray::NewLC(iDb->SortedItemsL());
for(TInt mode=0;mode<2;mode++)
{
for(TInt loop=0;loop<aNumTimesToLoop;loop++)
{
TInt err=KErrNone;
switch(mode)
{
case 0:
{
for(TInt idIndex=0;idIndex<idarray->Count();idIndex++)
{
CContactItem *openItem=NULL;
TContactItemId id=(*idarray)[idIndex];
TRAP(err,openItem=iDb->OpenContactL(id));
CleanupStack::PushL(openItem);
if (err==KErrNone)
{
TRAP(err,iDb->CommitContactL(*openItem));
iDb->CloseContactL(id);
}
CleanupStack::PopAndDestroy(); // openItem
openResults->ProcessErrorL(err,totalResults);
}
break;
}
case 1:
{
CVCardTestStore* store=NULL;
TRAP(err,store=ExportContactsL(iDb,idarray,CContactDatabase::EExcludeUid,KVCardStoreTypeBuf,NULL,1000));
exportResults->ProcessErrorL(err,totalResults);
if (err==KErrNone)
{
CleanupStack::PushL(store);
CArrayPtr<CContactItem>* items=NULL;
TRAP(err,items=ImportContactsL(iDb,store,CContactDatabase::EExcludeUid));
importResults->ProcessErrorL(err,totalResults);
if (err==KErrNone)
{
CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,items));
for(TInt delLoop=0;delLoop<items->Count();delLoop++)
{
TRAP(err,iDb->DeleteContactL((*items)[delLoop]->Id()));
deleteResults->ProcessErrorL(err,totalResults);
}
CleanupStack::PopAndDestroy(); // items
}
CleanupStack::PopAndDestroy(); // store
}
}
break;
}
TBuf<128> buf;
buf.Format(_L("M:%d,C:%d "),mode,loop);
totalResults.AppendResults(buf);
User::InfoPrint(buf);
}
}
CleanupStack::PopAndDestroy(); // idArray
*aParams->iRemoteThreadFinished=ETrue;
}
void CFred2::RunTestL()
{
switch(Func())
{
case EThreadFuncHoldOpenForever:
case EThreadFuncHoldOpenForAWhile:
{
TContactItemId id=(*TheIds)[((TOpenBaseParams *)(Params()->Ptr()))->iParam1];
CContactItem *item=iDb->OpenContactL(id);
CleanupStack::PushL(item);
if (Func()==EThreadFuncHoldOpenForAWhile)
{
User::After(1000000);
iDb->CommitContactL(*item);
CleanupStack::PopAndDestroy(); // item
}
else
{
TRequestStatus request;
User::WaitForRequest(request); // Wait forever
}
}
break;
case EThreadFuncHoldOpenRangeForAWhile:
case EThreadFuncHoldOpenRangeForever:
{
TInt start=((TOpenBaseParams *)(Params()->Ptr()))->iParam1;
TInt end=((TOpenBaseParams *)(Params()->Ptr()))->iParam2;
CContactItem *items[KTotalNumRecords];
for(TInt loop=start;loop<end;loop++)
{
TContactItemId id=(*TheIds)[loop];
items[loop]=iDb->OpenContactL(id);
/* Weird MARM Death
TRAPD(x,items[loop]=iDb->OpenContactL(id));
TBuf<32> buf;
buf.Format(_L("oc-%d"),x);
test.Printf(buf);*/
CleanupStack::PushL(items[loop]);
}
if (Func()==EThreadFuncHoldOpenRangeForAWhile)
{
User::After(100000);
for(TInt loop=start;loop<end;loop++)
iDb->CommitContactL(*items[loop]);
CleanupStack::PopAndDestroy(end-start);
}
else
{
TRequestStatus request;
User::WaitForRequest(request); // Wait forever
}
}
break;
case EThreadFuncKeepUpdatingContacts:
{
TOpenBaseParams *params=(TOpenBaseParams *)(Params()->Ptr());
KeepUpdatingContactsL(params->iParam1,(SDbmsErrParams *)params->iParam2);
}
break;
}
}
void CFred2::DoTestL()
{
OpenDbEtcL();
RunTestL();
CloseDbEtcL();
}
//
// Test code for DEF022709 (Propagated) Contact DB Monitoring Error Messages //
//
const TInt32 KNumContactsToAdd=50;
// If there are many contacts, it is worth using the #define below.
// This will compact the database when necessary.
#define __KEEP_DB_SMALL__
// CWaitForCompletion implementation
class CWaitForCompletion : public CActive
{
public:
CWaitForCompletion() : CActive(EPriorityStandard)
{
CActiveScheduler::Add(this);
}
virtual ~CWaitForCompletion() {}
void Activate() { SetActive(); }
TRequestStatus& RequestStatus() { return iStatus; }
protected:
// From CActive
virtual void DoCancel() { ; }
virtual void RunL() { CActiveScheduler::Stop(); }
};
class CTestMultipleRequests : public CBase
{
public:
static CTestMultipleRequests* NewLC(TRequestStatus& aRequest);
void ConstructL(TRequestStatus& aRequest);
TContactItemId CreateTestContactL(const TDesC& aFamilyName, TBool aWithPhoneNumber);
CTestMultipleRequests();
~CTestMultipleRequests();
TBool Synchronize();
static TInt OnIdle(TAny* aObject);
TInt DoIdle();
void Complete();
private:
// CPbkContactEngine* iPbkEngine;
CContactDatabase* iContactDatabase;
CContactChangeNotifier* iContactChangeNotifier;
TBool iRepeat;
CContactIdArray* iContactDbIdArray;
CIdle* iIdleTask;
TInt iContactIdCount;
TInt iCount;
TInt iTimesThrough;
TRequestStatus* iClientRequest;
TBool iFinished;
};
CTestMultipleRequests::CTestMultipleRequests()
{
}
CTestMultipleRequests::~CTestMultipleRequests()
{
// delete iPbkEngine;
delete iContactChangeNotifier;
delete iContactDbIdArray;
delete iIdleTask;
delete iContactDatabase;
}
CTestMultipleRequests* CTestMultipleRequests::NewLC(TRequestStatus& aRequest)
{
CTestMultipleRequests* self=new(ELeave) CTestMultipleRequests();
CleanupStack::PushL(self);
self->ConstructL(aRequest);
return self;
}
void CTestMultipleRequests::ConstructL(TRequestStatus& aRequest)
{
iCount = 0;
iTimesThrough = 0;
iIdleTask = CIdle::NewL(-20);
iRepeat = EFalse;
iFinished=EFalse;
iClientRequest=&aRequest;
*iClientRequest=KRequestPending;
// BaseConstructL();
// iPbkEngine = CPbkContactEngine::NewL();
// iContactDatabase = CContactDatabase::CreateL(KDatabaseFileName);
iContactDatabase = CContactDatabase::ReplaceL(KDataBaseName);
CRandomContactGenerator* randomGenerator=CRandomContactGenerator::NewL();
CleanupStack::PushL(randomGenerator);
randomGenerator->SetDbL(*iContactDatabase);
for (TInt i=0; i<KNumContactsToAdd; i++)
randomGenerator->AddTypicalRandomContactL();
CleanupStack::PopAndDestroy(randomGenerator);
const CContactIdArray* contactDbIdArray = iContactDatabase->SortedItemsL();
iContactDbIdArray = CContactIdArray::NewL(contactDbIdArray);
iContactIdCount = iContactDbIdArray->Count();
User::After(15000000); // 15 seconds
iIdleTask->Start(TCallBack(OnIdle,this)); // sync the VoCoS and Contact DB using CIdle
}
TBool CTestMultipleRequests::Synchronize()
{
if (iTimesThrough == 5)
return EFalse;
if (iCount == iContactIdCount)
{
iCount = 0;
iTimesThrough++;
}
CContactItem* item = iContactDatabase->OpenContactLX((*iContactDbIdArray)[iCount]);
CleanupStack::PushL(item);
iContactDatabase->CommitContactL( *item );
CleanupStack::PopAndDestroy(item);
CleanupStack::PopAndDestroy(); // lock
#ifdef __KEEP_DB_SMALL__
if (iContactDatabase->CompressRequired())
iContactDatabase->CompactL(); // keep the database to a sensible size.
#endif
iCount++;
return ETrue;
}
TInt CTestMultipleRequests::OnIdle(TAny* aObject)
{
TBool ret = ((CTestMultipleRequests*)aObject)->DoIdle();
if (!ret)
((CTestMultipleRequests*)aObject)->Complete();
return ret;
}
TInt CTestMultipleRequests::DoIdle()
{
TBool ret=EFalse;
if (!iFinished)
{
ret = Synchronize();
if (!ret)
{
iFinished=ETrue;
}
}
return ret;
}
void CTestMultipleRequests::Complete()
{
if (iIdleTask)
{
iIdleTask->Cancel();
}
User::RequestComplete(iClientRequest, KErrNone);
}
void DoDEF022709TestL()
{
test.Start(_L("Test for defect DEF022709"));
// Create test resources
CWaitForCompletion* transactionWait=new (ELeave)CWaitForCompletion();
CleanupStack::PushL(transactionWait);
CTestMultipleRequests* res = CTestMultipleRequests::NewLC(transactionWait->RequestStatus());
transactionWait->Activate();
CActiveScheduler::Start();
// Cleanup
CleanupStack::PopAndDestroy(res);
CleanupStack::PopAndDestroy(transactionWait);
test.End();
}