kerneltest/e32test/bench/t_svr.cpp
author Slion
Tue, 08 Dec 2009 08:11:42 +0100
branchanywhere
changeset 19 f6d3d9676ee4
parent 0 a41df078684a
permissions -rw-r--r--
Trying to figure out how to implement my WINC like compatibility layer. Going the emulation way is probably not so smart. We should not use the kernel but rather hook native functions in the Exec calls.

// 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\bench\t_svr.cpp
// Overview:
// Tests and benchmarks the Client/Server architecture of the Symbian platform.
// The client and server run in different threads in the same process. 
// API Information:
// CSession2, CServer2, RSessionBase.
// Details:
// - Create and start a server thread. Verify results are as expected.
// - Open a connection with the server, verify arguments are passed to the server
// and back correctly.
// - Server can read and write messages to/from the client and return verify that 
// the results are as expected.
// - Send dummy messages that the server completes immediately and display how many 
// are completed per second.
// - Stop the server and verify the results are as expected.
// - Verify that the kernel does not crash by completing a message with an invalid 
// handle and verify the client is panicked with EBadMessageHandle.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__

#include <e32base.h>
#include <e32base_private.h>
#include <e32test.h>
#include <e32svr.h>
#include <hal.h>
#include <e32math.h>

#include "../mmu/mmudetect.h"

const TInt KHeapSize=0x2000;
const TInt KMajorVersionNumber=1;
const TInt KMinorVersionNumber=0;
const TInt KBuildVersionNumber=1;

_LIT(KServerName,"Display");

_LIT(KReadDesContents, "Testing read");
_LIT(KLengthDesContents, "What-ever");
_LIT(KWriteDesContents, "It worked!");
_LIT(KLocalWriteDesContents, "Local write");

enum TSpeedTest
	{
	ESpeedNull,
	ESpeedUnusedDes,
	ESpeedGetDesLength,
	ESpeedGetMaxDesLength,
	ESpeedReadDes,
	ESpeedWriteDes,
	};

_LIT(KTestNameNull,            "Null");
_LIT(KTestNameUnusedDes,       "UnusedDes");
_LIT(KTestNameGetDesLength,    "GetDesLength");
_LIT(KTestNameGetMaxDesLength, "GetMaxDesLength");
_LIT(KTestNameReadDes,         "ReadDes");
_LIT(KTestNameWriteDes,        "WriteDes");

_LIT(KLitLocal, 			   "Local");
_LIT(KLitRemote, 			   "Remote");

const TDesC* KSpeedTestNames[] =
	{
	&KTestNameNull,
	&KTestNameUnusedDes,
	&KTestNameGetDesLength,
	&KTestNameGetMaxDesLength,
	&KTestNameReadDes,
	&KTestNameWriteDes,
	};

class CMySession : public CSession2
	{
public:
	CMySession();
	~CMySession();
	void DisplayName(const RMessage2& aM);
	virtual void ServiceL(const RMessage2& aMessage);		 	 //pure virtual fns.
private:
	RArray<RMessagePtr2> iAsyncRequests;
	};

class CMyServer : public CServer2
	{
public:
	enum {EDisplay,ERead,EGetDesLength,EGetDesMaxLength,EWrite,ELocalWrite,EDupDes,ENull,ESimpleRead,ESimpleWrite,EStartAsync,ECompleteAsync,EStop};
public:
	CMyServer(TInt aPriority);
	static CMyServer* New(TInt aPriority);
	virtual CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const;//Overloading
	};

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

class RDisplay : public RSessionBase
	{
public:
	TInt Open(TInt aMessageSlots=0);
	TInt Display(const TDesC& aMessage);
	TInt Read(TInt aArgIndex);
	TInt Write(TInt aArgIndex);
	TInt LocalWrite(TInt aArgIndex);
	TInt TestDesLength(TInt aArgIndex);
	TInt Stop();
	TInt SpeedTest(TSpeedTest);
	TInt Echo(TInt aWhat, TInt a0, TInt a1, TInt a2, TInt a3);
	void StartAsync(TRequestStatus& aStatus);
	void CompleteAsync(TInt aIndex);
	TInt SendBlind();
	TVersion Version();
private:
	TInt SendMessage(TInt aMessage, TInt aArgIndex, TDesC* aDesArg, TInt8 aOffset = 0);
	TInt SendMessage(TInt aMessage, TInt aArgIndex, TDes* aDesArg, TInt8 aOffset = 0);
	TInt SendMessageDup(TInt aMessage, TInt aArgIndex, TInt aArgIndex2, TDes* aDesArgs);
	};

LOCAL_D RTest test(_L("T_SVR"));
LOCAL_D RTest testSvr(_L("T_SVR Server"));
LOCAL_D RTest testSpeedy(_L("T_SVR Speedy"));
LOCAL_D TRequestStatus speedTestStatus;
LOCAL_D RThread serverThread;
LOCAL_D RProcess serverProcess;

//
// Constructor
// 
CMySession::CMySession()
	{}

//
// Destructor
// 
CMySession::~CMySession()
	{
	// Call User::Exit so server shuts down when client disconnects
	User::Exit(KErrNone);
	}

void CMySession::DisplayName(const RMessage2& aM)
//
// Display the client's name.
//
	{

	RThread t;
	TInt r = aM.Client(t);
	testSvr(r==KErrNone);
	TFullName fn(t.FullName());
	t.Close();
	TBuf<256> text;
	r=aM.Read(0,text);
	testSvr(r==KErrNone);
	testSvr.Printf(_L("Session %S\n%S\n"), &fn, &text);
	}

CMyServer* CMyServer::New(TInt aPriority)
//
// Create a new CMyServer.
//
	{

	return new CMyServer(aPriority);
	}

CMyServer::CMyServer(TInt aPriority)
//
// Constructor.
//
	: CServer2(aPriority)
	{}

CSession2* CMyServer::NewSessionL(const TVersion& aVersion, const RMessage2&) const
//
// Create a new client for this server.
//
	{

	TVersion v(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
	if (!User::QueryVersionSupported(v,aVersion))
		User::Leave(KErrNotSupported);
	return(new(ELeave) CMySession());
	}

void CMySession::ServiceL(const RMessage2& aMessage)
//
// Handle messages for this server.
//
	{

	TInt f = aMessage.Function();
	if (f & 0x40000000)
		{
		TInt what = f & 0x3fffffff;
		TInt a0 = aMessage.Int0();
		TInt a1 = aMessage.Int1();
		TInt a2 = aMessage.Int2();
		TInt a3 = aMessage.Int3();
		switch (what)
			{
			case 0:
				aMessage.Complete(a0);
				return;
			case 1:
				aMessage.Complete(a1);
				return;
			case 2:
				aMessage.Complete(a2);
				return;
			case 3:
				aMessage.Complete(a3);
				return;
			case 4:
				aMessage.Complete(a0+a1+a2+a3);
				return;
			case 5:
				aMessage.Complete(a0*a0+a1*a1+a2*a2+a3*a3);
				return;
			default:
				break;
			}
		}

	TInt r=KErrNone;
	TBuf<0x10> b;
	TDes* des = NULL;

	TInt message = f & 0xff;
	TInt arg = (f >> 8) & 0xff;
	TInt8 offset = (TInt8)((f >> 16) & 0xff);

	switch (message)
		{
	case CMyServer::EDisplay:
		DisplayName(aMessage);
		break;
	case CMyServer::ERead:
		r=aMessage.Read(arg,b,offset);
		if (r==KErrNone && b!=KReadDesContents)
			r=KErrGeneral;
		break;
	case CMyServer::EGetDesLength:
		r=aMessage.GetDesLength(arg);
		break;
	case CMyServer::EGetDesMaxLength:
		r=aMessage.GetDesMaxLength(arg);
		break;
	case CMyServer::EWrite:
		r=aMessage.Write(arg,KWriteDesContents,offset);
		// Test descriptor length updated
		if (r == KErrNone && aMessage.GetDesLength(arg) != 10)
			r = KErrGeneral;
		// Test reading descriptor back again
		if (r == KErrNone)
			r = aMessage.Read(arg,b,offset);
		if (r==KErrNone && b!=KWriteDesContents)
			r = KErrGeneral;
		break;
	case CMyServer::ELocalWrite:
		switch(arg)
			{
			case 0:
				des = (TDes*)aMessage.Int0();
				break;
			case 1:
				des = (TDes*)aMessage.Int1();
				break;
			case 2:
				des = (TDes*)aMessage.Int2();
				break;
			case 3:
				des = (TDes*)aMessage.Int3();
				break;
			default:
				r = KErrGeneral;
			}
		if (r==KErrNone)
			{
			*des = KLocalWriteDesContents;
			r = aMessage.GetDesLength(arg) == 11 ? KErrNone : KErrGeneral;
			}
		if (r==KErrNone)
			r=aMessage.Read(arg,b,0);
		if (r==KErrNone && b!=KLocalWriteDesContents)
			r=KErrGeneral;
		break;
	case CMyServer::EDupDes:
		r=aMessage.Write(arg,KWriteDesContents);
		if (r == KErrNone && aMessage.GetDesLength(offset/* used to pass 2nd arg here*/) != 10)
			r = KErrGeneral;
		if (r == KErrNone)
			r = aMessage.Read(offset,b);
		if (r==KErrNone && b!=KWriteDesContents)
			r = KErrGeneral;
		break;
	case CMyServer::ENull:
		break;
	case CMyServer::ESimpleRead:
		r=aMessage.Read(arg,b);
		break;
	case CMyServer::ESimpleWrite:
		r=aMessage.Write(arg,KWriteDesContents);
		break;
	case CMyServer::EStartAsync:
		r=iAsyncRequests.Append(aMessage);
		if (r == KErrNone)
			return;  // don't complete message
		break;
	case CMyServer::ECompleteAsync:
		{
		TInt index = aMessage.Int0();
		if (iAsyncRequests.Count() <= index)
			r = KErrNotFound;
		else
			{
			RMessagePtr2 asyncMessage = iAsyncRequests[index];
			iAsyncRequests.Remove(index);
			asyncMessage.Complete(KErrNone);
			r=KErrNone;
			}
		}
		break;
	case CMyServer::EStop:
		CActiveScheduler::Stop();
		break;
	default:
		r=KErrNotSupported;
		}
	aMessage.Complete(r);
	}

void CMyActiveScheduler::Error(TInt anError) const
//
// Called if any Run() method leaves.
//
	{

	testSvr.Panic(anError,_L("CMyActiveScheduler::Error"));
	}

TInt RDisplay::Open(TInt aMessageSlots)
//
// Open the server.
//
	{

	return(CreateSession(KServerName,Version(),aMessageSlots));
	}

TInt RDisplay::Display(const TDesC& aMessage)
//
// Display a message.
//
	{

	TBuf<0x10> b(aMessage);
	return(SendReceive(CMyServer::EDisplay,TIpcArgs(&b)));
	}

TInt RDisplay::SendMessage(TInt aMessage, TInt aArgIndex, TDesC* aDesArg, TInt8 aOffset)
	{
	TInt f = aMessage | (aArgIndex << 8) | ((aOffset << 16) & 0x00ff0000);
	TIpcArgs args;
	args.Set(aArgIndex, aDesArg);
	return SendReceive(f, args);
	}

TInt RDisplay::SendMessage(TInt aMessage, TInt aArgIndex, TDes* aDesArg, TInt8 aOffset)
	{
	TInt f = aMessage | (aArgIndex << 8) | ((aOffset << 16) & 0x00ff0000);
	TIpcArgs args;
	args.Set(aArgIndex, aDesArg);
	return SendReceive(f, args);
	}

TInt RDisplay::SendMessageDup(TInt aMessage, TInt aArgIndex1, TInt aArgIndex2, TDes* aDesArg)
	{
	TInt f = aMessage | (aArgIndex1 << 8) | (aArgIndex2 << 16);
	TIpcArgs args;
	args.Set(aArgIndex1, aDesArg);
	args.Set(aArgIndex2, aDesArg);
	return SendReceive(f, args);
	}

TInt RDisplay::Read(TInt aArgIndex)
//
// Test RMessage2::Read
//
	{
	HBufC* buf = HBufC::New(0x10);
	test_NotNull(buf);
	*buf = KReadDesContents;

	TBufC<0x10> des1(KReadDesContents);
	TBuf<0x10> des2(KReadDesContents);
	TPtrC des3(des1);
	TPtr des4((TUint16*)des2.Ptr(), des2.Length(), des2.MaxLength());
	RBuf des5(buf);
	
	// test successful read
	test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, &des1));
	test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, &des2));
	test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, &des3));
	test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, &des4));
	test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, &des5));

	// test negative offset
	test_Equal(KErrArgument, SendMessage(CMyServer::ERead, aArgIndex, &des1, -1));

	// test bad descriptors
	if (HaveVirtMem())
		{
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, (TDesC*)0x30000000));

		RChunk chunk;
		const TInt KChunkSize = 4096;
		test_KErrNone(chunk.CreateLocal(KChunkSize, KChunkSize));
		test_Equal(KChunkSize, chunk.Size());

		TDesC* ptr = (TDesC*)(chunk.Base() + KChunkSize - 8);
		Mem::Copy(ptr, &des3, 8);
		test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 4);
		Mem::Copy(ptr, &des3, 4);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 12);
		Mem::Copy(ptr, &des4, 12);
		test_Equal(KErrNone, SendMessage(CMyServer::ERead, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 8);
		Mem::Copy(ptr, &des4, 8);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 4);
		Mem::Copy(ptr, &des4, 4);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, ptr));
		
		chunk.Close();
		
		((TUint32*)&des3)[1] = 0x00001000; // make descriptor point to invalid memory
		((TUint32*)&des4)[2] = 0x00001000; // make descriptor point to invalid memory
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, &des3));
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::ERead, aArgIndex, &des4));
		}

	delete buf;
	return KErrNone;
	}

TInt RDisplay::TestDesLength(TInt aArgIndex)
//
// Test RMessage2::GetDesLength and RMessage2::GetDesMaxLength
//
	{
	HBufC* buf = HBufC::New(0x10);
	test_NotNull(buf);
	*buf = KLengthDesContents;

	TBufC<0x10> des1(KLengthDesContents);
	TBuf<0x10> des2(KLengthDesContents);
	TPtrC des3(des1);
	TPtr des4((TUint16*)des2.Ptr(), des2.Length(), des2.MaxLength());
	RBuf des5(buf);
	
	// test GetDesLength
	test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, &des1));
	test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, &des2));
	test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, &des3));
	test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, &des4));
	test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, &des5));

	// test GetDesMaxLength
	test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, &des1));
	test_Equal(0x10, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, &des2));
	test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, &des3));
	test_Equal(0x10, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, &des4));
	test_Equal(0x10, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, &des5));

	// test bad descriptors
	if (HaveVirtMem())
		{
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesLength, aArgIndex, (TDesC*)0x30000000));

		RChunk chunk;
		const TInt KChunkSize = 4096;
		test_KErrNone(chunk.CreateLocal(KChunkSize, KChunkSize));
		test_Equal(KChunkSize, chunk.Size());

		TDesC* ptr = (TDesC*)(chunk.Base() + KChunkSize - 8);
		Mem::Copy(ptr, &des3, 8);
		test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 4);
		Mem::Copy(ptr, &des3, 4);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesLength, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 12);
		Mem::Copy(ptr, &des4, 12);
		test_Equal(9, SendMessage(CMyServer::EGetDesLength, aArgIndex, ptr));
		test_Equal(0x10, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 8);
		Mem::Copy(ptr, &des4, 8);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesLength, aArgIndex, ptr));
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, ptr));
		
		ptr = (TDesC*)(chunk.Base() + KChunkSize - 4);
		Mem::Copy(ptr, &des4, 4);
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesLength, aArgIndex, ptr));
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EGetDesMaxLength, aArgIndex, ptr));
		
		chunk.Close();
		}

	delete buf;
	return KErrNone;
	}

TInt RDisplay::Write(TInt aArgIndex)
//
// Get session to test CSession2::WriteL.
//
	{
	HBufC* buf = HBufC::New(0x10);
	test_NotNull(buf);

	TBufC<0x10> des1;
	TBuf<0x10> des2;
	TPtrC des3(des1);
	TPtr des4((TUint16*)des2.Ptr(), des2.Length(), des2.MaxLength());
	RBuf des5(buf);

	// test successful write
	test_Equal(KErrNone, SendMessage(CMyServer::EWrite, aArgIndex, &des2));
	test(des2 == KWriteDesContents);	
	test_Equal(KErrNone, SendMessage(CMyServer::EWrite, aArgIndex, &des4));
	test(des4 == KWriteDesContents);	
	test_Equal(KErrNone, SendMessage(CMyServer::EWrite, aArgIndex, &des5));
	test(des5 == KWriteDesContents);
	test(*buf == KWriteDesContents);

	// test buffer too short
	TBuf<1> small;
	test_Equal(KErrOverflow, SendMessage(CMyServer::EWrite, aArgIndex, &small));

	// test write to constant descriptors
	test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EWrite, aArgIndex, &des1));
	test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EWrite, aArgIndex, &des3));

	// test negative offset
	test_Equal(KErrArgument, SendMessage(CMyServer::EWrite, aArgIndex, &des2, -1));

	// test multiple instances of same descriptor
	for (TInt i = 0 ; i < 4 ; ++i)
		{
		if (i != aArgIndex)
			{
			des2.Zero();
			test_Equal(KErrNone, SendMessageDup(CMyServer::EDupDes, aArgIndex, i, &des2));
			test(des2 == KWriteDesContents);
			}
		}

	// test bad descriptors - do this last as it corrupts the descriptors.
	if (HaveVirtMem())
		{
		((TUint32*)&des3)[1] = 0x00001000; // make descriptor point to invalid memory
		((TUint32*)&des4)[2] = 0x00001000; // make descriptor point to invalid memory
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EWrite, aArgIndex, &des3));
		test_Equal(KErrBadDescriptor, SendMessage(CMyServer::EWrite, aArgIndex, &des4));
		}

	delete buf;
	return KErrNone;
	}

TInt RDisplay::LocalWrite(TInt aArgIndex)
	{
	// test local write to descriptor
	TBuf<0x10> des2;
	des2.Zero();
	test_Equal(KErrNone, SendMessage(CMyServer::ELocalWrite, aArgIndex, &des2));
	test(des2 == KLocalWriteDesContents);
	return KErrNone;
	}

void RDisplay::StartAsync(TRequestStatus& aStatus)
	{
	SendReceive(CMyServer::EStartAsync, TIpcArgs(), aStatus);
	}

void RDisplay::CompleteAsync(TInt aIndex)
	{
	test_KErrNone(SendReceive(CMyServer::ECompleteAsync, TIpcArgs(aIndex)));
	}

TInt RDisplay::SendBlind()
	{
	return Send(CMyServer::EStartAsync);
	}

TInt RDisplay::SpeedTest(TSpeedTest aTest)
	{
	TBuf<0x10> des(KReadDesContents);
	
	TInt count = 0;
	TInt r = KErrNone;
	switch (aTest)
		{
		case ESpeedNull:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::ENull, TIpcArgs());
				count++;
				}
			r = (r == KErrNone) ? count : KErrGeneral;
			break;
			
		case ESpeedUnusedDes:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::ENull, TIpcArgs(&des));
				count++;
				}
			r = (r == KErrNone) ? count : KErrGeneral;
			break;
			
		case ESpeedGetDesLength:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::EGetDesLength, TIpcArgs(&des));
				count++;
				}
			r = (r == 12) ? count : KErrGeneral;
			break;
			
		case ESpeedGetMaxDesLength:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::EGetDesMaxLength, TIpcArgs(&des));
				count++;
				}
			r = (r == 0x10) ? count : KErrGeneral;
			break;
			
		case ESpeedReadDes:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::ESimpleRead, TIpcArgs(&des));
				count++;
				}
			r = (r == KErrNone) ? count : KErrGeneral;
			break;
			
		case ESpeedWriteDes:
			while (speedTestStatus == KRequestPending)
				{
				r = SendReceive(CMyServer::ESimpleWrite, TIpcArgs(&des));
				count++;
				}
			r = (r == KErrNone) ? count : KErrGeneral;
			break;
			
		default:
			r = KErrArgument;
		}
	return r;
	}

TInt RDisplay::Stop()
//
// Stop the server.
//
	{

	return SendReceive(CMyServer::EStop, TIpcArgs());
	}

TVersion RDisplay::Version()
//
// Return the current version.
//
	{

	TVersion v(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
	return v;
	}

TInt RDisplay::Echo(TInt aWhat, TInt a0, TInt a1, TInt a2, TInt a3)
	{
	return SendReceive(0x40000000|aWhat, TIpcArgs(a0,a1,a2,a3));
	}

LOCAL_C TInt serverThreadEntryPoint(TAny*)
//
// The entry point for the producer thread.
//
	{
	RThread().SetPriority(EPriorityMore);

	CMyActiveScheduler* pR=new CMyActiveScheduler;
	testSvr(pR!=NULL);
	CActiveScheduler::Install(pR);
	
	CMyServer* pS=CMyServer::New(0);
	testSvr(pS!=NULL);
	
	TInt r=pS->Start(KServerName);
	testSvr(r==KErrNone);
	
	RThread::Rendezvous(KErrNone);
	RProcess::Rendezvous(KErrNone);
	
	CActiveScheduler::Start();
	
	delete pS;
	testSvr.Close();
	return(KErrNone);
	}

LOCAL_C TInt RunPanicThread(RThread& aThread)
	{
	TRequestStatus s;
	aThread.Logon(s);
	TBool jit = User::JustInTime();
	User::SetJustInTime(EFalse);
	aThread.Resume();
	User::WaitForRequest(s);
	User::SetJustInTime(jit);
	return s.Int();
	}

LOCAL_C TInt RogueThread1(TAny*)
	{
	// try to kill the kernel
	RMutex mutex;
	TPtrC* p=(TPtrC*)0x00001000; // make descriptor point to invalid memory
	mutex.CreateGlobal(*p,EOwnerProcess);	// this should panic the thread
	return KErrNone;
	}

class RMessageT : public RMessage2
	{
public:
	RMessageT(TLinAddr anAddr) { iFunction=0; iHandle=(TInt)anAddr; }
	};

LOCAL_C TInt RogueThread2(TAny*)
	{
	// try to kill the kernel
	RMessageT m(0x30000000);
	m.Complete(KErrNone);					// this should panic the thread
	return KErrNone;
	}

LOCAL_C TInt RogueThread3(TAny*)
	{
	// try to kill the kernel
	RMessageT m(0x80000000);
	m.Complete(KErrNone);					// this should panic the thread
	return KErrNone;
	}

LOCAL_C TInt RogueThread4(TAny*)
	{
	// try to kill the kernel
	RMessageT m(0x800fff00);				// this should be off the end of the kernel heap
	m.Complete(KErrNone);					// this should panic the thread
	return KErrNone;
	}

LOCAL_C void DisplayThreadExitInfo(const RThread& aThread)
	{
	TFullName fn=aThread.FullName();
	TExitType exitType=aThread.ExitType();
	TInt exitReason=aThread.ExitReason();
	TBuf<32> exitCat=aThread.ExitCategory();
	test.Printf(_L("Thread %S exited %d,%d,%S\n"),&fn,exitType,exitReason,&exitCat);
	}

LOCAL_C void RogueThreadTest()
	{
	test.Start(_L("Rogue thread tests"));
	
	RThread thread;
	TInt r;
	if (HaveVirtMem())
		{
		test.Next(_L("Rogue thread test 1"));
		r=thread.Create(_L("Rogue1"),RogueThread1,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
		test(r==KErrNone);
		RunPanicThread(thread);
		DisplayThreadExitInfo(thread);
		test(thread.ExitType()==EExitPanic);
		test(thread.ExitReason()==ECausedException);
		CLOSE_AND_WAIT(thread);
		}

	test.Next(_L("Rogue thread test 2"));
	r=thread.Create(_L("Rogue2"),RogueThread2,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	RunPanicThread(thread);
	DisplayThreadExitInfo(thread);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EBadMessageHandle);
	CLOSE_AND_WAIT(thread);

	test.Next(_L("Rogue thread test 3"));
	r=thread.Create(_L("Rogue3"),RogueThread3,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	RunPanicThread(thread);
	DisplayThreadExitInfo(thread);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EBadMessageHandle);
	CLOSE_AND_WAIT(thread);

	test.Next(_L("Rogue thread test 4"));
	r=thread.Create(_L("Rogue4"),RogueThread4,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	RunPanicThread(thread);
	DisplayThreadExitInfo(thread);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EBadMessageHandle);
	CLOSE_AND_WAIT(thread);
	test.End();
	}

class RMySession : public RSessionBase
	{
public:
	TInt Connect(RServer2 aSrv,TRequestStatus& aStat)
		{return CreateSession(aSrv,TVersion(),1,EIpcSession_Unsharable,0,&aStat);}
	void SendTestMessage(TRequestStatus& aStat)
		{SendReceive(0,TIpcArgs(1,2,3,4), aStat);}
	};

TInt BadServerThread(TAny*)
	{
	RServer2 srv;
	RMySession sess;
	TRequestStatus stat;
	RMessage2 msg;
	RMessage2* badMsg = 0;
	TInt r;
	
	// Test receiving connect message to bad address
	
	r = srv.CreateGlobal(KNullDesC);
	if (r != KErrNone)
		return r;
	r = sess.Connect(srv,stat);
	if (r != KErrNone)
		return r;
	srv.Receive(*badMsg);
	srv.Close();
	User::WaitForRequest(stat);
	if (stat != KErrServerTerminated)
		return KErrGeneral;
	sess.Close();

	// Test receiving normal message to bad address
	
	r = srv.CreateGlobal(KNullDesC);
	if (r != KErrNone)
		return r;
	r = sess.Connect(srv,stat);
	if (r != KErrNone)
		return r;
	srv.Receive(msg);
	msg.Complete(KErrNone);
	User::WaitForRequest(stat);
	if (stat != KErrNone)
		return KErrGeneral;
	sess.SendTestMessage(stat);
	srv.Receive(*badMsg);
	srv.Close();
	User::WaitForRequest(stat);
	if (stat != KErrServerTerminated)
		return KErrGeneral;
	sess.Close();

	return 23;
	}

void BadServerTest()
	{
	// This tests the current behaviour of RServer2::Receive when passed a dodgy RMessage2 pointer,
	// which is to ingore exceptions and not panic the server thread.

	RThread thread;
	TInt r=thread.Create(_L("BadServer"),BadServerThread,KDefaultStackSize,NULL,NULL);
	test(r==KErrNone);
	TRequestStatus status;
	thread.Logon(status);
	thread.Resume();
	User::WaitForRequest(status);
	test(thread.ExitType()==EExitKill);
	test(thread.ExitReason()==23);
	CLOSE_AND_WAIT(thread);
	}

void StartServerThread()
	{
	TInt r=serverThread.Create(_L("Server"),serverThreadEntryPoint,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	
	TRequestStatus status;
	serverThread.Rendezvous(status);
	serverThread.Resume();
	
	User::WaitForRequest(status);
	test(status == KErrNone);
	}

void WaitServerThreadDeath()
	{
	TRequestStatus status;
	serverThread.Logon(status);
	User::WaitForRequest(status);
	test(status == KErrNone);
	test(serverThread.ExitReason() == EExitKill);
	CLOSE_AND_WAIT(serverThread);
	}

void StartServerProcess()
	{
	TInt r=serverProcess.Create(_L("t_svr"),_L("slave"));
	test(r==KErrNone);

	TRequestStatus status;
	serverProcess.Rendezvous(status);
	serverProcess.Resume();

	User::WaitForRequest(status);
	test(status == KErrNone);
	}

void WaitServerProcessDeath()
	{
	TRequestStatus status;
	serverProcess.Logon(status);
	User::WaitForRequest(status);
	test(status == KErrNone);
	test(serverProcess.ExitReason() == EExitKill);
	CLOSE_AND_WAIT(serverProcess);
	}

void RunSpeedTest(RDisplay& aSess, TBool aLocal, TSpeedTest aTest)
	{
    User::After(300000);

	RTimer timer;
	test(timer.CreateLocal() == KErrNone);
	
    timer.After(speedTestStatus, 300000);
	TInt r = aSess.SpeedTest(aTest);
	User::WaitForRequest(speedTestStatus);
	test(r > KErrNone);

    timer.After(speedTestStatus, 3000000);
	TUint startCount = User::FastCounter();
	r = aSess.SpeedTest(aTest);
	TUint endCount = User::FastCounter();
	User::WaitForRequest(speedTestStatus);
	test(r > KErrNone);

	timer.Close();

	const TDesC* loc = aLocal ? &KLitLocal : &KLitRemote;
	const TDesC* name = KSpeedTestNames[aTest];
	
	TInt countFreq = 0;
	test(HAL::Get(HAL::EFastCounterFrequency, countFreq) == KErrNone);

	TBool fcCountsUp = 0;
	test(HAL::Get(HAL::EFastCounterCountsUp, fcCountsUp) == KErrNone);

	TInt countDiff = fcCountsUp ? (endCount - startCount) : (startCount - endCount);
	TReal elapsedTimeUs = (1000000.0 * countDiff) / countFreq;
	TReal time = elapsedTimeUs / r;
	
    test.Printf(_L("%S, %S, %f\n"), loc, name, time);
	}

void RunAllSpeedTests(TBool aLocal)
	{
	RDisplay t;
	test(t.Open() == KErrNone);
	
	RunSpeedTest(t, aLocal, ESpeedNull);
	RunSpeedTest(t, aLocal, ESpeedUnusedDes);
	RunSpeedTest(t, aLocal, ESpeedGetDesLength);
	RunSpeedTest(t, aLocal, ESpeedGetMaxDesLength);
	RunSpeedTest(t, aLocal, ESpeedReadDes);
	RunSpeedTest(t, aLocal, ESpeedWriteDes);
	
	t.Close();
	}

const TInt KMaxRequests = 20;
const TInt KSoakIterations = 1000;

void DoTestMultipleOutstandingRequests(RDisplay t)
	{
	TRequestStatus status[KMaxRequests];
	
	test.Start(_L("Test multiple async requests\n"));
	
	test.Next(_L("Test multiple async requests, complete in order\n"));
	TInt i;
	for (i = 0 ; i < KMaxRequests ; ++i)
		{
		t.StartAsync(status[i]);
		test_Equal(KRequestPending, status[i].Int());
		}
	for (i = 0 ; i < KMaxRequests ; ++i)
		{
		t.CompleteAsync(0);  // complete first remaining async request
		User::WaitForAnyRequest();
		test_KErrNone(status[i].Int());
		}

	test.Next(_L("Test multiple async requests, complete in reverse order\n"));
	for (i = 0 ; i < KMaxRequests ; ++i)
		{
		t.StartAsync(status[i]);
		test_Equal(KRequestPending, status[i].Int());
		}
	for (i = KMaxRequests - 1 ; i >= 0 ; --i)
		{
		t.CompleteAsync(i);  // complete last remaining async request
		User::WaitForAnyRequest();
		test_KErrNone(status[i].Int());
		}

	test.Next(_L("Soak test multiple async requests, complete in random order\n"));
	for (i = 0 ; i < KMaxRequests ; ++i)
		{
		t.StartAsync(status[i]);
		test_Equal(KRequestPending, status[i].Int());
		}
	for (TInt j = 0 ; j < KSoakIterations ; ++j)
		{
		// complete random async request
		i = Math::Random() % KMaxRequests;
		t.CompleteAsync(i);
		User::WaitForAnyRequest();
		
		// find which one got completed
		for (i = 0 ; i < KMaxRequests ; ++i)
			if (status[i] != KRequestPending)
				break;
		test(i < KMaxRequests);
		test_KErrNone(status[i].Int());
		
		// re-start request
		t.StartAsync(status[i]);
		test_Equal(KRequestPending, status[i].Int());

		if (j % 100 == 0)
			test.Printf(_L("."));
		}
	test.Printf(_L("\n"));
	for (i = 0 ; i < KMaxRequests ; ++i)
		{
		t.CompleteAsync(0);
		User::WaitForAnyRequest();
		}
	for (i = 0 ; i < KMaxRequests ; ++i)
		test_KErrNone(status[i].Int());

	test.End();
	}

void TestMultipleOutstandingRequests()
	{
	TRequestStatus status[2];

	test.Next(_L("Test zero async message slots\n"));
	RDisplay t;
	StartServerThread();
	test_KErrNone(t.Open(0));
	t.StartAsync(status[0]);
	User::WaitForAnyRequest();
	test_Equal(KErrServerBusy, status[0].Int());
	t.Close();
	WaitServerThreadDeath();

	test.Next(_L("Test one async request\n"));
	StartServerThread();
	test_KErrNone(t.Open(1));
	t.StartAsync(status[0]);
	t.StartAsync(status[1]);
	User::WaitForAnyRequest();
	test_Equal(KRequestPending, status[0].Int());
	test_Equal(KErrServerBusy, status[1].Int());
	User::After(1000);
	test_Equal(KRequestPending, status[0].Int());
	t.CompleteAsync(0);
	User::WaitForAnyRequest();
	test_KErrNone(status[0].Int());

	test.Next(_L("Test one async request, again\n"));
	t.StartAsync(status[0]);
	test_Equal(KRequestPending, status[0].Int());
	t.CompleteAsync(0);
	User::WaitForAnyRequest();
	test_KErrNone(status[0].Int());
	t.Close();
	WaitServerThreadDeath();

	test.Next(_L("Test multiple async requests using dedicated message slots\n"));
	StartServerThread();
	test_KErrNone(t.Open(KMaxRequests));
	DoTestMultipleOutstandingRequests(t);
	t.Close();
	WaitServerThreadDeath();

	test.Next(_L("Test multiple async requests using global pool\n"));
	StartServerThread();
	test_KErrNone(t.Open(-1));
	DoTestMultipleOutstandingRequests(t);
	t.Close();
	WaitServerThreadDeath();	
	}

void CheckNoOutstandingSignals()
	{
	RTimer timer;
	test_KErrNone(timer.CreateLocal());
	TRequestStatus status;
	timer.After(status, 1000);
	User::WaitForAnyRequest();
	test_KErrNone(status.Int());
	timer.Close();
	}

void TestBlindMessages()
	{
	test.Start(_L("Test sending blind messages to server"));
	CheckNoOutstandingSignals();
	
	RDisplay t;
	StartServerThread();
	test_KErrNone(t.Open(0));
	test_Equal(KErrServerBusy, t.SendBlind());
	t.Close();
	WaitServerThreadDeath();
	
	StartServerThread();
	test_KErrNone(t.Open(2));
	test_KErrNone(t.SendBlind());
	test_KErrNone(t.SendBlind());
	test_Equal(KErrServerBusy, t.SendBlind());
	t.CompleteAsync(0);
	test_KErrNone(t.SendBlind());
	test_Equal(KErrServerBusy, t.SendBlind());
	t.CompleteAsync(0);
	t.CompleteAsync(0);
	test_KErrNone(t.SendBlind());
	test_KErrNone(t.SendBlind());
	test_Equal(KErrServerBusy, t.SendBlind());
	t.CompleteAsync(0);
	t.CompleteAsync(0);
	t.Close();
	WaitServerThreadDeath();
	
	CheckNoOutstandingSignals();
	test.End();
	}

void RunCommonServerTests(TBool /*aSameProcess*/)
	{
	test.Start(_L("Connect to server"));
	RDisplay t;
	TInt r=t.Open();
	test(r==KErrNone);

	test.Next(_L("Test all args passed"));
	test(t.Echo(0,3,5,7,11)==3);
	test(t.Echo(1,3,5,7,11)==5);
	test(t.Echo(2,3,5,7,11)==7);
	test(t.Echo(3,3,5,7,11)==11);
	test(t.Echo(4,3,5,7,11)==26);
	test(t.Echo(5,3,5,7,11)==204);

	test.Next(_L("Send to server"));
	r=t.Display(_L("First message"));
	test(r==KErrNone);

	for (TInt i = 0 ; i < 4 ; ++i)
		{
		test.Start(_L("Testing passing descriptors"));
		test.Printf(_L("Descriptor passed as arg %d\n"), i);
		
		test.Next(_L("Read"));
		r=t.Read(i);
		test(r==KErrNone);

		test.Next(_L("GetDesLength, GetDesMaxLength"));
		r=t.TestDesLength(i);
		test(r==KErrNone);

		test.Next(_L("Write"));
		r=t.Write(i);
		test(r==KErrNone);

		/*
		This is now explicitly not supported! 
		if (aSameProcess)
			{
			test.Next(_L("Local write"));
			r=t.LocalWrite(i);
			test(r==KErrNone);
			}
		*/
		
		test.End();
		}

	test.Next(_L("Test RServer2::Receive to dodgy pointer"));
	BadServerTest();

	t.Close();

	test.End();
	}

void RunTests()
//
// Test timers.
//
    {
	test.Title();

	// Turn off evil lazy dll unloading
	RLoader l;
	test(l.Connect()==KErrNone);
	test(l.CancelLazyDllUnload()==KErrNone);
	l.Close();

	test.Start(_L("Running tests for server in remote process"));

	StartServerProcess();
	RunCommonServerTests(EFalse);
	WaitServerProcessDeath();
	
	test.Next(_L("Running tests for server in same process"));
	StartServerThread();
	RunCommonServerTests(ETrue);
	WaitServerThreadDeath();

	test.Next(_L("Running rogue thread test"));
	RogueThreadTest();
	
	test.Next(_L("Test multiple outstanding requests"));
	TestMultipleOutstandingRequests();
	
	test.Next(_L("Test sending blind async requests"));
	TestBlindMessages();	
	
#ifndef _DEBUG
	test.Next(_L("Running speed tests"));
    test.Printf(_L("Server process, Test name, Time (uS)\n"));
	
	StartServerProcess();
	RunAllSpeedTests(EFalse);
	WaitServerProcessDeath();
	
	StartServerThread();
	RunAllSpeedTests(ETrue);	
	WaitServerThreadDeath();
#endif

	test.End();
    }


GLDEF_C TInt E32Main()
	{
	if (User::CommandLineLength() == 0)
		{
		RunTests();
		return KErrNone;
		}
	else
		return serverThreadEntryPoint(NULL);
	}