diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs index c6040f3a90..8ab1e1c55a 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestElement.cs @@ -34,6 +34,7 @@ internal abstract class TestElement : ITestElement, IXmlTestStore protected TestExecId _parentExecutionId; protected TestCategoryItemCollection _testCategories; protected WorkItemCollection _workItems; + protected TestPropertyItemCollection _testProperties; protected TestListCategoryId _catId; public TestElement(Guid id, string name, string adapter) @@ -178,6 +179,16 @@ public WorkItemCollection WorkItems } } + public TestPropertyItemCollection TestProperties + { + get { return _testProperties; } + set + { + EqtAssert.ParameterNotNull(value, "TestProperties"); + _testProperties = value; + } + } + /// /// Gets the adapter name. /// @@ -238,6 +249,7 @@ public virtual void Save(System.Xml.XmlElement element, XmlTestStoreParameters p h.SaveSimpleField(element, "@priority", _priority, DefaultPriority); h.SaveSimpleField(element, "Owners/Owner/@name", _owner, string.Empty); h.SaveObject(_testCategories, element, "TestCategory", parameters); + h.SaveObject(_testProperties, element, "Properties", parameters); if (_executionId != null) h.SaveGuid(element, "Execution/@id", _executionId.Id); @@ -262,6 +274,7 @@ private void Initialize() _parentExecutionId = TestExecId.Empty; _testCategories = new TestCategoryItemCollection(); _workItems = new WorkItemCollection(); + _testProperties = new TestPropertyItemCollection(); _isRunnable = true; _catId = TestListCategoryId.Uncategorized; } diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestPropertyItems.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestPropertyItems.cs new file mode 100644 index 0000000000..e854e09f9d --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/ObjectModel/TestPropertyItems.cs @@ -0,0 +1,244 @@ +// 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.Text; + +using Utility; + +using XML; + +#region TestPropertyItem +/// +/// Stores a string which categorizes the Test +/// +internal sealed class TestPropertyItem : IXmlTestStore +{ + #region Fields + [StoreXmlSimpleField(Location = "Key", DefaultValue = "")] + private readonly string _key = string.Empty; + + [StoreXmlSimpleField(Location = "Value", DefaultValue = "")] + private readonly string _value = string.Empty; + + #endregion + + #region Constructors + /// + /// Create a new item with the key/value set + /// + /// The key. + /// The value. + public TestPropertyItem(string key, string value) + { + // Treat null as empty. + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + + if (value == null) + { + value = String.Empty; + } + + _key = key; + _value = value; + } + + #endregion + + #region Properties/Methods + /// + /// Gets the Key for this TestProperty + /// + public string Key + { + get + { + return _key; + } + } + + /// + /// Gets the Value for this TestProperty + /// + public string Value + { + get + { + return _value; + } + } + + #endregion + + #region Methods - overrides + /// + /// Compare the values of the items + /// + /// Value being compared to. + /// True if the values are the same and false otherwise. + public override bool Equals(object other) + { + TestPropertyItem otherItem = other as TestPropertyItem; + if (otherItem == null) + { + return false; + } + return String.Equals(_key, otherItem._key, StringComparison.OrdinalIgnoreCase) && String.Equals(_value, otherItem._value, StringComparison.Ordinal); + } + + /// + /// Convert the property name to a hashcode + /// + /// Hashcode of the category. + public override int GetHashCode() + { + return _key.ToUpperInvariant().GetHashCode() ^ _value.GetHashCode(); + } + + /// + /// Convert the property name to a string + /// + /// The property. + public override string ToString() + { + return _key + " = " + _value; + } + #endregion + + #region IXmlTestStore Members + + /// + /// Saves the class under the XmlElement. + /// + /// XmlElement element + /// XmlTestStoreParameters parameters + public void Save(System.Xml.XmlElement element, XmlTestStoreParameters parameters) + { + new XmlPersistence().SaveSingleFields(element, this, parameters); + } + + #endregion +} +#endregion + +#region TestPropertyItemCollection +/// +/// A collection of strings which categorize the test. +/// +internal sealed class TestPropertyItemCollection : EqtBaseCollection +{ + #region Constructors + /// + /// Creates an empty TestPropertyItemCollection. + /// + public TestPropertyItemCollection() + { + _childElementName = "Property"; + } + + #endregion + + #region Methods + + /// + /// Adds the property. + /// + /// Key to be added. + /// Value to be added. + public void Add(string key, string value) + { + Add(new TestPropertyItem(key, value)); + } + + /// + /// Adds the property. + /// + /// Property to be added. + public override void Add(TestPropertyItem item) + { + EqtAssert.ParameterNotNull(item, nameof(item)); + + // Don't add empty items. + if (!String.IsNullOrEmpty(item.Key)) + { + base.Add(item); + } + } + + /// + /// Convert the TestPropertyItemCollection to a string. + /// each item is surrounded by a comma (,) + /// + /// + public override string ToString() + { + var returnString = new StringBuilder(); + if (Count > 0) + { + returnString.Append(','); + foreach (TestPropertyItem item in this) + { + returnString.Append(item.ToString()); + returnString.Append(','); + } + } + + return returnString.ToString(); + } + + /// + /// Compare the collection items + /// + /// other collection + /// true if the collections contain the same items + public override bool Equals(object obj) + { + TestPropertyItemCollection other = obj as TestPropertyItemCollection; + bool result = false; + + if (other == null) + { + // Other object is not a TestPropertyItemCollection. + result = false; + } + else if (Object.ReferenceEquals(this, other)) + { + // The other object is the same object as this one. + result = true; + } + else if (Count != other.Count) + { + // The count of categories in the other object does not + // match this one, so they are not equal. + result = false; + } + else + { + // Check each item and return on the first mismatch. + foreach (TestPropertyItem item in this) + { + if (!other.Contains(item)) + { + result = false; + break; + } + } + } + + return result; + } + + /// + /// Return the hash code of this collection + /// + /// The hashcode. + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion +} +#endregion \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs index 7ad22686e5..4569e3efb0 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Collection.cs @@ -61,7 +61,7 @@ public void Dispose() #region Fields protected Hashtable _container; - private string _childElementName; + protected string _childElementName; #endregion #region Constructors diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs index e26b44f53a..02ee8708e2 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs @@ -75,6 +75,11 @@ public ITestElement ToTestElement( testElement.WorkItems.Add(workItem); } + foreach (var trait in rockSteadyTestCase.Traits) + { + testElement.TestProperties.Add(trait.Name, trait.Value); + } + return testElement; } diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs index 85b027ab70..b1a14bbaca 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs @@ -804,6 +804,53 @@ public void TestRunInformationShouldContainUtcDateTime() ValidateDateTimeInTrx(_testableTrxLogger.TrxFile); } + [TestMethod] + public void TraitsForTestCaseShouldBeOutputtedInTrx() + { + ObjectModel.TestCase testCase = CreateTestCase("TestCaseWithTraits"); + testCase.Traits.Add("Trait1", "Value1"); + testCase.Traits.Add("Trait2", "Value2"); + + var result = new ObjectModel.TestResult(testCase); + var resultEventArg1 = new Mock(result); + + _testableTrxLogger.TestResultHandler(new object(), resultEventArg1.Object); + + var testRunCompleteEventArgs = TrxLoggerTests.CreateTestRunCompleteEventArgs(); + _testableTrxLogger.TestRunCompleteHandler(new object(), testRunCompleteEventArgs); + + Assert.IsTrue(File.Exists(_testableTrxLogger.TrxFile), string.Format("TRX file: {0}, should have got created.", _testableTrxLogger.TrxFile)); + + // Validate TRX output - we expect something like this: + /* + + + Trait1 + Value1 + + + Trait2 + Value2 + + + */ + + using FileStream file = File.OpenRead(_testableTrxLogger.TrxFile); + using XmlReader reader = XmlReader.Create(file); + XDocument document = XDocument.Load(reader); + XNamespace ns = document.Root.GetDefaultNamespace(); + var testDefinitionsElement = document.Descendants(ns.GetName("TestDefinitions")).FirstOrDefault(); + var unitTestElement = testDefinitionsElement.Element(ns.GetName("UnitTest")); + + Assert.IsNotNull(unitTestElement, "Unable to find UnitTest element in TRX"); + + var propertiesElement = unitTestElement.Element(ns.GetName("Properties")); + Assert.IsNotNull(propertiesElement, "Unable to find Properties element for UnitTest element in TRX"); + + var propertyElements = propertiesElement.Elements(ns.GetName("Property")); + Assert.AreEqual(2, propertyElements.Count(), "We should have two properties logged in TRX"); + } + private void ValidateDateTimeInTrx(string trxFileName) { using FileStream file = File.OpenRead(trxFileName); diff --git a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs index 1e8864df3a..328673ee0c 100644 --- a/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs +++ b/test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/Utility/ConverterTests.cs @@ -114,6 +114,24 @@ public void ToTestElementShouldAssignWorkItemOfUnitTestElement() CollectionAssert.AreEquivalent(expected, unitTestElement.WorkItems.ToArray()); } + [TestMethod] + public void ToTestElementShouldShouldAssignTraitsOfUnitTestElement() + { + var testCase = CreateTestCase("TestCaseWithTraits"); + var result = new TestPlatformObjectModel.TestResult(testCase); + + testCase.Traits.Add("Trait1", "Value1"); + testCase.Traits.Add("Trait2", "Value2"); + + var unitTestElement = _converter.ToTestElement(testCase.Id, Guid.Empty, Guid.Empty, testCase.DisplayName, TrxLoggerConstants.UnitTestType, testCase); + + // They only way to check for TestProperties is to cast the ITestElement object to UnitTestElement + Assert.IsInstanceOfType(unitTestElement, typeof(UnitTestElement)); + + var expected = new[] { new TestPropertyItem("Trait1", "Value1"), new TestPropertyItem("Trait2", "Value2") }; + CollectionAssert.AreEquivalent(expected, ((UnitTestElement)unitTestElement).TestProperties.ToArray()); + } + /// /// Unit test for regression when there's no test categories. ///