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


#include "t_testsetup.h"
#include "t_testactionspec.h"
#include "t_input.h"
#include "t_certstoreactionmemfail.h"
#include "tcancel.h"
#include "t_message.h"
#include "tScriptSetup.h"
#include "Thardcodedsetup.h"
#include "t_testhandler.h"
#include "t_output.h"
#include "tTestSpec.h"
#include "ttesthandlersettings.h"
#include "testexecuteinterface.h"
#include "t_logger.h"
#include "t_testrunner.h"
#include "t_dummyconsole.h"

EXPORT_C HBufC* CTestSetup::GetArgument(TInt nPos)
	{
	// Get command line
	HBufC *argv = HBufC::NewLC(User::CommandLineLength());
	TPtr cmd(argv->Des());
	User::CommandLine(cmd);

	TLex arguments(cmd);
	TPtrC token;
	token.Set(KNullDesC);
	
	// finds nth parameter that doesnt have a - 
	while(nPos >= 0 && !arguments.Eos())
		{
		token.Set(arguments.NextToken());
		if(token.Length() > 0 && token[0] != '-')
			nPos--;
		}

	HBufC* result = token.AllocL();

	CleanupStack::PopAndDestroy(argv);

	return result;
	}

void CTestSetup::InitFileserverSessionLC(RFs& aFs)
	{
	User::LeaveIfError(aFs.Connect());
	CleanupClosePushL(aFs);
	
	// enable tests to pass file handles between processes
	aFs.ShareProtected();
	
	// Create the private directory, to fix failures in tests that write to
	// relative paths that were broken when the default path was changed to the
	// private path in build 03429
	TFileName privatePath;
	User::LeaveIfError(aFs.PrivatePath(privatePath));
	TInt err = aFs.MkDir(privatePath);
	if (err != KErrNone && err != KErrAlreadyExists)
		{
		User::Leave(err);
		}
	}

EXPORT_C void CTestSetup::CreateAndRunTestsL(TScriptTests theTestTypes[], 
		const TDesC& aScript, const TDesC& aLogFile, TBool aUseCommandLine, 
		CConsoleBase* aConsole, TBool* aResult)
{
    LOG(_L("-- Test handler starting")); 
 	
	RFs fs;

	InitFileserverSessionLC(fs);
	
	CScriptSetup* testSetup = CScriptSetup::NewLC(aConsole);
	CTestHandlerSettings* commandLineArgs = CTestHandlerSettings::NewLC();
 	CTestSpec* testSpec = CTestSpec::NewL();
	CleanupStack::PushL(testSpec);

	TTestSummary summary;
	if(testSetup->InitialiseL(fs, aScript, aLogFile, aUseCommandLine))
		{
		
		// Store the state of the heap and RFs resource count before the tests
		
		TInt initAllocCount = User::CountAllocCells();
		TInt initRFsCount = fs.ResourceCount();
		
		// Store the initial count of process and thread handles
		TInt initThreadHandleCount;
		TInt initProcessHandleCount;
		RThread().HandleCount(initProcessHandleCount, initThreadHandleCount);
		

		testSetup->LogFile().write(_L("State of the system before the tests:\n"));
		testSetup->LogFile().write(_L("\tRFs resource count: %d\n"), initRFsCount);
		testSetup->LogFile().write(_L("\tProcess handle count: %d\n"), initProcessHandleCount);
		testSetup->LogFile().write(_L("\tThread handle count: %d\n"), initThreadHandleCount);
			
		TRAPD(error, testSetup->SetupTestsL(fs, *testSpec, theTestTypes, *commandLineArgs))
			
		if(error==KErrNone)
			{
			CTestHandler* handler = CTestHandler::NewLC(fs, *testSpec, *commandLineArgs, 
														&testSetup->Console(),
														&testSetup->LogFile());
			handler->RunTestsL();
			summary = handler->Summary();
			testSpec->FreeAllTests();
			CleanupStack::PopAndDestroy(handler);
			
			// Do heap, RFs resource, process and thread handle balance checks	
			
			TInt finalRFsCount = fs.ResourceCount();
			
			testSetup->LogFile().write(_L("State of the system after the tests:\n"));
			testSetup->LogFile().write(_L("\tRFs resource count: %d\n"), finalRFsCount);

			if (initRFsCount == finalRFsCount)
				{
				testSetup->LogFile().write(_L("\nRFs resource count ok: %d final\n\n"),
										   finalRFsCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nRFs resource count inbalance: %d final\n\n"),
										   finalRFsCount);
				++summary.iTestsFailed;
				}
	

			TInt finalAllocCount = User::CountAllocCells();
			if (initAllocCount == finalAllocCount)
				{
				testSetup->LogFile().write(_L("\nHeap alloc count ok: %d final vs %d initial\n\n"),
										   finalAllocCount, initAllocCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nHeap alloc count inbalance: %d final vs %d initial\n\n"),
										   finalAllocCount, initAllocCount);
				++summary.iTestsFailed;
				}

			TInt finalThreadHandleCount;
			TInt finalProcessHandleCount;
			RThread().HandleCount(finalProcessHandleCount, finalThreadHandleCount);

			if (initProcessHandleCount == finalProcessHandleCount)
				{
				testSetup->LogFile().write(_L("\nProcess handle count ok: %d final vs %d initial\n\n"),
										   finalProcessHandleCount, initProcessHandleCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nProcess handle count imbalance: %d final vs %d initial\n\n"),
										   finalProcessHandleCount, initProcessHandleCount);
				++summary.iTestsFailed;
				}

			if (initThreadHandleCount == finalThreadHandleCount)
				{
				testSetup->LogFile().write(_L("\nThread handle count ok: %d final vs %d initial\n\n"),
										   finalThreadHandleCount, initThreadHandleCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nThread handle count imbalance: %d final vs %d initial\n\n"),
										   finalThreadHandleCount, initThreadHandleCount);
				++summary.iTestsFailed;
				}
			++summary.iTestsRun;
			}
		
		// Set the result if required by caller
		if (aResult)
			{
			*aResult = summary.AllTestsPassed();
			}

		summary.PrintL(testSetup->LogFile());
	
		// pauses runtime if command line requests it i.e. -w
		if(commandLineArgs->iWaitForKeyPressAtEnd)
			{
			testSetup->Console().Printf(_L("\nPress a key to quit"));
			testSetup->Console().Getch();
			}

		}

	CleanupStack::PopAndDestroy(4, &fs); // fs, testsetup, commandLineArgs and testspec
	// this MUST be the last thing to do incase objects being destructed on cleanup
	// have debug info
	//if (bTestSuccess)
	//	RDebug::RawPrint(_L("RTEST: SUCCESS : testhandler\n"));
}

EXPORT_C void CTestSetup::CreateAndRunTestsL(THardcodedTests theTestTypes[], const TDesC& aDefaultLog)
{
    LOG(_L("-- Test handler starting")); 
 
	RFs fs;

	InitFileserverSessionLC(fs);
	
	InitFileserverSessionLC(fs);
	
	CHardcodedSetup* testSetup = CHardcodedSetup::NewLC();
	CTestHandlerSettings* commandLineArgs = CTestHandlerSettings::NewLC();
 	CTestSpec* testSpec = CTestSpec::NewL();
	CleanupStack::PushL(testSpec);

	TTestSummary summary;
	if(testSetup->InitialiseL(fs, aDefaultLog))
		{
		
		// Store the state of the heap and RFs resource count before the tests
		
		TInt initAllocCount = User::CountAllocCells();
		TInt initRFsCount = fs.ResourceCount();
				
		TRAPD(error, testSetup->SetupTestsL(fs, *testSpec, theTestTypes, *commandLineArgs));
			
		if(error==KErrNone)
			{
			CTestHandler* handler = CTestHandler::NewLC(fs, *testSpec, *commandLineArgs, 
														&testSetup->Console(),
														&testSetup->LogFile());
			handler->RunTestsL();
			summary = handler->Summary();
			testSpec->FreeAllTests();
			CleanupStack::PopAndDestroy(handler);

			// Do heap and RFs resource balance checks
			
			TInt finalRFsCount = fs.ResourceCount();
			
			testSetup->LogFile().write(_L("State of the system after the tests:\n"));
			testSetup->LogFile().write(_L("\tRFs resource count: %d\n"), finalRFsCount);

			if (initRFsCount == finalRFsCount)
				{
				testSetup->LogFile().write(_L("\nRFs resource count ok: %d final\n\n"),
										   finalRFsCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nRFs resource count inbalance: %d final\n\n"),
										   finalRFsCount);
				++summary.iTestsFailed;
				}

			TInt finalAllocCount = User::CountAllocCells();
			if (initAllocCount == finalAllocCount)
				{
				testSetup->LogFile().write(_L("\nHeap alloc count ok: %d final vs %d initial\n\n"),
										   finalAllocCount, initAllocCount);
				}
			else
				{
				testSetup->LogFile().write(_L("\nHeap alloc count inbalance: %d final vs %d initial\n\n"),
										   finalAllocCount, initAllocCount);
				++summary.iTestsFailed;
				}
			++summary.iTestsRun;
			}

		summary.PrintL(testSetup->LogFile());

		// pauses runtime if command line requests it i.e. -w
		if(commandLineArgs->iWaitForKeyPressAtEnd)
			{
			testSetup->Console().Printf(_L("\nPress a key to quit"));
			testSetup->Console().Getch();
			}
		}

	CleanupStack::PopAndDestroy(4, &fs); // fs, testsetup, commandLineArgs and testspec
	
	//if (bTestSuccess)
	//	RDebug::RawPrint(_L("RTEST: SUCCESS : testhandler\n"));
}

//Check all flags is provided for understanability - Each grouping function called calls the next function
// if it has succeeded
TBool CTestSetup::CheckAllFlags(const CTestHandlerSettings& aCommandLineSettings, TInt& aScriptGroupings)
	{
	return CheckExhaustiveandSmoke(aCommandLineSettings,aScriptGroupings);
	}

TBool CTestSetup::CheckExhaustiveandSmoke(const CTestHandlerSettings& aCommandLineSettings, TInt& aScriptGroupings)
	{
	
	if (aCommandLineSettings.iExhaust || (aScriptGroupings & SMOKE))
		{
		//Do other flags
		return CheckSkipped(aCommandLineSettings, aScriptGroupings);
		}
	else

		return EFalse;
	}


TBool CTestSetup::CheckSkipped(const CTestHandlerSettings& aCommandLineSettings,
							   TInt& aScriptGroupings)
	{
	if (aCommandLineSettings.iSkip || !(aScriptGroupings & SKIP))
		{
		//Do other flags
		return CheckInteractive(aCommandLineSettings, aScriptGroupings);
		}
	else
		return EFalse;
	}

TBool CTestSetup::CheckInteractive(const CTestHandlerSettings& aCommandLineSettings,
								   TInt& aScriptGroupings)
	{
	if (aCommandLineSettings.iInt || !(aScriptGroupings & INTER))
		{
		//Do other flags
		return CheckOOMandCancel(aCommandLineSettings, aScriptGroupings);
		}
	else
		return EFalse;
	}

TBool CTestSetup::CheckOOMandCancel(const CTestHandlerSettings& aCommandLineSettings, 
									TInt& aScriptGroupings)
	{

	if ((aScriptGroupings & (EXOOM | INOOM)) == (EXOOM | INOOM))
		User::Panic(_L("Test is both Included and Excluded from OOM"), 1);

	if ((aScriptGroupings & (EXCANCEL | INCANCEL)) == (EXCANCEL | INCANCEL))
		User::Panic(_L("Test is both Included and Excluded from Cancel"), 1);

	if ((aScriptGroupings & (INOOM | INCANCEL)) == (INOOM | INCANCEL))
		User::Panic(_L("Test is in both OOM and Cancel groups"), 1);

	// Check to see whether -o set
	if (aCommandLineSettings.iOOM)
		{
		//Is the test in OOM group?
		if (aScriptGroupings & INOOM)
			return ETrue;
		else
		//Is the test excluded from OOM?
		if (aScriptGroupings & EXOOM)
			return EFalse;
		}
	else
	if (aScriptGroupings & INOOM)
			return EFalse;

	// Check to see whether -c set
	if (aCommandLineSettings.iCancel)
		{
		//Is the test in CANCEL group?
		if (aScriptGroupings & INCANCEL)
			return ETrue;
		else
		//Is the test excluded from CANCEL?
		if (aScriptGroupings & EXCANCEL)
			return EFalse;
		}
	else
	if (aScriptGroupings & INCANCEL)
			return EFalse;


	return ETrue;
	}


CTestSetup::~CTestSetup()
	{
	delete iLogFile;
	delete iTestConsole;
	if (iConsoleOwned)
		{
		// Note that the heap count when this is deleted must be the same as
		// when the console was allocated, otherwise a panic will occur.  This
		// is due to the techview implementation of the console doing a heap
		// mark in its destructor.
		delete iConsole;
		}
	}

CTestSetup::CTestSetup(CConsoleBase* aConsole) 
	: iConsole(aConsole), iConsoleOwned(aConsole == NULL)
	{
	}

void CTestSetup::ConstructL()
	{
	if (iConsoleOwned)
		{
		iConsole = Console::NewL(_L("Test code"), TSize(KDefaultConsWidth, KDefaultConsHeight));
		}

	// Currently the console passed to the test actions discards all output -
	// this is an attempt to make hardware tests run faster.  All information
	// should be written to the log file anyway, and I'd like to remove use of a
	// console as well as the log file.  -- jc
	iTestConsole = new (ELeave) CDummyConsole();
	}

void CTestSetup::OpenLogFileL(RFs &aFs, TInt nPos, const TDesC &aLogFile, TBool aUseCommandline)
	{
	HBufC* logFileName = NULL;

	if (aUseCommandline)
		{
		logFileName = GetArgument(nPos);
		CleanupStack::PushL(logFileName);
		
		// check if logfile was specified on command line
		if(logFileName->Length()==0)
			{
			// empty so remove it
			CleanupStack::PopAndDestroy(logFileName);
			logFileName = NULL;
			}
		}
	
	if(logFileName == NULL)
		{
		if(aLogFile.Length()==0)
			{
			PRINTANDLOG(_L("No log file specified on command line and no default given"));
			User::Leave(KErrArgument);
			}
		else
			{
			// there is a default copy that
			logFileName = aLogFile.AllocL();
			CleanupStack::PushL(logFileName);
			}
		}

	PRINTANDLOG1(_L("Log file: %S"), logFileName);
	
	RFile logFile;

	// attempts to create directories incase they dont exist
	aFs.MkDirAll(*logFileName); // ignore errors
 
	// write over any existing log
	User::LeaveIfError(logFile.Replace(aFs, *logFileName, EFileWrite));

	// output goes only to the log file
	iLogFile = new(ELeave) FileOutput(logFile);
	CleanupStack::PopAndDestroy(logFileName);
	};
