// Copyright (c) 2001-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 <cntdb.h>
#include <cntitem.h>
#include <cntfield.h>
#include <cntfldst.h>
#include <phbksync.h>

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "cntsyncecom.h"
#endif

// include templates for CleanupResetAndDestroyPushL(T)
#include "cntviewprivate.h"

#include "T_UTILS.H"
#include "cntsyncchecker.h"

_LIT(KTestName,"CContactICCEntry tests with multiple phonebooks");
_LIT(KDatabaseFileName,"C:T_ICCMULTIPLEPHONEBOOK");
_LIT(KGivenEditName,"edited name");
_LIT(KIccName,"icc entry name");
_LIT(KNonIccName,"non-icc name");
_LIT(KIccNumber,"020 794 60123");
_LIT(KNonIccNumber,"020 794 60060");

_LIT(KPluginName,"phone book synchronizer Implementation");
_LIT(KTestPluginName,"Test phone book synchronizer Implementation");

CCntTest* CntTest=NULL;
LOCAL_D RTest test(KTestName);

LOCAL_D CContactSyncChecker* syncChecker;

//===============================================================================================
// SETUP & UTILITIES
//===============================================================================================
class TFieldEntry
	{
	public:
		TFieldEntry(TFieldType aField);
		TFieldEntry(TFieldType aField,TUid aFieldType);
	public:
		TFieldType iField;
		TUid iFieldType;
	};

TFieldEntry::TFieldEntry(TFieldType aField)
:iField(aField)
,iFieldType(KUidIccPhonebookNotFound)
	{}

TFieldEntry::TFieldEntry(TFieldType aField,TUid aFieldType)
:iField(aField)
,iFieldType(aFieldType)
	{}

/**
 * Create a new template and add it to the database
 * @param aDb Contact database
 * @param aTemplateName Name for the template
 * @param aFieldSet Array of fields which should are added to the template
 * @return TContactItemId Id of the newly created template
 */
TContactItemId CreatePhonebookTemplateL(CContactDatabase& aDb, const TDesC& aTemplateName, RArray<TFieldEntry>& aFieldSet)
	{
	CContactItem* temp = aDb.CreateContactCardTemplateLC(aTemplateName);
	TContactItemId templateId = temp->Id();
	CleanupStack::PopAndDestroy(temp);
	temp=NULL;

	//Remove all the unnecessary fields
	temp = aDb.OpenContactLX(templateId);
	CleanupStack::PushL(temp);	
	const TInt fieldCount = temp->CardFields().Count();
	for(TInt i=fieldCount-1;i>=0;i--)
		temp->RemoveField(i);

	// Add each of the required fields to the template
	for (TInt j=0; j<aFieldSet.Count(); ++j)
		{
		TFieldEntry fieldEntry=aFieldSet[j];

		CContactItemField* field = CContactItemField::NewLC(KStorageTypeText,fieldEntry.iField);
		if (fieldEntry.iFieldType!=KNullUid)
			field->AddFieldTypeL(fieldEntry.iFieldType);
		temp->AddFieldL(*field);
		CleanupStack::Pop(field);
		}

	aDb.CommitContactL(*temp);
	CleanupStack::PopAndDestroy(2); // temp, close template
	
	return templateId;
	}

/**
 * Return an array of fields for the phonebook specified
 * @param aPhonebookUid The phonebook uid
 * @return RArray<TFieldEntry> Field set for this specific phonebook.  Any contact item
 * which belongs to this phonebook should contain these fields
 */
RArray<TFieldEntry> fieldSetForPhonebook(TUid aPhonebookUid)
	{
	RArray<TFieldEntry> entries;
	entries.Append(TFieldEntry(KUidContactFieldFamilyName));
	entries.Append(TFieldEntry(KUidContactFieldGivenName));
	entries.Append(TFieldEntry(KUidContactFieldPhoneNumber,KUidContactFieldVCardMapHOME));
	entries.Append(TFieldEntry(KUidContactFieldICCPhonebook));
	
	if (aPhonebookUid==KUidUsimAppAdnPhonebook)
		{
		entries.Append(TFieldEntry(KUidContactFieldEMail,KUidContactFieldVCardMapHOME));
		entries.Append(TFieldEntry(KUidContactFieldEMail,KUidContactFieldVCardMapWORK));
		entries.Append(TFieldEntry(KUidContactFieldPhoneNumber,KUidContactFieldVCardMapHOME));
		entries.Append(TFieldEntry(KUidContactFieldPhoneNumber,KUidContactFieldVCardMapWORK));
		entries.Append(TFieldEntry(KUidContactFieldPhoneNumber,KUidContactFieldVCardMapCELL));
		entries.Append(TFieldEntry(KUidContactFieldPhoneNumber,KUidContactFieldVCardMapPAGER));
		}

	return entries;
	}

void SetUpIccPhonebooksL(CContactDatabase& aDb)
	{
	test.Next(_L("Set up phonebooks"));
	RArray<TUid>	phbkList;
	CleanupClosePushL(phbkList);

	phbkList.AppendL(KUidIccGlobalAdnPhonebook);
	phbkList.AppendL(KUidIccGlobalSdnPhonebook);
	phbkList.AppendL(KUidIccGlobalLndPhonebook);
	phbkList.AppendL(KUidUsimAppAdnPhonebook);
	// tell phonebook sync simulator what phonebooks to use
	test(syncChecker->SetPhonebookListL(phbkList) == KErrNone);

	// simulate synchronisation - includes creating/finding template & group
	// also defaults all validate responses to KErrNone
	test(syncChecker->SynchroniseIccAllPhonebooksL(aDb, KErrNone, KErrNone) == KErrNone);
	
	CleanupStack::PopAndDestroy(); // phbkList
	}

/**
 * Check the content of the phonebook field in the contact item
 * @param aContactItem Contact item
 * @param aPhonebookUid The phonebook uid
 * @param aFieldIsSet EFalse if the phonebook field should not contain a value, else ETrue
 */
void CheckPhonebookField(CContactItem& aContactItem,TUid aPhonebookUid,TBool aFieldIsSet)
	{
	const CContactItemFieldSet& fieldset = aContactItem.CardFields();
	const TInt pos = fieldset.Find(KUidContactFieldICCPhonebook);
	test(pos!=KErrNotFound);
	TPtrC phonebookField=fieldset[pos].TextStorage()->Text();

	if(aFieldIsSet)
		{
		TLex input(phonebookField);
		TInt phonebookFieldVal;
		/*TInt ignore  = */ input.Val(phonebookFieldVal);
		TUid phonebookFieldUid;
		phonebookFieldUid.iUid=phonebookFieldVal;
		test(phonebookFieldUid==aPhonebookUid);
		}
	else
		test(phonebookField==KNullDesC);
	}

/**
 * Verify that the template id returned by CContactDatabase::ICCTemplateIdL is the
 * same as that returned directly from the plugin
 * @param aDb Contact database
 */
void VerifyPhonebookTemplateIdsL(CContactDatabase& aDb)
	{
	test.Next(_L("Verify template ids"));
	syncChecker->ResetMethodCallCountsL();
	test(aDb.ICCTemplateIdL() == syncChecker->ICCTemplateIdL(KUidIccGlobalAdnPhonebook));
	test(aDb.ICCTemplateIdL(KUidIccGlobalAdnPhonebook) == syncChecker->ICCTemplateIdL(KUidIccGlobalAdnPhonebook));
	test(aDb.ICCTemplateIdL(KUidIccGlobalSdnPhonebook) == syncChecker->ICCTemplateIdL(KUidIccGlobalSdnPhonebook));
	test(aDb.ICCTemplateIdL(KUidIccGlobalLndPhonebook) == syncChecker->ICCTemplateIdL(KUidIccGlobalLndPhonebook));
	test(aDb.ICCTemplateIdL(KUidUsimAppAdnPhonebook) == syncChecker->ICCTemplateIdL(KUidUsimAppAdnPhonebook));
	test(aDb.ICCTemplateIdL(KNullUid) == KNullContactId);
	test(syncChecker->ValidateMethodCallCountL() == 6);
	}

/**
 * Set the name field of the contact with id aId to contain the name aName
 * @param aDb Contact database
 * @param aId Contact item id
 * @param aName A name
 */
void SetNameField(CContactDatabase& aDb,TContactItemId aId,const TDesC& aName)
	{
	syncChecker->ResetMethodCallCountsL();
	CContactItem* item = aDb.OpenContactLX(aId);
	CleanupStack::PushL(item);
	if (item->Type() == KUidContactICCEntry)
		{
		test(syncChecker->ValidateMethodCallCountL() == 2);
		}
	else
		{
		test(syncChecker->ValidateMethodCallCountL() == 0);
		}
	CContactItemFieldSet& fieldset = item->CardFields();
	TInt pos = fieldset.Find(KUidContactFieldFamilyName);
	test(pos!=KErrNotFound);
	CContactItemField& field = fieldset[pos];
	CContactTextField* textfield = field.TextStorage();
	textfield->SetTextL(aName);

	syncChecker->ResetMethodCallCountsL();
	aDb.CommitContactL(*item);
	if (item->Type() == KUidContactICCEntry)
		{
		test(syncChecker->ValidateMethodCallCountL() == 2);
		}
	else
		{
		test(syncChecker->ValidateMethodCallCountL() == 0);
		}
	CleanupStack::PopAndDestroy(2); // item, lock record
	}

/**
 * Set the phone number field of the contact with id aId to contain the number aNumber
 * @param aDb Contact database
 * @param aId Contact item id
 * @param aNumber A telephone number
 */
void SetPhoneNumberField(CContactDatabase& aDb,TContactItemId aId,const TDesC& aNumber)
	{
	syncChecker->ResetMethodCallCountsL();
	CContactItem* item = aDb.OpenContactLX(aId);
	CleanupStack::PushL(item);
	if (item->Type() == KUidContactICCEntry)
		{
		test(syncChecker->ValidateMethodCallCountL() == 2);
		}
	else
		{
		test(syncChecker->ValidateMethodCallCountL() == 0);
		}
	CContactItemFieldSet& fieldset = item->CardFields();
	TInt pos = fieldset.Find(KUidContactFieldPhoneNumber);
	test(pos!=KErrNotFound);
	
	CContactItemField& field = fieldset[pos];
	CContactTextField* textfield = field.TextStorage();
	textfield->SetTextL(aNumber);

	syncChecker->ResetMethodCallCountsL();
	aDb.CommitContactL(*item);
	if (item->Type() == KUidContactICCEntry)
		{
		test(syncChecker->ValidateMethodCallCountL() == 2);
		}
	else
		{
		test(syncChecker->ValidateMethodCallCountL() == 0);
		}
	CleanupStack::PopAndDestroy(2); // item, lock record
	}

//===============================================================================================
// ADDING entries
//===============================================================================================
/**
 * Create an entry based on the template for this specific phonebook, and add
 * the entry to the contact database
 * @param aDb Contact database
 * @param aPhonebookUid The phonebook uid
 * @return TContactItemId Id of the newly created icc entry
 */
TContactItemId doAddIccEntryL(CContactDatabase& aDb,TUid aPhonebookUid)
	{
	syncChecker->ResetMethodCallCountsL();
	TContactItemId templateId = aDb.ICCTemplateIdL(aPhonebookUid);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	CContactItem* iccTemplate = aDb.ReadContactLC(templateId);
	CContactICCEntry* entry = CContactICCEntry::NewL(*iccTemplate);
	CleanupStack::PopAndDestroy(iccTemplate);
	CleanupStack::PushL(entry);
	// Add to the database
	CheckPhonebookField(*entry,aPhonebookUid, EFalse);
	syncChecker->ResetMethodCallCountsL();
	TContactItemId id = aDb.AddNewContactL(*entry); 
	test(syncChecker->ValidateMethodCallCountL() == 3);
	CleanupStack::PopAndDestroy(entry);
	test(id!=KNullContactId);
	// Check group membership
	syncChecker->ResetMethodCallCountsL();
	CContactICCEntry* fetchedItem = static_cast<CContactICCEntry*>(aDb.ReadContactL(id));
	test(syncChecker->ValidateMethodCallCountL() == 1);
	CleanupStack::PushL(fetchedItem);	
	const CContactIdArray* owned = fetchedItem->GroupsJoined();
	test(owned!=NULL && owned->Count() == 1);
	test((*owned)[0]==syncChecker->GroupIdL(aPhonebookUid));
	test(fetchedItem->Type() == KUidContactICCEntry);
	// Verify that the phonebook field has been set
	CheckPhonebookField(*fetchedItem,aPhonebookUid, ETrue);
	CleanupStack::PopAndDestroy(fetchedItem);	

	return id;
	}

TContactItemId AddIccEntryL(CContactDatabase& aDb,TUid aPhonebookUid)
	{
	syncChecker->SetValidateWriteResponseL(KErrNone);
	return doAddIccEntryL(aDb, aPhonebookUid);
	}

void UnsuccessfulAddIccEntryL(CContactDatabase& aDb,TUid aPhonebookUid, TInt aReason)
	{
	syncChecker->SetValidateWriteResponseL(aReason);
	TRAPD(err,doAddIccEntryL(aDb, aPhonebookUid));
	test(err==aReason);
	}

void AddEntriesBasedOnEachOfTheTemplatesL(CContactDatabase& aDb)
	{
	test.Next(_L("Test successful adding"));
	TInt numberOfAdnEntries=3;
	TInt numberOfSdnEntries=2;
	TInt numberOfLdnEntries=1;
	TInt numberOfUsimEntries=4;
	TInt iter=0;
	for (iter=0; iter<numberOfAdnEntries; ++iter)
		{
		TContactItemId id=AddIccEntryL(aDb,KUidIccGlobalAdnPhonebook);
		CContactGroup* group=static_cast<CContactGroup*>(aDb.ReadContactLC(syncChecker->GroupIdL(KUidIccGlobalAdnPhonebook)));
		CContactIdArray* entries=group->ItemsContainedLC();
		test(entries->Count() == iter+1);
		CleanupStack::PopAndDestroy(entries);
		test(group->ContainsItem(id));
		CleanupStack::PopAndDestroy(group);
		}
	for (iter=0; iter<numberOfSdnEntries; ++iter)
		{
		TContactItemId id=AddIccEntryL(aDb,KUidIccGlobalSdnPhonebook);
		CContactGroup* group=static_cast<CContactGroup*>(aDb.ReadContactLC(syncChecker->GroupIdL(KUidIccGlobalSdnPhonebook)));
		CContactIdArray* entries=group->ItemsContainedLC();
		test(entries->Count() == iter+1);
		CleanupStack::PopAndDestroy(entries);
		test(group->ContainsItem(id));
		CleanupStack::PopAndDestroy(group);
		}
	for (iter=0; iter<numberOfLdnEntries; ++iter)
		{
		TContactItemId id=AddIccEntryL(aDb, KUidIccGlobalLndPhonebook);
		CContactGroup* group=static_cast<CContactGroup*>(aDb.ReadContactLC(syncChecker->GroupIdL(KUidIccGlobalLndPhonebook)));
		CContactIdArray* entries=group->ItemsContainedLC();
		test(entries->Count() == iter+1);
		CleanupStack::PopAndDestroy(entries);
		test(group->ContainsItem(id));
		CleanupStack::PopAndDestroy(group);
		}
	for (iter=0; iter<numberOfUsimEntries; ++iter)
		{
		TContactItemId id=AddIccEntryL(aDb,KUidUsimAppAdnPhonebook);
		CContactGroup* group=static_cast<CContactGroup*>(aDb.ReadContactLC(syncChecker->GroupIdL(KUidUsimAppAdnPhonebook)));
		CContactIdArray* entries=group->ItemsContainedLC();
		test(entries->Count() == iter+1);
		CleanupStack::PopAndDestroy(entries);
		test(group->ContainsItem(id));
		CleanupStack::PopAndDestroy(group);
		}
	// Add an ICC entry based on the system template, not one of the ICC templates
	TContactItemId templateId=aDb.TemplateId();
	CContactItem* systemTemplate = aDb.ReadContactLC(templateId);
	CContactICCEntry* entry = CContactICCEntry::NewL(*systemTemplate);
	CleanupStack::PopAndDestroy(systemTemplate);
	CleanupStack::PushL(entry);
	syncChecker->ResetMethodCallCountsL();
	TContactItemId id = aDb.AddNewContactL(*entry); 
	test(syncChecker->ValidateMethodCallCountL() == 3);

	syncChecker->ResetMethodCallCountsL();
	CContactItem* item=aDb.ReadContactLC(id);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	// check the entry hasn't been added to any group
	CContactIdArray* array=static_cast<CContactICCEntry*>(item)->GroupsJoinedLC();
	test(array->Count() == 0);
	CleanupStack::PopAndDestroy(3); // array, close item, item
	}

void TestUnsuccessfulAddL(CContactDatabase& aDb)
	{
	// Test adding entries to each of the phonebooks when the ICC is locked, unaccessible or corrupt
	test.Next(_L("Test unsuccessful adding"));
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalAdnPhonebook,KErrLocked);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalAdnPhonebook,KErrAccessDenied);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalAdnPhonebook,KErrCorrupt);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalSdnPhonebook,KErrLocked);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalSdnPhonebook,KErrAccessDenied);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalSdnPhonebook,KErrCorrupt);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalLndPhonebook,KErrLocked);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalLndPhonebook,KErrAccessDenied);
	UnsuccessfulAddIccEntryL(aDb,KUidIccGlobalLndPhonebook,KErrCorrupt);
	UnsuccessfulAddIccEntryL(aDb,KUidUsimAppAdnPhonebook,KErrLocked);
	UnsuccessfulAddIccEntryL(aDb,KUidUsimAppAdnPhonebook,KErrAccessDenied);
	UnsuccessfulAddIccEntryL(aDb,KUidUsimAppAdnPhonebook,KErrCorrupt);
	}

/** 
 * Verify that adding contact cards doesn't call the synchroniser interface
 * @param aDb Contact database
 */
TContactItemId AddContactCardL(CContactDatabase& aDb)
	{
	_LIT(KTemplateName,"contact card template");
	RArray<TFieldEntry> entries=fieldSetForPhonebook(KUidIccGlobalAdnPhonebook);
	TContactItemId templateId=CreatePhonebookTemplateL(aDb,KTemplateName,entries);
	entries.Close();
	CContactItem* iccTemplate = aDb.ReadContactLC(templateId);
	CContactCard* card = CContactCard::NewLC(iccTemplate);
	syncChecker->ResetMethodCallCountsL();
	TContactItemId id=aDb.AddNewContactL(*card);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	CleanupStack::PopAndDestroy(2,iccTemplate);
	return id;
	}
//===============================================================================================
// DELETING entries
//===============================================================================================
/**
 * Verify that the synchroniser is called when deleting an icc entry, and that the 
 * deletion of an icc entry fails if the icc is locked.
 * @param aDb Contact database
 */
void TestDeleteL(CContactDatabase& aDb)
	{
	test.Next(_L("Test delete when ICC locked"));
	syncChecker->SetDeleteContactResponseL(KErrLocked);
	TContactItemId iccId=AddIccEntryL(aDb,KUidIccGlobalAdnPhonebook);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err,aDb.DeleteContactL(iccId));
	test(err==KErrLocked);
	test(syncChecker->ValidateMethodCallCountL() == 2);
	syncChecker->ResetMethodCallCountsL();
	CContactItem* item=aDb.ReadContactLC(iccId);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(item!=NULL);
	CleanupStack::PopAndDestroy(item);
	item=NULL;

	test.Next(_L("Test successful delete of non-icc entry when ICC locked"));
	TContactItemId nonIccId=AddContactCardL(aDb);
	syncChecker->ResetMethodCallCountsL();
	TRAP(err,aDb.DeleteContactL(nonIccId));
	test(err==KErrNone);
	test(syncChecker->ValidateMethodCallCountL() == 0);

	test.Next(_L("Test successful delete"));
	syncChecker->SetDeleteContactResponseL(KErrNone);
	syncChecker->ResetMethodCallCountsL();
	TRAP(err,aDb.DeleteContactL(iccId));
	test(syncChecker->ValidateMethodCallCountL() == 2);
	test(err==KErrNone);
	syncChecker->ResetMethodCallCountsL();
	TRAP(err,aDb.ReadContactLC(iccId));
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(err==KErrNotFound);
	}

//===============================================================================================
// READING entries
//===============================================================================================
void TestReadMinimalContactL(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	test.Next(_L("Test ReadMinimalContactL"));
	// Successful read of icc entry
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->ResetMethodCallCountsL();
	CContactItem* item=aDb.ReadMinimalContactLC(aIccId);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(item!=NULL);
	CleanupStack::PopAndDestroy(item);
	item=NULL;

	// Unsuccessful read of icc entry because icc locked
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrAccessDenied);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err,item=aDb.ReadMinimalContactLC(aIccId));
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(item==NULL);
	test(err==KErrAccessDenied);
	
	// successful read of non-icc entry, even though icc locked
	syncChecker->ResetMethodCallCountsL();
	item=aDb.ReadMinimalContactLC(aNonIccId);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(item!=NULL);
	CleanupStack::PopAndDestroy(item);
	item=NULL;
	}

void TestReadContactL(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	test.Next(_L("Test ReadContactL"));
	// Successful read of icc entry
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);	
	CContactItem* item=aDb.ReadContactLC(aIccId);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(item!=NULL);
	CleanupStack::PopAndDestroy(item);
	item=NULL;

	// Unsuccessful read of icc entry because icc locked
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrAccessDenied);
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);
	TRAPD(err,item=aDb.ReadContactL(aIccId));
	test(item==NULL);
	test(err==KErrAccessDenied);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	
	// successful read of non-icc entry, even though icc locked
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);	
	item=aDb.ReadContactLC(aNonIccId);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(item!=NULL);
	CleanupStack::PopAndDestroy(item);
	item=NULL;
	}

void TestReadTextDefinitionL(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	test.Next(_L("Test ReadTextDefinitionL"));
	// Successful read of icc entry
	CContactTextDef* textDef=CContactTextDef::NewLC();
	textDef->AppendL(TContactTextDefItem(KUidContactFieldFamilyName));
	TBuf<64> result;
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->ResetMethodCallCountsL();
	aDb.ReadContactTextDefL(aIccId,result,textDef);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(result==KIccName);
	result.Zero();

	// Unsuccessful read of icc entry because icc locked
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrAccessDenied);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err, aDb.ReadContactTextDefL(aIccId,result,textDef));
	test(err==KErrAccessDenied);
	test(result.Length() == 0);
	test(syncChecker->ValidateMethodCallCountL() == 1);

	// successful read of non-icc entry, even though icc locked
	syncChecker->ResetMethodCallCountsL();
	TRAP(err, aDb.ReadContactTextDefL(aNonIccId,result,textDef));
	test(err==KErrNone);
	test(result==KNonIccName);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	CleanupStack::PopAndDestroy(textDef);
	}

/**
 * Verify that the synchroniser is called when reading an icc entry
 * @param aDb Contact database
 */
void TestReadL(CContactDatabase& aDb)
	{
	TContactItemId iccId=AddIccEntryL(aDb,KUidIccGlobalAdnPhonebook);
	SetNameField(aDb,iccId,KIccName);
	TContactItemId nonIccId = AddContactCardL(aDb);
	SetNameField(aDb,nonIccId,KNonIccName);
	TestReadMinimalContactL(aDb,iccId,nonIccId);
	TestReadContactL(aDb,iccId,nonIccId);
	TestReadTextDefinitionL(aDb,iccId,nonIccId);
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->ResetMethodCallCountsL();
	aDb.DeleteContactL(iccId);
	aDb.DeleteContactL(nonIccId);
	test(syncChecker->ValidateMethodCallCountL() == 2);
	}

//===============================================================================================
// EDITING entries
//===============================================================================================
/**
 * Verify that edit validation is being done for ICC entries
 * and write validation is done when Commit is called.
 * @param aDb Contact database
 * @param aId Item Id to edit
 */
void TestSuccessfulIccEditL(CContactDatabase& aDb, TContactItemId aId)
	{
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->SetValidateResponseL(MContactSynchroniser::EEdit, KErrNone);
	syncChecker->SetValidateWriteResponseL(KErrNone);
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);
	CContactItem* item = aDb.OpenContactLX(aId);
	CleanupStack::PushL(item);
	test(syncChecker->ValidateMethodCallCountL() == 2);	

	CContactItemFieldSet& fieldset = item->CardFields();
	const TInt pos = fieldset.Find(KUidContactFieldFamilyName);	
	CContactItemField& field = fieldset[pos];
	CContactTextField* textfield = field.TextStorage();
	textfield->SetTextL(KGivenEditName);

	aDb.CommitContactL(*item);
	test(syncChecker->ValidateMethodCallCountL() == 4);	
	CleanupStack::PopAndDestroy(2); //item, lock record
	}

/**
 * Verify that edit of non-icc entries is successful when the icc is
 * locked, and that the synchroniser is not called
 * @param aDb Contact database
 * @param aId Item Id to edit
 */
void TestSuccessfulNonIccEditL(CContactDatabase& aDb, TContactItemId aId)
	{
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrLocked);
	syncChecker->SetValidateResponseL(MContactSynchroniser::EEdit, KErrLocked);
	syncChecker->SetValidateWriteResponseL(KErrLocked);
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);
	CContactItem* item = aDb.OpenContactLX(aId);
	CleanupStack::PushL(item);
	test(syncChecker->ValidateMethodCallCountL() == 0);	

	CContactItemFieldSet& fieldset = item->CardFields();
	const TInt pos = fieldset.Find(KUidContactFieldFamilyName);	
	CContactItemField& field = fieldset[pos];
	CContactTextField* textfield = field.TextStorage();
	textfield->SetTextL(KGivenEditName);

	aDb.CommitContactL(*item);
	test(syncChecker->ValidateMethodCallCountL() == 0);	
	CleanupStack::PopAndDestroy(2); //item, lock record
	}

/**
 * Unsuccessful edit. Fail the edit validation request
 * @param aDb Contact database
 * @param aId Item Id to edit
 */
void FailEditValidationL(CContactDatabase& aDb, TContactItemId aId)
	{
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->SetValidateResponseL(MContactSynchroniser::EEdit, KErrAccessDenied);
	syncChecker->ResetMethodCallCountsL();
	test(syncChecker->ValidateMethodCallCountL() == 0);
	TRAPD(err, aDb.OpenContactL(aId));
	test(syncChecker->ValidateMethodCallCountL() == 2);	
	test(err==KErrAccessDenied);
	}

/**
 * Unsuccessful edit. Fail the write validation request
 * @param aDb Contact database
 * @param aId Item Id to edit
 */
void FailWriteValidationL(CContactDatabase& aDb, TContactItemId aId)
	{
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->SetValidateResponseL(MContactSynchroniser::EEdit, KErrNone);
	syncChecker->SetValidateWriteResponseL(KErrAccessDenied);
	syncChecker->ResetMethodCallCountsL();

	CContactItem* item = aDb.OpenContactLX(aId);
	CleanupStack::PushL(item);
	CContactItemFieldSet& fieldset = item->CardFields();
	const TInt pos = fieldset.Find(KUidContactFieldFamilyName);	
	CContactItemField& field = fieldset[pos];
	CContactTextField* textfield = field.TextStorage();
	textfield->SetTextL(KGivenEditName);

	TRAPD(err, aDb.CommitContactL(*item));
	test(syncChecker->ValidateMethodCallCountL() == 4);	
	test(err==KErrAccessDenied);
	CleanupStack::PopAndDestroy(2); //item, lock record
	}

/**
 * Verify that edit validation is being done for ICC entries
 * and write validation is done when Commit is called.
 * @param aDb Contact database
 */
void TestEditL(CContactDatabase& aDb)
	{
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	TContactItemId iccId = AddIccEntryL(aDb,KUidIccGlobalAdnPhonebook);
	TContactItemId nonIccId = AddContactCardL(aDb);
	test.Next(_L("Test successful edit"));
	TestSuccessfulIccEditL(aDb,iccId);
	TestSuccessfulNonIccEditL(aDb,nonIccId);
	test.Next(_L("Test unsuccessful edit - fail edit"));
	FailEditValidationL(aDb,iccId);
	test.Next(_L("Test unsuccessful edit - fail write"));
	FailWriteValidationL(aDb,iccId);
	}
//===============================================================================================
// SEARCHING entries
//===============================================================================================
void TestFindLC(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	test.Next(_L("Test FindLC"));
	// Successful find of icc entry
	CContactItemFieldDef* fieldDef=new(ELeave) CContactItemFieldDef;
	CleanupStack::PushL(fieldDef);
	fieldDef->AppendL(KUidContactFieldFamilyName);
	syncChecker->ResetMethodCallCountsL();
	CContactIdArray* array = aDb.FindLC(KIccName,fieldDef);
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aIccId);
	CleanupStack::PopAndDestroy(array);
	array=NULL;

	test.Next(_L("Test searching when ICC locked"));
	// Unsuccessful find of icc entry because icc locked
	syncChecker->SetValidateResponseL(MContactSynchroniser::ESearch,KErrLocked);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err,array = aDb.FindLC(KIccName,fieldDef));
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(err==KErrLocked);
	test(array==NULL);

	// successful find of non-icc entry, even though icc locked
	syncChecker->ResetMethodCallCountsL();
	array = aDb.FindLC(KNonIccName,fieldDef);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aNonIccId);
	CleanupStack::PopAndDestroy(2,fieldDef); // array, fieldDef
	}

TInt findWordSplitterL(TAny *aParams)
	{
	SFindInTextDefWordParser *parser=(SFindInTextDefWordParser *)aParams;
	parser->iWordArray->AppendL(*parser->iSearchString);
	return(KErrNone);
	}

void TestFindInTextDefLC(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	test.Next(_L("Test FindInTextDefLC"));
	// Successful find of icc entry
	syncChecker->SetValidateResponseL(MContactSynchroniser::ESearch,KErrNone);
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);

	TCallBack callBack(findWordSplitterL);
	CContactTextDef* textDef=CContactTextDef::NewLC();
	textDef->AppendL(TContactTextDefItem(KUidContactFieldFamilyName));
	CDesCArray* desArray = new(ELeave) CDesCArrayFlat(5);
	desArray->AppendL(_L("icc"));
	syncChecker->ResetMethodCallCountsL();
	CContactIdArray* array = aDb.FindInTextDefLC(*desArray,textDef,callBack);
	//test(syncChecker->ValidateMethodCallCountL() == 3);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aIccId);
	CleanupStack::PopAndDestroy(array);
	array=NULL;

	test.Next(_L("Test searching when ICC locked"));
	// Unsuccessful find of icc entry because icc locked
	syncChecker->SetValidateResponseL(MContactSynchroniser::ESearch,KErrLocked);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err,array = aDb.FindInTextDefLC(*desArray,textDef,callBack));
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(err==KErrLocked);
	test(array==NULL);
	desArray->Delete(0);
	delete desArray;

	// successful find of non-icc entry, even though icc locked
	CDesCArray* desArray2 = new(ELeave) CDesCArrayFlat(5);
	desArray2->AppendL(_L("non-icc"));
	syncChecker->ResetMethodCallCountsL();
	array = aDb.FindInTextDefLC(*desArray2,textDef,callBack);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aNonIccId);

	// both the icc and non-icc entry should match the search criteria, but only the
	// non-icc entry should be returned since icc is locked
	SetNameField(aDb,aIccId,KNonIccName);
	syncChecker->ResetMethodCallCountsL();
	TRAP(err,array = aDb.FindInTextDefLC(*desArray2,textDef,callBack));
	test(syncChecker->ValidateMethodCallCountL() == 1);
	test(err==KErrLocked);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aNonIccId);
	desArray2->Delete(0);
	delete desArray2;
	SetNameField(aDb,aIccId,KIccName);
	CleanupStack::PopAndDestroy(2,textDef); // array, textDef
	}

void TestMatchPhoneNumberL(CContactDatabase& aDb, TContactItemId aIccId, TContactItemId aNonIccId)
	{
	// The synchroniser is not called to validate the IDs at for this method because 
	// phone number resolution needs to be as quick as possible. 
	// Instead, validation is done when the client attempts to read the items.
	test.Next(_L("Test MatchPhoneNumberL"));
	// Successful find of icc entry phone number
	syncChecker->ResetMethodCallCountsL();
	CContactIdArray* array = aDb.MatchPhoneNumberL(KIccNumber,KMaxPhoneMatchLength);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aIccId);
	delete array;
	array=NULL;

	test.Next(_L("Test searching when ICC locked"));
	// successful find when icc locked - validation done when entry read
	syncChecker->SetValidateResponseL(MContactSynchroniser::ESearch,KErrLocked);
	syncChecker->ResetMethodCallCountsL();
	TRAPD(err,array = aDb.MatchPhoneNumberL(KIccNumber,KMaxPhoneMatchLength));
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(array!=NULL);
	test(err==KErrNone);
	test((*array)[0]==aIccId);
	delete array;
	array=NULL;
	syncChecker->ResetMethodCallCountsL();
	array = aDb.MatchPhoneNumberL(KNonIccNumber,KMaxPhoneMatchLength);
	test(syncChecker->ValidateMethodCallCountL() == 0);
	test(array!=NULL);
	test(array->Count() == 1);
	test((*array)[0]==aNonIccId);
	delete array;
	array=NULL;
	}

void TestSearchL(CContactDatabase& aDb)
	{
	test.Next(_L("Test searching"));
	syncChecker->SetValidateResponseL(MContactSynchroniser::ERead,KErrNone);
	syncChecker->SetValidateResponseL(MContactSynchroniser::ESearch,KErrNone);
	TContactItemId iccId = AddIccEntryL(aDb,KUidIccGlobalAdnPhonebook);
	SetNameField(aDb,iccId,KIccName);
	SetPhoneNumberField(aDb,iccId,KIccNumber);
	TContactItemId nonIccId = AddContactCardL(aDb);
	SetNameField(aDb,nonIccId,KNonIccName);
	SetPhoneNumberField(aDb,nonIccId,KNonIccNumber);

	TestFindLC(aDb,iccId,nonIccId);
	TestFindInTextDefLC(aDb,iccId,nonIccId);
	TestMatchPhoneNumberL(aDb,iccId,nonIccId);
	}


void CheckForPhbkSyncPluginL()
	{
	test.Next(_L("Check for PhbkSync test plug-in"));

	RImplInfoPtrArray	implInfoArray;
	CleanupResetAndDestroyPushL(implInfoArray);
	REComSession::ListImplementationsL(KUidEcomCntPhBkSyncInterface, implInfoArray);
	//Find implementations of KUidEcomCntPhBkSyncInterface
	TInt availCount = implInfoArray.Count(); 
	TInt count;
	for(count = 0; count < availCount; count++)
		{
		const TUid firstImplementationFound = implInfoArray[count]->ImplementationUid();
		CImplementationInformation *info = implInfoArray[count];
		test.Printf(_L("\n"));
		test.Printf(_L("PhbkSync plugin #%i, Implementation UID 0x%08X version %i\n"),
			count + 1, info->ImplementationUid(), info->Version());
		test.Printf(_L("Plugin name = \"%S\"\n"), &(info->DisplayName()));
		}

	// is telephony's plug-in in the list?
	for(count = 0; count < availCount; count++)
		{
		const TUid firstImplementationFound = implInfoArray[count]->ImplementationUid();
		CImplementationInformation *info = implInfoArray[count];
		if(info->DisplayName() == KTestPluginName)
			{
			test.Printf(_L("\n"));
			test.Printf(_L("This test has now loaded the test plugin"));
			test.Printf(_L("\n"));
			availCount = 1;
			break;
			}		
		
		if(info->DisplayName() == KPluginName)
			{
			test.Printf(_L("\n"));
			test.Printf(_L("This test only works with Contacts the test plugin and not the original phonebooksync plugin."));
			test.Printf(_L("Depending on the build to removed the plugin in different ways:"));
			test.Printf(_L("hardware - delete the line \"ECOM_PLUGIN(phbksyncplugin.dll,1020428C.rsc)\" from phbksync.iby"));
			test.Printf(_L("winscw - delete phbksyncplugin.dll from %epocroot%/epoc32/release/winscw/udeb or similarly named directory"));
			test.Printf(_L("\n"));
			test(0);  // stop
			break;
			}
		}

	// only continue test if there is exactly one plug-in present
	test(availCount == 1);	

	CleanupStack::PopAndDestroy(&implInfoArray);
	}

	
//===============================================================================================

/** Lists tests to execute */

/**

@SYMTestCaseID     PIM-T-ICCMULTIPLEPHONEBOOK-0001

*/
void DoTestsL()
    {
	test.Start(_L("@SYMTestCaseID:PIM-T-ICCMULTIPLEPHONEBOOK-0001 CContactICCEntry tests with multiple phonebooks"));

	CheckForPhbkSyncPluginL();

	CntTest->CreateDatabaseL();	
	CntTest->OpenDatabaseL();
	CContactDatabase& db= *CntTest->Db();

	syncChecker = CContactSyncChecker::NewL();
	syncChecker->ResetEverythingL();

	SetUpIccPhonebooksL(db);
	VerifyPhonebookTemplateIdsL(db);
	AddEntriesBasedOnEachOfTheTemplatesL(db);
	TestUnsuccessfulAddL(db);
	TestDeleteL(db);
	TestReadL(db);
	TestEditL(db);
	TestSearchL(db);
	syncChecker->ResetMethodCallCountsL();

	CntTest->CloseDatabase();
	CntTest->DeleteDatabaseL();


	delete syncChecker;
	syncChecker = NULL;
    }


/** Standard E32Main method */
GLDEF_C TInt E32Main()
	{
    CntTest=new(ELeave) CCntTest;
	CntTest->ConstructL(test,KDatabaseFileName);
    TRAPD(err,DoTestsL());
	if (syncChecker)
		{
		delete syncChecker;
		syncChecker = NULL;
		}
	CntTest->EndTestLib(err);
	return KErrNone;
    }

