kerneltest/e32test/bench/t_svr.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/bench/t_svr.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1313 @@
+// 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);
+	}