sl@0: // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Testing new RDbs methods, which handle "Out of disk space" situations. sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: ///////////////////////////////////////////////////////////////// sl@0: //Globals sl@0: sl@0: //If you change KTestDrive, don't forget to change KTestDatabase too!!! sl@0: sl@0: #if defined __WINSCW__ || defined __WINS__ sl@0: sl@0: //The C: drive may be too big and may be used concurently by other applications. sl@0: //The T: drive is more suitable for the test if running on the emulator sl@0: const TInt KTestDrive = EDriveT; sl@0: _LIT( KTestDatabase, "T:\\DBMS-TST\\T_DbmsOOD.DB"); sl@0: sl@0: #elif defined __X86GCC__ sl@0: sl@0: const TInt KTestDrive = EDriveG; sl@0: _LIT( KTestDatabase, "G:\\DBMS-TST\\T_DbmsOOD.DB"); sl@0: sl@0: #else sl@0: sl@0: const TInt KTestDrive = EDriveE; sl@0: _LIT( KTestDatabase, "E:\\DBMS-TST\\T_DbmsOOD.DB"); sl@0: sl@0: #endif sl@0: sl@0: const TInt KReservedSpaceSize = 0; //The aSpace parameter of RDbs::ReserveDriveSpace() sl@0: //is not used at the moment and shall be set to 0. sl@0: sl@0: static RTest TheTest(_L("t_dbood - \"Out of Disk space\" test")); sl@0: static RFs TheFs; sl@0: static RDbNamedDatabase TheDb; sl@0: static RDbs TheDbSession; sl@0: sl@0: //Test table defs sl@0: _LIT(KTestTableName, "TABLE1"); sl@0: sl@0: const TInt KTestRecordsCount = 350; sl@0: sl@0: struct TColDef sl@0: { sl@0: const TText* iName; sl@0: TDbColType iType; sl@0: TInt iAttributes; sl@0: }; sl@0: static TColDef const KColDefs[]= sl@0: { sl@0: {_S("ID"), EDbColUint32, TDbCol::EAutoIncrement}, sl@0: {_S("DATA"), EDbColBinary, TDbCol::ENotNull}, sl@0: {0} sl@0: }; sl@0: sl@0: //One or more files with KLargeFileName name and "." extension, where n is sl@0: //000, 001, 002, 003... sl@0: //will be created and they will occupy almost all available disk space. sl@0: //The idea is to perform after that "delete" sl@0: //transaction, which has to fail, because of "out of disk space" condition. sl@0: #if defined __WINSCW__ || defined __WINS__ sl@0: sl@0: _LIT(KLargeFileName, "T:\\DBMS-TST\\DeleteMe"); sl@0: sl@0: #elif defined __X86GCC__ sl@0: sl@0: _LIT(KLargeFileName, "G:\\DBMS-TST\\DeleteMe"); sl@0: sl@0: #else sl@0: sl@0: _LIT(KLargeFileName, "E:\\DBMS-TST\\DeleteMe"); sl@0: sl@0: #endif sl@0: sl@0: static void AssembleLargeFileName(const TDesC& aFileName, TInt aFileNumber, TDes& aResultPath) sl@0: { sl@0: _LIT(KFormatStr, "%S.%03d"); sl@0: aResultPath.Format(KFormatStr, &aFileName, aFileNumber); sl@0: } sl@0: sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: //Create/Destroy test environment - global functions sl@0: sl@0: //Deletes "aFullName" file. sl@0: static TInt DeleteDataFile(const TDesC& aFullName) sl@0: { sl@0: RFs fsSession; sl@0: TInt err = fsSession.Connect(); sl@0: if(err == KErrNone) sl@0: { sl@0: TEntry entry; sl@0: err = fsSession.Entry(aFullName, entry); sl@0: if(err == KErrNone) sl@0: { sl@0: RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName); sl@0: err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly); sl@0: if(err != KErrNone) sl@0: { sl@0: RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName); sl@0: } sl@0: err = fsSession.Delete(aFullName); sl@0: if(err != KErrNone) sl@0: { sl@0: RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName); sl@0: } sl@0: } sl@0: fsSession.Close(); sl@0: } sl@0: else sl@0: { sl@0: RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: //Deletes large data files only sl@0: static void DeleteLargeDataFiles() sl@0: { sl@0: for(TInt i=0;i<1000;++i) sl@0: { sl@0: TBuf filePath; sl@0: AssembleLargeFileName(KLargeFileName, i, filePath); sl@0: if(DeleteDataFile(filePath) != KErrNone) sl@0: { sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: //Deletes data files used by the test sl@0: static void DeleteDataFiles() sl@0: { sl@0: if(TheDbSession.Handle()) sl@0: { sl@0: TheDb.Close(); sl@0: } sl@0: TheDbSession.Close(); sl@0: DeleteDataFile(KTestDatabase); sl@0: DeleteLargeDataFiles(); sl@0: } sl@0: sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: //Tests macros and functions. sl@0: //If (!aValue) then the test will be panicked, the test data files will be deleted. sl@0: static void Check(TInt aValue, TInt aLine) sl@0: { sl@0: if(!aValue) sl@0: { sl@0: DeleteDataFiles(); sl@0: TheTest(EFalse, aLine); sl@0: } sl@0: } sl@0: //If (aValue != aExpected) then the test will be panicked, the test data files will be deleted. sl@0: static void Check(TInt aValue, TInt aExpected, TInt aLine) sl@0: { sl@0: if(aValue != aExpected) sl@0: { sl@0: RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue); sl@0: DeleteDataFiles(); sl@0: TheTest(EFalse, aLine); sl@0: } sl@0: } sl@0: //Use these to test conditions. sl@0: #define TEST(arg) Check((arg), __LINE__) sl@0: #define TEST2(aValue, aExpected) Check(aValue, aExpected, __LINE__) sl@0: sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: /////////////////////////////////////////////////////////////////////////////////////// sl@0: //Global functions sl@0: sl@0: //Prepares the test directory. sl@0: //TheFs.Connect() has to be called already. sl@0: static void SetupTestDirectory() sl@0: { sl@0: TInt err = TheFs.MkDir(KTestDatabase); sl@0: if(err != KErrNone) sl@0: { sl@0: RDebug::Print(_L("*** SetupTestDirectory(), RFs::MkDir(), err=%d\r\n"), err); sl@0: } sl@0: TEST(err == KErrNone || err == KErrAlreadyExists); sl@0: } sl@0: sl@0: //Leaves with info message printed out sl@0: static void LeaveL(TInt aError, TInt aLine) sl@0: { sl@0: RDebug::Print(_L("*** Leave. Error: %d, Line: %d\r\n"), aError, aLine); sl@0: User::Leave(aError); sl@0: } sl@0: sl@0: //Leaves if aError < 0 with info message printed out sl@0: static void LeaveIfErrorL(TInt aError, TInt aLine) sl@0: { sl@0: if(aError < KErrNone) sl@0: { sl@0: LeaveL(aError, aLine); sl@0: } sl@0: } sl@0: sl@0: //Use LEAVE() macro instead of User::Leave() and LEAVE_IF_ERROR() macro instead of sl@0: //User::LeaveIfError(). They will print the line number, where the "leave" was called. sl@0: #define LEAVE(aError) LeaveL(aError, __LINE__) sl@0: #define LEAVE_IF_ERROR(aError) LeaveIfErrorL(aError, __LINE__) sl@0: sl@0: //Creates one or more large files with the total size near to the size of the available disk space. sl@0: //The idea is to cause an "out of disk space" condition. sl@0: static void FillLargeDataFileL(RFile& aFile, TInt aSize) sl@0: { sl@0: TInt err = KErrDiskFull; sl@0: while(err == KErrDiskFull) sl@0: { sl@0: err = aFile.SetSize(aSize); sl@0: aSize -= 100; sl@0: if(aSize <= 0) sl@0: { sl@0: break; sl@0: } sl@0: } sl@0: TEST(err == KErrNone || err == KErrDiskFull); sl@0: } sl@0: sl@0: //Gets the available space of the tested drive. sl@0: static TInt64 FreeDiskSpaceL() sl@0: { sl@0: TVolumeInfo volInfoBefore; sl@0: LEAVE_IF_ERROR(TheFs.Volume(volInfoBefore, KTestDrive)); sl@0: return volInfoBefore.iFree; sl@0: } sl@0: sl@0: //Creates large data file with aSize size (in bytes). sl@0: static void DoCreateLargeFileL(const TDesC& aPath, TInt aSize) sl@0: { sl@0: RFile file; sl@0: CleanupClosePushL(file); sl@0: LEAVE_IF_ERROR(file.Replace(TheFs, aPath, EFileRead | EFileWrite)); sl@0: FillLargeDataFileL(file, aSize); sl@0: LEAVE_IF_ERROR(file.Flush()); sl@0: CleanupStack::PopAndDestroy(&file); sl@0: } sl@0: sl@0: //Creates enough number of large data files to fill the available disk space. sl@0: //It will change FilesCount global variable's value. sl@0: static void CreateLargeFileL() sl@0: { sl@0: TInt fileNo = 0; sl@0: const TInt KLargeFileSize = 1000000000; sl@0: TInt64 diskSpace = FreeDiskSpaceL(); sl@0: RDebug::Print(_L("CreateLargeFileL: free space before = %ld\n"), diskSpace); sl@0: TBuf filePath; sl@0: const TInt64 KMinDiskSpace = 200; sl@0: //Reserve almost all disk space, except a small amount - 200 bytes. sl@0: while(diskSpace > KMinDiskSpace) sl@0: { sl@0: AssembleLargeFileName(KLargeFileName, fileNo++, filePath); sl@0: TInt fileSize = KLargeFileSize; sl@0: if(diskSpace < (TInt64)KLargeFileSize) sl@0: { sl@0: TInt64 lastFileSize = diskSpace - KMinDiskSpace; sl@0: fileSize = I64LOW(lastFileSize); sl@0: } sl@0: DoCreateLargeFileL(filePath, fileSize); sl@0: diskSpace = FreeDiskSpaceL(); sl@0: RDebug::Print(_L("----CreateLargeFileL, step %d, free space = %ld\n"), fileNo, diskSpace); sl@0: } sl@0: diskSpace = FreeDiskSpaceL(); sl@0: RDebug::Print(_L("CreateLargeFileL: free space after = %ld\n"), diskSpace); sl@0: } sl@0: sl@0: //Reserves disk space for TheDbSession instance. sl@0: //TheDbSession instance has to be connected already. sl@0: static void ReserveDiskSpace() sl@0: { sl@0: TInt err = TheDbSession.ReserveDriveSpace(KTestDrive, KReservedSpaceSize); sl@0: TEST2(err, KErrNone); sl@0: } sl@0: sl@0: //Frees already reserved disk space for TheDbSession instance. sl@0: //TheDbSession instance has to be connected already. sl@0: static void FreeReservedSpace() sl@0: { sl@0: TheDbSession.FreeReservedSpace(KTestDrive); sl@0: } sl@0: sl@0: //Gets an access to the reserved disk space for TheDbSession instance. sl@0: //TheDbSession instance has to be connected already. sl@0: static void UnlockReservedSpace() sl@0: { sl@0: TInt err = TheDbSession.GetReserveAccess(KTestDrive); sl@0: TEST2(err, KErrNone); sl@0: } sl@0: sl@0: //Releases the access to the reserved disk space. sl@0: //TheDbSession instance has to be connected already. sl@0: static void LockReservedSpace() sl@0: { sl@0: (void)TheDbSession.ReleaseReserveAccess(KTestDrive); sl@0: } sl@0: sl@0: //Creates the test DBMS session sl@0: static void CreateTestDbSession() sl@0: { sl@0: TInt err = TheDbSession.Connect(); sl@0: TEST2(err, KErrNone); sl@0: } sl@0: sl@0: sl@0: //Creates the test database sl@0: //TheDbSession instance has to be connected already. sl@0: //TheFs.Connect() has to be called already. sl@0: static void CreateTestDatabase(RDbs& aDbs, RDbNamedDatabase& aDb) sl@0: { sl@0: //Create the test database. sl@0: TInt err = aDb.Replace(TheFs, KTestDatabase); sl@0: TEST2(err, KErrNone); sl@0: TheDb.Close(); sl@0: //Open it now using DBMS session (so, on DBMS server side), because we want to test sl@0: //server side RFs sessions - handling "out of disk space" situations. sl@0: err = aDb.Open(aDbs, KTestDatabase); sl@0: TEST2(err, KErrNone); sl@0: } sl@0: sl@0: //Creates a test table sl@0: static void CreateTestTableL(RDbNamedDatabase& aDb) sl@0: { sl@0: CDbColSet* colSet = CDbColSet::NewLC(); sl@0: for(const TColDef* colDef=KColDefs;colDef->iName;++colDef) sl@0: { sl@0: TDbCol col(TPtrC(colDef->iName), colDef->iType); sl@0: col.iAttributes = colDef->iAttributes; sl@0: colSet->AddL(col); sl@0: } sl@0: TEST2(aDb.CreateTable(KTestTableName, *colSet), KErrNone); sl@0: CleanupStack::PopAndDestroy(colSet); sl@0: } sl@0: sl@0: //Adds some data to the test table sl@0: static void AddTestDataL(RDbNamedDatabase& aDb) sl@0: { sl@0: RDbTable tbl; sl@0: CleanupClosePushL(tbl); sl@0: TEST2(tbl.Open(aDb, KTestTableName, RDbRowSet::EUpdatable), KErrNone); sl@0: for(TInt i=0;i