[d16-5] Cherry-picked all xharness fixes done for Xml parsing.

This commit is contained in:
Manuel de la Pena 2020-02-07 16:25:14 -05:00 коммит произвёл GitHub
Родитель 4cc97e6767 1f80ebe15e
Коммит 768ee83dad
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 709 добавлений и 222 удалений

Просмотреть файл

@ -48,14 +48,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2" />
<PackageReference Include="System.Buffers" Version="4.4.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.0" />
<PackageReference Include="xunit.runner.utility" Version="2.4.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System">
@ -99,6 +101,9 @@
<Compile Include="templates\common\TestRunner.NUnit\ClassOrNamespaceFilter.cs">
<Link>TestRunner.NUnit\ClassOrNamespaceFilter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.NUnit\XmlOutputWriter.cs">
<Link>TestRunner.NUnit\XmlOutputWriter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.NUnit\TestMethodFilter.cs">
<Link>TestRunner.NUnit\TestMethodFilter.cs</Link>
</Compile>

Просмотреть файл

@ -122,15 +122,15 @@
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
<PackageReference Include="System.Buffers" Version="4.4.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.0" />
<PackageReference Include="xunit.runner.utility" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
@ -176,6 +176,9 @@
<Compile Include="templates\common\TestRunner.NUnit\TestMethodFilter.cs">
<Link>TestRunner.NUnit\TestMethodFilter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.NUnit\XmlOutputWriter.cs">
<Link>TestRunner.NUnit\XmlOutputWriter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.Core\Extensions.Bool.cs">
<Link>TestRunner.Core\Extensions.Bool.cs</Link>
</Compile>

Просмотреть файл

@ -128,15 +128,15 @@
<MtouchLink>SdkOnly</MtouchLink>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
<PackageReference Include="System.Buffers" Version="4.4.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.0" />
<PackageReference Include="xunit.runner.utility" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
@ -206,6 +206,9 @@
<Compile Include="templates\common\TestRunner.NUnit\TestMethodFilter.cs">
<Link>TestRunner.NUnit\TestMethodFilter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.NUnit\XmlOutputWriter.cs">
<Link>TestRunner.NUnit\XmlOutputWriter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.xUnit\XUnitFilter.cs">
<Link>TestRunner.xUnit\XUnitFilter.cs</Link>
</Compile>

Просмотреть файл

@ -135,15 +135,15 @@
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
<PackageReference Include="System.Buffers" Version="4.4.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.0" />
<PackageReference Include="xunit.runner.utility" Version="2.4.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
@ -189,6 +189,9 @@
<Compile Include="templates\common\TestRunner.NUnit\TestMethodFilter.cs">
<Link>TestRunner.NUnit\TestMethodFilter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.NUnit\XmlOutputWriter.cs">
<Link>TestRunner.NUnit\XmlOutputWriter.cs</Link>
</Compile>
<Compile Include="templates\common\TestRunner.Core\Extensions.Bool.cs">
<Link>TestRunner.Core\Extensions.Bool.cs</Link>
</Compile>

Просмотреть файл

@ -9,10 +9,8 @@ using System.Threading.Tasks;
using Foundation;
using NUnitLite.Runner;
using NUnit.Framework.Api;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.WorkItems;
using NUnit.Framework.Internal.Filters;
using NUnitTest = NUnit.Framework.Internal.Test;

Просмотреть файл

@ -0,0 +1,349 @@
// ***********************************************************************
// Copyright (c) 2011 Charlie Poole
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.IO;
using NUnit.Framework.Api;
using NUnit.Framework.Internal;
using NUnitLite.Runner;
#if CLR_2_0 || CLR_4_0
using System.Collections.Generic;
#else
using System.Collections.Specialized;
#endif
namespace Xamarin.iOS.UnitTests.NUnit {
/// <summary>
/// NUnit2XmlOutputWriter is able to create an xml file representing
/// the result of a test run in NUnit 2.x format.
/// </summary>
public class NUnit2XmlOutputWriter : OutputWriter {
private XmlWriter xmlWriter;
private DateTime startTime;
#if CLR_2_0 || CLR_4_0
private static Dictionary<string, string> resultStates = new Dictionary<string, string>();
#else
private static StringDictionary resultStates = new StringDictionary ();
#endif
static NUnit2XmlOutputWriter ()
{
resultStates ["Passed"] = "Success";
resultStates ["Failed"] = "Failure";
resultStates ["Failed:Error"] = "Error";
resultStates ["Failed:Cancelled"] = "Cancelled";
resultStates ["Inconclusive"] = "Inconclusive";
resultStates ["Skipped"] = "Skipped";
resultStates ["Skipped:Ignored"] = "Ignored";
resultStates ["Skipped:Invalid"] = "NotRunnable";
}
public NUnit2XmlOutputWriter (DateTime startTime)
{
this.startTime = startTime;
}
/// <summary>
/// Writes the result of a test run to a specified TextWriter.
/// </summary>
/// <param name="result">The test result for the run</param>
/// <param name="writer">The TextWriter to which the xml will be written</param>
public override void WriteResultFile (ITestResult result, TextWriter writer)
{
// NOTE: Under .NET 1.1, XmlTextWriter does not implement IDisposable,
// but does implement Close(). Hence we cannot use a 'using' clause.
//using (XmlTextWriter xmlWriter = new XmlTextWriter(writer))
#if SILVERLIGHT
XmlWriter xmlWriter = XmlWriter.Create(writer);
#else
XmlTextWriter xmlWriter = new XmlTextWriter (writer);
xmlWriter.Formatting = Formatting.Indented;
#endif
try {
WriteXmlOutput (result, xmlWriter);
} finally {
writer.Close ();
}
}
private void WriteXmlOutput (ITestResult result, XmlWriter xmlWriter)
{
this.xmlWriter = xmlWriter;
InitializeXmlFile (result);
WriteResultElement (result);
TerminateXmlFile ();
}
private void InitializeXmlFile (ITestResult result)
{
ResultSummary summaryResults = new ResultSummary (result);
xmlWriter.WriteStartDocument (false);
xmlWriter.WriteComment ("This file represents the results of running a test suite");
xmlWriter.WriteStartElement ("test-results");
xmlWriter.WriteAttributeString ("name", result.FullName);
xmlWriter.WriteAttributeString ("total", summaryResults.TestCount.ToString ());
xmlWriter.WriteAttributeString ("errors", summaryResults.ErrorCount.ToString ());
xmlWriter.WriteAttributeString ("failures", summaryResults.FailureCount.ToString ());
xmlWriter.WriteAttributeString ("not-run", summaryResults.NotRunCount.ToString ());
xmlWriter.WriteAttributeString ("inconclusive", summaryResults.InconclusiveCount.ToString ());
xmlWriter.WriteAttributeString ("ignored", summaryResults.IgnoreCount.ToString ());
xmlWriter.WriteAttributeString ("skipped", summaryResults.SkipCount.ToString ());
xmlWriter.WriteAttributeString ("invalid", summaryResults.InvalidCount.ToString ());
xmlWriter.WriteAttributeString ("date", XmlConvert.ToString (startTime, "yyyy-MM-dd"));
xmlWriter.WriteAttributeString ("time", XmlConvert.ToString (startTime, "HH:mm:ss"));
WriteEnvironment ();
WriteCultureInfo ();
xmlWriter.Flush ();
}
private void WriteCultureInfo ()
{
xmlWriter.WriteStartElement ("culture-info");
xmlWriter.WriteAttributeString ("current-culture",
CultureInfo.CurrentCulture.ToString ());
xmlWriter.WriteAttributeString ("current-uiculture",
CultureInfo.CurrentUICulture.ToString ());
xmlWriter.WriteEndElement ();
xmlWriter.Flush ();
}
private void WriteEnvironment ()
{
xmlWriter.WriteStartElement ("environment");
AssemblyName assemblyName = AssemblyHelper.GetAssemblyName (Assembly.GetExecutingAssembly ());
xmlWriter.WriteAttributeString ("nunit-version",
assemblyName.Version.ToString ());
xmlWriter.WriteAttributeString ("clr-version",
Environment.Version.ToString ());
xmlWriter.WriteAttributeString ("os-version",
Environment.OSVersion.ToString ());
xmlWriter.WriteAttributeString ("platform",
Environment.OSVersion.Platform.ToString ());
#if !NETCF
xmlWriter.WriteAttributeString ("cwd",
Environment.CurrentDirectory);
#if !SILVERLIGHT
xmlWriter.WriteAttributeString ("machine-name",
Environment.MachineName);
xmlWriter.WriteAttributeString ("user",
Environment.UserName);
xmlWriter.WriteAttributeString ("user-domain",
Environment.UserDomainName);
#endif
#endif
xmlWriter.WriteEndElement ();
xmlWriter.Flush ();
}
private void WriteResultElement (ITestResult result)
{
StartTestElement (result);
WriteProperties (result);
switch (result.ResultState.Status) {
case TestStatus.Skipped:
WriteReasonElement (result.Message);
break;
case TestStatus.Failed:
WriteFailureElement (result.Message, result.StackTrace);
break;
}
if (result.Test is TestSuite)
WriteChildResults (result);
xmlWriter.WriteEndElement (); // test element
xmlWriter.Flush ();
}
private void TerminateXmlFile ()
{
xmlWriter.WriteEndElement (); // test-results
xmlWriter.WriteEndDocument ();
xmlWriter.Flush ();
xmlWriter.Close ();
}
#region Element Creation Helpers
private void StartTestElement (ITestResult result)
{
ITest test = result.Test;
TestSuite suite = test as TestSuite;
if (suite != null) {
xmlWriter.WriteStartElement ("test-suite");
xmlWriter.WriteAttributeString ("type", suite.TestType);
xmlWriter.WriteAttributeString ("name", suite.TestType == "Assembly"
? result.Test.FullName
: result.Test.Name);
} else {
xmlWriter.WriteStartElement ("test-case");
xmlWriter.WriteAttributeString ("name", result.Name);
}
if (test.Properties.ContainsKey (PropertyNames.Description)) {
string description = (string)test.Properties.Get (PropertyNames.Description);
xmlWriter.WriteAttributeString ("description", description);
}
TestStatus status = result.ResultState.Status;
string translatedResult = resultStates [result.ResultState.ToString ()];
if (status != TestStatus.Skipped) {
xmlWriter.WriteAttributeString ("executed", "True");
xmlWriter.WriteAttributeString ("result", translatedResult);
xmlWriter.WriteAttributeString ("success", status == TestStatus.Passed ? "True" : "False");
xmlWriter.WriteAttributeString ("time", result.Duration.TotalSeconds.ToString ());
xmlWriter.WriteAttributeString ("asserts", result.AssertCount.ToString ());
} else {
xmlWriter.WriteAttributeString ("executed", "False");
xmlWriter.WriteAttributeString ("result", translatedResult);
}
}
private void WriteProperties (ITestResult result)
{
IPropertyBag properties = result.Test.Properties;
int nprops = 0;
foreach (string key in properties.Keys) {
if (key != PropertyNames.Category) {
if (nprops++ == 0)
xmlWriter.WriteStartElement ("properties");
foreach (object prop in properties [key]) {
xmlWriter.WriteStartElement ("property");
xmlWriter.WriteAttributeString ("name", key);
xmlWriter.WriteAttributeString ("value", prop.ToString ());
xmlWriter.WriteEndElement ();
}
}
}
if (nprops > 0)
xmlWriter.WriteEndElement ();
}
private void WriteReasonElement (string message)
{
xmlWriter.WriteStartElement ("reason");
xmlWriter.WriteStartElement ("message");
xmlWriter.WriteCData (message);
xmlWriter.WriteEndElement ();
xmlWriter.WriteEndElement ();
}
private void WriteFailureElement (string message, string stackTrace)
{
xmlWriter.WriteStartElement ("failure");
xmlWriter.WriteStartElement ("message");
WriteCData (message);
xmlWriter.WriteEndElement ();
xmlWriter.WriteStartElement ("stack-trace");
if (stackTrace != null)
WriteCData (stackTrace);
xmlWriter.WriteEndElement ();
xmlWriter.WriteEndElement ();
}
private void WriteChildResults (ITestResult result)
{
xmlWriter.WriteStartElement ("results");
foreach (ITestResult childResult in result.Children)
WriteResultElement (childResult);
xmlWriter.WriteEndElement ();
}
#endregion
#region Output Helpers
///// <summary>
///// Makes string safe for xml parsing, replacing control chars with '?'
///// </summary>
///// <param name="encodedString">string to make safe</param>
///// <returns>xml safe string</returnCs>
//private static string CharacterSafeString(string encodedString)
//{
// /*The default code page for the system will be used.
// Since all code pages use the same lower 128 bytes, this should be sufficient
// for finding uprintable control characters that make the xslt processor error.
// We use characters encoded by the default code page to avoid mistaking bytes as
// individual characters on non-latin code pages.*/
// char[] encodedChars = System.Text.Encoding.Default.GetChars(System.Text.Encoding.Default.GetBytes(encodedString));
// System.Collections.ArrayList pos = new System.Collections.ArrayList();
// for (int x = 0; x < encodedChars.Length; x++)
// {
// char currentChar = encodedChars[x];
// //unprintable characters are below 0x20 in Unicode tables
// //some control characters are acceptable. (carriage return 0x0D, line feed 0x0A, horizontal tab 0x09)
// if (currentChar < 32 && (currentChar != 9 && currentChar != 10 && currentChar != 13))
// {
// //save the array index for later replacement.
// pos.Add(x);
// }
// }
// foreach (int index in pos)
// {
// encodedChars[index] = '?';//replace unprintable control characters with ?(3F)
// }
// return System.Text.Encoding.Default.GetString(System.Text.Encoding.Default.GetBytes(encodedChars));
//}
private void WriteCData (string text)
{
int start = 0;
while (true) {
int illegal = text.IndexOf ("]]>", start);
if (illegal < 0)
break;
xmlWriter.WriteCData (text.Substring (start, illegal - start + 2));
start = illegal + 2;
if (start >= text.Length)
return;
}
if (start > 0)
xmlWriter.WriteCData (text.Substring (start));
else
xmlWriter.WriteCData (text);
}
#endregion
}
}

Просмотреть файл

@ -23,7 +23,7 @@ namespace Xamarin.iOS.UnitTests.XUnit
List<XUnitFilter> filters = new List<XUnitFilter> ();
bool runAssemblyByDefault;
public XUnitResultFileFormat ResultFileFormat { get; set; } = XUnitResultFileFormat.NUnit;
public XUnitResultFileFormat ResultFileFormat { get; set; } = XUnitResultFileFormat.XunitV2;
public AppDomainSupport AppDomainSupport { get; set; } = AppDomainSupport.Denied;
protected override string ResultsFileName { get; set; } = "TestResults.xUnit.xml";
@ -795,9 +795,10 @@ namespace Xamarin.iOS.UnitTests.XUnit
{
if (assembliesElement == null)
return String.Empty;
// remove all the empty nodes
assembliesElement.Descendants ().Where (e => e.Name == "collection" && !e.Descendants ().Any ()).Remove ();
string outputFilePath = GetResultsFilePath ();
var settings = new XmlWriterSettings { Indent = true };
var settings = new XmlWriterSettings { Indent = true};
using (var xmlWriter = XmlWriter.Create (outputFilePath, settings)) {
switch (ResultFileFormat) {
case XUnitResultFileFormat.XunitV2:
@ -818,6 +819,8 @@ namespace Xamarin.iOS.UnitTests.XUnit
{
if (assembliesElement == null)
return;
// remove all the empty nodes
assembliesElement.Descendants ().Where (e => e.Name == "collection" && !e.Descendants ().Any ()).Remove ();
var settings = new XmlWriterSettings { Indent = true };
using (var xmlWriter = XmlWriter.Create (writer, settings)) {
switch (ResultFileFormat) {
@ -837,7 +840,7 @@ namespace Xamarin.iOS.UnitTests.XUnit
}
}
}
void Transform_Results (string xsltResourceName, XElement element, XmlWriter writer)
{
var xmlTransform = new System.Xml.Xsl.XslCompiledTransform ();

Просмотреть файл

@ -334,120 +334,9 @@ namespace xharness
}
}
bool IsTouchUnitResult (StreamReader stream)
(string resultLine, bool failed, bool crashed) ParseResult (string test_log_path, bool timed_out, bool crashed)
{
// TouchUnitTestRun is the very first node in the TouchUnit xml result
// which is not preset in the xunit xml, therefore we know the runner
// quite quickly
bool isTouchUnit = false;
using (var reader = XmlReader.Create (stream)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "TouchUnitTestRun") {
isTouchUnit = true;
break;
}
}
}
// we want to reuse the stream (and we are sync)
stream.BaseStream.Position = 0;
stream.DiscardBufferedData ();
return isTouchUnit;
}
(string resultLine, bool failed) ParseTouchUnitXml (StreamReader stream, StreamWriter writer)
{
long total, errors, failed, notRun, inconclusive, ignored, skipped, invalid;
total = errors = failed = notRun = inconclusive = ignored = skipped = invalid = 0L;
using (var reader = XmlReader.Create (stream)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-results") {
total = long.Parse (reader ["total"]);
errors = long.Parse (reader ["errors"]);
failed = long.Parse (reader ["failures"]);
notRun = long.Parse (reader ["not-run"]);
inconclusive = long.Parse (reader ["inconclusive"]);
ignored = long.Parse (reader ["ignored"]);
skipped = long.Parse (reader ["skipped"]);
invalid = long.Parse (reader ["invalid"]);
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "TouchUnitExtraData") {
// move fwd to get to the CData
if (reader.Read ())
writer.Write (reader.Value);
}
}
}
var passed = total - errors - failed - notRun - inconclusive - ignored - skipped - invalid;
var resultLine = $"Tests run: {total} Passed: {passed} Inconclusive: {inconclusive} Failed: {failed + errors} Ignored: {ignored + skipped + invalid}";
return (resultLine, total == 0 || errors != 0 || failed != 0);
}
(string resultLine, bool failed) ParseNUnitXml (StreamReader stream, StreamWriter writer)
{
long total, errors, failed, notRun, inconclusive, ignored, skipped, invalid;
total = errors = failed = notRun = inconclusive = ignored = skipped = invalid = 0L;
using (var reader = XmlReader.Create (stream)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-results") {
total = long.Parse (reader ["total"]);
errors = long.Parse (reader ["errors"]);
failed = long.Parse (reader ["failures"]);
notRun = long.Parse (reader ["not-run"]);
inconclusive = long.Parse (reader ["inconclusive"]);
ignored = long.Parse (reader ["ignored"]);
skipped = long.Parse (reader ["skipped"]);
invalid = long.Parse (reader ["invalid"]);
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-suite" && (reader["type"] == "TestFixture" || reader["type"] == "TestCollection")) {
var testCaseName = reader ["name"];
writer.WriteLine (testCaseName);
var time = reader.GetAttribute ("time") ?? "0"; // some nodes might not have the time :/
// get the first node and then move in the siblings of the same type
reader.ReadToDescendant ("test-case");
do {
if (reader.Name != "test-case")
break;
// read the test cases in the current node
var status = reader ["result"];
switch (status) {
case "Success":
writer.Write ("\t[PASS] ");
break;
case "Ignored":
writer.Write ("\t[IGNORED] ");
break;
case "Error":
case "Failure":
writer.Write ("\t[FAIL] ");
break;
case "Inconclusive":
writer.Write ("\t[INCONCLUSIVE] ");
break;
default:
writer.Write ("\t[INFO] ");
break;
}
writer.Write (reader ["name"]);
if (status == "Failure" || status == "Error") { // we need to print the message
writer.Write ($" : {reader.ReadElementContentAsString ()}");
}
// add a new line
writer.WriteLine ();
} while (reader.ReadToNextSibling ("test-case"));
writer.WriteLine ($"{testCaseName} {time} ms");
}
}
}
var passed = total - errors - failed - notRun - inconclusive - ignored - skipped - invalid;
string resultLine = $"Tests run: {total} Passed: {passed} Inconclusive: {inconclusive} Failed: {failed + errors} Ignored: {ignored + skipped + invalid}";
writer.WriteLine (resultLine);
return (resultLine, total == 0 | errors != 0 || failed != 0);
}
(string resultLine, bool failed, bool crashed) ParseResult (Log listener_log, bool timed_out, bool crashed)
{
if (!File.Exists (listener_log.FullPath))
if (!File.Exists (test_log_path))
return (null, false, true); // if we do not have a log file, the test crashes
// parsing the result is different if we are in jenkins or not.
@ -457,58 +346,47 @@ namespace xharness
// wraps the NUnit xml output with additional information, which we need to unwrap so that Jenkins understands it.
//
// On the other hand, the nunit and xunit do not have that data and have to be parsed.
if (Harness.InJenkins) {
(string resultLine, bool failed, bool crashed) parseResult = (null, false, false);
// move the xml to a tmp path, that path will be use to read the xml
// in the reader, and the writer will use the stream from the logger to
// write the human readable log
var tmpFile = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ());
//
// This if statement has a small trick, we found out that internet sharing in some of the bots (VSTS) does not work, in
// that case, we cannot do a TCP connection to xharness to get the log, this is a problem since if we did not get the xml
// from the TCP connection, we are going to fail when trying to read it and not parse it. Therefore, we are not only
// going to check if we are in CI, but also if the listener_log is valid.
var path = Path.ChangeExtension (test_log_path, "xml");
XmlResultParser.CleanXml (test_log_path, path);
File.Move (listener_log.FullPath, tmpFile);
if (Harness.InCI && XmlResultParser.IsValidXml (path, out var xmlType)) {
(string resultLine, bool failed, bool crashed) parseResult = (null, false, false);
crashed = false;
try {
using (var streamReaderTmp = new StreamReader (tmpFile)) {
var isTouchUnit = IsTouchUnitResult (streamReaderTmp); // method resets position
using (var writer = new StreamWriter (listener_log.FullPath, true)) { // write the human result to the log file
if (isTouchUnit) {
var (resultLine, failed)= ParseTouchUnitXml (streamReaderTmp, writer);
parseResult.resultLine = resultLine;
parseResult.failed = failed;
} else {
var (resultLine, failed)= ParseNUnitXml (streamReaderTmp, writer);
parseResult.resultLine = resultLine;
parseResult.failed = failed;
}
}
// reset pos of the stream
streamReaderTmp.BaseStream.Position = 0;
streamReaderTmp.DiscardBufferedData ();
var path = listener_log.FullPath;
path = Path.ChangeExtension (path, "xml");
// both the nunit and xunit runners are not
// setting the test results correctly, lets add them
using (var xmlWriter = new StreamWriter (path)) {
string line;
while ((line = streamReaderTmp.ReadLine ()) != null) {
if (line.Contains ("<test-results")) {
if (line.Contains ("name=\"\"")) { // NUnit case
xmlWriter.WriteLine (line.Replace ("name=\"\"", $"name=\"{appName + " " + configuration}\""));
xmlWriter.WriteLine (line);
} else if (line.Contains ($"name=\"com.xamarin.bcltests.{appName}\"")) { // xunit case
xmlWriter.WriteLine (line.Replace ($"name=\"com.xamarin.bcltests.{appName}\"", $"name=\"{appName + " " + configuration}\""));
}
} else {
xmlWriter.WriteLine (line);
}
}
}
// we do not longer need the tmp file
Logs.AddFile (path, "Test xml");
}
var newFilename = XmlResultParser.GetXmlFilePath (path, xmlType);
// rename the path to the correct value
File.Move (path, newFilename);
path = newFilename;
// write the human readable results in a tmp file, which we later use to step on the logs
var tmpFile = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ());
(parseResult.resultLine, parseResult.failed) = XmlResultParser.GenerateHumanReadableResults (path, tmpFile, xmlType);
File.Copy (tmpFile, test_log_path, true);
File.Delete (tmpFile);
// we do not longer need the tmp file
Logs.AddFile (path, "Test xml");
return parseResult;
} catch (Exception e) {
main_log.WriteLine ("Could not parse xml result file: {0}", e);
// print file for better debugging
main_log.WriteLine ("File data is:");
main_log.WriteLine (new string ('#', 10));
using (var stream = new StreamReader (path)) {
string line;
while ((line = stream.ReadLine ()) != null) {
main_log.WriteLine (line);
}
}
main_log.WriteLine (new string ('#', 10));
main_log.WriteLine ("End of xml results.");
if (timed_out) {
Harness.LogWrench ($"@MonkeyWrench: AddSummary: <b><i>{mode} timed out</i></b><br/>");
return parseResult;
@ -519,17 +397,16 @@ namespace xharness
parseResult.crashed = true;
return parseResult;
}
} finally {
if (File.Exists (tmpFile))
File.Delete (tmpFile);
}
} else {
// delete not needed copy
File.Delete (path);
// not the most efficient way but this just happens when we run
// the tests locally and we usually do not run all tests, we are
// more interested to be efficent on the bots
string resultLine = null;
using (var reader = new StreamReader (listener_log.FullPath)) {
using (var reader = new StreamReader (test_log_path)) {
string line = null;
bool failed = false;
while ((line = reader.ReadLine ()) != null)
@ -548,9 +425,9 @@ namespace xharness
}
}
public bool TestsSucceeded (Log listener_log, bool timed_out, bool crashed)
public bool TestsSucceeded (string test_log_path, bool timed_out, bool crashed)
{
var (resultLine, failed, crashed_out) = ParseResult (listener_log, timed_out, crashed);
var (resultLine, failed, crashed_out) = ParseResult (test_log_path, timed_out, crashed);
// read the parsed logs in a human readable way
if (resultLine != null) {
var tests_run = resultLine.Replace ("Tests run: ", "");
@ -614,7 +491,7 @@ namespace xharness
args.Add ("-argument=-app-arg:-enablenetwork");
args.Add ("-setenv=NUNIT_ENABLE_NETWORK=true");
// detect if we are using a jenkins bot.
var useXmlOutput = Harness.InJenkins;
var useXmlOutput = Harness.InCI;
if (useXmlOutput) {
args.Add ("-setenv=NUNIT_ENABLE_XML_OUTPUT=true");
args.Add ("-setenv=NUNIT_ENABLE_XML_MODE=wrapped");
@ -899,7 +776,7 @@ namespace xharness
var crashed = false;
if (File.Exists (listener_log.FullPath)) {
Harness.LogWrench ("@MonkeyWrench: AddFile: {0}", listener_log.FullPath);
success = TestsSucceeded (listener_log, timed_out, crashed);
success = TestsSucceeded (listener_log.FullPath, timed_out, crashed);
} else if (timed_out) {
Harness.LogWrench ("@MonkeyWrench: AddSummary: <b><i>{0} never launched</i></b><br/>", mode);
main_log.WriteLine ("Test run never launched");
@ -1103,4 +980,4 @@ namespace xharness
copy_to.Write (buffer, offset, count);
}
}
}
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace xharness.BCLTestImporter {
MacMonoSDKPath = Harness.MONO_MAC_SDK_DESTDIR,
Override = true,
GuidGenerator = Harness.NewStableGuid,
GroupTests = Harness.InJenkins || Harness.UseGroupedApps,
GroupTests = Harness.InCI || Harness.UseGroupedApps,
};
}

Просмотреть файл

@ -115,7 +115,7 @@ namespace xharness
public Harness ()
{
LaunchTimeout = InWrench ? 3 : 120;
LaunchTimeout = InCI ? 3 : 120;
}
public bool GetIncludeSystemPermissionTests (TestPlatform platform, bool device)
@ -667,25 +667,11 @@ namespace xharness
public void LogWrench (string message)
{
if (!InWrench)
if (!InCI)
return;
Console.WriteLine (message);
}
public bool InWrench {
get {
var buildRev = Environment.GetEnvironmentVariable ("BUILD_REVISION");
return !string.IsNullOrEmpty (buildRev) && buildRev != "jenkins";
}
}
public bool InJenkins {
get {
var buildRev = Environment.GetEnvironmentVariable ("BUILD_REVISION");
return !string.IsNullOrEmpty (buildRev) && buildRev == "jenkins";
}
}
public bool InCI {
get {

Просмотреть файл

@ -1153,7 +1153,7 @@ namespace xharness
try {
Directory.CreateDirectory (LogDirectory);
Log log = Logs.Create ($"Harness-{Harness.Timestamp}.log", "Harness log");
if (Harness.InWrench)
if (Harness.InCI)
log = Log.CreateAggregatedLog (log, new ConsoleLog ());
Harness.HarnessLog = MainLog = log;
@ -1161,7 +1161,7 @@ namespace xharness
if (IsServerMode)
tasks.Add (RunTestServer ());
if (Harness.InJenkins) {
if (Harness.InCI) {
Task.Factory.StartNew (async () => {
while (true) {
await Task.Delay (TimeSpan.FromMinutes (10));
@ -3735,7 +3735,7 @@ namespace xharness
}
// Also clean up after us locally.
if (Harness.InJenkins || Harness.InWrench || (Jenkins.CleanSuccessfulTestRuns && Succeeded))
if (Harness.InCI || (Jenkins.CleanSuccessfulTestRuns && Succeeded))
await BuildTask.CleanAsync ();
}
}

Просмотреть файл

@ -211,7 +211,7 @@ namespace xharness
public static void CreateMakefile (Harness harness, IEnumerable<UnifiedTarget> unified_targets, IEnumerable<TVOSTarget> tvos_targets, IEnumerable<WatchOSTarget> watchos_targets, IEnumerable<TodayExtensionTarget> today_targets)
{
var executeSim32 = !harness.InWrench; // Waiting for iOS 10.3 simulator to be installed on wrench
var executeSim32 = !harness.InCI; // Waiting for iOS 10.3 simulator to be installed on wrench
var makefile = Path.Combine (Harness.RootDirectory, "Makefile.inc");
using (var writer = new StreamWriter (makefile, false, new UTF8Encoding (false))) {
writer.WriteLine (".stamp-configure-projects: Makefile xharness/xharness.exe");

Просмотреть файл

@ -34,6 +34,7 @@ namespace xharness
do {
Log.WriteLine ("Test log server listening on: {0}:{1}", Address, Port);
using (TcpClient client = server.AcceptTcpClient ()) {
client.ReceiveBufferSize = buffer.Length;
processed = Processing (client);
}
} while (!AutoExit || !processed);

Просмотреть файл

@ -0,0 +1,258 @@
using System;
using System.IO;
using System.Xml;
namespace xharness {
public static class XmlResultParser {
public enum Jargon {
TouchUnit,
NUnit,
xUnit,
Missing,
}
// test if the file is valid xml, or at least, that can be read it.
public static bool IsValidXml (string path, out Jargon type)
{
type = Jargon.Missing;
if (!File.Exists (path))
return false;
using (var stream = File.OpenText (path)) {
string line;
while ((line = stream.ReadLine ()) != null) { // special case when get got the tcp connection
if (line.Contains ("ping"))
continue;
if (line.Contains ("TouchUnitTestRun")) {
type = Jargon.TouchUnit;
return true;
}
if (line.Contains ("nunit-version")) {
type = Jargon.NUnit;
return true;
}
if (line.Contains ("xUnit")) {
type = Jargon.xUnit;
return true;
}
}
}
return false;
}
static (string resultLine, bool failed) ParseTouchUnitXml (StreamReader stream, StreamWriter writer)
{
long total, errors, failed, notRun, inconclusive, ignored, skipped, invalid;
total = errors = failed = notRun = inconclusive = ignored = skipped = invalid = 0L;
using (var reader = XmlReader.Create (stream)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-results") {
long.TryParse (reader ["total"], out total);
long.TryParse (reader ["errors"], out errors);
long.TryParse (reader ["failures"], out failed);
long.TryParse (reader ["not-run"], out notRun);
long.TryParse (reader ["inconclusive"], out inconclusive);
long.TryParse (reader ["ignored"], out ignored);
long.TryParse (reader ["skipped"], out skipped );
long.TryParse (reader ["invalid"], out invalid);
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "TouchUnitExtraData") {
// move fwd to get to the CData
if (reader.Read ())
writer.Write (reader.Value);
}
}
}
var passed = total - errors - failed - notRun - inconclusive - ignored - skipped - invalid;
var resultLine = $"Tests run: {total} Passed: {passed} Inconclusive: {inconclusive} Failed: {failed + errors} Ignored: {ignored + skipped + invalid}";
return (resultLine, total == 0 || errors != 0 || failed != 0);
}
static (string resultLine, bool failed) ParseNUnitXml (StreamReader stream, StreamWriter writer)
{
long total, errors, failed, notRun, inconclusive, ignored, skipped, invalid;
total = errors = failed = notRun = inconclusive = ignored = skipped = invalid = 0L;
XmlReaderSettings settings = new XmlReaderSettings ();
settings.ValidationType = ValidationType.None;
using (var reader = XmlReader.Create (stream, settings)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-results") {
long.TryParse (reader ["total"], out total);
long.TryParse (reader ["errors"], out errors);
long.TryParse (reader ["failures"], out failed);
long.TryParse (reader ["not-run"], out notRun);
long.TryParse (reader ["inconclusive"], out inconclusive);
long.TryParse (reader ["ignored"], out ignored);
long.TryParse (reader ["skipped"], out skipped);
long.TryParse (reader ["invalid"], out invalid);
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "test-suite" && (reader ["type"] == "TestFixture" || reader ["type"] == "TestCollection")) {
var testCaseName = reader ["name"];
writer.WriteLine (testCaseName);
var time = reader.GetAttribute ("time") ?? "0"; // some nodes might not have the time :/
// get the first node and then move in the siblings of the same type
reader.ReadToDescendant ("test-case");
do {
if (reader.Name != "test-case")
break;
// read the test cases in the current node
var status = reader ["result"];
switch (status) {
case "Success":
writer.Write ("\t[PASS] ");
break;
case "Ignored":
writer.Write ("\t[IGNORED] ");
break;
case "Error":
case "Failure":
writer.Write ("\t[FAIL] ");
break;
case "Inconclusive":
writer.Write ("\t[INCONCLUSIVE] ");
break;
default:
writer.Write ("\t[INFO] ");
break;
}
writer.Write (reader ["name"]);
if (status == "Failure" || status == "Error") { // we need to print the message
reader.ReadToDescendant ("message");
writer.Write ($" : {reader.ReadElementContentAsString ()}");
reader.ReadToNextSibling ("stack-trace");
writer.Write ($" : {reader.ReadElementContentAsString ()}");
}
// add a new line
writer.WriteLine ();
} while (reader.ReadToNextSibling ("test-case"));
writer.WriteLine ($"{testCaseName} {time} ms");
}
}
}
var passed = total - errors - failed - notRun - inconclusive - ignored - skipped - invalid;
string resultLine = $"Tests run: {total} Passed: {passed} Inconclusive: {inconclusive} Failed: {failed + errors} Ignored: {ignored + skipped + invalid}";
writer.WriteLine (resultLine);
return (resultLine, total == 0 | errors != 0 || failed != 0);
}
static (string resultLine, bool failed) ParsexUnitXml (StreamReader stream, StreamWriter writer)
{
long total, errors, failed, notRun, inconclusive, ignored, skipped, invalid;
total = errors = failed = notRun = inconclusive = ignored = skipped = invalid = 0L;
using (var reader = XmlReader.Create (stream)) {
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element && reader.Name == "assembly") {
long.TryParse (reader ["total"], out var assemblyCount);
total += assemblyCount;
long.TryParse (reader ["errors"], out var assemblyErrors);
errors += assemblyErrors;
long.TryParse (reader ["failed"], out var assemblyFailures);
failed += assemblyFailures;
long.TryParse (reader ["skipped"], out var assemblySkipped);
skipped += assemblySkipped;
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "collection") {
var testCaseName = reader ["name"].Replace ("Test collection for ", "");
writer.WriteLine (testCaseName);
var time = reader.GetAttribute ("time") ?? "0"; // some nodes might not have the time :/
// get the first node and then move in the siblings of the same type
reader.ReadToDescendant ("test");
do {
if (reader.Name != "test")
break;
// read the test cases in the current node
var status = reader ["result"];
switch (status) {
case "Pass":
writer.Write ("\t[PASS] ");
break;
case "Skip":
writer.Write ("\t[IGNORED] ");
break;
case "Fail":
writer.Write ("\t[FAIL] ");
break;
default:
writer.Write ("\t[FAIL] ");
break;
}
writer.Write (reader ["name"]);
if (status == "Fail") { // we need to print the message
reader.ReadToDescendant ("message");
writer.Write ($" : {reader.ReadElementContentAsString ()}");
reader.ReadToNextSibling ("stack-trace");
writer.Write ($" : {reader.ReadElementContentAsString ()}");
}
// add a new line
writer.WriteLine ();
} while (reader.ReadToNextSibling ("test"));
writer.WriteLine ($"{testCaseName} {time} ms");
}
}
}
var passed = total - errors - failed - notRun - inconclusive - ignored - skipped - invalid;
string resultLine = $"Tests run: {total} Passed: {passed} Inconclusive: {inconclusive} Failed: {failed + errors} Ignored: {ignored + skipped + invalid}";
writer.WriteLine (resultLine);
return (resultLine, total == 0 | errors != 0 || failed != 0);
}
public static string GetXmlFilePath (string path, Jargon xmlType)
{
var fileName = Path.GetFileName (path);
switch (xmlType) {
case Jargon.TouchUnit:
case Jargon.NUnit:
return path.Replace (fileName, $"nunit-{fileName}");
case Jargon.xUnit:
return path.Replace (fileName, $"xunit-{fileName}");
default:
return path;
}
}
public static void CleanXml (string source, string destination)
{
using (var reader = new StreamReader (source))
using (var writer = new StreamWriter (destination)) {
string line;
while ((line = reader.ReadLine ()) != null) {
if (line.StartsWith ("ping", StringComparison.Ordinal) || line.Contains ("TouchUnitTestRun") || line.Contains ("NUnitOutput") || line.Contains ("<!--")) {
continue;
}
if (line == "") // remove white lines, some files have them
continue;
if (line.Contains ("TouchUnitExtraData")) // always last node in TouchUnit
break;
writer.WriteLine (line);
}
}
}
public static (string resultLine, bool failed) GenerateHumanReadableResults (string source, string destination, Jargon xmlType)
{
(string resultLine, bool failed) parseData;
using (var reader = new StreamReader (source))
using (var writer = new StreamWriter (destination, true)) {
switch (xmlType) {
case Jargon.TouchUnit:
parseData = ParseTouchUnitXml (reader, writer);
break;
case Jargon.NUnit:
parseData = ParseNUnitXml (reader, writer);
break;
case Jargon.xUnit:
parseData = ParsexUnitXml (reader, writer);
break;
default:
parseData = ("", true);
break;
}
}
return parseData;
}
}
}

Просмотреть файл

@ -128,6 +128,7 @@
<Compile Include="..\..\tools\bcl-test-importer\BCLTestImporter\BCLTestInfoPlistGenerator.cs">
<Link>BCLTestImporter\BCLTestInfoPlistGenerator.cs</Link>
</Compile>
<Compile Include="XmlResultParser.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="BCLTestImporter\" />