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

Fix satellite resolution for Microsoft.TestPlatform.Common #4147

Merged
merged 2 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,15 @@ private Dictionary<string, TPluginInfo> GetTestExtensions<TPluginInfo, TExtensio

protected void SetupAssemblyResolver(string? extensionAssembly)
{
// If we don't load the resource for Microsoft.TestPlatform.Common before
// to set the assembly resolver we won't be able to use the resources.
// This should be the algorithm followed during the satellite assembly resolution
// https://learn.microsoft.com/dotnet/core/extensions/package-and-deploy-resources#net-framework-resource-fallback-process
// BUT for some unknown reason the point 10 is not working as explained.
// Satellite resolution should fallback to the NeutralResourcesLanguageAttribute
// that we set to en-US but don't and we fail with FileNotFoundException.
_ = Resources.Resources.FailedToLoadAdapaterFile;

IList<string> resolutionPaths = extensionAssembly.IsNullOrEmpty()
? GetDefaultResolutionPaths()
: GetResolutionPaths(extensionAssembly);
Expand Down
65 changes: 1 addition & 64 deletions src/Microsoft.TestPlatform.Common/Utilities/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,71 +123,8 @@ internal void AddSearchDirectories(IEnumerable<string> directories)

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

// 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)
foreach (var dir in _searchDirectories)
{
if (dir.IsNullOrEmpty())
{
Expand Down