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

Abstracting Assembly Resolver #913

Merged
merged 11 commits into from Jul 19, 2017
Expand Up @@ -10,16 +10,16 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework
using System.Reflection;
using System.Text.RegularExpressions;

#if NET46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need System.Threading for net46 only?

using System.Threading;
#endif

using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
#if NET46
using System.Threading;
#else
using System.Runtime.Loader;
#endif

/// <summary>
/// The test plugin cache.
Expand All @@ -29,7 +29,6 @@ public class TestPluginCache
{
#region Private Members

private readonly Dictionary<string, Assembly> resolvedAssemblies;
private readonly IFileHelper fileHelper;

/// <summary>
Expand All @@ -45,7 +44,7 @@ public class TestPluginCache
/// <summary>
/// Assembly resolver used to resolve the additional extensions
/// </summary>
private AssemblyResolver assemblyResolver;
private IAssemblyResolver assemblyResolver;

/// <summary>
/// Lock for extensions update
Expand All @@ -68,7 +67,6 @@ public class TestPluginCache
/// </param>
protected TestPluginCache(IFileHelper fileHelper)
{
this.resolvedAssemblies = new Dictionary<string, Assembly>();
this.pathToExtensions = null;
this.loadOnlyWellKnownExtensions = false;
this.lockForExtensionsUpdate = new object();
Expand Down Expand Up @@ -158,11 +156,8 @@ public bool LoadOnlyWellKnownExtensions
// and that succeeds.
// Because of this assembly failure, below domain.CreateInstanceAndUnwrap() call fails with error
// "Unable to cast transparent proxy to type 'Microsoft.VisualStudio.TestPlatform.Core.TestPluginsFramework.TestPluginDiscoverer"
#if NET46
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomainAssemblyResolve);
#else
AssemblyLoadContext.Default.Resolving += this.CurrentDomainAssemblyResolve;
#endif
this.assemblyResolver.SetCurrentDomainAssemblyResolve();

try
{
EqtTrace.Verbose("TestPluginCache: Discovering the extensions using extension path.");
Expand Down Expand Up @@ -218,17 +213,7 @@ public bool LoadOnlyWellKnownExtensions
}
finally
{
#if NET46
AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomainAssemblyResolve);
#else
AssemblyLoadContext.Default.Resolving -= this.CurrentDomainAssemblyResolve;
#endif

// clear the assemblies
lock (this.resolvedAssemblies)
{
this.resolvedAssemblies?.Clear();
}
this.assemblyResolver.RemoveCurrentDomainAssemblyResolve();
}

return pluginInfos;
Expand Down Expand Up @@ -274,7 +259,6 @@ public void UpdateExtensions(IEnumerable<string> additionalExtensionsPath, bool
// directory. The path to nuget directory is automatically setup for CLR to resolve.
// Test platform tries to load every extension by assembly name. If it is not resolved, we don't
// an error.

if (this.pathToExtensions != null)
{
extensions.AddRange(this.pathToExtensions);
Expand Down Expand Up @@ -316,6 +300,7 @@ internal IEnumerable<string> DefaultExtensionPaths
{
return this.defaultExtensionPaths;
}

set
{
if (value != null)
Expand Down Expand Up @@ -501,55 +486,14 @@ private void SetupAssemblyResolver(string extensionAssembly)
// Add assembly resolver which can resolve the extensions from the specified directory.
if (this.assemblyResolver == null)
{
this.assemblyResolver = new AssemblyResolver(resolutionPaths);
this.assemblyResolver = new AssemblyResolver(resolutionPaths, EqtTrace.PlatformTrace);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AssemblyResolver just requires a way to LogMessage(level, message). Why pass the platform trace to it?

}
else
{
this.assemblyResolver.AddSearchDirectories(resolutionPaths);
}
}

#if NET46
private Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
#else
private Assembly CurrentDomainAssemblyResolve(AssemblyLoadContext loadContext, AssemblyName args)
#endif
{
var assemblyName = new AssemblyName(args.Name);

Assembly assembly = null;
lock (this.resolvedAssemblies)
{
try
{
EqtTrace.Verbose("CurrentDomain_AssemblyResolve: Resolving assembly '{0}'.", args.Name);

if (this.resolvedAssemblies.TryGetValue(args.Name, out assembly))
{
return assembly;
}

// Put it in the resolved assembly so that if below Assembly.Load call
// triggers another assembly resolution, then we dont end up in stack overflow
this.resolvedAssemblies[args.Name] = null;

assembly = Assembly.Load(assemblyName);

// Replace the value with the loaded assembly
this.resolvedAssemblies[args.Name] = assembly;

return assembly;
}
finally
{
if (null == assembly)
{
EqtTrace.Verbose("CurrentDomainAssemblyResolve: Failed to resolve assembly '{0}'.", args.Name);
}
}
}
}

/// <summary>
/// Log the extensions
/// </summary>
Expand Down
Expand Up @@ -13,9 +13,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework

using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
#if !NET46
using System.Runtime.Loader;
#endif

/// <summary>
/// Discovers test extensions in a directory.
Expand Down Expand Up @@ -133,11 +130,11 @@ internal class TestPluginDiscoverer
Debug.Assert(pluginInfos != null, "null pluginInfos");

// TODO: Do not see why loadOnlyWellKnowExtensions is still needed.
//AssemblyName executingAssemblyName = null;
//if (loadOnlyWellKnownExtensions)
//{
// AssemblyName executingAssemblyName = null;
// if (loadOnlyWellKnownExtensions)
// {
// executingAssemblyName = new AssemblyName(typeof(TestPluginDiscoverer).GetTypeInfo().Assembly.FullName);
//}
// }

// Scan each of the files for data extensions.
foreach (var file in files)
Expand All @@ -153,15 +150,15 @@ internal class TestPluginDiscoverer
}

// Check whether this assembly is known or not.
//if (loadOnlyWellKnownExtensions && assembly != null)
//{
// if (loadOnlyWellKnownExtensions && assembly != null)
// {
// var extensionAssemblyName = new AssemblyName(assembly.FullName);
// if (!AssemblyUtilities.PublicKeyTokenMatches(extensionAssemblyName, executingAssemblyName))
// {
// EqtTrace.Warning("TestPluginDiscoverer: Ignoring extensions in assembly {0} as it is not a known assembly.", assembly.FullName);
// continue;
// }
//}
// }
}
catch (Exception e)
{
Expand Down
19 changes: 16 additions & 3 deletions src/Microsoft.TestPlatform.CoreUtilities/Tracing/EqtTrace.cs
Expand Up @@ -64,6 +64,14 @@ public static PlatformTraceLevel TraceLevel
}
#endif

public static IPlatformEqtTrace PlatformTrace
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to provide this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

{
get
{
return traceImpl;
}
}

public static string LogFile
{
get
Expand Down Expand Up @@ -126,18 +134,23 @@ public static bool IsWarningEnabled
/// Initializes the verbose tracing with custom log file
/// And overrides if any trace is set before
/// </summary>
/// <param name="customLogFile">A custom log file for trace messages.</param>
/// <param name="customLogFile">
/// A custom log file for trace messages.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public static bool InitializeVerboseTrace(string customLogFile)
{
if(!traceImpl.InitializeVerboseTrace(customLogFile))
if (!traceImpl.InitializeVerboseTrace(customLogFile))
{
ErrorOnInitialization = PlatformEqtTrace.ErrorOnInitialization;
return false;
}

return true;
}


/// <summary>
/// Prints an error message and prompts with a Debug dialog
/// </summary>
Expand Down
@@ -0,0 +1,30 @@
// 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.TestPlatform.PlatformAbstractions.Interfaces
{
using System;
using System.Collections.Generic;

/// <summary>
/// The AssemblyResolver interface.
/// </summary>
public interface IAssemblyResolver : IDisposable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assembly resolution is probably a runtime aspect (and not a system wide aspect). Suggest to move to Interfaces/Runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

{
/// <summary>
/// Sets up the Assembly resovler for current Appdomain
/// </summary>
void SetCurrentDomainAssemblyResolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Require documentation.


/// <summary>
/// Removes the Assembly resolver from current Appdomain
/// </summary>
void RemoveCurrentDomainAssemblyResolve();

/// <summary>
/// Add the probing directories look for in case of Aseembly Resolve Event
/// </summary>
/// <param name="directories"></param>
void AddSearchDirectories(IEnumerable<string> directories);
}
}
@@ -1,34 +1,12 @@
namespace Microsoft.VisualStudio.TestPlatform.ObjectModel
{
//
// Summary:
// Specifies what messages to output for the System.Diagnostics.Debug, System.Diagnostics.Trace
// and System.Diagnostics.TraceSwitch classes.
public enum PlatformTraceLevel
{
//
// Summary:
// Output no tracing and debugging messages.
Off = 0,
//
// Summary:
// Output error-handling messages.
Error = 1,
//
// Summary:
// Output warnings and error-handling messages.
Warning = 2,
//
// Summary:
// Output informational messages, warnings, and error-handling messages.
Info = 3,
//
// Summary:
// Output all debugging and tracing messages.
Verbose = 4
}

// 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.TestPlatform.ObjectModel
{
/// <summary>
/// Specifies what messages to output for the System.Diagnostics.Debug, System.Diagnostics.Trace
/// and System.Diagnostics.TraceSwitch classes.
/// </summary>
public partial interface IPlatformEqtTrace
{
/// <summary>
Expand All @@ -44,7 +22,12 @@ public partial interface IPlatformEqtTrace
/// Initializes the verbose tracing with custom log file
/// And overrides if any trace is set before
/// </summary>
/// <param name="customLogFile">A custom log file for trace messages.</param>
/// <param name="customLogFile">
/// A custom log file for trace messages.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
bool InitializeVerboseTrace(string customLogFile);

/// <summary>
Expand All @@ -63,12 +46,17 @@ public partial interface IPlatformEqtTrace
/// <summary>
/// Sets platfrom specific trace value for tracing verbosity.
/// </summary>
/// <param name="traceLevel">PlatformTraceLevel.</param>
/// <param name="value">
/// The value.
/// </param>
void SetTraceLevel(PlatformTraceLevel value);

/// <summary>
/// Gets platfrom specific trace value for tracing verbosity.
/// </summary>
/// <returns>
/// The <see cref="PlatformTraceLevel"/>.
/// </returns>
PlatformTraceLevel GetTraceLevel();
}
}
@@ -0,0 +1,33 @@
// 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.TestPlatform.ObjectModel
{
public enum PlatformTraceLevel
{
/// <summary>
/// Output no tracing and debugging messages..
/// </summary>
Off = 0,

/// <summary>
/// Output error-handling messages.
/// </summary>
Error = 1,

/// <summary>
/// Output warnings and error-handling messages.
/// </summary>
Warning = 2,

/// <summary>
/// Output informational messages, warnings, and error-handling messages..
/// </summary>
Info = 3,

/// <summary>
/// Output all debugging and tracing messages..
/// </summary>
Verbose = 4
}
}
Binary file not shown.