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