Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DisplayMessage to TestContext #4345

Merged
merged 7 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
#endif
public class TestExecutionManager
{
private sealed class RemotingMessageLogger : MarshalByRefObject, IMessageLogger
{
private readonly IMessageLogger _realMessageLogger;

public RemotingMessageLogger(IMessageLogger messageLogger)
=> _realMessageLogger = messageLogger;

public void SendMessage(TestMessageLevel testMessageLevel, string message)
=> _realMessageLogger.SendMessage(testMessageLevel, message);
}

/// <summary>
/// Dictionary for test run parameters.
/// </summary>
Expand Down Expand Up @@ -451,7 +462,10 @@ private void ExecuteTestsWithTestRunner(
// Run single test passing test context properties to it.
IDictionary<TestProperty, object?> tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
Dictionary<string, object?> testContextProperties = GetTestContextProperties(tcmProperties, sourceLevelParameters);
UnitTestResult[] unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties);

// testRunner could be in a different AppDomain. We cannot pass the testExecutionRecorder directly.
// Instead, we pass a proxy (remoting object) that is marshallable by ref.
UnitTestResult[] unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties, new RemotingMessageLogger(testExecutionRecorder));

PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed test {0}", unitTestElement.TestMethod.Name);

Expand Down
5 changes: 3 additions & 2 deletions src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome;
Expand Down Expand Up @@ -130,15 +131,15 @@ internal FixtureTestResult GetFixtureTestResult(TestMethod testMethod, string fi
/// <param name="testMethod"> The test Method. </param>
/// <param name="testContextProperties"> The test context properties. </param>
/// <returns> The <see cref="UnitTestResult"/>. </returns>
internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<string, object?> testContextProperties)
internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<string, object?> testContextProperties, IMessageLogger messageLogger)
{
Guard.NotNull(testMethod);

try
{
using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context");
var properties = new Dictionary<string, object?>(testContextProperties);
ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties);
ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties, messageLogger);
testContext.SetOutcome(UTF.UnitTestOutcome.InProgress);

// Get the testMethod
Expand Down
3 changes: 2 additions & 1 deletion src/Adapter/MSTest.TestAdapter/IPlatformServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;

Expand Down Expand Up @@ -121,5 +122,5 @@ ITestSourceHost CreateTestSourceHost(
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object?> properties);
ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object?> properties, IMessageLogger messageLogger);
}
5 changes: 3 additions & 2 deletions src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;

Expand Down Expand Up @@ -222,9 +223,9 @@ public ITestSourceHost CreateTestSourceHost(
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object?> properties)
public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object?> properties, IMessageLogger messageLogger)
{
var testContextImplementation = new TestContextImplementation(testMethod, writer, properties);
var testContextImplementation = new TestContextImplementation(testMethod, writer, properties, messageLogger);
TestRunCancellationToken?.Register(CancelDelegate, testContextImplementation);
return testContextImplementation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,6 @@ public interface ITestContext
/// </summary>
/// <param name="displayName">The display name.</param>
void SetDisplayName(string? displayName);

void DisplayMessage(MessageLevel messageLevel, string message);
}
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void
override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;

internal static class MessageLevelExtensions
{
public static TestMessageLevel ToTestMessageLevel(this MessageLevel messageLevel)
=> messageLevel switch
{
MessageLevel.Informational => TestMessageLevel.Informational,
MessageLevel.Warning => TestMessageLevel.Warning,
MessageLevel.Error => TestMessageLevel.Error,
_ => throw new ArgumentOutOfRangeException(nameof(messageLevel)),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Globalization;

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using ITestMethod = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod;
Expand Down Expand Up @@ -49,6 +50,7 @@ public class TestContextImplementation : TestContext, ITestContext
/// Properties.
/// </summary>
private readonly Dictionary<string, object?> _properties;
private readonly IMessageLogger? _messageLogger;

/// <summary>
/// Specifies whether the writer is disposed or not.
Expand All @@ -72,6 +74,17 @@ public class TestContextImplementation : TestContext, ITestContext
private DataRow? _dataRow;
#endif

/// <summary>
/// Initializes a new instance of the <see cref="TestContextImplementation"/> class.
/// </summary>
/// <param name="testMethod">The test method.</param>
/// <param name="stringWriter">The writer where diagnostic messages are written to.</param>
/// <param name="properties">Properties/configuration passed in.</param>
/// <param name="messageLogger">The message logger to use.</param>
internal TestContextImplementation(ITestMethod testMethod, StringWriter stringWriter, IDictionary<string, object?> properties, IMessageLogger messageLogger)
: this(testMethod, stringWriter, properties)
=> _messageLogger = messageLogger;

/// <summary>
/// Initializes a new instance of the <see cref="TestContextImplementation"/> class.
/// </summary>
Expand Down Expand Up @@ -357,5 +370,7 @@ public void ClearDiagnosticMessages()
public void SetDisplayName(string? displayName)
=> TestDisplayName = displayName;

public override void DisplayMessage(MessageLevel messageLevel, string message)
=> _messageLogger?.SendMessage(messageLevel.ToTestMessageLevel(), message);
#endregion
}
25 changes: 25 additions & 0 deletions src/TestFramework/TestFramework.Extensions/MessageLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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.VisualStudio.TestTools.UnitTesting;

/// <summary>
/// Specifies the severity level of messages displayed using the <see cref="TestContext.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel, string)"/> API.
/// </summary>
public enum MessageLevel
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// The message will be displayed as informational, typically used for general updates or non-critical messages.
/// </summary>
Informational,

/// <summary>
/// The message will be displayed as a warning, indicating a potential issue or something requiring attention.
/// </summary>
Warning,

/// <summary>
/// The message will be displayed as an error, representing a significant issue or failure.
/// </summary>
Error,
}
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void
Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel
Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Error = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel
Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Informational = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel
Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Warning = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel
2 changes: 2 additions & 0 deletions src/TestFramework/TestFramework.Extensions/TestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ public abstract class TestContext
/// <param name="args">the arguments.</param>
public abstract void WriteLine(string format, params object?[] args);

public abstract void DisplayMessage(MessageLevel messageLevel, string message);

private T? GetProperty<T>(string name)
where T : class
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#endif

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Moq;

Expand Down Expand Up @@ -389,4 +391,21 @@ public void GetResultFilesShouldReturnListOfAddedResultFiles()
Verify(resultFiles.Contains("C:\\files\\myfile2.txt"));
}
#endif

public void DisplayMessageShouldForwardToIMessageLogger()
{
var messageLoggerMock = new Mock<IMessageLogger>(MockBehavior.Strict);

messageLoggerMock
.Setup(l => l.SendMessage(It.IsAny<TestMessageLevel>(), It.IsAny<string>()));

_testContextImplementation = new TestContextImplementation(_testMethod.Object, new ThreadSafeStringWriter(null, "test"), _properties, messageLoggerMock.Object);
_testContextImplementation.DisplayMessage(MessageLevel.Informational, "InfoMessage");
_testContextImplementation.DisplayMessage(MessageLevel.Warning, "WarningMessage");
_testContextImplementation.DisplayMessage(MessageLevel.Error, "ErrorMessage");

messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Informational, "InfoMessage"), Times.Once);
messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Warning, "WarningMessage"), Times.Once);
messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Error, "ErrorMessage"), Times.Once);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ public void ConstructorShouldPopulateSettings()
#region RunSingleTest tests

public void RunSingleTestShouldThrowIfTestMethodIsNull() =>
VerifyThrows<ArgumentNullException>(() => _unitTestRunner.RunSingleTest(null, null));
VerifyThrows<ArgumentNullException>(() => _unitTestRunner.RunSingleTest(null, null, null));

public void RunSingleTestShouldThrowIfTestRunParametersIsNull()
{
var testMethod = new TestMethod("M", "C", "A", isAsync: false);
VerifyThrows<ArgumentNullException>(() => _unitTestRunner.RunSingleTest(testMethod, null));
VerifyThrows<ArgumentNullException>(() => _unitTestRunner.RunSingleTest(testMethod, null, null));
}

public void RunSingleTestShouldReturnTestResultIndicateATestNotFoundIfTestMethodCannotBeFound()
Expand All @@ -100,7 +100,7 @@ public void RunSingleTestShouldReturnTestResultIndicateATestNotFoundIfTestMethod
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -117,7 +117,7 @@ public void RunSingleTestShouldReturnTestResultIndicatingNotRunnableTestIfTestMe
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

string expectedMessage = string.Format(
CultureInfo.InvariantCulture,
Expand All @@ -140,7 +140,7 @@ public void ExecuteShouldSkipTestAndFillInClassIgnoreMessageIfIgnoreAttributeIsP
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -157,7 +157,7 @@ public void ExecuteShouldSkipTestAndSkipFillingIgnoreMessageIfIgnoreAttributeIsP
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -174,7 +174,7 @@ public void ExecuteShouldSkipTestAndFillInMethodIgnoreMessageIfIgnoreAttributeIs
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -191,7 +191,7 @@ public void ExecuteShouldSkipTestAndSkipFillingIgnoreMessageIfIgnoreAttributeIsP
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -208,7 +208,7 @@ public void ExecuteShouldSkipTestAndFillInClassIgnoreMessageIfIgnoreAttributeIsP
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -225,7 +225,7 @@ public void ExecuteShouldSkipTestAndFillInMethodIgnoreMessageIfIgnoreAttributeIs
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -241,7 +241,7 @@ public void RunSingleTestShouldReturnTestResultIndicatingFailureIfThereIsAnyType
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

string expectedMessage = string.Format(
CultureInfo.InvariantCulture,
Expand All @@ -264,7 +264,7 @@ public void RunSingleTestShouldReturnTestResultsForAPassingTestMethod()
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny<bool>()))
.Returns(Assembly.GetExecutingAssembly());

UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -282,7 +282,7 @@ public void RunSingleTestShouldSetTestsAsInProgressInTestContext()
.Returns(Assembly.GetExecutingAssembly());

// Asserting in the test method execution flow itself.
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(results is not null);
Verify(results.Length == 1);
Expand All @@ -308,7 +308,7 @@ public void RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMethodsIn
DummyTestClassWithInitializeMethods.AssemblyInitializeMethodBody = () => validator <<= 2;
DummyTestClassWithInitializeMethods.ClassInitializeMethodBody = () => validator >>= 2;

_unitTestRunner.RunSingleTest(testMethod, _testRunParameters);
_unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null);

Verify(validator == 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void GetTestContextShouldReturnAValidTestContext()
testMethod.Setup(tm => tm.Name).Returns("M");

// Act.
PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, writer, properties);
PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, writer, properties, null);

// Assert.
Verify(testContext.Context.FullyQualifiedTestClassName == "A.C.M");
Expand Down
Loading
Loading