diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs index 81a3151552..d94da9b348 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -162,21 +162,35 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, testNode.Properties.Add(new TimingProperty(new(testResult.StartTime, testResult.EndTime, testResult.Duration), [])); + var standardErrorMessages = new List(); + var standardOutputMessages = new List(); foreach (TestResultMessage testResultMessage in testResult.Messages) { if (testResultMessage.Category == TestResultMessage.StandardErrorCategory) { - testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", testResultMessage.Text ?? string.Empty)); - testNode.Properties.Add(new StandardErrorProperty(testResultMessage.Text ?? string.Empty)); + string message = testResultMessage.Text ?? string.Empty; + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", message)); + standardErrorMessages.Add(message); } if (testResultMessage.Category == TestResultMessage.StandardOutCategory) { - testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", testResultMessage.Text ?? string.Empty)); - testNode.Properties.Add(new StandardOutputProperty(testResultMessage.Text ?? string.Empty)); + string message = testResultMessage.Text ?? string.Empty; + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", message)); + standardOutputMessages.Add(message); } } + if (standardErrorMessages.Count > 0) + { + testNode.Properties.Add(new StandardErrorProperty(string.Join(Environment.NewLine, standardErrorMessages))); + } + + if (standardOutputMessages.Count > 0) + { + testNode.Properties.Add(new StandardOutputProperty(string.Join(Environment.NewLine, standardOutputMessages))); + } + return testNode; } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs new file mode 100644 index 0000000000..8adef82ace --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs @@ -0,0 +1,88 @@ +// 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.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class OutputTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public OutputTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) => _testAssetFixture = testAssetFixture; + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task DetailedOutputIsAsExpected(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--output detailed"); + + // Assert + testHostResult.AssertOutputContains("Assert.AreEqual failed. Expected:<1>. Actual:<2>."); + testHostResult.AssertOutputContains(""" + Standard output + Console message + TestContext Messages: + TestContext message + Error output + """); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "TestOutput"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file TestOutput.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + public TestContext TestContext { get; set; } + + [TestMethod] + public void TestMethod() + { + Debug.WriteLine("Debug message"); + Console.WriteLine("Console message"); + TestContext.WriteLine("TestContext message"); + + Assert.AreEqual(1, 2); + } +} +"""; + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs index ab1f69cd40..980c4000e2 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs @@ -252,4 +252,42 @@ public void ToTestNode_WhenTestResultHasTraits_TestNodePropertiesContainIt() Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Key == "key"); Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Value == "value"); } + + public void ToTestNode_WhenTestResultHasMultipleStandardOutputMessages_TestNodePropertiesHasASingleOne() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + Messages = + { + new TestResultMessage(TestResultMessage.StandardOutCategory, "message1"), + new TestResultMessage(TestResultMessage.StandardOutCategory, "message2"), + }, + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + StandardOutputProperty[] standardOutputProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(standardOutputProperties.Length == 1); + Assert.AreEqual($"message1{Environment.NewLine}message2", standardOutputProperties[0].StandardOutput); + } + + public void ToTestNode_WhenTestResultHasMultipleStandardErrorMessages_TestNodePropertiesHasASingleOne() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + Messages = + { + new TestResultMessage(TestResultMessage.StandardErrorCategory, "message1"), + new TestResultMessage(TestResultMessage.StandardErrorCategory, "message2"), + }, + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + StandardErrorProperty[] standardErrorProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(standardErrorProperties.Length == 1); + Assert.AreEqual($"message1{Environment.NewLine}message2", standardErrorProperties[0].StandardError); + } }