Skip to content

Commit ba4c9b0

Browse files
committed
[Breaking]: Support test artifacts in VS
1 parent d1fc4a4 commit ba4c9b0

20 files changed

Lines changed: 142 additions & 122 deletions

File tree

src/Adapter/MSTest.Engine/Engine/TestExecutionContext.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,15 @@ internal sealed class TestExecutionContext : ITestExecutionContext
1717
private readonly PlatformTestNode _platformTestNode;
1818
private readonly ITrxReportCapability? _trxReportCapability;
1919
private readonly SessionUid _sessionUid;
20-
private readonly Func<IData, Task> _publishDataAsync;
2120
private readonly CancellationToken _originalCancellationToken;
2221

2322
public TestExecutionContext(IConfiguration configuration, TestNode testNode, PlatformTestNode platformTestNode,
24-
ITrxReportCapability? trxReportCapability, SessionUid sessionUid, Func<IData, Task> publishDataAsync, CancellationToken cancellationToken)
23+
ITrxReportCapability? trxReportCapability, SessionUid sessionUid, CancellationToken cancellationToken)
2524
{
2625
Configuration = configuration;
2726
_platformTestNode = platformTestNode;
2827
_trxReportCapability = trxReportCapability;
2928
_sessionUid = sessionUid;
30-
_publishDataAsync = publishDataAsync;
3129
TestInfo = new TestInfo(testNode);
3230
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
3331
_originalCancellationToken = cancellationToken;
@@ -76,8 +74,11 @@ OperationCanceledException canceledException
7674
}
7775
}
7876

79-
public async Task AddTestAttachmentAsync(FileInfo file, string displayName, string? description = null)
80-
=> await _publishDataAsync(new TestNodeFileArtifact(_sessionUid, _platformTestNode, file, displayName, description));
77+
public Task AddTestAttachmentAsync(FileInfo file, string displayName, string? description = null)
78+
{
79+
_platformTestNode.Properties.Add(new TestFileArtifactProperty(_sessionUid, file, displayName, description));
80+
return Task.CompletedTask;
81+
}
8182

8283
private static void AddTrxExceptionInformation(PropertyBag propertyBag, Exception? exception)
8384
{

src/Adapter/MSTest.Engine/Engine/ThreadPoolTestNodeRunner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ private async Task<Result> InvokeTestNodeAndPublishResultAsync(TestNode testNode
215215
platformTestNode.Properties.Add(new TrxFullyQualifiedTypeNameProperty(platformTestNode.Uid.Value[..platformTestNode.Uid.Value.LastIndexOf('.')]));
216216
}
217217

218-
TestExecutionContext testExecutionContext = new(_configuration, testNode, platformTestNode, _trxReportCapability, _sessionUid, _publishDataAsync, _cancellationToken);
218+
TestExecutionContext testExecutionContext = new(_configuration, testNode, platformTestNode, _trxReportCapability, _sessionUid, _cancellationToken);
219219
try
220220
{
221221
// If we're already enqueued we cancel the test before the start

src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ internal sealed class TrxReportGenerator :
4040
private readonly TrxTestApplicationLifecycleCallbacks? _trxTestApplicationLifecycleCallbacks;
4141
private readonly ILogger<TrxReportGenerator> _logger;
4242
private readonly List<TestNodeUpdateMessage> _tests = [];
43-
private readonly Dictionary<TestNodeUid, List<SessionFileArtifact>> _artifactsByTestNode = new();
4443
private readonly Dictionary<IExtension, List<SessionFileArtifact>> _artifactsByExtension = new();
4544
private readonly bool _isEnabled;
4645

@@ -84,7 +83,6 @@ public TrxReportGenerator(
8483
public Type[] DataTypesConsumed { get; } =
8584
[
8685
typeof(TestNodeUpdateMessage),
87-
typeof(TestNodeFileArtifact),
8886
typeof(SessionFileArtifact)
8987
];
9088

@@ -140,18 +138,7 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
140138
}
141139

142140
break;
143-
case TestNodeFileArtifact testNodeFileArtifact:
144-
if (!_artifactsByTestNode.TryGetValue(testNodeFileArtifact.TestNode.Uid, out List<SessionFileArtifact>? nodeFileArtifacts))
145-
{
146-
nodeFileArtifacts = [testNodeFileArtifact];
147-
_artifactsByTestNode[testNodeFileArtifact.TestNode.Uid] = nodeFileArtifacts;
148-
}
149-
else
150-
{
151-
nodeFileArtifacts.Add(testNodeFileArtifact);
152-
}
153141

154-
break;
155142
case SessionFileArtifact fileArtifact:
156143
if (!_artifactsByExtension.TryGetValue(dataProducer, out List<SessionFileArtifact>? sessionFileArtifacts))
157144
{
@@ -235,7 +222,7 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio
235222

236223
int exitCode = _testApplicationProcessExitCode.GetProcessExitCode();
237224
TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptionsService, _configuration,
238-
_clock, _tests.ToArray(), _failedTestsCount, _passedTestsCount, _notExecutedTestsCount, _timeoutTestsCount, _artifactsByExtension, _artifactsByTestNode,
225+
_clock, _tests.ToArray(), _failedTestsCount, _passedTestsCount, _notExecutedTestsCount, _timeoutTestsCount, _artifactsByExtension,
239226
_adapterSupportTrxCapability, _testFramework, _testStartTime.Value, exitCode, cancellationToken);
240227
(string reportFileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync();
241228
if (warning is not null)

src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH
164164
TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptions, _configuration,
165165
_clock, [], 0, 0, 0, 0,
166166
artifacts,
167-
new Dictionary<TestNodeUid, List<SessionFileArtifact>>(),
168167
adapterSupportTrxCapability: null,
169168
new TestAdapterInfo(_testAdapterInformationRequest!.TestAdapterId, _testAdapterInformationRequest.TestAdapterVersion),
170169
_startTime,
@@ -201,7 +200,6 @@ await _messageBus.PublishAsync(
201200
TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptions, _configuration,
202201
_clock, [], 0, 0, 0, 0,
203202
artifacts,
204-
new Dictionary<TestNodeUid, List<SessionFileArtifact>>(),
205203
false,
206204
new TestAdapterInfo(_testAdapterInformationRequest!.TestAdapterId, _testAdapterInformationRequest.TestAdapterVersion),
207205
_startTime,

src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ internal sealed partial class TrxReportEngine
8787
private readonly int _notExecutedTestsCount;
8888
private readonly int _timeoutTestsCount;
8989
private readonly Dictionary<IExtension, List<SessionFileArtifact>> _artifactsByExtension;
90-
private readonly Dictionary<TestNodeUid, List<SessionFileArtifact>> _artifactsByTestNode;
9190
private readonly bool? _adapterSupportTrxCapability;
9291
private readonly ITestFramework _testFrameworkAdapter;
9392
private readonly DateTimeOffset _testStartTime;
@@ -97,7 +96,7 @@ internal sealed partial class TrxReportEngine
9796
private readonly IFileSystem _fileSystem;
9897
private readonly bool _isCopyingFileAllowed;
9998

100-
public TrxReportEngine(ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, int notExecutedTestsCount, int timeoutTestsCount, Dictionary<IExtension, List<SessionFileArtifact>> artifactsByExtension, Dictionary<TestNodeUid, List<SessionFileArtifact>> artifactsByTestNode, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, int exitCode, CancellationToken cancellationToken)
99+
public TrxReportEngine(ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, int notExecutedTestsCount, int timeoutTestsCount, Dictionary<IExtension, List<SessionFileArtifact>> artifactsByExtension, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, int exitCode, CancellationToken cancellationToken)
101100
: this(
102101
new SystemFileSystem(),
103102
testApplicationModuleInfo,
@@ -111,7 +110,6 @@ public TrxReportEngine(ITestApplicationModuleInfo testApplicationModuleInfo, IEn
111110
notExecutedTestsCount,
112111
timeoutTestsCount,
113112
artifactsByExtension,
114-
artifactsByTestNode,
115113
adapterSupportTrxCapability,
116114
testFrameworkAdapter,
117115
testStartTime,
@@ -120,7 +118,7 @@ public TrxReportEngine(ITestApplicationModuleInfo testApplicationModuleInfo, IEn
120118
{
121119
}
122120

123-
public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, int notExecutedTestsCount, int timeoutTestsCount, Dictionary<IExtension, List<SessionFileArtifact>> artifactsByExtension, Dictionary<TestNodeUid, List<SessionFileArtifact>> artifactsByTestNode, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, int exitCode, CancellationToken cancellationToken, bool isCopyingFileAllowed = true)
121+
public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, int notExecutedTestsCount, int timeoutTestsCount, Dictionary<IExtension, List<SessionFileArtifact>> artifactsByExtension, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, int exitCode, CancellationToken cancellationToken, bool isCopyingFileAllowed = true)
124122
{
125123
_testApplicationModuleInfo = testApplicationModuleInfo;
126124
_environment = environment;
@@ -133,7 +131,6 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp
133131
_notExecutedTestsCount = notExecutedTestsCount;
134132
_timeoutTestsCount = timeoutTestsCount;
135133
_artifactsByExtension = artifactsByExtension;
136-
_artifactsByTestNode = artifactsByTestNode;
137134
_adapterSupportTrxCapability = adapterSupportTrxCapability;
138135
_testFrameworkAdapter = testFrameworkAdapter;
139136
_testStartTime = testStartTime;
@@ -521,17 +518,17 @@ private void AddResults(string testAppModule, XElement testRun, out XElement tes
521518
unitTestResult.Add(output);
522519
}
523520

524-
if (_artifactsByTestNode.TryGetValue(testNode.Uid, out List<SessionFileArtifact>? fileArtifacts))
521+
XElement? resultFiles = null;
522+
foreach (TestFileArtifactProperty testFileArtifact in testNode.Properties.OfType<TestFileArtifactProperty>())
525523
{
526-
var resultFiles = new XElement("ResultFiles");
527-
528-
foreach (SessionFileArtifact fileArtifact in fileArtifacts)
529-
{
530-
resultFiles.Add(new XElement(
531-
"ResultFile",
532-
new XAttribute("path", fileArtifact.FileInfo.FullName)));
533-
}
524+
resultFiles ??= new XElement("ResultFiles");
525+
resultFiles.Add(new XElement(
526+
"ResultFile",
527+
new XAttribute("path", testFileArtifact.FileInfo.FullName)));
528+
}
534529

530+
if (resultFiles is not null)
531+
{
535532
unitTestResult.Add(resultFiles);
536533
}
537534

src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void RecordAttachments(IList<AttachmentSet> attachmentSets)
9797
{
9898
_logger.LogTrace($"{nameof(FrameworkHandlerAdapter)}.RecordAttachments");
9999
_frameworkHandle?.RecordAttachments(attachmentSets);
100-
PublishAttachmentsAsync(attachmentSets).Await();
100+
PublishTestSessionAttachmentsAsync(attachmentSets).Await();
101101
}
102102

103103
/// <inheritdoc/>
@@ -126,12 +126,10 @@ public void RecordResult(TestResult testResult)
126126
_frameworkHandle?.RecordResult(testResult);
127127

128128
// Publish node state change to Microsoft Testing Platform
129-
var testNode = testResult.ToTestNode(_isTrxEnabled, _clientInfo);
129+
var testNode = testResult.ToTestNode(_isTrxEnabled, _clientInfo, _session.SessionUid);
130130

131131
var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode);
132132
_messageBus.PublishAsync(_adapterExtensionBase, testNodeChange).Await();
133-
134-
PublishAttachmentsAsync(testResult.Attachments, testNode).Await();
135133
}
136134

137135
/// <inheritdoc/>
@@ -158,7 +156,7 @@ public void RecordStart(TestCase testCase)
158156
public void SendMessage(TestMessageLevel testMessageLevel, string message)
159157
=> _comboMessageLogger.SendMessage(testMessageLevel, message);
160158

161-
private async Task PublishAttachmentsAsync(IEnumerable<AttachmentSet> attachments, TestNode? testNode = null)
159+
private async Task PublishTestSessionAttachmentsAsync(IEnumerable<AttachmentSet> attachments)
162160
{
163161
foreach (AttachmentSet attachmentSet in attachments)
164162
{
@@ -169,9 +167,7 @@ private async Task PublishAttachmentsAsync(IEnumerable<AttachmentSet> attachment
169167
throw new FormatException($"Test adapter {_adapterExtensionBase.DisplayName} only supports file attachments.");
170168
}
171169

172-
SessionFileArtifact fileArtifact = testNode is null
173-
? new SessionFileArtifact(_session.SessionUid, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description)
174-
: new TestNodeFileArtifact(_session.SessionUid, testNode, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description);
170+
var fileArtifact = new SessionFileArtifact(_session.SessionUid, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description);
175171
await _messageBus.PublishAsync(_adapterExtensionBase, fileArtifact);
176172
}
177173
}

src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private static void CopyVSTestProperties(IEnumerable<TestProperty> testPropertie
127127
/// <summary>
128128
/// Converts a VSTest <see cref="TestResult"/> to a Microsoft Testing Platform <see cref="TestNode"/>.
129129
/// </summary>
130-
public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, IClientInfo client)
130+
public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, IClientInfo client, SessionUid sessionUid)
131131
{
132132
var testNode = testResult.TestCase.ToTestNode(isTrxEnabled, client, testResult.DisplayName);
133133
CopyVSTestProperties(testResult.Properties, testNode, testResult.TestCase, testResult.GetPropertyValue, isTrxEnabled, client);
@@ -179,6 +179,14 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled,
179179
}
180180
}
181181

182+
foreach (AttachmentSet attachmentSet in testResult.Attachments)
183+
{
184+
foreach (UriDataAttachment attachment in attachmentSet.Attachments)
185+
{
186+
testNode.Properties.Add(new TestFileArtifactProperty(sessionUid, new FileInfo(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description));
187+
}
188+
}
189+
182190
if (standardErrorMessages.Count > 0)
183191
{
184192
testNode.Properties.Add(new StandardErrorProperty(string.Join(Environment.NewLine, standardErrorMessages)));

src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ protected VSTestBridgedTestFrameworkBase(IServiceProvider serviceProvider, ITest
5252
[
5353
typeof(TestNodeUpdateMessage),
5454
typeof(SessionFileArtifact),
55-
typeof(TestNodeFileArtifact)
5655
];
5756

5857
/// <summary>

src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public override string ToString()
125125
/// <summary>
126126
/// Represents a test node file artifact.
127127
/// </summary>
128+
[Obsolete("This API is obsolete and will be removed in v2. Instead of publishing this to MessageBus, add TestFileArtifactProperty property to the TestNode when publishing TestNodeUpdateMessage with a test result", error: true)]
128129
public class TestNodeFileArtifact : SessionFileArtifact
129130
{
130131
/// <summary>

src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using Microsoft.Testing.Platform.TestHost;
5+
46
namespace Microsoft.Testing.Platform.Extensions.Messages;
57

68
/// <summary>
@@ -372,6 +374,15 @@ public record StandardOutputProperty(string StandardOutput) : IProperty;
372374
[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")]
373375
public record StandardErrorProperty(string StandardError) : IProperty;
374376

377+
/// <summary>
378+
/// Property that represents multiple artifacts/attachments to associate with a test node.
379+
/// </summary>
380+
/// <param name="SessionUid">The session UID.</param>
381+
/// <param name="FileInfo">The file information.</param>
382+
/// <param name="DisplayName">The display name.</param>
383+
/// <param name="Description">The description.</param>
384+
public record TestFileArtifactProperty(SessionUid SessionUid, FileInfo FileInfo, string DisplayName, string? Description = null) : IProperty;
385+
375386
internal sealed record SerializableKeyValuePairStringProperty(string Key, string Value) : KeyValuePairStringProperty(Key, Value);
376387

377388
internal sealed record SerializableNamedKeyValuePairsStringProperty(string Name, KeyValuePair<string, string>[] Pairs) : IProperty

0 commit comments

Comments
 (0)