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

Pass coverlet codebase in runsettings for inproc data collector initialization #2288

Merged
merged 11 commits into from
Jan 30, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ public void ClearExtensions()
this.TestExtensions?.InvalidateCache();
}

/// <summary>
/// Add search directories to assembly resolver
/// </summary>
/// <param name="directories"></param>
public void AddResolverSearchDirectories(string[] directories)
{
assemblyResolver.AddSearchDirectories(directories);
}

#endregion

#region Utility methods
Expand Down Expand Up @@ -486,7 +495,7 @@ private Dictionary<string, TPluginInfo> GetTestExtensions<TPluginInfo, TExtensio
return discoverer.GetTestExtensionsInformation<TPluginInfo, TExtension>(extensionPaths);
}

private void SetupAssemblyResolver(string extensionAssembly)
protected void SetupAssemblyResolver(string extensionAssembly)
{
IList<string> resolutionPaths;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ internal class ProxyExecutionManager : ProxyOperationManager, IProxyExecutionMan
private bool skipDefaultAdapters;
private readonly IFileHelper fileHelper;

// Only send coverlet inproc datacollector dll to be initialized via testhost,
// ideally this should get initialized via InProcessDC Node in runsettings, but
// somehow it is failing, hence putting this ugly HACK, to fix issues like
// https://developercommunity.visualstudio.com/content/problem/738856/could-not-load-file-or-assembly-microsoftintellitr.html
private const string CoverletDataCollector = "coverlet.collector.dll";

/// <inheritdoc/>
public bool IsInitialized { get; private set; } = false;

Expand All @@ -54,7 +48,7 @@ internal class ProxyExecutionManager : ProxyOperationManager, IProxyExecutionMan
/// <param name="requestData">The Request Data for providing services and data for Run.</param>
/// <param name="requestSender">Test request sender instance.</param>
/// <param name="testHostManager">Test host manager for this proxy.</param>
public ProxyExecutionManager(IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager) :
public ProxyExecutionManager(IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager) :
this(requestData, requestSender, testHostManager, JsonDataSerializer.Instance, new FileHelper())
{
}
Expand Down Expand Up @@ -108,7 +102,7 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH
{
EqtTrace.Verbose("ProxyExecutionManager: Test host is always Lazy initialize.");
}

var testSources = new List<string>(testRunCriteria.HasSpecificSources ? testRunCriteria.Sources :
// If the test execution is with a test filter, group them by sources
testRunCriteria.Tests.GroupBy(tc => tc.Source).Select(g => g.Key));
Expand All @@ -118,7 +112,7 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH
if (this.isCommunicationEstablished)
{
this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested();

this.InitializeExtensions(testSources);

// This code should be in sync with InProcessProxyExecutionManager.StartTestRun executionContext
Expand Down Expand Up @@ -181,7 +175,7 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH
public virtual void Cancel(ITestRunEventsHandler eventHandler)
{
// Just in case ExecuteAsync isn't called yet, set the eventhandler
if(this.baseTestRunEventsHandler == null)
if (this.baseTestRunEventsHandler == null)
{
this.baseTestRunEventsHandler = eventHandler;
}
Expand All @@ -207,7 +201,7 @@ public virtual int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testPr
public void Abort(ITestRunEventsHandler eventHandler)
{
// Just in case ExecuteAsync isn't called yet, set the eventhandler
if(this.baseTestRunEventsHandler == null)
if (this.baseTestRunEventsHandler == null)
{
this.baseTestRunEventsHandler = eventHandler;
}
Expand Down Expand Up @@ -238,7 +232,7 @@ public void HandleRawMessage(string rawMessage)
{
var message = this.dataSerializer.DeserializeMessage(rawMessage);

if(string.Equals(message.MessageType, MessageType.ExecutionComplete))
if (string.Equals(message.MessageType, MessageType.ExecutionComplete))
{
this.Close();
}
Expand Down Expand Up @@ -267,9 +261,6 @@ private void LogMessage(TestMessageLevel testMessageLevel, string message)
private void InitializeExtensions(IEnumerable<string> sources)
{
var extensions = TestPluginCache.Instance.GetExtensionPaths(TestPlatformConstants.TestAdapterEndsWithPattern, this.skipDefaultAdapters);

// remove this line once we figure out why coverlet inproc DC is not initialized via runsetting inproc node.
extensions = extensions.Concat(TestPluginCache.Instance.GetExtensionPaths(ProxyExecutionManager.CoverletDataCollector, true)).ToList();

// Filter out non existing extensions
var nonExistingExtensions = extensions.Where(extension => !this.fileHelper.Exists(extension));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection
{
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
Expand Down Expand Up @@ -45,7 +46,7 @@ public InProcDataCollector(
string assemblyQualifiedName,
TypeInfo interfaceTypeInfo,
string configXml)
: this(codeBase, assemblyQualifiedName, interfaceTypeInfo, configXml, new PlatformAssemblyLoadContext())
: this(codeBase, assemblyQualifiedName, interfaceTypeInfo, configXml, new PlatformAssemblyLoadContext(), TestPluginCache.Instance)
{
}

Expand All @@ -62,7 +63,7 @@ public InProcDataCollector(
/// </param>
/// <param name="assemblyLoadContext">
/// </param>
internal InProcDataCollector(string codeBase, string assemblyQualifiedName, TypeInfo interfaceTypeInfo, string configXml, IAssemblyLoadContext assemblyLoadContext)
internal InProcDataCollector(string codeBase, string assemblyQualifiedName, TypeInfo interfaceTypeInfo, string configXml, IAssemblyLoadContext assemblyLoadContext, TestPluginCache testPluginCache)
{
this.configXml = configXml;
this.assemblyLoadContext = assemblyLoadContext;
Expand All @@ -75,6 +76,10 @@ internal InProcDataCollector(string codeBase, string assemblyQualifiedName, Type
// If we're loading coverlet collector we skip to check the version of assembly
// to allow upgrade throught nuget package
filterPredicate = (x) => x.FullName.Equals(Constants.CoverletDataCollectorTypeName) && interfaceTypeInfo.IsAssignableFrom(x.GetTypeInfo());

// Coverlet collector is consumed as nuget package we need to add assemblies directory to resolver to correctly load references.
Debug.Assert(Path.IsPathRooted(codeBase), "Absolute path expected");
testPluginCache.AddResolverSearchDirectories(new string[] { Path.GetDirectoryName(codeBase) });
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public class DotnetTestHostManager : ITestRuntimeProvider
private const string DotnetTestHostUri = "HostProvider://DotnetTestHost";
private const string DotnetTestHostFriendlyName = "DotnetTestHost";
private const string TestAdapterRegexPattern = @"TestAdapter.dll";
private const string CoverletDataCollectorRegexPattern = @"coverlet.collector.dll";

private IDotnetHostHelper dotnetHostHelper;
private IEnvironment platformEnvironment;
Expand Down Expand Up @@ -316,11 +315,6 @@ public IEnumerable<string> GetTestPlatformExtensions(IEnumerable<string> sources
extensionPaths.AddRange(this.fileHelper.EnumerateFiles(sourceDirectory, SearchOption.TopDirectoryOnly, TestAdapterRegexPattern));
}

if (extensions != null && extensions.Any())
{
extensionPaths.AddRange(extensions.Where(x => x.EndsWith(CoverletDataCollectorRegexPattern, StringComparison.OrdinalIgnoreCase)));
}

return extensionPaths;
}

Expand Down
45 changes: 34 additions & 11 deletions src/vstest.console/Processors/CollectArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors
using System.Collections.Generic;

using System.Globalization;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.Common;
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources;

/// <summary>
Expand Down Expand Up @@ -58,7 +61,7 @@ public Lazy<IArgumentExecutor> Executor
{
if (this.executor == null)
{
this.executor = new Lazy<IArgumentExecutor>(() => new CollectArgumentExecutor(RunSettingsManager.Instance));
this.executor = new Lazy<IArgumentExecutor>(() => new CollectArgumentExecutor(RunSettingsManager.Instance, new FileHelper()));
}

return this.executor;
Expand All @@ -71,7 +74,7 @@ public Lazy<IArgumentExecutor> Executor
}
}


internal class CollectArgumentProcessorCapabilities : BaseArgumentProcessorCapabilities
{
public override string CommandName => CollectArgumentProcessor.CommandName;
Expand All @@ -90,11 +93,13 @@ internal class CollectArgumentProcessorCapabilities : BaseArgumentProcessorCapab
/// <inheritdoc />
internal class CollectArgumentExecutor : IArgumentExecutor
{
private IRunSettingsProvider runSettingsManager;
private readonly IRunSettingsProvider runSettingsManager;
private readonly IFileHelper fileHelper;
internal static List<string> EnabledDataCollectors = new List<string>();
internal CollectArgumentExecutor(IRunSettingsProvider runSettingsManager)
internal CollectArgumentExecutor(IRunSettingsProvider runSettingsManager, IFileHelper fileHelper)
{
this.runSettingsManager = runSettingsManager;
this.fileHelper = fileHelper;
}

/// <inheritdoc />
Expand All @@ -113,11 +118,29 @@ public void Initialize(string argument)
argument));
}

if(InferRunSettingsHelper.IsTestSettingsEnabled(this.runSettingsManager.ActiveRunSettings.SettingsXml))
if (InferRunSettingsHelper.IsTestSettingsEnabled(this.runSettingsManager.ActiveRunSettings.SettingsXml))
{
throw new SettingsException(string.Format(CommandLineResources.CollectWithTestSettingErrorMessage, argument));
}
AddDataCollectorToRunSettings(argument, this.runSettingsManager);
AddDataCollectorToRunSettings(argument, this.runSettingsManager, this.fileHelper);
}

/// <summary>
/// Returns coverlet codebase searching coverlet.collector.dll assembly inside adaptersPaths
/// </summary>
private static string GetCoverletCodeBasePath(IRunSettingsProvider runSettingProvider, IFileHelper fileHelper)
{
foreach (string adapterPath in RunSettingsUtilities.GetTestAdaptersPaths(runSettingProvider.ActiveRunSettings.SettingsXml))
{
string collectorPath = Path.Combine(adapterPath, CoverletConstants.CoverletDataCollectorCodebase);
if (fileHelper.Exists(collectorPath))
{
EqtTrace.Verbose("CoverletDataCollector in-process codeBase path '{0}'", collectorPath);
return collectorPath;
}
}

return null;
}

/// <inheritdoc />
Expand Down Expand Up @@ -146,7 +169,7 @@ internal static void EnableDataCollectorUsingFriendlyName(string argument, DataC
/// <summary>
/// Enables coverlet inproc datacollector
/// </summary>
internal static void EnableCoverletInProcDataCollector(string argument, DataCollectionRunSettings dataCollectionRunSettings)
internal static void EnableCoverletInProcDataCollector(string argument, DataCollectionRunSettings dataCollectionRunSettings, IRunSettingsProvider runSettingProvider, IFileHelper fileHelper)
{
DataCollectorSettings dataCollectorSettings = null;

Expand All @@ -156,7 +179,7 @@ internal static void EnableCoverletInProcDataCollector(string argument, DataColl
dataCollectorSettings = new DataCollectorSettings();
dataCollectorSettings.FriendlyName = argument;
dataCollectorSettings.AssemblyQualifiedName = CoverletConstants.CoverletDataCollectorAssemblyQualifiedName;
dataCollectorSettings.CodeBase = CoverletConstants.CoverletDataCollectorCodebase;
dataCollectorSettings.CodeBase = GetCoverletCodeBasePath(runSettingProvider, fileHelper) ?? CoverletConstants.CoverletDataCollectorCodebase;
dataCollectorSettings.IsEnabled = true;
dataCollectionRunSettings.DataCollectorSettingsList.Add(dataCollectorSettings);
}
Expand Down Expand Up @@ -186,7 +209,7 @@ private static bool DoesDataCollectorSettingsExist(string friendlyName,
return false;
}

internal static void AddDataCollectorToRunSettings(string argument, IRunSettingsProvider runSettingsManager)
internal static void AddDataCollectorToRunSettings(string argument, IRunSettingsProvider runSettingsManager, IFileHelper fileHelper)
{
EnabledDataCollectors.Add(argument.ToLower());

Expand All @@ -198,7 +221,7 @@ internal static void AddDataCollectorToRunSettings(string argument, IRunSettings
}

var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settings) ?? new DataCollectionRunSettings();
var inProcDataCollectionRunSettings = XmlRunSettingsUtilities.GetInProcDataCollectionRunSettings(settings)
var inProcDataCollectionRunSettings = XmlRunSettingsUtilities.GetInProcDataCollectionRunSettings(settings)
?? new DataCollectionRunSettings(
Constants.InProcDataCollectionRunSettingsName,
Constants.InProcDataCollectorsSettingName,
Expand All @@ -212,7 +235,7 @@ internal static void AddDataCollectorToRunSettings(string argument, IRunSettings
if (string.Equals(argument, CoverletConstants.CoverletDataCollectorFriendlyName, StringComparison.OrdinalIgnoreCase))
{
// Add inproc data collector to runsetings if coverlet code coverage is enabled
EnableCoverletInProcDataCollector(argument, inProcDataCollectionRunSettings);
EnableCoverletInProcDataCollector(argument, inProcDataCollectionRunSettings, runSettingsManager, fileHelper);
runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.InProcDataCollectionRunSettingsName, inProcDataCollectionRunSettings.ToXml().InnerXml);
}
}
Expand Down
15 changes: 11 additions & 4 deletions src/vstest.console/Processors/EnableBlameArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Utilities;

using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources;

internal class EnableBlameArgumentProcessor : IArgumentProcessor
Expand Down Expand Up @@ -61,7 +62,7 @@ public Lazy<IArgumentExecutor> Executor
{
if (this.executor == null)
{
this.executor = new Lazy<IArgumentExecutor>(() => new EnableBlameArgumentExecutor(RunSettingsManager.Instance, new PlatformEnvironment()));
this.executor = new Lazy<IArgumentExecutor>(() => new EnableBlameArgumentExecutor(RunSettingsManager.Instance, new PlatformEnvironment(), new FileHelper()));
}

return this.executor;
Expand Down Expand Up @@ -111,13 +112,19 @@ internal class EnableBlameArgumentExecutor : IArgumentExecutor
/// </summary>
private IEnvironment environment;

/// <summary>
/// For file related operation
/// </summary>
private readonly IFileHelper fileHelper;

#region Constructor

internal EnableBlameArgumentExecutor(IRunSettingsProvider runSettingsManager, IEnvironment environment)
internal EnableBlameArgumentExecutor(IRunSettingsProvider runSettingsManager, IEnvironment environment, IFileHelper fileHelper)
{
this.runSettingsManager = runSettingsManager;
this.environment = environment;
this.Output = ConsoleOutput.Instance;
this.fileHelper = fileHelper;
}

#endregion
Expand Down Expand Up @@ -182,7 +189,7 @@ private void InitializeBlame(bool enableDump, Dictionary<string, string> collect
LoggerUtilities.AddLoggerToRunSettings(BlameFriendlyName, null, this.runSettingsManager);

// Add Blame Data Collector
CollectArgumentExecutor.AddDataCollectorToRunSettings(BlameFriendlyName, this.runSettingsManager);
CollectArgumentExecutor.AddDataCollectorToRunSettings(BlameFriendlyName, this.runSettingsManager, this.fileHelper);


// Add default run settings if required.
Expand Down
Loading