Skip to content

Commit

Permalink
Fix satellite resolution for Microsoft.TestPlatform.Common (#4147) (#…
Browse files Browse the repository at this point in the history
…4150)

Fix satellite resolution for Microsoft.TestPlatform.Common
  • Loading branch information
MarcoRossignoli committed Nov 29, 2022
1 parent 3789e2b commit 31e8b95
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 64 deletions.
Expand Up @@ -463,6 +463,15 @@ private static bool TryMergeExtensionPaths(List<string> extensionsList, List<str

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
Expand Up @@ -122,71 +122,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

0 comments on commit 31e8b95

Please sign in to comment.