// Copyright (c) 2004-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 <csch_cli.h>
#include "Thelpers.h"

#include <e32base.h>
#include <e32test.h>
#include <f32file.h>
#include <s32file.h>
#include <e32property.h>
#include <schinfointernal.h>
#include "TestUtils.h"

//
// Literal constants
//
_LIT(KTestName,								"TC_TSCH_SCHSVR_OOM");


//
// Type definitions
//
typedef CArrayFixFlat<TScheduleEntryInfo>	CSchEntryInfoArray;
typedef CArrayFixFlat<TTaskInfo>			CTaskInfoArray;
typedef CArrayFixFlat<TSchedulerItemRef>    CSchItemRefArray;
typedef CArrayFixFlat<TTaskSchedulerCondition> CSchConditionArray;

//
// Global data
//
RTest										TheTest(KTestName);
static RScheduler							TheScheduler;
static CTrapCleanup*						TheCleanup;
static RFs									TheFsSession;

const TUid KTestUid = TUid::Uid(0x12345678);
const TInt KTestKey1 = 1;


//***********************************************************************************
static void GetTaskInfoL(CTaskInfoArray& aTaskInfoArray,
						TInt aScheduleId)
	// Extract schedule references from the schedule server based on a filter. If
	{
	aTaskInfoArray.Reset();
	TTime nextTimeScheduleIsDue;
	TScheduleState state;
	CSchEntryInfoArray* entries 
		= new (ELeave) CSchEntryInfoArray(3);
	CleanupStack::PushL(entries);
	TInt res = TheScheduler.GetScheduleL(aScheduleId, 
										state, 
										*entries, 
										aTaskInfoArray, 
										nextTimeScheduleIsDue);
	TEST2(res, KErrNone);
	CleanupStack::PopAndDestroy(entries);
	}

//***********************************************************************************
static TInt CountTasksL(TInt aScheduleId)	
	{
	CTaskInfoArray* tasks = new (ELeave) CTaskInfoArray(3);
	CleanupStack::PushL(tasks);
	GetTaskInfoL(*tasks, aScheduleId);
	TInt ret = tasks->Count();
	CleanupStack::PopAndDestroy(tasks);
	return ret;
	}

//***********************************************************************************
static TInt CountScheduledItemsL(TScheduleFilter aFilter, 
								RScheduler& aScheduler)
	// Extract schedule references from the schedule server based on a filter. If
	{
	CSchItemRefArray* refs = new (ELeave) CSchItemRefArray(3);
	CleanupStack::PushL(refs);

	TInt res = aScheduler.GetScheduleRefsL(*refs, aFilter);
	TEST2(res, KErrNone);

	TInt count = refs->Count();
	CleanupStack::PopAndDestroy(); // refs
	return count;
	}


//***********************************************************************************
static CSchEntryInfoArray* CreateScheduleArrayLC()
	{
	CSchEntryInfoArray* entryList = new (ELeave) CSchEntryInfoArray(3);
	CleanupStack::PushL(entryList);

	TScheduleEntryInfo entry1;
	entry1.iStartTime		= SchSvrHelpers::TimeBasedOnOffset(0, 20); // 20m from "now"
	entry1.iInterval		= 1;
	entry1.iIntervalType	= EDaily;
	entry1.iValidityPeriod	= 20;
	entryList->AppendL(entry1);
	
	return entryList;
	}

//***********************************************************************************
static CSchConditionArray* CreateConditionArrayLC()
	{
	CSchConditionArray* conditionList = new (ELeave) CSchConditionArray(3);
	CleanupStack::PushL(conditionList);

	TTaskSchedulerCondition condition1;
	condition1.iCategory = KTestUid;
	condition1.iKey		= KTestKey1;
	condition1.iState	= 10;
	condition1.iType	= TTaskSchedulerCondition::EEquals;
	conditionList->AppendL(condition1);
	
	return conditionList;
	}

/**
@SYMTestCaseID          SYSLIB-SCHSVR-CT-1027
@SYMTestCaseDesc	    Tests out of memory errors for time based API's
@SYMTestPriority 	    High
@SYMTestActions  	    Test for memory errors,while creating,enabling,disabling,editing and other operations on schedule task 
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/		
static void DoTest1L()
// Time based API's
	{     
	TInt err = KErrNone;
	TInt tryCount = 0;
	
	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SCHSVR-CT-1027 ===== Starting OOM test ===== "));
	
	CSchEntryInfoArray* scheduleArray = CreateScheduleArrayLC();
	TSchedulerItemRef ref1;

	TheTest.Next(_L("===== Testing create persistent schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		// These allocations are ignored because they cause corruptness
		// of the persistent store.  Its a bit rubbish.
		if(tryCount < 7 || tryCount >8 )
			{
			TheScheduler.__DbgFailNext(tryCount);
			TheScheduler.__DbgMarkHeap();
			err = TheScheduler.CreatePersistentSchedule(ref1, *scheduleArray);
			if (err==KErrNone)
				{
				TheScheduler.__DbgResetHeap();
				break;
				}
			TEST2(err, KErrNoMemory);		
			// reset server side heap for next iteration.
			TheScheduler.__DbgMarkEnd(0);			
			}
		}
	// Check schedule count
	TInt scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 

	// OK, now we have a persistent schedule
	TheTest.Next(_L("===== Testing disable schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.DisableSchedule(ref1.iHandle);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 

	TheTest.Next(_L("===== Testing enable schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.EnableSchedule(ref1.iHandle);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 

	TheTest.Next(_L("===== Testing edit schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		// Even though we use the same array as before, the task scheduler
		// code is not smart enough to just return but actually replaces
		// the existing one, hence we are actually testing this method fully.
		err = TheScheduler.EditSchedule(ref1.iHandle, *scheduleArray);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 

	TheTest.Next(_L("===== Testing delete schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.DeleteSchedule(ref1.iHandle);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 0); 

	// delete old files.
	// We need to do this because we have actually corrupted the store, 
	// even though its empty.  Without deleting it we get problems below.
	// This is a bit rubbish having to do this but what else can be done??
	TheTest.Next(_L("Delete old files"));
	SchSvrHelpers::DeleteScheduleFilesL();
	
	// OK now add back schedule in preparation for adding tasks.
	err = TheScheduler.CreatePersistentSchedule(ref1, *scheduleArray);
	TEST2(err, KErrNone);

	TheTest.Next(_L("===== Testing ScheduleTask ====="));
	
	_LIT(KTaskName, "TheTaskName");
	TName name = KTaskName();
	TTaskInfo taskInfo(0, name, 2, 0);

	HBufC* data = _L("the data").AllocLC();
	// cant test ScheduleTask API using OOM loop as it does dodgy things to 
	// the store which cause allocation problems.  Need to investigate this 
	// later at some stage.
	TEST2(TheScheduler.ScheduleTask(taskInfo, *data, ref1.iHandle), KErrNone);
	
	// Check schedule and task count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
	TInt taskCount = CountTasksL(ref1.iHandle);
	TEST(taskCount = 1);	

	TheTest.Next(_L("===== Testing GetScheduleRefsL ====="));

	CSchItemRefArray* refs = new (ELeave) CSchItemRefArray(3);
	CleanupStack::PushL(refs);
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetScheduleRefsL(*refs, EPendingSchedules);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule and task count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
	taskCount = CountTasksL(ref1.iHandle);
	TEST(taskCount = 1);	

	TheTest.Next(_L("===== Testing GetScheduleL ====="));

	CTaskInfoArray* tasks = new (ELeave) CTaskInfoArray(3);
	CleanupStack::PushL(tasks);
	TScheduleState state;
	TTime time;
	CSchEntryInfoArray* entryList = new (ELeave) CSchEntryInfoArray(3);
	CleanupStack::PushL(entryList);
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetScheduleL(ref1.iHandle,
										state,
										*entryList,
										*tasks, time);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule and task count
	TEST(entryList->Count() == 1);
	TEST(tasks->Count() == 1);
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
	taskCount = CountTasksL(ref1.iHandle);
	TEST(taskCount = 1);	

	CleanupStack::PopAndDestroy(entryList);
	CleanupStack::PopAndDestroy(tasks);

	TheTest.Next(_L("===== Testing GetTaskRefsL ====="));

	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetTaskRefsL(*refs,
										EAllSchedules,
										EAllTasks);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	CleanupStack::PopAndDestroy(refs);

	TheTest.Next(_L("===== Testing GetTaskDataSize ====="));

	TInt size;
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetTaskDataSize(ref1.iHandle,
										size);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}

	TheTest.Next(_L("===== Testing GetTaskInfoL ====="));

	HBufC* newData = HBufC::NewLC(size);
	TPtr pTaskData = newData->Des();

	TTaskInfo newTaskInfo;
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetTaskInfoL(taskInfo.iTaskId,
										newTaskInfo,
										pTaskData,
										ref1,
										time);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	TEST(newData->MatchF(*data) == 0);
	
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
	taskCount = CountTasksL(ref1.iHandle);
	TEST(taskCount = 1);	

	CleanupStack::PopAndDestroy(newData);
	CleanupStack::PopAndDestroy(data);

	TheTest.Next(_L("===== Testing GetScheduleTypeL ====="));

	TScheduleType type;
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetScheduleTypeL(ref1.iHandle, type);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	TEST(type == ETimeSchedule);

	CleanupStack::PopAndDestroy(scheduleArray);

	TheTest.Next(_L("===== Testing DeleteTask ====="));

	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.DeleteTask(taskInfo.iTaskId);
		//include test for KErrNotFound here as task is actually deleted and
		// then store is updated (which causes mem failure).  Problems
		// will still occur if you add a new task again and try and access store
		// as store is likely to still be corrupt. Investigate this??
		if (err==KErrNone || err==KErrNotFound) 
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
	taskCount = CountTasksL(ref1.iHandle);
	TEST(taskCount == 0);
	
	//Now delete schedule to setup for next test
	TEST2(TheScheduler.DeleteSchedule(ref1.iHandle), KErrNone);
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 0); 

	SchSvrHelpers::Pause(TheTest);
	}

/**
@SYMTestCaseID          SYSLIB-SCHSVR-CT-1028
@SYMTestCaseDesc	    Tests out of memory errors for Condition based API's
@SYMTestPriority 	    High
@SYMTestActions  	    Create a persistent schedule,get and delete schedule.
                        Check for memory and no errors.
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/		
static void DoTest2L()
// Condition based API's
	{	
	TInt err = KErrNone;
	TInt tryCount = 0;
	
	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SCHSVR-CT-1028 ===== Starting Condition OOM test ===== "));
	
	// delete old files.
	// We need to do this because we have actually corrupted the store, 
	// with the DeleteTask call.  Without deleting it we get problems below.
	// This is a bit rubbish having to do this but what else can be done??
	TheTest.Next(_L("Delete old files"));
	SchSvrHelpers::DeleteScheduleFilesL();

	//Ensure P&S variables are defined.
	err = RProperty::Define(KTestUid, KTestKey1, RProperty::EInt);
	TEST(err == KErrNone || err == KErrAlreadyExists);
	TEST2(RProperty::Set(KTestUid, KTestKey1,1), KErrNone);
	
	CSchConditionArray* conditionArray = CreateConditionArrayLC();
	TSchedulerItemRef ref1;

	TheTest.Next(_L("===== Testing create persistent schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.CreatePersistentSchedule(ref1, *conditionArray, Time::MaxTTime());
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);			
		}
	// Check schedule count
	TInt scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 

// This test code causes problems when EditSchedule fails, it deletes the entries 
// but doenst delete the actual CSchedule object.  We can't do this really as the 
// schedule may have oustanding tasks.  Need to clean up data in a consistent manner.
// Fix this! Same applied for time based EditSchedule
/*	TheTest.Next(_L("===== Testing edit schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		// Even though we use the same array as before, the task scheduler
		// code is not smart enough to just return but actually replaces
		// the existing one, hence we are actually testing this method fully.
		err = TheScheduler.EditSchedule(ref1.iHandle, *conditionArray,
										SchSvrHelpers::TimeBasedOnOffset(0, 20));
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 1); 
*/
	TheTest.Next(_L("===== Testing GetScheduleL ====="));

	CTaskInfoArray* tasks = new (ELeave) CTaskInfoArray(3);
	CleanupStack::PushL(tasks);
	TScheduleState state;
	TTime time;
	CSchConditionArray* entryList = new (ELeave) CSchConditionArray(3);
	CleanupStack::PushL(entryList);
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.GetScheduleL(ref1.iHandle,
										state,
										*entryList,
										time, *tasks);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	CleanupStack::PopAndDestroy(entryList);
	CleanupStack::PopAndDestroy(tasks);

	TheTest.Next(_L("===== Testing delete schedule ====="));
	for (tryCount = 0; ;++tryCount)
		{
		TheScheduler.__DbgFailNext(tryCount);
		TheScheduler.__DbgMarkHeap();
		err = TheScheduler.DeleteSchedule(ref1.iHandle);
		if (err==KErrNone)
			{
			TheScheduler.__DbgResetHeap();
			break;
			}
		TEST2(err, KErrNoMemory);		
		// reset server side heap for next iteration.
		TheScheduler.__DbgMarkEnd(0);
		}
	// Check schedule count
	scheduleCount = CountScheduledItemsL(EAllSchedules, TheScheduler);
	TEST(scheduleCount == 0); 

	CleanupStack::PopAndDestroy(conditionArray);

	SchSvrHelpers::Pause(TheTest);
	}
	
//***********************************************************************************
static TInt RunTestsL()
	{
	TheTest.Next(_L("Delete old files"));
	SchSvrHelpers::DeleteScheduleFilesL();
	
	TheTest.Next(_L("Create Task notification semaphore"));
	//initialise task notification semaphore
	STaskSemaphore sem;
	sem.CreateL();

	// Connect to the server
	TheTest.Next(_L("===== Connect to Scheduler ====="));
	TInt res = TheScheduler.Connect();
	TEST2(res, KErrNone);
	// Register a client with the server
	TheTest.Next(_L("===== Registering Client ====="));
	res = SchSvrHelpers::RegisterClientL(TheScheduler);
	TEST2(res, KErrNone);

	TheTest.Next(_L("Start tests"));
	DoTest1L();
	DoTest2L();
	
	// Need to add OOM tests for when a task is executed
	// to clarify behaviour.  This will be tricky!!!
	
	TheTest.Next(_L("Tidying up"));
	//Tidying up so next test will be clear.
	TheTest.Next(_L("Delete all schedules"));
	SchSvrHelpers::DeleteAllSchedulesL(TheScheduler);
	SchSvrHelpers::Pause(TheTest, 2);
	TheTest.Next(_L("Delete old files\n"));
	SchSvrHelpers::DeleteScheduleFilesL();

	TheScheduler.Close();
	
	//close handle to semaphore
	sem.Close();

	return KErrNone;
	}

//***********************************************************************************
GLDEF_C TInt E32Main()
//	
// TheTest the scheduler
//
    {
	__UHEAP_MARK;
	TheTest.Start(_L("OOM testing"));
	TheTest.Title();
	TheCleanup = CTrapCleanup::New();
	
	//If the previous test fails, SCHSVR.exe may stay in memory.
	TRAPD(error,CleanupHelpers::TestCleanupL());
	TEST2(error, KErrNone);

	TheTest(TheFsSession.Connect() == KErrNone);;
	TRAP(error, RunTestsL());	
	TEST2(error,KErrNone);
	TRAP(error,CleanupHelpers::TestCleanupL());
	TEST2(error, KErrNone);
	delete TheCleanup;
	
	TheFsSession.Close();
	TheTest.End();
	TheTest.Close();
	__UHEAP_MARKEND;
	return KErrNone;
	}
