// Copyright (c) 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:
//

/**
Instructions:

This is a manual test created to verify DEFXXXXX, 2 executables needs to be run to do this test
1) t_sqlfilesrvcrash1.exe - Generate a corrupted journal file, this will cause the device to reset. 
2) t_sqlfilesrvcrash2.exe - After the reboot, tests if SQL can handle the courrpted journal file.

This test requires a non-rugged drive to store the database file and therefore will only work in hardware mode
*/

#include <e32test.h>
#include <f32file.h>
#include <Sqldb.h>

RTest TheTest(_L("t_sqlfilesrvcrash1 test"));

#if !defined __WINS__ && !defined __WINSCW__

RFs TheFs;
RSqlDatabase TheDb;

const TInt KControlIoRuggedOn=2;
const TInt KControlIoRuggedOff=3;
const TInt KControlIoIsRugged=4;

const TChar KDrvLetter = ('E');
_LIT(KTestDir, "E:\\test\\");
_LIT(KDbName, "E:\\test\\t_sqlfilesrvcrash.db");
_LIT8(KConfigStr, "page_size=32768");
_LIT(KFileSrvName, "efile.exe");

///////////////////////////////////////////////////////////////////////////////////////
//Deletes all created test files.
void DeleteTestFiles()
    {
    TheDb.Close();
    (void)RSqlDatabase::Delete(KDbName);
    }

///////////////////////////////////////////////////////////////////////////////////////
//Test macros and functions
void Check(TInt aValue, TInt aLine)
    {
    if(!aValue)
        {
        DeleteTestFiles();
        RDebug::Print(_L("*** Line %d\r\n"), aLine);
        TheTest(EFalse, aLine);
        }
    }
void Check(TInt aValue, TInt aExpected, TInt aLine)
    {
    if(aValue != aExpected)
        {
        DeleteTestFiles();
        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__)

///////////////////////////////////////////////////////////////////////////////////////
//Set the drive to Rugged or non-rugged depending on the input parameter
void SetDrive(TBool aSetToRugged)
    {
    TUint8 isRugged;
    TPtr8 pRugged(&isRugged,1,1);

    TInt drvNum;
    TInt err = TheFs.CharToDrive(KDrvLetter, drvNum);
    TEST2(err, KErrNone);
    err=TheFs.ControlIo(drvNum,KControlIoIsRugged,pRugged);
    TEST2(err, KErrNone);
    
    if(!aSetToRugged)
        {
        if(isRugged)
            {
            err=TheFs.ControlIo(drvNum,KControlIoRuggedOff);
            TEST2(err, KErrNone);
            }
        }
    else
        {
        if(!isRugged)
            {
            err=TheFs.ControlIo(drvNum,KControlIoRuggedOn);
            TEST2(err, KErrNone);
            }
        }
    }

//Creates file session instance and the test directory
void CreateTestEnv()
    {
    TInt err = TheFs.Connect();
    TEST2(err, KErrNone);
    
    //Set the drive to non-rugged
    TBool setDriveToRugged = EFalse;
    SetDrive(setDriveToRugged);
    
    err = TheFs.MkDir(KTestDir);
    TEST(err == KErrNone || err == KErrAlreadyExists);
    }

TInt KillProcess(const TDesC& aProcessName)
    {
    TFullName name;
    //RDebug::Print(_L("Find and kill \"%S\" process.\n"), &aProcessName);
    TBuf<64> pattern(aProcessName);
    TInt length = pattern.Length();
    pattern += _L("*");
    TFindProcess procFinder(pattern);

    while (procFinder.Next(name) == KErrNone)
        {
        if (name.Length() > length)
            {//If found name is a string containing aProcessName string.
            TChar c(name[length]);
            if (c.IsAlphaDigit() ||
                c == TChar('_') ||
                c == TChar('-'))
                {
                //RDebug::Print(_L(":: Process name: \"%S\".\n"), &name);
                continue;
                }
            }
        RProcess proc;
        if (proc.Open(name) == KErrNone)
            {
            RDebug::Print(_L("About to kill process \"%S\", This will force a reboot\n"), &name);
            proc.Kill(0);
            
            }
        proc.Close();
        }
    return KErrNone;
    }

/**
@SYMTestCaseID          PDS-SQL-CT-4164
@SYMTestCaseDesc        Tests for DEF144027: SQL Open returns error if the reported and actual file size are different
                        This function creates a corrupted jorunal file on a non-rugged FAT, where the actual and 
                        reported file size is different. This is done by killing the file server forcing the device to 
                        reset. After the reset t_sqlfilesrvcrash2.exe is used to verify SQL can handle this corrupted
                        journal file properly.
@SYMTestActions         DEF144027: SQL Open returns error if the reported and actual file size are different
@SYMTestExpectedResults Test must not fail
@SYMTestPriority        Medium
@SYMDEF                 DEF144027
                        DEF144238
*/
void DEF144027()
	{
    TInt err = TheDb.Create(KDbName, &KConfigStr);
    TEST2(err, KErrNone);
    
    err = TheDb.Exec(_L("CREATE TABLE t1(NUM INTEGER)"));
    TEST2(err, 1);
    err = TheDb.Exec(_L("CREATE TABLE t2(NUM INTEGER)"));
    TEST2(err, 1);
    
    err = TheDb.Exec(_L("INSERT INTO t1(NUM) VALUES (1)"));
    TEST2(err, 1);
    
    err = TheDb.Exec(_L("INSERT INTO t2(NUM) VALUES (1)"));
    TEST2(err, 1);
    
    TheDb.Close();
    
    err = TheDb.Open(KDbName);
    TEST2(err, KErrNone);
    
    err = TheDb.Exec(_L("BEGIN"));
    TEST(err >= 0);
    
    err = TheDb.Exec(_L("CREATE TABLE t3(NUM INTEGER)"));
    TEST2(err, KErrNone);
    
    err = TheDb.Exec(_L("INSERT INTO t1(NUM) VALUES (1)"));
    TEST2(err, 1);
    
    err = TheDb.Exec(_L("INSERT INTO t2(NUM) VALUES (1)"));
    TEST2(err, 1);
    
    err = TheDb.Exec(_L("INSERT INTO t3(NUM) VALUES (1)"));
    TEST2(err, 1);
    
   
    //Here the transaction is committed but the database is not close
    //this makes sure that the journal is truncated but not deleted 
    err = TheDb.Exec(_L("COMMIT"));
    TEST(err >= 0);
    
    //Reset the drive to rugged
    TBool setDriveToRugged = ETrue;
    SetDrive(setDriveToRugged);
    
    //When the file server is killed, the file size meta data will not be updated
    KillProcess(KFileSrvName);  
	}

void DoTests()
    {    
    TheTest.Start(_L(" @SYMTestCaseID:PDS-SQL-CT-4164 DEF144027: SQL Open returns error if the reported and actual file size are different"));
    DEF144027();    
    }
#endif //#if !defined __WINS__ && !defined __WINSCW__

TInt E32Main()
    {
    TheTest.Title();
    
    CTrapCleanup* tc = CTrapCleanup::New();
    
    __UHEAP_MARK;
    
#if !defined __WINS__ && !defined __WINSCW__
    CreateTestEnv();
    DeleteTestFiles();
    DoTests();
    TheFs.Close();
    TheTest.End();
#else
    TheTest.Start(_L("This test works only works on hardware!"));
    TheTest.End();
#endif  
 
    __UHEAP_MARKEND;
    
    TheTest.Close();
    
    delete tc;

    User::Heap().Check();
    return KErrNone;
    }
