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 custom satellite assemblies resolution for Microsoft.VisualStudio.TestPlatform* #4133

Merged
merged 4 commits into from
Nov 23, 2022
Merged
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
68 changes: 66 additions & 2 deletions src/Microsoft.TestPlatform.Common/Utilities/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;

using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
Expand Down Expand Up @@ -122,7 +123,71 @@ internal void AddSearchDirectories(IEnumerable<string> directories)

TPDebug.Assert(requestedName != null && !requestedName.Name.IsNullOrEmpty(), "AssemblyResolver.OnResolve: requested is null or name is empty!");

foreach (var dir in _searchDirectories)
// Workaround: adding expected folder for the satellite assembly related to the current CurrentThread.CurrentUICulture relative to the current assembly location.
// After the move to the net461 the runtime doesn't resolve anymore the satellite assembly correctly.
// The expected workflow should be https://learn.microsoft.com/en-us/dotnet/core/extensions/package-and-deploy-resources#net-framework-resource-fallback-process
// But the resolution never fallback to the CultureInfo.Parent folder and fusion log return a failure like:
// ...
// LOG: The same bind was seen before, and was failed with hr = 0x80070002.
// ERR: Unrecoverable error occurred during pre - download check(hr = 0x80070002).
// ...
// The bizarre thing is that as a result we're failing caller task like discovery and when for reporting reason
// we're accessing again to the resource it works.
// Looks like a loading timing issue but we're not in control of the assembly loader order.
var isResource = requestedName.Name.EndsWith(".resources");
string[]? satelliteLocation = null;

// We help to resolve only test platform resources to be less invasive as possible with the default/expected behavior
if (isResource && requestedName.Name.StartsWith("Microsoft.VisualStudio.TestPlatform"))
{
try
{
string? currentAssemblyLocation = null;
try
{
currentAssemblyLocation = Assembly.GetExecutingAssembly().Location;
// In .NET 5 and later versions, for bundled assemblies, the value returned is an empty string.
currentAssemblyLocation = currentAssemblyLocation == string.Empty ? null : Path.GetDirectoryName(currentAssemblyLocation);
}
catch (NotSupportedException)
{
// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.location
}

if (currentAssemblyLocation is not null)
{
List<string> satelliteLocations = new();

// We mimic the satellite workflow and we add CurrentUICulture and CurrentUICulture.Parent folder in order
string? currentUICulture = Thread.CurrentThread.CurrentUICulture?.Name;
if (currentUICulture is not null)
{
satelliteLocations.Add(Path.Combine(currentAssemblyLocation, currentUICulture));
}

// CurrentUICulture.Parent
string? parentCultureInfo = Thread.CurrentThread.CurrentUICulture?.Parent?.Name;
if (parentCultureInfo is not null)
{
satelliteLocations.Add(Path.Combine(currentAssemblyLocation, parentCultureInfo));
}

if (satelliteLocations.Count > 0)
{
satelliteLocation = satelliteLocations.ToArray();
}
}
}
catch (Exception ex)
{
// We catch here because this is a workaround, we're trying to substitute the expected workflow of the runtime
// and this shouldn't be needed, but if we fail we want to log what's happened and give a chance to the in place
// resolution workflow
EqtTrace.Error($"AssemblyResolver.OnResolve: Exception during the custom satellite resolution\n{ex}");
}
}

foreach (var dir in (satelliteLocation is not null) ? _searchDirectories.Union(satelliteLocation) : _searchDirectories)
{
if (dir.IsNullOrEmpty())
{
Expand All @@ -136,7 +201,6 @@ internal void AddSearchDirectories(IEnumerable<string> directories)
var assemblyPath = Path.Combine(dir, requestedName.Name + extension);
try
{
var isResource = requestedName.Name.EndsWith(".resources");
bool pushed = false;
try
{
Expand Down