Skip to content

Commit ec2dca6

Browse files
Update logic code and unit test
1 parent 8a4c5dd commit ec2dca6

File tree

2 files changed

+38
-39
lines changed

2 files changed

+38
-39
lines changed

src/Build.UnitTests/BackEnd/SdkResolverLoader_Tests.cs

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -415,21 +415,17 @@ public void LoadResolverAssembly_MSBuildSdkResolver_WithAndWithoutFallback(bool
415415
var assemblyFile = Path.Combine(resolverFolder, "Microsoft.DotNet.MSBuildSdkResolver.dll");
416416

417417
// Create file based on test scenario
418-
if (shouldSucceed)
419-
{
420-
// For fallback test: copy test assembly (which is already loaded) instead of Microsoft.Build.dll
421-
// This reduces side effects because the test assembly is already in the load context
422-
var testAssembly = typeof(SdkResolverLoader_Tests).Assembly;
423-
File.Copy(testAssembly.Location, assemblyFile, true);
424-
}
425-
else
418+
// For shouldSucceed=false: create invalid file to test Assembly.Load failure
419+
// For shouldSucceed=true: we don't create a file - we'll simulate success in the mock
420+
// to avoid side effects from loading Microsoft.Build.dll copy
421+
if (!shouldSucceed)
426422
{
427423
// For no-fallback test: create invalid assembly content to force Assembly.Load to fail
428424
File.WriteAllText(assemblyFile, "invalid assembly content");
429425
}
430426

431-
// Use MockSdkResolverLoader to test actual Assembly.LoadFrom behavior
432-
// but with test assembly (already loaded) instead of Microsoft.Build.dll to reduce side effects
427+
// Use MockSdkResolverLoader to simulate behavior without modifying global state
428+
// We avoid actually calling Assembly.LoadFrom with Microsoft.Build.dll copy to prevent side effects
433429
var loader = new MockSdkResolverLoader
434430
{
435431
FindPotentialSdkResolversFunc = (_, __) => new List<string> { assemblyFile },
@@ -441,10 +437,13 @@ public void LoadResolverAssembly_MSBuildSdkResolver_WithAndWithoutFallback(bool
441437
{
442438
// Capture test parameters via closure
443439
bool simulatedIsRunningInVS = isRunningInVS;
440+
bool simulatedShouldSucceed = shouldSucceed;
444441

445442
if (simulatedIsRunningInVS)
446443
{
447444
// VS behavior: try Assembly.Load directly, no fallback
445+
// We only call Assembly.Load for invalid file case (shouldSucceed=false)
446+
// to test that exception propagates correctly
448447
AssemblyName assemblyName = new AssemblyName(resolverFileName)
449448
{
450449
CodeBase = resolverPath,
@@ -455,21 +454,20 @@ public void LoadResolverAssembly_MSBuildSdkResolver_WithAndWithoutFallback(bool
455454
else
456455
{
457456
// Non-VS behavior: try Assembly.Load first, fallback to LoadFrom if it fails
458-
// We actually call Assembly.LoadFrom here but with test assembly (already loaded)
459-
// to reduce side effects compared to loading Microsoft.Build.dll copy
460-
try
457+
// We simulate this without actually calling Assembly.Load or Assembly.LoadFrom
458+
// to avoid side effects from loading Microsoft.Build.dll copy
459+
if (simulatedShouldSucceed)
461460
{
462-
// Try Assembly.Load first (will fail for invalid file, succeed for valid)
463-
AssemblyName assemblyName = new AssemblyName(resolverFileName)
464-
{
465-
CodeBase = resolverPath,
466-
};
467-
return Assembly.Load(assemblyName);
461+
// Simulate successful fallback: return an existing assembly
462+
// We use an assembly that's already loaded and contains a valid resolver type
463+
// to avoid side effects from loading Microsoft.Build.dll copy
464+
return typeof(MockSdkResolverWithAssemblyPath).Assembly;
468465
}
469-
catch (Exception)
466+
else
470467
{
471-
// Fallback to LoadFrom
472-
return Assembly.LoadFrom(resolverPath);
468+
// This branch shouldn't be reached in non-VS + shouldSucceed=false case
469+
// but if it is, simulate Assembly.Load failure
470+
throw new BadImageFormatException("Assembly could not be loaded");
473471
}
474472
}
475473
}
@@ -608,7 +606,5 @@ protected override void LoadResolvers(string resolverPath, ElementLocation locat
608606
base.LoadResolvers(resolverPath, location, resolvers);
609607
}
610608
}
611-
612-
// Removed TestableSdkResolverLoader; tests now set environment directly and use MockSdkResolverLoader
613609
}
614610
}

src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -239,15 +239,18 @@ protected virtual Assembly LoadResolverAssembly(string resolverPath)
239239
// This will load the resolver assembly into the default load context if possible, and fall back to LoadFrom context.
240240
// We very much prefer the default load context because it allows native images to be used by the CLR, improving startup perf.
241241
bool isRunningInVS = BuildEnvironmentHelper.Instance.RunningInVisualStudio;
242-
if (!isRunningInVS)
242+
AssemblyName assemblyName = CreateAssemblyNameWithCodeBase(resolverFileName, resolverPath);
243+
if (isRunningInVS)
244+
{
245+
// Inside VS use optimization without a fallback. VS environment should not fail loading.
246+
// If for some reason it fails, we will want to find it out rather than silently catch the exception and allow a performance regression.
247+
return Assembly.Load(assemblyName);
248+
}
249+
else
243250
{
244251
// Apply compatibility fallback for external API users
245252
try
246253
{
247-
AssemblyName assemblyName = new AssemblyName(resolverFileName)
248-
{
249-
CodeBase = resolverPath,
250-
};
251254
return Assembly.Load(assemblyName);
252255
}
253256
catch (Exception)
@@ -256,16 +259,6 @@ protected virtual Assembly LoadResolverAssembly(string resolverPath)
256259
return Assembly.LoadFrom(resolverPath);
257260
}
258261
}
259-
else
260-
{
261-
// Inside VS: use original optimization (no fallback)
262-
// If it fails, let it fail - VS environment should work
263-
AssemblyName assemblyName = new AssemblyName(resolverFileName)
264-
{
265-
CodeBase = resolverPath,
266-
};
267-
return Assembly.Load(assemblyName);
268-
}
269262
}
270263
}
271264
return Assembly.LoadFrom(resolverPath);
@@ -274,6 +267,16 @@ protected virtual Assembly LoadResolverAssembly(string resolverPath)
274267
#endif
275268
}
276269

270+
#if !FEATURE_ASSEMBLYLOADCONTEXT
271+
private AssemblyName CreateAssemblyNameWithCodeBase(string assemblyName, string codeBase)
272+
{
273+
return new AssemblyName(assemblyName)
274+
{
275+
CodeBase = codeBase,
276+
};
277+
}
278+
#endif
279+
277280
protected internal virtual IReadOnlyList<SdkResolver> LoadResolversFromManifest(SdkResolverManifest manifest, ElementLocation location)
278281
{
279282
MSBuildEventSource.Log.SdkResolverLoadResolversStart();

0 commit comments

Comments
 (0)