Example #1
0
void CTextNode::addChild(const CSerializationID& keyID, const ISerializableNode* child)
{
    const CTextNode* textChild = static_cast<const CTextNode*>(child);
    XmlNodeRef cloneNode = textChild->m_constXmlNode->clone();
    cloneNode->setTag(keyID.GetString());
    m_xmlNode->addChild(cloneNode);
    RebuildChildrenList();
}
// create the xmlnode before passing it in
void CAutoTester::CreateTestCase(XmlNodeRef &testCase, const char *testName, bool passed, const char *failedType, const char *failedMessage)
{
	assert(testName);

	testCase->setTag("testcase");
	testCase->setAttr("name", testName ? testName : "NULL");
	testCase->setAttr("time", 0);

	if (!passed)
	{
		assert(failedType);
		assert(failedMessage);

		XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
		failedCase->setTag("failure");
		failedCase->setAttr("type", failedType ? failedType : "NULL");
		failedCase->setAttr("message", failedMessage ? failedMessage : "NULL");
		testCase->addChild(failedCase);
	}
}
/// Runs all registered tests (if they meet their dependencies)
void CFeatureTestMgr::RunAll()
{
	// Writing the list of the active registered tests. 
	// This can be useful to later check, when a crash occurs, 
	// which tests were executed and which are skipped 
	// (We will have no valid results for them)
	{
		if(m_pAutoTester && !m_testManifestWritten)
		{
			XmlNodeRef testManifest = GetISystem()->CreateXmlNode("testManifest");
			testManifest->setTag("testmanifest");		

			CryLogAlways("About to dump out the testmanifest xml...");

			for (TFeatureTestVec::iterator iter(m_featureTests.begin()); iter != m_featureTests.end(); ++iter)
			{
				FeatureTestState& fTest = *iter;
				XmlNodeRef testDescrNode = fTest.m_pTest->XmlDescription();
				if(testDescrNode)
				{
					testManifest->addChild(testDescrNode);
				}
				
			}

			m_pAutoTester->WriteTestManifest(testManifest);
			m_testManifestWritten = true;
		}
	}



	if (!IsRunning())
	{
		// Ensure all tests are cleaned up and scheduled to run
		ResetAllTests(eFTS_Scheduled);

		if (StartNextTest() || WaitingForScheduledTests())
		{
			CryLog("Running all map feature tests...");
		}
		else
		{
			CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "No tests available to run!");
		}
	}
	else
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "Tests are already running, can't start more until tests are complete.");
	}
}
void CAutoTester::AddSimpleTestCase(const char * groupName, const char * testName, float duration, const char * failureReason, const char * owners)
{
	bool passed = true;
	XmlNodeRef testCase = GetISystem()->CreateXmlNode();
	testCase->setTag("testcase");
	testCase->setAttr("name", testName);
	
	if (owners)
	{
		testCase->setAttr("owners", owners);
	}

	if (duration >= 0.f)
	{
		testCase->setAttr("time", duration);
	}

	// Set whatever other attributes are useful here!

	if (failureReason == NULL || failureReason[0] == '\0')
	{
		CryLogAlways ("CAutoTester::AddSimpleTestCase() Group '%s' test '%s' passed!", groupName, testName);
		testCase->setAttr("status", "run");
	}
	else
	{
		CryLogAlways ("CAutoTester::AddSimpleTestCase() Group '%s' test '%s' failed: %s", groupName, testName, failureReason);
		XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
		failedCase->setTag("failure");
		failedCase->setAttr("type", "TestCaseFailed");
		failedCase->setAttr("message", failureReason);
		testCase->addChild(failedCase);
		passed = false;
	}

	AddTestCaseResult(string().Format("%s: %s", m_includeThisInFileName, groupName ? groupName : "No group name specified"), testCase, passed);
}
Example #5
0
bool CEditorGame::BuildEntitySerializationList(XmlNodeRef output)
{
	if(!output)
		return false;

	typedef std::vector<IFlowGraph*> TFGVector;

	TSerializationData entityData;	// all entities
	TFGVector flowGraphs;						// all flowgraphs

	// build the all-entity list, and keep a record of those entities 
	//	which have a flowgraph attached
	IEntityIt* pIt = gEnv->pEntitySystem->GetEntityIterator();
	while(IEntity* pEntity = pIt->Next())
	{
		IEntityFlowGraphProxy* pFGProxy = static_cast<IEntityFlowGraphProxy*>(pEntity->GetProxy(ENTITY_PROXY_FLOWGRAPH));
		if(pFGProxy)
		{
			flowGraphs.push_back(pFGProxy->GetFlowGraph());
			MarkEntityForSerialize(entityData, pEntity, eST_FlowGraphContainer);
		}

		if (!stricmp(pEntity->GetClass()->GetName(), "Constraint"))
		{
			MarkEntityForSerialize(entityData, pEntity, eST_Class);

			float constraintRadius = -FLT_MAX;

			if (IScriptTable* entityTable = pEntity->GetScriptTable())
			{
				SmartScriptTable props;
				if (entityTable->GetValue("Properties", props))
					props->GetValue("radius", constraintRadius);
			}

			if (constraintRadius > 0.0f)
			{
				const Vec3 boxMin = pEntity->GetWorldPos() - Vec3(constraintRadius + 0.05f);
				const Vec3 boxMax = pEntity->GetWorldPos() + Vec3(constraintRadius + 0.05f);

				IPhysicalEntity** nearbyEntities = 0;

				if (size_t entityCount = gEnv->pPhysicalWorld->GetEntitiesInBox(boxMin, boxMax, nearbyEntities, ent_all))
				{
					for (size_t i = 0; i < entityCount; ++i)
					{
						if (IEntity* nearbyEntity = gEnv->pEntitySystem->GetEntityFromPhysics(nearbyEntities[i]))
							MarkEntityForSerialize(entityData, nearbyEntity, eST_ConstraintNeighbour);
					}
				}
			}
			else
			{
				CryLogAlways("Failed to find a value radius property for constraint '%s'. "
					"Serialization for this constraint might not work.", pEntity->GetName());
			}
		}
		else if(ShouldSaveEntityClass(pEntity)) // some classes should always be saved
			MarkEntityForSerialize(entityData, pEntity, eST_Class);
	}

	// for each FG, mark all entities referenced as needing saving
	for(TFGVector::const_iterator it = flowGraphs.begin(); it != flowGraphs.end(); ++it)
	{
		if(!*it)
			continue;

		IFlowNodeIteratorPtr nodeIt = (*it)->CreateNodeIterator();
		TFlowNodeId nodeId = 0;
		while(IFlowNodeData* pNode = nodeIt->Next(nodeId))
		{
			EntityId nodeEntity = (*it)->GetEntityId(nodeId);
			if(nodeEntity != 0)
			{
				IEntity* pEntity = gEnv->pEntitySystem->GetEntity(nodeEntity);
				MarkEntityForSerialize(entityData, pEntity, eST_FlowGraph);
			}
		}
	}


	// now do the same for entities moved by trackview
	for(int i=0; i<gEnv->pMovieSystem->GetNumSequences(); ++i)
	{
		IAnimSequence* pSeq = gEnv->pMovieSystem->GetSequence(i);
		int nodeCount = pSeq->GetNodeCount();
		for(int j=0; j<nodeCount; ++j)
		{
			IAnimNode* pNode = pSeq->GetNode(j);
			if(pNode)
			{
				IEntity* pEntity = pNode->GetEntity();
				if(pEntity && ShouldSaveTrackViewEntityClass(pEntity)) 
				{
					MarkEntityForSerialize(entityData, pEntity, eST_TrackView);
				}

				if(EntityGUID* guid = pNode->GetEntityGuid())
				{
					EntityId id = gEnv->pEntitySystem->FindEntityByGuid(*guid);
					if(id != 0)
					{
						IEntity* pEntity2 = gEnv->pEntitySystem->GetEntity(id);
						MarkEntityForSerialize(entityData, pEntity2, eST_TrackView);
					}
				}
			}
		}
	}

	// now check entity links: any entity linked to or from a serialized
	//	entity must also be serialized
	for(TSerializationData::iterator it = entityData.begin(), end = entityData.end(); it != end; ++it)
	{
		IEntity* pEntity = gEnv->pEntitySystem->GetEntity(it->first);
		assert(pEntity);

		IEntityLink* pLink = pEntity->GetEntityLinks();
		while(pLink)
		{
			IEntity* pLinkedEntity = gEnv->pEntitySystem->GetEntity(pLink->entityId);
			assert(pLinkedEntity);

			MarkEntityForSerialize(entityData, pLinkedEntity, eST_Linked);

			pLink = pLink->next;
		}
	}

	// output the final file, plus debug info

#if SERIALIZATION_EXPORT_DEBUG
	int saveCount = 0;
	int totalCount = 0;

	int fgCount = 0;
	int fgRefCount = 0;
	int classCount = 0;
	int tvCount = 0;
	int childCount = 0;
	int parentCount = 0;
	int linkCount = 0;

	int fgUnique = 0;
	int fgRefUnique = 0;
	int classUnique = 0;
	int tvUnique = 0;
	int linkUnique = 0;

	typedef std::map<string, int> TClassSaveInfo;
	TClassSaveInfo classesNotSaved;
	TClassSaveInfo classesSaved;
#endif

	output->setTag("EntitySerialization");
	for(TSerializationData::const_iterator it = entityData.begin(); it != entityData.end(); ++it)
	{
		IEntity* pEntity = gEnv->pEntitySystem->GetEntity(it->first);

#if SERIALIZATION_EXPORT_DEBUG
		++totalCount;
		string reasons = "Saving: ";
#endif

		if(it->second != eST_NotSerialized)
		{
			XmlNodeRef child = output->createNode("Entity");
			child->setAttr("id", it->first);
			//child->setAttr("class", pEntity->GetClass()->GetName());	// debug check
			//child->setAttr("name", pEntity->GetName());								// debug check
			output->addChild(child);

#if SERIALIZATION_EXPORT_DEBUG
			classesSaved[pEntity->GetClass()->GetName()]++;

			++saveCount;
			if(it->second & eST_FlowGraphContainer)
			{
				reasons += "FG Container; ";
				++fgCount;
			}
			if(it->second & eST_FlowGraph)
			{
				reasons += "FG reference; ";
				++fgRefCount;
			}
			if(it->second & eST_Class)
			{
				reasons += "Class; ";
				++classCount;
			}
			if(it->second & eST_TrackView)
			{
				reasons += "Trackview; ";
				++tvCount;
			}
			if(it->second & eST_Child)
			{
				reasons += "Child; ";
				++childCount;
			}
			if(it->second & eST_Parent)
			{
				reasons += "Parent; ";
				++parentCount;
			}
			if(it->second & eST_Linked)
			{
				reasons += "Linked; ";
				++linkCount;
			}

			// unique counts (things added as a result of only one condition)
			switch(it->second)
			{
			case eST_FlowGraph:
				++fgRefUnique;
				break;
			case eST_FlowGraphContainer:
				++fgUnique;
				break;
			case eST_Class:
				++classUnique;
				break;
			case eST_TrackView:
				++tvUnique;
				break;
			case eST_Linked:
				++linkUnique;
				break;
			default:
				break;
			}
		}
		else
		{
			reasons = "Not saving";
			classesNotSaved[pEntity->GetClass()->GetName()]++;
		}

		CryLogAlways("Entity %s (%d, class %s) : %s", pEntity->GetName(), it->first, pEntity->GetClass()->GetName(), reasons.c_str());		
#endif
		}
	}
void CAutoTester::UpdateTestNumClientsLevelRotate()
{
	if(gEnv->bServer)
	{
		IGameFramework *pFramework = gEnv->pGame->GetIGameFramework();
		int numChannels = 1; //local channel

		if(pFramework)
		{
			INetNub *pNub = pFramework->GetServerNetNub();

			if(pNub)
			{
				numChannels = pNub->GetNumChannels();
			}
		}

		if (numChannels > m_stateData.testNumClientsRotate.m_maxNumClientsConnected)
		{
			m_stateData.testNumClientsRotate.m_maxNumClientsConnected=numChannels;
		}

		float timeSeconds=gEnv->pTimer->GetFrameStartTime().GetSeconds();
		CryWatch("time=%f; nextTimeOut=%f; numClients=%d; maxNumClients=%d; numClientsExpected=%d", timeSeconds, m_stateData.testNumClientsRotate.m_nextTimeOut, numChannels, m_stateData.testNumClientsRotate.m_maxNumClientsConnected, m_stateData.testNumClientsRotate.m_numClientsExpected);

		if (timeSeconds > m_stateData.testNumClientsRotate.m_debugTimer)
		{
			m_stateData.testNumClientsRotate.m_debugTimer = timeSeconds+2.0f;
			CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() updating time=%f; nextTimeOut=%f; numClients=%d; maxNumClients=%d; numClientsExpected=%d", timeSeconds, m_stateData.testNumClientsRotate.m_nextTimeOut, numChannels, m_stateData.testNumClientsRotate.m_maxNumClientsConnected, m_stateData.testNumClientsRotate.m_numClientsExpected);
		}

		if (timeSeconds > m_stateData.testNumClientsRotate.m_nextTimeOut)
		{
			CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() testing num clients and time has expired. numClients=%d; maxNumClients=%d; numClientsExpected=%d", numChannels, m_stateData.testNumClientsRotate.m_maxNumClientsConnected, m_stateData.testNumClientsRotate.m_numClientsExpected);

			bool passed=false;

			ILevelRotation *pLevelRotation = g_pGame->GetIGameFramework()->GetILevelSystem()->GetLevelRotation();

			string mapName = g_pGame->GetIGameFramework()->GetILevelSystem()->GetCurrentLevel()->GetLevelInfo()->GetName();
			string gameRulesName;
			gameRulesName = g_pGame->GetGameRules()->GetEntity()->GetClass()->GetName();

			XmlNodeRef testCase = GetISystem()->CreateXmlNode();
			string nameStr;
			if (m_stateData.testNumClientsRotate.m_levelIndex == 0)
			{
				nameStr.Format("%02d/%d) Level: %s; gameRules: %s; numClients=%d; timeTested=%.1f seconds", m_stateData.testNumClientsRotate.m_levelIndex+1, pLevelRotation->GetLength(), mapName.c_str(), gameRulesName.c_str(), numChannels, m_stateData.testNumClientsRotate.m_firstLevelTimeOut);
			}
			else
			{
				nameStr.Format("%02d/%d) Level: %s; gameRules: %s; numClients=%d; timeTested=%.1f seconds", m_stateData.testNumClientsRotate.m_levelIndex+1, pLevelRotation->GetLength(), mapName.c_str(), gameRulesName.c_str(), numChannels, m_stateData.testNumClientsRotate.m_levelTimeOut);
			}

			CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() outputting a test result with these details [%s]", nameStr.c_str());

			testCase->setTag("testcase");
			testCase->setAttr("name", nameStr.c_str());
			testCase->setAttr("time", 0);

			testCase->setAttr("numClients", numChannels);
			testCase->setAttr("maxNumClientsConnected",m_stateData.testNumClientsRotate.m_maxNumClientsConnected);
			testCase->setAttr("numClientsExpected", m_stateData.testNumClientsRotate.m_numClientsExpected);

			if (numChannels == m_stateData.testNumClientsRotate.m_maxNumClientsConnected)
			{
				CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() testing num clients and time has expired. We have the same number of clients are our maxNumClients %d", numChannels);

				if (numChannels == m_stateData.testNumClientsRotate.m_numClientsExpected)	// may want to remove this check as keeping the number that joined should be sufficient
				{
					CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() testing num clients and time has expired. We have the same number of clients as we expected to have %d", numChannels);
					testCase->setAttr("status", "run");
					passed=true;
				}
				else
				{
					CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() testing num clients and time has expired. We DON'T have the same number of clients %d as we expected to have %d", numChannels, m_stateData.testNumClientsRotate.m_numClientsExpected);
					//testCase->setAttr("status", "failed");
					XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
					failedCase->setTag("failure");
					failedCase->setAttr("type", "NotEnoughClients");
					failedCase->setAttr("message", string().Format("testing num clients and time has expired. We DON'T have the same number of clients %d as we expected to have %d", numChannels, m_stateData.testNumClientsRotate.m_numClientsExpected));
					testCase->addChild(failedCase);
				}
			}
			else
			{
				//testCase->setAttr("status", "failed");
				XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
				failedCase->setTag("failure");
				failedCase->setAttr("type", "NotEnoughClients");
				failedCase->setAttr("message", string().Format("testing num clients and time has expired. We DON'T have the same number of clients %d as we peaked at %d", numChannels, m_stateData.testNumClientsRotate.m_maxNumClientsConnected));
				testCase->addChild(failedCase);
			}

			AddTestCaseResult("Test Clients In Level Rotation", testCase, passed);
			Stop();


		
			if (pLevelRotation->GetNext() != 0)
			{
				CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() has found we're not at the end of the level rotation moving on to the next level - levelIndex=%d; rotation->GetNext()=%d\n", m_stateData.testNumClientsRotate.m_levelIndex+1, pLevelRotation->GetNext());
				Restart();
				gEnv->pConsole->ExecuteString("g_nextlevel");	// has to be a better way of doing this
				m_stateData.testNumClientsRotate.m_nextTimeOut = timeSeconds + m_stateData.testNumClientsRotate.m_levelTimeOut;
				m_stateData.testNumClientsRotate.m_levelIndex++;
			}
			else
			{
				CryLogAlways("CAutoTester::UpdateTestNumClientsLevelRotate() has found we ARE at the end of the level rotation. Not doing anymore tests\n");
			}
		}
	}
}
// TODO parameterise and refactor this now its mainly duplicated between the two runs
void CAutoTester::UpdateTestNumClients()
{
	if(gEnv->bServer)
	{
		IGameFramework *pFramework = gEnv->pGame->GetIGameFramework();
		int numChannels = 1; //local channel

		if(pFramework)
		{
			INetNub *pNub = pFramework->GetServerNetNub();

			if(pNub)
			{
				numChannels = pNub->GetNumChannels();
			}
		}

		if (numChannels > m_stateData.testNumClients.m_maxNumClientsConnected)
		{
			m_stateData.testNumClients.m_maxNumClientsConnected=numChannels;
		}

		float timeSeconds=gEnv->pTimer->GetFrameStartTime().GetSeconds();
		CryWatch("time=%f; numClients=%d; maxNumClients=%d; numClientsExpected=%d", timeSeconds, numChannels, m_stateData.testNumClients.m_maxNumClientsConnected, m_stateData.testNumClients.m_numClientsExpected);

		if (timeSeconds > m_stateData.testNumClients.m_debugTimer)
		{
			m_stateData.testNumClients.m_debugTimer = timeSeconds+2.0f;
			CryLogAlways("CAutoTester::UpdateTestNumClients() updating time=%f; numClients=%d; maxNumClients=%d; numClientsExpected=%d", timeSeconds, numChannels, m_stateData.testNumClients.m_maxNumClientsConnected, m_stateData.testNumClients.m_numClientsExpected);
		}

		if (timeSeconds > m_stateData.testNumClients.m_timeOut)
		{
			CryLogAlways("CAutoTester::UpdateTestNumClients() testing num clients and time has expired. numClients=%d; maxNumClients=%d; numClientsExpected=%d", numChannels, m_stateData.testNumClients.m_maxNumClientsConnected, m_stateData.testNumClients.m_numClientsExpected);

			bool passed=false;

			string mapName = g_pGame->GetIGameFramework()->GetILevelSystem()->GetCurrentLevel()->GetLevelInfo()->GetName();
			string gameRulesName;
			gameRulesName = g_pGame->GetGameRules()->GetEntity()->GetClass()->GetName();

			XmlNodeRef testCase = GetISystem()->CreateXmlNode();
			string nameStr;
			nameStr.Format("Level: %s; gameRules: %s; numClients=%d; timeTested=%.1f seconds", mapName.c_str(), gameRulesName.c_str(), numChannels, m_stateData.testNumClients.m_timeOut);
			testCase->setTag("testcase");
			testCase->setAttr("name", nameStr.c_str());
			testCase->setAttr("time", 0);

			testCase->setAttr("numClients", numChannels);
			testCase->setAttr("maxNumClientsConnected",m_stateData.testNumClients.m_maxNumClientsConnected);
			testCase->setAttr("numClientsExpected", m_stateData.testNumClients.m_numClientsExpected);

			if (numChannels == m_stateData.testNumClients.m_maxNumClientsConnected)
			{
				CryLogAlways("CAutoTester::UpdateTestNumClients() testing num clients and time has expired. We have the same number of clients are our maxNumClients %d", numChannels);

				if (numChannels == m_stateData.testNumClients.m_numClientsExpected)	// may want to remove this check as keeping the number that joined should be sufficient
				{
					CryLogAlways("CAutoTester::UpdateTestNumClients() testing num clients and time has expired. We have the same number of clients as we expected to have %d", numChannels);
					testCase->setAttr("status", "run");
					passed=true;
				}
				else
				{
					CryLogAlways("CAutoTester::UpdateTestNumClients() testing num clients and time has expired. We DON'T have the same number of clients %d as we expected to have %d", numChannels, m_stateData.testNumClients.m_numClientsExpected);
					//testCase->setAttr("status", "failed");
					XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
					failedCase->setTag("failure");
					failedCase->setAttr("type", "NotEnoughClients");
					failedCase->setAttr("message", string().Format("testing num clients and time has expired. We DON'T have the same number of clients %d as we expected to have %d", numChannels, m_stateData.testNumClients.m_numClientsExpected));
					testCase->addChild(failedCase);
				}
			}
			else
			{
				//testCase->setAttr("status", "failed");
				XmlNodeRef failedCase = GetISystem()->CreateXmlNode();
				failedCase->setTag("failure");
				failedCase->setAttr("type", "NotEnoughClients");
				failedCase->setAttr("message", string().Format("testing num clients and time has expired. We DON'T have the same number of clients %d as we peaked at %d", numChannels, m_stateData.testNumClients.m_maxNumClientsConnected));
				testCase->addChild(failedCase);
			}






			AddTestCaseResult("Test Clients In Levels", testCase, passed);

			Stop();
		}
	}
}
void CAutoTester::WriteResults(TBitfield flags, const string * additionalTestSuiteName, const XmlNodeRef * additionalTestCase)
{
	const int& autotest_enabled = g_pGameCVars->autotest_enabled;
	if (!autotest_enabled)
	{
		return;
	}

	//If result generation is skipped, exit early
	if (autotest_enabled == 2)
	{
		if (m_quitWhenDone)
		{
			gEnv->pConsole->ExecuteString("quit");
		}

		return;
	}

	// test workaround
	//DesignerWarning(0, "test warning");
	// test workaround

	const char * mapName;
	string gameRulesName;
	const char * serverText = "";
	const char * dedicatedText = gEnv->IsDedicated() ? "_Dedicated" : "";

	CGameRules * rules = g_pGame ? g_pGame->GetGameRules() : NULL;
	if (rules)
	{
		gameRulesName = rules->GetEntity()->GetClass()->GetName();
		serverText = gEnv->bServer ? "_Server" : "_Client";
	}
	else
	{
		gameRulesName = "FrontEnd";
	}

	 if (g_pGame && g_pGame->GetIGameFramework()->GetILevelSystem()->GetCurrentLevel())
	 {
		 mapName = g_pGame->GetIGameFramework()->GetILevelSystem()->GetCurrentLevel()->GetLevelInfo()->GetName();
	 }
	 else
	 {
		 mapName = "NoMapLoaded";
	 }

#ifndef _RELEASE
	 CryLogAlways ("CAutoTester::WriteResults(%s): map is '%s', game mode is '%s'", AutoEnum_GetStringFromBitfield(flags, s_writeResultsFlagNames, WriteResultsFlagList_numBits).c_str(), mapName, gameRulesName.c_str());
#endif
















	XmlNodeRef testSuites = GetISystem()->CreateXmlNode("testSuites");
	testSuites->setTag("testsuites");

	for (TTestSuites::const_iterator it=m_testSuites.begin(); it!=m_testSuites.end(); ++it)
	{
		const STestSuite &testSuiteStruct = it->second;
		int numTests = testSuiteStruct.m_testCases.size();
		int numFails = testSuiteStruct.m_numTestCasesFailed;

		XmlNodeRef testSuite = GetISystem()->CreateXmlNode("AutoTest");
		testSuite->setTag("testsuite");
		testSuite->setAttr("name", it->first);
		testSuite->setAttr("LevelName", mapName);
		testSuite->setAttr("GameRulesName", gameRulesName);

		if (additionalTestSuiteName && it->first == *additionalTestSuiteName)
		{
			CryLog ("Writing additional test case to existing '%s' suite", additionalTestSuiteName->c_str());
			++ numFails; // Assumption here that any additional test data provided is a failure... [TF]
			++ numTests;
			testSuite->addChild(*additionalTestCase);
			additionalTestSuiteName = NULL;
		}

		testSuite->setAttr("tests", numTests);
		testSuite->setAttr("failures", numFails);
		testSuite->setAttr("errors", 0);
		testSuite->setAttr("time", 0);

		for (size_t i = 0; i < testSuiteStruct.m_testCases.size(); i++)
		{
			testSuite->addChild(testSuiteStruct.m_testCases[i]);
		}

		testSuites->addChild(testSuite);
	}

	if (additionalTestSuiteName) // Still haven't written our additional test case because the suite name didn't match any existing suite...
	{
		CryLog ("CAutoTester writing additional test case to unique '%s' suite", additionalTestSuiteName->c_str());
		XmlNodeRef testSuite = GetISystem()->CreateXmlNode("AutoTest");
		testSuite->setTag("testsuite");
		testSuite->setAttr("name", *additionalTestSuiteName);
		testSuite->setAttr("LevelName", mapName);
		testSuite->setAttr("GameRulesName", gameRulesName);
		testSuite->setAttr("tests", 1);
		testSuite->setAttr("failures", 1);
		testSuite->setAttr("errors", 0);
		testSuite->setAttr("time", 0);
		testSuite->addChild(*additionalTestCase);
		testSuites->addChild(testSuite);
	}

	char mapNameChrs[256];
	char gameRulesNameChrs[256];

	cry_strncpy(mapNameChrs, mapName, sizeof(mapNameChrs));
	cry_strncpy(gameRulesNameChrs, gameRulesName, sizeof(gameRulesNameChrs));

	for (int i=0; mapNameChrs[i]; i++)
	{
		if (mapNameChrs[i] == '/' || mapNameChrs[i] == ' ')
		{
			mapNameChrs[i] = '_'; 
		}
	}

	for (int i=0; gameRulesNameChrs[i]; i++)
	{
		if (gameRulesNameChrs[i] == '/' || gameRulesNameChrs[i] == ' ')
		{
			gameRulesNameChrs[i] = '_';
		}
	}

	const char * resultsCompleteFailMessage = NULL;

	if (flags & kWriteResultsFlag_unfinished)
	{
		resultsCompleteFailMessage = "Didn't finish all tests!";
	}
	else if (m_testSuites.size() == 0)
	{
		resultsCompleteFailMessage = "Ran zero tests!";
	}

	if (resultsCompleteFailMessage || m_writeResultsCompleteTestCasePass)
	{
		int numFailures = 0;
		XmlNodeRef testSuite = GetISystem()->CreateXmlNode("testsuite");
		testSuite->setAttr("name", "Are results complete?");

		XmlNodeRef unfinishedFailure = GetISystem()->CreateXmlNode("testcase");

		if (m_createVerboseFilename)
		{
			unfinishedFailure->setAttr("name", string().Format("%s_%s_%s%s%s", m_includeThisInFileName, mapNameChrs, gameRulesNameChrs, dedicatedText, serverText));
		}
		else
		{
			unfinishedFailure->setAttr("name", string().Format("%s%s", m_includeThisInFileName, dedicatedText));
		}

		if (resultsCompleteFailMessage)
		{
			XmlNodeRef failedCase = GetISystem()->CreateXmlNode("failure");
			failedCase->setAttr("type", "Unfinished");
			failedCase->setAttr("message", resultsCompleteFailMessage);
			unfinishedFailure->addChild(failedCase);
			++ numFailures;
		}
		else
		{
			unfinishedFailure->setAttr("status", "run");
		}

		testSuite->setAttr("tests", 1);
		testSuite->setAttr("errors", 0);
		testSuite->setAttr("time", 0);
		testSuite->setAttr("failures", numFailures);
		testSuite->addChild(unfinishedFailure);

		testSuites->addChild(testSuite);
	}

	string justTheFilename;

	if (m_createVerboseFilename)
	{
		justTheFilename.Format("autotest_%s_%s_%s%s%s.xml", m_includeThisInFileName, mapNameChrs, gameRulesNameChrs, dedicatedText, serverText); // TODO add datestamp if needed
	}
	else
	{
		// If m_createVerboseFilename is false, then only include things in the filename which WON'T CHANGE between the test starting and ending [TF]
		justTheFilename.Format("autotest_%s%s.xml", m_includeThisInFileName, dedicatedText);
	}





	m_outputPath.Format("../Xml-Reports/%s", justTheFilename.c_str());


#ifndef _RELEASE
	CryLogAlways ("Outputting test to %s", m_outputPath.c_str());
#endif

	SaveToValidXmlFile(testSuites, m_outputPath.c_str());

	if (flags & kWriteResultsFlag_writeDoneMarkerFile)
	{
		XmlNodeRef finished = GetISystem()->CreateXmlNode("Finished");
		SaveToValidXmlFile(finished, m_outputPath + ".done");

		if (m_quitWhenDone)
		{
			gEnv->pConsole->ExecuteString("unload");
			gEnv->pConsole->ExecuteString("quit");
		}
	}
}