contentmgmt/cafstreamingsupport/test/tscaf/source/tscafstep.cpp
branchRCL_3
changeset 43 2f10d260163b
child 61 641f389e9157
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contentmgmt/cafstreamingsupport/test/tscaf/source/tscafstep.cpp	Mon Mar 15 12:46:43 2010 +0200
@@ -0,0 +1,827 @@
+// Copyright (c) 2007-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:
+// Implements the basic test step for the Streaming CAF test harness
+// 
+//
+
+#include "tscafstep.h"
+#ifdef INTERNALLY_ENABLE_UPWARD_DEPENDENCY
+#include <sdpconnectionfield.h>
+#include <sdporiginfield.h>
+#endif
+
+TSdpAttribute::TSdpAttribute()
+	{
+	}
+
+CTestDecoderConfiguration* CTestDecoderConfiguration::NewLC()
+	{
+	CTestDecoderConfiguration* self = new (ELeave) CTestDecoderConfiguration();
+	CleanupStack::PushL(self);
+	return self;	
+	}
+	
+CTestDecoderConfiguration::~CTestDecoderConfiguration()
+	{
+	iAttributeArray.Close();	
+	}
+
+CTestDecoderConfiguration::CTestDecoderConfiguration()
+	{	
+	} 
+
+TExpectedKeyStreamDecoderAttributes::TExpectedKeyStreamDecoderAttributes()
+	{
+	}
+
+CScafStep::CScafStep(CScafServer& aParent)
+/**
+ 	Constructor.
+ */
+	: iParent(aParent), iThreadId(0), iOOMTest(EFalse)
+	{
+	//empty
+	}
+
+CScafStep::~CScafStep()
+/**
+ 	Destructor.
+ */
+	{
+	//empty
+	}
+	
+	
+TVerdict CScafStep::doTestStepPreambleL()
+/**
+ 	From CTestStep. Creates an active scheduler for the test step.
+ */
+	{
+	__UHEAP_MARK;
+	INFO_PRINTF2(_L("HEAP CELLS: %d"), User::CountAllocCells());
+	
+	iActiveScheduler = new (ELeave) CActiveScheduler;
+	CActiveScheduler::Install(iActiveScheduler);
+	
+	ReadTestConfigurationL();
+	
+	SetTestStepResult(EPass);
+	return TestStepResult();
+	}
+
+TVerdict CScafStep::doTestStepL()
+/**
+ * From CTestStep. Default behaviour of doTestStepL() allows for the test case to be run both
+ * under 'Normal' and 'Out of Memory' Conditions. 
+ * 
+ * Implementation of the test case itself is called from the doTestL() of the derived test step.
+ * 
+ * The state of the iOOMTest member variable determines the type of test conditons:
+ * EFalse - Normal Test 
+ * ETrue - Out of Memory Test
+ */
+	{
+	if (!iOOMTest)
+		{
+		doTestL();
+		}
+	else
+		{
+		doOOMTestL();
+		}	
+	
+	return TestStepResult();
+	}
+
+TVerdict CScafStep::doTestStepPostambleL()
+/**
+ 	From CTestStep. Destroys the active scheduler of the test step.
+ */
+	{
+	CActiveScheduler::Install(NULL);
+	delete iActiveScheduler;
+	
+	iDecoderConfigurationArray.ResetAndDestroy();
+	iExpectedKeyStreamDecoderData.Close();
+	
+	INFO_PRINTF2(_L("HEAP CELLS: %d"),User::CountAllocCells());
+	__UHEAP_MARKEND;
+	
+	return TestStepResult();
+	}
+
+#ifdef INTERNALLY_ENABLE_UPWARD_DEPENDENCY
+// We need this dummy function because TCleanupItem c'tor (see below) does not accept functions without parameters
+void CloseSdpCodecPool(TAny *)
+	{
+	SdpCodecStringPool::Close();
+	}
+
+CSdpDocument* CScafStep::CreateSdpDocumentLC()
+/**
+ 	Creates an SDP document object which is a collection of all media fields and session attributes.
+ 	@return A pointer to the SDP document object. 
+ 			The ownership is transferred. Please note that
+ 			the returned object must be deleted by DeleteSdpAndCloseCodecPool method.
+ */
+	{
+	// Open the string pool to access all predefined SDP constants
+	SdpCodecStringPool::OpenL();
+	CleanupStack::PushL(TCleanupItem(CloseSdpCodecPool,this)); //Pass the class pointer to make armv5 compiler (for urel) happy
+	
+	// Create the CSdpDocument object
+	CSdpDocument* sdpDocument = CSdpDocument::NewLC();
+	
+	// Define the session name
+	sdpDocument->SetSessionNameL(_L8("ScafTest"));
+	
+	// Set the origin field. The values are not important. 
+	// Because they are not used in our tests.
+	TInt64 sessionId(TUint(2055478987));
+	TInt64 sessionVersion(TUint(2027813655));
+	TInetAddr address;
+	const TUint32 KInetAddr = INET_ADDR(192,168,0,3);
+	address.SetAddress( KInetAddr );
+	CSdpOriginField* originField = CSdpOriginField::NewLC(_L8("scafuser"), sessionId, sessionVersion, address);
+	sdpDocument->SetOriginField(originField);
+	CleanupStack::Pop(originField);
+	
+	CleanupStack::Pop(); // Pop the temporary guard on SDP Codec string pool close
+	CleanupStack::Pop(); // Pop sdpDocument object
+	CleanupStack::PushL(TCleanupItem(CScafStep::DeleteSdpDocAndCloseCodecPool, reinterpret_cast<TAny *>(sdpDocument)));
+	
+	return sdpDocument;
+	}
+
+void CScafStep::AddMediaFieldL(CSdpDocument& aSdpDoc, const CSdpMediaField* aSdpKeyStream)
+/**
+ 	Adds an SDP media field object to a given SDP document object.
+ 	@param aSdpDoc The SDP document object
+ 	@param aSdpKeyStream Sdp media field which will be appended to the SDP document object.
+ 						 This object should be popped from the cleanupstack after this function runs successfully.
+ */
+	{
+	// Set the key stream field given
+	User::LeaveIfError(aSdpDoc.MediaFields().Append(aSdpKeyStream));
+	}
+
+CSdpMediaField* CScafStep::CreateSdpLC(TInt aSdpNum)
+/**
+ 	Creates a simple SDP media field object.
+ 	@param aSdpNum An integer representing the accessor to the SDP parameter
+ 	 		array and the required data 
+ 	@return A pointer to the SDP media field object. 
+ 			The ownership is transferred. Please note that
+ 			the returned object must be deleted by DeleteSdpAndCloseCodecPool method.
+ 	@see CScafStep::DeleteSdp
+ */
+	{
+	TPtrC pMedia;
+	pMedia.Set(iDecoderConfigurationArray[aSdpNum]->iMedia);
+	
+	TInt mIndex = 0;
+	if(!pMedia.CompareF(KSdpMediaAudio))
+		{
+		mIndex = SdpCodecStringConstants::EMediaAudio;
+		}
+	else if(!pMedia.CompareF(KSdpMediaVideo))
+		{
+		mIndex = SdpCodecStringConstants::EMediaVideo;
+		}
+	else if(!pMedia.CompareF(KSdpMediaData))
+		{
+		mIndex = SdpCodecStringConstants::EMediaData;
+		}
+	else
+		{
+		ERR_PRINTF2(_L("Unsupported media type: '%S'"),&pMedia);
+		SetTestStepResult(EFail);
+		User::Leave(KErrNotFound);
+		}
+	
+	TPtrC pProtocol;
+	pProtocol.Set(iDecoderConfigurationArray[aSdpNum]->iProtocol);
+	
+	TInt mProtocol = 0;
+	if(!pProtocol.CompareF(KSdpProtocolUdp))
+		{
+		mProtocol = SdpCodecStringConstants::EProtocolUdp;
+		}
+	else if(!pProtocol.CompareF(KSdpProtocolTcp))
+		{
+		mProtocol = SdpCodecStringConstants::EProtocolTcp;
+		}
+	else if(!pProtocol.CompareF(KSdpProtocolRtp))
+		{
+		mProtocol = SdpCodecStringConstants::EProtocolRtpAvp;
+		}
+	else
+		{
+		ERR_PRINTF2(_L("Unsupported protocol type: '%S'"),mProtocol);
+		SetTestStepResult(EFail);
+		User::Leave(KErrNotFound);
+		}
+	
+	TInt port = iDecoderConfigurationArray[aSdpNum]->iPort;
+	
+	TPtrC pFormat;
+	pFormat.Set(iDecoderConfigurationArray[aSdpNum]->iFormat);
+	
+	//Convert 16-bit to 8-bit
+	TPtr8 ptrFormat(Convert16To8LC(pFormat));
+		
+	RStringPool pool = SdpCodecStringPool::StringPoolL();	
+	RStringF mediaData = pool.StringF(mIndex, SdpCodecStringPool::StringTableL());
+	CleanupClosePushL(mediaData);
+	RStringF protocol = pool.StringF(mProtocol, SdpCodecStringPool::StringTableL());
+	CleanupClosePushL(protocol);
+
+	CSdpMediaField* sdp = CSdpMediaField::NewL(mediaData, port, protocol, ptrFormat);
+	CleanupStack::PopAndDestroy(3);
+	CleanupStack::PushL(sdp);
+	
+	//If a connection address is defined, create an SDP connection field and add it to the SDP media field
+	if(iDecoderConfigurationArray[aSdpNum]->iConnAddr.Length()>0)
+		{
+		//The only supported network type is ENetType
+		RStringF netType = pool.StringF(SdpCodecStringConstants::ENetType, SdpCodecStringPool::StringTableL());
+		CleanupClosePushL(netType);
+		//The only supported address type is IP v4
+		RStringF addressType = pool.StringF(SdpCodecStringConstants::EAddressTypeIP4, SdpCodecStringPool::StringTableL());
+		CleanupClosePushL(addressType);
+		//Create the connection field
+		CSdpConnectionField* connField = CSdpConnectionField::NewL(netType, addressType, Convert16To8LC(iDecoderConfigurationArray[aSdpNum]->iConnAddr));
+		CleanupStack::PushL(connField);
+		//Set the connection field into the SDP media field
+		sdp->ConnectionFields().AppendL(connField);
+		CleanupStack::Pop(connField);
+		CleanupStack::PopAndDestroy(3, &netType); //netType, addressType, Convert16To8LC
+		}
+	
+	//Add atrributes
+	AddAttributes2SdpL(*sdp, aSdpNum);
+	
+	return sdp;
+	}
+#endif
+
+CKeyStreamSink* CScafStep::CreateKeyStreamSinkLC(const TDesC& aFileName, const TDesC& aPrivPath)
+/**
+ 	Creates a test key stream sink object.
+ 	@param aFilePath The output file name of the test key stream sink.
+ 	@param aPrivPath Stream Agents Private Folder Path
+ 	@return A pointer to the test key stream sink object. 
+ 			The ownership is transferred.
+ */
+	{
+	HBufC* privFolder = GetFullPathLC(aPrivPath, aFileName);
+	CTestKeyStreamSink* sink = CTestKeyStreamSink::NewL(*privFolder);
+	CleanupStack::PopAndDestroy(privFolder);
+	CleanupStack::PushL(sink);
+	return sink;
+	}
+
+void CScafStep::CleanAgentsPrivateFolderL(const TDesC& aPrivatePath)
+/**
+ 	Delete all files and folders under the private directory of the test agent.
+ */
+	{
+	HBufC* agentPrivFol = GetFullPathLC(aPrivatePath, _L("*.*"));
+	CFileMan *fm = CFileMan::NewL(iParent.Fs());
+	CleanupStack::PushL(fm);
+	
+	TInt ret = fm->Delete(*agentPrivFol,0);
+	if((ret != KErrNone)&&(ret != KErrNotFound)&&(ret != KErrPathNotFound))
+		{
+		User::Leave(ret);
+		}
+	CleanupStack::PopAndDestroy(2, agentPrivFol);
+	}
+
+ 
+void CScafStep::DeleteSdpDocAndCloseCodecPool(TAny* aSdpDoc)
+/**
+ 	Delete the SDP document object and close the codec pool
+ 	@param aSdp The SDP object which will be deleted.
+ */
+	{
+#ifdef INTERNALLY_ENABLE_UPWARD_DEPENDENCY
+	delete reinterpret_cast<CSdpDocument *>(aSdpDoc);
+	SdpCodecStringPool::Close();
+#else
+	(void) aSdpDoc;
+#endif
+	}
+
+void CScafStep::CopyFile2AgentsPrivateFolderL(RFs& aFs, const TDesC& aFileName, const TDesC& aPrivPath)
+/**
+ 	Copy a test file from Z drive to the private folder of the test agent server.
+ 	@param aFs File Server session.
+ 	@param aPrivPath Stream Agents Private Folder Path
+ 	@param aFileName The name of the file which lives in the folder of Z drive.
+ */
+	{
+	//Gets the target file path
+	HBufC* fTarget = GetFullPathLC(aPrivPath, aFileName);
+	//Make sure that the path exists
+	TInt err = aFs.MkDirAll(*fTarget);
+	if(err != KErrNone && err != KErrAlreadyExists)
+		{
+		User::Leave(err);
+		}
+	
+	//Get the file source path
+	TFileName fSource(KDataFilesPath);
+	fSource.Append(aFileName);
+	
+	//Create a file manager
+	CFileMan *fm = CFileMan::NewL(aFs);
+	CleanupStack::PushL(fm);
+	
+	//Copy the source file to the target
+	User::LeaveIfError(fm->Copy(fSource, *fTarget));
+	// Make the file writeable 
+    User::LeaveIfError(fm->Attribs(*fTarget, 0, KEntryAttReadOnly, TTime(0), 0));
+	CleanupStack::PopAndDestroy(2, fTarget);
+	}
+
+#ifdef INTERNALLY_ENABLE_UPWARD_DEPENDENCY
+void CScafStep::AddAttributes2SdpL(CSdpMediaField& aSdp, TInt aSdpNum)
+/**
+ * Add attributes from the instance within the CSdpConfiguration array, where 
+ * the attribute information is stored, into the SDP object being constructed.
+ * @param aSdp The SDP object where the attribute is added.
+ */
+	{	
+	TInt attrCount = iDecoderConfigurationArray[aSdpNum]->iAttributeArray.Count();
+
+	RStringPool pool = SdpCodecStringPool::StringPoolL();
+	
+	for(TInt i=0; i < attrCount; ++i)
+		{
+		TPtrC pAttrType;
+		pAttrType.Set(iDecoderConfigurationArray[aSdpNum]->iAttributeArray[i].iAttributeType);
+		
+		TPtrC pAttrName;
+		pAttrName.Set(iDecoderConfigurationArray[aSdpNum]->iAttributeArray[i].iAttributeName);
+		
+		//Convert 16-bit to 8-bit
+		TPtr8 ptrAttrName(Convert16To8LC(pAttrName));
+		
+		TPtrC pAttrValue;
+		pAttrValue.Set(iDecoderConfigurationArray[aSdpNum]->iAttributeArray[i].iAttributeValue);
+		
+		//Convert 16-bit to 8-bit
+		TPtr8 ptrAttrValue(Convert16To8LC(pAttrValue));
+		
+		if(pAttrType.Compare(_L("Format")))
+			{
+			RStringF attrName = pool.OpenFStringL(ptrAttrName);
+			CleanupClosePushL(attrName);
+			CSdpAttributeField *attribute = CSdpAttributeField::NewLC(attrName, ptrAttrValue);
+			User::LeaveIfError((aSdp.AttributeFields()).Append(attribute));
+			CleanupStack::Pop(attribute);
+			}
+		else
+			{
+			RStringF fmtpStr = pool.StringF(SdpCodecStringConstants::EAttributeFmtp, SdpCodecStringPool::StringTableL());
+			CleanupClosePushL(fmtpStr);
+			CSdpFmtAttributeField* fmtpAttribute = CSdpFmtAttributeField::NewLC(fmtpStr, ptrAttrName, ptrAttrValue);
+			User::LeaveIfError(aSdp.FormatAttributeFields().Append(fmtpAttribute));
+			CleanupStack::Pop(fmtpAttribute);
+			}
+		CleanupStack::PopAndDestroy(3);
+		}//for
+	}//End of function
+#endif
+
+void CScafStep::PrintErrorAndLeaveL(TDesC& aKey)
+/**
+ 	Prints an error message and then leaves with not found error.
+ 	@param aKey The name of the key which is not found.
+ */
+	{
+	ERR_PRINTF2(_L("Failed to read '%S' key of configuration"), &aKey);
+	SetTestStepResult(EFail);
+	User::Leave(KErrNotFound);
+	}
+
+TPtr8 CScafStep::Convert16To8LC(TDesC& aDes)
+/**
+ 	Convert a 16-bit descriptor into 8-bit.
+ 	@param aVal The 16-bit descriptor.
+ 	@return The descriptor converted to 8-bit.
+ */
+	{
+	HBufC8* buf = HBufC8::NewLC(aDes.Size());
+	TPtr8 ptr(buf->Des());
+	ptr.Copy(aDes);
+	return ptr;
+	}
+
+HBufC* CScafStep::GetFullPathLC(const TDesC& aPath, const TDesC& aFileName)
+/**
+ 	Create a fully qualified file path.
+ 	@param aPath The folder path of the file.
+ 	@param aFileName The name of the file.
+ 	@return A pointer to the fully qualified file path.
+ */
+	{
+	HBufC* fullPath = HBufC::NewLC(aPath.Length()+aFileName.Length()+1);
+	TPtr ptr(fullPath->Des());
+	ptr.Copy(aPath);
+	ptr[0] = iParent.Fs().GetSystemDriveChar();
+	if(aFileName.Length()>0)
+		{
+		ptr.Append(aFileName);
+		}
+	return fullPath;
+	}
+
+CSraRightsObject* CScafStep::GetRightsObjectLC(const TDesC& aName, const TDesC& aPrivPath)
+/**
+ 	Reads the given RO file and creates a RO object.
+ 	@param aName The name of the RO file.
+ 	@param aPrivPath Stream Agents Private Folder Path
+ 	@return The RO object.
+ */
+	{
+	RFile f;
+	HBufC* path = GetFullPathLC(aPrivPath, aName);
+	User::LeaveIfError(f.Open(iParent.Fs(), *path, EFileRead));
+	CleanupStack::PopAndDestroy(path);
+	CleanupClosePushL(f);
+	RFileReadStream stream(f);
+	CleanupClosePushL(stream);
+	CSraRightsObject* ro = CSraRightsObject::NewL(stream);
+	CleanupStack::PopAndDestroy(2, &f);
+	CleanupStack::PushL(ro);
+	return ro;
+	}
+
+TVerdict CScafStep::doOOMTestL()
+/**
+ * 	Runs the test step under OOM Conditions checking that each heap allocation is fail safe
+ */
+	{	
+	// Pre and Post test heap cell allocation counts
+ 	TInt cellCountAfter = 0;
+	TInt cellCountBefore = 0;
+	
+	/**
+	 * The loop tests each heap allocation under out of memory conditions to determine whether
+	 * the framework cleans up correctly without leaking memory.
+	 * 
+	 * The 'for' loop does not have any completion criteria, so the loop breaks out as soon 
+	 * as any of the following events occur:
+	 * a) The pre and post heap cell counts mismatch signalling a memory leakage
+	 * b) An unexpected leave (any leave with an error code other than 'KErrNoMemory')
+	 * c) All heap allocations have been tested and the test returns 'KErrNone'
+	 */
+	for (TInt testCount = 0; ; ++testCount)
+ 		{
+ 		__UHEAP_MARK;
+ 		__UHEAP_SETFAIL(RHeap::EDeterministic, testCount+1);
+ 		cellCountBefore = User::CountAllocCells();
+ 		TRAPD(err, doTestL());
+ 		cellCountAfter = User::CountAllocCells();
+ 		__UHEAP_MARKEND;
+ 		
+ 		INFO_PRINTF3(_L("OOM Test %d: Status = %d"),testCount,err);
+ 		
+ 		if (err == KErrNone)
+ 			{
+			INFO_PRINTF1(_L("OOM Test Finished"));
+ 			break;
+ 			}
+ 		else if(err == KErrNoMemory)
+ 			{
+ 			if (cellCountBefore != cellCountAfter)
+ 				{
+ 				ERR_PRINTF2(_L("OOM Test Result: Failed - Memory leakage on iteration %d"), testCount);
+ 				ERR_PRINTF2(_L("Pre-Test Heap Cell Count: %d"), cellCountBefore);
+ 				ERR_PRINTF2(_L("Post-Test Heap Cell Count: %d"), cellCountAfter);
+ 				SetTestStepResult(EFail);
+ 				break;
+ 				}
+ 			}
+ 		else
+ 			{
+ 			User::Leave(err);
+ 			break;
+ 			}
+		}
+	
+	return TestStepResult();
+	}
+
+TVerdict CScafStep::doTestL()
+	{
+	return TestStepResult();
+	}
+
+void CScafStep::ReadTestConfigurationL()
+	{
+	TInt baseIndex=0;
+	
+	// Read SDP Configuration Data
+	TName fMedia;
+	TName fProtocol;
+	TName fPort;
+	TName fFormat;
+	TName fConnAddr;
+	
+	fMedia.Format(KSdpMedia, baseIndex);
+	fProtocol.Format(KSdpProtocol, baseIndex);
+	fPort.Format(KSdpPort, baseIndex);
+	fFormat.Format(KSdpFormat, baseIndex);
+	fConnAddr.Format(KSdpConnAddr, baseIndex);
+	
+	TPtrC mediaData;
+	TPtrC protocolData;
+	TInt portData;
+	TPtrC formatData;
+	TPtrC connAddrData;
+	
+	while (GetStringFromConfig(ConfigSection(), fMedia, mediaData) &&
+			GetStringFromConfig(ConfigSection(), fProtocol, protocolData) &&
+			GetIntFromConfig(ConfigSection(), fPort, portData) &&
+			GetStringFromConfig(ConfigSection(), fFormat, formatData))
+		{
+		
+		CTestDecoderConfiguration* newSdpConfig = CTestDecoderConfiguration::NewLC();
+		
+		newSdpConfig->iMedia.Set(mediaData);
+		newSdpConfig->iProtocol.Set(protocolData);
+		newSdpConfig->iPort = portData;
+		newSdpConfig->iFormat.Set(formatData);
+		
+		if(GetStringFromConfig(ConfigSection(), fConnAddr, connAddrData))
+			{
+			newSdpConfig->iConnAddr.Set(connAddrData);
+			}
+		
+		// Read Stream Associated Agents Private Folder Path
+		TName fPrivateFolderPath;
+		TName fSingleProcessAgent;
+		
+		fPrivateFolderPath.Format(KScafConfigPrivateFolderPath, baseIndex);
+		fSingleProcessAgent.Format(KScafConfigSingleProcessAgent, baseIndex);
+		
+		TPtrC privateFolderPath;
+		TBool singleProcessAgent;
+		
+		if(GetStringFromConfig(ConfigSection(), fPrivateFolderPath, privateFolderPath) &&
+			GetBoolFromConfig(ConfigSection(), fSingleProcessAgent, singleProcessAgent))
+			{
+			newSdpConfig->iPrivateFolderPath.Set(privateFolderPath);
+			newSdpConfig->iSingleProcessAgent = singleProcessAgent;
+			}
+		
+		// Read SDP Attribute Data
+		TInt attributeIndex = 0;
+		
+		TName fAttributeType;
+		TName fAttributeName;
+		TName fAttributeValue;
+		
+		fAttributeType.Format(KSdpAttributeType,baseIndex,attributeIndex);
+		fAttributeName.Format(KSdpAttributeName,baseIndex,attributeIndex);
+		fAttributeValue.Format(KSdpAttributeValue,baseIndex,attributeIndex);
+		
+		TPtrC attributeType;
+		TPtrC attributeName;
+		TPtrC attributeValue;
+		
+		while (GetStringFromConfig(ConfigSection(), fAttributeType, attributeType) &&
+				GetStringFromConfig(ConfigSection(), fAttributeName, attributeName) &&
+				GetStringFromConfig(ConfigSection(), fAttributeValue, attributeValue))
+				{
+				TSdpAttribute newAttribute;
+				
+				newAttribute.iAttributeType.Set(attributeType);
+				newAttribute.iAttributeName.Set(attributeName);
+				newAttribute.iAttributeValue.Set(attributeValue);
+				
+				newSdpConfig->iAttributeArray.Append(newAttribute);
+				
+				attributeIndex++;
+				fAttributeType.Format(KSdpAttributeType,baseIndex,attributeIndex);
+				fAttributeName.Format(KSdpAttributeName,baseIndex,attributeIndex);
+				fAttributeValue.Format(KSdpAttributeValue,baseIndex,attributeIndex);
+				}
+		
+		iDecoderConfigurationArray.Append(newSdpConfig);
+		
+		CleanupStack::Pop(newSdpConfig);
+		
+		baseIndex++;
+		fMedia.Format(KSdpMedia, baseIndex);
+		fProtocol.Format(KSdpProtocol, baseIndex);
+		fPort.Format(KSdpPort, baseIndex);
+		fFormat.Format(KSdpFormat, baseIndex);
+		fConnAddr.Format(KSdpConnAddr, baseIndex);
+		}
+	
+	// Read Expected Key Stream Decoder Attribute Values
+	TName fIsProgramProtected;
+	TName fIsServiceProtected;
+	TName fCanExport;
+	TName fMustProtectIfRecording;
+	TName fCanPlay;
+	TName fContentId;
+	TName fRightsIssuerUri;
+	
+	baseIndex = 0;
+	
+	fIsProgramProtected.Format(KScafConfigIsProgramProtected,baseIndex);
+	fIsServiceProtected.Format(KScafConfigIsServiceProtected,baseIndex);
+	fCanExport.Format(KScafConfigCanExport,baseIndex);
+	fMustProtectIfRecording.Format(KScafConfigMustProtectIfRecording,baseIndex);
+	fCanPlay.Format(KScafConfigCanPlay,baseIndex);
+	fContentId.Format(KScafConfigContentId,baseIndex);
+	fRightsIssuerUri.Format(KScafConfigRightsIssuerUri,baseIndex);
+	
+	TBool isProgramProtected;
+	TBool isServiceProtected;
+	TBool canExport;
+	TBool mustProtectIfRecording;
+	TBool canPlay;
+	TPtrC contentId;
+	TPtrC rightsIssuerUri;
+	
+	while(GetBoolFromConfig(ConfigSection(),fIsProgramProtected,isProgramProtected) &&
+		GetBoolFromConfig(ConfigSection(),fIsServiceProtected,isServiceProtected) &&
+		GetBoolFromConfig(ConfigSection(),fCanExport,canExport) &&
+		GetBoolFromConfig(ConfigSection(),fMustProtectIfRecording,mustProtectIfRecording) &&
+		GetBoolFromConfig(ConfigSection(),fCanPlay,canPlay) &&
+		GetStringFromConfig(ConfigSection(),fContentId,contentId) &&
+		GetStringFromConfig(ConfigSection(),fRightsIssuerUri,rightsIssuerUri))
+		{
+		TExpectedKeyStreamDecoderAttributes newExpectedValues;
+		
+		newExpectedValues.IsProgramProtected = isProgramProtected;
+		newExpectedValues.IsServiceProtected = isServiceProtected;
+		newExpectedValues.CanExport = canExport;
+		newExpectedValues.MustProtectIfRecording = mustProtectIfRecording;
+		newExpectedValues.CanPlay = canPlay;
+		newExpectedValues.ContentId.Set(contentId);
+		newExpectedValues.RightsIssuerUri.Set(rightsIssuerUri);
+		
+		iExpectedKeyStreamDecoderData.Append(newExpectedValues);
+		
+		baseIndex++;
+		fIsProgramProtected.Format(KScafConfigIsProgramProtected,baseIndex);
+		fIsServiceProtected.Format(KScafConfigIsServiceProtected,baseIndex);
+		fCanExport.Format(KScafConfigCanExport,baseIndex);
+		fMustProtectIfRecording.Format(KScafConfigMustProtectIfRecording,baseIndex);
+		fCanPlay.Format(KScafConfigCanPlay,baseIndex);
+		fContentId.Format(KScafConfigContentId,baseIndex);
+		fRightsIssuerUri.Format(KScafConfigRightsIssuerUri,baseIndex);
+		}
+	
+	// Read OOM Test Flag
+	GetBoolFromConfig(ConfigSection(), KScafConfigOOMTest, iOOMTest);
+	}
+
+TBool CScafStep::CheckKeyStreamDecoderAttributesL(const CKeyStreamDecoder& aDecoder,
+													const TExpectedKeyStreamDecoderAttributes& aExpectedData)
+	{
+	// Overall result of the attribute value checks (ETrue = Pass / EFalse = Fail)
+	TBool result = ETrue;
+	
+	_LIT(KIsProgramProtectedDescription,"IsProgramProtected");
+	_LIT(KIsServiceProtectedDescription,"IsServiceProtected");
+	_LIT(KCanExportDescription,"CanExport");
+	_LIT(KMustProtectIfRecordingDescription,"MustProtectIfRecording");
+	_LIT(KCanPlayDescription,"CanPlay");
+	_LIT(KContentIDDescription,"ContentID");
+	_LIT(KRightIssuerURIDescription,"RightIssuerURI");
+	
+	// Query the stream agent to determine whether the program is protected
+	if(!CompareBooleanAttributeL(aDecoder,
+								EIsProgramProtected,
+								KIsProgramProtectedDescription,
+								aExpectedData.IsProgramProtected))
+		{
+		result = EFalse;
+		}	
+	
+	// Query the stream agent to determine whether the whole service is protected
+	if(!CompareBooleanAttributeL(aDecoder,
+								EIsServiceProtected,
+								KIsServiceProtectedDescription,
+								aExpectedData.IsServiceProtected))
+		{
+		result = EFalse;
+		}
+	
+	// Query the stream agent to determine whether the content can be exported
+	if(!CompareBooleanAttributeL(aDecoder,
+								ECanExport,
+								KCanExportDescription,
+								aExpectedData.CanExport))
+		{
+		result = EFalse;
+		}
+	
+	// Query the stream agent to determine whether the content must be protected whilst recording
+	if(!CompareBooleanAttributeL(aDecoder,
+								EMustProtectIfRecording,
+								KMustProtectIfRecordingDescription,
+								aExpectedData.MustProtectIfRecording))
+		{
+		result = EFalse;
+		}	
+	
+	// Query the stream agent to determine whether the content can be played
+	if(!CompareBooleanAttributeL(aDecoder,
+								ECanPlay,
+								KCanPlayDescription,
+								aExpectedData.CanPlay))
+		{
+		result = EFalse;
+		}	
+	
+	// Query the stream agent to retrieve the Content ID
+	if(!CompareStringAttributeL(aDecoder,
+								EContentID,
+								KContentIDDescription,
+								aExpectedData.ContentId))
+		{
+		result = EFalse;
+		}
+	
+	// Query the stream agent to retrieve the Rights Issuer URI
+	if(!CompareStringAttributeL(aDecoder,
+								ERightsIssuerURI,
+								KRightIssuerURIDescription,
+								aExpectedData.RightsIssuerUri))
+		{
+		result = EFalse;
+		}
+	
+	return result;
+	}
+
+TBool CScafStep::CompareBooleanAttributeL(const CKeyStreamDecoder& aDecoder,
+											const TAttribute& aAttributeEnum,
+											const TDesC& aAttributeDescription,
+											const TBool aExpectedValue)
+	{
+	TBool retrievedValue;
+	
+	// Query the stream agent to determine the actual value of the attribute
+	TRAPD(err,aDecoder.GetAttributeL(aAttributeEnum,retrievedValue));
+	
+	if(err != KErrNone)
+		{
+		ERR_PRINTF4(_L("*** Thread %d: Failed to Retrieve '%S' Value - %d ***"),iThreadId,&aAttributeDescription,err);
+		User::Leave(err);
+		}
+	
+	if(retrievedValue != aExpectedValue)
+		{
+		INFO_PRINTF4(_L("Thread %d: Expected '%S' Value = %d"),iThreadId,&aAttributeDescription,aExpectedValue);
+		INFO_PRINTF4(_L("Thread %d: Retrieved '%S' Value = %d"),iThreadId,&aAttributeDescription,retrievedValue);
+		ERR_PRINTF3(_L("*** Thread %d: Unexpected '%S' Value ***"),iThreadId,&aAttributeDescription);
+		return EFalse;
+		}
+	
+	return ETrue;
+	}
+
+TBool CScafStep::CompareStringAttributeL(const CKeyStreamDecoder& aDecoder,
+											const TStringAttribute& aAttributeEnum,
+											const TDesC& aAttributeDescription,
+											const TDesC& aExpectedValue)
+	{
+	TBool result = ETrue;
+	
+	// Query the stream agent to determine the actual value of the attribute
+	HBufC* retrievedValue = aDecoder.GetStringAttributeLC(aAttributeEnum);
+	
+	if(retrievedValue->Des() != aExpectedValue)
+		{
+		INFO_PRINTF4(_L("Thread %d: Expected '%S' Value = %S"),iThreadId,&aAttributeDescription,&aExpectedValue);
+		INFO_PRINTF4(_L("Thread %d: Retrieved '%S' Value = %S"),iThreadId,&aAttributeDescription,retrievedValue);
+		ERR_PRINTF3(_L("*** Thread %d: Unexpected '%S' Value ***"),iThreadId,&aAttributeDescription);
+		result = EFalse;
+		}
+	
+	// Destroy the heap based descriptor
+	CleanupStack::PopAndDestroy(retrievedValue);
+	
+	return result;
+	}