using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Security; using System.Text; using UnityEngine; namespace UnityTest { public class XmlResultWriter { private readonly StringBuilder m_ResultWriter = new StringBuilder(); private int m_Indend; private readonly string m_SuiteName; private readonly ITestResult[] m_Results; string m_Platform; public XmlResultWriter(string suiteName, string platform, ITestResult[] results) { m_SuiteName = suiteName; m_Results = results; m_Platform = platform; } private const string k_NUnitVersion = "2.6.2-Unity"; public string GetTestResult() { InitializeXmlFile(m_SuiteName, new ResultSummarizer(m_Results)); foreach (var result in m_Results) { WriteResultElement(result); } TerminateXmlFile(); return m_ResultWriter.ToString(); } private void InitializeXmlFile(string resultsName, ResultSummarizer summaryResults) { WriteHeader(); DateTime now = DateTime.Now; var attributes = new Dictionary { {"name", "Unity Tests"}, {"total", summaryResults.TestsRun.ToString()}, {"errors", summaryResults.Errors.ToString()}, {"failures", summaryResults.Failures.ToString()}, {"not-run", summaryResults.TestsNotRun.ToString()}, {"inconclusive", summaryResults.Inconclusive.ToString()}, {"ignored", summaryResults.Ignored.ToString()}, {"skipped", summaryResults.Skipped.ToString()}, {"invalid", summaryResults.NotRunnable.ToString()}, {"date", now.ToString("yyyy-MM-dd")}, {"time", now.ToString("HH:mm:ss")} }; WriteOpeningElement("test-results", attributes); WriteEnvironment(m_Platform); WriteCultureInfo(); WriteTestSuite(resultsName, summaryResults); WriteOpeningElement("results"); } private void WriteOpeningElement(string elementName) { WriteOpeningElement(elementName, new Dictionary()); } private void WriteOpeningElement(string elementName, Dictionary attributes) { WriteOpeningElement(elementName, attributes, false); } private void WriteOpeningElement(string elementName, Dictionary attributes, bool closeImmediatelly) { WriteIndend(); m_Indend++; m_ResultWriter.Append("<"); m_ResultWriter.Append(elementName); foreach (var attribute in attributes) { m_ResultWriter.AppendFormat(" {0}=\"{1}\"", attribute.Key, SecurityElement.Escape(attribute.Value)); } if (closeImmediatelly) { m_ResultWriter.Append(" /"); m_Indend--; } m_ResultWriter.AppendLine(">"); } private void WriteIndend() { for (int i = 0; i < m_Indend; i++) { m_ResultWriter.Append(" "); } } private void WriteClosingElement(string elementName) { m_Indend--; WriteIndend(); m_ResultWriter.AppendLine(""); } private void WriteHeader() { m_ResultWriter.AppendLine(""); m_ResultWriter.AppendLine(""); } static string GetEnvironmentUserName() { return Environment.UserName; } static string GetEnvironmentMachineName() { return Environment.MachineName; } static string GetEnvironmentUserDomainName() { return Environment.UserDomainName; } static string GetEnvironmentVersion() { return Environment.Version.ToString(); } static string GetEnvironmentOSVersion() { return Environment.OSVersion.ToString(); } static string GetEnvironmentOSVersionPlatform() { return Environment.OSVersion.Platform.ToString(); } static string EnvironmentGetCurrentDirectory() { return Environment.CurrentDirectory; } private void WriteEnvironment( string targetPlatform ) { var attributes = new Dictionary { {"nunit-version", k_NUnitVersion}, {"clr-version", GetEnvironmentVersion()}, {"os-version", GetEnvironmentOSVersion()}, {"platform", GetEnvironmentOSVersionPlatform()}, {"cwd", EnvironmentGetCurrentDirectory()}, {"machine-name", GetEnvironmentMachineName()}, {"user", GetEnvironmentUserName()}, {"user-domain", GetEnvironmentUserDomainName()}, {"unity-version", Application.unityVersion}, {"unity-platform", targetPlatform} }; WriteOpeningElement("environment", attributes, true); } private void WriteCultureInfo() { var attributes = new Dictionary { {"current-culture", CultureInfo.CurrentCulture.ToString()}, {"current-uiculture", CultureInfo.CurrentUICulture.ToString()} }; WriteOpeningElement("culture-info", attributes, true); } private void WriteTestSuite(string resultsName, ResultSummarizer summaryResults) { var attributes = new Dictionary { {"name", resultsName}, {"type", "Assembly"}, {"executed", "True"}, {"result", summaryResults.Success ? "Success" : "Failure"}, {"success", summaryResults.Success ? "True" : "False"}, {"time", summaryResults.Duration.ToString("#####0.000", NumberFormatInfo.InvariantInfo)} }; WriteOpeningElement("test-suite", attributes); } private void WriteResultElement(ITestResult result) { StartTestElement(result); switch (result.ResultState) { case TestResultState.Ignored: case TestResultState.NotRunnable: case TestResultState.Skipped: WriteReasonElement(result); break; case TestResultState.Failure: case TestResultState.Error: case TestResultState.Cancelled: WriteFailureElement(result); break; case TestResultState.Success: case TestResultState.Inconclusive: if (result.Message != null) WriteReasonElement(result); break; }; WriteClosingElement("test-case"); } private void TerminateXmlFile() { WriteClosingElement("results"); WriteClosingElement("test-suite"); WriteClosingElement("test-results"); } #region Element Creation Helpers private void StartTestElement(ITestResult result) { var attributes = new Dictionary { {"name", result.FullName}, {"executed", result.Executed.ToString()} }; string resultString; switch (result.ResultState) { case TestResultState.Cancelled: resultString = TestResultState.Failure.ToString(); break; default: resultString = result.ResultState.ToString(); break; } attributes.Add("result", resultString); if (result.Executed) { attributes.Add("success", result.IsSuccess.ToString()); attributes.Add("time", result.Duration.ToString("#####0.000", NumberFormatInfo.InvariantInfo)); } WriteOpeningElement("test-case", attributes); } private void WriteReasonElement(ITestResult result) { WriteOpeningElement("reason"); WriteOpeningElement("message"); WriteCData(result.Message); WriteClosingElement("message"); WriteClosingElement("reason"); } private void WriteFailureElement(ITestResult result) { WriteOpeningElement("failure"); WriteOpeningElement("message"); WriteCData(result.Message); WriteClosingElement("message"); WriteOpeningElement("stack-trace"); if (result.StackTrace != null) WriteCData(StackTraceFilter.Filter(result.StackTrace)); WriteClosingElement("stack-trace"); WriteClosingElement("failure"); } #endregion private void WriteCData(string text) { if (string.IsNullOrEmpty(text)) return; m_ResultWriter.AppendFormat("", text); m_ResultWriter.AppendLine(); } public void WriteToFile(string resultDestiantion, string resultFileName) { try { var path = Path.Combine(resultDestiantion, resultFileName); Debug.Log("Saving results in " + path); File.WriteAllText(path, GetTestResult(), Encoding.UTF8); } catch (Exception e) { Debug.LogError("Error while opening file"); Debug.LogException(e); } } } }