diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/centralrepository/test/T_CenRepTrans.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/persistentstorage/centralrepository/test/T_CenRepTrans.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,705 @@ +// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "t_cenrep_helper.h" +#include +#include +#include "srvrepos_noc.h" +#include "srvres.h" +#include "cachemgr.h" +#include +#include "setting.h" + +//Forward declarations +class CTestObserver; + +/////////////////////////////////////////////////////////////////////////////////////// +// Globals + +static RTest TheTest(_L("T_CenRepTrans")); +const TUid KTestRepositoryId = {0x00000010}; +//CentralRepository server UID +static const TUid KCenRepServerUID = {0x10202BE9}; + +static CTestObserver* TheObserver = NULL; +static CServerRepository* TheServerRepository = NULL; + +static const TUint32 KIntSettingID = 843; +static const TInt KIntSettingVal = 25; + +static const TUint32 KRealSettingID = 844; +static const TReal KRealSettingVal = 8.76; +static const TReal KRealSettingVal2 = 19.234; + +static const TUint32 KStringSettingID = 845; +_LIT8(KStringSettingVal, "a test string"); +_LIT8(KStringSettingVal2, "another string"); +_LIT8(KEmptyString, ""); + +_LIT(KTestFile1, "C:\\PRIVATE\\10202BE9\\PERSISTS\\00000010.TXT"); +_LIT(KCreTestFile1, "C:\\PRIVATE\\10202BE9\\PERSISTS\\00000010.CRE"); +_LIT(KTmpTestFile1, "C:\\PRIVATE\\10202BE9\\PERSISTS\\00000010.TMP"); + + +/////////////////////////////////////////////////////////////////////////////////////// +// + +//Delete file function. +//Used by the test application to cleanup the C drive at the end of the test. +static void DeleteFile(const TDesC& aFullName) + { + RFs fsSession; + TInt err = fsSession.Connect(); + if(err == KErrNone) + { + TEntry entry; + if(fsSession.Entry(aFullName, entry) == KErrNone) + { + RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName); + err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly); + if(err != KErrNone) + { + RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName); + } + err = fsSession.Delete(aFullName); + if(err != KErrNone) + { + RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName); + } + } + fsSession.Close(); + } + else + { + RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); + } + } + +static void DeleteFiles() + { + ::DeleteFile(KTestFile1); + ::DeleteFile(KCreTestFile1); + ::DeleteFile(KTmpTestFile1); + } + +static TInt GetFileTimeStamp(const TDesC& aFullName, TTime& aTime) + { + RFs fsSession; + TInt err = fsSession.Connect(); + if(err == KErrNone) + { + TEntry entry; + if(fsSession.Entry(aFullName, entry) == KErrNone) + { + aTime=entry.iModified; + } + fsSession.Close(); + } + else + { + RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); + } + return err; + } + + +/////////////////////////////////////////////////////////////////////////////////////// +// Test macros and functions. + +static void Check(TInt aValue, TInt aLine) + { + if(!aValue) + { + ::DeleteFiles(); + TheTest(EFalse, aLine); + } + } +static void Check(TInt aValue, TInt aExpected, TInt aLine) + { + if(aValue != aExpected) + { + ::DeleteFiles(); + RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue); + TheTest(EFalse, aLine); + } + } +#define TEST(arg) ::Check((arg), __LINE__) +#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__) + +/////////////////////////////////////////////////////////////////////////////////////// + +static void CheckTmpFileDeleted() + { + RFs fsSession; + TInt err = fsSession.Connect(); + if(err == KErrNone) + { + // Allow time for file to be deleted + User::After(500*1000); + TEST2 (BaflUtils::FileExists (fsSession, KTmpTestFile1), EFalse); + fsSession.Close(); + } + else + { + RDebug::Print(_L("Error %d connecting file session.\n"), err); + } + } + + +/////////////////////////////////////////////////////////////////////////////////////// +// CTestObserver class + +//It is needed for the creation of CServerRepository object. +//Used also to track Get/Set/Create/Delete setting notifications, received +//from CServerRepository object. +class CTestObserver : public CBase, public MObserver + { +public: + CTestObserver(); + void Notify(TUint32 aId); + +public: + TBool iAssertNotification; + + }; + +CTestObserver::CTestObserver() : + iAssertNotification(EFalse) + { + } + +void CTestObserver::Notify(TUint32 aId) + { + RDebug::Print(_L("Notification! Id=%d.\n"), aId); + if(iAssertNotification) + { + TEST(0); + } + } + +/////////////////////////////////////////////////////////////////////////////////////// +// + +//Deletes the global TheObserver object. Sets it to NULL. +static void ReleaseObserver(TAny*) + { + delete TheObserver; + TheObserver = NULL; + } + +//Deletes the global TheServerRepository object. Sets it to NULL. +static void ReleaseRepository(TAny*) + { + TheServerRepository->Close(); + delete TheServerRepository; + TheServerRepository = NULL; + TServerResources::iCacheManager->DisableCache(ETrue); + TServerResources::iObserver->CloseiOpenRepositories(); + TServerResources::iCacheManager->EnableCache(); + } + +//This function is called from CreateSetting_OOMtestL(). +//It creates an integer, real or string setting in the repository +//with ID=KTestRepositoryId. All this is done in an OOM loop. +//The output from this test is: the repository file with ID = KTestRepositoryId +//will be copied from Z to C drive; +// An integer, real or string setting will be created in that repository. +//Changes in the repository will be stored on C drive (in the related repository file) +static void DoCreateSettingL(TBool aTrapping,TServerSetting::TType aSettingType) + { + TheObserver = new (ELeave) CTestObserver; + TCleanupItem clnItem1(&ReleaseObserver, NULL); + CleanupStack::PushL(clnItem1); + + TheServerRepository = new (ELeave) CServerRepository; + TCleanupItem clnItem2(&ReleaseRepository, NULL); + CleanupStack::PushL(clnItem2); + + // Turn OOM trapping on Repository Open on/off for testing purposes. + TServerResources::iObserver->iTrapOOMOnOpen=aTrapping; + // Open the repository + TheServerRepository->OpenL(KTestRepositoryId, *TheObserver); + + // All write operations, including CreateL must be done in a transaction + TheServerRepository->StartTransaction(CRepository::EConcurrentReadWriteTransaction); + TheServerRepository->CleanupCancelTransactionPushL(); + + //Create the appropriate setting based on aValueType + switch(aSettingType) + { + case TServerSetting::EInt: + { + // Create setting - fails if value already there (it must not be) + User::LeaveIfError(TheServerRepository->TransactionCreateL(KIntSettingID, KIntSettingVal, NULL)); + + // Try to get the value from the transaction cache + TInt val = 0; + User::LeaveIfError(TheServerRepository->Get(KIntSettingID, val)); + TEST2(val,KIntSettingVal); + break; + } + + case TServerSetting::EReal: + { + // Create setting - fails if value already there (it must not be) + User::LeaveIfError(TheServerRepository->TransactionCreateL(KRealSettingID, KRealSettingVal, NULL)); + + // Try to get the value from the transaction cache + TReal val = 0; + User::LeaveIfError(TheServerRepository->Get(KRealSettingID, val)); + TEST(val == KRealSettingVal); + break; + } + + case TServerSetting::EString: + { + // Create setting - fails if value already there (it must not be) + User::LeaveIfError(TheServerRepository->TransactionCreateL(KStringSettingID, KStringSettingVal, NULL)); + + // Try to get the value from the transaction cache + TBuf8<16> bufVal(KEmptyString); + User::LeaveIfError(TheServerRepository->Get(KStringSettingID, bufVal)); + TEST(bufVal == KStringSettingVal); + break; + } + default: + break; + } + + // Commit the changes + CleanupStack::Pop(); + TUint32 keyInfo; + User::LeaveIfError(TheServerRepository->CommitTransaction(keyInfo)); + + TServerResources::iObserver->iTrapOOMOnOpen=EFalse; + // Close the repository + TheServerRepository->Close(); + + CleanupStack::PopAndDestroy(2);//TheObserver & TheServerRepository + } + +//This function is called from SetSetting_OOMtest(). +//It expects that the previous test (CreateSetting_OOMtestL()) had completed successfully +//and there is a repository file (ID=0x10) on drive C and there is an integer setting +//in that repository (ID=KIntSettingID, Value=KIntSettingVal). +//The test will try to change the setting value in a transaction. Because the test is +//executed in an OOM loop, CommitChangesL() - will fail when called by SetL, leaving the +//repository content inconsistent - the in-memory repository content will be deleted +//(no settings), but in the file the setting will exist and its value will still be KIntSettingVal. +static void DoSetIntSettingL() + { + TEST(TheObserver != NULL); + TEST(TheServerRepository != NULL); + // Check if the setting is there - and has (or is restored to) the original value + TInt val = 0; + User::LeaveIfError(TheServerRepository->Get(KIntSettingID, val)); + TEST2(val,KIntSettingVal); + + // Change the setting value + // All write operations, including CreateL must be done in a transaction + TheServerRepository->StartTransaction(CRepository::EConcurrentReadWriteTransaction); + TheServerRepository->CleanupCancelTransactionPushL(); + // Change the setting value + User::LeaveIfError(TheServerRepository->TransactionSetL(KIntSettingID, KIntSettingVal + 1)); + + // Check if the setting is there - it should be in transaction cache now + User::LeaveIfError(TheServerRepository->Get(KIntSettingID, val)); + TEST2(val,(KIntSettingVal + 1)); + + // Commit the changes + CleanupStack::Pop(); + TUint32 keyInfo; + User::LeaveIfError(TheServerRepository->CommitTransaction(keyInfo)); + + // Check if the setting is there. The transaction was successful so the new value + // should be returned. + val = 0; + User::LeaveIfError(TheServerRepository->Get(KIntSettingID, val)); + TEST2(val,(KIntSettingVal + 1)); + } + +//This function is called from SetSetting_OOMtest(). +//It expects that the previous test (CreateSetting_OOMtestL()) had completed successfully +//and there is a repository file (ID=0x10) on drive C and there is an rea; setting +//in that repository (ID=KRealSettingID, Value=KRealSettingVal). +//The test will try to change the setting value in a transaction. Because the test is +//executed in an OOM loop, CommitChangesL() - will fail when called by SetL, leaving the +//repository content inconsistent - the in-memory repository content will be deleted +//(no settings), but in the file the setting will exist and its value will still be KRealSettingVal. +static void DoSetRealSettingL() + { + TEST(TheObserver != NULL); + TEST(TheServerRepository != NULL); + // Check if the setting is there - and has (or is restored to) the original value + TReal val = 0; + User::LeaveIfError(TheServerRepository->Get(KRealSettingID, val)); + TEST(val == KRealSettingVal); + + // Change the setting value + // All write operations, including CreateL must be done in a transaction + TheServerRepository->StartTransaction(CRepository::EConcurrentReadWriteTransaction); + TheServerRepository->CleanupCancelTransactionPushL(); + // Change the setting value + User::LeaveIfError(TheServerRepository->TransactionSetL(KRealSettingID, KRealSettingVal2)); + + // Check if the setting is there - it should be in transaction cache now + User::LeaveIfError(TheServerRepository->Get(KRealSettingID, val)); + TEST(val == KRealSettingVal2); + + // Commit the changes + CleanupStack::Pop(); + TUint32 keyInfo; + User::LeaveIfError(TheServerRepository->CommitTransaction(keyInfo)); + + // Check if the setting is there. The transaction was successful so the new value + // should be returned. + val = 0; + User::LeaveIfError(TheServerRepository->Get(KRealSettingID, val)); + TEST(val == KRealSettingVal2); + } + +//This function is called from SetSetting_OOMtest(). +//It expects that the previous test (CreateSetting_OOMtestL()) had completed successfully +//and there is a repository file (ID=0x10) on drive C and there is an string setting +//in that repository (ID=KStringSettingID, Value=KStringSettingVal). +//The test will try to change the setting value in a transaction. Because the test is +//executed in an OOM loop, CommitChangesL() - will fail when called by SetL, leaving the +//repository content inconsistent - the in-memory repository content will be deleted +//(no settings), but in the file the setting will exist and its value will still +//be KStringSettingVal. +static void DoSetStringSettingL() + { + TEST(TheObserver != NULL); + TEST(TheServerRepository != NULL); + + // Check if the setting is there - and has (or is restored to) the original value + TBuf8<16> bufVal(KEmptyString); + User::LeaveIfError(TheServerRepository->Get(KStringSettingID, bufVal)); + TEST(bufVal == KStringSettingVal); + + // Change the setting value + // All write operations, including CreateL must be done in a transaction + TheServerRepository->StartTransaction(CRepository::EConcurrentReadWriteTransaction); + TheServerRepository->CleanupCancelTransactionPushL(); + + // Change the setting value + User::LeaveIfError(TheServerRepository->TransactionSetL(KStringSettingID, KStringSettingVal2)); + + // Check if the setting is there - it should be in transaction cache now + User::LeaveIfError(TheServerRepository->Get(KStringSettingID, bufVal)); + TEST(bufVal == KStringSettingVal2); + // Commit the changes + CleanupStack::Pop(); + TUint32 keyInfo; + User::LeaveIfError(TheServerRepository->CommitTransaction(keyInfo)); + + // Check if the setting is there. The transaction was successful so the new value + // should be returned. + bufVal = KEmptyString; + User::LeaveIfError(TheServerRepository->Get(KStringSettingID, bufVal)); + TEST(bufVal == KStringSettingVal2); + } + +//This function is called from SetSetting_OOMtest()in the case where previous +//DoSetSettingL() call failed with KErrNoMemory, leaving the repository object +//in inconsisten state - the in-memory presentation of the repository differs from +//the repository file content. The function will try to repair the repository, +//as it would have to happen in a real situation. Then it will check that +//the repository content is consistent, which means the tested setting value should +//be the same as it was before DoSetSettingL() call. +static void DoRecoverRepositoryL(TServerSetting::TType aSettingType) + { + //Repair the repository as it happens in a real situation + //AccessL calls RestoreConsistencyL() indirectly + //RestoreConsistencyL() is called in the production code from the session object, + //ServiceL() implementation. So, it will be called before any other repository call + //and it should restore the repository content - it should repair the repository + //consitency. + TServerResources::iObserver->AccessL(KTestRepositoryId); + //Check if the setting is there - the old, pre-transactional value should be + //in-memory now. + switch(aSettingType) + { + case TServerSetting::EInt: + { + TInt val = 0; + User::LeaveIfError(TheServerRepository->Get(KIntSettingID, val)); + TEST2(val,KIntSettingVal); + break; + } + + case TServerSetting::EReal: + { + TReal val = 0; + User::LeaveIfError(TheServerRepository->Get(KRealSettingID, val)); + TEST(val == KRealSettingVal); + break; + } + + case TServerSetting::EString: + { + TBuf8<16> bufVal(KEmptyString); + User::LeaveIfError(TheServerRepository->Get(KStringSettingID, bufVal)); + TEST(bufVal == KStringSettingVal); + break; + } + default: + break; + } + } + +//Inits TServerResources object. +//It should be called once, before the creation of CServerRepository object. +static void InitEnvL() + { + TServerResources::InitialiseL(); + + delete TServerResources::iDataDirectory; TServerResources::iDataDirectory = NULL; + delete TServerResources::iRomDirectory; TServerResources::iRomDirectory = NULL; + delete TServerResources::iInstallDirectory; TServerResources::iInstallDirectory = NULL; + + TServerResources::iDataDirectory = HBufC::NewL(KMaxFileName); + TServerResources::iRomDirectory = HBufC::NewL(KMaxFileName); + TServerResources::iInstallDirectory = HBufC::NewL(KMaxFileName); + + TBuf<32> uidName; + uidName.Format(_L("%08X"), KCenRepServerUID.iUid); + + TPtr ptr1(TServerResources::iDataDirectory->Des()); + ptr1.Append(_L("C:\\PRIVATE\\")); + ptr1.Append(uidName); + ptr1.Append(_L("\\PERSISTS\\")); + + TPtr ptr2(TServerResources::iRomDirectory->Des()); + ptr2.Append(_L("Z:\\PRIVATE\\")); + ptr2.Append(uidName); + ptr2.Append(_L("\\")); + + TPtr ptr3(TServerResources::iInstallDirectory->Des()); + ptr3.Append(_L("C:\\PRIVATE\\")); + ptr3.Append(uidName); + ptr3.Append(_L("\\")); + } + +//Destroys TServerResources object. +//It should be called after the CServerRepository object was destroyed. +static void ReleaseEnv() + { + TServerResources::Close(); + } + +//Creates global TheServerRepository and TheObserver objects +//It is used in SetSetting_OOMtest() +static void InitEnv2L() + { + TheObserver = new CTestObserver; + TEST(TheObserver != NULL); + + TheServerRepository = new CServerRepository; + TEST(TheServerRepository != NULL); + + TheServerRepository->OpenL(KTestRepositoryId, *TheObserver); + } + +//Destroys global TheServerRepository and TheObserver objects +//It is used in SetSetting_OOMtest() +static void ReleaseEnv2() + { + ::ReleaseRepository(NULL); + ::ReleaseObserver(NULL); + } + +//Central repository test: it creates a setting in OOM conditions. +static void CreateSetting_OOMtestL(TBool aTrapping,TServerSetting::TType aSettingType) + { + TInt err; + TRAP(err, ::InitEnvL()); + TEST2(err, KErrNone); + for(TInt count=1;;++count) + { + CleanupCDriveL(); + __UHEAP_FAILNEXT(count); + __UHEAP_MARK; + + TRAP(err, ::DoCreateSettingL(aTrapping,aSettingType)); + + TServerResources::iOwnerIdLookUpTable.Reset(); + TServerResources::iObserver->Reset(); + + __UHEAP_MARKEND; + + if(err == KErrNone) + { + RDebug::Print(_L("The test succeeded at heap failure rate=%d.\n"), count); + break; + } + else if(err != KErrNoMemory) + { + TEST2(err, KErrNone); + } + } + + ::ReleaseEnv(); + __UHEAP_RESET; + } + +//Central repository test: it sets new setting value in OOM conditions. +//Then ComitChangesL() is called to store the new setting's value in the repository +//file. Because of the simulated OOM conditions ComitChangesL() can fail, leaving the +//repository in inconsistent state. +//DoRecoverRepositoryL() is called to repair the repository and restore the consistency. +static void SetSetting_OOMtest(TServerSetting::TType aSettingType) + { + TTime before; + TTime after; + // Check that cre file exists and get modification time + TEST2 (GetFileTimeStamp(KCreTestFile1, before), KErrNone); + after=before; + + TInt err; + TRAP(err, ::InitEnvL()); + TEST2(err, KErrNone); + TRAP(err, ::InitEnv2L()); + TEST2(err, KErrNone); + for(TInt count=1;;++count) + { + __UHEAP_FAILNEXT(count); + __UHEAP_MARK; + + switch(aSettingType) + { + case TServerSetting::EInt: + { + TRAP(err, ::DoSetIntSettingL()); + break; + } + case TServerSetting::EReal: + { + TRAP(err, ::DoSetRealSettingL()); + break; + } + case TServerSetting::EString: + { + TRAP(err, ::DoSetStringSettingL()); + break; + } + default: + break; + } + + // Get timestamp of cre file. It is now possible for the commit to + // fail with KErrNoMemory after a successful write. If filestamp + // changes, the cre file has been written and the test is complete + TEST2 (GetFileTimeStamp(KCreTestFile1, after), KErrNone); + if(before!=after) + { + err=KErrNone; + } + + if(err == KErrNoMemory) + { + __UHEAP_MARKEND; + //DoSetSettingL() call failed (so the transaction). Try to recover. + TInt err2; + TRAP(err2, ::DoRecoverRepositoryL(aSettingType)); + TEST2(err2, KErrNone); + } + else if(err == KErrNone) + { + //The heap cannot be checked at this point because memory may have been + //allocated which must be freed by a call to ReleaseEnv2. + //It is checked outside the loop instead + RDebug::Print(_L("The test succeeded at heap failure rate=%d.\n"), count); + break; + } + else + { + __UHEAP_MARKEND; + TEST2(err, KErrNone); + } + // Check that tmp file generated by attempted commit is deleted + // by RestoreConsistencyL() in DoRecoverRepositoryL(). + ::CheckTmpFileDeleted(); + } + // Check that no tmp file is left over + ::ReleaseEnv2(); + ::ReleaseEnv(); + //This __UHEAP_MARKEND checks the heap after the call to DoSetxSettings + //succeeds as calls to CommitTRansactions can allocate memory which must be freed + //by calling ReleaseEnv2 + __UHEAP_MARKEND; + __UHEAP_RESET; + } + +/////////////////////////////////////////////////////////////////////////////////////// + +void RunTestsL(TServerSetting::TType aSettingType) + { + + TheTest.Next(_L("Create setting - OOM test - OOM Trapping off")); + ::CreateSetting_OOMtestL(EFalse,aSettingType); + + TheTest.Next(_L("Create setting - OOM test - OOM Trapping on")); + ::CreateSetting_OOMtestL(ETrue,aSettingType); + + TheTest.Next(_L("Set setting - transaction - OOM test")); + ::SetSetting_OOMtest(aSettingType); + + } + +static void MainL() + { + // create and install the active scheduler we need for the cache manager in TServerResources::InitialiseL + CActiveScheduler* cacheManagerAS=new(ELeave) CActiveScheduler; + CleanupStack::PushL(cacheManagerAS); + CActiveScheduler::Install(cacheManagerAS); + + ::DeleteFiles(); + + TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-CENTRALREPOSITORY-LEGACY-T_CENREPTRANS-0001 Int Setting Tests ")); + RunTestsL(TServerSetting::EInt); + + TheTest.Next(_L("Real Setting Tests")); + RunTestsL(TServerSetting::EReal); + + TheTest.Next(_L("String Setting Tests")); + RunTestsL(TServerSetting::EString); + + TheTest.End(); + TheTest.Close(); + + CleanupStack::PopAndDestroy(cacheManagerAS); + } + +TInt E32Main() + { + __UHEAP_MARK; + + CTrapCleanup* tc = CTrapCleanup::New(); + TEST(tc != NULL); + + TheTest.Title(); + TRAPD(err, ::MainL()); + TEST2(err, KErrNone); + + ::DeleteFiles(); + + delete tc; + + __UHEAP_MARKEND; + + User::Heap().Check(); + return KErrNone; + }