From 2b6ce734b2d364b4f473dad8813ffa8e159f7f75 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 09:49:20 +0530 Subject: [PATCH 01/19] making classes internal --- .../Interfaces/IDataAttachment.cs | 2 +- .../Interfaces/IXmlTestStore.cs | 2 +- .../Interfaces/IXmlTestStoreCustom.cs | 2 +- .../Interfaces/XmlTestStoreParameters.cs | 2 +- .../ObjectModel/TestCategoryItems.cs | 4 ++-- .../ObjectModel/TestExecId.cs | 2 +- .../ObjectModel/TestId.cs | 2 +- .../ObjectModel/TestListCategory.cs | 2 +- .../ObjectModel/TestListCategoryId.cs | 2 +- .../ObjectModel/TestOutcome.cs | 2 +- .../ObjectModel/TestRun.cs | 4 ++-- .../ObjectModel/TestType.cs | 2 +- .../ObjectModel/UriDataAttachment.cs | 2 +- .../Utility/Collection.cs | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IDataAttachment.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IDataAttachment.cs index 735dc0048c..cae4bcd6c8 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IDataAttachment.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IDataAttachment.cs @@ -6,7 +6,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Interface used to define a data attachment. /// - public interface IDataAttachment + internal interface IDataAttachment { /// /// Gets the description for the attachment. diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStore.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStore.cs index bfaf94d069..cb020b977d 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStore.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStore.cs @@ -9,7 +9,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// Implementing this interface indicates a custom persistence logic is provided. /// The attribute based persistence is ignored in such a case. /// - public interface IXmlTestStore + internal interface IXmlTestStore { /// /// Saves the class under the XmlElement. diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStoreCustom.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStoreCustom.cs index ca5bcec137..7ff25b9fc0 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStoreCustom.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/IXmlTestStoreCustom.cs @@ -8,7 +8,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.XML /// /// Implementing this interface allows you to customize XmlStore persistence. /// - public interface IXmlTestStoreCustom + internal interface IXmlTestStoreCustom { /// /// Gets the name of the tag to use to persist this object. diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/XmlTestStoreParameters.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/XmlTestStoreParameters.cs index 11e8f37567..aa086638b4 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/XmlTestStoreParameters.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/XmlTestStoreParameters.cs @@ -15,7 +15,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// saved when 'MyClass.SaveDetails' parameter is set to 'true'. /// [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")] - public sealed class XmlTestStoreParameters : Dictionary + internal sealed class XmlTestStoreParameters : Dictionary { private XmlTestStoreParameters() { diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestCategoryItems.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestCategoryItems.cs index 9ebc88feaf..14ffe14426 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestCategoryItems.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestCategoryItems.cs @@ -14,7 +14,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Stores a string which categorizes the Test /// - public sealed class TestCategoryItem : IXmlTestStore + internal sealed class TestCategoryItem : IXmlTestStore { #region Fields [StoreXmlSimpleField(Location = "@TestCategory", DefaultValue = "")] @@ -123,7 +123,7 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter /// /// A collection of strings which categorize the test. /// - public sealed class TestCategoryItemCollection : EqtBaseCollection + internal sealed class TestCategoryItemCollection : EqtBaseCollection { #region Constructors /// diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestExecId.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestExecId.cs index a960834e99..dcf48aa29b 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestExecId.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestExecId.cs @@ -10,7 +10,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// Class identifying test execution id. /// Execution ID is assigned to test at run creation time and is guaranteed to be unique within that run. /// - public sealed class TestExecId + internal sealed class TestExecId { private static TestExecId emptyId = new TestExecId(Guid.Empty); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestId.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestId.cs index a871790b2f..6bfada65f3 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestId.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestId.cs @@ -15,7 +15,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Class that uniquely identifies a test. /// - public sealed class TestId : IEquatable, IComparable, IComparable, IXmlTestStore + internal sealed class TestId : IEquatable, IComparable, IComparable, IXmlTestStore { #region Constants diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategory.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategory.cs index df9eaf7406..22c88e0021 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategory.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategory.cs @@ -13,7 +13,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// The test list category. /// - public class TestListCategory : IXmlTestStore + internal class TestListCategory : IXmlTestStore { #region Fields diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategoryId.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategoryId.cs index cfad2cc563..d50fd06cf8 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategoryId.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestListCategoryId.cs @@ -9,7 +9,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Class to categorize the tests. /// - public sealed class TestListCategoryId + internal sealed class TestListCategoryId { private static TestListCategoryId emptyId = new TestListCategoryId(Guid.Empty); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestOutcome.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestOutcome.cs index b302628dc2..97b336b316 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestOutcome.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestOutcome.cs @@ -13,7 +13,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// NOTE: the order is important and is used for computing outcome for aggregations. /// More important outcomes come first. See TestOutcomeHelper.GetAggregationOutcome. /// - public enum TestOutcome + internal enum TestOutcome { /// /// There was a system error while we were trying to execute a test. diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestRun.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestRun.cs index d2d2500e7b..a40949ef3f 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestRun.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestRun.cs @@ -17,7 +17,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Class having information about a test run. /// - public sealed class TestRun + internal sealed class TestRun { #region Fields @@ -159,7 +159,7 @@ internal Guid Id /// /// Result directory. /// - internal string GetResultFilesDirectory(UnitTestResult result) + internal string GetResultFilesDirectory(TestResult result) { EqtAssert.ParameterNotNull(result, "result"); return Path.Combine(this.GetResultsDirectory(), result.RelativeTestResultsDirectory); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestType.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestType.cs index a7d6611009..2ca37de652 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestType.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestType.cs @@ -10,7 +10,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Class identifying test type. /// - public sealed class TestType : IXmlTestStore + internal sealed class TestType : IXmlTestStore { [StoreXmlSimpleField(".")] private Guid typeId; diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UriDataAttachment.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UriDataAttachment.cs index efe86e3862..918a0839a2 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UriDataAttachment.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UriDataAttachment.cs @@ -15,7 +15,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// Class that provides a basic implementation of IUriAttachment, which can be used by plugin /// writers to send any resource accessible by a URI as an attachment. /// - public class UriDataAttachment : IDataAttachment, IXmlTestStore + internal class UriDataAttachment : IDataAttachment, IXmlTestStore { #region Private fields diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs index 2cd1eeaf92..6d50491458 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs @@ -15,7 +15,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility /// Base class for Eqt Collections. /// Fast collection, default implementations (Add/Remove/etc) do not allow null items and ignore duplicates. /// - public class EqtBaseCollection : ICollection, IXmlTestStore + internal class EqtBaseCollection : ICollection, IXmlTestStore { #region private classes /// From c6dfed15f14db9ddfe50e85c71922e74509b4bc8 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 10:24:45 +0530 Subject: [PATCH 02/19] Model classes to be consumed by trx logger --- .../Interfaces/ITestAggregation.cs | 12 + .../Interfaces/ITestElement.cs | 21 + .../Interfaces/ITestResult.cs | 27 + .../Interfaces/ITestResultAggregation.cs | 13 + .../ObjectModel/OrderedTestElement.cs | 34 ++ .../ObjectModel/TestAggregation.cs | 33 ++ .../ObjectModel/TestElement.cs | 239 +++++++++ .../ObjectModel/TestEntry.cs | 30 +- .../ObjectModel/TestLink.cs | 97 ++++ .../ObjectModel/TestResult.cs | 474 +++++++++++++++++- .../ObjectModel/TestResultAggregation.cs | 37 ++ .../ObjectModel/UnitTestElement.cs | 315 +----------- .../ObjectModel/UnitTestResult.cs | 454 +---------------- .../Utility/Constants.cs | 55 ++ 14 files changed, 1081 insertions(+), 760 deletions(-) create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs create mode 100644 src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs new file mode 100644 index 0000000000..1007e6029f --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + internal interface ITestAggregation : ITestElement + { + List TestLinks { get; } // move to ITestElement + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs new file mode 100644 index 0000000000..0541c57e29 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + internal interface ITestElement + { + TestId Id { get; } + string Name { get; set; } + string Owner { get; set; } + int Priority { get; set; } + string Storage { get; set; } + TestExecId ExecutionId { get; set; } + TestExecId ParentExecutionId { get; set; } + bool IsRunnable { get; } + TestListCategoryId CategoryId { get; set; } + TestCategoryItemCollection TestCategories { get; } + TestType TestType { get; } + string Adapter { get; } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs new file mode 100644 index 0000000000..b45a8364e0 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + internal interface ITestResult + { + DateTime StartTime { get; set; } + DateTime EndTime { get; set; } + TimeSpan Duration { get; set; } + string ComputerName { get; } + TestOutcome Outcome { get; set; } + TestResultId Id { get; } + string ErrorMessage { get; set; } + string ErrorStackTrace { get; set; } + string[] TextMessages { get; set; } + string StdOut { get; set; } + string StdErr { get; set; } + string DebugTrace { get; set; } + string TestResultsDirectory { get; } + string RelativeTestResultsDirectory { get; } + int DataRowInfo { get; set; } + string ResultType { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs new file mode 100644 index 0000000000..f5775115c2 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + internal interface ITestResultAggregation : ITestResult + { + List InnerResults { get; } // move to itestresult + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs new file mode 100644 index 0000000000..f1d6077833 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + using System; + using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + + internal class OrderedTestElement : TestAggregation, IXmlTestStoreCustom + { + private static readonly Guid TestTypeGuid = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); // move to constants + private static readonly TestType TestTypeInstance = new TestType(TestTypeGuid); + + public OrderedTestElement(Guid id, string name, string adapter) : base(id, name, adapter) { } + + string IXmlTestStoreCustom.ElementName + { + get { return "OrderedTest"; } + } + + string IXmlTestStoreCustom.NamespaceUri + { + get { return null; } + } + + /// + /// Gets the test type. + /// + public override TestType TestType + { + get { return TestTypeInstance; } + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs new file mode 100644 index 0000000000..cb82503787 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + using System; + using System.Collections.Generic; + using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + + internal abstract class TestAggregation : TestElement, ITestAggregation + { + protected List testLinks = new List(); + + public TestAggregation( + Guid id, + string name, + string adapter) : base(id, name, adapter) { } + + public List TestLinks + { + get { return testLinks; } + } + + public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + base.Save(element, parameters); + + XmlPersistence h = new XmlPersistence(); + if (testLinks.Count > 0) + h.SaveIEnumerable(testLinks, element, "TestLinks", ".", "TestLink", parameters); + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs new file mode 100644 index 0000000000..90eb7403af --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + using System; + using System.Diagnostics; + using System.Globalization; + using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; + using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; + + internal abstract class TestElement : ITestElement, IXmlTestStore + { + /// + /// Default priority for a test method that does not specify a priority + /// + protected const int DefaultPriority = int.MaxValue; + + protected TestId id; + protected string name; + protected string owner; + protected int priority; + protected string storage; + protected TestExecId executionId; + protected TestExecId parentExecutionId; + protected bool isRunnable; + protected TestCategoryItemCollection testCategories; + protected string adapter; + protected TestListCategoryId catId; + + public TestElement( + Guid id, + string name, + string adapter) + { + Debug.Assert(!string.IsNullOrEmpty(name), "name is null"); + Debug.Assert(!string.IsNullOrEmpty(adapter), "adapter is null"); + + this.Initialize(); + + this.id = new TestId(id); + this.name = name; + this.adapter = adapter; + } + + + /// + /// Gets the id. + /// + public TestId Id + { + get { return this.id; } + } + + /// + /// Gets or sets the name. + /// + public string Name + { + get { return this.name; } + + set + { + EqtAssert.ParameterNotNull(value, "Name"); + this.name = value; + } + } + + /// + /// Gets or sets the owner. + /// + public string Owner + { + get { return this.owner; } + + set + { + EqtAssert.ParameterNotNull(value, "Owner"); + this.owner = value; + } + } + + /// + /// Gets or sets the priority. + /// + public int Priority + { + get { return this.priority; } + set { this.priority = value; } + } + + /// + /// Gets or sets the storage. + /// + public string Storage + { + get { return this.storage; } + + set + { + EqtAssert.StringNotNullOrEmpty(value, "Storage"); + this.storage = value.ToLowerInvariant(); + } + } + + /// + /// Gets or sets the execution id. + /// + public TestExecId ExecutionId // remove variables for properties if not required + { + get { return this.executionId; } + set { this.executionId = value; } + } + + /// + /// Gets or sets the parent execution id. + /// + public TestExecId ParentExecutionId + { + get { return this.parentExecutionId; } + set { this.parentExecutionId = value; } + } + + public bool IsRunnable + { + get { return this.isRunnable; } + } + + /// + /// Gets or sets the category id. + /// + /// + /// Instead of setting to null use TestListCategoryId.Uncategorized + /// + public TestListCategoryId CategoryId + { + get { return this.catId; } + + set + { + EqtAssert.ParameterNotNull(value, "CategoryId"); + this.catId = value; + } + } + + /// + /// Gets or sets the test categories. + /// + public TestCategoryItemCollection TestCategories + { + get { return this.testCategories; } + + set + { + EqtAssert.ParameterNotNull(value, "value"); + this.testCategories = value; + } + } + + public string Adapter + { + get { return adapter; } + } + + public abstract TestType TestType { get; } + + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "'{0}' {1}", + this.name != null ? this.name : TrxLoggerResources.Common_NullInMessages, + this.id != null ? this.id.ToString() : TrxLoggerResources.Common_NullInMessages); + } + + /// + /// Override for Equals. + /// + /// + /// The object to compare. + /// + /// + /// The . + /// + public override bool Equals(object other) + { + TestElement otherTest = other as TestElement; + return (otherTest == null) ? + false : + this.id.Equals(otherTest.id); + } + + /// + /// Override for GetHashCode + /// + /// + /// The . + /// + public override int GetHashCode() + { + return this.id.GetHashCode(); + } + + public virtual void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + XmlPersistence h = new XmlPersistence(); + + h.SaveSimpleField(element, "@name", this.name, null); + h.SaveSimpleField(element, "@storage", this.storage, string.Empty); + h.SaveSimpleField(element, "@priority", this.priority, DefaultPriority); + h.SaveSimpleField(element, "Owners/Owner/@name", this.owner, string.Empty); + h.SaveObject(this.testCategories, element, "TestCategory", parameters); + + if (this.executionId != null) + h.SaveGuid(element, "Execution/@id", this.executionId.Id); + if (this.parentExecutionId != null) + h.SaveGuid(element, "Execution/@parentId", this.parentExecutionId.Id); + + XmlTestStoreParameters testIdParameters = XmlTestStoreParameters.GetParameters(); + testIdParameters[TestId.IdLocationKey] = "@id"; + h.SaveObject(this.id, element, testIdParameters); + } + + private void Initialize() + { + this.id = TestId.Empty; + this.name = string.Empty; + this.owner = string.Empty; + this.priority = DefaultPriority; + this.storage = string.Empty; + this.executionId = TestExecId.Empty; + this.parentExecutionId = TestExecId.Empty; + this.testCategories = new TestCategoryItemCollection(); + this.isRunnable = true; + this.catId = TestListCategoryId.Uncategorized; + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs index 7b6b8d791d..d2a31e9b39 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs @@ -3,9 +3,9 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { + using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; - using Microsoft.TestPlatform.Extensions.TrxLogger.XML; /// @@ -17,7 +17,9 @@ internal sealed class TestEntry : IXmlTestStore private TestId testId; private TestExecId execId; + private TestExecId parentExecId; private TestListCategoryId categoryId; + private List testEntries = new List(); #endregion @@ -48,18 +50,34 @@ public TestEntry(TestId testId, TestListCategoryId catId) /// public TestExecId ExecId { - get + get { return this.execId; } + + set { - return this.execId; + Debug.Assert(value != null, "ExecId is null"); + this.execId = value; } + } + + /// + /// Gets or sets the parent exec id. + /// + public TestExecId ParentExecId + { + get { return this.parentExecId; } set { Debug.Assert(value != null, "ExecId is null"); - this.execId = value; + this.parentExecId = value; } } + public List TestEntries + { + get { return this.testEntries; } + } + #endregion #region Overrides @@ -127,7 +145,11 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter helper.SaveObject(this.testId, element, null); helper.SaveGuid(element, "@executionId", this.execId.Id); + if (parentExecId != null) + helper.SaveGuid(element, "@parentExecutionId", this.parentExecId.Id); helper.SaveGuid(element, "@testListId", this.categoryId.Id); + if (testEntries.Count > 0) + helper.SaveIEnumerable(testEntries, element, "TestEntries", ".", "TestEntry", parameters); } #endregion diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs new file mode 100644 index 0000000000..5de74fc666 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + using System; + using System.Diagnostics; + using System.Globalization; + using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; + using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + + internal sealed class TestLink : IXmlTestStore + { + private Guid id; + private string name = string.Empty; + private string storage = string.Empty; + + public TestLink(Guid id, string name, string storage) + { + if (id == Guid.Empty) + { + Debug.Assert(id != Guid.Empty, "id == Guid.Empty!"); + throw new ArgumentException("ID cant be empty"); // error resource? + } + + EqtAssert.StringNotNullOrEmpty(name, "name"); + EqtAssert.ParameterNotNull(storage, "storage"); + + this.id = id; + this.name = name; + this.storage = storage; + } + + public Guid Id + { + get { return this.id; } + } + + public string Name + { + get { return this.name; } + } + + public string Storage + { + get { return this.storage; } + } + + /// + /// Whether this Link is equal to other Link. Compares by Id. + /// + public override bool Equals(object other) + { + TestLink link = other as TestLink; + return (link == null) ? + false : + this.id.Equals(link.id); + } + + /// + /// Whether this Link is exactly the same as other Link. Compares all fields. + /// + public bool IsSame(TestLink other) + { + if (other == null) + { + return false; + } + + return this.id.Equals(other.id) && + this.name.Equals(other.name) && + this.storage.Equals(other.storage); + } + + public override int GetHashCode() + { + return this.id.GetHashCode(); + } + public override string ToString() + { + return string.Format( + CultureInfo.InvariantCulture, + "Link to '{0}' {1} '{2}'.", + this.name != null ? this.name : "(null)", + this.id.ToString("B"), + this.storage != null ? this.storage : "(null)"); + } + + public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + XmlPersistence h = new XmlPersistence(); + h.SaveGuid(element, "@id", this.Id); + h.SaveSimpleField(element, "@name", this.name, null); + h.SaveSimpleField(element, "@storage", this.storage, string.Empty); + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs index e34dbabc8d..ccb6f6442e 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs @@ -4,20 +4,26 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { using System; + using System.Collections; + using System.Collections.Generic; using System.Diagnostics; + using System.Globalization; + using System.IO; + using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; /// /// Class to uniquely identify test results /// - public sealed class TestResultId : IXmlTestStore + internal sealed class TestResultId : IXmlTestStore { #region Fields - private Guid runId; - - // id of test within run - private TestExecId executionId; + private Guid runId; + private Guid executionId; + private Guid parentExecutionId; private TestId testId; #endregion @@ -39,10 +45,11 @@ public sealed class TestResultId : IXmlTestStore /// /// The test id. /// - public TestResultId(Guid runId, TestExecId executionId, TestId testId) + public TestResultId(Guid runId, Guid executionId, Guid parentExecutionId, TestId testId) { this.runId = runId; this.executionId = executionId; + this.parentExecutionId = parentExecutionId; this.testId = testId; } @@ -53,11 +60,19 @@ public TestResultId(Guid runId, TestExecId executionId, TestId testId) /// /// Gets the execution id. /// - public TestExecId ExecutionId + public Guid ExecutionId { get { return this.executionId; } } + /// + /// Gets the test id. + /// + public TestId TestId + { + get { return this.testId; } + } + #endregion #region Overrides @@ -101,7 +116,7 @@ public override int GetHashCode() /// public override string ToString() { - return this.executionId.Id.ToString("B"); + return this.executionId.ToString("B"); } #endregion @@ -121,9 +136,9 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter XmlPersistence helper = new XmlPersistence(); if (this.executionId != null) - { - helper.SaveGuid(element, "@executionId", this.executionId.Id); - } + helper.SaveGuid(element, "@executionId", this.executionId); + if (this.parentExecutionId != null) + helper.SaveGuid(element, "@parentExecutionId", this.parentExecutionId); helper.SaveObject(this.testId, element, null); } @@ -190,4 +205,441 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter #endregion } + + /// + /// Class for test result. + /// + internal class TestResult : ITestResult, IXmlTestStore + { + #region Fields + + /// + /// Id of test within run + /// + private TestResultId id; + + /// + /// Name of test within run + /// + private string testName; + + private string computerInfo; + + private TimeSpan duration; + + private DateTime startTime; + + private DateTime endTime; + + /// + /// Type of test (Guid) + /// + private TestType testType; + + /// + /// The outcome of the test result + /// + private TestOutcome outcome; + + /// + /// The test run in which the test was executed + /// + private TestRun testRun; + + private string stdOut; + + private string stdErr; + + private string debugTrace; + + private TestResultErrorInfo errorInfo; + + private TestListCategoryId categoryId; + + private ArrayList textMessages; + + private int dataRowInfo; + + private string resultType; + + /// + /// Directory containing the test result files, relative to the root test results directory + /// + private string relativeTestResultsDirectory; + + /// + /// Paths to test result files, relative to the test results folder, sorted in increasing order + /// + private SortedList resultFiles = new SortedList(StringComparer.OrdinalIgnoreCase); + + /// + /// Information provided by data collectors for the test case + /// + private List collectorDataEntries = new List(); + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + /// + /// The computer name. + /// + /// + /// The run id. + /// + /// + /// The test. + /// + /// + /// The outcome. + /// + public TestResult(string testName, + string computerName, + Guid runId, Guid executionId, + Guid parentExecutionId, + ITestElement test, + TestOutcome outcome) + { + // check if we can remove dependency on test element. + Debug.Assert(computerName != null, "computername is null"); + Debug.Assert(test != null, "test is null"); + Debug.Assert(!Guid.Empty.Equals(test.ExecutionId.Id), "ExecutionId is empty"); + Debug.Assert(!Guid.Empty.Equals(test.Id.Id), "Id is empty"); + + this.Initialize(); + + this.id = new TestResultId(runId, executionId, parentExecutionId, test.Id); + this.testName = testName; + this.testType = test.TestType; + this.computerInfo = computerName; + + this.outcome = outcome; + this.categoryId = test.CategoryId; + this.relativeTestResultsDirectory = TestRunDirectories.GetRelativeTestResultsDirectory(test.ExecutionId.Id); + } + + #endregion + + #region properties + + /// + /// Gets or sets the end time. + /// + public DateTime EndTime + { + get { return this.endTime; } + set { this.endTime = value; } + } + + /// + /// Gets or sets the start time. + /// + public DateTime StartTime + { + get { return this.startTime; } + set { this.startTime = value; } + } + + /// + /// Gets or sets the duration. + /// + public TimeSpan Duration + { + get { return this.duration; } + + set + { + // On some hardware the Stopwatch.Elapsed can return a negative number. This tends + // to happen when the duration of the test is very short and it is hardware dependent + // (seems to happen most on virtual machines or machines with AMD processors). To prevent + // reporting a negative duration, use TimeSpan.Zero when the elapsed time is less than zero. + EqtTrace.WarningIf(value < TimeSpan.Zero, "TestResult.Duration: The duration is being set to {0}. Since the duration is negative the duration will be updated to zero.", value); + this.duration = value > TimeSpan.Zero ? value : TimeSpan.Zero; + } + } + + /// + /// Gets the computer name. + /// + public string ComputerName + { + get { return this.computerInfo; } + } + + /// + /// Gets or sets the outcome. + /// + public TestOutcome Outcome + { + get { return this.outcome; } + set { this.outcome = value; } + } + + + /// + /// Gets or sets the id. + /// + public TestResultId Id + { + get { return this.id; } + internal set { this.id = value; } + } + + /// + /// Gets or sets the error message. + /// + public string ErrorMessage + { + get { return (this.errorInfo == null) ? string.Empty : this.errorInfo.Message; } + set { this.errorInfo = new TestResultErrorInfo(value); } + } + + /// + /// Gets or sets the error stack trace. + /// + public string ErrorStackTrace + { + get { return (this.errorInfo == null) ? string.Empty : this.errorInfo.StackTrace; } + + set + { + Debug.Assert(this.errorInfo != null, "errorInfo is null"); + this.errorInfo.StackTrace = value; + } + } + + /// + /// Gets the text messages. + /// + /// + /// Additional information messages from TestTextResultMessage, e.g. generated by TestOutcome.WriteLine. + /// Avoid using this property in the following way: for (int i=0; i<prop.Length; i++) { ... prop[i] ...} + /// + public string[] TextMessages + { + get { return (string[])this.textMessages.ToArray(typeof(string)); } + + set + { + if (value != null) + this.textMessages = new ArrayList(value); + else + this.textMessages.Clear(); + } + } + + /// + /// Gets or sets the standard out. + /// + public string StdOut + { + get { return this.stdOut ?? string.Empty; } + set { this.stdOut = value; } + } + + /// + /// Gets or sets the standard err. + /// + public string StdErr + { + get { return this.stdErr ?? string.Empty; } + set { this.stdErr = value; } + } + + /// + /// Gets or sets the debug trace. + /// + public string DebugTrace + { + get { return this.debugTrace ?? string.Empty; } + set { this.debugTrace = value; } + } + + /// + /// Gets the path to the test results directory + /// + public string TestResultsDirectory + { + get + { + if (this.testRun == null) + { + Debug.Fail("'m_testRun' is null"); + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, TrxLoggerResources.Common_MissingRunInResult)); + } + + return this.testRun.GetResultFilesDirectory(this); + } + } + + /// + /// Gets the directory containing the test result files, relative to the root results directory + /// + public string RelativeTestResultsDirectory + { + get { return this.relativeTestResultsDirectory; } + } + + public int DataRowInfo + { + get { return this.dataRowInfo; } + set { this.dataRowInfo = value; } + } + + public string ResultType + { + get { return this.resultType; } + set { this.resultType = value; } + } + + #endregion + + #region Overrides + public override bool Equals(object obj) + { + TestResult trm = obj as TestResult; + if (trm == null) + { + return false; + } + Debug.Assert(this.id != null, "id is null"); + Debug.Assert(trm.id != null, "test result message id is null"); + return this.id.Equals(trm.id); + } + + public override int GetHashCode() + { + Debug.Assert(this.id != null, "id is null"); + return this.id.GetHashCode(); + } + + #endregion + + /// + /// Helper function to add a text message info to the test result + /// + /// Message to be added + public void AddTextMessage(string text) + { + EqtAssert.ParameterNotNull(text, "text"); + this.textMessages.Add(text); + } + + /// + /// Sets the test run the test was executed in + /// + /// The test run the test was executed in + internal virtual void SetTestRun(TestRun testRun) + { + Debug.Assert(testRun != null, "'testRun' is null"); + this.testRun = testRun; + } + + /// + /// Adds result files to the collection + /// + /// Paths to the result files + internal void AddResultFiles(IEnumerable resultFileList) + { + Debug.Assert(resultFileList != null, "'resultFileList' is null"); + + string testResultsDirectory = this.TestResultsDirectory; + foreach (string resultFile in resultFileList) + { + Debug.Assert(!string.IsNullOrEmpty(resultFile), "'resultFile' is null or empty"); + Debug.Assert(resultFile.Trim() == resultFile, "'resultFile' has whitespace at the ends"); + Debug.Assert(Path.IsPathRooted(resultFile), "'resultFile' is a relative path"); + + this.resultFiles[FileHelper.MakePathRelative(resultFile, testResultsDirectory)] = null; + } + } + + /// + /// Adds collector data entries to the collection + /// + /// The collector data entry to add + internal void AddCollectorDataEntries(IEnumerable collectorDataEntryList) + { + Debug.Assert(collectorDataEntryList != null, "'collectorDataEntryList' is null"); + + string testResultsDirectory = this.TestResultsDirectory; + foreach (CollectorDataEntry collectorDataEntry in collectorDataEntryList) + { + Debug.Assert(collectorDataEntry != null, "'collectorDataEntry' is null"); + Debug.Assert(!this.collectorDataEntries.Contains(collectorDataEntry), "The collector data entry already exists in the collection"); +#if DEBUG + // Verify that any URI data attachments in the entry have relative paths + foreach (IDataAttachment attachment in collectorDataEntry.Attachments) + { + UriDataAttachment uriDataAttachment = attachment as UriDataAttachment; + if (uriDataAttachment != null) + { + Debug.Assert(uriDataAttachment.Uri.IsAbsoluteUri, "'collectorDataEntry' contains a URI data attachment with a relative URI"); + } + } +#endif + + this.collectorDataEntries.Add(collectorDataEntry.Clone(testResultsDirectory, false)); + } + } + + + #region IXmlTestStore Members + + /// + /// Saves the class under the XmlElement.. + /// + /// + /// The parent xml. + /// + /// + /// The parameter + /// + public virtual void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + XmlPersistence helper = new XmlPersistence(); + + helper.SaveObject(this.id, element, ".", parameters); + helper.SaveSimpleField(element, "@testName", this.testName, string.Empty); + helper.SaveSimpleField(element, "@computerName", this.computerInfo, string.Empty); + helper.SaveSimpleField(element, "@duration", this.duration, default(TimeSpan)); + helper.SaveSimpleField(element, "@startTime", this.startTime, default(DateTime)); + helper.SaveSimpleField(element, "@endTime", this.endTime, default(DateTime)); + helper.SaveGuid(element, "@testType", this.testType.Id); + + if (this.stdOut != null) + this.stdOut = this.stdOut.Trim(); + + if (this.stdErr != null) + this.stdErr = this.stdErr.Trim(); + + helper.SaveSimpleField(element, "@outcome", this.outcome, default(TestOutcome)); + helper.SaveSimpleField(element, "Output/StdOut", this.stdOut, string.Empty); + helper.SaveSimpleField(element, "Output/StdErr", this.stdErr, string.Empty); + helper.SaveSimpleField(element, "Output/DebugTrace", this.debugTrace, string.Empty); + helper.SaveObject(this.errorInfo, element, "Output/ErrorInfo", parameters); + helper.SaveGuid(element, "@testListId", this.categoryId.Id); + helper.SaveIEnumerable(this.textMessages, element, "Output/TextMessages", ".", "Message", parameters); + helper.SaveSimpleField(element, "@relativeResultsDirectory", this.relativeTestResultsDirectory, null); + helper.SaveIEnumerable(this.resultFiles.Keys, element, "ResultFiles", "@path", "ResultFile", parameters); + helper.SaveIEnumerable(this.collectorDataEntries, element, "CollectorDataEntries", ".", "Collector", parameters); + + if (this.dataRowInfo >= 0) + helper.SaveSimpleField(element, "@dataRowInfo", this.dataRowInfo, -1); + + if (!string.IsNullOrEmpty(this.resultType)) + helper.SaveSimpleField(element, "@resultType", this.resultType, string.Empty); + } + + #endregion + + private void Initialize() + { + this.textMessages = new ArrayList(); + this.dataRowInfo = -1; + } + } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs new file mode 100644 index 0000000000..b1f3b31334 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel +{ + using System; + using System.Collections.Generic; + using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + + internal class TestResultAggregation : TestResult, ITestResultAggregation + { + protected List innerResults = new List(); + + public TestResultAggregation( + string testName, + string computerName, + Guid runId, + Guid executionId, + Guid parentExecutionId, + ITestElement test, + TestOutcome outcome) : base(testName, computerName, runId, executionId, parentExecutionId, test, outcome) { } + + public List InnerResults + { + get { return innerResults; } + } + + public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + base.Save(element, parameters); + + XmlPersistence helper = new XmlPersistence(); + if (innerResults.Count > 0) + helper.SaveIEnumerable(innerResults, element, "InnerResults", ".", null, parameters); + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs index 6e6c3105eb..1ab80001d7 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs @@ -5,103 +5,30 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { using System; using System.Diagnostics; - using System.Globalization; - using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; - using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; - - /// - /// Class for all tests - /// - internal class UnitTestElement : IXmlTestStore, IXmlTestStoreCustom + internal class UnitTestElement : TestElement, IXmlTestStoreCustom { - #region Constants - /// - /// Default priority for a test method that does not specify a priority - /// - internal const int DefaultPriority = int.MaxValue; - - /// - /// Timeout value indicating a not-set timeout - /// - internal const int NotSetTimeout = 0; - - private static readonly Guid TestTypeGuid = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); + private static readonly Guid TestTypeGuid = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); // move to constants private static readonly TestType TestTypeInstance = new TestType(TestTypeGuid); - #endregion - - #region Fields - - private TestId id; - - private string name; - - private string owner; - - private int priority; - - private TestCategoryItemCollection testCategories; - - private TestExecId executionId; - - private string storage; - private string codeBase; - - // partial or fully qualified name of the adapter used to execute the test - private string executorUriOfAdapter; - private TestMethod testMethod; - private bool isRunnable; - - private TestListCategoryId catId; - - #endregion - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The id. - /// - /// - /// The name. - /// - /// - /// The adapter type name. - /// - /// - /// The test method. - /// public UnitTestElement( Guid id, string name, - string executorUriOfAdapter, - TestMethod testMethod) + string adapter, + TestMethod testMethod) : base(id, name, adapter) { - Debug.Assert(!string.IsNullOrEmpty(name), "name is null"); - Debug.Assert(!string.IsNullOrEmpty(executorUriOfAdapter), "executorUriOfAdapter is null"); + Debug.Assert(!string.IsNullOrEmpty(adapter), "adapter is null"); Debug.Assert(testMethod != null, "testMethod is null"); + Debug.Assert(testMethod != null && testMethod.ClassName != null, "className is null"); - this.Initialize(); - - this.id = new TestId(id); - this.name = name; - this.executorUriOfAdapter = executorUriOfAdapter; this.testMethod = testMethod; - Debug.Assert(this.testMethod.ClassName != null, "className is null"); } - #endregion - - #region IXmlTestStoreCustom - string IXmlTestStoreCustom.ElementName { get { return "UnitTest"; } @@ -112,207 +39,28 @@ string IXmlTestStoreCustom.NamespaceUri get { return null; } } - #endregion - - /// - /// Gets or sets the category id. - /// - /// - /// Instead of setting to null use TestListCategoryId.Uncategorized - /// - public TestListCategoryId CategoryId - { - get - { - return this.catId; - } - - set - { - EqtAssert.ParameterNotNull(value, "CategoryId"); - this.catId = value; - } - } - - /// - /// Gets the id. - /// - public TestId Id - { - get { return this.id; } - } - - /// - /// Gets or sets the execution id. - /// - public TestExecId ExecutionId - { - get { return this.executionId; } - set { this.executionId = value; } - } - - /// - /// Gets or sets the name. - /// - public string Name - { - get - { - return this.name; - } - - set - { - EqtAssert.ParameterNotNull(value, "Name"); - - this.name = value; - } - } - - /// - /// Gets or sets the storage. - /// - public string Storage - { - get - { - return this.storage; - } - - set - { - EqtAssert.StringNotNullOrEmpty(value, "Storage"); - this.storage = value.ToLowerInvariant(); - } - } - - /// - /// Gets or sets the priority. - /// - public int Priority - { - get - { - return this.priority; - } - - set - { - this.priority = value; - } - } - - /// - /// Gets or sets the owner. - /// - public string Owner - { - get - { - return this.owner; - } - - set - { - EqtAssert.ParameterNotNull(value, "Owner"); - this.owner = value; - } - } - /// /// Gets the test type. /// - public TestType TestType + public override TestType TestType { get { return TestTypeInstance; } } /// - /// Gets or sets the test categories. + /// Gets or sets the storage. /// - public TestCategoryItemCollection TestCategories + public string CodeBase { - get - { - return this.testCategories; - } + get { return this.codeBase; } set { - EqtAssert.ParameterNotNull(value, "value"); - this.testCategories = value; + EqtAssert.StringNotNullOrEmpty(value, "CodeBase"); + this.codeBase = value; } } - /// - /// The assign code base. - /// - /// - /// The code base. - /// - public void AssignCodeBase(string cb) - { - EqtAssert.StringNotNullOrEmpty(cb, "codeBase"); - this.codeBase = cb; - } - - public bool IsRunnable - { - get { return this.isRunnable; } - } - - #region Overrides - - /// - /// Override for Tostring. - /// - /// - /// The . - /// - public override string ToString() - { - return string.Format( - CultureInfo.InvariantCulture, - "'{0}' {1}", - this.name != null ? this.name : TrxLoggerResources.Common_NullInMessages, - this.id != null ? this.id.ToString() : TrxLoggerResources.Common_NullInMessages); - } - - /// - /// Override for Equals. - /// - /// - /// The object to compare. - /// - /// - /// The . - /// - public override bool Equals(object other) - { - UnitTestElement otherTest = other as UnitTestElement; - if (otherTest == null) - { - return false; - } - - return this.id.Equals(otherTest.id); - } - - /// - /// Override for GetHashCode - /// - /// - /// The . - /// - public override int GetHashCode() - { - return this.id.GetHashCode(); - } - - #endregion - - #region IXmlTestStore Members - /// /// Saves the class under the XmlElement.. /// @@ -322,47 +70,14 @@ public override int GetHashCode() /// /// The parameter /// - public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) { + base.Save(element, parameters); XmlPersistence h = new XmlPersistence(); - h.SaveSimpleField(element, "@name", this.name, null); - h.SaveSimpleField(element, "@storage", this.storage, string.Empty); - h.SaveSimpleField(element, "@priority", this.priority, DefaultPriority); - h.SaveSimpleField(element, "Owners/Owner/@name", this.owner, string.Empty); - h.SaveObject(this.testCategories, element, "TestCategory", parameters); - - // Save the test ID. We exclude "test" from the default locations used by TestId, since this is already a test - // element. Ideally, we would let TestId save the IDs to the default locations, but the previous behavior of - // TestElement was to store the test ID at @testId, and since we can't change this, TestId supports custom - // locations for the IDs. See TestId.GetLocations for more info. - XmlTestStoreParameters testIdParameters = XmlTestStoreParameters.GetParameters(); - testIdParameters[TestId.IdLocationKey] = "@id"; - h.SaveObject(this.id, element, testIdParameters); - - if (this.executionId != null) - { - h.SaveGuid(element, "Execution/@id", this.executionId.Id); - } - h.SaveSimpleField(element, "TestMethod/@codeBase", this.codeBase, string.Empty); - h.SaveSimpleField(element, "TestMethod/@executorUriOfAdapter", this.executorUriOfAdapter, string.Empty); + h.SaveSimpleField(element, "TestMethod/@executorUri", this.adapter, string.Empty); h.SaveObject(this.testMethod, element, "TestMethod", parameters); } - - #endregion //IXmlTestStore - - private void Initialize() - { - this.id = TestId.Empty; - this.name = string.Empty; - this.owner = string.Empty; - this.priority = DefaultPriority; - this.executionId = TestExecId.Empty; - this.testCategories = new TestCategoryItemCollection(); - this.storage = string.Empty; - this.isRunnable = true; - this.catId = TestListCategoryId.Uncategorized; - } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs index 4c7027370d..fe8f7c7469 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs @@ -4,455 +4,19 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - - using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; - using Microsoft.TestPlatform.Extensions.TrxLogger.XML; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - - using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; /// /// Class for unit test result. /// - internal class UnitTestResult: IXmlTestStore + internal class UnitTestResult: TestResultAggregation { - #region Fields - // id of test within run - private TestResultId id; - - // name of test within run - private string testName; - - private string computerInfo; - - private TimeSpan duration; - - private DateTime startTime; - - private DateTime endTime; - - // type of test (Guid) - private TestType testType; - - /// - /// The outcome of the test result - /// - private TestOutcome outcome; - - /// - /// The test run in which the test was executed - /// - private TestRun testRun; - - private string stdOut; - - private string stdErr; - - private string debugTrace; - - private TestResultErrorInfo errorInfo; - - private TestListCategoryId categoryId; - - private ArrayList textMessages; - - /// - /// Directory containing the test result files, relative to the root test results directory - /// - private string relativeTestResultsDirectory; - - /// - /// Paths to test result files, relative to the test results folder, sorted in increasing order - /// - private SortedList resultFiles = new SortedList(StringComparer.OrdinalIgnoreCase); - - /// - /// Information provided by data collectors for the test case - /// - private List collectorDataEntries = new List(); - - #endregion - - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// - /// The computer name. - /// - /// - /// The run id. - /// - /// - /// The test. - /// - /// - /// The outcome. - /// - public UnitTestResult(string computerName, Guid runId, UnitTestElement test, TestOutcome outcome) - { - Debug.Assert(computerName != null, "computername is null"); - Debug.Assert(test != null, "test is null"); - Debug.Assert(!Guid.Empty.Equals(test.ExecutionId.Id), "ExecutionId is empty"); - Debug.Assert(!Guid.Empty.Equals(test.Id.Id), "Id is empty"); - - this.Initialize(); - - this.id = new TestResultId(runId, test.ExecutionId, test.Id); - this.testName = test.Name; - this.testType = test.TestType; - this.computerInfo = computerName; - - this.outcome = outcome; - this.categoryId = test.CategoryId; - this.relativeTestResultsDirectory = TestRunDirectories.GetRelativeTestResultsDirectory(test.ExecutionId.Id); - } - - #endregion - - #region properties - - /// - /// Gets or sets the end time. - /// - public DateTime EndTime - { - get { return this.endTime; } - set { this.endTime = value; } - } - - /// - /// Gets or sets the start time. - /// - public DateTime StartTime - { - get { return this.startTime; } - set { this.startTime = value; } - } - - /// - /// Gets or sets the duration. - /// - public TimeSpan Duration - { - get - { - return this.duration; - } - - set - { - // On some hardware the Stopwatch.Elapsed can return a negative number. This tends - // to happen when the duration of the test is very short and it is hardware dependent - // (seems to happen most on virtual machines or machines with AMD processors). To prevent - // reporting a negative duration, use TimeSpan.Zero when the elapsed time is less than zero. - EqtTrace.WarningIf(value < TimeSpan.Zero, "TestResult.Duration: The duration is being set to {0}. Since the duration is negative the duration will be updated to zero.", value); - this.duration = value > TimeSpan.Zero ? value : TimeSpan.Zero; - } - } - - /// - /// Gets the computer name. - /// - public string ComputerName - { - get { return this.computerInfo; } - } - - /// - /// Gets or sets the outcome. - /// - public TestOutcome Outcome - { - get { return this.outcome; } - set { this.outcome = value; } - } - - - /// - /// Gets or sets the id. - /// - public TestResultId Id - { - get { return this.id; } - internal set { this.id = value; } - } - - /// - /// Gets or sets the error message. - /// - public string ErrorMessage - { - get - { - if (this.errorInfo == null) - { - return string.Empty; - } - - return this.errorInfo.Message; - } - - set - { - this.errorInfo = new TestResultErrorInfo(value); - } - } - - /// - /// Gets or sets the error stack trace. - /// - public string ErrorStackTrace - { - get - { - if (this.errorInfo == null) - { - return string.Empty; - } - - return this.errorInfo.StackTrace; - } - - set - { - Debug.Assert(this.errorInfo != null, "errorInfo is null"); - this.errorInfo.StackTrace = value; - } - } - - /// - /// Gets the text messages. - /// - /// - /// Additional information messages from TestTextResultMessage, e.g. generated by TestOutcome.WriteLine. - /// Avoid using this property in the following way: for (int i=0; i<prop.Length; i++) { ... prop[i] ...} - /// - public string[] TextMessages - { - get - { - return (string[])this.textMessages.ToArray(typeof(string)); - } - - internal set - { - if (value != null) - { - this.textMessages = new ArrayList(value); - } - else - { - this.textMessages.Clear(); - } - } - } - - /// - /// Gets or sets the standard out. - /// - public string StdOut - { - get { return this.stdOut ?? string.Empty; } - set { this.stdOut = value; } - } - - /// - /// Gets or sets the standard err. - /// - public string StdErr - { - get { return this.stdErr ?? string.Empty; } - set { this.stdErr = value; } - } - - /// - /// Gets or sets the debug trace. - /// - public string DebugTrace - { - get { return this.debugTrace ?? string.Empty; } - set { this.debugTrace = value; } - } - - /// - /// Gets the path to the test results directory - /// - public string TestResultsDirectory - { - get - { - if (this.testRun == null) - { - Debug.Fail("'m_testRun' is null"); - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, TrxLoggerResources.Common_MissingRunInResult)); - } - - return this.testRun.GetResultFilesDirectory(this); - } - } - - /// - /// Gets the directory containing the test result files, relative to the root results directory - /// - internal string RelativeTestResultsDirectory - { - get - { - return this.relativeTestResultsDirectory; - } - } - - #endregion - - #region Overrides - public override bool Equals(object obj) - { - UnitTestResult trm = obj as UnitTestResult; - if (trm == null) - { - return false; - } - Debug.Assert(this.id != null, "id is null"); - Debug.Assert(trm.id != null, "test result message id is null"); - return this.id.Equals(trm.id); - } - - public override int GetHashCode() - { - Debug.Assert(this.id != null, "id is null"); - return this.id.GetHashCode(); - } - - #endregion - - /// - /// Helper function to add a text message info to the test result - /// - /// Message to be added - public void AddTextMessage(string text) - { - EqtAssert.ParameterNotNull(text, "text"); - this.textMessages.Add(text); - } - - /// - /// Sets the test run the test was executed in - /// - /// The test run the test was executed in - internal virtual void SetTestRun(TestRun testRun) - { - Debug.Assert(testRun != null, "'testRun' is null"); - this.testRun = testRun; - } - - /// - /// Adds result files to the collection - /// - /// Paths to the result files - internal void AddResultFiles(IEnumerable resultFileList) - { - Debug.Assert(resultFileList != null, "'resultFileList' is null"); - - string testResultsDirectory = this.TestResultsDirectory; - foreach (string resultFile in resultFileList) - { - Debug.Assert(!string.IsNullOrEmpty(resultFile), "'resultFile' is null or empty"); - Debug.Assert(resultFile.Trim() == resultFile, "'resultFile' has whitespace at the ends"); - Debug.Assert(Path.IsPathRooted(resultFile), "'resultFile' is a relative path"); - - this.resultFiles[FileHelper.MakePathRelative(resultFile, testResultsDirectory)] = null; - } - } - - /// - /// Adds collector data entries to the collection - /// - /// The collector data entry to add - internal void AddCollectorDataEntries(IEnumerable collectorDataEntryList) - { - Debug.Assert(collectorDataEntryList != null, "'collectorDataEntryList' is null"); - - string testResultsDirectory = this.TestResultsDirectory; - foreach (CollectorDataEntry collectorDataEntry in collectorDataEntryList) - { - Debug.Assert(collectorDataEntry != null, "'collectorDataEntry' is null"); - Debug.Assert(!this.collectorDataEntries.Contains(collectorDataEntry), "The collector data entry already exists in the collection"); -#if DEBUG - // Verify that any URI data attachments in the entry have relative paths - foreach (IDataAttachment attachment in collectorDataEntry.Attachments) - { - UriDataAttachment uriDataAttachment = attachment as UriDataAttachment; - if (uriDataAttachment != null) - { - Debug.Assert(uriDataAttachment.Uri.IsAbsoluteUri, "'collectorDataEntry' contains a URI data attachment with a relative URI"); - } - } -#endif - - this.collectorDataEntries.Add(collectorDataEntry.Clone(testResultsDirectory, false)); - } - } - - - #region IXmlTestStore Members - - /// - /// Saves the class under the XmlElement.. - /// - /// - /// The parent xml. - /// - /// - /// The parameter - /// - public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) - { - XmlPersistence helper = new XmlPersistence(); - - helper.SaveObject(this.id, element, ".", parameters); - helper.SaveSimpleField(element, "@testName", this.testName, string.Empty); - helper.SaveSimpleField(element, "@computerName", this.computerInfo, string.Empty); - helper.SaveSimpleField(element, "@duration", this.duration, default(TimeSpan)); - helper.SaveSimpleField(element, "@startTime", this.startTime, default(DateTime)); - helper.SaveSimpleField(element, "@endTime", this.endTime, default(DateTime)); - helper.SaveGuid(element, "@testType", this.testType.Id); - - if (this.stdOut != null) - { - this.stdOut = this.stdOut.Trim(); - } - - if (this.stdErr != null) - { - this.stdErr = this.stdErr.Trim(); - } - - helper.SaveSimpleField(element, "@outcome", this.outcome, default(TestOutcome)); - helper.SaveSimpleField(element, "Output/StdOut", this.stdOut, string.Empty); - helper.SaveSimpleField(element, "Output/StdErr", this.stdErr, string.Empty); - helper.SaveSimpleField(element, "Output/DebugTrace", this.debugTrace, string.Empty); - helper.SaveObject(this.errorInfo, element, "Output/ErrorInfo", parameters); - - helper.SaveGuid(element, "@testListId", this.categoryId.Id); - - helper.SaveIEnumerable(this.textMessages, element, "Output/TextMessages", ".", "Message", parameters); - helper.SaveSimpleField(element, "@relativeResultsDirectory", this.relativeTestResultsDirectory, null); - helper.SaveIEnumerable(this.resultFiles.Keys, element, "ResultFiles", "@path", "ResultFile", parameters); - helper.SaveIEnumerable(this.collectorDataEntries, element, "CollectorDataEntries", ".", "Collector", parameters); - } - - #endregion - - private void Initialize() - { - this.textMessages = new ArrayList(); - } + public UnitTestResult( + string testName, + string computerName, + Guid runId, + Guid executionId, + Guid parentExecutionId, + ITestElement test, + TestOutcome outcome) : base(testName, computerName, runId, executionId, parentExecutionId, test, outcome) { } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs new file mode 100644 index 0000000000..3fcc3921e1 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; // check where it should be? within namespace or outside? + +namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility +{ + internal static class Constants + { + /// + /// Uri used to uniquely identify the TRX logger. + /// + public const string ExtensionUri = "logger://Microsoft/TestPlatform/TrxLogger/v1"; + + /// + /// Alternate user friendly string to uniquely identify the console logger. + /// + public const string FriendlyName = "Trx"; + + /// + /// Prefix of the data collector + /// + public const string DataCollectorUriPrefix = "dataCollector://"; + + /// + /// Log file parameter key + /// + public const string LogFileNameKey = "LogFileName"; + + /// + /// Property Id storing the ExecutionId. + /// + public const string ExecutionIdPropertyIdentifier = "ExecutionId"; + + /// + /// Property Id storing the ParentExecutionId. + /// + public const string ParentExecutionIdPropertyIdentifier = "ParentExecId"; + + public const string TestTypePropertyIdentifier = "TestType"; + + /// + /// Property Id storing the ExecutionId. + /// + public const string InnerResultsCountPropertyIdentifier = "InnerResultsCount"; // remove if not required + + /// + /// Property Id storing the TMITestId. + /// + public const string TmiTestIdPropertyIdentifier = "MSTestDiscoverer.TmiTestId"; + + public static readonly Guid OrderedTestType = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); + public static readonly Guid UnitTestType = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); + } +} From c0628c9e88088e4e0747788969df4f5289f58359 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 10:31:33 +0530 Subject: [PATCH 03/19] initial changes for trx hierarchical --- .../TrxLogger.cs | 259 +++++++++++++----- .../Utility/Converter.cs | 180 ++++++------ .../TrxLoggerTests.cs | 19 +- 3 files changed, 290 insertions(+), 168 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 0470951b77..9293f90105 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -9,52 +9,27 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; + using System.Linq; using System.Text; using System.Xml; - using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.Utilities; - using ObjectModel.Logging; - + using TrxLoggerConstants = Microsoft.TestPlatform.Extensions.TrxLogger.Utility.Constants; using TrxLoggerObjectModel = Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; /// /// Logger for Generating TRX /// - [FriendlyName(TrxLogger.FriendlyName)] - [ExtensionUri(TrxLogger.ExtensionUri)] + [FriendlyName(TrxLoggerConstants.FriendlyName)] + [ExtensionUri(TrxLoggerConstants.ExtensionUri)] internal class TrxLogger : ITestLoggerWithParameters { - #region Constants - - /// - /// Uri used to uniquely identify the TRX logger. - /// - public const string ExtensionUri = "logger://Microsoft/TestPlatform/TrxLogger/v1"; - - /// - /// Alternate user friendly string to uniquely identify the console logger. - /// - public const string FriendlyName = "Trx"; - - /// - /// Prefix of the data collector - /// - public const string DataCollectorUriPrefix = "dataCollector://"; - - /// - /// Log file parameter key - /// - public const string LogFileNameKey = "LogFileName"; - - #endregion - #region Fields /// @@ -63,9 +38,10 @@ internal class TrxLogger : ITestLoggerWithParameters private string trxFilePath; private TrxLoggerObjectModel.TestRun testRun; - private List results; - private List testElements; - private List entries; + private Dictionary results; + private Dictionary additionalResults; + private Dictionary testElements; + private Dictionary entries; /// /// Specifies the run level "out" messages @@ -208,6 +184,7 @@ internal TrxLoggerObjectModel.TestOutcome TestResultOutcome /// internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) { + System.Diagnostics.Debugger.Launch(); ValidateArg.NotNull(sender, "sender"); ValidateArg.NotNull(e, "e"); @@ -232,6 +209,71 @@ internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) } } + /// + /// Creates test run. + /// + private void CreateTestRun() + { + // Don't create run if already created. + if (testRun != null) + return; + + Guid runId = Guid.NewGuid(); + this.testRun = new TestRun(runId); + + // We cannot rely on the StartTime for the first test result + // In case of parallel, first test result is the fastest test and not the one which started first. + // Setting Started to DateTime.Now in Intialize will make sure we include the startup cost, which was being ignored earlier. + // This is in parity with the way we set this.testRun.Finished + this.testRun.Started = this.testRunStartTime; + + // Save default test settings + string runDeploymentRoot = FileHelper.ReplaceInvalidFileNameChars(this.testRun.Name); + TestRunConfiguration testrunConfig = new TestRunConfiguration("default"); + + testrunConfig.RunDeploymentRootDirectory = runDeploymentRoot; + + this.testRun.RunConfiguration = testrunConfig; + } + + private int GetInnerResultsCount(ObjectModel.TestResult testResult) + { + ObjectModel.TestProperty innerResultsCountProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.InnerResultsCountPropertyIdentifier)); + return innerResultsCountProperty == null ? 0 : testResult.GetPropertyValue(innerResultsCountProperty, default(int)); + } + + private Guid GetParentExecutionId(ObjectModel.TestResult testResult) + { + ObjectModel.TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.ParentExecutionIdPropertyIdentifier)); + return parentExecutionIdProperty == null ? Guid.Empty : testResult.GetPropertyValue(parentExecutionIdProperty, default(Guid)); + } + + private Guid GetExecutionId(ObjectModel.TestResult testResult) + { + ObjectModel.TestProperty executionIdProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? + var executionId = Guid.Empty; + if (executionIdProperty != null) + { + executionId = testResult.GetPropertyValue(executionIdProperty, Guid.NewGuid()); + } + return executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; + } + + private Guid GetTestType(ObjectModel.TestResult testResult) + { + var testType = TrxLoggerConstants.UnitTestType; + + // Get test type from property. default to unit test type. + ObjectModel.TestProperty testTypeProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.TestTypePropertyIdentifier)); + testType = (testTypeProperty == null) ? testType : testResult.GetPropertyValue(testTypeProperty, testType); + + // Except OrderedTest, all test types are updated to unit test type. + testType = (testType == TrxLoggerConstants.OrderedTestType) ? testType : TrxLoggerConstants.UnitTestType; + + return testType; + } + + /// /// Called when a test result is received. /// @@ -243,39 +285,121 @@ internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) /// internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEventArgs e) { + System.Diagnostics.Debugger.Launch(); if (this.testRun == null) - { - Guid runId = Guid.NewGuid(); + CreateTestRun(); - this.testRun = new TestRun(runId); + // Convert skipped test to a log entry as that is the behaviour of mstest. + if (e.Result.Outcome == ObjectModel.TestOutcome.Skipped) + this.HandleSkippedTest(e.Result); + + var innerResultsCount = GetInnerResultsCount(e.Result); + var executionId = GetExecutionId(e.Result); // TODO: instead of creating these in converter also, create at only one place. + var parentExecutionId = GetParentExecutionId(e.Result); - // We cannot rely on the StartTime for the first test result - // In case of parallel, first test result is the fastest test and not the one which started first. - // Setting Started to DateTime.Now in Intialize will make sure we include the startup cost, which was being ignored earlier. - // This is in parity with the way we set this.testRun.Finished - this.testRun.Started = this.testRunStartTime; + ITestResult parentTestResult = null; + ITestElement parentTestElement = null; + if (parentExecutionId != Guid.Empty) + { + this.results.TryGetValue(parentExecutionId, out parentTestResult); // todo: handle guid empty case + if (parentTestResult == null) + { + this.additionalResults.TryGetValue(parentExecutionId, out parentTestResult); + } + // we should always get parentTestResult. If not that a problem. + this.testElements.TryGetValue(parentTestResult.Id.TestId.Id, out parentTestElement); // todo: handle parent test result null case. + } - // Save default test settings - string runDeploymentRoot = FileHelper.ReplaceInvalidFileNameChars(this.testRun.Name); - TestRunConfiguration testrunConfig = new TestRunConfiguration("default"); + var testType = GetTestType(e.Result); + //e.Result.TestCase.ExecutorUri.ToString().ToLower().Contains("orderedtestadapter") ? "orderedtest" : "unittest"; //TODO: all hard coded vars in consts - testrunConfig.RunDeploymentRootDirectory = runDeploymentRoot; + // Create MSTest test element from rocksteady test case + ITestElement testElement = null; + if (parentExecutionId == Guid.Empty) + { + Guid testId = Converter.GetTestId(e.Result.TestCase); + if (!this.testElements.ContainsKey(testId)) + { + var name = e.Result.TestCase.DisplayName; + testElement = Converter.ToTestElement(testType, name, executionId, parentExecutionId, e.Result.TestCase); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. + testElements.Add(testId, testElement); + } + else + { + this.testElements.TryGetValue(testId, out testElement); + } + } + else if(parentTestElement != null && parentTestElement is OrderedTestElement) + { + Guid testId = Converter.GetTestId(e.Result.TestCase); // TODO: if we are adding test id here, then dont create test id in totestelement. its a duplicacy. + if (!this.testElements.ContainsKey(testId)) + { + var name = e.Result.TestCase.DisplayName; + testElement = Converter.ToTestElement(testType, name, executionId, parentExecutionId, e.Result.TestCase); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. + testElements.Add(testId, testElement); + (parentTestElement as OrderedTestElement).TestLinks.Add(new TestLink(testElement.Id.Id, testElement.Name, testElement.Storage)); + } + else + { + this.testElements.TryGetValue(testId, out testElement); + } + //create/get test element if not exists. and link this test element to parent test element + } + else if (parentTestElement != null) + { + testElement = parentTestElement; + //getParent test element + } + else + { + testElement = null; + // this case should never come. If it comes, throw error. + } - this.testRun.RunConfiguration = testrunConfig; + // Convert the rocksteady result to MSTest result + ITestResult testResult; + TrxLoggerObjectModel.TestOutcome testOutcome = Converter.ToOutcome(e.Result.Outcome); + if (testElement is OrderedTestElement) + { + // it should be orderedtestelement + testResult = Converter.ToTestResult(e.Result, executionId, parentExecutionId, testElement, testOutcome, this.testRun, this.testResultsDirPath); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. + } + else + { + // it should be unitTestElement + testResult = Converter.ToTestResult(e.Result, executionId, parentExecutionId, testElement, testOutcome, this.testRun, this.testResultsDirPath); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. } - // Convert skipped test to a log entry as that is the behaviour of mstest. - if (e.Result.Outcome == ObjectModel.TestOutcome.Skipped) + if (parentExecutionId == Guid.Empty) { - this.HandleSkippedTest(e.Result); + this.results.Add(executionId, testResult); + + TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); + te.ExecId = testElement.ExecutionId; + this.entries.Add(executionId, te); } + else if (parentTestElement is OrderedTestElement) + { + (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); + this.additionalResults.Add(executionId, testResult); - // Create MSTest test element from rocksteady test case - UnitTestElement testElement = Converter.ToUnitTestElement(e.Result); + TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); + te.ExecId = testElement.ExecutionId; + te.ParentExecId = testElement.ParentExecutionId; + + this.entries.TryGetValue(parentExecutionId, out var parentTestEntry); // todo: handle null case. + parentTestEntry.TestEntries.Add(te); + } + else + { + testResult.DataRowInfo = (parentTestResult as TestResultAggregation).InnerResults.Count; + testResult.ResultType = "DataDrivenDataRow"; // todo: hard coding + parentTestResult.ResultType = "DataDrivenTest"; // todo: hard coding + (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); + } + // todo: is there any use of inner results? If not remove it. + // todo: in case parent test result or parent test element is not found, what should we do? this case should never happen. So we should error out. - // Conver the rocksteady result to MSTest result - TrxLoggerObjectModel.TestOutcome testOutcome = Converter.ToOutcome(e.Result.Outcome); - TrxLoggerObjectModel.UnitTestResult testResult = Converter.ToUnitTestResult(e.Result, testElement, testOutcome, this.testRun, this.testResultsDirPath); // Set various counts (passtests, failed tests, total tests) this.totalTests++; @@ -288,19 +412,6 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve { this.passTests++; } - - // Add results to in-memory lists that are saved to the xml at completion. - this.results.Add(testResult); - - if (!this.testElements.Contains(testElement)) - { - this.testElements.Add(testElement); - } - - // create a test entry - TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); - te.ExecId = testElement.ExecutionId; - this.entries.Add(te); } /// @@ -314,6 +425,7 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve /// internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) { + System.Diagnostics.Debugger.Launch(); if (this.testRun != null) { XmlPersistence helper = new XmlPersistence(); @@ -328,13 +440,13 @@ internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) helper.SaveObject(this.testRun.RunConfiguration, rootElement, "TestSettings", parameters); // Save test results - helper.SaveIEnumerable(this.results, rootElement, "Results", ".", null, parameters); + helper.SaveIEnumerable(this.results.Values, rootElement, "Results", ".", null, parameters); // Save test definitions - helper.SaveIEnumerable(this.testElements, rootElement, "TestDefinitions", ".", null, parameters); + helper.SaveIEnumerable(this.testElements.Values, rootElement, "TestDefinitions", ".", null, parameters); // Save test entries - helper.SaveIEnumerable(this.entries, rootElement, "TestEntries", ".", "TestEntry", parameters); + helper.SaveIEnumerable(this.entries.Values, rootElement, "TestEntries", ".", "TestEntry", parameters); // Save default categories List categories = new List(); @@ -426,9 +538,10 @@ internal virtual void PopulateTrxFile(string trxFileName, XmlElement rootElement // Initializes trx logger cache. private void InitializeInternal() { - this.results = new List(); - this.testElements = new List(); - this.entries = new List(); + this.results = new Dictionary(); + this.additionalResults = new Dictionary(); + this.testElements = new Dictionary(); + this.entries = new Dictionary(); this.runLevelErrorsAndWarnings = new List(); this.testRun = null; this.totalTests = 0; @@ -465,7 +578,7 @@ private void DeriveTrxFilePath() { if (this.parametersDictionary != null) { - var isLogFileNameParameterExists = this.parametersDictionary.TryGetValue(TrxLogger.LogFileNameKey, out string logFileNameValue); + var isLogFileNameParameterExists = this.parametersDictionary.TryGetValue(TrxLoggerConstants.LogFileNameKey, out string logFileNameValue); if (isLogFileNameParameterExists && !string.IsNullOrWhiteSpace(logFileNameValue)) { this.trxFilePath = Path.Combine(this.testResultsDirPath, logFileNameValue); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs index 137421ed19..a9a331982d 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs @@ -24,71 +24,37 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility /// internal class Converter { - /// - /// Property Id storing the TMITestId. - /// - private const string TmiTestIdPropertyIdentifier = "MSTestDiscoverer.TmiTestId"; - - /// - /// Converts the parameter rockSteady test case to test element - /// - /// - /// The rockSteady Test Result. - /// - /// - /// The . - /// - internal static TrxObjectModel.UnitTestElement ToUnitTestElement(ObjectModel.TestResult rockSteadyTestResult) + internal static TrxObjectModel.TestElement ToTestElement(Guid testType, string name, Guid executionId, Guid parentExecutionId, ObjectModel.TestCase rockSteadyTestCase) { - return GetQToolsTestElementFromTestCase(rockSteadyTestResult); - } + //ObjectModel.TestCase rockSteadyTestCase = rockSteadyTestResult.TestCase; - /// - /// Returns QToolsCommon.TestElement from rockSteady TestCase. - /// - /// - /// The rockSteady Test Result. - /// - /// - /// The . - /// - internal static TrxObjectModel.UnitTestElement GetQToolsTestElementFromTestCase(ObjectModel.TestResult rockSteadyTestResult) - { - ObjectModel.TestCase rockSteadyTestCase = rockSteadyTestResult.TestCase; + Guid id = GetTestId(rockSteadyTestCase); + //string name = !string.IsNullOrEmpty(rockSteadyTestCase.DisplayName) ? rockSteadyTestCase.DisplayName : rockSteadyTestResult.DisplayName;// TODO: in case testDisplayName is null, assign the value from rsTestResult.DisplayName. Dont do if its not required. - // Fix for bug# 868033 - // Use TMI Test id when available. This is needed to ensure that test id in trx files is same as specified in - // .vsmdi files. - // (This is required for test explorer: It removes all test nodes where test id is not in expected test id when merging - // trx files from different batches). - Guid testId = GetTmiTestId(rockSteadyTestCase); + // If it is an inner test case name + //if (!string.IsNullOrEmpty(rockSteadyTestResult.DisplayName)) + //{ + // testId = Guid.NewGuid(); // Changing of guid is done so that VS can load trx otherwise it fails with duplicate id error. + // name = rockSteadyTestResult.DisplayName; + //} -#if NET451 - if (Guid.Empty.Equals(testId)) - { - testId = rockSteadyTestCase.Id; - } -#else - testId = Guid.NewGuid(); -#endif + string adapter = rockSteadyTestCase.ExecutorUri.ToString(); - string testDisplayName = rockSteadyTestCase.DisplayName; + //ObjectModel.TestProperty executionIdProperty = rockSteadyTestResult.Properties.FirstOrDefault(property => property.Id.Equals(ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? + //var executionId = Guid.Empty; + //if (executionIdProperty != null) + //{ + // executionId = rockSteadyTestResult.GetPropertyValue(executionIdProperty, Guid.NewGuid()); + //} + //executionId = executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; - // If it is an inner test case name - if (!string.IsNullOrEmpty(rockSteadyTestResult.DisplayName)) - { - testId = Guid.NewGuid(); // Changing of guid is done so that VS can load trx otherwise it fails with duplicate id error. - testDisplayName = rockSteadyTestResult.DisplayName; - } + //ObjectModel.TestProperty parentExecutionIdProperty = rockSteadyTestResult.Properties.FirstOrDefault(property => property.Id.Equals(ParentExecutionIdPropertyIdentifier)); + //var parentExecutionId = (parentExecutionIdProperty == null) ? Guid.Empty : rockSteadyTestResult.GetPropertyValue(parentExecutionIdProperty, default(Guid)); - TrxObjectModel.TestMethod testMethod = GetTestMethod(testDisplayName, rockSteadyTestCase); - - // convert the rocksteady tests to TestElement. - TrxObjectModel.UnitTestElement testElement = new TrxObjectModel.UnitTestElement(testId, testDisplayName, rockSteadyTestCase.ExecutorUri.ToString(), testMethod); - testElement.ExecutionId = new TrxObjectModel.TestExecId(Guid.NewGuid()); - testElement.AssignCodeBase(rockSteadyTestCase.Source); - testElement.Storage = rockSteadyTestCase.Source; + var storage = rockSteadyTestCase.Source; + int priority = int.MaxValue;// TODO: better way is to create a method and pass testElement to it. + string owner = null; if (rockSteadyTestCase.Traits != null) { ObjectModel.Trait priorityTrait = rockSteadyTestCase.Traits.FirstOrDefault(t => t.Name.Equals("Priority")); @@ -97,19 +63,40 @@ internal static TrxObjectModel.UnitTestElement GetQToolsTestElementFromTestCase( int priorityValue; if (Int32.TryParse(priorityTrait.Value, out priorityValue)) { - testElement.Priority = priorityValue; + priority = priorityValue; } } ObjectModel.Trait ownerTrait = rockSteadyTestCase.Traits.FirstOrDefault(t => t.Name.Equals("Owner")); if (ownerTrait != null) { - testElement.Owner = ownerTrait.Value; + owner = ownerTrait.Value; } } - - // reading TestCategories from the testcase var testCategories = GetCustomPropertyValueFromTestCase(rockSteadyTestCase, "MSTestDiscoverer.TestCategory"); + + TestElement testElement = null; + if (testType == Constants.OrderedTestType) + { + // todo: change name to storage without file extension + testElement = new TrxObjectModel.OrderedTestElement(id, name, adapter); // todo: create toorderestestelement and put if else content in it. + } + else + { + var codeBase = rockSteadyTestCase.Source; + var testMethod = GetTestMethod(name, rockSteadyTestCase.FullyQualifiedName); + testElement = new TrxObjectModel.UnitTestElement(id, name, adapter, testMethod); + + (testElement as TrxObjectModel.UnitTestElement).CodeBase = codeBase; + }// TODO: factory pattern rqd? + + testElement.Storage = storage; + testElement.Priority = priority; + if (owner != null) testElement.Owner = owner; + + testElement.ExecutionId = new TrxObjectModel.TestExecId(executionId); + testElement.ParentExecutionId = new TrxObjectModel.TestExecId(parentExecutionId); + foreach (string testCategory in testCategories) { testElement.TestCategories.Add(testCategory); @@ -162,14 +149,16 @@ internal static TrxObjectModel.TestOutcome ToOutcome(ObjectModel.TestOutcome roc /// test run object /// TRX file directory /// TestResult object - internal static TrxObjectModel.UnitTestResult ToUnitTestResult( + internal static TrxObjectModel.TestResult ToTestResult( ObjectModel.TestResult rockSteadyTestResult, - TrxObjectModel.UnitTestElement testElement, + Guid executionId, + Guid parentExecutionId, + TrxObjectModel.ITestElement testElement, TrxObjectModel.TestOutcome testOutcome, TrxObjectModel.TestRun testRun, string trxFileDirectory) { - TrxObjectModel.UnitTestResult qtoolsResult = GetQToolsTestResultFromTestResult(rockSteadyTestResult, testElement, testOutcome, testRun); + TrxObjectModel.TestResult qtoolsResult = GetQToolsTestResultFromTestResult(rockSteadyTestResult, executionId, parentExecutionId, testElement, testOutcome, testRun); // Clear exsting messages and store rocksteady result messages. qtoolsResult.TextMessages = null; @@ -191,7 +180,7 @@ internal static List ToCollectionEntries(IEnumerable ToResultFiles(IEnumerable ToResultFiles(IEnumerable Test outcome /// test run object /// TestResult object - private static TrxObjectModel.UnitTestResult GetQToolsTestResultFromTestResult( + private static TrxObjectModel.TestResult GetQToolsTestResultFromTestResult( ObjectModel.TestResult rockSteadyTestResult, - TrxObjectModel.UnitTestElement testElement, + Guid executionId, + Guid parentExecutionId, + TrxObjectModel.ITestElement testElement, TrxObjectModel.TestOutcome testOutcome, TrxObjectModel.TestRun testRun) { - UnitTestResult testResult = new UnitTestResult(Environment.MachineName, testRun.Id, testElement, testOutcome); + // TODO: change here. + TestResult testResult; + var testName = !string.IsNullOrEmpty(rockSteadyTestResult.DisplayName) ? rockSteadyTestResult.DisplayName : testElement.Name; + if (testElement is OrderedTestElement) + { + testResult = new TestResultAggregation( + testName, + Environment.MachineName, + testRun.Id, + executionId, + parentExecutionId, + testElement, + testOutcome); + } + else + { + testResult = new UnitTestResult(testName, Environment.MachineName, testRun.Id, executionId, parentExecutionId, testElement, testOutcome); + } + if (rockSteadyTestResult.ErrorMessage != null) { testResult.ErrorMessage = rockSteadyTestResult.ErrorMessage; @@ -281,7 +290,7 @@ private static TrxObjectModel.UnitTestResult GetQToolsTestResultFromTestResult( /// /// TRX TestResult /// rock steady test result - private static void UpdateResultMessages(TrxObjectModel.UnitTestResult unitTestResult, ObjectModel.TestResult testResult) + private static void UpdateResultMessages(TrxObjectModel.TestResult unitTestResult, ObjectModel.TestResult testResult) { StringBuilder debugTrace = new StringBuilder(); StringBuilder stdErr = new StringBuilder(); @@ -351,15 +360,20 @@ internal static List GetCustomPropertyValueFromTestCase(ObjectModel.Test /// /// The . /// - private static Guid GetTmiTestId(ObjectModel.TestCase rockSteadyTestCase) + public static Guid GetTestId(ObjectModel.TestCase rockSteadyTestCase) { - Guid tmiTestId = Guid.Empty; - ObjectModel.TestProperty tmiTestIdProperty = rockSteadyTestCase.Properties.FirstOrDefault(property => property.Id.Equals(TmiTestIdPropertyIdentifier)); + Guid testId = Guid.Empty; + + // Setting test id to tmi test id. + ObjectModel.TestProperty tmiTestIdProperty = rockSteadyTestCase.Properties.FirstOrDefault(property => property.Id.Equals(Constants.TmiTestIdPropertyIdentifier)); if (null != tmiTestIdProperty) - { - tmiTestId = rockSteadyTestCase.GetPropertyValue(tmiTestIdProperty, Guid.Empty); - } - return tmiTestId; + testId = rockSteadyTestCase.GetPropertyValue(tmiTestIdProperty, Guid.Empty); + + // TOOD: check framework other than net451 that why we were having a #if check earlier. + if (Guid.Empty.Equals(testId)) + testId = rockSteadyTestCase.Id; + + return testId; } /// @@ -368,10 +382,10 @@ private static Guid GetTmiTestId(ObjectModel.TestCase rockSteadyTestCase) /// test case display name /// rockSteady Test Case /// The - private static TrxObjectModel.TestMethod GetTestMethod(string testDisplayName, ObjectModel.TestCase rockSteadyTestCase) + private static TrxObjectModel.TestMethod GetTestMethod(string testDisplayName, string testCaseName) { string className = "DefaultClassName"; - string testCaseName = rockSteadyTestCase.FullyQualifiedName; + //string testCaseName = rockSteadyTestCase.FullyQualifiedName; if (testCaseName.Contains(".")) { className = testCaseName.Substring(0, testCaseName.LastIndexOf('.')); @@ -388,7 +402,7 @@ private static TrxObjectModel.TestMethod GetTestMethod(string testDisplayName, O return new TrxObjectModel.TestMethod(testDisplayName, className); } - private static void UpdateTestResultAttachments(ObjectModel.TestResult rockSteadyTestResult, TrxObjectModel.UnitTestResult testResult, TestRun testRun, string trxFileDirectory, bool addAttachments) + private static void UpdateTestResultAttachments(ObjectModel.TestResult rockSteadyTestResult, TrxObjectModel.TestResult testResult, TestRun testRun, string trxFileDirectory, bool addAttachments) { if (rockSteadyTestResult.Attachments == null || rockSteadyTestResult.Attachments.Count == 0) { @@ -409,14 +423,14 @@ private static void UpdateTestResultAttachments(ObjectModel.TestResult rockStead try { // If the attachement is from data collector - if (attachmentSet.Uri.AbsoluteUri.StartsWith(TrxLogger.DataCollectorUriPrefix, StringComparison.OrdinalIgnoreCase)) + if (attachmentSet.Uri.AbsoluteUri.StartsWith(Constants.DataCollectorUriPrefix, StringComparison.OrdinalIgnoreCase)) { - CollectorDataEntry collectorEntry = ToCollectorEntry(attachmentSet, testResult.Id.ExecutionId.Id, testRun, trxFileDirectory); + CollectorDataEntry collectorEntry = ToCollectorEntry(attachmentSet, testResult.Id.ExecutionId, testRun, trxFileDirectory); collectorEntries.Add(collectorEntry); } else { - IList testResultFiles = ToResultFiles(attachmentSet, testResult.Id.ExecutionId.Id, testRun, trxFileDirectory); + IList testResultFiles = ToResultFiles(attachmentSet, testResult.Id.ExecutionId, testRun, trxFileDirectory); resultFiles.AddRange(testResultFiles); } } diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs index ea33391d12..a3a251b1e3 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs @@ -10,22 +10,17 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests using System.IO; using System.Linq; using System.Xml; - + using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger; using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - - using Utility; - using VisualStudio.TestPlatform.ObjectModel; using VisualStudio.TestPlatform.ObjectModel.Client; using VisualStudio.TestPlatform.ObjectModel.Logging; - using ObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel; + using TrxLoggerConstants = Microsoft.TestPlatform.Extensions.TrxLogger.Utility.Constants; using TrxLoggerObjectModel = Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; - using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; [TestClass] public class TrxLoggerTests @@ -44,7 +39,7 @@ public void Initialize() this.testableTrxLogger = new TestableTrxLogger(); this.parameters = new Dictionary(2); this.parameters[DefaultLoggerParameterNames.TestRunDirectory] = TrxLoggerTests.DefaultTestRunDirectory; - this.parameters[TrxLogger.LogFileNameKey] = TrxLoggerTests.DefaultLogFileNameParameterValue; + this.parameters[TrxLoggerConstants.LogFileNameKey] = TrxLoggerTests.DefaultLogFileNameParameterValue; this.testableTrxLogger.Initialize(this.events.Object, this.parameters); } @@ -365,7 +360,7 @@ public void OutcomeOfRunWillBeCompletedIfNoTestsFails() public void TheDefaultTrxFileNameShouldNotHaveWhiteSpace() { // To create default trx file, log file parameter should be null. - this.parameters[TrxLogger.LogFileNameKey] = null; + this.parameters[TrxLoggerConstants.LogFileNameKey] = null; this.testableTrxLogger.Initialize(this.events.Object, this.parameters); this.MakeTestRunComplete(); @@ -378,7 +373,7 @@ public void TheDefaultTrxFileNameShouldNotHaveWhiteSpace() public void DefaultTrxFileShouldCreateIfLogFileNameParameterNotPassed() { // To create default trx file, If LogFileName parameter not passed - this.parameters.Remove(TrxLogger.LogFileNameKey); + this.parameters.Remove(TrxLoggerConstants.LogFileNameKey); this.testableTrxLogger.Initialize(this.events.Object, this.parameters); this.MakeTestRunComplete(); @@ -426,7 +421,7 @@ public void GetQToolsTestElementFromTestCaseShouldAssignTestCategoryOfUnitTestEl testCase.SetPropertyValue(testProperty, new[] { "AsmLevel", "ClassLevel", "MethodLevel" }); - TrxLoggerObjectModel.UnitTestElement unitTestElement = Converter.GetQToolsTestElementFromTestCase(result); + TrxLoggerObjectModel.UnitTestElement unitTestElement = null; // Converter.GetQToolsTestElementFromTestCase(result); object[] expected = new[] { "MethodLevel", "ClassLevel", "AsmLevel" }; @@ -442,7 +437,7 @@ public void GetQToolsTestElementFromTestCaseShouldNotFailWhenThereIsNoTestCatego ObjectModel.TestCase testCase = CreateTestCase("TestCase1"); ObjectModel.TestResult result = new ObjectModel.TestResult(testCase); - TrxLoggerObjectModel.UnitTestElement unitTestElement = Converter.GetQToolsTestElementFromTestCase(result); + TrxLoggerObjectModel.UnitTestElement unitTestElement = null; // Converter.GetQToolsTestElementFromTestCase(result); object[] expected = Enumerable.Empty().ToArray(); From f7a513e01f01ec3d34c10db32b14b5d53ce9ed94 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 15:45:38 +0530 Subject: [PATCH 04/19] refactoring --- .../Interfaces/ITestAggregation.cs | 3 +- .../ObjectModel/TestAggregation.cs | 6 ++-- .../ObjectModel/TestEntry.cs | 31 ++++++++++--------- .../ObjectModel/TestResult.cs | 1 - .../ObjectModel/UnitTestElement.cs | 2 +- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs index 1007e6029f..86775d77d2 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs @@ -1,12 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Collections.Generic; namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { internal interface ITestAggregation : ITestElement { - List TestLinks { get; } // move to ITestElement + Dictionary TestLinks { get; } // move to ITestElement } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs index cb82503787..8488962607 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs @@ -9,14 +9,14 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel internal abstract class TestAggregation : TestElement, ITestAggregation { - protected List testLinks = new List(); + protected Dictionary testLinks = new Dictionary(); public TestAggregation( Guid id, string name, string adapter) : base(id, name, adapter) { } - public List TestLinks + public Dictionary TestLinks { get { return testLinks; } } @@ -27,7 +27,7 @@ public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters XmlPersistence h = new XmlPersistence(); if (testLinks.Count > 0) - h.SaveIEnumerable(testLinks, element, "TestLinks", ".", "TestLink", parameters); + h.SaveIEnumerable(testLinks.Values, element, "TestLinks", ".", "TestLink", parameters); } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs index d2a31e9b39..daee7b3737 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs @@ -3,6 +3,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { + using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -16,8 +17,8 @@ internal sealed class TestEntry : IXmlTestStore #region Fields private TestId testId; - private TestExecId execId; - private TestExecId parentExecId; + private Guid executionId; + private Guid parentExecutionId; private TestListCategoryId categoryId; private List testEntries = new List(); @@ -48,28 +49,28 @@ public TestEntry(TestId testId, TestListCategoryId catId) /// /// Gets or sets the exec id. /// - public TestExecId ExecId + public Guid ExecutionId { - get { return this.execId; } + get { return this.executionId; } set { Debug.Assert(value != null, "ExecId is null"); - this.execId = value; + this.executionId = value; } } /// /// Gets or sets the parent exec id. /// - public TestExecId ParentExecId + public Guid ParentExecutionId { - get { return this.parentExecId; } + get { return this.parentExecutionId; } set { Debug.Assert(value != null, "ExecId is null"); - this.parentExecId = value; + this.parentExecutionId = value; } } @@ -101,10 +102,10 @@ public override bool Equals(object obj) return false; } - Debug.Assert(this.execId != null, "this.execId is null"); - Debug.Assert(e.execId != null, "e.execId is null"); + Debug.Assert(this.executionId != null, "this.executionId is null"); + Debug.Assert(e.executionId != null, "e.executionId is null"); - if (!this.execId.Equals(e.execId)) + if (!this.executionId.Equals(e.executionId)) { return false; } @@ -122,7 +123,7 @@ public override bool Equals(object obj) /// public override int GetHashCode() { - return this.execId.GetHashCode(); + return this.executionId.GetHashCode(); } #endregion @@ -144,9 +145,9 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter helper.SaveSingleFields(element, this, parameters); helper.SaveObject(this.testId, element, null); - helper.SaveGuid(element, "@executionId", this.execId.Id); - if (parentExecId != null) - helper.SaveGuid(element, "@parentExecutionId", this.parentExecId.Id); + helper.SaveGuid(element, "@executionId", this.executionId); + if (parentExecutionId != null) + helper.SaveGuid(element, "@parentExecutionId", this.parentExecutionId); helper.SaveGuid(element, "@testListId", this.categoryId.Id); if (testEntries.Count > 0) helper.SaveIEnumerable(testEntries, element, "TestEntries", ".", "TestEntry", parameters); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs index ccb6f6442e..02924763a5 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs @@ -551,7 +551,6 @@ internal void AddResultFiles(IEnumerable resultFileList) { Debug.Assert(!string.IsNullOrEmpty(resultFile), "'resultFile' is null or empty"); Debug.Assert(resultFile.Trim() == resultFile, "'resultFile' has whitespace at the ends"); - Debug.Assert(Path.IsPathRooted(resultFile), "'resultFile' is a relative path"); this.resultFiles[FileHelper.MakePathRelative(resultFile, testResultsDirectory)] = null; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs index 1ab80001d7..0db3830014 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs @@ -76,7 +76,7 @@ public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters XmlPersistence h = new XmlPersistence(); h.SaveSimpleField(element, "TestMethod/@codeBase", this.codeBase, string.Empty); - h.SaveSimpleField(element, "TestMethod/@executorUri", this.adapter, string.Empty); + h.SaveSimpleField(element, "TestMethod/@adapterTypeName", this.adapter, string.Empty); h.SaveObject(this.testMethod, element, "TestMethod", parameters); } } From 9fe2b79e6da7b4eea0819d4a06b4727594a7a550 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 15:46:25 +0530 Subject: [PATCH 05/19] fix for hierarchical level more than 1 --- .../TrxLogger.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 9293f90105..977fb1b481 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -22,6 +22,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger using TrxLoggerConstants = Microsoft.TestPlatform.Extensions.TrxLogger.Utility.Constants; using TrxLoggerObjectModel = Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; + // TODO***: write test cases /// /// Logger for Generating TRX @@ -42,6 +43,7 @@ internal class TrxLogger : ITestLoggerWithParameters private Dictionary additionalResults; private Dictionary testElements; private Dictionary entries; + private Dictionary additionalTestEntries; /// /// Specifies the run level "out" messages @@ -268,7 +270,7 @@ private Guid GetTestType(ObjectModel.TestResult testResult) testType = (testTypeProperty == null) ? testType : testResult.GetPropertyValue(testTypeProperty, testType); // Except OrderedTest, all test types are updated to unit test type. - testType = (testType == TrxLoggerConstants.OrderedTestType) ? testType : TrxLoggerConstants.UnitTestType; + testType = (testType == TrxLoggerConstants.OrderedTestType) ? testType : TrxLoggerConstants.UnitTestType; // think about it as its only present in tmi adn is used only for orderedtest return testType; } @@ -337,12 +339,15 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve var name = e.Result.TestCase.DisplayName; testElement = Converter.ToTestElement(testType, name, executionId, parentExecutionId, e.Result.TestCase); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. testElements.Add(testId, testElement); - (parentTestElement as OrderedTestElement).TestLinks.Add(new TestLink(testElement.Id.Id, testElement.Name, testElement.Storage)); } else { this.testElements.TryGetValue(testId, out testElement); } + if (!(parentTestElement as OrderedTestElement).TestLinks.ContainsKey(testElement.Id.Id)) + { + (parentTestElement as OrderedTestElement).TestLinks.Add(testElement.Id.Id, new TestLink(testElement.Id.Id, testElement.Name, testElement.Storage)); + } //create/get test element if not exists. and link this test element to parent test element } else if (parentTestElement != null) @@ -375,7 +380,7 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve this.results.Add(executionId, testResult); TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); - te.ExecId = testElement.ExecutionId; + te.ExecutionId = executionId; this.entries.Add(executionId, te); } else if (parentTestElement is OrderedTestElement) @@ -384,11 +389,16 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve this.additionalResults.Add(executionId, testResult); TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); - te.ExecId = testElement.ExecutionId; - te.ParentExecId = testElement.ParentExecutionId; + te.ExecutionId = executionId; + te.ParentExecutionId = parentExecutionId; this.entries.TryGetValue(parentExecutionId, out var parentTestEntry); // todo: handle null case. + if (parentTestEntry == null) + { + this.additionalTestEntries.TryGetValue(parentExecutionId, out parentTestEntry); + } parentTestEntry.TestEntries.Add(te); + this.additionalTestEntries.Add(executionId, te); } else { @@ -542,6 +552,7 @@ private void InitializeInternal() this.additionalResults = new Dictionary(); this.testElements = new Dictionary(); this.entries = new Dictionary(); + this.additionalTestEntries = new Dictionary(); this.runLevelErrorsAndWarnings = new List(); this.testRun = null; this.totalTests = 0; From f7efc364f3943ef81e7cd763c3b65bfd8feeaf10 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Fri, 8 Dec 2017 16:42:57 +0530 Subject: [PATCH 06/19] removing unnecessary inner resutls count --- .../TrxLogger.cs | 7 ------- .../Utility/Constants.cs | 5 ----- 2 files changed, 12 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 977fb1b481..5222081f85 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -238,12 +238,6 @@ private void CreateTestRun() this.testRun.RunConfiguration = testrunConfig; } - private int GetInnerResultsCount(ObjectModel.TestResult testResult) - { - ObjectModel.TestProperty innerResultsCountProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.InnerResultsCountPropertyIdentifier)); - return innerResultsCountProperty == null ? 0 : testResult.GetPropertyValue(innerResultsCountProperty, default(int)); - } - private Guid GetParentExecutionId(ObjectModel.TestResult testResult) { ObjectModel.TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.ParentExecutionIdPropertyIdentifier)); @@ -295,7 +289,6 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve if (e.Result.Outcome == ObjectModel.TestOutcome.Skipped) this.HandleSkippedTest(e.Result); - var innerResultsCount = GetInnerResultsCount(e.Result); var executionId = GetExecutionId(e.Result); // TODO: instead of creating these in converter also, create at only one place. var parentExecutionId = GetParentExecutionId(e.Result); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index 3fcc3921e1..d2cc38e6ca 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -39,11 +39,6 @@ internal static class Constants public const string TestTypePropertyIdentifier = "TestType"; - /// - /// Property Id storing the ExecutionId. - /// - public const string InnerResultsCountPropertyIdentifier = "InnerResultsCount"; // remove if not required - /// /// Property Id storing the TMITestId. /// From 982160a758a924590482cde9aba6376add5e7da7 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 00:30:20 +0530 Subject: [PATCH 07/19] refactoring --- .../TrxLogger.cs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 5222081f85..7b9ea1d5b5 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -232,26 +232,39 @@ private void CreateTestRun() // Save default test settings string runDeploymentRoot = FileHelper.ReplaceInvalidFileNameChars(this.testRun.Name); TestRunConfiguration testrunConfig = new TestRunConfiguration("default"); - testrunConfig.RunDeploymentRootDirectory = runDeploymentRoot; - this.testRun.RunConfiguration = testrunConfig; } + /// + /// Gets parent execution id of test result. + /// + /// + /// Parent execution id. private Guid GetParentExecutionId(ObjectModel.TestResult testResult) { - ObjectModel.TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.ParentExecutionIdPropertyIdentifier)); - return parentExecutionIdProperty == null ? Guid.Empty : testResult.GetPropertyValue(parentExecutionIdProperty, default(Guid)); + TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault( + property => property.Id.Equals(TrxLoggerConstants.ParentExecutionIdPropertyIdentifier)); + + return parentExecutionIdProperty == null ? + Guid.Empty : + testResult.GetPropertyValue(parentExecutionIdProperty, Guid.Empty); } + /// + /// Gets execution Id of test result. Creates new id if not present in test result properties. + /// + /// + /// Execution id. private Guid GetExecutionId(ObjectModel.TestResult testResult) { - ObjectModel.TestProperty executionIdProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? + TestProperty executionIdProperty = testResult.Properties.FirstOrDefault( + property => property.Id.Equals(TrxLoggerConstants.ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? + var executionId = Guid.Empty; if (executionIdProperty != null) - { - executionId = testResult.GetPropertyValue(executionIdProperty, Guid.NewGuid()); - } + executionId = testResult.GetPropertyValue(executionIdProperty, Guid.Empty); + return executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; } @@ -281,7 +294,9 @@ private Guid GetTestType(ObjectModel.TestResult testResult) /// internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEventArgs e) { + // TODO: if this value is false for any adapter, then parent result will not go. So in that case trx logger will get parent execution id of results but not the result. So for this case, flatten out the results. System.Diagnostics.Debugger.Launch(); + // Create test run if (this.testRun == null) CreateTestRun(); From 7f98cfdeac5485f63a3ed74fc7cb86e63ced53b4 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 00:38:55 +0530 Subject: [PATCH 08/19] Interfaces refactoring --- .../Interfaces/ITestAggregation.cs | 2 +- .../Interfaces/ITestElement.cs | 6 +++--- .../Interfaces/ITestResult.cs | 18 +++++++++--------- .../Interfaces/ITestResultAggregation.cs | 3 +-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs index 86775d77d2..d34c3a7ebb 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs @@ -8,6 +8,6 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { internal interface ITestAggregation : ITestElement { - Dictionary TestLinks { get; } // move to ITestElement + Dictionary TestLinks { get; } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs index 0541c57e29..92d7db22f4 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestElement.cs @@ -8,14 +8,14 @@ internal interface ITestElement TestId Id { get; } string Name { get; set; } string Owner { get; set; } - int Priority { get; set; } string Storage { get; set; } + string Adapter { get; } + int Priority { get; set; } + bool IsRunnable { get; } TestExecId ExecutionId { get; set; } TestExecId ParentExecutionId { get; set; } - bool IsRunnable { get; } TestListCategoryId CategoryId { get; set; } TestCategoryItemCollection TestCategories { get; } TestType TestType { get; } - string Adapter { get; } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs index b45a8364e0..7834016331 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs @@ -7,21 +7,21 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { internal interface ITestResult { - DateTime StartTime { get; set; } - DateTime EndTime { get; set; } - TimeSpan Duration { get; set; } - string ComputerName { get; } - TestOutcome Outcome { get; set; } TestResultId Id { get; } - string ErrorMessage { get; set; } - string ErrorStackTrace { get; set; } - string[] TextMessages { get; set; } + string ResultType { get; set; } string StdOut { get; set; } string StdErr { get; set; } string DebugTrace { get; set; } string TestResultsDirectory { get; } string RelativeTestResultsDirectory { get; } + string ErrorMessage { get; set; } + string ErrorStackTrace { get; set; } + string ComputerName { get; } + string[] TextMessages { get; set; } int DataRowInfo { get; set; } - string ResultType { get; set; } + DateTime StartTime { get; set; } + DateTime EndTime { get; set; } + TimeSpan Duration { get; set; } + TestOutcome Outcome { get; set; } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs index f5775115c2..33d3b0474e 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; using System.Collections.Generic; namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { internal interface ITestResultAggregation : ITestResult { - List InnerResults { get; } // move to itestresult + List InnerResults { get; } } } From 2daf49528dd6bb565fc6a51c1ca909d23757dc94 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 01:31:25 +0530 Subject: [PATCH 09/19] refactoring --- .../ObjectModel/OrderedTestElement.cs | 11 +++-- .../ObjectModel/TestAggregation.cs | 11 +++-- .../ObjectModel/TestElement.cs | 29 ++++++++---- .../ObjectModel/TestLink.cs | 23 ++++++++- .../ObjectModel/TestResult.cs | 47 +++++-------------- .../ObjectModel/TestResultAggregation.cs | 6 +++ .../ObjectModel/UnitTestElement.cs | 10 ++-- .../Utility/Constants.cs | 7 +++ 8 files changed, 84 insertions(+), 60 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs index f1d6077833..2e26770f79 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs @@ -4,18 +4,19 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { using System; + using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + /// + /// Ordered test element. + /// internal class OrderedTestElement : TestAggregation, IXmlTestStoreCustom { - private static readonly Guid TestTypeGuid = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); // move to constants - private static readonly TestType TestTypeInstance = new TestType(TestTypeGuid); - public OrderedTestElement(Guid id, string name, string adapter) : base(id, name, adapter) { } string IXmlTestStoreCustom.ElementName { - get { return "OrderedTest"; } + get { return Constants.OrderedTestElementName; } } string IXmlTestStoreCustom.NamespaceUri @@ -28,7 +29,7 @@ string IXmlTestStoreCustom.NamespaceUri /// public override TestType TestType { - get { return TestTypeInstance; } + get { return Constants.OrderedTestTypeInstance; } } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs index 8488962607..930366dd10 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs @@ -7,15 +7,18 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel using System.Collections.Generic; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + /// + /// Test aggregation element. + /// internal abstract class TestAggregation : TestElement, ITestAggregation { protected Dictionary testLinks = new Dictionary(); - public TestAggregation( - Guid id, - string name, - string adapter) : base(id, name, adapter) { } + public TestAggregation(Guid id, string name, string adapter) : base(id, name, adapter) { } + /// + /// Test links. + /// public Dictionary TestLinks { get { return testLinks; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs index 90eb7403af..9562822962 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs @@ -10,6 +10,9 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel using Microsoft.TestPlatform.Extensions.TrxLogger.XML; using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; + /// + /// Test element. + /// internal abstract class TestElement : ITestElement, IXmlTestStore { /// @@ -20,19 +23,16 @@ internal abstract class TestElement : ITestElement, IXmlTestStore protected TestId id; protected string name; protected string owner; - protected int priority; protected string storage; + protected string adapter; + protected int priority; + protected bool isRunnable; protected TestExecId executionId; protected TestExecId parentExecutionId; - protected bool isRunnable; protected TestCategoryItemCollection testCategories; - protected string adapter; protected TestListCategoryId catId; - public TestElement( - Guid id, - string name, - string adapter) + public TestElement(Guid id, string name, string adapter) { Debug.Assert(!string.IsNullOrEmpty(name), "name is null"); Debug.Assert(!string.IsNullOrEmpty(adapter), "adapter is null"); @@ -107,7 +107,7 @@ public string Storage /// /// Gets or sets the execution id. /// - public TestExecId ExecutionId // remove variables for properties if not required + public TestExecId ExecutionId { get { return this.executionId; } set { this.executionId = value; } @@ -122,6 +122,9 @@ public TestExecId ParentExecutionId set { this.parentExecutionId = value; } } + /// + /// Gets the isRunnable value. + /// public bool IsRunnable { get { return this.isRunnable; } @@ -158,13 +161,23 @@ public TestCategoryItemCollection TestCategories } } + /// + /// Gets the adapter name. + /// public string Adapter { get { return adapter; } } + /// + /// Gets the test type. + /// public abstract TestType TestType { get; } + /// + /// Override for ToString. + /// + /// String representation of test element. public override string ToString() { return string.Format( diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs index 5de74fc666..151c10f73f 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs @@ -9,6 +9,9 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + /// + /// Test link. + /// internal sealed class TestLink : IXmlTestStore { private Guid id; @@ -31,16 +34,25 @@ public TestLink(Guid id, string name, string storage) this.storage = storage; } + /// + /// Gets the id. + /// public Guid Id { get { return this.id; } } + /// + /// Gets the name. + /// public string Name { get { return this.name; } } + /// + /// Gets the storage. + /// public string Storage { get { return this.storage; } @@ -63,19 +75,26 @@ public override bool Equals(object other) public bool IsSame(TestLink other) { if (other == null) - { return false; - } return this.id.Equals(other.id) && this.name.Equals(other.name) && this.storage.Equals(other.storage); } + /// + /// Override for GetHashCode. + /// + /// public override int GetHashCode() { return this.id.GetHashCode(); } + + /// + /// Override for ToString. + /// + /// public override string ToString() { return string.Format( diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs index 02924763a5..cba4eea820 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs @@ -213,55 +213,24 @@ internal class TestResult : ITestResult, IXmlTestStore { #region Fields - /// - /// Id of test within run - /// private TestResultId id; - - /// - /// Name of test within run - /// private string testName; - private string computerInfo; - + private string stdOut; + private string stdErr; + private string debugTrace; + private string resultType; + private int dataRowInfo; private TimeSpan duration; - private DateTime startTime; - private DateTime endTime; - - /// - /// Type of test (Guid) - /// private TestType testType; - - /// - /// The outcome of the test result - /// private TestOutcome outcome; - - /// - /// The test run in which the test was executed - /// private TestRun testRun; - - private string stdOut; - - private string stdErr; - - private string debugTrace; - private TestResultErrorInfo errorInfo; - private TestListCategoryId categoryId; - private ArrayList textMessages; - private int dataRowInfo; - - private string resultType; - /// /// Directory containing the test result files, relative to the root test results directory /// @@ -483,12 +452,18 @@ public string RelativeTestResultsDirectory get { return this.relativeTestResultsDirectory; } } + /// + /// Gets or sets the data row info. + /// public int DataRowInfo { get { return this.dataRowInfo; } set { this.dataRowInfo = value; } } + /// + /// Gets or sets the result type. + /// public string ResultType { get { return this.resultType; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs index b1f3b31334..943613b635 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs @@ -7,6 +7,9 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel using System.Collections.Generic; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + /// + /// Test result aggregation. + /// internal class TestResultAggregation : TestResult, ITestResultAggregation { protected List innerResults = new List(); @@ -20,6 +23,9 @@ public TestResultAggregation( ITestElement test, TestOutcome outcome) : base(testName, computerName, runId, executionId, parentExecutionId, test, outcome) { } + /// + /// Gets the inner results. + /// public List InnerResults { get { return innerResults; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs index 0db3830014..dbcfcdc035 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs @@ -8,11 +8,11 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel using Microsoft.TestPlatform.Extensions.TrxLogger.Utility; using Microsoft.TestPlatform.Extensions.TrxLogger.XML; + /// + /// Unit test element. + /// internal class UnitTestElement : TestElement, IXmlTestStoreCustom { - private static readonly Guid TestTypeGuid = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); // move to constants - private static readonly TestType TestTypeInstance = new TestType(TestTypeGuid); - private string codeBase; private TestMethod testMethod; @@ -31,7 +31,7 @@ public UnitTestElement( string IXmlTestStoreCustom.ElementName { - get { return "UnitTest"; } + get { return Constants.UnitTestElementName; } } string IXmlTestStoreCustom.NamespaceUri @@ -44,7 +44,7 @@ string IXmlTestStoreCustom.NamespaceUri /// public override TestType TestType { - get { return TestTypeInstance; } + get { return Constants.UnitTestTypeInstance; } } /// diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index d2cc38e6ca..91f7202abf 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using System; // check where it should be? within namespace or outside? namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility @@ -27,6 +28,9 @@ internal static class Constants /// public const string LogFileNameKey = "LogFileName"; + public const string OrderedTestElementName = "OrderedTest"; + public const string UnitTestElementName = "UnitTest"; + /// /// Property Id storing the ExecutionId. /// @@ -45,6 +49,9 @@ internal static class Constants public const string TmiTestIdPropertyIdentifier = "MSTestDiscoverer.TmiTestId"; public static readonly Guid OrderedTestType = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); + public static readonly TestType OrderedTestTypeInstance = new TestType(OrderedTestType); + public static readonly Guid UnitTestType = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); + public static readonly TestType UnitTestTypeInstance = new TestType(UnitTestType); } } From b7168eed41c8b17c187ab784c01410611e7525e2 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 01:35:45 +0530 Subject: [PATCH 10/19] refactoring --- .../Utility/Constants.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index 91f7202abf..f73183a0b2 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; -using System; // check where it should be? within namespace or outside? namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility { @@ -28,7 +28,14 @@ internal static class Constants /// public const string LogFileNameKey = "LogFileName"; + /// + /// Ordered test element name + /// public const string OrderedTestElementName = "OrderedTest"; + + /// + /// Unit test element name + /// public const string UnitTestElementName = "UnitTest"; /// @@ -41,6 +48,9 @@ internal static class Constants /// public const string ParentExecutionIdPropertyIdentifier = "ParentExecId"; + /// + /// Property If storing the TestType. + /// public const string TestTypePropertyIdentifier = "TestType"; /// @@ -48,10 +58,24 @@ internal static class Constants /// public const string TmiTestIdPropertyIdentifier = "MSTestDiscoverer.TmiTestId"; + /// + /// Ordered test type + /// public static readonly Guid OrderedTestType = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); + + /// + /// Ordered test type instance + /// public static readonly TestType OrderedTestTypeInstance = new TestType(OrderedTestType); + /// + /// Unit test type + /// public static readonly Guid UnitTestType = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); + + /// + /// Unit test type instance. + /// public static readonly TestType UnitTestTypeInstance = new TestType(UnitTestType); } } From a128e79ff1de16c44104924461e25938ced84b06 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 15:59:25 +0530 Subject: [PATCH 11/19] Refactoring --- .../ObjectModel/OrderedTestElement.cs | 2 +- .../ObjectModel/TestResult.cs | 41 +- .../ObjectModel/TestResultAggregation.cs | 12 +- .../ObjectModel/UnitTestElement.cs | 2 +- .../ObjectModel/UnitTestResult.cs | 12 +- .../TrxLogger.cs | 383 ++++++++------- .../Utility/Constants.cs | 26 +- .../Utility/Converter.cs | 454 ++++++++++-------- 8 files changed, 511 insertions(+), 421 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs index 2e26770f79..d3244f26db 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs @@ -29,7 +29,7 @@ string IXmlTestStoreCustom.NamespaceUri /// public override TestType TestType { - get { return Constants.OrderedTestTypeInstance; } + get { return Constants.OrderedTestType; } } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs index cba4eea820..17ac56b034 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs @@ -24,7 +24,7 @@ internal sealed class TestResultId : IXmlTestStore private Guid runId; private Guid executionId; private Guid parentExecutionId; - private TestId testId; + private Guid testId; #endregion @@ -45,7 +45,7 @@ internal sealed class TestResultId : IXmlTestStore /// /// The test id. /// - public TestResultId(Guid runId, Guid executionId, Guid parentExecutionId, TestId testId) + public TestResultId(Guid runId, Guid executionId, Guid parentExecutionId, Guid testId) { this.runId = runId; this.executionId = executionId; @@ -68,7 +68,7 @@ public Guid ExecutionId /// /// Gets the test id. /// - public TestId TestId + public Guid TestId { get { return this.testId; } } @@ -214,7 +214,7 @@ internal class TestResult : ITestResult, IXmlTestStore #region Fields private TestResultId id; - private string testName; + private string resultName; private string computerInfo; private string stdOut; private string stdErr; @@ -265,29 +265,30 @@ internal class TestResult : ITestResult, IXmlTestStore /// /// The outcome. /// - public TestResult(string testName, - string computerName, - Guid runId, Guid executionId, + public TestResult( + Guid runId, + Guid testId, + Guid executionId, Guid parentExecutionId, - ITestElement test, - TestOutcome outcome) + string resultName, + string computerName, + TestOutcome outcome, + TestType testType, + TestListCategoryId testCategoryId) { - // check if we can remove dependency on test element. Debug.Assert(computerName != null, "computername is null"); - Debug.Assert(test != null, "test is null"); - Debug.Assert(!Guid.Empty.Equals(test.ExecutionId.Id), "ExecutionId is empty"); - Debug.Assert(!Guid.Empty.Equals(test.Id.Id), "Id is empty"); + Debug.Assert(!Guid.Empty.Equals(executionId), "ExecutionId is empty"); + Debug.Assert(!Guid.Empty.Equals(testId), "TestId is empty"); this.Initialize(); - this.id = new TestResultId(runId, executionId, parentExecutionId, test.Id); - this.testName = testName; - this.testType = test.TestType; + this.id = new TestResultId(runId, executionId, parentExecutionId, testId); + this.resultName = resultName; + this.testType = testType; this.computerInfo = computerName; - this.outcome = outcome; - this.categoryId = test.CategoryId; - this.relativeTestResultsDirectory = TestRunDirectories.GetRelativeTestResultsDirectory(test.ExecutionId.Id); + this.categoryId = testCategoryId; + this.relativeTestResultsDirectory = TestRunDirectories.GetRelativeTestResultsDirectory(executionId); } #endregion @@ -577,7 +578,7 @@ public virtual void Save(System.Xml.XmlElement element, XmlTestStoreParameters p XmlPersistence helper = new XmlPersistence(); helper.SaveObject(this.id, element, ".", parameters); - helper.SaveSimpleField(element, "@testName", this.testName, string.Empty); + helper.SaveSimpleField(element, "@testName", this.resultName, string.Empty); helper.SaveSimpleField(element, "@computerName", this.computerInfo, string.Empty); helper.SaveSimpleField(element, "@duration", this.duration, default(TimeSpan)); helper.SaveSimpleField(element, "@startTime", this.startTime, default(DateTime)); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs index 943613b635..3a7a3945eb 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs @@ -15,13 +15,15 @@ internal class TestResultAggregation : TestResult, ITestResultAggregation protected List innerResults = new List(); public TestResultAggregation( - string testName, - string computerName, Guid runId, + Guid testId, Guid executionId, Guid parentExecutionId, - ITestElement test, - TestOutcome outcome) : base(testName, computerName, runId, executionId, parentExecutionId, test, outcome) { } + string resultName, + string computerName, + TestOutcome outcome, + TestType testType, + TestListCategoryId testCategoryId) : base(runId, testId, executionId, parentExecutionId, resultName, computerName, outcome, testType, testCategoryId) { } /// /// Gets the inner results. @@ -40,4 +42,4 @@ public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters helper.SaveIEnumerable(innerResults, element, "InnerResults", ".", null, parameters); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs index dbcfcdc035..b50a9ef1e1 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestElement.cs @@ -44,7 +44,7 @@ string IXmlTestStoreCustom.NamespaceUri /// public override TestType TestType { - get { return Constants.UnitTestTypeInstance; } + get { return Constants.UnitTestType; } } /// diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs index fe8f7c7469..66c815765e 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/UnitTestResult.cs @@ -11,12 +11,14 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel internal class UnitTestResult: TestResultAggregation { public UnitTestResult( - string testName, - string computerName, - Guid runId, + Guid runId, + Guid testId, Guid executionId, Guid parentExecutionId, - ITestElement test, - TestOutcome outcome) : base(testName, computerName, runId, executionId, parentExecutionId, test, outcome) { } + string resultName, + string computerName, + TestOutcome outcome, + TestType testType, + TestListCategoryId testCategoryId) : base(runId, testId, executionId, parentExecutionId, resultName, computerName, outcome, testType, testCategoryId) { } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 7b9ea1d5b5..fb602c3387 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -9,7 +9,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; - using System.Linq; using System.Text; using System.Xml; using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; @@ -22,7 +21,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger using TrxLoggerConstants = Microsoft.TestPlatform.Extensions.TrxLogger.Utility.Constants; using TrxLoggerObjectModel = Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; - // TODO***: write test cases /// /// Logger for Generating TRX @@ -184,7 +182,7 @@ internal TrxLoggerObjectModel.TestOutcome TestResultOutcome /// /// Event args /// - internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) + public void TestMessageHandler(object sender, TestRunMessageEventArgs e) { System.Diagnostics.Debugger.Launch(); ValidateArg.NotNull(sender, "sender"); @@ -211,78 +209,6 @@ internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) } } - /// - /// Creates test run. - /// - private void CreateTestRun() - { - // Don't create run if already created. - if (testRun != null) - return; - - Guid runId = Guid.NewGuid(); - this.testRun = new TestRun(runId); - - // We cannot rely on the StartTime for the first test result - // In case of parallel, first test result is the fastest test and not the one which started first. - // Setting Started to DateTime.Now in Intialize will make sure we include the startup cost, which was being ignored earlier. - // This is in parity with the way we set this.testRun.Finished - this.testRun.Started = this.testRunStartTime; - - // Save default test settings - string runDeploymentRoot = FileHelper.ReplaceInvalidFileNameChars(this.testRun.Name); - TestRunConfiguration testrunConfig = new TestRunConfiguration("default"); - testrunConfig.RunDeploymentRootDirectory = runDeploymentRoot; - this.testRun.RunConfiguration = testrunConfig; - } - - /// - /// Gets parent execution id of test result. - /// - /// - /// Parent execution id. - private Guid GetParentExecutionId(ObjectModel.TestResult testResult) - { - TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault( - property => property.Id.Equals(TrxLoggerConstants.ParentExecutionIdPropertyIdentifier)); - - return parentExecutionIdProperty == null ? - Guid.Empty : - testResult.GetPropertyValue(parentExecutionIdProperty, Guid.Empty); - } - - /// - /// Gets execution Id of test result. Creates new id if not present in test result properties. - /// - /// - /// Execution id. - private Guid GetExecutionId(ObjectModel.TestResult testResult) - { - TestProperty executionIdProperty = testResult.Properties.FirstOrDefault( - property => property.Id.Equals(TrxLoggerConstants.ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? - - var executionId = Guid.Empty; - if (executionIdProperty != null) - executionId = testResult.GetPropertyValue(executionIdProperty, Guid.Empty); - - return executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; - } - - private Guid GetTestType(ObjectModel.TestResult testResult) - { - var testType = TrxLoggerConstants.UnitTestType; - - // Get test type from property. default to unit test type. - ObjectModel.TestProperty testTypeProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(TrxLoggerConstants.TestTypePropertyIdentifier)); - testType = (testTypeProperty == null) ? testType : testResult.GetPropertyValue(testTypeProperty, testType); - - // Except OrderedTest, all test types are updated to unit test type. - testType = (testType == TrxLoggerConstants.OrderedTestType) ? testType : TrxLoggerConstants.UnitTestType; // think about it as its only present in tmi adn is used only for orderedtest - - return testType; - } - - /// /// Called when a test result is received. /// @@ -292,10 +218,8 @@ private Guid GetTestType(ObjectModel.TestResult testResult) /// /// The eventArgs. /// - internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEventArgs e) + public void TestResultHandler(object sender, ObjectModel.Logging.TestResultEventArgs e) { - // TODO: if this value is false for any adapter, then parent result will not go. So in that case trx logger will get parent execution id of results but not the result. So for this case, flatten out the results. - System.Diagnostics.Debugger.Launch(); // Create test run if (this.testRun == null) CreateTestRun(); @@ -304,120 +228,31 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve if (e.Result.Outcome == ObjectModel.TestOutcome.Skipped) this.HandleSkippedTest(e.Result); - var executionId = GetExecutionId(e.Result); // TODO: instead of creating these in converter also, create at only one place. - var parentExecutionId = GetParentExecutionId(e.Result); + var testType = Converter.GetTestType(e.Result); + var executionId = Converter.GetExecutionId(e.Result); - ITestResult parentTestResult = null; - ITestElement parentTestElement = null; - if (parentExecutionId != Guid.Empty) + // Setting parent properties like parent result, parent test element, parent execution id. + var parentExecutionId = Converter.GetParentExecutionId(e.Result); + var parentTestResult = GetTestResult(parentExecutionId); + var parentTestElement = (parentTestResult != null) ? GetTestElement(parentTestResult.Id.TestId) : null; + if (parentTestResult == null || parentTestElement == null || parentExecutionId == Guid.Empty) { - this.results.TryGetValue(parentExecutionId, out parentTestResult); // todo: handle guid empty case - if (parentTestResult == null) - { - this.additionalResults.TryGetValue(parentExecutionId, out parentTestResult); - } - // we should always get parentTestResult. If not that a problem. - this.testElements.TryGetValue(parentTestResult.Id.TestId.Id, out parentTestElement); // todo: handle parent test result null case. + parentTestResult = null; + parentTestElement = null; + parentExecutionId = Guid.Empty; } - var testType = GetTestType(e.Result); - //e.Result.TestCase.ExecutorUri.ToString().ToLower().Contains("orderedtestadapter") ? "orderedtest" : "unittest"; //TODO: all hard coded vars in consts + // Create trx test element from rocksteady test case + var testElement = GetOrCreateTestElement(executionId, parentExecutionId, testType, parentTestElement, e.Result.TestCase); - // Create MSTest test element from rocksteady test case - ITestElement testElement = null; - if (parentExecutionId == Guid.Empty) - { - Guid testId = Converter.GetTestId(e.Result.TestCase); - if (!this.testElements.ContainsKey(testId)) - { - var name = e.Result.TestCase.DisplayName; - testElement = Converter.ToTestElement(testType, name, executionId, parentExecutionId, e.Result.TestCase); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. - testElements.Add(testId, testElement); - } - else - { - this.testElements.TryGetValue(testId, out testElement); - } - } - else if(parentTestElement != null && parentTestElement is OrderedTestElement) - { - Guid testId = Converter.GetTestId(e.Result.TestCase); // TODO: if we are adding test id here, then dont create test id in totestelement. its a duplicacy. - if (!this.testElements.ContainsKey(testId)) - { - var name = e.Result.TestCase.DisplayName; - testElement = Converter.ToTestElement(testType, name, executionId, parentExecutionId, e.Result.TestCase); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. - testElements.Add(testId, testElement); - } - else - { - this.testElements.TryGetValue(testId, out testElement); - } - if (!(parentTestElement as OrderedTestElement).TestLinks.ContainsKey(testElement.Id.Id)) - { - (parentTestElement as OrderedTestElement).TestLinks.Add(testElement.Id.Id, new TestLink(testElement.Id.Id, testElement.Name, testElement.Storage)); - } - //create/get test element if not exists. and link this test element to parent test element - } - else if (parentTestElement != null) - { - testElement = parentTestElement; - //getParent test element - } - else - { - testElement = null; - // this case should never come. If it comes, throw error. - } - - // Convert the rocksteady result to MSTest result - ITestResult testResult; - TrxLoggerObjectModel.TestOutcome testOutcome = Converter.ToOutcome(e.Result.Outcome); - if (testElement is OrderedTestElement) - { - // it should be orderedtestelement - testResult = Converter.ToTestResult(e.Result, executionId, parentExecutionId, testElement, testOutcome, this.testRun, this.testResultsDirPath); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. - } - else - { - // it should be unitTestElement - testResult = Converter.ToTestResult(e.Result, executionId, parentExecutionId, testElement, testOutcome, this.testRun, this.testResultsDirPath); // TODO: if resulttype is not used anywhere else, then move creation of result type also in converter. - } - - if (parentExecutionId == Guid.Empty) - { - this.results.Add(executionId, testResult); - - TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); - te.ExecutionId = executionId; - this.entries.Add(executionId, te); - } - else if (parentTestElement is OrderedTestElement) - { - (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); - this.additionalResults.Add(executionId, testResult); + // Update test links + UpdateTestLinks(testElement, parentTestElement); - TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); - te.ExecutionId = executionId; - te.ParentExecutionId = parentExecutionId; - - this.entries.TryGetValue(parentExecutionId, out var parentTestEntry); // todo: handle null case. - if (parentTestEntry == null) - { - this.additionalTestEntries.TryGetValue(parentExecutionId, out parentTestEntry); - } - parentTestEntry.TestEntries.Add(te); - this.additionalTestEntries.Add(executionId, te); - } - else - { - testResult.DataRowInfo = (parentTestResult as TestResultAggregation).InnerResults.Count; - testResult.ResultType = "DataDrivenDataRow"; // todo: hard coding - parentTestResult.ResultType = "DataDrivenTest"; // todo: hard coding - (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); - } - // todo: is there any use of inner results? If not remove it. - // todo: in case parent test result or parent test element is not found, what should we do? this case should never happen. So we should error out. + // Convert the rocksteady result to trx test result + var testResult = CreateTestResult(executionId, parentExecutionId, testType, testElement, parentTestElement, parentTestResult, e.Result); + // Update test entries + UpdateTestEntries(executionId, parentExecutionId, testElement, parentTestElement); // Set various counts (passtests, failed tests, total tests) this.totalTests++; @@ -441,7 +276,7 @@ internal void TestResultHandler(object sender, ObjectModel.Logging.TestResultEve /// /// Test run complete events arguments. /// - internal void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) + public void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) { System.Diagnostics.Debugger.Launch(); if (this.testRun != null) @@ -622,6 +457,182 @@ private void SetDefaultTrxFilePath() this.trxFilePath = FileHelper.GetNextIterationFileName(this.testResultsDirPath, defaultTrxFileName, false); } + /// + /// Creates test run. + /// + private void CreateTestRun() + { + // Skip run creation if already exists. + if (testRun != null) + return; + + Guid runId = Guid.NewGuid(); + this.testRun = new TestRun(runId); + + // We cannot rely on the StartTime for the first test result + // In case of parallel, first test result is the fastest test and not the one which started first. + // Setting Started to DateTime.Now in Intialize will make sure we include the startup cost, which was being ignored earlier. + // This is in parity with the way we set this.testRun.Finished + this.testRun.Started = this.testRunStartTime; + + // Save default test settings + string runDeploymentRoot = FileHelper.ReplaceInvalidFileNameChars(this.testRun.Name); + TestRunConfiguration testrunConfig = new TestRunConfiguration("default"); + testrunConfig.RunDeploymentRootDirectory = runDeploymentRoot; + this.testRun.RunConfiguration = testrunConfig; + } + + /// + /// Gets test result from stored test results. + /// + /// + /// Test result + private ITestResult GetTestResult(Guid executionId) + { + ITestResult testResult = null; + + if (executionId != Guid.Empty) + { + this.results.TryGetValue(executionId, out testResult); // todo: handle guid empty case + + if (testResult == null) + this.additionalResults.TryGetValue(executionId, out testResult); // change this additional results name + } + + return testResult; + } + + /// + /// Gets test element from stored test elements. + /// + /// + /// + private ITestElement GetTestElement(Guid testId) + { + this.testElements.TryGetValue(testId, out var testElement); + return testElement; + } + + /// + /// Gets or creates test element. + /// + /// + /// + /// + /// + /// + /// Trx test element + private ITestElement GetOrCreateTestElement(Guid executionId, Guid parentExecutionId, TestType testType, ITestElement parentTestElement, ObjectModel.TestCase rockSteadyTestCase) + { + ITestElement testElement = parentTestElement; + if (parentTestElement == null || parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) + { + Guid testId = Converter.GetTestId(rockSteadyTestCase); + testElement = GetTestElement(testId); + + if (testElement == null) + { + testElement = Converter.ToTestElement(testId, executionId, parentExecutionId, testType, rockSteadyTestCase); + testElements.Add(testId, testElement); + } + } + + return testElement; + } + + /// + /// Update test links + /// + /// + /// + private void UpdateTestLinks(ITestElement testElement, ITestElement parentTestElement) + { + if (parentTestElement != null && + parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType) && + !(parentTestElement as OrderedTestElement).TestLinks.ContainsKey(testElement.Id.Id)) + { + (parentTestElement as OrderedTestElement).TestLinks.Add(testElement.Id.Id, new TestLink(testElement.Id.Id, testElement.Name, testElement.Storage)); + } + } + + /// + /// Creates test result + /// + /// + /// + /// + /// + /// + /// + /// + /// Trx test result + private ITestResult CreateTestResult(Guid executionId, Guid parentExecutionId, TestType testType, + ITestElement testElement, ITestElement parentTestElement, ITestResult parentTestResult, ObjectModel.TestResult rocksteadyTestResult) + { + // Create test result + TrxLoggerObjectModel.TestOutcome testOutcome = Converter.ToOutcome(rocksteadyTestResult.Outcome); + var testResult = Converter.ToTestResult(testElement.Id.Id, executionId, parentExecutionId, testElement.Name, + this.testResultsDirPath, testType, testElement.CategoryId, testOutcome, this.testRun, rocksteadyTestResult); + + // Normal result scenario + if (parentTestResult == null) + { + this.results.Add(executionId, testResult); + return testResult; + } + + // Ordered test inner result scenario + if (parentTestElement != null && parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) + { + (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); + this.additionalResults.Add(executionId, testResult); + return testResult; + } + + // Data driven inner result scenario + if (parentTestElement != null && parentTestElement.TestType.Equals(TrxLoggerConstants.UnitTestType)) + { + (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); + testResult.DataRowInfo = (parentTestResult as TestResultAggregation).InnerResults.Count; + testResult.ResultType = TrxLoggerConstants.InnerDataDrivenResultType; + parentTestResult.ResultType = TrxLoggerConstants.ParentDataDrivenResultType; + return testResult; + } + + return testResult; + } + + /// + /// Update test entries + /// + /// + /// + /// + /// + private void UpdateTestEntries(Guid executionId, Guid parentExecutionId, ITestElement testElement, ITestElement parentTestElement) + { + TestEntry te = new TestEntry(testElement.Id, TestListCategory.UncategorizedResults.Id); + te.ExecutionId = executionId; + + if (parentTestElement == null) + { + this.entries.Add(executionId, te); + } + else if (parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) + { + te.ParentExecutionId = parentExecutionId; + + this.entries.TryGetValue(parentExecutionId, out var parentTestEntry); + if (parentTestEntry == null) + this.additionalTestEntries.TryGetValue(parentExecutionId, out parentTestEntry); + + if (parentTestEntry != null) + parentTestEntry.TestEntries.Add(te); + + this.additionalTestEntries.Add(executionId, te); + } + } + #endregion } } \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index f73183a0b2..94343f6766 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -53,29 +53,39 @@ internal static class Constants /// public const string TestTypePropertyIdentifier = "TestType"; + /// + /// Parent data driven result type. + /// + public const string ParentDataDrivenResultType = "DataDrivenTest"; + + /// + /// Inner data driven result type. + /// + public const string InnerDataDrivenResultType = "DataDrivenDataRow"; + /// /// Property Id storing the TMITestId. /// public const string TmiTestIdPropertyIdentifier = "MSTestDiscoverer.TmiTestId"; /// - /// Ordered test type + /// Ordered test type guid /// - public static readonly Guid OrderedTestType = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); + public static readonly Guid OrderedTestTypeGuid = new Guid("ec4800e8-40e5-4ab3-8510-b8bf29b1904d"); /// - /// Ordered test type instance + /// Ordered test type /// - public static readonly TestType OrderedTestTypeInstance = new TestType(OrderedTestType); + public static readonly TestType OrderedTestType = new TestType(OrderedTestTypeGuid); /// - /// Unit test type + /// Unit test type guid /// - public static readonly Guid UnitTestType = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); + public static readonly Guid UnitTestTypeGuid = new Guid("13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"); /// - /// Unit test type instance. + /// Unit test type /// - public static readonly TestType UnitTestTypeInstance = new TestType(UnitTestType); + public static readonly TestType UnitTestType = new TestType(UnitTestTypeGuid); } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs index a9a331982d..d2342e2a24 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs @@ -11,11 +11,9 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility using System.IO; using System.Linq; using System.Text; - using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using ObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel; - using TrxLoggerResources = Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.Resources.TrxResource; using TrxObjectModel = Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; @@ -24,85 +22,92 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility /// internal class Converter { - internal static TrxObjectModel.TestElement ToTestElement(Guid testType, string name, Guid executionId, Guid parentExecutionId, ObjectModel.TestCase rockSteadyTestCase) + /// + /// Converts platform test case to trx test element. + /// + /// + /// + /// + /// + /// + /// + /// Trx test element + public static ITestElement ToTestElement( + Guid testId, + Guid executionId, + Guid parentExecutionId, + TestType testType, + ObjectModel.TestCase rockSteadyTestCase) { - //ObjectModel.TestCase rockSteadyTestCase = rockSteadyTestResult.TestCase; - - Guid id = GetTestId(rockSteadyTestCase); - //string name = !string.IsNullOrEmpty(rockSteadyTestCase.DisplayName) ? rockSteadyTestCase.DisplayName : rockSteadyTestResult.DisplayName;// TODO: in case testDisplayName is null, assign the value from rsTestResult.DisplayName. Dont do if its not required. + var testElement = CreateTestElement(testId, rockSteadyTestCase.DisplayName, rockSteadyTestCase.FullyQualifiedName, rockSteadyTestCase.ExecutorUri.ToString(), rockSteadyTestCase.Source, testType); - // If it is an inner test case name - //if (!string.IsNullOrEmpty(rockSteadyTestResult.DisplayName)) - //{ - // testId = Guid.NewGuid(); // Changing of guid is done so that VS can load trx otherwise it fails with duplicate id error. - // name = rockSteadyTestResult.DisplayName; - //} + testElement.Storage = rockSteadyTestCase.Source; + testElement.Priority = GetPriority(rockSteadyTestCase); + testElement.Owner = GetOwner(rockSteadyTestCase); + testElement.ExecutionId = new TestExecId(executionId); + testElement.ParentExecutionId = new TestExecId(parentExecutionId); - string adapter = rockSteadyTestCase.ExecutorUri.ToString(); - - //ObjectModel.TestProperty executionIdProperty = rockSteadyTestResult.Properties.FirstOrDefault(property => property.Id.Equals(ExecutionIdPropertyIdentifier));// TODO: should we pass executionid and parentExecutionId from trxlogger itself? - //var executionId = Guid.Empty; - //if (executionIdProperty != null) - //{ - // executionId = rockSteadyTestResult.GetPropertyValue(executionIdProperty, Guid.NewGuid()); - //} - //executionId = executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; + var testCategories = GetCustomPropertyValueFromTestCase(rockSteadyTestCase, "MSTestDiscoverer.TestCategory"); + foreach (string testCategory in testCategories) + { + testElement.TestCategories.Add(testCategory); + } - //ObjectModel.TestProperty parentExecutionIdProperty = rockSteadyTestResult.Properties.FirstOrDefault(property => property.Id.Equals(ParentExecutionIdPropertyIdentifier)); - //var parentExecutionId = (parentExecutionIdProperty == null) ? Guid.Empty : rockSteadyTestResult.GetPropertyValue(parentExecutionIdProperty, default(Guid)); + return testElement; + } - var storage = rockSteadyTestCase.Source; + /// + /// Converts the rockSteady result to unit test result + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Trx test result object + public static ITestResult ToTestResult( + Guid testId, + Guid executionId, + Guid parentExecutionId, + string testName, + string trxFileDirectory, + TestType testType, + TestListCategoryId testCategoryId, + TrxObjectModel.TestOutcome testOutcome, + TestRun testRun, + ObjectModel.TestResult rockSteadyTestResult) + { + var resultName = !string.IsNullOrEmpty(rockSteadyTestResult.DisplayName) ? rockSteadyTestResult.DisplayName : testName; + var testResult = CreateTestResult(testRun.Id, testId, executionId, parentExecutionId, resultName, Environment.MachineName, testOutcome, testType, testCategoryId); - int priority = int.MaxValue;// TODO: better way is to create a method and pass testElement to it. - string owner = null; - if (rockSteadyTestCase.Traits != null) - { - ObjectModel.Trait priorityTrait = rockSteadyTestCase.Traits.FirstOrDefault(t => t.Name.Equals("Priority")); - if (priorityTrait != null) - { - int priorityValue; - if (Int32.TryParse(priorityTrait.Value, out priorityValue)) - { - priority = priorityValue; - } - } + if (rockSteadyTestResult.ErrorMessage != null) + testResult.ErrorMessage = rockSteadyTestResult.ErrorMessage; - ObjectModel.Trait ownerTrait = rockSteadyTestCase.Traits.FirstOrDefault(t => t.Name.Equals("Owner")); - if (ownerTrait != null) - { - owner = ownerTrait.Value; - } - } - var testCategories = GetCustomPropertyValueFromTestCase(rockSteadyTestCase, "MSTestDiscoverer.TestCategory"); + if (rockSteadyTestResult.ErrorStackTrace != null) + testResult.ErrorStackTrace = rockSteadyTestResult.ErrorStackTrace; - TestElement testElement = null; - if (testType == Constants.OrderedTestType) - { - // todo: change name to storage without file extension - testElement = new TrxObjectModel.OrderedTestElement(id, name, adapter); // todo: create toorderestestelement and put if else content in it. - } - else - { - var codeBase = rockSteadyTestCase.Source; - var testMethod = GetTestMethod(name, rockSteadyTestCase.FullyQualifiedName); - testElement = new TrxObjectModel.UnitTestElement(id, name, adapter, testMethod); + if (rockSteadyTestResult.EndTime != null) + testResult.EndTime = rockSteadyTestResult.EndTime.UtcDateTime; - (testElement as TrxObjectModel.UnitTestElement).CodeBase = codeBase; - }// TODO: factory pattern rqd? + if (rockSteadyTestResult.StartTime != null) + testResult.StartTime = rockSteadyTestResult.StartTime.UtcDateTime; - testElement.Storage = storage; - testElement.Priority = priority; - if (owner != null) testElement.Owner = owner; + if (rockSteadyTestResult.Duration != null) + testResult.Duration = rockSteadyTestResult.Duration; - testElement.ExecutionId = new TrxObjectModel.TestExecId(executionId); - testElement.ParentExecutionId = new TrxObjectModel.TestExecId(parentExecutionId); + // Clear exsting messages and store rocksteady result messages. + testResult.TextMessages = null; + UpdateResultMessages(testResult, rockSteadyTestResult); - foreach (string testCategory in testCategories) - { - testElement.TestCategories.Add(testCategory); - } + // Save result attachments to target location. + UpdateTestResultAttachments(rockSteadyTestResult, testResult, testRun, trxFileDirectory, true); - return testElement; + return testResult; } /// @@ -115,7 +120,7 @@ internal static TrxObjectModel.TestElement ToTestElement(Guid testType, string n /// The . /// [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")] - internal static TrxObjectModel.TestOutcome ToOutcome(ObjectModel.TestOutcome rockSteadyOutcome) + public static TrxObjectModel.TestOutcome ToOutcome(ObjectModel.TestOutcome rockSteadyOutcome) { TrxObjectModel.TestOutcome outcome = TrxObjectModel.TestOutcome.Failed; @@ -140,37 +145,7 @@ internal static TrxObjectModel.TestOutcome ToOutcome(ObjectModel.TestOutcome roc return outcome; } - /// - /// Converts the rockSteady result to unit test result - /// - /// rock steady test result - /// testElement of that test - /// Test outcome - /// test run object - /// TRX file directory - /// TestResult object - internal static TrxObjectModel.TestResult ToTestResult( - ObjectModel.TestResult rockSteadyTestResult, - Guid executionId, - Guid parentExecutionId, - TrxObjectModel.ITestElement testElement, - TrxObjectModel.TestOutcome testOutcome, - TrxObjectModel.TestRun testRun, - string trxFileDirectory) - { - TrxObjectModel.TestResult qtoolsResult = GetQToolsTestResultFromTestResult(rockSteadyTestResult, executionId, parentExecutionId, testElement, testOutcome, testRun); - - // Clear exsting messages and store rocksteady result messages. - qtoolsResult.TextMessages = null; - UpdateResultMessages(qtoolsResult, rockSteadyTestResult); - - // Save result attachments to target location. - UpdateTestResultAttachments(rockSteadyTestResult, qtoolsResult, testRun, trxFileDirectory, true); - - return qtoolsResult; - } - - internal static List ToCollectionEntries(IEnumerable attachmentSets, TestRun testRun, string trxFileDirectory) + public static List ToCollectionEntries(IEnumerable attachmentSets, TestRun testRun, string trxFileDirectory) { List collectorEntries = new List(); if (attachmentSets == null) @@ -190,7 +165,7 @@ internal static List ToCollectionEntries(IEnumerable ToResultFiles(IEnumerable attachmentSets, TestRun testRun, string trxFileDirectory, List errorMessages) + public static IList ToResultFiles(IEnumerable attachmentSets, TestRun testRun, string trxFileDirectory, List errorMessages) { List resultFiles = new List(); if (attachmentSets == null) @@ -222,69 +197,6 @@ internal static IList ToResultFiles(IEnumerable - /// Returns the QToolsCommon.TestResult object created from rockSteady TestResult. - /// - /// rock steady test result - /// testElement of that test - /// Test outcome - /// test run object - /// TestResult object - private static TrxObjectModel.TestResult GetQToolsTestResultFromTestResult( - ObjectModel.TestResult rockSteadyTestResult, - Guid executionId, - Guid parentExecutionId, - TrxObjectModel.ITestElement testElement, - TrxObjectModel.TestOutcome testOutcome, - TrxObjectModel.TestRun testRun) - { - // TODO: change here. - TestResult testResult; - var testName = !string.IsNullOrEmpty(rockSteadyTestResult.DisplayName) ? rockSteadyTestResult.DisplayName : testElement.Name; - if (testElement is OrderedTestElement) - { - testResult = new TestResultAggregation( - testName, - Environment.MachineName, - testRun.Id, - executionId, - parentExecutionId, - testElement, - testOutcome); - } - else - { - testResult = new UnitTestResult(testName, Environment.MachineName, testRun.Id, executionId, parentExecutionId, testElement, testOutcome); - } - - if (rockSteadyTestResult.ErrorMessage != null) - { - testResult.ErrorMessage = rockSteadyTestResult.ErrorMessage; - } - - if (rockSteadyTestResult.ErrorStackTrace != null) - { - testResult.ErrorStackTrace = rockSteadyTestResult.ErrorStackTrace; - } - - // set start and end times - if (rockSteadyTestResult.EndTime != null) - { - testResult.EndTime = rockSteadyTestResult.EndTime.UtcDateTime; - } - if (rockSteadyTestResult.StartTime != null) - { - testResult.StartTime = rockSteadyTestResult.StartTime.UtcDateTime; - } - - if (rockSteadyTestResult.Duration != null) - { - testResult.Duration = rockSteadyTestResult.Duration; - } - - return testResult; - } - /// /// Copies the result messages to unitTestResult /// @@ -331,7 +243,7 @@ private static void UpdateResultMessages(TrxObjectModel.TestResult unitTestResul /// TestCase object extracted from the TestResult /// Property Name from the list of properties in TestCase /// list of properties - internal static List GetCustomPropertyValueFromTestCase(ObjectModel.TestCase testCase, string categoryID) + public static List GetCustomPropertyValueFromTestCase(ObjectModel.TestCase testCase, string categoryID) { var customProperty = testCase.Properties.FirstOrDefault(t => t.Id.Equals(categoryID)); @@ -352,24 +264,23 @@ internal static List GetCustomPropertyValueFromTestCase(ObjectModel.Test } /// - /// Return TMI Test id when available for TestPlatform TestCase. + /// Gets test id. + /// Return TMI Test id when available for TestPlatform test case. /// - /// - /// The rock Steady Test Case. - /// - /// - /// The . - /// + /// + /// Test id public static Guid GetTestId(ObjectModel.TestCase rockSteadyTestCase) { Guid testId = Guid.Empty; // Setting test id to tmi test id. - ObjectModel.TestProperty tmiTestIdProperty = rockSteadyTestCase.Properties.FirstOrDefault(property => property.Id.Equals(Constants.TmiTestIdPropertyIdentifier)); + ObjectModel.TestProperty tmiTestIdProperty = rockSteadyTestCase.Properties.FirstOrDefault( + property => property.Id.Equals(Constants.TmiTestIdPropertyIdentifier)); + if (null != tmiTestIdProperty) testId = rockSteadyTestCase.GetPropertyValue(tmiTestIdProperty, Guid.Empty); - // TOOD: check framework other than net451 that why we were having a #if check earlier. + // If tmi test id not present, picking from platform test id. if (Guid.Empty.Equals(testId)) testId = rockSteadyTestCase.Id; @@ -377,31 +288,65 @@ public static Guid GetTestId(ObjectModel.TestCase rockSteadyTestCase) } /// - /// Returns TestMethod for given testCase name and its class name. + /// Gets parent execution id of test result. /// - /// test case display name - /// rockSteady Test Case - /// The - private static TrxObjectModel.TestMethod GetTestMethod(string testDisplayName, string testCaseName) + /// + /// Parent execution id. + public static Guid GetParentExecutionId(ObjectModel.TestResult testResult) { - string className = "DefaultClassName"; - //string testCaseName = rockSteadyTestCase.FullyQualifiedName; - if (testCaseName.Contains(".")) - { - className = testCaseName.Substring(0, testCaseName.LastIndexOf('.')); - } - else if (testCaseName.Contains("::")) - { - // if this is a C++ test case then we would have a "::" instaed of a '.' - className = testCaseName.Substring(0, testCaseName.LastIndexOf("::")); + TestProperty parentExecutionIdProperty = testResult.Properties.FirstOrDefault( + property => property.Id.Equals(Constants.ParentExecutionIdPropertyIdentifier)); - // rename for a consistent behaviour for all tests. - className = className.Replace("::", "."); - } + return parentExecutionIdProperty == null ? + Guid.Empty : + testResult.GetPropertyValue(parentExecutionIdProperty, Guid.Empty); + } + + /// + /// Gets execution Id of test result. Creates new id if not present in test result properties. + /// + /// + /// Execution id. + public static Guid GetExecutionId(ObjectModel.TestResult testResult) + { + TestProperty executionIdProperty = testResult.Properties.FirstOrDefault( + property => property.Id.Equals(Constants.ExecutionIdPropertyIdentifier)); + + var executionId = Guid.Empty; + if (executionIdProperty != null) + executionId = testResult.GetPropertyValue(executionIdProperty, Guid.Empty); + + return executionId.Equals(Guid.Empty) ? Guid.NewGuid() : executionId; + } + + /// + /// Gets test type of test result. + /// Currently trx supports ordered test and unit test. All tests except ordered test are modified as unit test type. + /// + /// + /// Test type + public static TestType GetTestType(ObjectModel.TestResult testResult) + { + var testTypeGuid = Constants.UnitTestTypeGuid; + + // Get test type from property. default to unit test type. + TestProperty testTypeProperty = testResult.Properties.FirstOrDefault(property => property.Id.Equals(Constants.TestTypePropertyIdentifier)); + testTypeGuid = (testTypeProperty == null) ? testTypeGuid : testResult.GetPropertyValue(testTypeProperty, testTypeGuid); - return new TrxObjectModel.TestMethod(testDisplayName, className); + // Currently trx supports ordered test and unit test. All tests except ordered test are modified as unit test type. + return (testTypeGuid.Equals(Constants.OrderedTestTypeGuid)) ? + Constants.OrderedTestType : + Constants.UnitTestType; } + /// + /// Updates test result attachments. + /// + /// + /// + /// + /// + /// private static void UpdateTestResultAttachments(ObjectModel.TestResult rockSteadyTestResult, TrxObjectModel.TestResult testResult, TestRun testRun, string trxFileDirectory, bool addAttachments) { if (rockSteadyTestResult.Attachments == null || rockSteadyTestResult.Attachments.Count == 0) @@ -503,7 +448,7 @@ private static CollectorDataEntry ToCollectorEntry(ObjectModel.AttachmentSet att // (Trx viewer automatically adds In\ to the collected file. string fileName = Path.Combine(Environment.MachineName, Path.GetFileName(sourceFile)); Uri sourceFileUri = new Uri(fileName, UriKind.Relative); - UriDataAttachment dataAttachment = new UriDataAttachment(uriDataAttachment.Description, sourceFileUri); + TrxObjectModel.UriDataAttachment dataAttachment = new TrxObjectModel.UriDataAttachment(uriDataAttachment.Description, sourceFileUri); uriDataAttachments.Add(dataAttachment); } @@ -575,5 +520,124 @@ private static void CopyFile(string sourceFile, string targetFile) throw; } } + + /// + /// Gets priority of test. + /// + /// + /// Priority + private static int GetPriority(ObjectModel.TestCase rockSteadyTestCase) + { + int priority = int.MaxValue; + + ObjectModel.Trait priorityTrait = rockSteadyTestCase.Traits?.FirstOrDefault(t => t.Name.Equals("Priority")); + if (priorityTrait != null && Int32.TryParse(priorityTrait.Value, out int priorityValue)) + priority = priorityValue; + + return priority; + } + + /// + /// Gets owner of test. + /// + /// + /// Owner + private static string GetOwner(ObjectModel.TestCase rockSteadyTestCase) + { + string owner = null; + + ObjectModel.Trait ownerTrait = rockSteadyTestCase.Traits?.FirstOrDefault(t => t.Name.Equals("Owner")); + if (ownerTrait != null) + owner = ownerTrait.Value; + + return owner ?? string.Empty; + } + + /// + /// Gets TestMethod for given testCase name and its class name. + /// + /// test case display name + /// rockSteady Test Case + /// The + private static TestMethod GetTestMethod(string testDisplayName, string testCaseName) + { + string className = "DefaultClassName"; + if (testCaseName.Contains(".")) + { + className = testCaseName.Substring(0, testCaseName.LastIndexOf('.')); + } + else if (testCaseName.Contains("::")) + { + // if this is a C++ test case then we would have a "::" instaed of a '.' + className = testCaseName.Substring(0, testCaseName.LastIndexOf("::")); + + // rename for a consistent behaviour for all tests. + className = className.Replace("::", "."); + } + + return new TestMethod(testDisplayName, className); + } + + /// + /// Create test element. + /// Currently trx supports only UnitTest and OrderedTest. All tests except OrderedTest all converted to UnitTest. + /// + /// + /// + /// + /// + /// + /// + /// Trx test element + private static TestElement CreateTestElement(Guid testId, string name, string fullyQualifiedName, string adapter, string source, TestType testType) + { + TestElement testElement = null; + + if (testType.Equals(Constants.OrderedTestType)) + { + testElement = new OrderedTestElement(testId, name, adapter); + } + else + { + var codeBase = source; + var testMethod = GetTestMethod(name, fullyQualifiedName); + + testElement = new UnitTestElement(testId, name, adapter, testMethod); + (testElement as UnitTestElement).CodeBase = codeBase; + } + + return testElement; + } + + /// + /// Create test result. + /// Currently trx supports only UnitTest and OrderedTest. All tests except OrderedTest all converted to unit test result. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Trx test result + private static TrxObjectModel.TestResult CreateTestResult( + Guid runId, + Guid testId, + Guid executionId, + Guid parentExecutionId, + string resultName, + string computerName, + TrxObjectModel.TestOutcome outcome, + TestType testType, + TestListCategoryId testCategoryId) + { + return testType.Equals(Constants.OrderedTestType) ? + new TestResultAggregation(runId, testId, executionId, parentExecutionId, resultName, Environment.MachineName, outcome, testType, testCategoryId) : + new UnitTestResult(runId, testId, executionId, parentExecutionId, resultName, Environment.MachineName, outcome, testType, testCategoryId); + } + } } From 8851e471961cddcb58fa58510c6decc083c384da Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 16:01:04 +0530 Subject: [PATCH 12/19] refactoring --- src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index fb602c3387..846a2b2fcf 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -493,10 +493,10 @@ private ITestResult GetTestResult(Guid executionId) if (executionId != Guid.Empty) { - this.results.TryGetValue(executionId, out testResult); // todo: handle guid empty case + this.results.TryGetValue(executionId, out testResult); if (testResult == null) - this.additionalResults.TryGetValue(executionId, out testResult); // change this additional results name + this.additionalResults.TryGetValue(executionId, out testResult); // todo: change this additional results name } return testResult; From a565b9ec04cf6f9a05fd1fdd0e75709e210a1a65 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 14 Dec 2017 16:08:38 +0530 Subject: [PATCH 13/19] refactoring --- .../TrxLogger.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 846a2b2fcf..68decab84f 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -38,10 +38,10 @@ internal class TrxLogger : ITestLoggerWithParameters private TrxLoggerObjectModel.TestRun testRun; private Dictionary results; - private Dictionary additionalResults; + private Dictionary innerResults; private Dictionary testElements; private Dictionary entries; - private Dictionary additionalTestEntries; + private Dictionary innerTestEntries; /// /// Specifies the run level "out" messages @@ -392,10 +392,10 @@ internal virtual void PopulateTrxFile(string trxFileName, XmlElement rootElement private void InitializeInternal() { this.results = new Dictionary(); - this.additionalResults = new Dictionary(); + this.innerResults = new Dictionary(); this.testElements = new Dictionary(); this.entries = new Dictionary(); - this.additionalTestEntries = new Dictionary(); + this.innerTestEntries = new Dictionary(); this.runLevelErrorsAndWarnings = new List(); this.testRun = null; this.totalTests = 0; @@ -496,7 +496,7 @@ private ITestResult GetTestResult(Guid executionId) this.results.TryGetValue(executionId, out testResult); if (testResult == null) - this.additionalResults.TryGetValue(executionId, out testResult); // todo: change this additional results name + this.innerResults.TryGetValue(executionId, out testResult); } return testResult; @@ -585,7 +585,7 @@ private ITestResult CreateTestResult(Guid executionId, Guid parentExecutionId, T if (parentTestElement != null && parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) { (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); - this.additionalResults.Add(executionId, testResult); + this.innerResults.Add(executionId, testResult); return testResult; } @@ -622,17 +622,34 @@ private void UpdateTestEntries(Guid executionId, Guid parentExecutionId, ITestEl { te.ParentExecutionId = parentExecutionId; - this.entries.TryGetValue(parentExecutionId, out var parentTestEntry); - if (parentTestEntry == null) - this.additionalTestEntries.TryGetValue(parentExecutionId, out parentTestEntry); - + var parentTestEntry = GetTestEntry(parentExecutionId); if (parentTestEntry != null) parentTestEntry.TestEntries.Add(te); - this.additionalTestEntries.Add(executionId, te); + this.innerTestEntries.Add(executionId, te); } } + /// + /// Gets test entry from stored test entries. + /// + /// + /// Test entry + private TestEntry GetTestEntry(Guid executionId) + { + TestEntry testEntry = null; + + if (executionId != Guid.Empty) + { + this.entries.TryGetValue(executionId, out testEntry); + + if (testEntry == null) + this.innerTestEntries.TryGetValue(executionId, out testEntry); + } + + return testEntry; + } + #endregion } } \ No newline at end of file From 59d4035b68f24a48da58a936ffcc8ace7c9eaf61 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Wed, 20 Dec 2017 20:55:07 +0530 Subject: [PATCH 14/19] remove debugger --- src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 68decab84f..f452b2a82c 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -184,7 +184,6 @@ internal TrxLoggerObjectModel.TestOutcome TestResultOutcome /// public void TestMessageHandler(object sender, TestRunMessageEventArgs e) { - System.Diagnostics.Debugger.Launch(); ValidateArg.NotNull(sender, "sender"); ValidateArg.NotNull(e, "e"); From 7c3884742948396a453085e37921a914808f75c5 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 4 Jan 2018 01:41:50 +0530 Subject: [PATCH 15/19] Review comments --- .../Interfaces/ITestAggregation.cs | 6 +++--- .../Interfaces/ITestResult.cs | 5 +++-- .../Interfaces/ITestResultAggregation.cs | 4 ++-- .../ObjectModel/OrderedTestElement.cs | 2 +- ...tAggregation.cs => TestElementAggregation.cs} | 6 ++++-- .../ObjectModel/TestEntry.cs | 16 ++++++++++++---- .../ObjectModel/TestResultAggregation.cs | 15 +++++++++++---- .../TrxLogger.cs | 9 ++++++--- .../Utility/Constants.cs | 8 ++++---- 9 files changed, 46 insertions(+), 25 deletions(-) rename src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/{TestAggregation.cs => TestElementAggregation.cs} (83%) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs index d34c3a7ebb..8a5b372067 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestAggregation.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; - namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { + using System; + using System.Collections.Generic; + internal interface ITestAggregation : ITestElement { Dictionary TestLinks { get; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs index 7834016331..6835dc5fc1 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResult.cs @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { + + using System; + internal interface ITestResult { TestResultId Id { get; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs index 33d3b0474e..7ee85e3e4c 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Interfaces/ITestResultAggregation.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Generic; - namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel { + using System.Collections.Generic; + internal interface ITestResultAggregation : ITestResult { List InnerResults { get; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs index d3244f26db..907b14bc6d 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/OrderedTestElement.cs @@ -10,7 +10,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Ordered test element. /// - internal class OrderedTestElement : TestAggregation, IXmlTestStoreCustom + internal class OrderedTestElement : TestElementAggregation, IXmlTestStoreCustom { public OrderedTestElement(Guid id, string name, string adapter) : base(id, name, adapter) { } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElementAggregation.cs similarity index 83% rename from src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs rename to src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElementAggregation.cs index 930366dd10..d814acaee6 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElementAggregation.cs @@ -10,11 +10,11 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// /// Test aggregation element. /// - internal abstract class TestAggregation : TestElement, ITestAggregation + internal abstract class TestElementAggregation : TestElement, ITestAggregation { protected Dictionary testLinks = new Dictionary(); - public TestAggregation(Guid id, string name, string adapter) : base(id, name, adapter) { } + public TestElementAggregation(Guid id, string name, string adapter) : base(id, name, adapter) { } /// /// Test links. @@ -30,7 +30,9 @@ public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters XmlPersistence h = new XmlPersistence(); if (testLinks.Count > 0) + { h.SaveIEnumerable(testLinks.Values, element, "TestLinks", ".", "TestLink", parameters); + } } } } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs index daee7b3737..294f50ad58 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestEntry.cs @@ -20,7 +20,7 @@ internal sealed class TestEntry : IXmlTestStore private Guid executionId; private Guid parentExecutionId; private TestListCategoryId categoryId; - private List testEntries = new List(); + private List testEntries; #endregion @@ -76,7 +76,15 @@ public Guid ParentExecutionId public List TestEntries { - get { return this.testEntries; } + get + { + if (this.testEntries == null) + { + this.testEntries = new List(); + } + + return this.testEntries; + } } #endregion @@ -149,8 +157,8 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter if (parentExecutionId != null) helper.SaveGuid(element, "@parentExecutionId", this.parentExecutionId); helper.SaveGuid(element, "@testListId", this.categoryId.Id); - if (testEntries.Count > 0) - helper.SaveIEnumerable(testEntries, element, "TestEntries", ".", "TestEntry", parameters); + if (this.TestEntries.Count > 0) + helper.SaveIEnumerable(TestEntries, element, "TestEntries", ".", "TestEntry", parameters); } #endregion diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs index 3a7a3945eb..1561fa287e 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResultAggregation.cs @@ -12,7 +12,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel /// internal class TestResultAggregation : TestResult, ITestResultAggregation { - protected List innerResults = new List(); + protected List innerResults; public TestResultAggregation( Guid runId, @@ -30,7 +30,14 @@ public TestResultAggregation( /// public List InnerResults { - get { return innerResults; } + get + { + if (innerResults == null) + { + innerResults = new List(); + } + return innerResults; + } } public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) @@ -38,8 +45,8 @@ public override void Save(System.Xml.XmlElement element, XmlTestStoreParameters base.Save(element, parameters); XmlPersistence helper = new XmlPersistence(); - if (innerResults.Count > 0) - helper.SaveIEnumerable(innerResults, element, "InnerResults", ".", null, parameters); + if (this.InnerResults.Count > 0) + helper.SaveIEnumerable(this.InnerResults, element, "InnerResults", ".", null, parameters); } } } \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index f452b2a82c..148174c757 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -38,9 +38,11 @@ internal class TrxLogger : ITestLoggerWithParameters private TrxLoggerObjectModel.TestRun testRun; private Dictionary results; - private Dictionary innerResults; private Dictionary testElements; private Dictionary entries; + + // Caching results and inner test entries for constant time lookup for inner parents. + private Dictionary innerResults; private Dictionary innerTestEntries; /// @@ -234,6 +236,8 @@ public void TestResultHandler(object sender, ObjectModel.Logging.TestResultEvent var parentExecutionId = Converter.GetParentExecutionId(e.Result); var parentTestResult = GetTestResult(parentExecutionId); var parentTestElement = (parentTestResult != null) ? GetTestElement(parentTestResult.Id.TestId) : null; + + // Switch to flat test results in case any parent related information is missing. if (parentTestResult == null || parentTestElement == null || parentExecutionId == Guid.Empty) { parentTestResult = null; @@ -244,7 +248,7 @@ public void TestResultHandler(object sender, ObjectModel.Logging.TestResultEvent // Create trx test element from rocksteady test case var testElement = GetOrCreateTestElement(executionId, parentExecutionId, testType, parentTestElement, e.Result.TestCase); - // Update test links + // Update test links. Test Links are updated in case of Ordered test. UpdateTestLinks(testElement, parentTestElement); // Convert the rocksteady result to trx test result @@ -277,7 +281,6 @@ public void TestResultHandler(object sender, ObjectModel.Logging.TestResultEvent /// public void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) { - System.Diagnostics.Debugger.Launch(); if (this.testRun != null) { XmlPersistence helper = new XmlPersistence(); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index 94343f6766..0021d5329e 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; - namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility -{ +{ + using System; + using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; + internal static class Constants { /// From e738cca98178b14191d14556f56b646e8b120cc4 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 4 Jan 2018 03:33:57 +0530 Subject: [PATCH 16/19] Unit tests --- .../Utility/Constants.cs | 11 + .../TrxLoggerTests.cs | 224 +++++++++++++++++- 2 files changed, 231 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs index 0021d5329e..420069e373 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs @@ -5,6 +5,7 @@ namespace Microsoft.TestPlatform.Extensions.TrxLogger.Utility { using System; using Microsoft.TestPlatform.Extensions.TrxLogger.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; internal static class Constants { @@ -87,5 +88,15 @@ internal static class Constants /// Unit test type /// public static readonly TestType UnitTestType = new TestType(UnitTestTypeGuid); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdPropertyIdentifier, typeof(Guid), TestPropertyAttributes.Hidden, typeof(ObjectModel.TestResult)); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecutionIdPropertyIdentifier, typeof(Guid), TestPropertyAttributes.Hidden, typeof(ObjectModel.TestResult)); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly TestProperty TestTypeProperty = TestProperty.Register("TestType", TestTypePropertyIdentifier, typeof(Guid), TestPropertyAttributes.Hidden, typeof(ObjectModel.TestResult)); + } } diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs index a3a251b1e3..6015dec4c6 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs @@ -288,6 +288,222 @@ public void TestResultHandlerShouldCreateOneUnitTestElementForEachTestCase() Assert.AreEqual(this.testableTrxLogger.UnitTestElementCount, 2, "TestResultHandler is not creating test result entry for each test case"); } + [TestMethod] + public void TestResultHandlerShouldAddFlatResultsIfParentTestResultIsNotPresent() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result1.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase1); + result2.Outcome = ObjectModel.TestOutcome.Failed; + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + + Assert.AreEqual(this.testableTrxLogger.TestResultCount, 2, "TestResultHandler is not creating flat results when parent result is not present."); + } + + [TestMethod] + public void TestResultHandlerShouldAddHierarchicalResultsIfParentTestResultIsPresent() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase1); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase1); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.TestResultCount, 1, "TestResultHandler is not creating hierarchical results when parent result is present."); + Assert.AreEqual(this.testableTrxLogger.TotalTestCount, 3, "TestResultHandler is not adding all inner results in parent test result."); + } + + [TestMethod] + public void TestResultHandlerShouldAddSingleTestElementForDataDrivenTests() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase1); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase1); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.UnitTestElementCount, 1, "TestResultHandler is adding multiple test elements for data driven tests."); + } + + [TestMethod] + public void TestResultHandlerShouldAddSingleTestEntryForDataDrivenTests() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase1); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase1); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.TestEntryCount, 1, "TestResultHandler is adding multiple test entries for data driven tests."); + } + + [TestMethod] + public void TestResultHandlerShouldAddHierarchicalResultsForOrderedTest() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + ObjectModel.TestCase testCase2 = CreateTestCase("TestCase2"); + ObjectModel.TestCase testCase3 = CreateTestCase("TestCase3"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + result1.SetPropertyValue(TrxLoggerConstants.TestTypeProperty, TrxLoggerConstants.OrderedTestTypeGuid); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase2); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase3); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.TestResultCount, 1, "TestResultHandler is not creating hierarchical results for ordered test."); + Assert.AreEqual(this.testableTrxLogger.TotalTestCount, 3, "TestResultHandler is not adding all inner results in ordered test."); + } + + [TestMethod] + public void TestResultHandlerShouldAddMultipleTestElementsForOrderedTest() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + ObjectModel.TestCase testCase2 = CreateTestCase("TestCase2"); + ObjectModel.TestCase testCase3 = CreateTestCase("TestCase3"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + result1.SetPropertyValue(TrxLoggerConstants.TestTypeProperty, TrxLoggerConstants.OrderedTestTypeGuid); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase2); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase3); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.UnitTestElementCount, 3, "TestResultHandler is not adding multiple test elements for ordered test."); + } + + [TestMethod] + public void TestResultHandlerShouldAddSingleTestEntryForOrderedTest() + { + ObjectModel.TestCase testCase1 = CreateTestCase("TestCase1"); + ObjectModel.TestCase testCase2 = CreateTestCase("TestCase2"); + ObjectModel.TestCase testCase3 = CreateTestCase("TestCase3"); + + Guid parentExecutionId = Guid.NewGuid(); + + ObjectModel.TestResult result1 = new ObjectModel.TestResult(testCase1); + result1.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, parentExecutionId); + result1.SetPropertyValue(TrxLoggerConstants.TestTypeProperty, TrxLoggerConstants.OrderedTestTypeGuid); + + ObjectModel.TestResult result2 = new ObjectModel.TestResult(testCase2); + result2.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result2.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + ObjectModel.TestResult result3 = new ObjectModel.TestResult(testCase3); + result3.Outcome = ObjectModel.TestOutcome.Failed; + result3.SetPropertyValue(TrxLoggerConstants.ExecutionIdProperty, Guid.NewGuid()); + result3.SetPropertyValue(TrxLoggerConstants.ParentExecIdProperty, parentExecutionId); + + Mock resultEventArg1 = new Mock(result1); + Mock resultEventArg2 = new Mock(result2); + Mock resultEventArg3 = new Mock(result3); + + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg2.Object); + this.testableTrxLogger.TestResultHandler(new object(), resultEventArg3.Object); + + Assert.AreEqual(this.testableTrxLogger.TestEntryCount, 1, "TestResultHandler is adding multiple test entries for ordered test."); + } + [TestMethod] public void OutcomeOfRunWillBeFailIfAnyTestsFails() { @@ -413,7 +629,7 @@ public void GetCustomPropertyValueFromTestCaseShouldReadCategoyrAttributesFromTe /// Unit test for assigning or populating test categories read to the unit test element. /// [TestMethod] - public void GetQToolsTestElementFromTestCaseShouldAssignTestCategoryOfUnitTestElement() + public void ToTestElementShouldAssignTestCategoryOfUnitTestElement() { ObjectModel.TestCase testCase = CreateTestCase("TestCase1"); ObjectModel.TestResult result = new ObjectModel.TestResult(testCase); @@ -421,7 +637,7 @@ public void GetQToolsTestElementFromTestCaseShouldAssignTestCategoryOfUnitTestEl testCase.SetPropertyValue(testProperty, new[] { "AsmLevel", "ClassLevel", "MethodLevel" }); - TrxLoggerObjectModel.UnitTestElement unitTestElement = null; // Converter.GetQToolsTestElementFromTestCase(result); + var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, TrxLoggerConstants.UnitTestType, testCase); object[] expected = new[] { "MethodLevel", "ClassLevel", "AsmLevel" }; @@ -432,12 +648,12 @@ public void GetQToolsTestElementFromTestCaseShouldAssignTestCategoryOfUnitTestEl /// Unit test for regression when there's no test categories. /// [TestMethod] - public void GetQToolsTestElementFromTestCaseShouldNotFailWhenThereIsNoTestCategoreis() + public void ToTestElementShouldNotFailWhenThereIsNoTestCategoreis() { ObjectModel.TestCase testCase = CreateTestCase("TestCase1"); ObjectModel.TestResult result = new ObjectModel.TestResult(testCase); - TrxLoggerObjectModel.UnitTestElement unitTestElement = null; // Converter.GetQToolsTestElementFromTestCase(result); + var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, TrxLoggerConstants.UnitTestType, testCase); object[] expected = Enumerable.Empty().ToArray(); From 373c938916c256d6c9099ddf4fb07260496a6c86 Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Thu, 4 Jan 2018 03:43:44 +0530 Subject: [PATCH 17/19] concurrent hash map --- .../TrxLogger.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index 148174c757..d014b2fd1d 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -4,6 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger { using System; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -37,13 +38,13 @@ internal class TrxLogger : ITestLoggerWithParameters private string trxFilePath; private TrxLoggerObjectModel.TestRun testRun; - private Dictionary results; - private Dictionary testElements; - private Dictionary entries; + private ConcurrentDictionary results; + private ConcurrentDictionary testElements; + private ConcurrentDictionary entries; // Caching results and inner test entries for constant time lookup for inner parents. - private Dictionary innerResults; - private Dictionary innerTestEntries; + private ConcurrentDictionary innerResults; + private ConcurrentDictionary innerTestEntries; /// /// Specifies the run level "out" messages @@ -393,11 +394,11 @@ internal virtual void PopulateTrxFile(string trxFileName, XmlElement rootElement // Initializes trx logger cache. private void InitializeInternal() { - this.results = new Dictionary(); - this.innerResults = new Dictionary(); - this.testElements = new Dictionary(); - this.entries = new Dictionary(); - this.innerTestEntries = new Dictionary(); + this.results = new ConcurrentDictionary(); + this.innerResults = new ConcurrentDictionary(); + this.testElements = new ConcurrentDictionary(); + this.entries = new ConcurrentDictionary(); + this.innerTestEntries = new ConcurrentDictionary(); this.runLevelErrorsAndWarnings = new List(); this.testRun = null; this.totalTests = 0; @@ -535,7 +536,7 @@ private ITestElement GetOrCreateTestElement(Guid executionId, Guid parentExecuti if (testElement == null) { testElement = Converter.ToTestElement(testId, executionId, parentExecutionId, testType, rockSteadyTestCase); - testElements.Add(testId, testElement); + testElements.TryAdd(testId, testElement); } } @@ -579,7 +580,7 @@ private ITestResult CreateTestResult(Guid executionId, Guid parentExecutionId, T // Normal result scenario if (parentTestResult == null) { - this.results.Add(executionId, testResult); + this.results.TryAdd(executionId, testResult); return testResult; } @@ -587,7 +588,7 @@ private ITestResult CreateTestResult(Guid executionId, Guid parentExecutionId, T if (parentTestElement != null && parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) { (parentTestResult as TestResultAggregation).InnerResults.Add(testResult); - this.innerResults.Add(executionId, testResult); + this.innerResults.TryAdd(executionId, testResult); return testResult; } @@ -618,7 +619,7 @@ private void UpdateTestEntries(Guid executionId, Guid parentExecutionId, ITestEl if (parentTestElement == null) { - this.entries.Add(executionId, te); + this.entries.TryAdd(executionId, te); } else if (parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) { @@ -628,7 +629,7 @@ private void UpdateTestEntries(Guid executionId, Guid parentExecutionId, ITestEl if (parentTestEntry != null) parentTestEntry.TestEntries.Add(te); - this.innerTestEntries.Add(executionId, te); + this.innerTestEntries.TryAdd(executionId, te); } } From 6ae9bd5eed16aedc784930c7153e7952ad5a2c2d Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Mon, 8 Jan 2018 11:40:35 +0530 Subject: [PATCH 18/19] Fixes --- .../ObjectModel/TestResult.cs | 10 +++++- .../TrxLogger.cs | 35 +++++++++++++------ .../Utility/Converter.cs | 3 +- .../TrxLoggerTests.cs | 4 +-- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs index 17ac56b034..517fecafb8 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestResult.cs @@ -65,6 +65,14 @@ public Guid ExecutionId get { return this.executionId; } } + /// + /// Gets the parent execution id. + /// + public Guid ParentExecutionId + { + get { return this.parentExecutionId; } + } + /// /// Gets the test id. /// @@ -140,7 +148,7 @@ public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameter if (this.parentExecutionId != null) helper.SaveGuid(element, "@parentExecutionId", this.parentExecutionId); - helper.SaveObject(this.testId, element, null); + helper.SaveGuid(element, "@testId", this.testId); } #endregion diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs index d014b2fd1d..7c96abb56f 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs @@ -247,7 +247,7 @@ public void TestResultHandler(object sender, ObjectModel.Logging.TestResultEvent } // Create trx test element from rocksteady test case - var testElement = GetOrCreateTestElement(executionId, parentExecutionId, testType, parentTestElement, e.Result.TestCase); + var testElement = GetOrCreateTestElement(executionId, parentExecutionId, testType, parentTestElement, e.Result); // Update test links. Test Links are updated in case of Ordered test. UpdateTestLinks(testElement, parentTestElement); @@ -525,19 +525,34 @@ private ITestElement GetTestElement(Guid testId) /// /// /// Trx test element - private ITestElement GetOrCreateTestElement(Guid executionId, Guid parentExecutionId, TestType testType, ITestElement parentTestElement, ObjectModel.TestCase rockSteadyTestCase) + private ITestElement GetOrCreateTestElement(Guid executionId, Guid parentExecutionId, TestType testType, ITestElement parentTestElement, ObjectModel.TestResult rockSteadyTestResult) { ITestElement testElement = parentTestElement; - if (parentTestElement == null || parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) + + // For scenarios like data driven tests, test element is same as parent test element. + if (parentTestElement != null && !parentTestElement.TestType.Equals(TrxLoggerConstants.OrderedTestType)) { - Guid testId = Converter.GetTestId(rockSteadyTestCase); - testElement = GetTestElement(testId); + return testElement; + } - if (testElement == null) - { - testElement = Converter.ToTestElement(testId, executionId, parentExecutionId, testType, rockSteadyTestCase); - testElements.TryAdd(testId, testElement); - } + Guid testId = Converter.GetTestId(rockSteadyTestResult.TestCase); + + // Scenario for inner test case when parent test element is not present. + var testName = rockSteadyTestResult.TestCase.DisplayName; + if (parentTestElement == null && !string.IsNullOrEmpty(rockSteadyTestResult.DisplayName)) + { + testId = Guid.NewGuid(); + testName = rockSteadyTestResult.DisplayName; + } + + // Get test element + testElement = GetTestElement(testId); + + // Create test element + if (testElement == null) + { + testElement = Converter.ToTestElement(testId, executionId, parentExecutionId, testName, testType, rockSteadyTestResult.TestCase); + testElements.TryAdd(testId, testElement); } return testElement; diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs index d2342e2a24..bcb3f6ad8c 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs @@ -36,10 +36,11 @@ public static ITestElement ToTestElement( Guid testId, Guid executionId, Guid parentExecutionId, + String testName, TestType testType, ObjectModel.TestCase rockSteadyTestCase) { - var testElement = CreateTestElement(testId, rockSteadyTestCase.DisplayName, rockSteadyTestCase.FullyQualifiedName, rockSteadyTestCase.ExecutorUri.ToString(), rockSteadyTestCase.Source, testType); + var testElement = CreateTestElement(testId, testName, rockSteadyTestCase.FullyQualifiedName, rockSteadyTestCase.ExecutorUri.ToString(), rockSteadyTestCase.Source, testType); testElement.Storage = rockSteadyTestCase.Source; testElement.Priority = GetPriority(rockSteadyTestCase); diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs index 6015dec4c6..293e874643 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs @@ -637,7 +637,7 @@ public void ToTestElementShouldAssignTestCategoryOfUnitTestElement() testCase.SetPropertyValue(testProperty, new[] { "AsmLevel", "ClassLevel", "MethodLevel" }); - var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, TrxLoggerConstants.UnitTestType, testCase); + var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, testCase.DisplayName, TrxLoggerConstants.UnitTestType, testCase); object[] expected = new[] { "MethodLevel", "ClassLevel", "AsmLevel" }; @@ -653,7 +653,7 @@ public void ToTestElementShouldNotFailWhenThereIsNoTestCategoreis() ObjectModel.TestCase testCase = CreateTestCase("TestCase1"); ObjectModel.TestResult result = new ObjectModel.TestResult(testCase); - var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, TrxLoggerConstants.UnitTestType, testCase); + var unitTestElement = Converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, testCase.DisplayName, TrxLoggerConstants.UnitTestType, testCase); object[] expected = Enumerable.Empty().ToArray(); From f3da229e3c4aa2fcceedd67339bf9517950a6d9e Mon Sep 17 00:00:00 2001 From: Abhishek Kumawat Date: Wed, 14 Feb 2018 11:30:08 +0530 Subject: [PATCH 19/19] PR comments --- .../ObjectModel/TestLink.cs | 3 +-- .../Utility/Converter.cs | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs index 151c10f73f..b770e30d20 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestLink.cs @@ -22,8 +22,7 @@ public TestLink(Guid id, string name, string storage) { if (id == Guid.Empty) { - Debug.Assert(id != Guid.Empty, "id == Guid.Empty!"); - throw new ArgumentException("ID cant be empty"); // error resource? + throw new ArgumentException("ID cant be empty"); } EqtAssert.StringNotNullOrEmpty(name, "name"); diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs index bcb3f6ad8c..ef531fedf5 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs @@ -560,7 +560,7 @@ private static string GetOwner(ObjectModel.TestCase rockSteadyTestCase) /// test case display name /// rockSteady Test Case /// The - private static TestMethod GetTestMethod(string testDisplayName, string testCaseName) + private static TestMethod GetTestMethod(string testDisplayName, string testCaseName, string source) { string className = "DefaultClassName"; if (testCaseName.Contains(".")) @@ -575,6 +575,22 @@ private static TestMethod GetTestMethod(string testDisplayName, string testCaseN // rename for a consistent behaviour for all tests. className = className.Replace("::", "."); } + else + { + // Setting class name as source name if FQDn doesnt have . or :: [E.g. ordered test, web test] + try + { + string testCaseSource = Path.GetFileNameWithoutExtension(source); + if (!String.IsNullOrEmpty(testCaseSource)) + { + className = testCaseSource; + } + } + catch (ArgumentException) + { + // If source is not valid file path, then className will continue to point default value. + } + } return new TestMethod(testDisplayName, className); } @@ -601,7 +617,7 @@ private static TestElement CreateTestElement(Guid testId, string name, string fu else { var codeBase = source; - var testMethod = GetTestMethod(name, fullyQualifiedName); + var testMethod = GetTestMethod(name, fullyQualifiedName, source); testElement = new UnitTestElement(testId, name, adapter, testMethod); (testElement as UnitTestElement).CodeBase = codeBase;