// Copyright (c) 2002-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 "TEST.H"
#include <logview.h>

#undef test  //there is a "test" macro which hides "RTest test" declaration.

RTest test(_L("Log Change Notification Test Harness"));

_LIT(KTestRemoteParty1, "Remote Party");
_LIT(KTestDirection1, "Direction");
const TLogDurationType KTestDurationType1 = 1;
const TLogDuration KTestDuration1 = 0x1234;
_LIT(KTestStatus1, "Status");
_LIT(KTestSubject1, "Subject");
_LIT(KTestNumber1, "TheNumber");
const TLogContactItemId KTestContact1 = 0x1234;
const TLogLink KTestLink1 = 0x1234;
_LIT8(KTestData1, "ABCDEFGH");
const TLogFlags KTestFlags1 = 0x5;

/**
@SYMTestCaseID          SYSLIB-LOGENG-CT-0926
@SYMTestCaseDesc	    Tests for CLogClient::NotifyChange() function
@SYMTestPriority 	    High
@SYMTestActions  	    Tests for notification of changes to the logengine database.
                        Add events within the time intervals ,check for the notification of an event
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestNotificationL()
	{
	test.Next(_L(" @SYMTestCaseID:SYSLIB-LOGENG-CT-0926 "));
	CLogClient* client = CLogClient::NewL(theFs);
	CleanupStack::PushL(client);

	CTestActive* active1 = new(ELeave)CTestActive;
	CleanupStack::PushL(active1);

	CTestActive* active2 = new(ELeave)CTestActive;
	CleanupStack::PushL(active2);

	CLogEvent* event = CLogEvent::NewL();
	CleanupStack::PushL(event);

	event->SetEventType(KLogCallEventTypeUid);
	event->SetRemoteParty(KTestRemoteParty1);
	event->SetDirection(KTestDirection1);
	event->SetDurationType(KTestDurationType1);
	event->SetDuration(KTestDuration1);
	event->SetStatus(KTestStatus1);
	event->SetSubject(KTestSubject1);
	event->SetNumber(KTestNumber1);
	event->SetContact(KTestContact1);
	event->SetLink(KTestLink1);
	event->SetDataL(KTestData1);
	event->SetFlags(KTestFlags1);

	// *** Notification when change made - no delay
	active1->StartL();
	client->NotifyChange(0, active1->iStatus);

	// Add an event
	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	
	// Not sure which will finish first
	CActiveScheduler::Start();
	if (!active1->IsActive())
		{
		TEST(active2->IsActive());
		TEST(active1->iStatus.Int() >= 0);
		CActiveScheduler::Start();
		TEST2(active2->iStatus.Int(), KErrNone);
		}
	else
		{
		TEST2(active2->iStatus.Int(), KErrNone);
		CActiveScheduler::Start();
		TEST(active1->iStatus.Int() >= 0);
		}

	// Remember the time
	TTime before;
	before.UniversalTime();

	const TTimeIntervalMicroSeconds32 delay = 3000000;

	// *** Notification when change made - with long delay
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Add an event
	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	
	// We can be fairly certain that the function will complete first
	CActiveScheduler::Start();
	TEST2(active2->iStatus.Int(), KErrNone);
	TEST(active1->IsActive());
	CActiveScheduler::Start();
	TEST(active1->iStatus.Int() >= 0);

	User::After(1000000);

	// Check delay
	TTime after;
	after.UniversalTime();
	TEST(before + delay <= after);

	before.UniversalTime();

	// *** Notification when change made - with long delay - multiple changes
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Add an event
	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	
	// The function should complete
	CActiveScheduler::Start();
	TEST(active1->IsActive());
	TEST2(active2->iStatus.Int(), KErrNone);

	// Change the event
	active2->StartL();
	client->ChangeEvent(*event, active2->iStatus);
	
	// The function should complete
	CActiveScheduler::Start();
	TEST(active1->IsActive());
	TEST2(active2->iStatus.Int(), KErrNone);

	// The notification should complete
	CActiveScheduler::Start();
	TEST(active1->iStatus.Int() >= 0);

#ifdef __WINS__
	User::After(1000000);
#endif

	// Check delay
	after.UniversalTime();
	TEST(before + TTimeIntervalMicroSeconds32(delay) <= after);

	// *** Notification when change made - after long delay
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Wait around for a time equal to the delay
	CTestTimer* timer = CTestTimer::NewL();
	timer->After(delay);
	CActiveScheduler::Start();
	delete timer;

	// Add an event
	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	
	// Not sure which will finish first
	CActiveScheduler::Start();
	if (!active1->IsActive())
		{
		TEST(active2->IsActive());
		TEST(active1->iStatus.Int() >= 0);
		CActiveScheduler::Start();
		TEST2(active2->iStatus.Int(), KErrNone);
		}
	else
		{
		TEST2(active2->iStatus.Int(), KErrNone);
		CActiveScheduler::Start();
		TEST(active1->iStatus.Int() >= 0);
		}

	CleanupStack::PopAndDestroy(4); // event, active2, active1, client
	}

/**
@SYMTestCaseID          SYSLIB-LOGENG-CT-0927
@SYMTestCaseDesc	    Tests for CLogClient::NotifyChangeCancel() function
@SYMTestPriority 	    High
@SYMTestActions  	    Tests for cancelling of any outstanding notification requests for changes to log engine database
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestCancelNotificationL()
	{
	test.Next(_L(" @SYMTestCaseID:SYSLIB-LOGENG-CT-0927 "));
	CLogClient* client = CLogClient::NewL(theFs);
	CleanupStack::PushL(client);

	CTestActive* active1 = new(ELeave)CTestActive;
	CleanupStack::PushL(active1);

	// *** Cancel without delay
	active1->StartL();
	client->NotifyChange(0, active1->iStatus);
	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	const TInt delay = 3000000;

	// *** Cancel with delay	
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Wait around for a bit
	CTestTimer* timer = CTestTimer::NewL();
	timer->After(delay / 2);
	CActiveScheduler::Start();
	delete timer;

	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	// *** Cancel with long delay	
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Wait around for a bit
	timer = CTestTimer::NewL();
	timer->After(delay * 2);
	CActiveScheduler::Start();
	delete timer;

	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	// *** Cancel after change
	CLogEvent* event = CLogEvent::NewL();
	CleanupStack::PushL(event);
	event->SetEventType(KLogCallEventTypeUid);

	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	CTestActive* active2 = new(ELeave)CTestActive;
	CleanupStack::PushL(active2);

	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	CActiveScheduler::Start();
	TEST(active1->IsActive());
	TEST2(active2->iStatus.Int(), KErrNone);

	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	// *** Cancel after change and delay
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	// Wait around for a bit
	timer = CTestTimer::NewL();
	timer->After(delay / 2);
	CActiveScheduler::Start();
	delete timer;

	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	CActiveScheduler::Start();
	TEST(active1->IsActive());
	TEST2(active2->iStatus.Int(), KErrNone);

	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	// *** Cancel after change and delay
	active1->StartL();
	client->NotifyChange(delay, active1->iStatus);

	active2->StartL();
	client->AddEvent(*event, active2->iStatus);
	CActiveScheduler::Start();
	TEST(active1->IsActive());
	TEST2(active2->iStatus.Int(), KErrNone);

	// Wait around for a bit
	timer = CTestTimer::NewL();
	timer->After(delay / 2);
	CActiveScheduler::Start();
	delete timer;

	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(active1->iStatus.Int(), KErrCancel);

	// *** Cancel when not active
	client->NotifyChangeCancel();

	CleanupStack::PopAndDestroy(4); // active2, event, active, client
	}

/**
@SYMTestCaseID          SYSLIB-LOGENG-CT-0928
@SYMTestCaseDesc	    Tests for notification requests on view purge
@SYMTestPriority 	    High
@SYMTestActions  	    Notify changes on log view event
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void TestViewPurgeNotifyL()
	{
	test.Next(_L(" @SYMTestCaseID:SYSLIB-LOGENG-CT-0928 "));
	CTestTimer* timer = CTestTimer::NewL();
	CleanupStack::PushL(timer);

	CTestActive* active = new(ELeave)CTestActive();
	CleanupStack::PushL(active);

	CLogClient* client = CLogClient::NewL(theFs);
	CleanupStack::PushL(client);

	CLogViewEvent* view = CLogViewEvent::NewL(*client);
	CleanupStack::PushL(view);

	CLogFilter* filter = CLogFilter::NewL();
	CleanupStack::PushL(filter);

	CTestActive* notify = new(ELeave)CTestActive;
	CleanupStack::PushL(notify);

	// Start notifier
	notify->StartL();
	client->NotifyChange(1000000, notify->iStatus);

	// Wait for 10 seconds
	TEST(notify->IsActive());
	timer = CTestTimer::NewL();
	timer->After(7000000);
	CActiveScheduler::Start();
	delete timer;

	// Setup a view
	TEST(notify->IsActive());
	if (view->SetFilterL(*filter, active->iStatus))
		{
		active->StartL();
		CActiveScheduler::Start();
		TEST(view->CountL() > 0);
		}

	// Wait for 10 seconds
	TEST(notify->IsActive());
	timer = CTestTimer::NewL();
	timer->After(10000000);
	CActiveScheduler::Start();
	delete timer;

	// We shouldn't be notified because nothing has changed
	TEST(notify->IsActive());
	client->NotifyChangeCancel();
	CActiveScheduler::Start();
	TEST2(notify->iStatus.Int(), KErrCancel);

	CleanupStack::PopAndDestroy(6); // notify, client, filter, view, active, timer
	}

/**
INC045485 - AV20 Messaging App crashes when attempting to write New SMS

@SYMTestCaseID          SYSLIB-LOGENG-CT-0929
@SYMTestCaseDesc	    Tests for defect number INC045485L.
@SYMTestPriority 	    High
@SYMTestActions  	    Tests by adding a fax event type
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/
LOCAL_C void INC045485L()
	{
	test.Next(_L(" @SYMTestCaseID:SYSLIB-LOGENG-CT-0929 "));
	const TInt KEventCnt = 5;//test events count
	const TInt KSizeOfEventData = 400;//should be bigger than KLogSizeOfEventGuess constant
	//Create client, active, event
	CLogClient* client = CLogClient::NewL(theFs);
	CleanupStack::PushL(client);
	CTestActive* active = new (ELeave) CTestActive;
	CleanupStack::PushL(active);
	CLogEvent* event = CLogEvent::NewL();
	CleanupStack::PushL(event);
	//Add KEventCnt events
	for(TInt i=0;i<KEventCnt;++i)
		{
		event->SetId(0x13579 + i);
		event->SetEventType(KLogFaxEventTypeUid);
		TUint8 buf[KSizeOfEventData];
		Mem::Fill(buf, KSizeOfEventData, TChar('1' + i));
		TPtr8 ptr(buf, KSizeOfEventData, KSizeOfEventData);
		event->SetDataL(ptr);
		//
		active->StartL();
		client->AddEvent(*event, active->iStatus);
		CActiveScheduler::Start();
		TEST(!active->IsActive());
		client->Cancel();
		TEST2(active->iStatus.Int(), KErrNone);
		}
	//Create LogViewEvent, CLogFilter
	CLogViewEvent* view = CLogViewEvent::NewL(*client);
	CleanupStack::PushL(view);
	CLogFilter* filter = CLogFilter::NewL();
	CleanupStack::PushL(filter);
	filter->SetEventType(KLogFaxEventTypeUid);//the same as the type UID of added events
	//Filter the events
	if(view->SetFilterL(*filter, active->iStatus))
		{
		active->StartL();
		CActiveScheduler::Start();
		TEST(view->CountL() == KEventCnt);
		}
	//Visit the events
	TInt j = 0;
	TEST(view->FirstL(active->iStatus));
	do
		{
		active->StartL();
		CActiveScheduler::Start();
		TEST2(active->iStatus.Int(), KErrNone);//If the defect is not fixed this check fails with "KErrOverflow"
		const CLogEvent& e = view->Event();
		RDebug::Print(_L("View Entry[%d]: Id:%d, Type:%x\n"), j, e.Id(), e.EventType().iUid);
		const TDesC8& data = e.Data();
		TEST(data.Length() == KSizeOfEventData);
		//Touch the data.
		for(TInt k=0;k<KSizeOfEventData;++k)
			{
			TChar c = data[k];
			TEST(c >= TChar('1') && c < TChar('1' + KEventCnt));
			}
		++j;
		}
	while(view->NextL(active->iStatus));
	//Destroy filter, view, event, active, client
	CleanupStack::PopAndDestroy(filter);
	CleanupStack::PopAndDestroy(view);
	CleanupStack::PopAndDestroy(event);
	CleanupStack::PopAndDestroy(active);
	CleanupStack::PopAndDestroy(client);
	}

//DEF060381  Propagated: Receiving Chinese SMSes causes the battery to drain too fast 
static void DEF060381L()
	{
	const TInt KSizeOfEventData = 1025; //should be bigger than KLogSizeOfEventGuess constant

	//Create two clients. The first one will be used to send one message to the server,
	//which length is too big. The second client will be used to receive the message from
	//the server.
	//If the defect is not fixed, the function hangs and its return point is never reached.
	CLogClient* client1 = CLogClient::NewL(theFs);
	CleanupStack::PushL(client1);

	CLogClient* client2 = CLogClient::NewL(theFs);
	CleanupStack::PushL(client2);

	CTestActive* active = new (ELeave) CTestActive;
	CleanupStack::PushL(active);

	//Create one SMS event, which holds a block of data with size bigger than the default 
	//size of the client side buffer (KLogSizeOfEventGuess).
	CLogEvent* event = CLogEvent::NewL();
	CleanupStack::PushL(event);

	event->SetId(0x9991118);
	event->SetEventType(KLogShortMessageEventTypeUid);
	TUint8 buf[KSizeOfEventData];
	for(TInt i=0;i<KSizeOfEventData;++i)
		{
		buf[i] = static_cast <TUint8> (i % 256);
		}
	TPtr8 ptr(buf, KSizeOfEventData, KSizeOfEventData);
	event->SetDataL(ptr);

	//Send the created event to the server unsing the first client.
	active->StartL();
	client1->AddEvent(*event, active->iStatus);
	CActiveScheduler::Start();
	TEST(!active->IsActive());
	TEST2(active->iStatus.Int(), KErrNone);

	//Create a view using the second client.
	//Create a filter for SMS messages.
	CLogViewEvent* view = CLogViewEvent::NewL(*client2);
	CleanupStack::PushL(view);
	CLogFilter* filter = CLogFilter::NewL();
	CleanupStack::PushL(filter);
	filter->SetEventType(KLogShortMessageEventTypeUid);//the same as the type UID of added events
	//Filter the events
	if(view->SetFilterL(*filter, active->iStatus))
		{
		active->StartL();
		CActiveScheduler::Start();//Here the test fucntion hangs and never returns, if the defect is not fixed.
		TEST(view->CountL() == 1);
		}
	//Visit the events
	TInt j = 0;
	TEST(view->FirstL(active->iStatus));
	do
		{
		active->StartL();
		CActiveScheduler::Start();
		TEST2(active->iStatus.Int(), KErrNone);
		const CLogEvent& e = view->Event();
		RDebug::Print(_L("View Entry[%d]: Id:%d, Type:%x\n"), j, e.Id(), e.EventType().iUid);
		const TDesC8& data = e.Data();
		TEST(data.Length() == KSizeOfEventData);
		//Touch the data.
		for(TInt k=0;k<KSizeOfEventData;++k)
			{
			TUint8 c = data[k];
			TEST(c == static_cast <TUint8> (k % 256));
			}
		++j;
		}
	while(view->NextL(active->iStatus));

	CleanupStack::PopAndDestroy(filter);
	CleanupStack::PopAndDestroy(view);
	CleanupStack::PopAndDestroy(event);
	CleanupStack::PopAndDestroy(active);
	CleanupStack::PopAndDestroy(client2);
	CleanupStack::PopAndDestroy(client1);
	}

void doTestsL()
	{
	TestUtils::Initialize(_L("T_LOGNOTIFY"));
	TestUtils::DeleteDatabaseL();

	test.Start(_L("Notifications"));
	TestNotificationL();
	theLog.Write(_L8("Test 1 OK\n"));

	test.Next(_L("INC045485"));
	::INC045485L();

	test.Next(_L("Cancelling Notifications"));
	TestCancelNotificationL();
	theLog.Write(_L8("Test 2 OK\n"));

	test.Next(_L("Notify with View Purge"));
	TestViewPurgeNotifyL();
	theLog.Write(_L8("Test 3 OK\n"));

	test.Next(_L("DEF060381"));
	::DEF060381L();
	}
