kerneltest/f32test/filesystem/fat/t_mount.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// f32test\server\t_mount.cpp
// Testing some issues connected with mounting/dismounting file fystems, drives finalisation etc.
//
//

/**
 @file
*/

#define __E32TEST_EXTENSION__

#include <f32file.h>
#include <e32test.h>
#include <e32math.h>
#include <e32property.h>
#include <f32dbg.h>

#include "t_server.h"
#include "fat_utils.h"

using namespace Fat_Test_Utils;

#ifdef __VC32__
    // Solve compilation problem caused by non-English locale
    #pragma setlocale("english")
#endif

RTest test(_L("T_Mount"));

static TInt     gDriveNum=-1; ///< drive number we are dealing with
static TInt64   gRndSeed;


const TInt KBM_Repetitions = 5;     ///< number of repetitions for BM testing
const TUint32 KCheckFileSize = 533; ///< size of the small file to be deleted

_LIT(KFileNameFirst,  "\\FIRST%d.DAT");
_LIT(KFileNameMiddle, "\\MID%d.DAT");
_LIT(KFileNameLast,   "\\LAST%d.DAT");
_LIT(KFileNameFiller, "\\FILL%d.DAT");

typedef void (*TStressFN)(TInt);    //-- pointer to the FAT mount stress function


//-------------------------------------------------------------------
//-- debug bit flags that may be set in the property which controls FAT volume mounting

const TUid KThisTestSID={0x10210EB3}; ///< this EXE SID

const TUint32 KMntProp_EnableALL    = 0x00000000; //-- enable all operations

#ifdef _DEBUG

const TUint32 KMntProp_DisableALL   = 0xFFFFFFFF; //-- disable all operations
const TUint32 KMntProp_Disable_FsInfo       = 0x00000001; //-- mask for disabling/enabling FSInfo information
const TUint32 KMntProp_Disable_FatBkGndScan = 0x00000002; //-- mask for disabling/enabling FAT background scanner

#endif

//-------------------------------------------------------------------

/** set a debug property value, which then wiil be read by FAT fsy */
void SetFsyDebugFlag(TInt aDriveNo, TUint32 aFlags)
{
    TInt nRes;

    nRes = RProperty::Set(KThisTestSID, aDriveNo, aFlags);
    test_KErrNone(nRes);
}

//-------------------------------------------------------------------
/**
    format the volume and read the boot sector
*/
static void FormatVolume(TBool aQuickFormat)
{
    TInt nRes;

    #if 0
    //-- FAT32 SPC:1; for the FAT32 testing on the emulator
    (void)aQuickFormat;

    #ifdef  __EPOC32__
    test.Printf(_L("This is emulator configuration!!!!\n"));
    test(0);
    #endif

    TFatFormatParam fp;
    fp.iFatType = EFat32;
    fp.iSecPerCluster = 1;
    nRes = FormatFatDrive(TheFs, CurrentDrive(), ETrue, &fp); //-- always quick; doesn't matter for the emulator
    #else
    nRes = FormatFatDrive(TheFs, CurrentDrive(), aQuickFormat);
    #endif

    test_KErrNone(nRes);

}

//-------------------------------------------------------------------

/**
    Prepare FAT volume for mounting performance testing

    1. quick format the drive
    2. create KBM_Repetitions files in the beginning (first files)
    3. create KMaxFillFiles/2 large files (they take about 40% of the volume);
    4. create KBM_Repetitions files (middle files)
    5. create KMaxFillFiles/2 large files (they take other 40% of the volume);
    6. create KBM_Repetitions files (last files)

    @return ETrue if everythis is OK
*/
TBool PrepareVolumeForBM()
{
    test.Printf(_L("Prepare the volume for BM testing...\n"));

    TInt nRes;
    TInt i;

    //-- 1. quick format the drive
    FormatVolume(ETrue);


    if(!Is_Fat32(TheFs, gDriveNum))
    {
        test.Printf(_L("This test requires FAT32 ! Skipping.\n"));
        return EFalse;
    }

    TVolumeInfo volInfo;
    nRes = TheFs.Volume(volInfo, gDriveNum);
    test_KErrNone(nRes);

    //-- the files will take 80% of the drive space
    const TInt    KMaxFillFiles = 100;
    const TUint32 KFillFileSz = (TUint32)((volInfo.iFree*8) / (10*KMaxFillFiles));

    //-- 2. create KBM_Repetitions files in the very begining (occupies first FAT entries)
    TBuf<64> buf;

    for(i=0; i<KBM_Repetitions; ++i)
    {
        buf.Format(KFileNameFirst, i);
        nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
        test_KErrNone(nRes);
    }

    //-- 3. Fill the FAT with entries and cteate a file in the middle
    const TInt nHalf1 = KMaxFillFiles / 2;

    for(i=0; i<nHalf1; ++i)
    {
        buf.Format(KFileNameFiller, i);
        nRes = CreateEmptyFile(TheFs, buf, KFillFileSz);
        test_KErrNone(nRes);
    }

    //-- 4. create a files in the middle
    for(i=0; i<KBM_Repetitions; ++i)
    {
        buf.Format(KFileNameMiddle, i);
        nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
        test_KErrNone(nRes);
    }

    //-- 5. fill second half FAT
    for(i=nHalf1; i<KMaxFillFiles; ++i)
    {
        buf.Format(KFileNameFiller, i);
        nRes = CreateEmptyFile(TheFs, buf, KFillFileSz);
        test_KErrNone(nRes);
    }


    //-- 6. create files in the very end (occupiy last FAT entries)
    for(i=0; i<KBM_Repetitions; ++i)
    {
        buf.Format(KFileNameLast, i);
        nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
        test_KErrNone(nRes);
    }

    return ETrue;
}

/**
    Mounts and dismounts FAT volume several times calculating average time taken to mount.
    Also can stress FS by calling stress function that can do some work on the volume.

    @param  apStressFN pointer to the stressing function which can be called just after mounting. Can be NULL.
    @return time in milliseconds taken to mout the volume
*/
static TUint32 DoMeasureMountTime(TStressFN apStressFN)
{

    TInt nRes;

    TTime   timeStart;
    TTime   timeEnd;

    TInt64   usMountTime=0;      //-- total time taken by "Mount"

    //-- disable FAT test utils print out, it can affects measured time
    EnablePrintOutput(EFalse);

    for(TInt i=0; i<KBM_Repetitions; ++i)
    {

        //-- A. remount FS taking the time when mounting starts
        nRes = RemountFS(TheFs, gDriveNum, &timeStart);
        test_KErrNone(nRes);

        //-- B. call a given stress function
        if(apStressFN)
            apStressFN(i);

        //-- C. take end time
        timeEnd.UniversalTime();
        test_KErrNone(nRes);

        usMountTime += (timeEnd.MicroSecondsFrom(timeStart)).Int64();
     }

    const TUint32 msMountTime = (TUint32)usMountTime / (K1mSec*KBM_Repetitions);

    EnablePrintOutput(ETrue); //-- Enable FAT test utils print out

    return msMountTime;
}

//-------------------------------------------------------------------

/**
    Stress function.
    Use case: read access to the first file on the volume in root dir.
*/
static void FirstReadAccess_FirstFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameFirst, aRepNo);

    TInt nRes = VerifyCheckableFile(TheFs, buf);
    test_KErrNone(nRes);
}


/**
    Stress function.
    Use case: read access to the middle file on the volume in root dir.
*/
static void FirstReadAccess_MiddleFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameMiddle, aRepNo);

    TInt nRes = VerifyCheckableFile(TheFs, buf);
    test_KErrNone(nRes);
}

/**
    Stress function.
    Use case: read access to the last file on the volume in root dir.
*/
static void FirstReadAccess_LastFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameLast, aRepNo);

    TInt nRes = VerifyCheckableFile(TheFs, buf);
    test_KErrNone(nRes);
}

/**
    Stress function.
    Use case: Getting volume information SYNCHRONOUSLY.
*/
static void GetVolInfo_Synch_Stress(TInt)
{
    TVolumeInfo volInfo;
    TInt nRes = TheFs.Volume(volInfo, gDriveNum);
    test_KErrNone(nRes);
}

/**
    Stress function.
    Use case: Getting volume information _ASYNCHRONOUSLY_, i.e. do not wait until
    FAT32 free space scan thread finishes
*/
static void GetVolInfo_Asynch_Stress(TInt)
{
    TVolumeInfo volInfo;

    //-- let's use special version of the RFS API
    TRequestStatus rqStat;
    TheFs.Volume(volInfo, gDriveNum, rqStat);

    User::WaitForRequest(rqStat);
    test(rqStat.Int()==KErrNone);

}

//-------------------------------------------------------------------

/**
    Stress function.
    Use case: deletes files in the beginning of the volume (or FAT table)
*/
static void DeleteFirstFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameFirst, aRepNo);

    TInt nRes = TheFs.Delete(buf);
    test_KErrNone(nRes);
}


/**
    Stress function.
    Use case: deletes files in the middle of the volume (or FAT table)
*/
static void DeleteMiddleFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameMiddle, aRepNo);

    TInt nRes = TheFs.Delete(buf);
    test_KErrNone(nRes);
}

/**
    Stress function.
    Use case: deletes files in the end of the volume (or FAT table)
*/
static void DeleteLastFile_Stress(TInt aRepNo)
{
    TBuf<64> buf;
    buf.Format(KFileNameLast, aRepNo);

    TInt nRes = TheFs.Delete(buf);
    test_KErrNone(nRes);
}


//-------------------------------------------------------------------

/**
    perform series of tests that measure and print out FAT volume mount time for different secenarios
*/
void MeasureMountTime()
{
    TUint32 msTime;

    if(!PrepareVolumeForBM()) //-- prepare the volume for BM tests: format it, create file structure etc.
        return;

    //-- 1. no stress function, measure just pure mount time
    msTime = DoMeasureMountTime(NULL);
    test.Printf(_L("#--> Pure mount:%d ms\n"), msTime);

    //-- 2.1 measure mount time with a read-acess to the first file on the volume
    msTime = DoMeasureMountTime(FirstReadAccess_FirstFile_Stress);
    test.Printf(_L("#--> mount and read access to 1st file:%d ms\n"), msTime);

    //-- 2.2 measure mount time with a read-acess to the middle file on the volume
    msTime = DoMeasureMountTime(FirstReadAccess_MiddleFile_Stress);
    test.Printf(_L("#--> mount and read access to middle file:%d ms\n"), msTime);

    //-- 2.3 measure mount time with a read-acess to the last file on the volume
    msTime = DoMeasureMountTime(FirstReadAccess_LastFile_Stress);
    test.Printf(_L("#--> mount and read access to last file:%d ms\n"), msTime);

    //-- 2.4 measure mount time with getting a volume information
    msTime = DoMeasureMountTime(GetVolInfo_Synch_Stress);
    test.Printf(_L("#--> mount and getting volInfo (synch):%d ms\n"), msTime);

    msTime = DoMeasureMountTime(GetVolInfo_Asynch_Stress);
    test.Printf(_L("#--> mount and getting volInfo (Asynch):%d ms\n"), msTime);


    //-- 2.4 measure mount time with deleting file in the beginning (write access to the first entries of the FAT32)
    msTime = DoMeasureMountTime(DeleteFirstFile_Stress);
    test.Printf(_L("#--> mount and delete first file:%d ms\n"), msTime);

    //-- 2.5 measure mount time with deleting file in the middle (write access to the middle entries of the FAT32)
    msTime = DoMeasureMountTime(DeleteMiddleFile_Stress);
    test.Printf(_L("#--> mount and delete middle file:%d ms\n"), msTime);

    //-- 2.6 measure mount time with deleting file in the end (write access to the last entries of the FAT32)
    msTime = DoMeasureMountTime(DeleteLastFile_Stress);
    test.Printf(_L("#--> mount and delete last file:%d ms\n"), msTime);

    test.Printf(_L("---\n"), msTime);
}

//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID      PBASE-T_MOUNT-0521
//! @SYMTestType        PT
//! @SYMPREQ            PREQ1721
//! @SYMTestCaseDesc    Testing FAT volume mount performance for various scenarios
//!
//! @SYMTestActions
//!             0   Prepare the volume by formatting it and creating 100 files to occupy the space.
//!             1   Turn OFF all mount enhancements
//!             2   Measure and print out volume mount time for the next scenarios:
//!                   a.  simple mount
//!                   b.  mount and read access to the media (reading the last file in the root dir.)
//!                   c.  mount and write access to the media (writing data into the last file in the root dir)
//!                   d.  mount getting volume information
//!
//!
//!             3     Turn ON using FSInfo.
//!             4     Repeat step 2 for this case.
//! @SYMTestExpectedResults Finishes ok.
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
void TestFAT_Mounting_Performance()
{
    test.Next(_L("\n#--> Measuring FAT volumes mount performance.\n"));
#ifndef _DEBUG
    test.Printf(_L("Skipping the test in the Release build! \n"));
    return;
#else

   //-- 1. turn OFF all mount enhancements like using FSInfo, backround FAT scan etc.
   test.Printf(_L("#--> ==== All mount enhancements are disabled ====\n"));
   SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL);
   MeasureMountTime();

   //-- 2. Turn ON using FSInfo
   test.Printf(_L("#--> ==== Enabled Using FSInfo ====\n"));
   SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FsInfo);
   MeasureMountTime();

   //-- 2. Turn OFF using FSInfo and ON FAT32 bacground scanning
   test.Printf(_L("#--> ==== Enabled FAT32 BkGnd scan, FSInfo disabled ====\n"));
   SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FatBkGndScan);
   MeasureMountTime();

   //-- restore mounting mechanism
   SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);

#endif //_DEBUG
}



//-------------------------------------------------------------------

/**
    Check if the drive aDriveNo is finalised or not.
    The "CleanShutDown" is obtained by QueryVolumeInfoExt API and by reading 2 FATs directly with checking their consistence.

    @param  aDriveNo drive number to query.
    @return ETrue if the drive if finalised
*/
static TBool DoCheckVolumeFinalised(TInt aDriveNo)
{
    TInt nRes;
    TPckgBuf<TBool> boolPckg;

    //-- 1. get "Finalised" state by using the API
    nRes = TheFs.QueryVolumeInfoExt(aDriveNo, EIsDriveFinalised, boolPckg);
    test_KErrNone(nRes);

    //-- N.B. for FAT12 the result can be either OK or "NotSupported"
    //-- If FAT12 is in explicit "finalised" state, the result will be OK
    //-- if not, we can't query the volume state, because FAT12 doesn't support flags in FAT[1]

    const TBool bFinalised_From_API = boolPckg() >0;
          TBool bFinalised_From_FAT1 = bFinalised_From_API;

    TBuf8<32> fatBuf(32);
    TFatBootSector  bootSec;
    const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;

    nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, bootSec);
    test_KErrNone(nRes);

    test(bootSec.IsValid());

    const TUint32 Fat1StartPos = bootSec.FirstFatSector() * bootSec.BytesPerSector();

    if(bootSec.FatType() == EFat16)
    {//-- FAT16
        TUint16 fatEntry;
        const TUint16 KClnShtdnMask = 0x8000; //-- "ClnShutBitMask", see FAT specs

        //-- read "CleanShutDown" flag directly from the 1st FAT
        nRes = MediaRawRead(TheFs, gDriveNum, Fat1StartPos, fatBuf.Size(), fatBuf);
        test_KErrNone(nRes);

        Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
        bFinalised_From_FAT1 = (fatEntry & KClnShtdnMask) >0;


        for(TInt i=1; i<bootSec.NumberOfFats(); ++i)
        {
            //-- read a flag from the next FAT
            const TUint32 currFatStartPos = (bootSec.FirstFatSector() + i*bootSec.TotalFatSectors())*bootSec.BytesPerSector();

            nRes = MediaRawRead(TheFs, gDriveNum, currFatStartPos, fatBuf.Size(), fatBuf);
            test_KErrNone(nRes);

            Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
            const TBool bFinalised_From_currFAT = (fatEntry & KClnShtdnMask)>0;

            test(bFinalised_From_currFAT == bFinalised_From_FAT1);
        }

    }
    else if(bootSec.FatType() == EFat32)
    {//-- FAT32
        TUint32 fatEntry;
        const TUint32 KClnShtdnMask = 0x08000000; //-- "ClnShutBitMask", see FAT specs

        //-- read "CleanShutDown" flag directly from the 1st FAT
        nRes = MediaRawRead(TheFs, gDriveNum, Fat1StartPos, fatBuf.Size(), fatBuf);
        test_KErrNone(nRes);

        Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
        bFinalised_From_FAT1 = (fatEntry & KClnShtdnMask) >0;

        for(TInt i=1; i<bootSec.NumberOfFats(); ++i)
        {
            //-- read a flag from the next FAT
            const TUint32 currFatStartPos = (bootSec.FirstFatSector() + i*bootSec.TotalFatSectors())*bootSec.BytesPerSector();

            nRes = MediaRawRead(TheFs, gDriveNum, currFatStartPos, fatBuf.Size(), fatBuf);
            test_KErrNone(nRes);

            Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
            const TBool bFinalised_From_currFAT = (fatEntry & KClnShtdnMask) >0;

            test(bFinalised_From_currFAT == bFinalised_From_FAT1);
        }

    }
    else  //-- FAT12
    {//-- FAT12 doesn't have flags in FAT[1]
     bFinalised_From_FAT1 = bFinalised_From_API;
    }

    test(bFinalised_From_FAT1 == bFinalised_From_API);

    return bFinalised_From_API;
}



//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID      PBASE-T_MOUNT-0522
//! @SYMTestType        UT
//! @SYMPREQ            PREQ1721
//! @SYMTestCaseDesc    RFs::FinaliseDrive() and RFs::FinaliseDrives() API
//!
//! @SYMTestActions
//!             0   Finalise the drive in RW mode and check the result.
//!             1   Finalise the drive in RW mode once again and check the result.
//!             2   Create a file and check that the volume has become unfinalised.
//!             3   Open a file, try to finalise the volume; it shall fail with KErrInUse
//!             4   close the file, finalise, check that the result is OK.
//!             5   "Unfinalise" the volume; check that the volume is not finalised any longer.
//!             6   Finalise the drive in RO mode and check the result.
//!             7   Try to create a file, shall fail with KErrAccessDenied
//!             8   Try to finalise into RW mode, shall fail with KErrAccessDenied
//!             9   Try to unfinalise volume, it shall remain RO
//!             10  Remount the drive, it shall become RW and finalised
//!             11  Test transition "Not finalised" -> EFinal_RW (expected: KErrNone)
//!             12  Test transition EFinal_RW -> EFinal_RO  (expected: KErrNone)
//!             13  Test transition EFinal_RO -> EFinal_RW  (expected: KErrAccessDenied)
//!             14  Remount the volume to reset RO flag
//!             15  test old RFs::FinaliseDrives() API by finalising all drives in the system
//!
//! @SYMTestExpectedResults finishes if the volume finalisation works correctly. panics otherwise
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
static void TestFinaliseFS()
{
    test.Next(_L("Testing RFs::FinaliseDrives() API\n"));

    if(!Is_Fat16(TheFs, gDriveNum) && !Is_Fat32(TheFs, gDriveNum))
    {
        test.Printf(_L("This step requires FAT16 or FAT32 ! Skipping.\n"));
        return;
    }

    TInt  nRes;
    TBool bDriveFinalised;

    //============= 1. finalise the drive (RW mode) and check the result
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //-- 1.1 finalise the drive second time EFinal_RW -> EFinal_RW shall work
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //============= 2. create a file. Shall succeed (EFinal_RW), the volume shall become unfinalised

    RFile file;
    _LIT(KFileName, "\\my_file1.dat");

    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(!bDriveFinalised); //-- the volume has become "unfinalised"

    //-- 2.1 open a file, try to finalise; Shall dail with KErrInUse
    nRes = file.Replace(TheFs, KFileName, EFileWrite | EFileRead);
    test_KErrNone(nRes);

    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test(nRes==KErrInUse); //-- can't finalise drives with opened objects

    file.Close();

    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //============= 3. test "unfinalise API"
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EForceUnfinalise);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(!bDriveFinalised); //-- the volume has become "unfinalised"

    //============= 4. test finalisation into RO mode
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RO); //-- the volume becomes RO
    test_KErrNone(nRes);

    //-- try to write a file on RO volume; it shall fail with KErrAccessDenied
    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test(nRes == KErrAccessDenied);
    file.Close();

    //-- 4.1 try to finalise into EFinal_RW mode, shall fail with KErrAccessDenied
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test(nRes == KErrAccessDenied);

    //-- 4.2 "unfinalise" the volume, it still shall remain RO
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EForceUnfinalise);
    test_KErrNone(nRes);

    //-- try to write a file on RO volume; it shall fail with KErrAccessDenied
    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test(nRes == KErrAccessDenied);
    file.Close();

    //-- remount FS, the drive shall become RW
    nRes = RemountFS(TheFs, gDriveNum);
    test_KErrNone(nRes);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //-- try to write a file on RW volume, shall be OK
    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test(nRes == KErrNone);
    file.Close();

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(!bDriveFinalised);

    //============= 5. test various finalisation modes

    //-- 5.1  Not finalised -> EFinal_RW (KErrNone)
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test(nRes == KErrNone);
    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //-- 5.2  EFinal_RW -> EFinal_RO (KErrNone)
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RO);
    test(nRes == KErrNone);
    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //-- 5.2  EFinal_RO -> EFinal_RW  (KErrAccessDenied)
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test(nRes == KErrAccessDenied);
    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    //-- 5.3 restore
    nRes = RemountFS(TheFs, gDriveNum);
    test_KErrNone(nRes);


    //============= 6. test old RFs::FinaliseDrives API

    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test(nRes == KErrNone);

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(!bDriveFinalised);

    TheFs.FinaliseDrives(); //-- shall work as TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW) but for ALL drives

    bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    nRes = CreateEmptyFile(TheFs, KFileName, 128000);
    test(nRes == KErrNone);

}

//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID      PBASE-T_MOUNT-0523
//! @SYMTestType        UT
//! @SYMPREQ            PREQ1721
//! @SYMTestCaseDesc    testing boot and backup boot sectors on FAT32 volume
//!
//! @SYMTestActions
//!             0   Quick format the drive
//!             1   read main and backup boot & fsinfo sectors and check their validity
//!             2   corrupt main boot sector and check that the drive can be mounted (backup boot sector is used)
//!             3   corrupt backup boot sector and check that the drive can not be mounted.
//!             4   Quick format the drive to restore test environment
//!
//! @SYMTestExpectedResults finishes if the boot sector and backup boot sector functionality compliant with the FAT specs. panics otherwise
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
static void TestBackupBootSector()
{
    test.Next(_L("Testing Backup Boot Sector.\n"));

    TInt nRes;

    //-- quick format the drive
    FormatVolume(ETrue);

    if(!Is_Fat32(TheFs, gDriveNum))
    {
        test.Printf(_L("This step requires FAT32 ! Skipping.\n"));
        return;
    }

    TFatBootSector  mainBootSec, backupBootSec;
    TFSInfo         mainFSInfo, backupFSInfo;

    const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;
    const TUint32 posBkBootSec   = KBkBootSectorNum << KDefaultSectorLog2;

    //-- read main and backup boot & fsinfo sectors and check their validity
    nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, mainBootSec);
    test_KErrNone(nRes);

    //-- backup boot sector # must be 6
    nRes = ReadBootSector(TheFs, gDriveNum, posBkBootSec, backupBootSec);
    test_KErrNone(nRes);

    test(mainBootSec.IsValid());
    test(backupBootSec.IsValid());
    test(mainBootSec == backupBootSec);

    //-- read fsinfo sectors
    const TUint32 posMainFSInfo = mainBootSec.FSInfoSectorNum() << KDefaultSectorLog2;
    const TUint32 posBkFSInfo   = (KBkBootSectorNum + mainBootSec.FSInfoSectorNum()) << KDefaultSectorLog2;

    test(posMainFSInfo != 0);
    test(posBkFSInfo != 0);

    nRes = ReadFSInfoSector(TheFs, gDriveNum, posMainFSInfo, mainFSInfo);
    test_KErrNone(nRes);

    nRes = ReadFSInfoSector(TheFs, gDriveNum, posBkFSInfo, backupFSInfo);
    test_KErrNone(nRes);

    test(mainFSInfo.IsValid());
    test(backupFSInfo.IsValid());
    test(mainFSInfo == backupFSInfo);

    //-- corrupt main boot sector and check that the drive can be mounted
    test.Printf(_L("Corrupting main boot sector...\n"));


    //-- A1. corrupt main boot sector starting from the pos:0
    nRes = FillMedia(TheFs, gDriveNum, posMainBootSec, posMainBootSec+KDefaultSectorSize, 0xaa);

    //-- A2. remount FS, it shall be OK because of the using backup boot sector
    nRes = RemountFS(TheFs, gDriveNum);
    test_KErrNone(nRes);


    //-- B1. corrupt BACKUP boot sector starting from the sec:6
    nRes = FillMedia(TheFs, gDriveNum, posBkBootSec, posBkBootSec+KDefaultSectorSize, 0xbb);

    //-- B2. remount FS, unable to mount.
    nRes = RemountFS(TheFs, gDriveNum);
    test(nRes == KErrCorrupt);


    //-- quick format the drive
    FormatVolume(ETrue);

}

//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID      PBASE-T_MOUNT-0524
//! @SYMTestType        UT
//! @SYMPREQ            PREQ1721
//! @SYMTestCaseDesc    testing FSInfo sector functionality.
//!
//! @SYMTestActions
//!             0   Quick format the drive
//!             1   finalise the drive to write correct data to the FSInfo & its backup copy.
//!             2   read FSInfo sector, its backup copy and check that they are both valid, identical and contain correct data
//!             3   check that after the formatting FS info values are the same as real, obtained from the FAT scanning.
//!             4   create a random - sized file, check that after finalisation the number of free clusters is identical to the FSinfo
//!             5   Quick format the drive to restore test environment
//!
//! @SYMTestExpectedResults finishes if the FSInfo sectors functionality compliant with the FAT specs. panics otherwise
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
static void TestFSInfoSector()
{
    test.Next(_L("Testing FSInfo Sector.\n"));

#ifndef _DEBUG
    test.Printf(_L("Skipping the test in the Release build! \n"));
    return;
#else

    TInt nRes;

    //-- quick format the drive
    FormatVolume(ETrue);

    if(!Is_Fat32(TheFs, gDriveNum))
    {
        test.Printf(_L("This step requires FAT32 ! Skipping.\n"));
        return;
    }

    TFatBootSector  bootSec;
    const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;
    nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, bootSec);
    test_KErrNone(nRes);

    const TUint32 bytesPerSector = bootSec.BytesPerSector();
    const TUint32 secPerClust = bootSec.SectorsPerCluster();

    //-- finalise the drive, just in case
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test_KErrNone(nRes);

    //============= 1. read FSInfo sector and its backup copy and compare them
    TFSInfo fsInfoSec;

    //-- main FSInfo
    nRes = ReadFSInfoSector(TheFs, gDriveNum, KFSInfoSectorNum*bytesPerSector, fsInfoSec);
    test_KErrNone(nRes);
    test(fsInfoSec.IsValid());

    TUint32 freeClusters_FSInfo = fsInfoSec.FreeClusterCount();
    TUint32 nextFree_FSInfo = fsInfoSec.NextFreeCluster();

    //-- backup FSInfo
    nRes = ReadFSInfoSector(TheFs, gDriveNum, KBkFSInfoSectorNum*bytesPerSector, fsInfoSec);
    test_KErrNone(nRes);
    test(fsInfoSec.IsValid());

    //-- both copies must be identical
    test(freeClusters_FSInfo == fsInfoSec.FreeClusterCount());
    test(nextFree_FSInfo == fsInfoSec.NextFreeCluster());

    //-- FAT[0] and FAT[1] are not used; FAT[2] is taken by the 1st cluster of the FAT32 Root directory.
    test(nextFree_FSInfo == (2+1));

    //============= 2. check that after the formatting FS info values are the same as real.

    //-- 2.1 disable using FSInfo and other stuff
    SetFsyDebugFlag(gDriveNum, KMntProp_Disable_FsInfo);

    //-- remount FAT; using FSInfo is disabled. FAT will be explicitly scanned.
    nRes = RemountFS(TheFs, gDriveNum);
    test_KErrNone(nRes);

    //-- restore mounting mechanism
    SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);

    //-- get free clusters number from the FSY, which in turn had counted them explicitly
    TVolumeInfo volInfo;
    nRes = TheFs.Volume(volInfo, gDriveNum);
    test_KErrNone(nRes);

    TUint32 freeClusters = (TUint32)(volInfo.iFree / (bytesPerSector * secPerClust));
    test(freeClusters == freeClusters_FSInfo);

    //============= 3. create a random - sized file, check that after finalisation the number of free clusters is identical to the FSinfo
    _LIT(KFileName, "\\FILE1.DAT");
    const TUint32 rndClusters = 7+((TUint32)Math::Rand(gRndSeed)) % 5000;
    const TUint32 fileSz = rndClusters * bytesPerSector * secPerClust;
    nRes = CreateEmptyFile(TheFs, KFileName, fileSz);
    test_KErrNone(nRes);

    //-- 3.1 get data from FS
    nRes = TheFs.Volume(volInfo, gDriveNum);
    test_KErrNone(nRes);
    freeClusters = (TUint32)(volInfo.iFree / (bytesPerSector * secPerClust));

    //-- 3.2 finalise the volume and get data from FSInfo
    nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
    test_KErrNone(nRes);

    //-- main FSInfo
    nRes = ReadFSInfoSector(TheFs, gDriveNum, KFSInfoSectorNum*bytesPerSector, fsInfoSec);
    test_KErrNone(nRes);
    test(fsInfoSec.IsValid());

    freeClusters_FSInfo = fsInfoSec.FreeClusterCount();
    nextFree_FSInfo = fsInfoSec.NextFreeCluster();

    //-- backup FSInfo
    nRes = ReadFSInfoSector(TheFs, gDriveNum, KBkFSInfoSectorNum*bytesPerSector, fsInfoSec);
    test_KErrNone(nRes);
    test(fsInfoSec.IsValid());

    //-- both copies must be identical
    test(freeClusters_FSInfo == fsInfoSec.FreeClusterCount());
    test(nextFree_FSInfo == fsInfoSec.NextFreeCluster());

    //-- the information in FSInfo must be the same as in FAT
    test(freeClusters == freeClusters_FSInfo);

    TBool bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
    test(bDriveFinalised);

    TheFs.Delete(KFileName);

    //-- restore mounting mechanism
    SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);

#endif //_DEBUG
}

//-------------------------------------------------------------------

/** initialise test global objects */
static void InitGlobals()
{
    TInt nRes;

    //-- define a propery which will control mount process in the fsy.
    //-- The property key is a drive number being tested

    _LIT_SECURITY_POLICY_PASS(KTestPropPolicy);
    nRes = RProperty::Define(KThisTestSID, gDriveNum, RProperty::EInt, KTestPropPolicy, KTestPropPolicy);
    test(nRes == KErrNone || nRes == KErrAlreadyExists);

    nRes = RProperty::Set(KThisTestSID, gDriveNum, KMntProp_EnableALL);
    test_KErrNone(nRes);

    gRndSeed = Math::Random();
    (void)&gRndSeed; //-- get rid of warning
}

/** destroy test global objects */
static void DestroyGlobals()
{
    //-- delete test property
    RProperty::Delete(KThisTestSID, gDriveNum);

    TVolumeInfo v;
    TheFs.Volume(v);
}

//-------------------------------------------------------------------
/**
    Manual test. Requires manual removing and putting back the media.
    On the emulator one can use pressing (and holding) F5 key to simulate media removal.
*/
void Manual_TestRemount_On_MediaRemoval()
{
    TInt nRes;

    _LIT(KFileName, "\\my_file1.dat");
    const TUint32 KFileSz = K1MegaByte;

    //-- 1. create a file
    nRes = CreateEmptyFile(TheFs, KFileName, KFileSz);
    test_KErrNone(nRes);

    RFile file;

    nRes = file.Open(TheFs, KFileName, EFileRead | EFileWrite);
    test_KErrNone(nRes);

    TBuf8<512> buf(512);
    TVolumeInfo vi;
    buf.FillZ();

    TKeyCode key;

    for(;;)
    {
        TheFs.SetDebugRegister(0x00);

        nRes = file.Read(0, buf);

        test.Printf(_L("Remove the media and press a key.\n"));
        key = test.Getch();
        if(key == EKeyEscape)
                break;

        TheFs.SetDebugRegister(KFSYS | KFSERV);
        nRes = file.Read(0, buf);

        if(nRes != KErrNone)
        {

            test.Printf(_L("ReadFile: %d!\n"), nRes);

            key = test.Getch();
            if(key == EKeyEscape)
                break;

            nRes = TheFs.Volume(vi,gDriveNum);
            test.Printf(_L("Volume: %d!\n"), nRes);

            key = test.Getch();
            if(key == EKeyEscape)
                break;

            nRes = file.Write(0, buf);
            test.Printf(_L("WriteFile: %d!\n"), nRes);

            key = test.Getch();
            if(key == EKeyEscape)
                break;

        }

    }


    file.Close();
}


//-------------------------------------------------------------------
/**
    Wait for the request aRqStat to be completed with timeout.

    @param  aRqStat         request status object we need to wait to complete
    @param  aTimeout_uS     timeout in microseconds

    @return ETrue   if the aRqStat is completed before time is out
            EFalse  if aTimeout_uS has passed. And the state of the aRqStat not changed.
*/
TBool WaitForRequestWithTimeout(TRequestStatus& aRqStat, TUint32 aTimeout_uS)
{
    TRequestStatus  rqStatTimeout(KRequestPending);
    RTimer          tmrTimeOut;
    TInt            nRes;
    TBool           bReqCompleted;

    if(aRqStat.Int() != KRequestPending)
        return ETrue; //-- nothing to wait for.

    //-- set up a timeout timer
    nRes = tmrTimeOut.CreateLocal();
    test(nRes == KErrNone);

    tmrTimeOut.After(rqStatTimeout, aTimeout_uS);

    User::WaitForRequest(aRqStat, rqStatTimeout);

    if(aRqStat == KRequestPending)
        {//-- timeout.
        bReqCompleted = EFalse;
        }
        else
        {//-- the main request has been completed, cancel timer
            bReqCompleted = ETrue;
            if(rqStatTimeout.Int() == KRequestPending)
                {
                tmrTimeOut.Cancel();
                User::WaitForRequest(rqStatTimeout);
                }
        }

    tmrTimeOut.Close();

    return bReqCompleted;
}

//-------------------------------------------------------------------

void TestNotifyDiskSpace()
{
    test.Next(_L("Testing NotifyDiskSpace() on asynchronous mounting\n"));

#ifndef _DEBUG
    test.Printf(_L("Skipping the test in the Release build! \n"));
    return;
#else

    TInt nRes;
    TRequestStatus rqStat;
    TVolumeInfo volInfo;

    //-- quick format the drive
    FormatVolume(ETrue);

    if(!Is_Fat32(TheFs, gDriveNum))
    {//-- only FAT32 supports asynch mounting;
        test.Printf(_L("This test step requires FAT32!\n"));
        return;
    }

    //-- FAT32 free space threshold that is supposed to be triggered by notifiers
    const TInt64 KFreeSpaceThreshold = 300*K1MegaByte;

    //-- Turn OFF using FSInfo and ON FAT32 background scanning; it will cause compulsory FAT32 background free clusters scan.
    //-- if FAT32 background free clusters scan is disabled in config, this test can hang on waiting for free space notifications.
    test.Printf(_L("==== Enabled FAT32 BkGnd scan, FSInfo disabled ====\n"));
    SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FatBkGndScan);


    //===== create a big file in the beginning in order to avoid freeSpaceThreshold being reached too fast
    nRes = CreateEmptyFile(TheFs, _L("\\big_empty_file.bin"), 10*K1MegaByte);
    test_KErrNone(nRes);

//TheFs.SetDebugRegister(0x03);

    //===== Test that FAT32 free space scanning thread updates File Server free space notifiers
    test.Printf(_L("Testing FAT32 free space scanning thread triggers notifiers..."));

    nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it must cause FAT32 free space scan in background
    test_KErrNone(nRes);

    test.Printf(_L("Waiting for %LU bytes available on the volume...\n"), KFreeSpaceThreshold);

    //-- check if we can use the notifiers at all... If free space is >= KFreeSpaceThreshold, the notifier won't be triggered
    //-- get _current_ amount of free space asynchronously
    TheFs.Volume(volInfo, gDriveNum, rqStat);
    User::WaitForRequest(rqStat);
    test(rqStat.Int()==KErrNone);

    if(volInfo.iSize <= KFreeSpaceThreshold)
    {
    test.Printf(_L("The volume is too small for %LU bytes notify ...\n"), KFreeSpaceThreshold);
    test.Printf(_L("The test is inconclusive ...\n"));
    return;
    }

    if(volInfo.iFree >= KFreeSpaceThreshold)
    {
    test.Printf(_L("The volume already has %LU free bytes...\n"), volInfo.iFree);
    test.Printf(_L("The test is inconclusive ...\n"));
    return;
    }


    TheFs.NotifyDiskSpace(KFreeSpaceThreshold, gDriveNum, rqStat);
    test(rqStat.Int()==KRequestPending);

    //-- wait for notification for 30 seconds; If for some reason FAT32 background scanning for free clusters doesn't
    //-- work (e.g. configued out in FAT), this test is inconclusive.
    TBool bCompleted = WaitForRequestWithTimeout(rqStat, 30*K1Sec);

    if(!bCompleted)
    {
        test.Printf(_L("Wait timeout! something is wrong...\n"));
        test(0);
    }

    test_KErrNone(rqStat.Int());

    //-- get _current_ amount of free space asynchronously
    TheFs.Volume(volInfo, gDriveNum, rqStat);
    User::WaitForRequest(rqStat);
    test(rqStat.Int()==KErrNone);
    test.Printf(_L("Current amount of free space on the volume: %LU \n"), volInfo.iFree);


    //===== Test that aborting FAT32 free space scanning thread will trigger notifiers
    test.Printf(_L("Testing aborting FAT32 free space scanning thread..."));

    nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it must cause FAT32 free space scan in background
    test_KErrNone(nRes);

    TheFs.NotifyDiskSpace(KFreeSpaceThreshold, gDriveNum, rqStat);
    test(rqStat.Int()==KRequestPending);

    nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it will abort the scanning thread
    test_KErrNone(nRes);

    test(rqStat.Int() != KRequestPending);

    //-- get _current_ amount of free space asynchronously
    TheFs.Volume(volInfo, gDriveNum, rqStat);
    User::WaitForRequest(rqStat);
    test(rqStat.Int()==KErrNone);
    test.Printf(_L("Current amount of free space on the volume: %LU \n"), volInfo.iFree);


    //-- find out free space on the volume; it will also blocks until FAT32 free space scanning finishes.
    nRes = TheFs.Volume(volInfo);
    test(nRes==KErrNone);
    test.Printf(_L("free space on the volume: %LU \n"), volInfo.iFree);


//TheFs.SetDebugRegister(0x00);

    //-- restore mounting mechanism
    SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);

#endif  //_DEBUG
}

//-------------------------------------------------------------------

void CallTestsL()
    {
    //-- set up console output
    Fat_Test_Utils::SetConsole(test.Console());

    TInt nRes=TheFs.CharToDrive(gDriveToTest, gDriveNum);
    test(nRes==KErrNone);

    //-- check if this is FAT
    if(!Is_Fat(TheFs, gDriveNum) || Is_Automounter(TheFs, gDriveNum))
    {//-- it doesn't make much sense to run this test under automounter+FAT. The automounter can't easily handle formatting corrupted media
     //-- and the mounting permaormance measurements don't make much sense in this case as well.

        test.Printf(_L("Skipping. This test requires explicitly mounted FAT file system.\n"));
        return;
    }

    //-- check this is not the internal ram drive
    TVolumeInfo v;

    nRes = TheFs.Volume(v);
    test(nRes==KErrNone);
    if(v.iDrive.iMediaAtt & KMediaAttVariableSize)
        {
        test.Printf(_L("Skipping. Internal ram drive not tested.\n"));
        return;
        }

    //-------------------------------------

    PrintDrvInfo(TheFs, gDriveNum);
    InitGlobals();


    TestNotifyDiskSpace();

    //-------------------------------------
    TestBackupBootSector();
    TestFSInfoSector();
    TestFinaliseFS();

    //-------------------------------------
    TestFAT_Mounting_Performance();
    //-------------------------------------
    //-- manual test
    //Manual_TestRemount_On_MediaRemoval();

    //-------------------------------------
    DestroyGlobals();

    }