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

Complete the attachment processors extension #3161

Merged
merged 47 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0444dc3
Complete attachment processors extension
MarcoRossignoli Nov 23, 2021
2e4ce70
revert formatting
MarcoRossignoli Nov 23, 2021
24edd07
add comment
MarcoRossignoli Nov 23, 2021
e9a1e53
add new versioning strategy
MarcoRossignoli Dec 1, 2021
1a01599
fix public api
MarcoRossignoli Dec 1, 2021
c8d065e
fix build
MarcoRossignoli Dec 2, 2021
d72d1ef
add header
MarcoRossignoli Dec 2, 2021
ddfea67
reorder usings
MarcoRossignoli Dec 2, 2021
24e4805
add new IConfigurableAttachmentProcessor and add second TestExtension…
MarcoRossignoli Dec 2, 2021
4fd8447
another round on the metadata discovery
MarcoRossignoli Dec 6, 2021
dfa45b4
nit refactor test
MarcoRossignoli Dec 6, 2021
2eb7b18
cleanup
MarcoRossignoli Dec 6, 2021
e4c009c
load extension for the analysis
MarcoRossignoli Dec 9, 2021
63db3e8
cache asms
MarcoRossignoli Dec 9, 2021
fa281d4
skip the extension cache for the attachment data processor
MarcoRossignoli Dec 15, 2021
6fef030
cache attachment processor resolution
MarcoRossignoli Dec 16, 2021
f79e25e
Break the IDataCollectorAttachmentProcessor and remove IConfigurableD…
MarcoRossignoli Dec 16, 2021
9b77adf
add telemetry for invoked collectors
MarcoRossignoli Dec 16, 2021
fc0577c
Make the processor logic fault resilient
MarcoRossignoli Dec 16, 2021
399dc05
Add message logger for datacollector attachment factory, fix UTs, add…
MarcoRossignoli Dec 17, 2021
361ab26
add some UTs
MarcoRossignoli Dec 18, 2021
7cf6e31
improve logging
MarcoRossignoli Dec 18, 2021
34e49f6
add some tests logs
MarcoRossignoli Dec 18, 2021
fe87ecf
Build AttachmentProcessorDataCollector project for UTs
MarcoRossignoli Dec 18, 2021
72174da
fix UTs
MarcoRossignoli Dec 19, 2021
39fb97e
Add telemetry, return friendly name for the invoked data collector
MarcoRossignoli Dec 20, 2021
a25b999
rename telemetry key
MarcoRossignoli Dec 20, 2021
f4a83e2
fix typo
MarcoRossignoli Dec 20, 2021
92bcc73
align public api names
MarcoRossignoli Dec 20, 2021
ae5ef16
refactor api name
MarcoRossignoli Dec 20, 2021
d5a42e7
fix UTs
MarcoRossignoli Dec 20, 2021
c1b4d0b
fix UT
MarcoRossignoli Dec 20, 2021
442a1b9
fix build on Unix
MarcoRossignoli Dec 20, 2021
09d322a
Improve logging
MarcoRossignoli Dec 20, 2021
f547aba
fix acceptance
MarcoRossignoli Dec 21, 2021
aa3eb53
Improve acceptance test
MarcoRossignoli Dec 21, 2021
ca79cfa
fix comment typo
MarcoRossignoli Dec 21, 2021
869f5ea
fix comment typo
MarcoRossignoli Dec 21, 2021
37e3042
address PR feedback
MarcoRossignoli Dec 21, 2021
570b983
fix test
MarcoRossignoli Dec 21, 2021
67499ce
Cleanup published api
MarcoRossignoli Dec 23, 2021
8e15a7d
address feedback on back-compat api
MarcoRossignoli Jan 10, 2022
9d07b05
add non-public ctor to avoid Newtosoft dep on the public ObjectModel
MarcoRossignoli Jan 10, 2022
1a30ca7
fix build
MarcoRossignoli Jan 11, 2022
657c129
fix comment typo
MarcoRossignoli Jan 11, 2022
2ae07e2
fix test
MarcoRossignoli Jan 11, 2022
936e678
fix TestRunCompleteEventArgs
MarcoRossignoli Jan 11, 2022
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
15 changes: 15 additions & 0 deletions TestPlatform.sln
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPlatform.Playground", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest1", "playground\MSTest1\MSTest1.csproj", "{57A61A09-10AD-44BE-8DF4-A6FD108F7DF7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AttachmentProcessorDataCollector", "test\TestAssets\AttachmentProcessorDataCollector\AttachmentProcessorDataCollector.csproj", "{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Microsoft.TestPlatform.Execution.Shared\Microsoft.TestPlatform.Execution.Shared.projitems*{10b6ade1-f808-4612-801d-4452f5b52242}*SharedItemsImports = 5
Expand Down Expand Up @@ -868,6 +870,18 @@ Global
{57A61A09-10AD-44BE-8DF4-A6FD108F7DF7}.Release|x64.Build.0 = Release|Any CPU
{57A61A09-10AD-44BE-8DF4-A6FD108F7DF7}.Release|x86.ActiveCfg = Release|Any CPU
{57A61A09-10AD-44BE-8DF4-A6FD108F7DF7}.Release|x86.Build.0 = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|x64.ActiveCfg = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|x64.Build.0 = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|x86.ActiveCfg = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Debug|x86.Build.0 = Debug|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|Any CPU.Build.0 = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|x64.ActiveCfg = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|x64.Build.0 = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|x86.ActiveCfg = Release|Any CPU
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -942,6 +956,7 @@ Global
{8238A052-D626-49EB-A011-51DC6D0DBA30} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959}
{545A88D3-1AE2-4D39-9B7C-C691768AD17F} = {6CE2F530-582B-4695-A209-41065E103426}
{57A61A09-10AD-44BE-8DF4-A6FD108F7DF7} = {6CE2F530-582B-4695-A209-41065E103426}
{B6AF6BCD-64C6-4F4E-ABCA-C8AA2AA66B7B} = {D9A30E32-D466-4EC5-B4F2-62E17562279B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0541B30C-FF51-4E28-B172-83F5F3934BCD}
Expand Down
2 changes: 1 addition & 1 deletion scripts/build/TestPlatform.Dependencies.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VSSdkBuildToolsVersion>17.0.1600</VSSdkBuildToolsVersion>
Expand Down
2 changes: 1 addition & 1 deletion scripts/common.lib.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function Install-DotNetCli
Write-Log "Install-DotNetCli: Get the latest dotnet cli toolset..."
$dotnetInstallPath = Join-Path $env:TP_TOOLS_DIR "dotnet"
New-Item -ItemType directory -Path $dotnetInstallPath -Force | Out-Null
& $dotnetInstallScript -Channel 6.0 -Quality Preview -InstallDir $dotnetInstallPath -Version $env:DOTNET_CLI_VERSION
& $dotnetInstallScript -Channel 6.0 -InstallDir $dotnetInstallPath -Version $env:DOTNET_CLI_VERSION

& $dotnetInstallScript -InstallDir "$dotnetInstallPath" -Runtime 'dotnet' -Version '2.1.30' -Channel '2.1' -Architecture x64 -NoPath
$env:DOTNET_ROOT= $dotnetInstallPath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ public void TestRunMessageHandler(object sender, TestRunMessageEventArgs e)
SendTestMessage(e.Level, e.Message);
break;


default:
throw new NotSupportedException($"Test message level '{e.Level}' is not supported.");
}
Expand Down Expand Up @@ -456,7 +456,7 @@ private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestMana
this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
var runCompletePayload = new TestRunCompletePayload()
{
TestRunCompleteArgs = new TestRunCompleteEventArgs(null, false, true, ex, null, TimeSpan.MinValue),
TestRunCompleteArgs = new TestRunCompleteEventArgs(null, false, true, ex, null, null, TimeSpan.MinValue),
LastRunTests = null
};

Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, Test
runCompleteArgs.Error,
// This is required as TMI adapter is sending attachments as List which cannot be type casted to Collection.
runContextAttachments != null ? new Collection<AttachmentSet>(runContextAttachments.ToList()) : null,
runCompleteArgs.InvokedDataCollectors,
this.runRequestTimeTracker.Elapsed);

// Ignore the time sent (runCompleteArgs.ElapsedTimeInRunningTests)
Expand Down Expand Up @@ -587,6 +588,7 @@ private void HandleLoggerManagerTestRunComplete(TestRunCompletePayload testRunCo
testRunCompletePayload.TestRunCompleteArgs.IsAborted,
testRunCompletePayload.TestRunCompleteArgs.Error,
testRunCompletePayload.TestRunCompleteArgs.AttachmentSets,
testRunCompletePayload.TestRunCompleteArgs.InvokedDataCollectors,
this.runRequestTimeTracker.Elapsed);
this.LoggerManager.HandleTestRunComplete(testRunCompleteArgs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollection
[DataContract]
public class AfterTestRunEndResult
{
// We have more than one ctor for backward-compatibility reason but we don't want to add dependency on Newtosoft([JsonConstructor])
// We want to fallback to the non-public default constructor https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_ConstructorHandling.htm during deserialization
private AfterTestRunEndResult()
{

}

/// <summary>
/// Initializes a new instance of the <see cref="AfterTestRunEndResult"/> class.
/// </summary>
Expand All @@ -24,14 +31,36 @@ public class AfterTestRunEndResult
/// The metrics.
/// </param>
public AfterTestRunEndResult(Collection<AttachmentSet> attachmentSets, IDictionary<string, object> metrics)
: this(attachmentSets, new Collection<InvokedDataCollector>(), metrics)
{ }

/// <summary>
/// Initializes a new instance of the <see cref="AfterTestRunEndResult"/> class.
/// </summary>
/// <param name="attachmentSets">
/// The collection of attachment sets.
/// </param>
/// <param name="invokedDataCollectors">
/// The collection of the DataCollectors invoked during test session
/// </param>
/// <param name="metrics">
/// The metrics.
/// </param>
public AfterTestRunEndResult(Collection<AttachmentSet> attachmentSets,
Collection<InvokedDataCollector> invokedDataCollectors,
IDictionary<string, object> metrics)
{
this.AttachmentSets = attachmentSets;
this.InvokedDataCollectors = invokedDataCollectors;
this.Metrics = metrics;
}

[DataMember]
public Collection<AttachmentSet> AttachmentSets { get; private set; }

[DataMember]
public Collection<InvokedDataCollector> InvokedDataCollectors { get; private set; }

[DataMember]
public IDictionary<string, object> Metrics { get; private set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector
using System.Linq;
using Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework;
using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities;
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.Logging;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Expand Down Expand Up @@ -257,6 +259,22 @@ public Collection<AttachmentSet> SessionEnded(bool isCancelled = false)
return new Collection<AttachmentSet>(result);
}

/// <inheritdoc/>
public Collection<InvokedDataCollector> GetInvokedDataCollectors()
{
List<InvokedDataCollector> invokedDataCollector = new List<InvokedDataCollector>();
foreach (DataCollectorInformation dataCollectorInformation in this.RunDataCollectors.Values)
{
invokedDataCollector.Add(new InvokedDataCollector(dataCollectorInformation.DataCollectorConfig.TypeUri,
dataCollectorInformation.DataCollectorConfig.FriendlyName,
dataCollectorInformation.DataCollectorConfig.DataCollectorType.AssemblyQualifiedName,
dataCollectorInformation.DataCollectorConfig.FilePath,
dataCollectorInformation.DataCollectorConfig.HasAttachmentsProcessor()));
}

return new Collection<InvokedDataCollector>(invokedDataCollector);
}

/// <inheritdoc/>
public void TestHostLaunched(int processId)
{
Expand Down Expand Up @@ -397,6 +415,29 @@ protected virtual bool TryGetUriFromFriendlyName(string friendlyName, out string
return false;
}

/// <summary>
/// Gets the DataCollectorConfig using uri.
/// </summary>
/// <param name="extensionUri">
/// The extension uri.
/// </param>
/// <returns>
/// The <see cref="DataCollectorConfig"/>.
/// </returns>
protected virtual DataCollectorConfig TryGetDataCollectorConfig(string extensionUri)
{
var extensionManager = this.dataCollectorExtensionManager;
foreach (var extension in extensionManager.TestExtensions)
{
if (string.Equals(extension.TestPluginInfo.IdentifierData, extensionUri, StringComparison.OrdinalIgnoreCase))
{
return (DataCollectorConfig)extension.TestPluginInfo;
}
}

return null;
}

protected virtual bool IsUriValid(string uri)
{
if (string.IsNullOrEmpty(uri))
Expand Down Expand Up @@ -470,7 +511,7 @@ private void LoadAndInitialize(DataCollectorSettings dataCollectorSettings, stri
return;
}

dataCollectorConfig = new DataCollectorConfig(dataCollector.GetType());
dataCollectorConfig = this.TryGetDataCollectorConfig(dataCollectorUri);

// Attempt to get the data collector information verifying that all of the required metadata for the collector is available.
dataCollectorInfo = new DataCollectorInformation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public DataCollectorConfig(Type type)
this.DataCollectorType = type;
this.TypeUri = GetTypeUri(type);
this.FriendlyName = GetFriendlyName(type);
this.AttachmentsProcessorType = GetAttachmentsProcessors(type);
}

/// <summary>
Expand Down Expand Up @@ -63,10 +64,21 @@ public override ICollection<object> Metadata
{
get
{
return new object[] { this.TypeUri.ToString(), this.FriendlyName };
return new object[] { this.TypeUri.ToString(), this.FriendlyName, this.AttachmentsProcessorType != null };
}
}

/// <summary>
/// Gets attachments processor
/// </summary>
public Type AttachmentsProcessorType { get; private set; }

/// <summary>
/// Check if collector registers an attachment processor.
/// </summary>
/// <returns>True if collector registers an attachment processor.</returns>
public bool HasAttachmentsProcessor() => AttachmentsProcessorType != null;

/// <summary>
/// Gets the Type Uri for the data collector.
/// </summary>
Expand All @@ -88,6 +100,27 @@ private static Uri GetTypeUri(Type dataCollectorType)
return typeUri;
}

/// <summary>
/// Gets the attachment processor for the data collector.
/// </summary>
/// <param name="dataCollectorType">The data collector to get the attachment processor for.</param>
/// <returns>Type of the attachment processor.</returns>
private static Type GetAttachmentsProcessors(Type dataCollectorType)
{
Type attachmentsProcessor = null;
var attachmenstProcessors = GetAttributes(dataCollectorType, typeof(DataCollectorAttachmentProcessorAttribute));
if (attachmenstProcessors != null && attachmenstProcessors.Length > 0)
{
var attachmenstProcessorsAttribute = (DataCollectorAttachmentProcessorAttribute)attachmenstProcessors[0];
if (attachmenstProcessorsAttribute.Type != null)
{
attachmentsProcessor = attachmenstProcessorsAttribute.Type;
}
}

return attachmentsProcessor;
}

/// <summary>
/// Gets the friendly name for the data collector.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,11 @@ internal interface IDataCollectionManager : IDisposable
/// Collection of session attachmentSet.
/// </returns>
Collection<AttachmentSet> SessionEnded(bool isCancelled);

/// <summary>
/// Return a collections of the invoked data collectors
/// </summary>
/// <returns>Collection of data collectors.</returns>
Collection<InvokedDataCollector> GetInvokedDataCollectors();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,39 @@ protected DataCollectorExtensionManager(
/// </returns>
public static DataCollectorExtensionManager Create(IMessageLogger messageLogger)
{

TestPluginManager.Instance.GetSpecificTestExtensions<DataCollectorConfig, DataCollector, IDataCollectorCapabilities, DataCollectorMetadata>(
TestPlatformConstants.DataCollectorEndsWithPattern,
out var unfilteredTestExtensions,
out var filteredTestExtensions);

return new DataCollectorExtensionManager(unfilteredTestExtensions, filteredTestExtensions, messageLogger);
}

/// <summary>
/// Gets an instance of the DataCollectorExtensionManager.
/// </summary>
/// <param name="extensionAssemblyFilePath">
/// File path that contains data collectors to load.
/// </param>
/// <param name="skipCache">
/// Skip the extensions cache.
/// </param>
/// <param name="messageLogger">
/// The message Logger.
/// </param>
/// <returns>
/// The DataCollectorExtensionManager.
/// </returns>
public static DataCollectorExtensionManager Create(string extensionAssemblyFilePath, bool skipCache, IMessageLogger messageLogger)
{
TestPluginManager.Instance.GetTestExtensions<DataCollectorConfig, DataCollector, IDataCollectorCapabilities, DataCollectorMetadata>(
extensionAssemblyFilePath,
out var unfilteredTestExtensions,
out var filteredTestExtensions,
skipCache);

return new DataCollectorExtensionManager(unfilteredTestExtensions, filteredTestExtensions, messageLogger);
}
}

/// <summary>
Expand All @@ -76,9 +101,25 @@ public class DataCollectorMetadata : IDataCollectorCapabilities
/// The friendly Name.
/// </param>
public DataCollectorMetadata(string extension, string friendlyName)
: this(extension, friendlyName, false)
{ }

/// <summary>
/// Constructor for DataCollectorMetadata
/// </summary>
/// <param name="extension">
/// Uri identifying the data collector.
/// </param>
/// <param name="friendlyName">
/// The friendly Name.
/// <param name="hasAttachmentProcessor">
/// Indicates if the current data collector registers an attachment processor
/// </param>
public DataCollectorMetadata(string extension, string friendlyName, bool hasAttachmentProcessor)
{
this.ExtensionUri = extension;
this.FriendlyName = friendlyName;
this.HasAttachmentProcessor = hasAttachmentProcessor;
}

/// <summary>
Expand All @@ -98,5 +139,14 @@ public string FriendlyName
get;
private set;
}

/// <summary>
/// Check if the data collector has got attachment processor registered
/// </summary>
public bool HasAttachmentProcessor
{
get;
private set;
}
}
}
Loading