Added fshell testexecute wrapper command. Fixed some uids.
authorTom Sutcliffe <thomas.sutcliffe@accenture.com>
Wed, 07 Jul 2010 16:07:08 +0100
changeset 5 9c58252c6740
parent 4 c061fa280d92
child 6 96d581d2147d
Added fshell testexecute wrapper command. Fixed some uids.
build/common/common.mmh
commands/group/bld.inf
commands/group/fshell_commands.iby
commands/testexecute/ProxyServer.cpp
commands/testexecute/ProxyServer.h
commands/testexecute/testexecute.cif
commands/testexecute/testexecute.cpp
commands/testexecute/testexecute.mmp
--- a/build/common/common.mmh	Sun Jul 04 16:06:46 2010 +0100
+++ b/build/common/common.mmh	Wed Jul 07 16:07:08 2010 +0100
@@ -515,9 +515,10 @@
 #define FSHELL_UID_LOCALDRIVE                 0x10286F6D
 #define FSHELL_UID_PIPSRUN                    0x102866DB
 #define FSHELL_UID_USB                        0x10286F6E
-#define FSHELL_UID_EXTRABTRACE                0x10286F6B
-#define FSHELL_UID_MEMSPY                     0x10286F6C
-#define FSHELL_UID_EXTRABTRACEK               0x10286F6D
+#define FSHELL_UID_EXTRABTRACE                0x10286F6F
+#define FSHELL_UID_MEMSPY                     0x10286F70
+#define FSHELL_UID_EXTRABTRACEK               0x10286F71
+#define FSHELL_UID_TESTEXECUTE                0x10286F72
 
 #else // Not FSHELL_PROTECTED_UIDS
 
@@ -645,9 +646,10 @@
 #define FSHELL_UID_LOCALDRIVE                 0xE0286F6D
 #define FSHELL_UID_PIPSRUN                    0xE02866DB
 #define FSHELL_UID_USB                        0xE0286F6E
-#define FSHELL_UID_EXTRABTRACE                0xE0286F6B
-#define FSHELL_UID_MEMSPY                     0xE0286F6C
-#define FSHELL_UID_EXTRABTRACEK               0xE0286F6D
+#define FSHELL_UID_EXTRABTRACE                0xE0286F6F
+#define FSHELL_UID_MEMSPY                     0xE0286F70
+#define FSHELL_UID_EXTRABTRACEK               0xE0286F71
+#define FSHELL_UID_TESTEXECUTE                0xE0286F72
 
 #endif // FSHELL_PROTECTED_UIDS
 
--- a/commands/group/bld.inf	Sun Jul 04 16:06:46 2010 +0100
+++ b/commands/group/bld.inf	Wed Jul 07 16:07:08 2010 +0100
@@ -37,6 +37,7 @@
 ..\fed\group\fed.cif                     z:\resource\cif\fshell\fed.cif
 ..\snake\snake.cif                       z:\resource\cif\fshell\snake.cif
 ..\iniedit\iniedit.cif                   z:\resource\cif\fshell\iniedit.cif
+..\testexecute\testexecute.cif           z:\resource\cif\fshell\testexecute.cif
 
 PRJ_MMPFILES
 ..\cat\cat.mmp
@@ -60,6 +61,7 @@
 ..\fed\group\fed.mmp
 ..\snake\snake.mmp
 ..\iniedit\iniedit.mmp
+..\testexecute\testexecute.mmp
 
 #ifdef FSHELL_CORE_SUPPORT_SWI
 PRJ_EXPORTS
--- a/commands/group/fshell_commands.iby	Sun Jul 04 16:06:46 2010 +0100
+++ b/commands/group/fshell_commands.iby	Wed Jul 07 16:07:08 2010 +0100
@@ -369,4 +369,7 @@
 FSHELL_COMMAND_INFO_FILE(fshell,top.cif)
 #endif
 
+FSHELL_EXECUTABLE_FILE(fshell_testexecute.exe)
+FSHELL_COMMAND_INFO_FILE(fshell,testexecute.cif)
+
 #endif // __FSHELL_COMMANDS_IBY__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/testexecute/ProxyServer.cpp	Wed Jul 07 16:07:08 2010 +0100
@@ -0,0 +1,433 @@
+// ProxyServer.cpp
+// 
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+#include "ProxyServer.h"
+#include <fshell/memoryaccess.h>
+
+#define LOG(x...)
+#define LOG8(x...)
+//#include <e32debug.h>
+//#define LOG(x...) RDebug::Print(x)
+//#define LOG8(x...) RDebug::Printf(x)
+
+//const TInt KShutdownProxy = 1000;
+//const TInt KPingProxy = 1001;
+
+NONSHARABLE_CLASS(CAsyncWaiter) : public CActive
+	{
+public:
+	CAsyncWaiter(RUnderlyingSession& aRealSession, const RMessage2& aOriginalMessage, const TIpcArgs& aArgs);
+	~CAsyncWaiter();
+	//void ForwardMessageL();
+
+protected:
+	void RunL();
+	void DoCancel();
+
+private:
+	RUnderlyingSession& iRealSession;
+	RMessage2 iMsg;
+	TIpcArgs iArgs;
+	};
+
+CProxyServer* CProxyServer::NewInSeparateThreadL(const TDesC& aServerToReplace, MMessageHandler* aHandler)
+	{
+	CProxyServer* self = new(ELeave) CProxyServer(aServerToReplace, aHandler);
+	CleanupStack::PushL(self);
+	self->ThreadConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CProxyServer::CProxyServer(const TDesC& aServerToReplace, MMessageHandler* aHandler)
+	: CServer2(0, ESharableSessions), iServerName(aServerToReplace), iHandler(aHandler)
+	{
+	// Name the server after tid and this pointer, should be unique enough
+	iRealServerName.AppendNum(RThread().Id(), EHex);
+	iRealServerName.Append('.');
+	iRealServerName.AppendNum((TInt)this, EHex);
+	}
+
+void CProxyServer::ThreadConstructL()
+	{
+	User::LeaveIfError(iServerThread.Create(iRealServerName, &StartServerThreadFunction, 8192, NULL, this));
+	TRequestStatus stat;
+	iServerThread.Rendezvous(stat);
+	if (stat == KRequestPending)
+		{
+		iServerThread.Resume();
+		}
+	else
+		{
+		iServerThread.Kill(stat.Int());
+		}
+	User::WaitForRequest(stat);
+	User::LeaveIfError(stat.Int());
+	}
+
+void CProxyServer::ConstructL()
+	{
+	iShutdownCallback = new(ELeave) CAsyncCallBack(CActive::EPriorityHigh);
+	iShutdownCallback->Set(TCallBack(&Shutdown, this));
+
+	User::LeaveIfError(iMemAccess.Open());
+	if (iRealServerName.Length() > iServerName.Length()) User::Leave(KErrTooBig); // Mem access doesn't like this
+
+	_LIT(KTempName, "TemporaryReallyLongServerNameThatLeavesUsSpaceToManuever");
+	StartL(KTempName);
+
+	TServerKernelInfoBuf buf;
+	TInt err = iMemAccess.GetObjectInfo(EServer, iServerName, buf);
+	User::LeaveIfError(err);
+	TUint8* realServer = buf().iAddressOfKernelObject;
+	err = iMemAccess.GetObjectInfo(EServer, KTempName, buf);
+	User::LeaveIfError(err);
+	TUint8* myServer = buf().iAddressOfKernelObject;
+
+	// Should really have some kind of swap operation here...
+	TBuf8<KMaxName> name8;
+	name8.Copy(iRealServerName);
+	iMemAccess.InPlaceObjectRename(EServer, realServer, name8);
+	name8.Copy(iServerName);
+	iMemAccess.InPlaceObjectRename(EServer, myServer, name8);
+	iProxying = ETrue;
+	}
+
+TInt CProxyServer::Shutdown(TAny* aSelf)
+	{
+	CProxyServer* self = static_cast<CProxyServer*>(aSelf);
+	delete self;
+	CActiveScheduler::Stop();
+	return 0;
+	}
+
+void CProxyServer::Destroy()
+	{
+	if (iShutdownCallback && iServerThread.Handle() && iServerThread.Id() != RThread().Id()) // Don't try shutting down the thread if the server is actually running in the main thread
+		{
+		TRequestStatus stat;
+		// This is far harder than it should be... need to make sure we don't reference any member vars after calling the shutdown callback
+		RThread serverThread;
+		Mem::Swap(&serverThread, &iServerThread, sizeof(RThread));
+		serverThread.Logon(stat);
+		iShutdownCallback->CallBack();
+		User::WaitForRequest(stat);
+		serverThread.Close();
+		}
+	else
+		{
+		delete this;
+		}
+	}
+
+CProxyServer::~CProxyServer()
+	{
+	if (iProxying)
+		{
+		TServerKernelInfoBuf buf;
+		TInt err = iMemAccess.GetObjectInfo(EServer, iRealServerName, buf);
+		if (!err)
+			{
+			TUint8* realServer = buf().iAddressOfKernelObject;
+			TBuf8<KMaxName> name8;
+			name8.Copy(iServerName);
+			iMemAccess.InPlaceObjectRename(EServer, realServer, name8);
+			// Temporarily both us and the real server will have name iServerName. When we finish destructing, we'll clean up and the system should be back in a sensible state
+			}
+		}
+	delete iShutdownCallback;
+	iServerThread.Close();
+	iMemAccess.Close();
+	}
+
+CSession2* CProxyServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const
+	{
+	CProxySession* res = new(ELeave) CProxySession();
+	CleanupStack::PushL(res);
+	res->ConstructL(iRealServerName, aVersion);
+	CleanupStack::Pop(res);
+	return res;
+	}
+
+
+void CProxyServer::ServerThreadRunL()
+	{
+	CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
+	CleanupStack::PushL(scheduler);
+	CActiveScheduler::Install(scheduler);
+	
+	ConstructL();
+	RThread::Rendezvous(KErrNone);
+
+	CActiveScheduler::Start();
+	CleanupStack::PopAndDestroy(scheduler);
+	}
+
+TInt CProxyServer::StartServerThreadFunction(TAny* aSelf)
+	{
+	TInt err = KErrNoMemory;
+	CTrapCleanup* cleanup = CTrapCleanup::New();
+	if (cleanup)
+		{
+		TRAP(err, static_cast<CProxyServer*>(aSelf)->ServerThreadRunL());
+		delete cleanup;
+		}
+	return err;
+	}
+
+MMessageHandler* CProxyServer::Handler() const
+	{
+	return iHandler;
+	}
+
+//
+
+void CProxySession::ConstructL(const TDesC& aServerName, const TVersion& aVersion)
+	{
+	User::LeaveIfError(iSession.CreateSession(aServerName, aVersion));
+	}
+
+CProxyServer& CProxySession::Server()
+	{
+	return *const_cast<CProxyServer*>(static_cast<const CProxyServer*>(CSession2::Server()));
+	}
+
+const CProxyServer& CProxySession::Server() const
+	{
+	return *static_cast<const CProxyServer*>(CSession2::Server());
+	}
+
+void CProxySession::ServiceL(const RMessage2 &aMessage)
+	{
+	MMessageHandler* handler = Server().Handler();
+	TBool handled = EFalse;
+	if (handler)
+		{
+		handled = handler->HandleMessageL(this, aMessage);
+		}
+
+	if (!handled)
+		{
+		ForwardUnhandledMessageL(aMessage);
+		}
+	}
+
+TInt Val(const RMessage2& aMessage, TInt aIndex)
+	{
+	switch (aIndex)
+		{
+	case 0:
+		return aMessage.Int0();
+	case 1:
+		return aMessage.Int1();
+	case 2:
+		return aMessage.Int2();
+	case 3:
+		return aMessage.Int3();
+	default:
+		return 0;
+		}
+	}
+
+void CleanupArgs(TAny* aArgs)
+	{
+	TIpcArgs& args = *static_cast<TIpcArgs*>(aArgs);
+	for (TInt i = 0; i < KMaxMessageArguments; i++)
+		{
+		if (args.iFlags & (TIpcArgs::EFlagDes<<(i*TIpcArgs::KBitsPerType)))
+			{
+			LOG8("+++ deleting %x", args.iArgs[i]);
+			delete (TAny*)args.iArgs[i];
+			}
+		}
+	args.iFlags = 0;
+	}
+
+void CProxySession::ForwardUnhandledMessageL(const RMessage2& aMessage)
+	{
+	TIpcArgs args;
+	CleanupStack::PushL(TCleanupItem(&CleanupArgs, &args));
+	
+	LOG(_L("Forwarding message fn=%d"), aMessage.Function());
+
+	for (TInt i = 0; i < KMaxMessageArguments; i++)
+		{
+		// For each arg, try and figure out if it's a descriptor and if so what type
+		TInt len = aMessage.GetDesLength(i);
+		if (len < 0)
+			{
+			// Not a descriptor
+			args.Set(i, Val(aMessage, i));
+			LOG(_L("Int arg %d: %d"), i, args.iArgs[i]);
+			}
+		else
+			{
+			// It's a descriptor, now see if it's 16-bit or 8-bit
+			TBuf<1> wbuf;
+			TInt err = aMessage.Read(i, wbuf); // This check only works if platsec enforcement is turned on (!) but fshell.bat was the only thing that generally ran with enforcement off, and I've fixed that
+			if (err == KErrNone)
+				{
+				// 16-bit
+				// Gaah where's HBuf when you need it
+				TPtr* buf = (TPtr*)User::AllocL(len*2 + sizeof(TPtr));
+				new(buf) TPtr((TUint16*)(buf+1), len);
+				aMessage.ReadL(i, *buf);
+
+				// Figure out if it's writeable by trying to write back what we just read
+				TBool writeable = (aMessage.Write(i, *buf) == KErrNone);
+
+				if (writeable)
+					{
+					LOG(_L("TDes16 arg %d: %S"), i, buf);
+					args.Set(i, buf);
+					}
+				else
+					{
+					LOG(_L("TDesC16 arg %d: %S"), i, buf);
+					args.Set(i, (const TDesC16*)buf);
+					}
+				}
+			else
+				{
+				// 8-bit
+				// Gaah where's HBuf when you need it
+				TPtr8* buf = (TPtr8*)User::AllocL(len + sizeof(TPtr8));
+				new(buf) TPtr8((TUint8*)(buf+1), len);
+				aMessage.ReadL(i, *buf);
+
+				// Figure out if it's writeable by trying to write back what we just read
+				TBool writeable = (aMessage.Write(i, *buf) == KErrNone);
+
+				if (writeable)
+					{
+					LOG8("TDes8 arg %d: %S", i, buf);
+					args.Set(i, buf);
+					}
+				else
+					{
+					LOG8("TDesC8 arg %d: %S", i, buf);
+					args.Set(i, (const TDesC8*)buf);
+					}
+				}
+			}
+		}
+	ForwardMessageArgsL(aMessage, args);
+	CleanupStack::Pop(&args); // ForwardMessageArgs takes ownership
+	}
+
+void CProxySession::ForwardMessageArgsL(const RMessage2& aMessage, const TIpcArgs& aArgs)
+	{
+	CAsyncWaiter* waiter = new(ELeave) CAsyncWaiter(iSession, aMessage, aArgs);
+	// That's all that's needed
+	}
+
+void CProxySession::Disconnect(const RMessage2 &aMessage)
+	{
+	iSession.Close();
+	//TODO Need to wait for any CAsyncWaiters to be completed? If so, do it here and defer the super call until that time
+	CSession2::Disconnect(aMessage);
+	}
+
+// CAsyncWaiter
+
+CAsyncWaiter::CAsyncWaiter(RUnderlyingSession& aRealSession, const RMessage2& aOriginalMessage, const TIpcArgs& aArgs)
+	: CActive(CActive::EPriorityStandard), iRealSession(aRealSession), iMsg(aOriginalMessage), iArgs(aArgs)
+	{
+	CActiveScheduler::Add(this);
+	LOG(_L("Sending to real server: fn=%d, args=%x,%x,%x,%x flags=%x"), aOriginalMessage.Function(), iArgs.iArgs[0], iArgs.iArgs[1], iArgs.iArgs[2], iArgs.iArgs[3], iArgs.iFlags);
+	iRealSession.SendReceive(aOriginalMessage.Function(), iArgs, iStatus);
+	SetActive();
+	}
+
+CAsyncWaiter::~CAsyncWaiter()
+	{
+	CleanupArgs(&iArgs);
+	}
+
+void CAsyncWaiter::RunL()
+	{
+	const TInt serverErr = iStatus.Int();
+	TInt writeErr = KErrNone;
+
+	// First check for any TDes args that we need to write back to the real client
+	for (TInt i = 0; i < KMaxMessageArguments; i++)
+		{
+		if ((iArgs.iFlags & (TIpcArgs::EFlagDes<<(i*TIpcArgs::KBitsPerType))) && !(iArgs.iFlags & (TIpcArgs::EFlagConst<<(i*TIpcArgs::KBitsPerType))))
+			{
+			if (iArgs.iFlags & (TIpcArgs::EFlag16Bit<<(i*TIpcArgs::KBitsPerType)))
+				{
+				TDes16* des = (TDes16*)iArgs.iArgs[i];
+				writeErr = iMsg.Write(i, *des);
+				LOG(_L("Writing back %S returned %d"), des, writeErr);
+				}
+			else
+				{
+				TDes8* des = (TDes8*)iArgs.iArgs[i];
+				writeErr = iMsg.Write(i, *des);
+				LOG8("Writing back %S returned %d", des, writeErr);
+				}
+
+			if (writeErr) break; // Who knows how the real server would have handled such a case
+			}
+		}
+
+	LOG(_L("Completing original request function %d with writeErr=%d serverErr=%d"), iMsg.Function(), writeErr, serverErr);
+	if (writeErr) iMsg.Complete(writeErr);
+	else iMsg.Complete(serverErr);
+
+	delete this; // Our work here is done
+	}
+
+void CAsyncWaiter::DoCancel()
+	{
+	// We never call Cancel on our waiters
+	}
+
+/*
+EXPORT_C TInt ShutdownProxyNotifier()
+	{
+	RDebugNotifier notifier;
+	TInt err = notifier.Connect();
+	if (err == KErrNotFound)
+		{
+		// Oh dear, !Notifier isn't running. Meaning we renamed it then crashed, probably. Try renaming the real one
+		RMemoryAccess::LoadDriver();
+		RMemoryAccess mem;
+		err = mem.Open();
+		if (err) return err;
+		TServerKernelInfoBuf buf;
+		TInt err = mem.GetObjectInfo(EServer, KRealNotifierServerName, buf);
+		if (!err)
+			{
+			TUint8* realServer = buf().iAddressOfKernelObject;
+			mem.InPlaceObjectRename(EServer, realServer, _L8("!Notifier"));
+			}
+		mem.Close();
+		return err;
+		}
+
+	err = notifier.ShutdownProxy();
+	if (err == KErrServerTerminated) err = KErrNone; // It's expected to get KErrServerTerminated, because we deliberately don't complete the message. This way the client is more likely to get the completion once the server has actually gone, and not slightly before
+	notifier.Close();
+
+	return err;
+	}
+
+EXPORT_C TBool NotifierProxyIsRunning()
+	{
+	RDebugNotifier notifier;
+	TInt err = notifier.Connect();
+	if (err) return EFalse; // Not even original notifier is running?!
+	err = notifier.PingProxy();
+	notifier.Close();
+	return err == KErrNone; // The real proxy will return KErrNotSupported in this scenario
+	}
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/testexecute/ProxyServer.h	Wed Jul 07 16:07:08 2010 +0100
@@ -0,0 +1,82 @@
+// ProxyServer.h
+// 
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+#ifndef PROXYSERVER_H
+#define PROXYSERVER_H
+
+#include <fshell/memoryaccess.h>
+#include <e32base.h>
+
+class CProxySession;
+
+class MMessageHandler
+	{
+public:
+	virtual TBool HandleMessageL(CProxySession* aSession, const RMessage2& aMessage)=0;
+	};
+
+NONSHARABLE_CLASS(CProxyServer) : public CServer2
+	{
+public:
+	static CProxyServer* NewInSeparateThreadL(const TDesC& aServerToReplace, MMessageHandler* aHandler=NULL);
+	void Destroy(); // This should be used instead of delete - the server object has to be destroyed from the thread in which it was started
+	MMessageHandler* Handler() const;
+
+protected:
+	CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const;
+
+private:
+	~CProxyServer();
+	CProxyServer(const TDesC& aServerToReplace, MMessageHandler* aHandler);
+	void ConstructL();
+	void ThreadConstructL();
+	static TInt StartServerThreadFunction(TAny* aSelf);
+	void ServerThreadRunL();
+	static TInt Shutdown(TAny* aSelf);
+
+private:
+	RMemoryAccess iMemAccess;
+	TBool iProxying;
+	TName iServerName;
+	TName iRealServerName;
+	RThread iServerThread;
+	MMessageHandler* iHandler;
+	CAsyncCallBack* iShutdownCallback;
+	};
+
+class RUnderlyingSession : public RSessionBase
+	{
+public:
+	friend class CProxySession;
+	friend class CAsyncWaiter;
+	};
+
+NONSHARABLE_CLASS(CProxySession) : public CSession2
+	{
+public:
+	void ConstructL(const TDesC& aServerName, const TVersion& aVersion);
+	void ForwardUnhandledMessageL(const RMessage2& aMessage);
+	void ForwardMessageArgsL(const RMessage2& aMessage, const TIpcArgs& aArgs);
+
+protected:
+	void ServiceL(const RMessage2 &aMessage);
+	void Disconnect(const RMessage2 &aMessage);
+
+private:
+	CProxyServer& Server();
+	const CProxyServer& Server() const;
+
+private:
+	RUnderlyingSession iSession;
+	};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/testexecute/testexecute.cif	Wed Jul 07 16:07:08 2010 +0100
@@ -0,0 +1,70 @@
+# testexecute.cif
+# 
+# Copyright (c) 2010 Accenture. All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the "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:
+# Accenture - Initial contribution
+#
+==name testexecute
+
+==short-description
+
+Fshell wrapper for testexecute.exe.
+
+==long-description
+
+An fshell exe that wraps up a Test Execute script run and redirects its output to the fshell console.
+
+==option bool d debug
+
+Used to put the system into debug mode. This means that the C<PAUSE> command pauses the script engine until the user hits a key.
+
+==option string i include
+
+(testexecute.exe's equivalent option is called C<-tci>. For compatability this is also allowed here.)
+
+Used to specify the test cases to be included during execution. The option is followed by a list of test case IDs to be included or excluded. You can specify the test case IDs in three different ways as follows:
+
+=over 5
+
+=item *
+
+A list of comma-separated test case IDs 
+
+=item *
+
+A range of test case IDs 
+
+=item *
+
+A .tcs file containing the test case IDs delimited by a new-line character. 
+
+=back
+
+You can specify the test case IDs using all the three different ways listed above. For example, in the following testexecute command, the test cases list is a combination of comma-separated test case IDs, test case ID range and a .tcs file containing IDs of the test cases to be included during execution: 
+
+ c:\>testexecute c:\tefintegrationtest\TEFIntegrationTest-regular.script -tci TEST-001,TEST-002,TEST-008:TEST-015,some.tcs
+
+==option bool t ignore
+
+For compatibility with testeexecute.exe, so you can specify includes with C<-tci>, which is a syntax fshell doesn't normally accept. Has no effect.
+
+==option bool c ignore2
+
+For compatibility with testeexecute.exe, so you can specify includes with C<-tci>, which is a syntax fshell doesn't normally accept. Has no effect.
+
+==option string x exclude
+
+Used to specify the test cases to be excluded during execution. Takes same syntax as C<--include>.
+
+==argument filename script-file optional
+
+The TEF script to execute. If not specified, the command replaces the TEF log server and waits forever for any logs to be written, and prints them to the console when they occur.
+
+==copyright
+
+Copyright (c) 2010 Accenture. All rights reserved.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/testexecute/testexecute.cpp	Wed Jul 07 16:07:08 2010 +0100
@@ -0,0 +1,205 @@
+// testexecute.cpp
+// 
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+
+#include <fshell/memoryaccesscmd.h>
+#include <fshell/descriptorutils.h>
+#include "proxyserver.h"
+#include <test/testexecutelogger.h>
+
+using namespace IoUtils;
+
+class CCmdTestExecute : public CMemoryAccessCommandBase, public MMessageHandler
+	{
+public:
+	static CCommandBase* NewLC();
+	~CCmdTestExecute();
+private:
+	CCmdTestExecute();
+
+private: // From MMessageHandler
+	virtual TBool HandleMessageL(CProxySession* aSession, const RMessage2& aMessage);
+
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual void DoRunL();
+	virtual void ArgumentsL(RCommandArgumentList& aArguments);
+	virtual void OptionsL(RCommandOptionList& aOptions);
+
+private: // From CActive
+	void RunL();
+	void DoCancel();
+
+	class CLogonCompleter : public CActive
+		{
+	public:
+		CLogonCompleter(CCmdTestExecute* aCommand) : CActive(CActive::EPriorityStandard), iCommand(aCommand)
+			{
+			CActiveScheduler::Add(this);
+			iCommand->iTefProc.Logon(iStatus);
+			SetActive();
+			}
+		void RunL() { iCommand->Complete(iStatus.Int()); }
+		void DoCancel() { iCommand->iTefProc.LogonCancel(iStatus); }
+		~CLogonCompleter() { Cancel(); }
+
+	private:
+		CCmdTestExecute* iCommand;
+		};
+
+private:
+	CProxyServer* iProxy;
+	RTestExecuteLogServ iLogger;
+	RBuf8 iTempBuf;
+
+	TFileName2 iScriptFile;
+	TBool iIgnoredT, iIgnoredC;
+	HBufC* iIncludes;
+	HBufC* iExcludes;
+	TBool iDebug;
+	CLogonCompleter* iLogonCompleter;
+	RProcess iTefProc;
+	};
+
+
+EXE_BOILER_PLATE(CCmdTestExecute)
+
+CCommandBase* CCmdTestExecute::NewLC()
+	{
+	CCmdTestExecute* self = new(ELeave) CCmdTestExecute();
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	return self;
+	}
+
+CCmdTestExecute::~CCmdTestExecute()
+	{
+	Cancel();
+	if (iProxy)
+		{
+		iProxy->Destroy();
+		}
+	iLogger.Close();
+	iTempBuf.Close();
+	delete iIncludes;
+	delete iExcludes;
+	delete iLogonCompleter;
+	iTefProc.Close();
+	}
+
+CCmdTestExecute::CCmdTestExecute()
+	: CMemoryAccessCommandBase(EManualComplete | ESharableIoSession)
+	{
+	iTefProc.SetHandle(0); // Don't default to the current process
+	}
+
+const TDesC& CCmdTestExecute::Name() const
+	{
+	_LIT(KName, "testexecute");	
+	return KName;
+	}
+
+void CCmdTestExecute::ArgumentsL(RCommandArgumentList& aArguments)
+	{
+	aArguments.AppendFileNameL(iScriptFile, _L("script-file"));
+	}
+
+void CCmdTestExecute::OptionsL(RCommandOptionList& aOptions)
+	{
+	aOptions.AppendBoolL(iDebug, _L("debug"));
+	aOptions.AppendBoolL(iIgnoredT, _L("ignore"));
+	aOptions.AppendBoolL(iIgnoredC, _L("ignore2"));
+	aOptions.AppendStringL(iIncludes, _L("include"));
+	aOptions.AppendStringL(iExcludes, _L("exclude"));
+	}
+
+#define CTRL(x) ((x)-'a'+1)
+
+void CCmdTestExecute::DoRunL()
+	{
+	LoadMemoryAccessL();
+	iTempBuf.CreateL(1024);
+	User::LeaveIfError(Stdin().CaptureKey(CTRL('c'), 0, 0)); // So we can do cleanup
+	Stdin().WaitForKey(iStatus);
+	SetActive();
+
+
+	User::LeaveIfError(iLogger.Connect()); // Keep it open
+	iLogger.CreateLog(_L("\\logs\\testexecute\\fshelllovesyou"), RTestExecuteLogServ::ELogModeOverWrite); // The server won't actually stay open unless you call CreateLog!
+
+	// Set up our proxy
+	iProxy = CProxyServer::NewInSeparateThreadL(_L("TestExecuteLogEngine"), this);
+
+	if (iScriptFile.Length())
+		{
+		LtkUtils::RLtkBuf cmdLine;
+		CleanupClosePushL(cmdLine);
+		cmdLine.AppendL(iScriptFile);
+		if (iDebug) cmdLine.AppendL(_L(" -d"));
+		if (iIncludes) cmdLine.AppendFormatL(_L(" -tci %S"), iIncludes);
+		if (iExcludes) cmdLine.AppendFormatL(_L(" -tcx %S"), iExcludes);
+
+		LeaveIfErr(iTefProc.Create(_L("testexecute.exe"), cmdLine), _L("Couldn't create testexecute.exe %S"), &cmdLine);
+		iLogonCompleter = new(ELeave) CLogonCompleter(this);
+		iTefProc.Resume();
+		CleanupStack::PopAndDestroy(&cmdLine);
+		}
+	else
+		{
+		// Just do some tests
+		RTestExecuteLogServ newlogger;
+		LeaveIfErr(newlogger.Connect(), _L("Couldn't connect to proxied RTestExecuteLogServ"));
+
+		newlogger.CreateLog(_L("\\logs\\testexecute\\scriptyscripty"), RTestExecuteLogServ::ELogModeOverWrite);
+		newlogger.Write(_L("I AM A LOG"));
+		newlogger.Close();
+		}
+	}
+
+TBool CCmdTestExecute::HandleMessageL(CProxySession* /*aSession*/, const RMessage2& aMessage)
+	{
+	switch (aMessage.Function())
+		{
+	case RTestExecuteLogServ::EWriteLog:
+		{
+		aMessage.ReadL(0, iTempBuf);
+		Write(iTempBuf.Expand());
+		break;
+		}
+	default:
+		break;
+		}
+
+	return EFalse; // We allow everything to go through to the real TEF as well
+	}
+
+void CCmdTestExecute::RunL()
+	{
+	if (Stdin().KeyCode() == CTRL('c'))
+		{
+		Printf(_L("CTRL-C detected, cleaning up proxy server...\r\n"));
+		iProxy->Destroy();
+		iProxy = NULL;
+		Printf(_L("Exiting...\r\n"));
+		SetErrorReported(ETrue); // Supress complaints about the cancel
+		Complete(KErrCancel);
+		}
+	else
+		{
+		Stdin().WaitForKey(iStatus);
+		SetActive();
+		}
+	}
+
+void CCmdTestExecute::DoCancel()
+	{
+	Stdin().WaitForKeyCancel();
+	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/testexecute/testexecute.mmp	Wed Jul 07 16:07:08 2010 +0100
@@ -0,0 +1,29 @@
+// testexecute.mmp
+// 
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+
+#include <fshell/common.mmh>
+
+target			fshell_testexecute.exe
+targettype		exe
+uid				FSHELL_UID2_FSHELL_EXE FSHELL_UID_TESTEXECUTE
+capability		FSHELL_CAP_MMP_NORMAL
+
+userinclude		.
+#include <fshell/fsh_system_include.mmh>
+sourcepath		.
+source			testexecute.cpp
+source			proxyserver.cpp
+
+library			euser.lib
+library			iocli.lib
+library			ltkutils.lib
+library			testexecutelogclient.lib testexecuteutils.lib