phonebookengines/contactsmodel/tsrc/t_cntvcard.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 27 May 2010 12:45:19 +0300
changeset 37 fd64c38c277d
parent 24 0ba2181d7c28
permissions -rw-r--r--
Revision: 201019 Kit: 2010121

/*
* Copyright (c) 2002-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: 
* Unit tests for functionality delivered by the CNTVCARD.DLL library.
* This test contains the tests originally in T_EXPDEL
*
*/


#include <e32test.h>
#include <f32file.h>
#include <s32file.h>
#include <s32mem.h>
#include <cntdb.h>
#include <cntvcard.h>
#include <cntitem.h>
#include <cntfldst.h>
#include <e32def.h>
#include "t_utils2.h"
#include "t_rndutils.h"
#include <coreappstest/testserver.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "cntdb_internal.h"
#endif

_LIT(KTestName,"T_CNTVCARD");



_LIT(KDatabaseFileName,"C:T_CNTVCARD");

_LIT(KVcard1, "Z:\\t_cntvcard\\email1.vcf");
_LIT(KVcard2, "Z:\\t_cntvcard\\email2.vcf");


_LIT(KOutputFileName, "C:\\Test_Output.txt");
_LIT(KExpDelAFileName, "c:\\expdela");
_LIT(KTestVcardFile, "Z:\\cntvcard\\testvcard.vcf");

_LIT(KImpTypeVcardName, "Z:\\t_cntvcard\\testTypeVcard.vcf");
_LIT(KImpNoTypeVcardName, "Z:\\t_cntvcard\\testNoTypeVcard.vcf");

_LIT(KTestVcardFile1, "Z:\\cntvcard\\testvcard1.vcf");
//UpdateVCardL
_LIT(KVCardUpdate1,"Z:\\System\\Programs\\CntmodelTest\\vcardupdate1.vcf");
_LIT(KVCardUpdate2,"C:\\vcardupdate2.vcf");
_LIT(KVCardUpdate3,"Z:\\System\\Programs\\CntmodelTest\\vcardupdate3.vcf");

// digram data files
_LIT(KFirstNameFileName,"z:\\cntvcard\\names.first.2gm");
_LIT(KLastNameFileName,"z:\\cntvcard\\names.last.2gm");

// vcard terminated by LF
_LIT(KVCardFileLF,"z:\\t_cntvcard\\cntvcardLF.vcf");
// vcard containing UID property
_LIT(KVCardFileUID,"z:\\t_cntvcard\\cntvcardUID.vcf");

_LIT8(KVCardBinaryKeyImportFileDes,"BEGIN:VCARD\r\n"
							"VERSION:2.1\r\n"
							"REV:20061120T111808Z\r\n"
							"UID:0000000000000000-00e1005eee8a27d8-44\r\n"
							"N:Name;;;;\r\n"
							"TEL;HOME;VOICE;CELL:1111\r\n"
							"KEY;ENCODING=BASE64:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA\r\n"
							"wdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYX\r\n"
							"Rpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0\r\n"
							"ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNj\r\n"
							"E5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYD\r\n"							 
							"VQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAx\r\n"
							"MPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRz\r\n"
							"Y2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQ\r\n"
							"EBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2\r\n"
							"dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MB\r\n"
							"EGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau\r\n"
							"+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIP\r\n"
							"mx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//y\r\n"
							"rZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7\r\n"
							"UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==\r\n"
							"\r\n"
							"PHOTO;ENCODING=BASE64:\r\n"
							"   ZmlsZTovLy9qcXB1YmxpYy5naWY=\r\n"
							"\r\n"
							"END:VCARD\r\n"
							);
							
_LIT8(KVCardTextKeyImportFileDes,"BEGIN:VCARD\r\n"
							"VERSION:2.1\r\n"
							"REV:20061120T111808Z\r\n"
							"UID:0000000000000000-00e1005eee8a27d8-44\r\n"
							"N:Name;;;;\r\n"
							"TEL;HOME;VOICE;CELL:1111\r\n"
							"KEY:X509\r\n"
							"PHOTO;ENCODING=BASE64:\r\n"
							"   ZmlsZTovLy9qcXB1YmxpYy5naWY=\r\n"
							"\r\n"
							"END:VCARD\r\n"
							);

_LIT8(KMultiParam,"BEGIN:VCARD\r\n"
				"VERSION:2.1\r\n"
				"N:;Neo;;Mr.;\r\n"
				"FN:Mr. TestName\r\n"
				"TEL;TYPE=HOME;TYPE=VOICE;TYPE=CELL:123\r\n"
				"END:VCARD\r\n"
				);
_LIT8(KTelExport, "TEL;HOME;VOICE;CELL:123\r\n");

_LIT8(KVCardBeforeChange,"BEGIN:VCARD\r\n"
 						"VERSION:2.1\r\n"
 						"N:Jm;Jg;;;\r\n"
 						"TEL;HOME:11111\r\n"
 						"TEL;HOME:67890\r\n"
 						"TEL;CELL:12345\r\n"
 						"UID:4c2d2b19ef07adea-00e115956542fd28-1\r\n"
 						"END:VCARD\r\n"
 						);
 						
 _LIT8(KVCardAfterChange,"BEGIN:VCARD\r\n"
 						"VERSION:2.1\r\n"
 						"N:Jm;Jg;;;\r\n"
 						"TEL;HOME:11112\r\n"
 						"TEL;HOME:22222\r\n"
 						"TEL;HOME:22223\r\n"
 						"TEL;HOME:22224\r\n"
 						"UID:4c2d2b19ef07adea-00e115956542fd28-1\r\n"
 						"END:VCARD\r\n"
 						);				
 
 _LIT(KGivenName, "Jg");
 _LIT(KFamilyName, "Jm");
 _LIT(KTelHome1,"11111");
 _LIT(KTelHome2,"67890");
 _LIT(KTelCell,"12345");
 _LIT(KTelHome1Modified,"11112");
 _LIT(KTelHome2Modified,"22222");
 _LIT(KTelHome3Modified,"22223");
 _LIT(KTelHome4Modified,"22224");

//vCard containing CompanyField
_LIT(KVCardWithCompany, "z:\\t_cntvcard\\withcompany.vcf");
//vCard without CompanyField
_LIT(KVCardWithoutCompany, "z:\\t_cntvcard\\withoutcompany.vcf");

_LIT(KVCardLargePhoto, "Z:\\t_cntvcard\\vcardwithlargephoto.vcf");


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

LOCAL_C void TestCondition(TBool aCondition, TInt aLineNumber);
#define TEST_CONDITION(x) TestCondition(x, __LINE__)

const TInt KNumAccessCountContacts= 5;
const TInt KMachineUniqueId=0;
const TInt KNumberOfRandomContacts = 40;

_LIT(KName,"Name");
_LIT(KPhoneNumber,"1111");
_LIT8(KModifiedNumberProperty,"TEL;HOME;CELL:11112222\r\n"); //no VOICE parameter!
_LIT8(KModifiedNumberProperty2,"tel;work;cell;2:4444\r\ntel;home;Voice;CELL:\r\n"); //empty property to delete
_LIT8(KModifiedNumberProperty3,"TEL;HOME;CELL:\r\nTEL;HOME;CELL:3333\r\n"); 

_LIT(KVCardFile1,"c:\\cntvcard1.vcf");
_LIT(KVCardFile2,"c:\\cntvcard2.vcf");
_LIT(KVCardFile3,"c:\\cntvcard3.vcf");

// data we expect to see in KTestVcardFile
_LIT(KFirstName, "first");
_LIT(KFirstNamePrn, "f-i-r-s-t");
_LIT(KSurName, "last");
_LIT(KSurNamePrn, "l-a-s-t");
_LIT(KOrgName, "I work here");
_LIT(KOrgNamePrn, "Eye w-o-r-k h-e-r-e");

// labels we expect from the conacts resource file
_LIT(KFirstNameLabel, "First name");
_LIT(KFirstNamePrnLabel, "First name reading");
_LIT(KSurNameLabel, "Last name");
_LIT(KSurNamePrnLabel, "Last name reading");
_LIT(KOrgNameLabel, "Company");
_LIT(KOrgNamePrnLabel, "Company reading");

_LIT(KVCardFile5, "c:\\cntvcard5.vcf");
_LIT(KVCardFile6, "c:\\cntvcard6.vcf");
_LIT8(KMasterVcard, "BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131829Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015e3ec0-18\r\n"
					"N:Aaaa;Pekka;;;\r\n"
					"END:VCARD\r\n"
					"BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131916Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015e8128-19\r\n"
					"N:Bbb;Pekka;;;\r\n"
					"END:VCARD\r\n"
					"BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131829Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015eb46d-20\r\n"
					"N:Cccc;Pekka;;;\r\n"
					"END:VCARD\r\n"
);
_LIT8(KModifierVcard,"BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131829Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015e3ec0-18\r\n"
					"N:Aaaa;Pekka;;;\r\n"
					"END:VCARD\r\n"
					"BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131916Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015e8128-19\r\n"
					"N:;Pekka;;;\r\n"
					"END:VCARD\r\n"
					"BEGIN:VCARD\r\n"
					"VERSION:2.1\r\n"
					"REV:20060821T131829Z\r\n"
					"UID:f356da00bfd5320f-00e0f93a015eb46d-20\r\n"
					"N:Cccc;Pekka;;;\r\n"
					"END:VCARD\r\n"
);
 
_LIT8(KPartialVCard,"BEGIN:VCARD\r\n"
        "\r"
);

const TAny* GNames[]  = {&KFirstName, &KFirstNamePrn, &KSurName, &KSurNamePrn, &KOrgName, &KOrgNamePrn};
const TAny* GLabels[]  = {&KFirstNameLabel, &KFirstNamePrnLabel, &KSurNameLabel, &KSurNamePrnLabel, &KOrgNameLabel, &KOrgNamePrnLabel};

const TInt KNumberOfExtendedFieldNames=6;

const TInt KExtendedFieldNames[KNumberOfExtendedFieldNames] = 
	{
	KUidContactFieldGivenNameValue, 
	KUidContactFieldGivenNamePronunciationValue,
	KUidContactFieldFamilyNameValue, 
	KUidContactFieldFamilyNamePronunciationValue,
	KUidContactFieldCompanyNameValue, 	
	KUidContactFieldCompanyNamePronunciationValue, 
	};

		
/** Tests for default VOICE property parameter handling (tests1-4), and the X-IRMC-x extentions for the SOUND property (tests 5-6) */
class CDefaultVoiceParamTests : public CBase
	{
public :
	enum THowToCheck {ECheckValue = 1, ECheckLabel = 2, EAppendValue = 4, EAppendLabel = 8};

public:
	static CDefaultVoiceParamTests* NewLC(CCntTest& aTestLibrary);
	~CDefaultVoiceParamTests();
	void RunTestsL();
private:
	CDefaultVoiceParamTests(CCntTest& aTestLibrary);
	void ConstructL();
	void TestOneL();
	void TestTwoL();
	void TestThreeL();
	void TestFourL();
	void TestFiveL();
	void TestSixL();
	void ImportContactsL(const TDesC& aFileName);
	void ExportContactsL(const TDesC& aFileName, const CContactIdArray* aIds);
	void CreateTestDataL(TContactItemId aId, const TDesC& aFileName, const TDesC8& aVCardData);
	TInt CountPhoneNumberFieldsL(TContactItemId aId);
	TContactItemId CreateItemL(const TDesC& aName, const TDesC& aNumber);
	static void CheckCard(CContactCard* aCard,  CDesCArray* aFields = NULL, CDesCArray* aLabels =NULL, TUint aFlags = ECheckValue|ECheckLabel);
	void CheckCardL(TContactItemId aId,  CDesCArray* aFields = NULL, CDesCArray* aLabels =NULL, TUint aFlags = ECheckValue|ECheckLabel) const;
	
private:
	CCntTest& iTest;
	CContactDatabase* iDb;
	};

CDefaultVoiceParamTests* CDefaultVoiceParamTests::NewLC(CCntTest& aTestLibrary)
	{
	CDefaultVoiceParamTests* self = new(ELeave) CDefaultVoiceParamTests(aTestLibrary);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CDefaultVoiceParamTests::~CDefaultVoiceParamTests()
	{
	}

void CDefaultVoiceParamTests::RunTestsL()
	{
	TestFiveL(); //these two have to run 1st since tests 1-4 leave the db in an inconvenient state
	TestSixL();
	TestOneL();
#if defined(_DEBUG)
	//this test cannot be run in release mode due to UID mismatch.
	TestTwoL();
#endif
	TestThreeL();
	TestFourL();	
	}

CDefaultVoiceParamTests::CDefaultVoiceParamTests(CCntTest& aTestLibrary) : iTest(aTestLibrary)
	{
	}

void CDefaultVoiceParamTests::ConstructL()
	{
	iDb=iTest.CreateDatabaseL();
	iDb->OverrideMachineUniqueId(KMachineUniqueId); 
	}

/** Check all the fields specified by KExtendedFieldNames for correctness.
	The specific behaviour on what is checked is regulated by aFlags. 
	This also prints each field with its label.
	
	@param aFields 	If present and the ECheckValue bit of aFlags set, the nth field text value is compared against
					the nth descriptor in aFields. 
					If present and EAppendValue bit of aFlags set, each field's text value is appended to the end of aFields. 	
						
	@param aLabels 	If present and the ECheckLabel bit of aFlags set, the nth field's label is compared against
					the nth descriptor in ECheckLabel. 
					If present and EAppendLabel bit of aFlags set, each field's label is appended to the end of aLabels. 
	@param aFlags	Bitfields indicating how aFields and aLabels are used. Note that some values from THowToCheck
					 (like ECheckValue and EAppendValue) cannot be used at the same time.
 */
void CDefaultVoiceParamTests::CheckCard(CContactCard* aCard,  CDesCArray* aFields, CDesCArray* aLabels, TUint aFlags)
	{
	CContactItemFieldSet& fieldSet=aCard->CardFields();
	for(TInt i=0;i<KNumberOfExtendedFieldNames;i++)
		{
		TInt index = fieldSet.Find( TUid::Uid(KExtendedFieldNames[i]));
		TEST_CONDITION(index != KErrNotFound);
      	TPtrC text;
		if(aFields && (ECheckValue& aFlags))
			{
			TPtrC compare((*aFields)[i]);
			TBool succeed(EFalse);
			while(index != KErrNotFound)
                {
    			text.Set(fieldSet[index].TextStorage()->Text());
    			succeed = (text.Compare(compare)==0);
    			if(succeed)
    			    {
    			    break;
    			    }
    			index = fieldSet.FindNext( TUid::Uid(KExtendedFieldNames[i]), index + 1);    
                }
            TEST_CONDITION(succeed);    
			}
		else if (aFields && (aFlags & EAppendValue))
			{
			text.Set(fieldSet[index].TextStorage()->Text());
			TRAP_IGNORE(aFields->AppendL(text));
			}
		
		TPtrC label(fieldSet[index].Label());
		if(aLabels && (ECheckLabel& aFlags))
			{
			TPtrC comparelabel((*aLabels)[i]);
			TEST_CONDITION(label.Compare(comparelabel)==0);
			}
		else if (aLabels && (aFlags & EAppendLabel))
			{
			TRAP_IGNORE(aLabels->AppendL(label));
			}
		test.Printf(_L("%S: %S\n") ,&label, &text);	// print this just to make sure
		}
}

void CDefaultVoiceParamTests::CheckCardL(TContactItemId aId,  CDesCArray* aFields, CDesCArray* aLabels, TUint aFlags) const
	{
	CContactCard* card = STATIC_CAST(CContactCard*, iDb->OpenContactL(aId));
	CleanupStack::PushL(card);
	CheckCard(card, aFields, aLabels, aFlags);
	iDb->CloseContactL(aId);
	CleanupStack::PopAndDestroy(card);
	}

/** 
Default number is edited in PIM and synchronisation is started. 
Default number is not edited in phone but new number is inserted

1. Choose a phone number field of a phone contact as a default (For example Tel home: 1111)
2. Start synchronisation
3. Edit the contact's default number in PIM (For example Home: 1111 -> 11112222)
4. Start synchronisation
5. Default number wasn't amended in phone but new edited number detail (Tel Home 11112222) was created.
*/
void CDefaultVoiceParamTests::TestOneL()
	{
	TContactItemId id = CreateItemL(KName(),KPhoneNumber());
	
	CContactIdArray* array = CContactIdArray::NewLC();
	array->AddL(id);


	ExportContactsL(KVCardFile1,array);
	CreateTestDataL(id, KVCardFile2,KModifiedNumberProperty); 
	ImportContactsL(KVCardFile2);
	ExportContactsL(KVCardFile3,array); //for checking
	CleanupStack::PopAndDestroy(array);

	TEST_CONDITION(CountPhoneNumberFieldsL(id)==1); //no new field was created
	}

/**
Case2
Default number is deleted from PIM and new number is created in PIM. 
After synchronisation default number is not deleted from PIM and new number is not created to phone.
(If default number is deleted only and synch is started, default number is not deleted from phone)

	1. Choose a phone number field of a phone contact as a default
		(For example Tel home: 1111)
	2. Start Synchronisation
	3. Delete default number (Home:1111) in PIM and create a new number in PIM (Work2: 4444)
	4. Start synchronisation
	5. Default number wasn't deleted from phone and new number wasn't inserted to phone
*/
void CDefaultVoiceParamTests::TestTwoL()
	{
	TContactItemId id = CreateItemL(KName,KPhoneNumber);

	CContactIdArray* array = CContactIdArray::NewLC();
	array->AddL(id);
	ExportContactsL(KVCardFile1,array);
	CreateTestDataL(id,KVCardFile2,KModifiedNumberProperty2); 
	ImportContactsL(KVCardFile2);
	ExportContactsL(KVCardFile3,array); //for checking
	CleanupStack::PopAndDestroy(array);
	TEST_CONDITION(CountPhoneNumberFieldsL(id)==1); 

	CContactItem* item = iDb->ReadContactLC(id);
	CContactItemFieldSet& fieldSet = item->CardFields();
	TInt pos=fieldSet.Find(KUidContactFieldPhoneNumber);
	while (pos!=KErrNotFound)
		{
		const CContactItemField& field = (fieldSet)[pos];
		if (field.Storage()->IsFull())
			{
			CContactTextField* textfield = field.TextStorage();
			_LIT(KExpectedNumber,"4444");
			TEST_CONDITION(textfield->Text()==KExpectedNumber);
			}
		pos=fieldSet.FindNext(KUidContactFieldPhoneNumber,++pos);
		}
	CleanupStack::PopAndDestroy(item);
	}


/**
Case3
Default number is selected in phone, and same number is edited in PIM. 
After synchronisation edited number is not syncronised at all
	1. Choose a phone number field of a phone contact as a default
		(For example Tel home: 1111)
	2. Edit the same number (phone's default) for example
                         home 1111 to 11114444 in PIM
	3. Start Synchronisation
	4. Default number was not edited in phone. New number was inserted to phone 
	but number was same as the default's number. So, edited number wasn't 
	synchronised at all.

*/
void CDefaultVoiceParamTests::TestThreeL()
	{
	//Not sure how to simulate. I believe this is case is duplicated by TestOneL()
	}

/*
Case 4
Default number is selected in phone, same number is deleted from PIM and new 
number is created in PIM. 
After synchronisation default number is not deleted from PIM but it is duplicated. 
	1. Choose a phone number field of a phone contact as a default
		(For example Tel home: 1111)
	2. Delete the same number (phone's default) in PIM and create a new number in PIM 
	3. Start Synchronisation
	4. Default is not deleted from PIM. New number is inserted to phone but number is 
	same as default's. Number you created in PIM, is copied correctly to phone.

*/
void CDefaultVoiceParamTests::TestFourL()
	{
	TContactItemId id = CreateItemL(KName,KPhoneNumber);

	CContactIdArray* array = CContactIdArray::NewLC();
	array->AddL(id);
	ExportContactsL(KVCardFile1,array);
	CreateTestDataL(id, KVCardFile2,KModifiedNumberProperty3); 
	ImportContactsL(KVCardFile2);
	ExportContactsL(KVCardFile3,array); //for checking
	CleanupStack::PopAndDestroy(array);
	TEST_CONDITION(CountPhoneNumberFieldsL(id)==1); //no new field was created	
	}


/**This checks the contact indentity fields extracted from a known vCard (KTestVcardFile) to ensure
	the contact is exactly what we expect. This tests both the field value and label in the generated contact.
	*/
void CDefaultVoiceParamTests::TestFiveL()
	{
	CDesCArray* value = new(ELeave) CDesCArrayFlat(KNumberOfExtendedFieldNames);
	CleanupStack::PushL(value);
	CDesCArray* label = new(ELeave) CDesCArrayFlat(KNumberOfExtendedFieldNames);	
	CleanupStack::PushL(label);

	for(TInt i=0;i<KNumberOfExtendedFieldNames;i++)
		{
		value->AppendL(*((const TDesC*) GNames[i]));
		label->AppendL(*((const TDesC*) GLabels[i]));
		}
	ImportContactsL(KTestVcardFile);
	const CContactIdArray& list = *iDb->SortedItemsL();
	CheckCardL(list[0], value, label);
	CleanupStack::PopAndDestroy(2,value); // + label
	iDb->DeleteContactL(list[0]);
	}

/** This generates a whole bunch of conacts (KNumberOfRandomContacts) and exports them to a vCard file.
	The contacts are then read back in. This tests the merge functionality of vCard parsing.
	The retrieved cards' fields are checked against the original cards' fields to make sure they're the same */
void CDefaultVoiceParamTests::TestSixL()
	{
	CDesCArray* fields = new(ELeave) CDesC16ArrayFlat(KNumberOfExtendedFieldNames*KNumberOfRandomContacts);
	CleanupStack::PushL(fields);

	CDesCArray* labels = new(ELeave) CDesC16ArrayFlat(KNumberOfExtendedFieldNames);	
	CleanupStack::PushL(labels);
	TInt  i;	
	for( i=0;i<KNumberOfExtendedFieldNames;i++)
		{
		labels->AppendL(*((const TDesC*) GLabels[i]));
		}
	RArray<TInt> allIds;
	CleanupClosePushL(allIds);
			
	TInt64 randseed = 0;
	CWordDigrams* firstname = CWordDigrams::NewLC(KFirstNameFileName, randseed);
	CWordDigrams* lastname = CWordDigrams::NewLC(KLastNameFileName, randseed);
	CleanupStack::Pop(lastname);
	CleanupStack::Pop(firstname);
	CRandomContactGenerator* generator = CRandomContactGenerator::NewL(firstname,lastname );

	CleanupStack::PushL(generator);
	
	generator->SetDbL(*iDb);
	RArray<TInt> nameIds;
	CleanupClosePushL(nameIds);
	for( i = 0; i<KNumberOfExtendedFieldNames;i++)
		{
		nameIds.AppendL(KExtendedFieldNames[i]);
		}


	for ( i = 0;i<KNumberOfRandomContacts;i++)
		{
		TContactItemId id = generator->AddTypicalRandomContactWithNamesL(nameIds, ETrue);
		allIds.AppendL(id);
		CContactCard* card = STATIC_CAST(CContactCard*, iDb->OpenContactL(id));
		CleanupStack::PushL(card);
		CheckCard(card,fields, labels,ECheckLabel|EAppendValue );
		iDb->CommitContactL(*card);	
		CleanupStack::PopAndDestroy(card);
	    }
	    
	CleanupStack::PopAndDestroy( 2, generator );	// + nameIds
	
	ExportContactsL(KVCardFile1, iDb->SortedItemsL());
	ImportContactsL(KVCardFile1);

	for (i =0; i<allIds.Count();i++)
		{
		CheckCardL(allIds[i], fields);
		for(TInt j=0;j<KNumberOfExtendedFieldNames;j++)
			{
			fields->Delete(0);
			}
		}
	CleanupStack::PopAndDestroy(3, fields );	// +label

	}


/** Import contacts from the vCard file specified by aFileName */
void CDefaultVoiceParamTests::ImportContactsL(const TDesC& aFileName)
	{
	RFileReadStream vcard;
	User::LeaveIfError(vcard.Open(iTest.Fs(), aFileName, EFileRead));
	CleanupClosePushL(vcard);

	TBool success=EFalse;
	CArrayPtr<CContactItem>* contactItems=iDb->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
	CleanupStack::PopAndDestroy(&vcard);
	contactItems->ResetAndDestroy();
	delete contactItems;
	}

/** Export contact items specified by aIds to aFileName */
void CDefaultVoiceParamTests::ExportContactsL(const TDesC& aFileName, const CContactIdArray* aIds)
	{
	RFile outfile;
	outfile.Replace(iTest.Fs(),aFileName,EFileWrite);
	CleanupClosePushL(outfile);
	RFileWriteStream writeStream(outfile);
	CleanupClosePushL(writeStream);
		
	TUid uid;
	uid.iUid=KUidVCardConvDefaultImpl;
	iDb->ExportSelectedContactsL(uid,*aIds,writeStream,CContactDatabase::EDefault);

	writeStream.CommitL();
	CleanupStack::PopAndDestroy(2); //writeStream.Close(), outfile.Close()
	}


/** Creates a vCard with the correct UID for the contact item specified by aId. */
void CDefaultVoiceParamTests::CreateTestDataL(TContactItemId aId, const TDesC& aFileName, const TDesC8& aVCardData)
	{
	RFile file;
	TInt err=file.Replace(iTest.Fs(), aFileName, EFileWrite+EFileShareAny+EFileStreamText);
	User::LeaveIfError(err);
	TPtrC8 header((const TText8*)	"BEGIN:VCARD\r\n"
									"VERSION:2.1\r\n"
									"REV:20020520T134824Z\r\n"
									"N:Name;;;;\r\n");
	TPtrC8 footer((const TText8*)	"END:VCARD\r\n");
	TPtrC8 uidProperty((const TText8*)	"UID:");
	TPtrC8 endProperty((const TText8*)	"\r\n");
	file.Write(header);
	file.Write(uidProperty);
	HBufC* buffer = iTest.ContactUidLC(aId,KMachineUniqueId);
	TBuf8<KMaxExternalizedTokenLength> des;
	des.Copy(*buffer);
	file.Write(des);
	CleanupStack::PopAndDestroy(buffer);
	file.Write(endProperty);
	file.Write(aVCardData);
	file.Write(footer);
	file.Close();
	}

/** Count the number of non-empty phone number fields */
TInt CDefaultVoiceParamTests::CountPhoneNumberFieldsL(TContactItemId aId)
	{
	CContactItem* item = iDb->ReadContactLC(aId);
	CContactItemFieldSet& fieldSet = item->CardFields();
	TInt numPhoneNumbers=0;
	TInt pos=fieldSet.Find(KUidContactFieldPhoneNumber);
	while (pos!=KErrNotFound)
		{
		const CContactItemField& field = (fieldSet)[pos];
		if (field.Storage()->IsFull())
			numPhoneNumbers++;
		pos=fieldSet.FindNext(KUidContactFieldPhoneNumber,++pos);
		}
	CleanupStack::PopAndDestroy(item);
	return numPhoneNumbers;
	}

/** Create a contact item */
TContactItemId CDefaultVoiceParamTests::CreateItemL(const TDesC& aName, const TDesC& aNumber)
	{
	TContactItemId templateId = iDb->TemplateId();
	CContactItem* templateCard = iDb->ReadContactLC(templateId);
	CContactCard* card=CContactCard::NewL(templateCard); 
	CleanupStack::PushL(card);
	SetNameL(*card, KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN, aName, EFalse);
	SetNameL(*card, KUidContactFieldPhoneNumber,KUidContactFieldVCardMapTEL, aNumber, EFalse);
	TContactItemId id = iDb->AddNewContactL(*card);
	CleanupStack::PopAndDestroy(2,templateCard); 
	return id;
	}


/** 
 * Regression testcode for defect EXT-599EF6 "Problems appear when contact's 
 * default number is deleted / edited in PIM - Lotus Organizer 6.0"
 * 
 * The basic problem is:
 * SymbianOS uses TEL;VOICE in a vCard to represent a phone number
 * TimeIS PC Connectivity code sends TEL;VOICE to PIM
 * PIM removes VOICE property parameter
 * TimeIS sends TEL back 
 * Contacts model does not match TEL;VOICE and TEL, so a duplicate number is added
 *
 */
LOCAL_C void DefaultVoiceParamTestsL()
	{
	CDefaultVoiceParamTests* test = CDefaultVoiceParamTests::NewLC(*CntTest);
	test->RunTestsL();
	CleanupStack::PopAndDestroy(test);		
	}

/** Add KNumAccessCountContacts contacts **/
LOCAL_C void AddNewContactsL()
	{
	_LIT(KNameFormat,"NAME #%d");
	_LIT(KPhoneNumber,"123");
	for (TInt ii=0;ii<KNumAccessCountContacts;ii++)
		{
		CContactCard* card=CContactCard::NewL();
		CleanupStack::PushL(card);
		TBuf<16> name;
		name.Format(KNameFormat,ii);
		SetNameL(*card, KUidContactFieldFamilyName,KUidContactFieldVCardMapUnusedN, name, EFalse);
		SetNameL(*card, KUidContactFieldPhoneNumber,KUidContactFieldVCardMapTEL, KPhoneNumber, EFalse);
		CntTest->Db()->AddNewContactL(*card);
		CleanupStack::PopAndDestroy(card); 
		}
	}	


LOCAL_C void EmptyDatabase()
	{
	TContactItemId theid;
	TContactIter iter(*CntTest->Db());
	theid=iter.FirstL();
	while(theid!=KNullContactId )
		{
		CntTest->Db()->DeleteContactL(theid);
		theid=iter.NextL();
		}
	}
	

enum TAccessCountFlags {EIncAccessCount,EDecAccessCount};

LOCAL_C void UpdateAccessCountL(TAccessCountFlags aUpdateType)
	{
	TContactItemId theid;
	TContactIter iter(*CntTest->Db());
	theid=iter.FirstL();
	while(theid!=KNullContactId )
		{
		CContactItem* contactItem=CntTest->Db()->OpenContactL(theid);
		CleanupStack::PushL(contactItem);
		if (contactItem->IsDeleted())
			test.Next(_L("Synchronizer knows record 2 is deleted"));

		if(aUpdateType==EIncAccessCount)
			contactItem->IncAccessCount();
		else
			contactItem->DecAccessCount();
		
		theid=iter.NextL();
		CntTest->Db()->CommitContactL(*contactItem);
		CleanupStack::PopAndDestroy(contactItem);	
		}
	}

/** Tests from T_EXPDEL */
LOCAL_C void AccessCountTestsL()
	{
	CContactDatabase* db=CntTest->CreateDatabaseL();
	AddNewContactsL();
	TEST_CONDITION(db->CountL()==KNumAccessCountContacts);

	test.Next(_L("Increase Access Count"));
	UpdateAccessCountL(EIncAccessCount);
	
	test.Next(_L("Removing second contact"));
	TContactIter iter(*db);
	TContactItemId theid=iter.FirstL();
	theid=iter.NextL();
	db->DeleteContactL(theid);
	TEST_CONDITION(db->CountL()==KNumAccessCountContacts-1);
	
#if defined(_DEBUG) //test case only valid in debug mode, see CContactDatabase::DeletedContactsLC for detail.
	test.Next(_L("Checking DeletedContactsLC Deleted Count and Identity"));
	CContactIdArray* delArray = db->DeletedContactsLC();
	TEST_CONDITION(delArray->Count() == 1);  // as second contact is deleted but has access count of > 1
	TEST_CONDITION((*delArray)[0] == theid); // test the DeletedContact Id is the second Id (that was deleted above)	
	
	CleanupStack::PopAndDestroy(delArray);
#endif //defined(_DEBUG)
	
	test.Next(_L("Synchronizing"));
	UpdateAccessCountL(EDecAccessCount);
	TEST_CONDITION(db->CountL()==KNumAccessCountContacts-1);
	test.Next(_L("Exporting"));
	CContactIdArray* TheIds=CContactIdArray::NewLC();
	theid=iter.FirstL();
	while(theid!=KNullContactId )
		{
		TheIds->AddL(theid);
		theid=iter.NextL();
		}			
	CFileStore* store = CDirectFileStore::ReplaceLC(CntTest->Fs(),KExpDelAFileName(),EFileWrite);
	store->SetTypeL(KDirectFileStoreLayoutUid);
	RStoreWriteStream outstream;
	TStreamId id = outstream.CreateLC(*store);
	TUid uid;
	uid.iUid= KVersitEntityUidVCard; // KUidVCardConvDefaultImpl;
	db->ExportSelectedContactsL(uid,*TheIds,outstream,CContactDatabase::EIncludeX);
	outstream.CommitL();
	store->SetRootL(id);
	store->CommitL();  	
	CleanupStack::PopAndDestroy(2); // store+ oustream
	CleanupStack::PopAndDestroy(TheIds); 
	test.Next(_L("Emptying database"));
	EmptyDatabase();
	}

//
/* Test Function Implementations                                             */
//
TContactItemId AddContactL (CContactDatabase& aDatabase)
	{
	TInt bit = 0;
	TContactItemId retval;
	CRandomContactGenerator* generator = NULL;

	generator = CRandomContactGenerator::NewL();
	CleanupStack::PushL(generator);

	generator->SetDbL(aDatabase);

	bit |= CContactDatabase::ESmsable;
	retval = generator->AddTypicalContactForFilterL(bit);

	CleanupStack::PopAndDestroy( generator );
	
	return retval;
	}

void ExportContactTestL()
	{
	TContactItemId itemId;
	RFs fsSession;
	RFileWriteStream fileStream;
	CContactIdArray* idArray = NULL;
	CContactItem* contactAdded = NULL;
	TInt firstNameIndex = KErrNotFound;

	idArray = CContactIdArray::NewL();

	CleanupStack::PushL(idArray);

	User::LeaveIfError(fsSession.Connect());
	User::LeaveIfError(fileStream.Replace(fsSession, KOutputFileName, EFileWrite));


	itemId = AddContactL(*CntTest->Db());
	contactAdded = CntTest->Db()->OpenContactL(itemId);
	CleanupStack::PushL(contactAdded);

	CContactItemFieldSet& fieldSet = contactAdded->CardFields();
	
	firstNameIndex = fieldSet.Find(KUidContactFieldGivenName);
	TEST_CONDITION(firstNameIndex != KErrNotFound);

	CContactItemField& field = fieldSet[firstNameIndex];
	field.SetLabelL(_L(" New Label "));

	CntTest->Db()->CommitContactL(*contactAdded);
	CleanupStack::PopAndDestroy(contactAdded);
	contactAdded = NULL;


	idArray->InsertL(0, itemId);

	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KUidVCardConvDefaultImpl), *idArray, fileStream, 0 /*option*/);
	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KUidVCardConvDefaultImpl), *idArray, fileStream, 1 /*option*/);

	CleanupStack::PopAndDestroy(idArray);

	fileStream.Close();
	fsSession.Close();
	}

// Export a contact which has no name field,
// and check that the exported file doesn't contain a name field:
void ExportNamelessContactTestL()
	{
	TContactItemId itemId;
	RFs fsSession;
	RFileWriteStream fileStream;
	RFileReadStream readStream;
	CContactIdArray* idArray = NULL;
	CContactItem* contactAdded = NULL;
	TInt Index = KErrNotFound;

	idArray = CContactIdArray::NewL();

	CleanupStack::PushL(idArray);

	User::LeaveIfError(fsSession.Connect());
	User::LeaveIfError(fileStream.Replace(fsSession, KOutputFileName, EFileWrite));

	// Create the usual random contact:
	itemId = AddContactL(*CntTest->Db());
	contactAdded = CntTest->Db()->OpenContactL(itemId);
	CleanupStack::PushL(contactAdded);

	// Find the name fields and remove them:
	CContactItemFieldSet& fieldSet = contactAdded->CardFields();
	Index = fieldSet.Find(KUidContactFieldGivenName);
	if(Index != KErrNotFound)
		fieldSet.Remove(Index);
	Index = fieldSet.Find(KUidContactFieldFamilyName);
	if(Index != KErrNotFound)
		fieldSet.Remove(Index);
	Index = fieldSet.Find(KUidContactFieldAdditionalName);
	if(Index != KErrNotFound)
		fieldSet.Remove(Index);
	Index = fieldSet.Find(KUidContactFieldPrefixName);
	if(Index != KErrNotFound)
		fieldSet.Remove(Index);
	Index = fieldSet.Find(KUidContactFieldSuffixName);
	if(Index != KErrNotFound)
		fieldSet.Remove(Index);

	// Put the contact back:
	CntTest->Db()->CommitContactL(*contactAdded);
	CleanupStack::PopAndDestroy(contactAdded);
	contactAdded = NULL;

	// Export it:
	idArray->InsertL(0, itemId);
	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KUidVCardConvDefaultImpl), *idArray, fileStream, 0 /*option*/);
	CleanupStack::PopAndDestroy(idArray);
	fileStream.Close();

	// Verify the output by reading it in and searching it long hand:
	User::LeaveIfError(readStream.Open(fsSession, KOutputFileName, EFileRead));
	HBufC8 * buf = HBufC8::NewLC(100);
	TPtr8 ptr = buf->Des();
	TRAPD(ret, readStream.ReadL(ptr));
	TEST_CONDITION(ret == KErrNone || (ret == KErrEof && buf->Length() > 0));

	// This is the important test - we should not find a name entry:
	_LIT8(str, "\nN:");
	TEST_CONDITION(buf->Find(str) == KErrNotFound);

	// Clean up:
	CleanupStack::PopAndDestroy(buf);

	readStream.Close();
	fsSession.Close();
	}

//=======================================================================
//FindInFields
//Finds a given value in the field set, assumes that the fields are text.
//Used by CheckTypeSupport.
//=======================================================================
TBool FindInFields(CContactItemFieldSet& aFieldSet,TPtrC aExpectedValue)
	{
	for (TInt i = 0; i< aFieldSet.Count(); i++ )
		{
		CContactItemField& aFieldValue = aFieldSet[i];
		TPtrC value = aFieldValue.TextStorage()->Text();
		if (value == aExpectedValue)
			{
			return ETrue;
			}
		}
		
	// Value not found, return false.
	return EFalse;
	}

//=======================================================================
//FieldCheck
//Compares the value of the CContactItemField at aIndex of aFieldSet
//with the expected value.  Used by UpdateVCardTestL
//=======================================================================
TBool FieldCheck(CContactItemFieldSet& aFieldSet,TInt aIndex,TPtrC aExpectedValue)
	{
	CContactItemField& aPhoneNum = aFieldSet[aIndex];
	TPtrC value = aPhoneNum.TextStorage()->Text();
	return (value == aExpectedValue);
	}
//=======================================================================
//UpdateVCardTestL()
//Checks updating of a contact via a modified vcard.
//1: Contact is imported from vcardupdate1.vcf
//2: Contact is exported to vcardupdate2.vcf
//3: Contact is updated from vcardupdate3.vcf
//=======================================================================
void UpdateVCardTestL()
	{
	test.Next(_L("Update VCard Test"));
	//Check the database is empty before we start
	EmptyDatabase();
	TInt count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 0);
	test.Next(_L("Database Empty"));
	
	RFs fsSession;
	User::LeaveIfError(fsSession.Connect());
	
	RFileReadStream readStream;
	RFileWriteStream writeStream;
	CContactIdArray* idArray = NULL;
	
	idArray = CContactIdArray::NewL();
	CleanupStack::PushL(idArray);
		
	//Import vcardupdate1.vcf
	test.Next(_L("Importing contact from vcardupdate1.vcf"));
	User::LeaveIfError(readStream.Open(fsSession,KVCardUpdate1, EFileRead));
	CleanupClosePushL(readStream);
	TBool success=EFalse;
	CArrayPtr<CContactItem>* contactItems=CntTest->Db()->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), readStream, success, CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
	//Store the id of the new contact - we need this later
	TContactItemId contactId = (*contactItems)[0]->Id();
	idArray->InsertL(0, contactId);
	readStream.Close();
	//Should be the only contact in the database at this point
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);
	test.Next(_L("Imported"));
	
	//Retrieve the new contact from the database and check the fields
	CContactItem* importedContact = CntTest->Db()->ReadContactLC(contactId);
	CContactItemFieldSet& importedContactFieldSet = importedContact->CardFields();

	/*
	* For this particular test we are only interested in the following field indexes:
	* 5:	Mobile 1
	* 6:	Mobile 2
	* 29:	Work tel 1
	* 30:	Work tel 2
	* 31:	Work tel 3
	* 32:	Work tel 4
	* 33:	Work tel 5
	* You can get other field indexes by using:
 	*
	 _LIT(KFormat,"%d:\t");
	 for (TInt i = 0; i < importedContactFieldSet.Count();i++)
		{
		CContactItemField& aPhoneNum = importedContactFieldSet[i];
		TPtrC label = aPhoneNum.Label();
		test.Printf(KFormat,i);
		test.Printf(label);
		}
	*/
	
	test.Next(_L("Check Mobile Number 1"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,5,_L("07905111")));
		
	test.Next(_L("Check Mobile Number 2"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,6,_L("07906222")));

	test.Next(_L("Check Work Number 1"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,29,_L("1111111111")));

	test.Next(_L("Check Work Number 2"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,30,_L("2222222222")));

	test.Next(_L("Check Work Number 3"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,31,_L("3333333333")));

	test.Next(_L("Check Work Number 4"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,32,_L("4444444444")));

	test.Next(_L("Check Work Number 5"));
	TEST_CONDITION(FieldCheck(importedContactFieldSet,33,_L("5555555555")));
	
	CleanupStack::PopAndDestroy(importedContact);

	//Export the contact to vcardupdate2.vcf
	test.Next(_L("Exporting contact to vcardupdate2.vcf"));
	User::LeaveIfError(writeStream.Replace(fsSession,KVCardUpdate2,EFileWrite));
	CleanupClosePushL(writeStream);
	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KUidVCardConvDefaultImpl), *idArray, writeStream,0);
	CleanupStack::PopAndDestroy(&writeStream);

	//Import vcardupdate3.vcf - this should have the same UID as vcardupdate2.vcf
	test.Next(_L("Updating contact from vcardupdate3.vcf"));
	User::LeaveIfError(readStream.Open(fsSession,KVCardUpdate3, EFileRead));
	success=EFalse;
	contactItems->ResetAndDestroy();
	delete contactItems;
	contactItems = CntTest->Db()->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), readStream, success, CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
	
	//We are updating the previous contact so we should still only have
	//one database entry.  If count == 2 here check the uids of C:\\vcardupdate2.vcf and 
	//Z:\\System\\Programs\\CntmodelTest\\vcardupdate3.vcf - the last digits of vcardupdate3 should match vcardupdate2
	count = CntTest->Db()->CountL();
	//TEST_CONDITION(count == 1);
	test.Next(_L("Imported"));

	//Retrieve the updated contact from the database and check the fields
	//Use the same contact id as before
	CContactItem* updatedContact = CntTest->Db()->ReadContactLC(contactId);
	CContactItemFieldSet& updatedContactFieldSet = updatedContact->CardFields();

	test.Next(_L("Check Updated Mobile Number 1"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,5,_L("7700900329")));
		
	test.Next(_L("Check Updated Mobile Number 2"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,6,_L("07700900529")));

	test.Next(_L("Check Updated Work Number 1"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,29,_L("7700900999")));

	test.Next(_L("Check Updated Work Number 2"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,30,_L("7700900888")));

	test.Next(_L("Check Updated Work Number 3"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,31,_L("7700900777")));

	test.Next(_L("Check Updated Work Number 4"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,32,_L("7700900666")));

	test.Next(_L("Check Updated Work Number 5"));
	TEST_CONDITION(FieldCheck(updatedContactFieldSet,33,_L("7700900555")));
 
	//cleanup
	CleanupStack::PopAndDestroy(updatedContact);
	CleanupStack::PopAndDestroy(&readStream);
	CleanupStack::PopAndDestroy(idArray);
	fsSession.Close();
	contactItems->ResetAndDestroy();
	delete contactItems;
	}

/*
The following is the test for trailing space to test DEF051853.
*/

const TUint KSpaceChar = 0x20;

_LIT( KSearchText, "\x2029" );
_LIT( KIncommingVCard, "z:\\cntvcard\\address-with-trailing-white-space.vcf" );
_LIT( KOutgoingVCard, "c:\\address-with-trailing-white-space-out.vcf" );

/** Import contacts from the vCard file specified by aFileName */
CArrayPtr<CContactItem>* ImportContactsL(const TDesC& aFileName, CContactDatabase& aDatabase);
void CheckForWhiteSpaceL( const TDesC& aFieldToCheck );
void ImportAndCheckForNoTrailSpaceStrippingL(const TDesC& aFileName, CContactIdArray& aArray);
void TrailingSpaceExportContactsL(const TDesC& aFileName, CContactIdArray* aIds);
void DoTrailingSpaceImportAndExportTestL();

// Implementations
CArrayPtr<CContactItem>* ImportContactsL(const TDesC& aFileName, CContactDatabase& aDatabase)
	{
	CArrayPtr<CContactItem>* retval = NULL;
	RFileReadStream vcard;
	RFs fileSystem;
	User::LeaveIfError( fileSystem.Connect() );
	CleanupClosePushL( fileSystem );
	User::LeaveIfError(vcard.Open( fileSystem, aFileName, EFileRead));
	CleanupClosePushL(vcard);

	TBool success=EFalse;
	retval = aDatabase.ImportContactsL(TUid::Uid(KVersitEntityUidVCard), vcard, success, CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
	CleanupStack::PopAndDestroy(&vcard);
	CleanupStack::PopAndDestroy(&fileSystem);

	return retval;
	}

void CheckForWhiteSpaceL( const TDesC& aFieldToCheck )
	{
	TInt trailingSpaceLocation = KErrNotFound;
	TUint thisShouldBeASpaceChar = 0;

	trailingSpaceLocation = ( aFieldToCheck.Find(KSearchText) -1 );

	thisShouldBeASpaceChar = aFieldToCheck[ trailingSpaceLocation ];
	test ( thisShouldBeASpaceChar == KSpaceChar );

	}

void ImportAndCheckForNoTrailSpaceStrippingL(const TDesC& aFileName, CContactIdArray& aArray)
	{
	CArrayPtr<CContactItem>* contactList = NULL;
	CContactItem* item = NULL;
	TInt addressIndex = KErrNotFound;

	aArray.Reset();

	contactList = ImportContactsL( aFileName, *CntTest->Db() );
	CleanupStack::PushL( contactList );

	item = (*contactList)[0];
	aArray.AddL( item->Id() );
	CContactItemFieldSet& fieldSet = item->CardFields();

	addressIndex = fieldSet.Find( KUidContactFieldAddress );

	CContactItemField& addressField = fieldSet[ addressIndex ];


	TPtrC data = addressField.TextStorage()->Text();

	CheckForWhiteSpaceL( data );

	contactList->ResetAndDestroy();
	CleanupStack::PopAndDestroy( contactList );

	}

/** Export contact items specified by aIds to aFileName */
void TrailingSpaceExportContactsL(const TDesC& aFileName, CContactIdArray* aIds)
	{
	RFs fsSession;
	RFileWriteStream fileStream;
	
	User::LeaveIfError(fsSession.Connect());
	User::LeaveIfError(fileStream.Replace(fsSession, aFileName, EFileWrite));

	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KVersitEntityUidVCard), *aIds, fileStream, 0 /*option*/);

	fileStream.Close();
	fsSession.Close();

	}


void DoTrailingSpaceImportAndExportTestL()
	{
	CContactIdArray* idArray = NULL;

	idArray = CContactIdArray::NewL();
	CleanupStack::PushL( idArray );

	ImportAndCheckForNoTrailSpaceStrippingL(KIncommingVCard, *idArray);
	TrailingSpaceExportContactsL( KOutgoingVCard, idArray );

	ImportAndCheckForNoTrailSpaceStrippingL(KOutgoingVCard, *idArray);
	CleanupStack::PopAndDestroy( idArray );
	}


void ImportLargeVCardTestL()
	{
 	test.Printf(_L("Importing large VCards"));
 	
	EmptyDatabase();
			
	RFs fsSession;
	User::LeaveIfError(fsSession.Connect());
	
	RFileReadStream readStream;
		
 	// Import vcardupdate1.vcf.
 	test.Printf(_L("Importing contact from testvcard1.vcf"));
	User::LeaveIfError(readStream.Open(fsSession,KTestVcardFile1, EFileRead));
 
 	CleanupClosePushL(readStream);
 
 	TBool success=EFalse;
 	CArrayPtr<CContactItem>* contactItems=CntTest->Db()->ImportContactsL(
 		TUid::Uid(KUidVCardConvDefaultImpl), readStream, success, 
 		CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
 
 	// If the above function does not leave or panic test has been successful.
 	
 	TEST_CONDITION(contactItems->Count() == 1);
 	
 	test.Printf(_L("Importing large VCards successful\n"));
 
 	contactItems->ResetAndDestroy();
 	delete contactItems;
 
 	CleanupStack::PopAndDestroy(&readStream);	
 
 	fsSession.Close();			
 	}
 	

//return the number of email fields within a contact
TInt ContactEmailCountL(TContactItemId aContactId)
	{
	
	CContactItem *item = CntTest->Db()->ReadContactLC(aContactId);
	TInt pos = 0;
	TInt many = 0;
	while (pos < item->CardFields().Count() )
		{
		pos = item->CardFields().FindNext(KUidContactFieldEMail, pos);
		if( pos == KErrNotFound )
			{
			break;
			}
		else
			{
			++many;
			++pos;
			}
		}
	
	CleanupStack::PopAndDestroy(item);
	return many;
	
	}

//import a single contact item from vcard into db, return id of contact in db
TContactItemId ImportVCardL(const TDesC &aVcard, const TInt aOption)
	{
	RFs fsSession;
	User::LeaveIfError(fsSession.Connect());
	
	RFileReadStream readStream;
		
	//Import vcf
	User::LeaveIfError(readStream.Open(fsSession,aVcard, EFileRead));
	CleanupClosePushL(readStream);
	TBool success=EFalse;
	CArrayPtr<CContactItem>* contactItems = CntTest->Db()->ImportContactsL(
												TUid::Uid(KVersitEntityUidVCard), readStream, success, 
												aOption
											);
											
	//Store the id of the new contact - we need this later
	TContactItemId contactId = (*contactItems)[0]->Id();
	contactItems->ResetAndDestroy();
	delete contactItems;
	contactItems = NULL;
	//cleanup
	CleanupStack::PopAndDestroy(&readStream);
	fsSession.Close();
	return contactId;
	}
	
//returns the number of non empty fields in contact
TInt ContactFilledFields(TContactItemId aContactId)
	{
	CContactItem *item = CntTest->Db()->ReadMinimalContactLC(aContactId);
	TInt ret = item->CardFields().Count();
	CleanupStack::PopAndDestroy(item);
	return ret;
	}

/**
@SYMTestCaseID PIM-T-CNTVCARD-DEF084708-0001
@SYMTestType UT
@SYMTestPriority High
@SYMDEF DEF084708
@SYMTestCaseDesc Vcard with full parameter description of TYPE=VALUE for properties 
ADDR or TEL should be correctly imported.

@SYMTestActions
1) import vcard with home and tel properties with parameters of TYPE=HOME and TYPE=WORK
2) check that imported contact contains the correct home and work address 

@SYMTestExpectedResults For the above tests:
On importing, the type=home and type=work fields should not be deleted.

*/
void CheckTypeSupport(const TDesC &inputVcard)
{
	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-DEF084708-0001 Checking if the type=value parameter is handled correctly."));
	
	//Check the database is empty before we start
	EmptyDatabase();
	TInt count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 0);

	// First import the vcard file.
	RFs fsSession;
	RFileReadStream readStream;
	User::LeaveIfError(fsSession.Connect());
	test.Printf(_L("Importing contact "));
	User::LeaveIfError(readStream.Open(fsSession,inputVcard, EFileRead));
	CleanupClosePushL(readStream);
	TBool success=EFalse;
	CArrayPtr<CContactItem>* contactItems=CntTest->Db()->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), readStream, success, CContactDatabase::EImportSingleContact+CContactDatabase::ETTFormat);
	readStream.Close();
	
	//Should be the only contact in the database at this point
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);
	test.Printf(_L("Imported the contact item"));

	// Check that the home address and work address has been imported correctly into the card.
	test.Printf(_L("Find whether all the address fields are present."));
	TContactItemId contactId = (*contactItems)[0]->Id();
	CContactItem* importedContact = CntTest->Db()->ReadContactLC(contactId);
	CContactItemFieldSet& fieldSet = importedContact->CardFields();
	
	TEST_CONDITION(FindInFields(fieldSet, _L("W211")));
	TEST_CONDITION(FindInFields(fieldSet, _L("W212")));
	TEST_CONDITION(FindInFields(fieldSet, _L("W213")));
	TEST_CONDITION(FindInFields(fieldSet, _L("W214")));
	TEST_CONDITION(FindInFields(fieldSet, _L("W215")));

	TEST_CONDITION(FindInFields(fieldSet, _L("H211")));
	TEST_CONDITION(FindInFields(fieldSet, _L("H212")));
	TEST_CONDITION(FindInFields(fieldSet, _L("H213")));
	TEST_CONDITION(FindInFields(fieldSet, _L("H214")));
	TEST_CONDITION(FindInFields(fieldSet, _L("H215")));
		
	// Check that the phone number has been correctly imported	
	TEST_CONDITION(FindInFields(fieldSet, _L("222222222222222222")));
	TEST_CONDITION(FindInFields(fieldSet, _L("333333333333333333")));

	//cleanup
	CleanupStack::PopAndDestroy(importedContact);
	CleanupStack::PopAndDestroy(&readStream);
	fsSession.Close();
	contactItems->ResetAndDestroy();
	delete contactItems;
}

/**
@SYMTestCaseID PIM-T-CNTVCARD-DEF081712-0001
@SYMTestType UT
@SYMTestPriority High
@SYMDEF DEF081712
@SYMTestCaseDesc Contact properties on device not deleted during synchronisation

@SYMTestActions
1) import vcard with 3 emails
2) check that imported contact contains 3 email fields
3) update uid of imported contact to 8
4) import vcard with no email fields and uid 8
5) check that all email fields are deleted
6) check that no other fields have been deleted

@SYMTestExpectedResults For the above tests:
It should be possible to delete multiple fields of the same type when a vcard is imported
*/

void VCardEmailTestL()
	{
	const TInt importOption =
		CContactDatabase::ELocalTime |
		CContactDatabase::EImportSingleContact | 
		CContactDatabase::ENullTemplateId;

	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-DEF081712-0001 VCard Email Test"));
	//start with fresh/clean database
	CntTest->CreateDatabaseL();
	TInt count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 0);
	
	test.Next(_L("Importing initial contact from 1.vcf"));
	TContactItemId contactId = ImportVCardL( KVcard1, importOption );
	TInt initialcount = ContactFilledFields(contactId);
	
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);
	
	CContactItem *item = CntTest->Db()->OpenContactLX(contactId);
	CleanupStack::PushL( item );
	
	_LIT(guid, "8");
	item->SetUidStringL( const_cast<TDesC &>( guid() ) );
	CntTest->Db()->CommitContactL( *item );
	CleanupStack::PopAndDestroy(item);
	CleanupStack::Pop();//lock
	item = NULL;
	
	test.Next(_L("check number of email fields"));
 	TEST_CONDITION(ContactEmailCountL(contactId) == 3);
 	
 	test.Next(_L("reimport contact from 2.vcf"));
	contactId = ImportVCardL( KVcard2, importOption );
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);
	
	TInt updatedcount = ContactFilledFields(contactId);
	
	test.Next(_L("check email fields are deleted"));
 	TEST_CONDITION(ContactEmailCountL(contactId) == 0);
 	
 	test.Next(_L("check no other fields are cleared or deleted"));
 	TEST_CONDITION( updatedcount == (initialcount - 3) );
	
	}
	
/**
@SYMTestCaseID PIM-T-CNTVCARD-DEF083613-0001
@SYMTestType UT
@SYMTestPriority High
@SYMDEF DEF083613
@SYMTestCaseDesc A VCard with lines terminated by LF(line-feed)s  only is not imported correctly.

@SYMTestActions
1) import a vcard containing only line-feed line terminators.
2) Check that the import was successful.

@SYMTestExpectedResults The vcard containing only LF line terminators is imported correctly.
*/

void VCardLineFeedOnlyTestL()
	{
	const TInt importOption =
		CContactDatabase::ELocalTime |
		CContactDatabase::EImportSingleContact | 
		CContactDatabase::ENullTemplateId;

	TRAPD(err, ImportVCardL( KVCardFileLF, importOption ));
	TEST_CONDITION(err==0);
	}
/**
@SYMTestCaseID PIM-T-CNTVCARD-DEF085873-0001
@SYMTestType UT
@SYMTestPriority High
@SYMDEF DEF085873
@SYMTestCaseDesc Check that use of the CContactVCardConverter::EIgnoreUid flag
behaves correctly.

@SYMTestActions
1) Empty database.
2) Import a vcard containing a UID without using the flag.
3) Check there is only one contact in the database.
4) Import the same vcard without using the flag.  The import should count as an
update of the existing contact item.
5) Check there is only one contact in the database.
6) Import the same vcard using the flag.  The import should count as an add of
a new item.
7) Check there are now 2 contacts in the database.

@SYMTestExpectedResults The vcard is added to the database a second time when
the CContactVCardConverter::EIgnoreUid flag is used.
*/
void VCardIgnoreUidTestL()
	{
	const TInt importOption1 = CContactDatabase::ELocalTime;

	const TInt importOption2 =
		CContactDatabase::ELocalTime |
		CContactVCardConverter::EIgnoreUid;

	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-DEF085873-0001 Check that use of the CContactVCardConverter::EIgnoreUid flag behaves correctly."));
	
	// 1) Empty database.
	EmptyDatabase();
	TInt count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 0);

	// 2) Import a vcard containing a UID without using the flag.
	TRAPD(err, ImportVCardL( KVCardFileUID, importOption1 ));	
	TEST_CONDITION(err==0);
		
	// 3) Check there is only one contact in the database.
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);
	
	// 4) Import the same vcard without using the flag.  The import should count
	// as an update of the existing contact item.
	TRAP(err, ImportVCardL( KVCardFileUID, importOption1 ));	
	TEST_CONDITION(err==0);

	// 5) Check there is only one contact in the database.
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 1);

	// 6) Import the same vcard using the flag.  The import should count as an
	// add of a new item.
	TRAP(err, ImportVCardL( KVCardFileUID, importOption2 ));	
	TEST_CONDITION(err==0);

	// 7) Check there are now 2 contacts in the database.
	count = CntTest->Db()->CountL();
	TEST_CONDITION(count == 2);
	} 
/*@SYMTestPriority High
@SYMDEF PDEF097999 
@SYMTestCaseDesc The identity table is not updated if we pass a null value for 
updation (as a result sorting goes wrong)
@SYMTestActions
1) Import a vcard containing firstname and lastname.
2) Import the same vcf file but without  having lastname for one entry
3) Check whether the old lastname is present for that entry.

@SYMTestExpectedResults The identity table must be updated even if we give a null value for lastname.
so if we search for the old lastname it should not be there .
*/
LOCAL_C void TestImportContactsL(const TDesC& aFileName1 , const TDesC& aFileName2 , const TDesC8& aVcfData1 , const TDesC8& aVcfData2)
	{
	test.Next(_L("checking  Updation of Identity table"));
	CContactDatabase *db=CntTest->CreateDatabaseL();	

	RFs fileSystem;
	User::LeaveIfError( fileSystem.Connect() );
	CleanupClosePushL( fileSystem );
	//Creating input Vcf files
	RFile file;
	file.Replace(fileSystem, aFileName1, EFileWrite+EFileShareAny+EFileStreamText);
	file.Write(aVcfData1);
	file.Close();
	
	file.Replace(fileSystem, aFileName2, EFileWrite+EFileShareAny+EFileStreamText);
	file.Write(aVcfData2);
	file.Close();
		
	RFileReadStream vcard;
	User::LeaveIfError(vcard.Open(fileSystem, aFileName1, EFileRead));
	CleanupClosePushL(vcard);
	TBool success=EFalse;
	CArrayPtr<CContactItem>* contactItems=db->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, CContactDatabase::ETTFormat);
	CleanupStack::PopAndDestroy(&vcard);
	contactItems->ResetAndDestroy();
	delete contactItems;
	
	User::LeaveIfError(vcard.Open(fileSystem, aFileName2, EFileRead));
	CleanupClosePushL(vcard);
	success=EFalse;
	contactItems=db->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, CContactDatabase::ETTFormat);
	CleanupStack::PopAndDestroy(&vcard);

	//Search for "Bbb" it must not be there
    CContactItemFieldDef* fieldDef = new(ELeave) CContactItemFieldDef;
	TFieldType fieldtype = KUidContactFieldFamilyName;
	fieldDef->AppendL(fieldtype);
	CContactIdArray* matchList = db->FindLC(_L("Bbb"), fieldDef);
	TInt matchCount = matchList->Count();
	//Count  zero means the vcard has been updated properly
	TEST_CONDITION(matchCount==0);
	test.Printf	(_L("Sucessfully modified identity table"));
	CleanupStack::PopAndDestroy(matchList);
	contactItems->ResetAndDestroy();
	delete contactItems;
	delete fieldDef;
	CntTest->CloseDatabase();
	CleanupStack::PopAndDestroy(&fileSystem);
	}

 CArrayPtr<CContactItem>* ImportContactsL(const TDesC8& aImportFileInputDescriptor, CContactDatabase* aDatabase)
 	{
 	CArrayPtr<CContactItem>* retval = NULL;
 	
 	// read the input contents 
 	RDesReadStream vCard(aImportFileInputDescriptor);
 	CleanupClosePushL(vCard);
 	
	//import
 	TBool success=EFalse;
	retval = aDatabase->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vCard, success, CContactDatabase::EDefault);
 
 	CleanupStack::PopAndDestroy(&vCard);
 	return retval;
 	}

void TestStorageType( CContactItem *aContactItem, TStorageType aExpectedStorageType)
	{
	//verify test
 	TInt fieldIndex = aContactItem->CardFields().Find(KUidContactFieldVCardMapKEY);
 	TStorageType  storageType = aContactItem->CardFields()[fieldIndex].StorageType();
 	TEST_CONDITION(storageType == aExpectedStorageType);
 	test.Next(_L("ImportExportVCardWithKeyL passed"));
	}
	
 /**
 @SYMTestCaseID PIM-T-CNTVCARD-DEF097565-0001
 @SYMTestType UT
 @SYMTestPriority Medium
 @SYMDEF DEF097565
 @SYMTestCaseDesc Importr the vcf file and then check whether the BInary Key is imported 
 properly or not.

 @SYMTestActions
 1) Create an empty database.
 2) Import a vcard.
 3) Check there is Key field imported is having storage type KStorageTypeStore.
 
 @SYMTestExpectedResults The vcard is containing the Binary Key value.
 */	
 void ImportExportVCardWithKeyL(const TDesC8& aImportFileInputDescriptor, TStorageType aExpectedStorageType)
 	{
 	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-DEF097565-0001 ImportExportVCardWithKeyL Begins"));
 
 	CntTest->CreateDatabaseL();
 	
 	//import
 	CArrayPtr <CContactItem>* contactItems = NULL;
 	contactItems = ImportContactsL(aImportFileInputDescriptor, CntTest->Db());
 
 	//verify test
 	TestStorageType((contactItems->At(0)), aExpectedStorageType);
 
 	// reclaim memory
 	contactItems->ResetAndDestroy() ;
 	delete contactItems;
 	
 	CntTest->CloseDatabase();
   	}
/**
@SYMTestCaseID PIM-T-CNTVCARD-INC096705-0001
@SYMTestType UT
@SYMTestPriority Medium
@SYMDEF INC096705
@SYMTestCaseDesc Check that use of the CContactVCardConverter::EReplaceIfExists flag
behaves correctly.

@SYMTestActions
1) Empty database.
2) Import a vcard containing a company field with EDefault flag.
3) Check of company field is imported
4) Import the same vcard without company field and using CContactVCardConverter::EReplaceIfExists option

@SYMTestExpectedResults The vcard is replaced with the contact in the database when
the CContactVCardConverter::EReplaceIfExists flag is used.
*/
void ImportVCardWithFlagReplaceIfExitstL()
{
	CContactDatabase *db=CntTest->CreateDatabaseL();
	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-INC096705-0001 hecking  Replacing old contact with new one"));
	const TInt importOption1 = CContactDatabase::EDefault;
	const TInt importOption2 = CContactDatabase::EConverterReserved2;
	
	// 1) Empty database.
	EmptyDatabase();
	TInt count = db->CountL();
	TEST_CONDITION(count == 0);	

	// 2) Import a vcard containing company filed with CContactDatabase::EDefault flag.
	// and ensure company field gets imported
	test.Next(_L("Importing Contact with Company field"));
	TContactItemId aContactId = ImportVCardL( KVCardWithCompany, importOption1 );		
	CContactItem *item = db->ReadContactLC(aContactId);
	CContactItemFieldSet& fieldSet = item->CardFields();	
	TInt companyIndex = fieldSet.Find( KUidContactFieldCompanyName );
	TEST_CONDITION(companyIndex  != KErrNotFound );	
	const CContactItemField& field = (fieldSet)[companyIndex];
	TEST_CONDITION(field.Storage()->IsFull() != 0);
	CContactTextField* textfield = field.TextStorage();
	_LIT(KExpectedNumber,"XYZ ltd");
	TEST_CONDITION(textfield->Text()==KExpectedNumber);				
	CleanupStack::PopAndDestroy(item);	
	
	//3)Import a vcard without containing company filed with CContactDatabase::EDefault flag.
	// and ensure company field gets merged
	test.Next(_L("Importing Same Contact and merging the old contact without company field"));
	TContactItemId aContactId1 = ImportVCardL( KVCardWithoutCompany, importOption1 );
	
	//4) Ensure that company field should be there
	CContactItem *item1 = db->ReadContactLC(aContactId1);
	CContactItemFieldSet& fieldSet1 = item1->CardFields();
	TInt companyIndex1 = fieldSet1.Find( KUidContactFieldCompanyName );
	TEST_CONDITION(companyIndex1  != KErrNotFound );			
	const CContactItemField& field1 = (fieldSet1)[companyIndex1];
	TEST_CONDITION(field1.Storage()->IsFull() != 0);
	CContactTextField* textfield1 = field1.TextStorage();
	_LIT(KExpectedNumber1,"XYZ ltd");
	TEST_CONDITION(textfield1->Text()==KExpectedNumber1);			
	
	CleanupStack::PopAndDestroy(item1);
	
	//5) Import the same vcard without company field and using CContactVCardConverter::EReplaceIfExists option
	test.Next(_L("Importing Same Contact and replacing with the old contact without company field"));
	TContactItemId aContactId2 = ImportVCardL( KVCardWithoutCompany, importOption2 );	
	
	//6) Ensure that company field should not be there	
	CContactItem *item2 = db->ReadContactLC(aContactId2);
	CContactItemFieldSet& fieldSet2 = item2->CardFields();
	TInt companyIndex2 = fieldSet2.Find( KUidContactFieldCompanyName );		
	const CContactItemField& field2 = (fieldSet2)[companyIndex2];	
	TEST_CONDITION(field2.Storage()->IsFull() == 0);
	CleanupStack::PopAndDestroy(item2);
}

 /**
 @SYMTestCaseID PIM-T-CNTVCARD-INC110795-0001
 @SYMTestType UT
 @SYMTestPriority Medium
 @SYMDEF INC110795
 @SYMTestCaseDesc Check the use of import option with CContactVCardConverter::EReplaceIfExists flag
 behaves correctly with duplicate fields.
 
 @SYMTestActions
 1) Empty database.
 2) Import a vcard containing 2 Home Tel fields with EDefault flag.
 3) Check of all fields are imported
 4) Import the same vcard with 4 modified Home Tel fields  and using CContactVCardConverter::EReplaceIfExists option
 
 @SYMTestExpectedResults The vcard is replaced with the contact in the database when
 the CContactVCardConverter::EReplaceIfExists flag is used.
 */
 void ImportDuplicateFieldVCardWithReplaceIfExitstL()
 {
 
 	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-INC110795-0001 checking Replacing existing contact with new one (has duplicate fields) using EReplaceIfExists"));
 	
 	const TInt importOption1 = CContactDatabase::EDefault;
 	const TInt importOption2 = CContactDatabase::EDefault|
 								CContactDatabase::EImportSingleContact|
 								CContactDatabase::ENullTemplateId |
 								CContactVCardConverter::EReplaceIfExists;  
 
 								
 	CContactDatabase *db=CntTest->CreateDatabaseL();
 
 	// 1) Empty database.
 	EmptyDatabase();
 	TInt count = db->CountL();
 	test(count == 0);
 	
 	// 2) Import a vcard containing 2 Home Tel fields with CContactDatabase::EDefault flag.
 	test.Next(_L("Importing Contact with 2 duplicate Home Tel fields"));
 	TContactItemId contactId;
 	
 	RDesReadStream vcard(KVCardBeforeChange());
 	CleanupClosePushL(vcard);
 
 	CArrayPtr<CContactItem>* contactItems;
 	TBool success = EFalse;
 	contactItems = db->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, importOption1);
 	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,contactItems));
 	contactId = (*contactItems)[0]->Id();
 	CleanupStack::PopAndDestroy(contactItems);
 	CleanupStack::PopAndDestroy(&vcard);
 	
 	CContactItem *item = db->ReadContactLC(contactId);
 	CContactItemFieldSet& fieldSet = item->CardFields();	
 
 	// Ensure all fields get imported
 	test(FindInFields(fieldSet, TPtrC16(KGivenName())));
 	test(FindInFields(fieldSet, TPtrC16(KFamilyName())));
 	test(FindInFields(fieldSet, TPtrC16(KTelHome1())));
 	test(FindInFields(fieldSet, TPtrC16(KTelHome2())));
 	test(FindInFields(fieldSet, TPtrC16(KTelCell())));
 
 	CleanupStack::PopAndDestroy(item);	
 
 
 	//3) Import the same vcard with 4 modified Home tel fields and using 
 	//CContactVCardConverter::EReplaceIfExists option
 	test.Next(_L("Importing Same Contact and replacing the old contact with 4 modified Home Tel field using EReplaceIfExists"));
 
 	RDesReadStream vcard2(KVCardAfterChange());
 	CleanupClosePushL(vcard2);
 	
 	success = EFalse;
 	contactItems = db->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard2, success, importOption2);
 	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,contactItems));
 	contactId = (*contactItems)[0]->Id();
 	CleanupStack::PopAndDestroy(contactItems);
 	CleanupStack::PopAndDestroy(&vcard2);
 	
 	item = db->ReadContactLC(contactId);
 
 	CContactItemFieldSet& fieldSet2 = item->CardFields();
 
 	//4) Ensure that the all 4 Home TEL field have been modified 	
 	test(FindInFields(fieldSet2, TPtrC16(KGivenName())));
 	test(FindInFields(fieldSet2, TPtrC16(KFamilyName())));
 	test(FindInFields(fieldSet2, TPtrC16(KTelHome1Modified())));
 	test(FindInFields(fieldSet2, TPtrC16(KTelHome2Modified())));
 	test(FindInFields(fieldSet2, TPtrC16(KTelHome3Modified())));
 	test(FindInFields(fieldSet2, TPtrC16(KTelHome4Modified())));
 	
 	CleanupStack::PopAndDestroy(item);
 	}
   	
/**
Utility function to check if a specific line is present in a file
*/	
TBool CheckExportFilesL(const TDesC8& aString, const TDesC16& aFile)
	{
	RFile fileHandle;
	TBool patternFound = EFalse;
	fileHandle.Open(CntTest->Fs(), aFile, EFileRead|EFileStreamText);
	CleanupClosePushL(fileHandle);
	TBuf8<256> line;
	
	while(fileHandle.Read(line) == KErrNone && line.Length() != 0)
		{
		if (line.Find(aString) != KErrNotFound)
			{
		    patternFound = ETrue;
		    break;	
			}	
		}
	
	CleanupStack::PopAndDestroy(&fileHandle);
	return patternFound;
	}

/**
@SYMTestCaseID PIM-T-CNTVCARD-INC102410-0001
@SYMTestType UT
@SYMTestPriority Critical
@SYMTestCaseDesc Properties having multiple parameters should be imported properly.

@SYMTestActions
1.Import vCard having property with multiple parameter.
2.Open imported contact and check if parameter values have been added as fields.
3.Export imported contact and check if the property has been exported properly.

@SYMTestExpectedResults For the above tests:
1.vCard should be imported without losing data.
2.Necessary parameters should be added as fields in contact.
3.Exported file should contain all the parameters which were there while importing.
4.No leaves or panics should occur
*/	
void TestMultipleParamsInVCardPropertyL()
	{
	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-INC102410-0001 Test Import of property having multiple parameters"));
	CntTest->CreateDatabaseL();
	TContactItemId contactId;
	
	RDesReadStream vcard(KMultiParam());
	CleanupClosePushL(vcard);

	CArrayPtr<CContactItem>* contactItems;
	TBool success = EFalse;
	contactItems = CntTest->Db()->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, CContactDatabase::ETTFormat);
	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,contactItems));
	contactId = (*contactItems)[0]->Id();
	CleanupStack::PopAndDestroy(contactItems);
	CleanupStack::PopAndDestroy(&vcard);
	
	CContactItem* item = CntTest->Db()->ReadContactLC(contactId);
	
	CContactItemFieldSet& fields = item->CardFields();
	TInt fieldPos = fields.FindNext(KUidContactFieldVCardMapTEL, KContactFieldSetSearchAll);
	const CContentType& content = fields[fieldPos].ContentType();
	
	TBool result = 0;
	result = content.ContainsFieldType(KUidContactFieldVCardMapCELL);
	TEST_CONDITION(result > 0);
	
	result = content.ContainsFieldType(KUidContactFieldVCardMapVOICE);
	TEST_CONDITION(result > 0);
	
	result = content.ContainsFieldType(KUidContactFieldVCardMapHOME);
	TEST_CONDITION(result > 0);
	  
	CleanupStack::PopAndDestroy(item);	//destroying contact item
	
	CContactIdArray* idArray = CContactIdArray::NewL();
	CleanupStack::PushL(idArray);
	idArray->AddL(contactId);

	RFileWriteStream fileStream;
	User::LeaveIfError(fileStream.Replace(CntTest->Fs(), KOutputFileName, EFileWrite));
	CleanupClosePushL(fileStream);

	CntTest->Db()->ExportSelectedContactsL(TUid::Uid(KUidVCardConvDefaultImpl), *idArray, fileStream,0);
	fileStream.CommitL();
	CleanupStack::PopAndDestroy(&fileStream);
	
	result = CheckExportFilesL(KTelExport(), KOutputFileName());
	TEST_CONDITION(result > 0);
	
	
	CleanupStack::PopAndDestroy();	//destroying id array
	}
	
/**
@SYMTestCaseID PIM-T-CNTVCARD-PDEF103252-0001
@SYMTestType UT
@SYMTestPriority High
@SYMTestCaseDesc Contact item having a large binary field should be exported properly without any leaves.

@SYMTestActions
1.Import vCard having PHOTO propery with very large property value.
2.Export the imported contacts.
3.Import the exported contacts.
4.Compare the value of the PICTURE field present in the contact items resultin from two Import actions performed.

@SYMTestExpectedResults For the above tests:
1.vCard should be Exported without losing data or causing any low memory leaves.
2.The Imported PHOTO data should be same in both cases.
.No leaves or panics should occur
*/
void TestExportofContactHavingLargeBinaryFieldL()
	{
	test.Next(_L("@SYMTestCaseID:PIM-T-CNTVCARD-PDEF103252-0001 Test Export of contact having large binary field"));
	
	//	Empty the Database
	CntTest->CreateDatabaseL();
	CContactIdArray* idarray = CContactIdArray::NewL();
	CleanupStack::PushL(idarray);
	RFs fsSession;
	User::LeaveIfError(fsSession.Connect());
	RFileReadStream readStream;

 	//Import vcard with large PHOTO property
 	test.Printf(_L("Importing contacts from VCard"));
	User::LeaveIfError(readStream.Open(fsSession, KVCardLargePhoto, EFileRead));
 	CleanupClosePushL(readStream);
 	TBool success = EFalse;
 	CArrayPtr<CContactItem>* contactItems = CntTest->Db()->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl),
 	 	 readStream, success, CContactDatabase::ETTFormat);
 	 
	CleanupStack::PushL( TCleanupItem( CleanUpResetAndDestroy, contactItems ) );

	TEST_CONDITION(contactItems->Count() == 1) ;

 	//adding imported contact Id's to idarray for export
	idarray->AddL((*contactItems)[0]->Id());

	CleanupStack::PopAndDestroy(contactItems);
 	CleanupStack::PopAndDestroy(&readStream);

	test.Printf(_L("Exporting the Imported contacts, Testing it for both vCard2.1 and vCard3.0 export."));
	RFileWriteStream writeStream;
	TUid uid;

	//vCard3.0
	User::LeaveIfError(writeStream.Replace(fsSession,KVCardFile2, EFileWrite));
 	CleanupClosePushL(writeStream);
	uid.iUid = KUidPBAPVCardConvImpl;
	TInt64 filter = 0;
	filter |= 0x08;
	//This call should not leave with a Low memory Error
	CntTest->Db()->ExportSelectedContactsL(uid, *idarray, writeStream, CContactDatabase::EDefault, filter, NULL, EPBAPVCard30,ETrue);
 	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	
	//vCard2.1
	User::LeaveIfError(writeStream.Replace(fsSession, KVCardFile3, EFileWrite));
 	CleanupClosePushL(writeStream);
	uid.iUid = KUidVCardConvDefaultImpl;
	//This call should not leave with a Low memory Error
	CntTest->Db()->ExportSelectedContactsL(uid, *idarray, writeStream, CContactDatabase::EDefault);
 	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	CleanupStack::PopAndDestroy(idarray);
	
	test.Printf(_L("Export of large binary property completed successfully"));		

}

/**
@SYMTestCaseID PIM-T-CNTVCARD-PDEF140328-0001
@SYMTestType UT
@SYMTestPriority High
@SYMTestCaseDesc Partial vCard's should be processed without any panics

@SYMTestActions
1.Import a partial vCard.

@SYMTestExpectedResults For the above tests:
1.There should be no panics while import is happening.
*/
 void TestImportingPartialOrEmptyVCardsL()
{
    test.Next(_L("Test import of partial vCard"));
    CArrayPtr<CContactItem>* contactItems = NULL;
    RDesReadStream vcard(KPartialVCard());
    CleanupClosePushL(vcard);
    TInt success = EFalse;
    CContactDatabase* db = CntTest->CreateDatabaseL();
    contactItems = db->ImportContactsL(TUid::Uid(KUidVCardConvDefaultImpl), vcard, success, CContactDatabase::EImportSingleContact);
    CleanupStack::PushL( TCleanupItem( CleanUpResetAndDestroy, contactItems ) );
    CleanupStack::PopAndDestroy(contactItems);
    CleanupStack::PopAndDestroy(&vcard);
    test.Printf(_L("Import of partial vCard completed successfully"));
}


/**

@SYMTestCaseID     PIM-T-CNTVCARD-0001

*/

LOCAL_C void DoTestsL()
	{
	test.Start(_L("@SYMTestCaseID:PIM-T-CNTVCARD-0001 Preparing tests"));
	CTestRegister* TempFiles = CTestRegister::NewLC();
	TempFiles->RegisterL(KVCardFile1);
	TempFiles->RegisterL(KVCardFile2);
	TempFiles->RegisterL(KVCardFile3);
	TempFiles->RegisterL(KVCardUpdate2);
	TempFiles->RegisterL(KDatabaseFileName, EFileTypeCnt);
	TempFiles->RegisterL(KOutputFileName);
	TempFiles->RegisterL(KExpDelAFileName);
	TempFiles->RegisterL(KImpTypeVcardName);
	TempFiles->RegisterL(KImpNoTypeVcardName);	
	TempFiles->RegisterL(KVCardFile5);
	TempFiles->RegisterL(KVCardFile6);

	//without the fix this test will cause User 21 panic in versit 
	TestImportingPartialOrEmptyVCardsL();
	VCardEmailTestL();
	AccessCountTestsL();			
	DefaultVoiceParamTestsL();
	ExportContactTestL();
	ExportNamelessContactTestL();
#if defined(_DEBUG)
	//this test cannot be run in release mode due to UID mismatch
	//UpdateVCardTestL();
#endif			
	
 	ImportLargeVCardTestL() ;			

	DoTrailingSpaceImportAndExportTestL() ;
	VCardLineFeedOnlyTestL();
	VCardIgnoreUidTestL();
	CheckTypeSupport(KImpTypeVcardName);
	CheckTypeSupport(KImpNoTypeVcardName);	
	TestImportContactsL(KVCardFile5, KVCardFile6, KMasterVcard, KModifierVcard);
	// ImportExportVCardWithBinaryKeyL called with binary KEY value
	ImportExportVCardWithKeyL(KVCardBinaryKeyImportFileDes, KStorageTypeStore);
	// ImportExportVCardWithBinaryKeyL called with text KEY value
	ImportExportVCardWithKeyL(KVCardTextKeyImportFileDes, KStorageTypeText);
	ImportVCardWithFlagReplaceIfExitstL();
	ImportDuplicateFieldVCardWithReplaceIfExitstL();

	TestMultipleParamsInVCardPropertyL();
	TestExportofContactHavingLargeBinaryFieldL();

	CntTest->CloseDatabase();
	CleanupStack::PopAndDestroy(TempFiles);
	}


LOCAL_C void CleanupFilesL()
	{
    // delete the database file
	if (CContactDatabase::ContactDatabaseExistsL(KDatabaseFileName) )
		{
		CContactDatabase::DeleteDatabaseL(KDatabaseFileName);
		}
	}


LOCAL_C void TestCondition(TBool aCondition, TInt aLineNumber)
	{
	// if the test is about to fail, cleanup files first
	if (!aCondition)
		{
		TRAP_IGNORE(CleanupFilesL());
		}
	test.operator()(aCondition, aLineNumber);
	}


GLDEF_C TInt E32Main()
	{
    __UHEAP_MARK; 
    CntTest=new(ELeave) CCntTest();
	CntTest->ConstructL(test,KDatabaseFileName);
    TRAPD(err,DoTestsL());
    // ensure files are cleaned up even if DoTestsL() leaves
    TRAP_IGNORE(CleanupFilesL() ); 
    CntTest->EndTestLib(err);
    __UHEAP_MARKEND;	
	return KErrNone;
    }