contentmgmt/cafstreamingsupport/test/tscaf/source/tscafstep.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 15 Mar 2010 12:46:43 +0200
branchRCL_3
changeset 43 2f10d260163b
child 61 641f389e9157
permissions -rw-r--r--
Revision: 201010 Kit: 201010

// 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;
	}