kerneltest/e32test/system/t_chnot.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/system/t_chnot.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,523 @@
+// 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:
+// e32test\system\t_chnot.cpp
+// Tests RChangeNotifier class
+// Overview:
+// Tests RChangeNotifier class
+// API Information:
+// RChangeNotifier
+// Details:
+// - Create a RChangeNotifier object and verify the logon status is 
+// as expected.
+// - Call the Logon and LogonCancel methods, verify results are as
+// expected.
+// - Test for the correct midnight crossover notifier results in a 
+// variety of situations: DST On, DST Off, various time offsets
+// and various dates.
+// - Test various locale changes and verify that the notifier response
+// is as expected.
+// - Test the notification of the death of a thread and check that
+// results are as expected. Check for normal exit, kill exit, 
+// terminate exit and panic exit.
+// Platforms/Drives/Compatibility:
+// All.
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+// 
+//
+
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include <e32hal.h>
+#include <e32svr.h>
+#include <u32hal.h>
+#include <e32def.h>
+#include <e32def_private.h>
+
+RTest test(_L("T_CHNOT"));
+
+RChangeNotifier notifier;
+
+void TestStat(const TRequestStatus& aStat, TInt aValue)
+	{
+	if (aStat.Int()!=aValue)
+		{
+		test.Printf(_L("Got %08x Expected %08x\n"),aStat.Int(),aValue);
+		test(0);
+		}
+	}
+
+void TestCreate()
+	{
+	notifier.Create();
+	TRequestStatus stat;
+	notifier.Logon(stat);
+
+	// Expect all except EChangesLowMemory
+	TUint expected =
+		EChangesLocale |
+		EChangesMidnightCrossover |
+		EChangesThreadDeath |
+		EChangesPowerStatus |
+		EChangesSystemTime |
+		EChangesFreeMemory |
+		EChangesOutOfMemory |
+		EChangesThrashLevel;
+	
+	test(stat==expected);
+	}
+
+void TestLogonLogoff()
+	{
+	TRequestStatus stat;
+	notifier.LogonCancel();
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	notifier.LogonCancel();
+	TestStat(stat,KErrCancel);
+	notifier.LogonCancel();
+	TestStat(stat,KErrCancel);
+	}
+
+void DoTestMidnight()
+	{
+	TTime time;
+	time.HomeTime();
+	TDateTime dateTime=time.DateTime();
+	dateTime.SetHour(23);
+	dateTime.SetMinute(59);
+	dateTime.SetSecond(58);
+	dateTime.SetMicroSecond(700000);
+	time=dateTime;
+	TRequestStatus stat;
+	TInt r=notifier.Logon(stat);
+	test(r==KErrNone);
+	TestStat(stat,KRequestPending);
+	test(User::SetHomeTime(time)==KErrNone);
+	time.HomeTime();
+	TDateTime dateTime2=time.DateTime();
+	User::WaitForRequest(stat);
+	TestStat(stat,EChangesSystemTime);
+	r=notifier.Logon(stat);
+	test(r==KErrNone);
+	User::WaitForRequest(stat);
+	time.HomeTime();
+	TestStat(stat,EChangesMidnightCrossover);
+	dateTime2=time.DateTime();
+	test(dateTime2.Second()==0);
+	test(dateTime2.Minute()==0);
+	test(dateTime2.Hour()==0);
+	if (dateTime2.Month()==dateTime.Month())
+		test(dateTime2.Day()==dateTime.Day()+1);
+	else
+		test(dateTime2.Day()==0);
+	time=dateTime;
+	r=notifier.Logon(stat);
+	test(r==KErrNone);
+	TestStat(stat,KRequestPending);
+	test(User::SetHomeTime(time)==KErrNone);
+	User::WaitForRequest(stat);
+	TestStat(stat,(EChangesSystemTime|EChangesMidnightCrossover));
+	time=dateTime2;
+	r=notifier.Logon(stat);
+	test(r==KErrNone);
+	TestStat(stat,KRequestPending);
+	test(User::SetHomeTime(time)==KErrNone);
+	User::WaitForRequest(stat);
+	TestStat(stat,(EChangesSystemTime|EChangesMidnightCrossover));
+
+	// Check that a change of secure time also triggers notification, even though the user time is unchanged
+	r = notifier.Logon(stat);
+	test(r == KErrNone);
+	TestStat(stat, KRequestPending);
+	if ((r = time.HomeTimeSecure()) == KErrNone)
+		r = User::SetHomeTimeSecure(time+TTimeIntervalSeconds(60));
+	if (r == KErrNone)
+		{
+		test(User::SetHomeTimeSecure(time) == KErrNone);
+		r = EChangesSystemTime;
+		}
+	else
+		{
+		RDebug::Printf("WARNING: Secure clock change test skipped because secure time could not be changed!");
+		notifier.LogonCancel();
+		r = KErrCancel;
+		}
+	User::WaitForRequest(stat);
+	TestStat(stat, r);
+	}
+
+void SetOffsetForMidnight(TTime time,TTimeIntervalSeconds offset)
+	{
+	test(User::SetHomeTime(time)==KErrNone);
+	User::SetUTCOffset(offset);
+// No longer need next line, as we now only get one notification
+//	User::After(999999);//So, if time has gone backwards, midnight crossover has been noticed
+	TRequestStatus stat;
+	notifier.Logon(stat);
+	User::WaitForRequest(stat);
+	test(stat.Int()&(EChangesSystemTime|EChangesLocale));
+	}
+
+void TestMidnightCrossover()
+	{
+	TTimeIntervalSeconds offset=User::UTCOffset();
+	TTime time;
+	time.HomeTime();
+	test.Start(_L("Normal"));
+	DoTestMidnight();
+	test.Next(_L("Now  offset 0"));
+	SetOffsetForMidnight(time,0);
+	DoTestMidnight();
+	test.Next(_L("Now  offset +30"));
+	SetOffsetForMidnight(time,30);
+	DoTestMidnight();
+	test.Next(_L("Now  offset -30"));
+	SetOffsetForMidnight(time,-30);
+	DoTestMidnight();
+	test.Next(_L("Now  offset +60"));
+	SetOffsetForMidnight(time,60);
+	DoTestMidnight();
+	test.Next(_L("Now  offset -60"));
+	SetOffsetForMidnight(time,-60);
+	DoTestMidnight();
+	test.Next(_L("Now  offset +120"));
+	SetOffsetForMidnight(time,120);
+	DoTestMidnight();
+	test.Next(_L("Now  offset -120"));
+	SetOffsetForMidnight(time,-120);
+	DoTestMidnight();
+//
+	TTime time1998=TDateTime(1998,EFebruary,2,3,4,5,6);
+	test.Next(_L("1998 offset 0"));
+	SetOffsetForMidnight(time1998,0);
+	DoTestMidnight();
+	test.Next(_L("1998 offset +30"));
+	SetOffsetForMidnight(time1998,30);
+	DoTestMidnight();
+	test.Next(_L("1998 offset -30"));
+	SetOffsetForMidnight(time1998,-30);
+	DoTestMidnight();
+	test.Next(_L("1998 offset +60"));
+	SetOffsetForMidnight(time1998,60);
+	DoTestMidnight();
+	test.Next(_L("1998 offset -60"));
+	SetOffsetForMidnight(time1998,-60);
+	DoTestMidnight();
+	test.Next(_L("1998 offset +120"));
+	SetOffsetForMidnight(time1998,120);
+	DoTestMidnight();
+	test.Next(_L("1998 offset -120"));
+	SetOffsetForMidnight(time1998,-120);
+	DoTestMidnight();
+//
+	TTime time1999=TDateTime(1999,EDecember,30,3,4,5,6);
+	test.Next(_L("1999 offset 0"));
+	SetOffsetForMidnight(time1999,0);
+	DoTestMidnight();
+	TTime now;
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset +30"));
+	SetOffsetForMidnight(time1999,30);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset -30"));
+	SetOffsetForMidnight(time1999,-30);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset +60"));
+	SetOffsetForMidnight(time1999,60);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset -60"));
+	SetOffsetForMidnight(time1999,-60);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset +120"));
+	SetOffsetForMidnight(time1999,120);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+	test.Next(_L("1999 offset -120"));
+	SetOffsetForMidnight(time1999,-120);
+	DoTestMidnight();
+	now.HomeTime();
+	test(now.DateTime().Year()==2000);
+	test(now.DateTime().Month()==EJanuary);
+//
+	TTime time2002=TDateTime(2002,EAugust,30,3,4,5,6);
+	test.Next(_L("2002 offset 0"));
+	SetOffsetForMidnight(time2002,0);
+	DoTestMidnight();
+	test.Next(_L("2002 offset +30"));
+	SetOffsetForMidnight(time2002,30);
+	DoTestMidnight();
+	test.Next(_L("2002 offset -30"));
+	SetOffsetForMidnight(time2002,-30);
+	DoTestMidnight();
+	test.Next(_L("2002 offset +60"));
+	SetOffsetForMidnight(time2002,60);
+	DoTestMidnight();
+	test.Next(_L("2002 offset -60"));
+	SetOffsetForMidnight(time2002,-60);
+	DoTestMidnight();
+	test.Next(_L("2002 offset +120"));
+	SetOffsetForMidnight(time2002,120);
+	DoTestMidnight();
+	test.Next(_L("2002 offset -120"));
+	SetOffsetForMidnight(time2002,-120);
+	DoTestMidnight();
+//
+	SetOffsetForMidnight(time,offset);
+	test.End();
+	}
+
+void TestLocaleChanges()
+	{
+	TRequestStatus stat;
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	TLocale	locale;
+	locale.Set();
+	User::WaitForRequest(stat);
+	TestStat(stat,EChangesLocale);
+	}
+
+void TestOffsetChanges()
+	{
+	TTimeIntervalSeconds oldOffset = User::UTCOffset();
+	User::SetUTCOffset(0);
+	
+	TRequestStatus stat;
+	TTime time;
+	time.HomeTime();
+	TDateTime dateTime=time.DateTime();
+	dateTime.SetHour(23);
+	dateTime.SetMinute(30);
+	dateTime.SetSecond(0);
+	dateTime.SetMicroSecond(0);
+	time=dateTime;
+	test(User::SetHomeTime(time)==KErrNone);
+	notifier.Logon(stat);
+	User::WaitForRequest(stat);
+	TestStat(stat,(EChangesSystemTime));
+
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	User::SetUTCOffset(3600);
+	User::WaitForRequest(stat);
+	TestStat(stat,(EChangesSystemTime|EChangesLocale|EChangesMidnightCrossover));
+	User::SetUTCOffset(oldOffset);
+	notifier.Logon(stat);
+	User::WaitForRequest(stat);
+	}
+
+const TInt retValue=65432;
+const TInt killValue=2081953;
+const TInt terminateValue=512123;
+const TInt panicValue=1257671;
+const TInt KHeapSize=0x200;
+
+TInt ThreadCode(TAny* aReturnImmetiateFlag)
+	{
+	if(!aReturnImmetiateFlag)
+		User::After(60000000); // wait a minute, (effectively forever as far as the test goes).
+	return retValue;
+	}
+
+void TestThreadDeath()
+	{
+	test.Start(_L("Normal Exit"));
+	RThread	thread;
+	TInt r=thread.Create(_L("T_CHNOT ThreadCode"),ThreadCode,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)ETrue);
+	test(r==KErrNone);
+	__KHEAP_MARK;
+	TRequestStatus threadStat;
+	thread.Logon(threadStat);
+	TRequestStatus stat;
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	thread.Resume();
+	User::WaitForRequest(stat);
+	TestStat(stat,EChangesThreadDeath);
+	test(threadStat==retValue);
+	test(thread.ExitReason()==retValue);
+	test(thread.ExitType()==EExitKill);
+	test(thread.ExitCategory()==_L("Kill"));
+	CLOSE_AND_WAIT(thread);
+	__KHEAP_MARKEND;
+
+	test.Next(_L("Kill"));
+	r=thread.Create(_L("T_CHNOT ThreadCode"),ThreadCode,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
+	test(r==KErrNone);
+	thread.Logon(threadStat);
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	thread.Resume();
+	thread.Kill(killValue);
+	User::WaitForRequest(stat);
+	TestStat(stat,EChangesThreadDeath);
+	test(threadStat==killValue);
+	test(thread.ExitReason()==killValue);
+	test(thread.ExitType()==EExitKill);
+	test(thread.ExitCategory()==_L("Kill"));
+	CLOSE_AND_WAIT(thread);
+
+	test.Next(_L("Terminate"));
+	r=thread.Create(_L("T_CHNOT ThreadCode"),ThreadCode,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
+	test(r==KErrNone);
+	thread.Logon(threadStat);
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	thread.Resume();
+	thread.Terminate(terminateValue);
+	User::WaitForRequest(stat);
+	TestStat(stat,EChangesThreadDeath);
+	test(threadStat==terminateValue);
+	test(thread.ExitReason()==terminateValue);
+	test(thread.ExitType()==EExitTerminate);
+	test(thread.ExitCategory()==_L("Terminate"));
+	CLOSE_AND_WAIT(thread);
+
+	test.Next(_L("Panic"));
+	r=thread.Create(_L("T_CHNOT ThreadCode"),ThreadCode,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
+	test(r==KErrNone);
+	thread.Logon(threadStat);
+	notifier.Logon(stat);
+	TestStat(stat,KRequestPending);
+	TBool justInTime=User::JustInTime(); 
+	User::SetJustInTime(EFalse); 
+	thread.Resume();
+	thread.Panic(_L("Testing panic"),panicValue);
+	User::WaitForRequest(stat);
+	User::SetJustInTime(justInTime); 
+	TestStat(stat,EChangesThreadDeath);
+	test(threadStat==panicValue);
+	test(thread.ExitReason()==panicValue);
+	test(thread.ExitType()==EExitPanic);
+	test(thread.ExitCategory()==_L("Testing panic"));
+	CLOSE_AND_WAIT(thread);
+	test.End();
+	}
+
+void TestCloseWhilstPending()
+	{
+	test_KErrNone(notifier.Create());
+	TRequestStatus stat;
+	test_KErrNone(notifier.Logon(stat));
+	User::WaitForRequest(stat);
+	test_KErrNone(notifier.Logon(stat));
+	notifier.Close();
+	test_Equal(KErrGeneral,stat.Int());
+	}
+
+void TestCloseAndCompleteRace()
+	{
+	RThread().SetPriority(EPriorityRealTime);
+
+	// setup notifier2
+	RChangeNotifier notifier2;
+	test_KErrNone(notifier2.Create());
+	TRequestStatus stat2;
+	test_KErrNone(notifier2.Logon(stat2));
+	User::WaitForRequest(stat2);
+	test_KErrNone(notifier2.Logon(stat2));
+
+	// setup notifier
+	test_KErrNone(notifier.Create());
+	TRequestStatus stat;
+	test_KErrNone(notifier.Logon(stat));
+	User::WaitForRequest(stat);
+	test_KErrNone(notifier.Logon(stat));
+
+	// create and kill a thread so notifiers get signaled
+	RThread	thread;
+	test_KErrNone(thread.Create(_L("T_CHNOT ThreadCode"),ThreadCode,KDefaultStackSize,KHeapSize,KHeapSize,NULL));
+	thread.Kill(0);
+
+	// wait for notifier2
+	User::WaitForRequest(stat2);
+
+	// as this thread is realtime priority, then (on unicore systems) it has preempted
+	// kernel supervisor thread after it completed 'notifier2' but before it completed
+	// 'notifier'. if we close both notifiers now we trigger a race conidition which
+	// previousely caused a null pointer dereference in the kernel...
+	notifier.Close();
+	notifier2.Close();
+
+	User::WaitForRequest(stat);
+	TInt result = stat.Int();
+
+	// expect KErrGeneral from closing notifier, or on SMP probably EChangesThreadDeath as
+	// the notifier had time to complete
+	const TInt numCpus = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0);
+	if(numCpus==1 || result!=EChangesThreadDeath)
+		test_Equal(KErrGeneral,result);
+
+	RThread().SetPriority(EPriorityNormal);
+	thread.Close();
+	}
+
+TInt E32Main()
+	{
+
+	User::After(1000000);//So WINS doesn't give an instant power-status change;
+	test.Start(_L("Create"));
+	TestCreate();
+
+	test.Next(_L("Close"));
+	notifier.Close();
+
+	test.Next(_L("Create"));
+	TestCreate();
+
+	test.Next(_L("Logon/Logoff"));
+	TestLogonLogoff();
+
+	test.Next(_L("Midnight crossover"));
+	TestMidnightCrossover();
+
+	test.Next(_L("Locale changes"));
+	TestLocaleChanges();
+
+	test.Next(_L("Offset changes"));
+	TestOffsetChanges();
+
+	test.Next(_L("Thread death"));
+	TestThreadDeath();
+
+	test.Next(_L("Close"));
+	notifier.Close();
+
+	test.Next(_L("Close whilst pending"));
+	TestCloseWhilstPending();
+
+	test.Next(_L("Race between Close and complete"));
+	TestCloseAndCompleteRace();
+
+	test.End();
+	return KErrNone;
+	}