Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
NUnit2XmlOutputWriter.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 #if SILICONSTUDIO_PLATFORM_IOS || SILICONSTUDIO_PLATFORM_ANDROID
4 // ***********************************************************************
5 // Copyright (c) 2011 Charlie Poole
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 // ***********************************************************************
26 
27 // Modified from original: output FullName for test-case
28 using System;
29 using System.Globalization;
30 using System.Reflection;
31 using System.Xml;
32 using System.IO;
33 using NUnit.Framework.Api;
34 using NUnit.Framework.Internal;
35 using System.Collections.Generic;
36 using NUnitLite.Runner;
37 
38 namespace SiliconStudio.Paradox.Graphics.Regression
39 {
40  /// <summary>
41  /// NUnit2XmlOutputWriter is able to create an xml file representing
42  /// the result of a test run in NUnit 2.x format.
43  /// </summary>
44  public class NUnit2XmlOutputWriter : OutputWriter
45  {
46  private XmlWriter xmlWriter;
47  private DateTime startTime;
48 
49  private static Dictionary<string, string> resultStates = new Dictionary<string, string>();
50 
51  static NUnit2XmlOutputWriter()
52  {
53  resultStates["Passed"] = "Success";
54  resultStates["Failed"] = "Failure";
55  resultStates["Failed:Error"] = "Error";
56  resultStates["Failed:Cancelled"] = "Cancelled";
57  resultStates["Inconclusive"] = "Inconclusive";
58  resultStates["Skipped"] = "Skipped";
59  resultStates["Skipped:Ignored"] = "Ignored";
60  resultStates["Skipped:Invalid"] = "NotRunnable";
61  }
62 
63  public NUnit2XmlOutputWriter(DateTime startTime)
64  {
65  this.startTime = startTime;
66  }
67 
68  /// <summary>
69  /// Writes the result of a test run to a specified TextWriter.
70  /// </summary>
71  /// <param name="result">The test result for the run</param>
72  /// <param name="writer">The TextWriter to which the xml will be written</param>
73  public override void WriteResultFile(ITestResult result, TextWriter writer)
74  {
75  // NOTE: Under .NET 1.1, XmlTextWriter does not implement IDisposable,
76  // but does implement Close(). Hence we cannot use a 'using' clause.
77  //using (XmlTextWriter xmlWriter = new XmlTextWriter(writer))
78 #if SILVERLIGHT
79  XmlWriter xmlWriter = XmlWriter.Create(writer);
80 #else
81  XmlTextWriter xmlWriter = new XmlTextWriter(writer);
82  xmlWriter.Formatting = Formatting.Indented;
83 #endif
84 
85  try
86  {
87  WriteXmlOutput(result, xmlWriter);
88  }
89  finally
90  {
91  writer.Close();
92  }
93  }
94 
95  private void WriteXmlOutput(ITestResult result, XmlWriter xmlWriter)
96  {
97  this.xmlWriter = xmlWriter;
98 
99  InitializeXmlFile(result);
100  WriteResultElement(result);
101  TerminateXmlFile();
102  }
103 
104  private void InitializeXmlFile(ITestResult result)
105  {
106  ResultSummary summaryResults = new ResultSummary(result);
107 
108  xmlWriter.WriteStartDocument(false);
109  xmlWriter.WriteComment("This file represents the results of running a test suite");
110 
111  xmlWriter.WriteStartElement("test-results");
112 
113  xmlWriter.WriteAttributeString("name", result.FullName);
114  xmlWriter.WriteAttributeString("total", summaryResults.TestCount.ToString());
115  xmlWriter.WriteAttributeString("errors", summaryResults.ErrorCount.ToString());
116  xmlWriter.WriteAttributeString("failures", summaryResults.FailureCount.ToString());
117  xmlWriter.WriteAttributeString("not-run", summaryResults.NotRunCount.ToString());
118  xmlWriter.WriteAttributeString("inconclusive", summaryResults.InconclusiveCount.ToString());
119  xmlWriter.WriteAttributeString("ignored", summaryResults.IgnoreCount.ToString());
120  xmlWriter.WriteAttributeString("skipped", summaryResults.SkipCount.ToString());
121  xmlWriter.WriteAttributeString("invalid", summaryResults.InvalidCount.ToString());
122 
123  xmlWriter.WriteAttributeString("date", XmlConvert.ToString(startTime, "yyyy-MM-dd"));
124  xmlWriter.WriteAttributeString("time", XmlConvert.ToString(startTime, "HH:mm:ss"));
125  WriteEnvironment();
126  WriteCultureInfo();
127  }
128 
129  private void WriteCultureInfo()
130  {
131  xmlWriter.WriteStartElement("culture-info");
132  xmlWriter.WriteAttributeString("current-culture",
133  CultureInfo.CurrentCulture.ToString());
134  xmlWriter.WriteAttributeString("current-uiculture",
135  CultureInfo.CurrentUICulture.ToString());
136  xmlWriter.WriteEndElement();
137  }
138 
139  private void WriteEnvironment()
140  {
141  xmlWriter.WriteStartElement("environment");
142  var assemblyName = AssemblyHelper.GetAssemblyName(Assembly.GetExecutingAssembly());
143  xmlWriter.WriteAttributeString("nunit-version",
144  assemblyName.Version.ToString());
145  xmlWriter.WriteAttributeString("clr-version",
146  Environment.Version.ToString());
147  xmlWriter.WriteAttributeString("os-version",
148  Environment.OSVersion.ToString());
149  xmlWriter.WriteAttributeString("platform",
150  Environment.OSVersion.Platform.ToString());
151 #if !NETCF
152  xmlWriter.WriteAttributeString("cwd",
153  Environment.CurrentDirectory);
154 #if !SILVERLIGHT
155  xmlWriter.WriteAttributeString("machine-name",
156  Environment.MachineName);
157  xmlWriter.WriteAttributeString("user",
158  Environment.UserName);
159  xmlWriter.WriteAttributeString("user-domain",
160  Environment.UserDomainName);
161 #endif
162 #endif
163  xmlWriter.WriteEndElement();
164  }
165 
166  private void WriteResultElement(ITestResult result)
167  {
168  StartTestElement(result);
169 
170  WriteCategories(result);
171  WriteProperties(result);
172 
173  switch (result.ResultState.Status)
174  {
175  case TestStatus.Skipped:
176  WriteReasonElement(result.Message);
177  break;
178  case TestStatus.Failed:
179  WriteFailureElement(result.Message, result.StackTrace);
180  break;
181  }
182 
183  if (result.Test is TestSuite)
184  WriteChildResults(result);
185 
186  xmlWriter.WriteEndElement(); // test element
187  }
188 
189  private void TerminateXmlFile()
190  {
191  xmlWriter.WriteEndElement(); // test-results
192  xmlWriter.WriteEndDocument();
193  xmlWriter.Flush();
194  xmlWriter.Close();
195  }
196 
197 
198  #region Element Creation Helpers
199 
200  private void StartTestElement(ITestResult result)
201  {
202  ITest test = result.Test;
203  TestSuite suite = test as TestSuite;
204 
205  if (suite != null)
206  {
207  xmlWriter.WriteStartElement("test-suite");
208  xmlWriter.WriteAttributeString("type", suite.TestType);
209  xmlWriter.WriteAttributeString("name", suite.TestType == "Assembly"
210  ? result.Test.FullName
211  : result.Test.Name);
212  }
213  else
214  {
215  xmlWriter.WriteStartElement("test-case");
216  xmlWriter.WriteAttributeString("name", result.FullName);
217  }
218 
219  if (test.Properties.ContainsKey(PropertyNames.Description))
220  {
221  string description = (string)test.Properties.Get(PropertyNames.Description);
222  xmlWriter.WriteAttributeString("description", description);
223  }
224 
225  TestStatus status = result.ResultState.Status;
226  string translatedResult = resultStates[result.ResultState.ToString()];
227 
228  if (status != TestStatus.Skipped)
229  {
230  xmlWriter.WriteAttributeString("executed", "True");
231  xmlWriter.WriteAttributeString("result", translatedResult);
232  xmlWriter.WriteAttributeString("success", status == TestStatus.Passed ? "True" : "False");
233  xmlWriter.WriteAttributeString("time", result.Duration.TotalSeconds.ToString("0.000", NumberFormatInfo.InvariantInfo));
234  xmlWriter.WriteAttributeString("asserts", result.AssertCount.ToString());
235  }
236  else
237  {
238  xmlWriter.WriteAttributeString("executed", "False");
239  xmlWriter.WriteAttributeString("result", translatedResult);
240  }
241  }
242 
243  private void WriteCategories(ITestResult result)
244  {
245  IPropertyBag properties = result.Test.Properties;
246 
247  if (properties.ContainsKey(PropertyNames.Category))
248  {
249  xmlWriter.WriteStartElement("categories");
250 
251  foreach (string category in properties[PropertyNames.Category])
252  {
253  xmlWriter.WriteStartElement("category");
254  xmlWriter.WriteAttributeString("name", category);
255  xmlWriter.WriteEndElement();
256  }
257 
258  xmlWriter.WriteEndElement();
259  }
260  }
261 
262  private void WriteProperties(ITestResult result)
263  {
264  IPropertyBag properties = result.Test.Properties;
265  int nprops = 0;
266 
267  foreach (string key in properties.Keys)
268  {
269  if (key != PropertyNames.Category)
270  {
271  if (nprops++ == 0)
272  xmlWriter.WriteStartElement("properties");
273 
274  foreach (object prop in properties[key])
275  {
276  xmlWriter.WriteStartElement("property");
277  xmlWriter.WriteAttributeString("name", key);
278  xmlWriter.WriteAttributeString("value", prop.ToString());
279  xmlWriter.WriteEndElement();
280  }
281  }
282  }
283 
284  if (nprops > 0)
285  xmlWriter.WriteEndElement();
286  }
287 
288  private void WriteReasonElement(string message)
289  {
290  xmlWriter.WriteStartElement("reason");
291  xmlWriter.WriteStartElement("message");
292  xmlWriter.WriteCData(message);
293  xmlWriter.WriteEndElement();
294  xmlWriter.WriteEndElement();
295  }
296 
297  private void WriteFailureElement(string message, string stackTrace)
298  {
299  xmlWriter.WriteStartElement("failure");
300  xmlWriter.WriteStartElement("message");
301  WriteCData(message);
302  xmlWriter.WriteEndElement();
303  xmlWriter.WriteStartElement("stack-trace");
304  if (stackTrace != null)
305  WriteCData(stackTrace);
306  xmlWriter.WriteEndElement();
307  xmlWriter.WriteEndElement();
308  }
309 
310  private void WriteChildResults(ITestResult result)
311  {
312  xmlWriter.WriteStartElement("results");
313 
314  foreach (ITestResult childResult in result.Children)
315  WriteResultElement(childResult);
316 
317  xmlWriter.WriteEndElement();
318  }
319 
320  #endregion
321 
322  #region Output Helpers
323  ///// <summary>
324  ///// Makes string safe for xml parsing, replacing control chars with '?'
325  ///// </summary>
326  ///// <param name="encodedString">string to make safe</param>
327  ///// <returns>xml safe string</returns>
328  //private static string CharacterSafeString(string encodedString)
329  //{
330  // /*The default code page for the system will be used.
331  // Since all code pages use the same lower 128 bytes, this should be sufficient
332  // for finding uprintable control characters that make the xslt processor error.
333  // We use characters encoded by the default code page to avoid mistaking bytes as
334  // individual characters on non-latin code pages.*/
335  // char[] encodedChars = System.Text.Encoding.Default.GetChars(System.Text.Encoding.Default.GetBytes(encodedString));
336 
337  // System.Collections.ArrayList pos = new System.Collections.ArrayList();
338  // for (int x = 0; x < encodedChars.Length; x++)
339  // {
340  // char currentChar = encodedChars[x];
341  // //unprintable characters are below 0x20 in Unicode tables
342  // //some control characters are acceptable. (carriage return 0x0D, line feed 0x0A, horizontal tab 0x09)
343  // if (currentChar < 32 && (currentChar != 9 && currentChar != 10 && currentChar != 13))
344  // {
345  // //save the array index for later replacement.
346  // pos.Add(x);
347  // }
348  // }
349  // foreach (int index in pos)
350  // {
351  // encodedChars[index] = '?';//replace unprintable control characters with ?(3F)
352  // }
353  // return System.Text.Encoding.Default.GetString(System.Text.Encoding.Default.GetBytes(encodedChars));
354  //}
355 
356  private void WriteCData(string text)
357  {
358  int start = 0;
359  while (true)
360  {
361  int illegal = text.IndexOf("]]>", start);
362  if (illegal < 0)
363  break;
364  xmlWriter.WriteCData(text.Substring(start, illegal - start + 2));
365  start = illegal + 2;
366  if (start >= text.Length)
367  return;
368  }
369 
370  if (start > 0)
371  xmlWriter.WriteCData(text.Substring(start));
372  else
373  xmlWriter.WriteCData(text);
374  }
375 
376  #endregion
377  }
378 }
379 #endif