kerneltest/e32test/active/t_messge.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 6 0173bcd7697c
child 39 2bb754abd467
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1995-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\active\t_messge.cpp
// Overview:
// Test the RMessage2 and RMessagePtr2 classes. 
// API Information:
// RMessage2, RMessagePtr2, RSessionBase, CSession2, CServer2
// Details:
// - Verify copy constructor and assignment operator of Rmessage2 is implemented correctly.
// - Verify default constructor, copy constructor and assignment operator of RMessagePtr2 
// is implemented correctly, construct RMessagePtr2 from pointer to RMessage2 and verify
// that it is constructed successfully.
// - Create client and server threads, open a session with specified number of message slots
// - Send integer message arguments from client and test that the server receives the
// same integers.
// - Send pointers as message arguments from client and test that the server receives the
// same pointers. 
// - Test Client() method of RMessage2 and verify it opened a handle to the client thread
// - Complete several messages using RMessage2::Complete and verify the client gets the
// error codes back correctly.
// - Complete several messages using RMessagePtr2::Complete and verify the client gets the
// error codes back correctly.
// - Check RMessage construction from RMessagePtr2 is successful.
// - Check creation of other sessions to the same server is successful.
// - Check the server return KErrServerBusy when client has more outstanding requests than
// available message slots.
// - Send stop signal and check that the server has stopped as expected.
// - Check the client and server have exited as expected.
// - Create client and server threads, open a connection to the server and verify that
// - Server is panicked when completing a message twice.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32std.h>
#include <e32std_private.h>
#include <e32test.h>
#include <e32ver.h>
#include <e32panic.h>

const TInt KHeapMinSize=0x1000;
const TInt KHeapMaxSize=0x1000;

_LIT(KServerName,"CTestServer");

class CTestServer : public CServer2
	{
public:
	IMPORT_C CTestServer(TInt aPriority);
	enum TPanicType{
		EInt0Error=1, EInt1Error, EInt2Error, EInt3Error,
		EPtr0Error, EPtr1Error, EPtr2Error, EPtr3Error,
		ECreateNameError, ENewSessionError,
		EClientError
		};
	static void Panic(TPanicType aReason);
protected:
	//override the pure virtual functions:
	IMPORT_C virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
	};


class CTestSession : public CSession2
	{
public:
	enum {EStop,ETestInt,ETestPtr,ETestClient,ETestComplete,ETestPtrComplete,ETestCompletePanic,ETestOtherSession,ETestCompleteAfter,ETestMessageConstruction, ETestRMessagePtr2LeavingInterface};
//Override pure virtual
	IMPORT_C virtual void ServiceL(const RMessage2& aMessage);
private:
	void TestInt(const RMessage2& aMessage);
	void TestPtr(const RMessage2& aMessage);
	void TestClient(const RMessage2& aMessage);
	void TestComplete(const RMessage2& aMessage);
	void TestPtrComplete(const RMessage2& aMessage);
	void TestCompletePanic();
	TInt TestMessageConstruction(const RMessage2& aMessage);
	TInt TestRMessagePtr2LeavingInterface(const RMessage2& aMessage);
	//	
	TInt count1;//initially ==0
	RMessage2 messages[5];//Used in TestComplete()
//
	TInt count2;//initially ==0
	RMessagePtr2 messagePtrs[5];//User in TestPtrComplete()
	};


class CMyActiveScheduler : public CActiveScheduler
	{
public:
	virtual void Error(TInt anError) const; //override pure virtual error function
	};


class RSession : public RSessionBase
	{
public:
	TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
		{
		return (SendReceive(aFunction, aPtr));
		}
	void PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr, TRequestStatus& aStatus)
		{
		SendReceive(aFunction, aPtr, aStatus);
		}
	TInt PublicCreateSession(const TDesC& aServer,TInt aMessageSlots)
		{
		return (CreateSession(aServer,User::Version(),aMessageSlots));
		}
	};

// CTestServer functions

CTestServer::CTestServer(TInt aPriority) 
//
// Constructor - sets name
//
	: CServer2(aPriority)
	{}

CSession2* CTestServer::NewSessionL(const TVersion& aVersion,const RMessage2& /*aMessage*/) const
//
// Virtual fn - checks version supported and creates a CTestSession
//
	{
	TVersion version(KE32MajorVersionNumber,KE32MinorVersionNumber,KE32BuildVersionNumber);
	if (User::QueryVersionSupported(version,aVersion)==EFalse)
		User::Leave(KErrNotSupported);
	CTestSession* newCTestSession = new CTestSession;
	if (newCTestSession==NULL)
		Panic(ENewSessionError);
	return(newCTestSession);
	}

RThread clientThread, serverThread;

void CTestServer::Panic(TPanicType aReason)	  //static function
	{
	clientThread.Kill(KErrNone);
	User::Panic(_L("CTestServer"),aReason);
	}

RSession session, otherSession;
RSemaphore sem;

const TInt KTestInt[] = {-3866,30566,0,200};

void CTestSession::TestInt(const RMessage2& aMessage)
//
// Tests to see that the correct Int0/1/2/3 have been received
//
	{
	if (aMessage.Int0()!=KTestInt[0])
		CTestServer::Panic(CTestServer::EInt0Error);
	if (aMessage.Int1()!=KTestInt[1])
		CTestServer::Panic(CTestServer::EInt1Error);
	if (aMessage.Int2()!=KTestInt[2])
		CTestServer::Panic(CTestServer::EInt2Error);
	if (aMessage.Int3()!=KTestInt[3])
		CTestServer::Panic(CTestServer::EInt3Error);
	}

const TAny* KTestPtr[]={&clientThread, &serverThread, &session, &sem};

void CTestSession::TestPtr(const RMessage2& aMessage)
//
// Tests to see that the correct Ptr0/1/2/3 have been received
//
	{
	if (aMessage.Ptr0()!=KTestPtr[0])
		CTestServer::Panic(CTestServer::EPtr0Error);
	if (aMessage.Ptr1()!=KTestPtr[1])
		CTestServer::Panic(CTestServer::EPtr1Error);
	if (aMessage.Ptr2()!=KTestPtr[2])
		CTestServer::Panic(CTestServer::EPtr2Error);
	if (aMessage.Ptr3()!=KTestPtr[3])
		CTestServer::Panic(CTestServer::EPtr3Error);
	}

void CTestSession::TestClient(const RMessage2& aMessage)
//
// Tests Client()
//
	{

	TFullName n=RProcess().Name();
	n+=_L("::Client Thread");
	RThread t;
	TInt r=aMessage.Client(t);
	if (r!=KErrNone)
		User::Invariant();
	if (t.FullName().CompareF(n)!=0)
		{
		clientThread.Kill(0);
		CTestServer::Panic(CTestServer::EClientError);
		}
	t.Close();
	}

void CTestSession::TestComplete(const RMessage2& aMessage)
//
// Stores messages up then Completes in reverse order 
//
	{
	messages[count1] = aMessage;
	if (++count1==5)
		for(count1=4; count1>=0; count1--)
			messages[count1].Complete(5-count1);  //Complete with different 'error messages'
	}

void CTestSession::TestPtrComplete(const RMessage2& aMessage)
//
// Stores messages up as RMessagePtrs then Completes in reverse order
// Also tests RMessage2::MessagePtr()
//
	{

	messagePtrs[count2] = aMessage;
	__ASSERT_ALWAYS(!messagePtrs[count2].IsNull(), User::Invariant());
	if (++count2==5)
		for(count2=4; count2>=0; count2--)
			{
			messagePtrs[count2].Complete(10-count2*2);
			__ASSERT_ALWAYS(messagePtrs[count2].IsNull(), User::Invariant());
			}
	}



TInt CTestSession::TestMessageConstruction(const RMessage2& aMessage)
	{
	RMessage2 m2((RMessagePtr2&)aMessage);
	if(m2!=aMessage)
		return KErrGeneral;
	return KErrNone;
	}

TInt CTestSession::TestRMessagePtr2LeavingInterface(const RMessage2& aMessage)
	{
	RThread thread1, thread2;
	TProcessPriority priority = EPriorityForeground;
	
	//Check that both Client and ClientL methods return the same thread and error code.
	if (aMessage.Client(thread1)) 
		return KErrGeneral;

	TRAPD(r1, aMessage.ClientL(thread2))
	if (KErrNone != r1)	
		{
		thread1.Close();
		return r1;
		}
	if (thread1.Id() != thread2.Id()) 
		{
		thread1.Close();
		thread2.Close();
		return KErrBadHandle;
		}

	//Check that both SetPriority & SetPriorityL methods return the same error code 
	r1 = aMessage.SetProcessPriority(priority);
	TRAPD(r2, aMessage.SetProcessPriorityL(priority))

	thread1.Close();
	thread2.Close();
	if (r1 != r2)
		return KErrGeneral;

	return KErrNone;
	}

void CTestSession::ServiceL(const RMessage2& aMessage)
//
// Virtual message-handler
//
	{
	TInt r=KErrNone;
	switch (aMessage.Function())
		{
		case EStop:
			CActiveScheduler::Stop();
			break;		
		case ETestInt:
			TestInt(aMessage);
			break;
		case ETestPtr:
			TestPtr(aMessage);
			break;
		case ETestClient:
			TestClient(aMessage);
			break;
		case ETestComplete:
			TestComplete(aMessage);
			return;
		case ETestPtrComplete:
			TestPtrComplete(aMessage);
			return;
		case ETestCompletePanic:
			aMessage.Complete(KErrNone);
			break;
		case ETestOtherSession:
			break;
		case ETestCompleteAfter:
			User::After(7000000);
			break;
		case ETestMessageConstruction:
			r=TestMessageConstruction(aMessage);
			break;
		case ETestRMessagePtr2LeavingInterface:
			r=TestRMessagePtr2LeavingInterface(aMessage);
			break;
		default:
			r=KErrNotSupported;

		}						  
 	aMessage.Complete(r);
	
	}

// CTestSession funtions

void CMyActiveScheduler::Error(TInt anError) const
//
// Virtual error handler
//
	{
	User::Panic(_L("CMyActiveScheduer::Error"), anError);
	}

#include <e32svr.h>

TInt ClientThread(TAny*)
//
// Passed as the first client thread - signals the server to do several tests
//
	{
	RTest test(_L("T_MESSGE...client"));
	
	test.Title(); 
	test.Start(_L("Wait for server to start"));
	sem.Wait();
	
	test.Next(_L("Create Session"));
	TInt r=session.PublicCreateSession(_L("CTestServer"),5);
	if (r!=KErrNone)
		User::Panic(_L("CreateSessn failure"),r);

	test.Next(_L("Signal to test Int0/1/2/3()"));
	r=session.PublicSendReceive(CTestSession::ETestInt,TIpcArgs(-3866,30566,0,200) );
	test(r==KErrNone);

	test.Next(_L("Signal to test Ptr0/1/2/3()"));
	
	r=session.PublicSendReceive(CTestSession::ETestPtr, TIpcArgs(&clientThread, &serverThread, &session, &sem));
	test(r==KErrNone);

	test.Next(_L("Signal to test Client()"));
	r=session.PublicSendReceive(CTestSession::ETestClient, TIpcArgs());
	test(r==KErrNone);

	test.Next(_L("Test RMessage2::Complete()"));
	TRequestStatus stat[7];
	for (r=0;r<4;r++)
		{
		session.PublicSendReceive(CTestSession::ETestComplete, TIpcArgs(), stat[r]);
		test(stat[r]==KRequestPending);
		}
	session.PublicSendReceive(CTestSession::ETestComplete, TIpcArgs(), stat[4]);
	User::WaitForRequest(stat[0]);
	for (r=0;r<5;r++)
		test(stat[r]==5-r);	 //Test the 'error messages' set by Complete()
	test.Next(_L("Test RMessagePtr2::Complete()"));
	for (r=0;r<4;r++)
		{
		session.PublicSendReceive(CTestSession::ETestPtrComplete, TIpcArgs(), stat[r]);
		test(stat[r]==KRequestPending);
		}
	session.PublicSendReceive(CTestSession::ETestPtrComplete, TIpcArgs(), stat[4]);
	User::WaitForRequest(stat[0]);
	for (r=0;r<5;r++)
		test(stat[r]==10-r*2);

	test.Next(_L("Test RMessage contruction from Ptr"));
	if(UserSvr::IpcV1Available())
		{
		r=session.PublicSendReceive(CTestSession::ETestMessageConstruction, TIpcArgs(111,222,333,444));
		test(r==KErrNone);
		}
	else
		{
		test.Printf(_L("NOT TESTED - IPC V1 is not supported by system"));
		}

	test.Next(_L("Test RMessagePtr leaving interface"));
	r=session.PublicSendReceive(CTestSession::ETestRMessagePtr2LeavingInterface, TIpcArgs());
	test(r==KErrNone);

	test.Next(_L("Try another session"));
	r=otherSession.PublicCreateSession(_L("CTestServer"),5);
	test(r==KErrNone);

	r=otherSession.PublicSendReceive(CTestSession::ETestOtherSession, TIpcArgs());
	test(r==KErrNone);
	
	test.Next(_L("Saturate server"));
	for(r=0;r<7;r++)
		{
		test.Printf(_L("Send %d\r"),r);
		session.PublicSendReceive(CTestSession::ETestCompleteAfter,TIpcArgs(),stat[r]);			
		if (r<5)
			test(stat[r]==KRequestPending);
		else
			test(stat[r]==KErrServerBusy);
		}
 	test.Printf(_L("\n"));
	for(r=0;r<5;r++)
		{
		test.Printf(_L("Wait %d\r"),r);
		User::WaitForRequest(stat[r]);
		test(stat[r]==KErrNone);
		}
 	test.Printf(_L("\n"));

	test.Next(_L("Signal to stop ActiveScheduler"));
	session.PublicSendReceive(CTestSession::EStop, TIpcArgs());	
	session.Close();
	otherSession.PublicSendReceive(CTestSession::EStop, TIpcArgs());
	otherSession.Close();
	test.Close();
	
	return (KErrNone);
	}

TInt ServerThread(TAny*)
//
// Passed as the server thread in 2 tests - sets up and runs CTestServer
//
	{
	RTest test(_L("T_MESSGE...server"));
	
	test.Title();
	test.Start(_L("Create and install ActiveScheduler"));
	CMyActiveScheduler* pScheduler = new CMyActiveScheduler;
	if (pScheduler==NULL)
		{
		clientThread.Kill(0);
		User::Panic(_L("CreateSched failure"),KErrNoMemory);
		}

	CActiveScheduler::Install(pScheduler);

	test.Next(_L("Creating and starting Server"));
	CTestServer* pServer = new CTestServer(0);
	if (pServer==NULL)
		{
		clientThread.Kill(0);
		User::Panic(_L("CreateServr failure"),KErrNoMemory);
		}

	TInt r=pServer->Start(KServerName);//Starting a CServer2 also Adds it to the ActiveScheduler
	if (r!=KErrNone)
		{
		clientThread.Kill(0);
		User::Panic(_L("StartServr failure"),r);
		}


	test.Next(_L("Start ActiveScheduler and signal to client"));
	test.Printf(_L("        There might be something going on beneath this window"));
	sem.Signal();
	CActiveScheduler::Start();
	test.Next(_L("Destroy ActiveScheduler"));
	delete pScheduler;
	delete pServer;

	test.Close();
		
	return (KErrNone);
	}

const TInt KTestPanic = 14849;

TInt PanicTestThread (TAny*)
//
// Passed as a thread entry - just calls RMessage2::Panic()
//
	{
	RMessage2 message;
	message.Panic(_L("Testing Panic"),KTestPanic);
	return(KErrNone);
	}

RTest test(_L("Main T_MESSGE test"));

TInt CompletePanicClientThread (TAny*)
//
// Passed as the second client thread entry - signals to server to call Complete() twice
//
	{
	sem.Wait();
	
	TInt r=session.PublicCreateSession(_L("CTestServer"),1);
	test(r==KErrNone);

	r=session.PublicSendReceive(CTestSession::ETestCompletePanic, TIpcArgs());
	test(r==KErrNone);

	session.PublicSendReceive(CTestSession::EStop, TIpcArgs());//panic should occur before this is serviced 
	return(KErrNone);
	}

void SimpleRMessage()
//
// Simple RMessage2 Tests - constructors and assignment
//
	{
	
	test.Start(_L("Default constructor"));
	RMessage2 message1;

	test.Next(_L("Copy constructor"));
	RMessage2 message2(message1);
	test(message1.Function()==message2.Function());
	test(message1.Int0()==message2.Int0());
	test(message1.Int1()==message2.Int1());
	test(message1.Int2()==message2.Int2());
	test(message1.Int3()==message2.Int3());

	test.Next(_L("Assignment operator"));
	RMessage2 message3;
	Mem::Fill(&message3,sizeof(message3),0xb9);	// first fill message 3 with rubbish
	message3=message1;
 	test(message1.Function()==message3.Function());
	test(message1.Int0()==message3.Int0());
	test(message1.Int1()==message3.Int1());
	test(message1.Int2()==message3.Int2());
	test(message1.Int3()==message3.Int3());

	test.End();
	}

void SimpleRMessagePtr()
//
// Simple RMessagePtr2 tests - constructors and assignment
//
	{
	test.Start(_L("Default constructor"));
	RMessagePtr2 messagePtr1;
	test(messagePtr1.IsNull());

	test.Next(_L("Copy constructor"));
	RMessagePtr2 messagePtr2(messagePtr1);
	test(messagePtr2.IsNull());

	test.Next(_L("Constructor from pointer to RMessage2"));
	RMessage2 message;
    messagePtr1= message;

	test.Next(_L("Assignment operator"));
	messagePtr1=messagePtr2;

	test.End();
	}


GLDEF_C TInt E32Main()
	{
	test.Title();
	
	test.Start(_L("Simple RMessage2 tests"));
	SimpleRMessage();

	test.Next(_L("Simple RMessagePtr2 tests"));
	SimpleRMessagePtr();

	test.Next(_L("Sending messages between two threads"));
	test.Start(_L("Create client and server threads"));
	clientThread.Create(_L("Client Thread"),ClientThread,KDefaultStackSize,KHeapMinSize,KHeapMaxSize,NULL);
	serverThread.Create(_L("Server Thread"),ServerThread,KDefaultStackSize,KHeapMinSize,KHeapMaxSize,NULL);
	
	test.Next(_L("Logon to the threads"));
	TRequestStatus clientStat,serverStat;	
	clientThread.Logon(clientStat);
	serverThread.Logon(serverStat);

	test.Next(_L("Start the threads"));
	sem.CreateLocal(0);
	clientThread.Resume();
	serverThread.Resume();
	
	test.Next(_L("Wait for the threads to stop"));
	if(clientStat==KRequestPending)
		User::WaitForRequest(clientStat);
	if(serverStat==KRequestPending)
		User::WaitForRequest(serverStat);
	switch (clientThread.ExitType())
		{
		case EExitKill:
			test.Printf(_L("  Client thread killed\n")); 
			break;
		case EExitTerminate:
			test.Printf(_L("!!Client thread terminated:"));
			test.Panic(clientThread.ExitCategory(), clientThread.ExitReason());
		case EExitPanic:
			test.Panic(_L("!!Client thread panicked:"));
			test.Panic(clientThread.ExitCategory(), clientThread.ExitReason());
		default:
			test.Panic(_L("!!Client thread did something bizarre"), clientThread.ExitReason());
		}
	switch (serverThread.ExitType())
		{
		case EExitKill:
			test.Printf(_L("  Server thread killed\n")); 
			break;
		case EExitTerminate:
			test.Printf(_L("!!Server thread terminated:"));
			test.Panic(serverThread.ExitCategory(), serverThread.ExitReason());
		case EExitPanic:
			test.Printf(_L("!!Server thread panicked:"));
			test.Panic(serverThread.ExitCategory(), serverThread.ExitReason());
			//
			// To catch a panic put a breakpoint in User::Panic() (in UCDT\UC_UNC.CPP).
			//
		default:
			test.Panic(_L("!!Server thread did something bizarre"), serverThread.ExitReason());
		}

	test.Next(_L("Close the threads"));
	CLOSE_AND_WAIT(serverThread);
	CLOSE_AND_WAIT(clientThread);
	test.End();

	TBool justInTime=User::JustInTime();
	
 	test.Next(_L("Check it Panics if you try to Complete a message twice"));
	test.Start(_L("Create client and server threads"));
	clientThread.Create(_L("Client Thread1"),CompletePanicClientThread,KDefaultStackSize,KHeapMinSize,KHeapMaxSize,NULL);
	serverThread.Create(_L("Server Thread"),ServerThread,KDefaultStackSize,KHeapMinSize,KHeapMaxSize,NULL);
	
	test.Next(_L("Logon to the threads"));
	clientThread.Logon(clientStat);
	serverThread.Logon(serverStat);

	test.Next(_L("Start the threads"));
	sem.CreateLocal(0);
	User::SetJustInTime(EFalse); 
	clientThread.Resume();
	serverThread.Resume();
	
	test.Next(_L("Wait for the threads to stop"));
	User::WaitForRequest(clientStat); //
	User::WaitForRequest(serverStat);
	User::SetJustInTime(justInTime); 
	test.Next(_L("Check the exit categories"));
	test(clientThread.ExitType()==EExitKill);
	test(clientThread.ExitCategory().Compare(_L("Kill"))==0);
	test(clientThread.ExitReason()==KErrNone);
		
	test(serverThread.ExitType()==EExitPanic);
	test(serverThread.ExitCategory().Compare(_L("USER"))==0);
	test(serverThread.ExitReason()==ETMesCompletion);

	test.Next(_L("Close the threads"));
	CLOSE_AND_WAIT(serverThread);
	CLOSE_AND_WAIT(clientThread);
	test.End();	  
  	
	test.End();
  
	return (KErrNone);
	}