phonebookengines/contactsmodel/tsrc/Integration/PerfFuncSuite/src/StateMachineStep.cpp
/*
* Copyright (c) 2006-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:
*
*/
/**
@file
@publishedAll
@released
*/
#include "statemachinestep.h"
#include "performancefunctionalitydefs.h"
#include <cntfilt.h>
const static TInt KNumberOfContacts = 10;
_LIT(KTestCase1,"OpeningRecovery");
_LIT(KTestCase2,"OpeningAsync");
_LIT(KTestCase3,"Writable");
_LIT(KTestCase4,"DefaultRecovery");
_LIT(KTestCase5,"DefaultCompress");
_LIT(KTestCase6,"AsyncInteg");
_LIT(KTestCase7,"TablesClosed");
_LIT(KTestCase8,"WritableSpeed");
_LIT(KTestCasePrint1, "Opening State statemachine Recovery tests");
_LIT(KTestCasePrint2, "Opening State statemachine Asynchronous open tests");
_LIT(KTestCasePrint3, "Writable State statemachine tests");
_LIT(KTestCasePrint4, "Default State recovery statemachine tests");
_LIT(KTestCasePrint5, "Default State compression statemachine tests");
_LIT(KTestCasePrint6, "Asynchronous Integrity tests");
_LIT(KTestCasePrint7, "Tables Closed State statemachine tests");
_LIT(KTestCasePrint8, "Writable State statemachine speed dial tests");
/**
* This class tests the internal implementation of the contact model server
* specifically it is designed to stress test the state machine
* so that all the major functionality of the state machine is exercised
* each test function usually encompasses one test case, the description for
* the test case is defined in the related test script file (StateMachineStep.script)
* each test step in the test script file usually call this test class with a non
* existent ini file section name, the section name correspond to the test case
* name stored within this class. The reasoning behind this is that each test case
* does not need to store any additional data within the ini file and as such a reference
* to an actual ini file section would be redundent.
*/
CStateMachineStep::CStateMachineStep()
: CPerformanceFunctionalityBase( KNumberOfContacts )
{
SetTestStepName(KStateMachineStep);
}
TVerdict CStateMachineStep::doTestStepPostambleL()
{
return CPerformanceFunctionalityBase::doTestStepPostambleL();
}
void CStateMachineStep::InitializeL()
{
CPerformanceFunctionalityBase::InitializeL();
//set timeout to 0, to minimize waits within the test
iContactsDatabase->SetOperationTimeOutL(0);
}
void CStateMachineStep::Cleanup()
{
//unload async opens
CLEAR( iContactOpenOperation1 );
CLEAR( iContactOpenOperation2 );
CPerformanceFunctionalityBase::Cleanup();
}
void CStateMachineStep::PreTestL()
{
//does nothing
}
TVerdict CStateMachineStep::doTestStepL()
{
__UHEAP_MARK;
InitializeL();
_LIT(KPrintStart, "CStateMachineStep::doTestStepL()");
INFO_PRINTF1(KPrintStart); //Block start
iIterate->Reset();
const TDesC &KSection = ConfigSection();
if( KSection == KTestCase1 )
{
INFO_PRINTF1( KTestCasePrint1 );
OpeningStateRecoverTestsL();
}
else if( KSection == KTestCase2 )
{
INFO_PRINTF1( KTestCasePrint2 );
OpeningStateAsyncOpenTestsL();
}
else if( KSection == KTestCase3 )
{
INFO_PRINTF1( KTestCasePrint3 );
WritableStateTestsL();
}
else if( KSection == KTestCase4 )
{
INFO_PRINTF1( KTestCasePrint4 );
DefaultStateCompressionTestsL();
}
else if( KSection == KTestCase5 )
{
INFO_PRINTF1( KTestCasePrint5 );
DefaultStateRecoveryTestsL();
}
else if( KSection == KTestCase6 )
{
INFO_PRINTF1( KTestCasePrint6 );
AsynchronousIntegrityTestL();
}
else if( KSection == KTestCase7 )
{
INFO_PRINTF1( KTestCasePrint7 );
TablesClosedStateTestsL();
}
else if( KSection == KTestCase8 )
{
INFO_PRINTF1( KTestCasePrint8 );
WritableStateSpeedDialTestsL();
}
else
{
MissingTestPanic();
}
Cleanup();
__UHEAP_MARKEND;
return TestStepResult();
}
void CStateMachineStep::OpeningStateRecoverTestsL()
{
TContactItemId con1 = iIterate->NextL();
CContactDatabase *database = CContactDatabase::OpenL();
iCompleteSteps = ETrue;
TInt error = KErrNone;
//transition to opening state
TRAP( error, StartRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//open tables while in opening state
TRAP( error, database->OpenTablesL() );
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#endif
//transition out of opening state
TRAP( error, EndRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
CLEAR( database );
iCompleteSteps = ETrue;
//transition to opening state
TRAP( error, StartRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//transition out of opening state, completing all recovery steps
TRAP( error, EndRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
iCompleteSteps = EFalse;
//transition to opening state
TRAP( error, StartRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//transition out of opening state, cancelling recovery
TRAP( error, EndRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
iCompleteSteps = EFalse;
//transition to opening state
TRAP( error, StartRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//can't access contacts as database is in recovery state
TRAP( error,
ReadL(con1);
CloseL( EFalse );
);
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
TRAP( error,
OpenL(con1);
CloseL( ETrue );
);
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
//transition out of opening state, cancelling recovery
TRAP( error, EndRecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
//after recovery is complete it should be possible to access contact normally
TRAP( error,
ReadL(con1);
CloseL( EFalse );
);
INTCOMPARE( KErrNone, ==, error, 0, 0 );
TRAP( error,
OpenL(con1);
CloseL( ETrue );
);
INTCOMPARE( KErrNone, ==, error, 0, 0 );
}
void CStateMachineStep::OpeningStateAsyncOpenTestsL()
{
CContactDatabase *database1 = NULL;
CContactDatabase *database2 = NULL;
CLEAR( iContactsDatabase );
WaitForServerToExitL();
//open databases asynchronously *2
iContactOpenOperation1 = CContactDatabase::Open(iStatus1);
iContactOpenOperation2 = CContactDatabase::Open(iStatus2);
//cancel second async open first
CLEAR( iContactOpenOperation2 );
CLEAR( iContactOpenOperation1 );
//check that async opens were cancelled
if( iStatus1.Int() != KErrNone )
{
INTCOMPARE( KErrCancel, ==, iStatus1.Int(), 0, 0 );
}
if( iStatus2.Int() != KErrNone )
{
INTCOMPARE( KErrCancel, ==, iStatus2.Int(), 0, 0 );
}
//open databases asynchronously *2
iContactOpenOperation1 = CContactDatabase::Open(iStatus1);
iContactOpenOperation2 = CContactDatabase::Open(iStatus2);
//cancel first async open
CLEAR( iContactOpenOperation1 );
User::WaitForRequest(iStatus2);
//get opened database sessions
database1 = iContactOpenOperation2->TakeDatabase();
//check that databases have been opened
INTCOMPARE( reinterpret_cast<TInt>(database1), !=, NULL, 0, 0 );
//check that first async open was cancelled
INTCOMPARE( KErrCancel, ==, iStatus1.Int(), 0, 0 );
//check that second async opened was successful
INTCOMPARE( KErrNone, ==, iStatus2.Int(), 0, 0 );
//unload opened database sessions
CLEAR( database1 );
CLEAR( iContactOpenOperation2 );
WaitForServerToExitL();
//open databases asynchronously *2
iContactOpenOperation1 = CContactDatabase::Open(iStatus1);
iContactOpenOperation2 = CContactDatabase::Open(iStatus2);
//wait for async opens to complete
User::WaitForRequest(iStatus1);
User::WaitForRequest(iStatus2);
//get opened database sessions
database1 = iContactOpenOperation1->TakeDatabase();
database2 = iContactOpenOperation2->TakeDatabase();
//check that databases have been opened
INTCOMPARE( reinterpret_cast<TInt>(database1), !=, NULL, 0, 0 );
INTCOMPARE( reinterpret_cast<TInt>(database2), !=, NULL, 0, 0 );
//unload async opens
CLEAR( iContactOpenOperation1 );
CLEAR( iContactOpenOperation2 );
//unload opened database sessions
CLEAR( database1 );
CLEAR( database2 );
//check that async opens completed successfully
INTCOMPARE( KErrNone, ==, iStatus1.Int(), 0, 0 );
INTCOMPARE( KErrNone, ==, iStatus2.Int(), 0, 0 );
iContactsDatabase = CContactDatabase::OpenL();
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
iContactsDatabase->DamageDatabaseL(0x666);
#endif
CLEAR( iContactsDatabase );
WaitForServerToExitL();
iActiveRecoverObserver.iCompressOrRecoverError = KErrNone;
//open databases asynchronously
iContactOpenOperation1 = CContactDatabase::Open(iStatus1);
//get partially opened database session
database1= iContactOpenOperation1->TakeDatabase();
//start async recovery
CContactActiveRecover* recover = database1->CreateRecoverLC();
recover->SetObserver( &iActiveRecoverObserver );
//complete async recovery
while(recover->Step()){}
CleanupStack::PopAndDestroy( recover );
recover = NULL;
//unload async opens
CLEAR( iContactOpenOperation1 );
//unload opened database sessions
CLEAR( database1 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
iContactsDatabase = CContactDatabase::OpenL();
}
void CStateMachineStep::WritableStateTestsL()
{
const TContactItemId KContactId = iIterate->NextL();
const TContactItemId KContactId2 = iIterate->NextL();
TInt error = KErrNone;
//set contact as own card
OpenL( KContactId );
TRAP( error, iContactsDatabase->SetOwnCardL( *iContactItem ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CommitL( ETrue );
//check contact is set as own card
INTCOMPARE( iContactsDatabase->OwnCardId(), ==, KContactId, 0, 0 );
//set contact as own card again, to test internal functionality of state machine
OpenL( KContactId );
TRAP( error, iContactsDatabase->SetOwnCardL( *iContactItem ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CommitL( ETrue );
//contact is still marked as own card
INTCOMPARE( iContactsDatabase->OwnCardId(), ==, KContactId, 0, 0 );
//read contact
ReadL( KContactId2 );
//set contact as own card using read contact
TRAP( error, iContactsDatabase->SetOwnCardL( *iContactItem ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CloseL( EFalse );
//check that contact is set as own card
INTCOMPARE( iContactsDatabase->OwnCardId(), ==, KContactId2, 0, 0 );
//Perform recovery while in writeable state and database not damaged
TRAP( error,
//start async recovery
CContactActiveRecover* recover = iContactsDatabase->CreateRecoverLC();
recover->SetObserver( &iActiveRecoverObserver );
//complete async recovery
while(recover->Step()){}
CleanupStack::PopAndDestroy( recover );
recover = NULL;
);
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
TRAP( error, iContactsDatabase->RecoverL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
}
void CStateMachineStep::WritableStateSpeedDialTestsL()
{
_LIT(KFieldValue, "FieldValue");
const TContactItemId KContactId = iIterate->NextL();
TInt error = KErrNone;
iActiveRecoverObserver.iCompressOrRecoverError = KErrNone;
OpenL( KContactId );
const TInt KFieldCount = iFields->Count();
CloseL( ETrue );
OpenL( KContactId );
//try setting an empty field as a SpeedDial
//no error should ensue
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*iContactItem, 1, 1) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//write some test data into all fields of the contact item
//parameter is used only for Text and Store type fields - others are created on the fly
SetAllFieldsL(KFieldValue);
CommitL( EFalse );
for( TInt i = 0; i < KFieldCount; ++i)
{
OpenL( KContactId );
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*iContactItem, i, 1) );
//if field is text, set speed dial should be successful
if( (*iFields)[i].StorageType() == KStorageTypeText)
{
CloseL( ETrue );
INTCOMPARE( KErrNone, ==, error, i, 0 );
ReadL( KContactId );
TESTPRINTI( (*iFields)[i].IsSpeedDial(), i );
CloseL( EFalse );
}
//if field not text, set speed dial should fail
else
{
CloseL( ETrue );
INTCOMPARE( KErrArgument, ==, error, i, 0 );
ReadL( KContactId );
TESTPRINTI( !(*iFields)[i].IsSpeedDial(), i );
CloseL( EFalse );
}
}
ReadL( KContactId );
//set speed dial for unopened/read contact
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*iContactItem, 1, 1) );
CloseL( EFalse );
//set speed dial should fail
INTCOMPARE( KErrAccessDenied, ==, error, 0, 0 );
ReadL( KContactId );
TESTPRINT( !(*iFields)[1].IsSpeedDial() );
CloseL( EFalse );
//set speed dial for system template - expect error here
CContactCard* contact = CContactCard::NewLC( iTemplate );
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*contact, 1, 1) );
INTCOMPARE( KErrArgument, ==, error, 0, 0 );
CleanupStack::PopAndDestroy(contact);
//set speed dial for contact with empty field set - expect error here too
contact = CContactCard::NewLC();
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*contact, 1, 1) );
INTCOMPARE( KErrArgument, ==, error, 0, 0 );
CleanupStack::PopAndDestroy(contact);
}
/**
Perform operations that are not supported by current state, so that default state is used
all operations should be unsuccessfull
*/
void CStateMachineStep::DefaultStateCompressionTestsL()
{
const TContactItemId KContactId = iIterate->NextL();
TInt error = KErrNone;
OpenL( KContactId );
//transition into compress state
StartCompressL();
//perform unsupported operations
TRAP( error, iContactsDatabase->CloseTables() );//non leaving function
INTCOMPARE( KErrNone, ==, error, 0, 0 );
TRAP( error, iContactsDatabase->OpenTablesL() );
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
TRAP( error, iContactsDatabase->RecoverL() );
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
// only do this with the DBMS version of the contacts model as DamageDatabaseL
// doesn't do anything in the SQLite version and so the test will always fail.
TRAP( error, iContactsDatabase->DamageDatabaseL(0x666); );
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#endif
TRAP( error,
CContactActiveRecover* recover = iContactsDatabase->CreateRecoverLC();
CleanupStack::PopAndDestroy(recover);
);
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
TRAP( error, iContactsDatabase->SetOwnCardL( *iContactItem ) );
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
TRAP( error, iContactsDatabase->SetFieldAsSpeedDialL(*iContactItem, 1, 1) );
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrInUse, ==, error, 0, 0 );
#endif
EndCompressL();
//end compress state
CloseL( ETrue );
}
/**
Perform operations that are not supported by current state, so that default state is used
all operations should be unsuccessfull
*/
void CStateMachineStep::DefaultStateRecoveryTestsL()
{
TInt error = KErrNone;
//transition into opening state
StartRecoverL();
//perform usupported operations
TRAP( error, iContactsDatabase->CompactL() );
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
TRAP( error,
CContactActiveCompress* compress = iContactsDatabase->CreateCompressorLC();
CleanupStack::PopAndDestroy( compress );
);
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
EndRecoverL();
//end opening stat
}
void CStateMachineStep::AsynchronousIntegrityTestL()
{
const TContactItemId KContactId = iIterate->NextL();
TInt error = KErrNone;
//test that it is possible to create multiple active compressor
iActiveRecoverObserver.iCompressOrRecoverError = KErrNone;
CContactActiveCompress* compress1 = iContactsDatabase->CreateCompressorLC();
compress1->SetObserver( &iActiveRecoverObserver );
CContactActiveCompress* compress2 = iContactsDatabase->CreateCompressorLC();
compress2->SetObserver( &iActiveRecoverObserver );
while(compress1->Step()){}
while(compress2->Step()){}
CleanupStack::PopAndDestroy( compress2 );
CleanupStack::PopAndDestroy( compress1 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
//test that it is possible to create multiple active recoverers
iActiveRecoverObserver.iCompressOrRecoverError = KErrNone;
CContactActiveRecover* recover1 = iContactsDatabase->CreateRecoverLC();
recover1->SetObserver( &iActiveRecoverObserver );
CContactActiveRecover* recover2 = iContactsDatabase->CreateRecoverLC();
recover2->SetObserver( &iActiveRecoverObserver );
while(recover1->Step()){}
while(recover2->Step()){}
CleanupStack::PopAndDestroy( recover2 );
CleanupStack::PopAndDestroy( recover1 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
//transition into compress state
iCompleteSteps = EFalse;
StartCompressL();
TRAP( error,
CContactActiveRecover* recover = iContactsDatabase->CreateRecoverLC();
CleanupStack::PopAndDestroy( recover );
);
#ifdef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#endif
EndCompressL();
//end compress state
//test that we have transitioned out of compress state
TRAP( error, ReadL( KContactId ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CloseL( EFalse );
iCompleteSteps = EFalse;
//transition into opening state
StartRecoverL();
TRAP( error,
CContactActiveCompress* compress = iContactsDatabase->CreateCompressorLC();
CleanupStack::PopAndDestroy( compress );
);
#ifdef _DEBUG
#ifndef __SYMBIAN_CNTMODEL_USE_SQLITE__
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
#else
INTCOMPARE( KErrNone, ==, error, 0, 0 );
#endif
EndRecoverL();
//transition out of opening state
//test that we have transitioned out of compress state
TRAP( error, ReadL( KContactId ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CloseL( EFalse );
}
void CStateMachineStep::TablesClosedStateTestsL()
{
const TContactItemId KContactId = iIterate->NextL();
TInt error = KErrNone;
iActiveRecoverObserver.iCompressOrRecoverError = KErrNone;
//transition to tables closed state
iContactsDatabase->CloseTables();
TRAP( error,
CContactActiveCompress* compress = iContactsDatabase->CreateCompressorLC();
compress->SetObserver( &iActiveRecoverObserver );
CleanupStack::PopAndDestroy( compress );//dont complete all steps
);
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
//test that we are still in tables closed state
TRAP( error, ReadL( KContactId ) );
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
CloseL( EFalse );
TRAP( error,
CContactActiveCompress* compress = iContactsDatabase->CreateCompressorLC();
compress->SetObserver( &iActiveRecoverObserver );
while(compress->Step()){}//complete all steps
CleanupStack::PopAndDestroy( compress );
);
INTCOMPARE( KErrNone, ==, error, 0, 0 );
INTCOMPARE( KErrNone, ==, iActiveRecoverObserver.iCompressOrRecoverError, 0, 0 );
//test that we are still in tables closed state
TRAP( error, ReadL( KContactId ) );
INTCOMPARE( KErrNotReady, ==, error, 0, 0 );
CloseL( EFalse );
//transition to writeable state
CContactDatabase *database = NULL;
TRAP( error, database = CContactDatabase::OpenL() );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
//test that we have transitioned out of tables closed state
TRAP( error, ReadL( KContactId ) );
INTCOMPARE( KErrNone, ==, error, 0, 0 );
CloseL( EFalse );
CLEAR( database );
}