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 ALC in ProjectReference scenarios #56662

Merged
merged 3 commits into from
Sep 27, 2021
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 @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -150,6 +151,44 @@ public void AssemblyLoading_DependencyLocationNotAdded()
Assert.Equal(@"", actual);
}

private static void VerifyAssemblies(IEnumerable<Assembly> assemblies, params (string simpleName, string version, string path)[] expected)
{
Assert.Equal(expected, assemblies.Select(assembly => (assembly.GetName().Name!, assembly.GetName().Version!.ToString(), assembly.Location)).Order());
}

[ConditionalFact(typeof(CoreClrOnly))]
public void AssemblyLoading_DependencyInDifferentDirectory()
{
StringBuilder sb = new StringBuilder();
var loader = new DefaultAnalyzerAssemblyLoader();

var tempDir = Temp.CreateDirectory();

var deltaFile = tempDir.CreateFile("Delta.dll").CopyContentFrom(_testFixture.Delta1.Path);
loader.AddDependencyLocation(deltaFile.Path);
loader.AddDependencyLocation(_testFixture.Gamma.Path);
Assembly gamma = loader.LoadFromPath(_testFixture.Gamma.Path);

var b = gamma.CreateInstance("Gamma.G")!;
var writeMethod = b.GetType().GetMethod("Write")!;
writeMethod.Invoke(b, new object[] { sb, "Test G" });

var actual = sb.ToString();
Assert.Equal(@"Delta: Gamma: Test G
Copy link
Member

Choose a reason for hiding this comment

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

Delta

How do we know which version of "Delta" was loaded? Was this the version from deltaFile.Path or from _textFixture.Delta1.Path (which seems like it might be the same path as _textFixture.Gamma.Path)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're asking which copy of Delta version 1, right? I think we can check what file paths the assemblies were loaded from using the TestAccessor, but I'd have to check.

", actual);

#if NETCOREAPP
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(1, alcs.Length);

VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "1.0.0.0", deltaFile.Path),
("Gamma", "0.0.0.0", _testFixture.Gamma.Path)
);
#endif
}

[Fact]
public void AssemblyLoading_MultipleVersions()
{
Expand All @@ -173,15 +212,16 @@ public void AssemblyLoading_MultipleVersions()
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(2, alcs.Length);

Assert.Equal(new[] {
VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "1.0.0.0", _testFixture.Delta1.Path),
("Gamma", "0.0.0.0", _testFixture.Gamma.Path)
}, alcs[0].Assemblies.Select(a => (a.GetName().Name!, a.GetName().Version!.ToString(), a.Location)).Order());
);

Assert.Equal(new[] {
VerifyAssemblies(
alcs[1].Assemblies,
("Delta", "2.0.0.0", _testFixture.Delta2.Path),
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path)
}, alcs[1].Assemblies.Select(a => (a.GetName().Name!, a.GetName().Version!.ToString(), a.Location)).Order());
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path));
#endif

var actual = sb.ToString();
Expand All @@ -203,6 +243,166 @@ public void AssemblyLoading_MultipleVersions()
}
}

[Fact]
public void AssemblyLoading_MultipleVersions_NoExactMatch()
{
StringBuilder sb = new StringBuilder();

var loader = new DefaultAnalyzerAssemblyLoader();
loader.AddDependencyLocation(_testFixture.Delta1.Path);
loader.AddDependencyLocation(_testFixture.Epsilon.Path);
loader.AddDependencyLocation(_testFixture.Delta3.Path);

Assembly epsilon = loader.LoadFromPath(_testFixture.Epsilon.Path);
var e = epsilon.CreateInstance("Epsilon.E")!;
e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" });

#if NETCOREAPP
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(1, alcs.Length);

VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "3.0.0.0", _testFixture.Delta3.Path),
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path));
#endif

var actual = sb.ToString();
if (ExecutionConditionUtil.IsCoreClr)
{
Assert.Equal(
@"Delta.3: Epsilon: Test E
",
actual);
}
else
{
Assert.Equal(
@"Delta: Epsilon: Test E
",
actual);
}
}

[Fact]
public void AssemblyLoading_MultipleVersions_MultipleEqualMatches()
{
StringBuilder sb = new StringBuilder();

// Delta2B and Delta2 have the same version, but we prefer Delta2 because it's in the same directory as Epsilon.
var loader = new DefaultAnalyzerAssemblyLoader();
loader.AddDependencyLocation(_testFixture.Delta2B.Path);
loader.AddDependencyLocation(_testFixture.Delta2.Path);
loader.AddDependencyLocation(_testFixture.Epsilon.Path);

Assembly epsilon = loader.LoadFromPath(_testFixture.Epsilon.Path);
var e = epsilon.CreateInstance("Epsilon.E")!;
e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" });

#if NETCOREAPP
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(1, alcs.Length);

VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "2.0.0.0", _testFixture.Delta2.Path),
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path));
#endif

var actual = sb.ToString();
if (ExecutionConditionUtil.IsCoreClr)
{
Assert.Equal(
@"Delta.2: Epsilon: Test E
",
actual);
}
else
{
Assert.Equal(
@"Delta: Epsilon: Test E
",
actual);
}
}

[Fact]
public void AssemblyLoading_MultipleVersions_ExactAndGreaterMatch()
{
StringBuilder sb = new StringBuilder();

var loader = new DefaultAnalyzerAssemblyLoader();
loader.AddDependencyLocation(_testFixture.Delta2B.Path);
loader.AddDependencyLocation(_testFixture.Delta3.Path);
loader.AddDependencyLocation(_testFixture.Epsilon.Path);

Assembly epsilon = loader.LoadFromPath(_testFixture.Epsilon.Path);
var e = epsilon.CreateInstance("Epsilon.E")!;
e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" });

#if NETCOREAPP
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(1, alcs.Length);

VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "2.0.0.0", _testFixture.Delta2B.Path),
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path));
#endif

var actual = sb.ToString();
if (ExecutionConditionUtil.IsCoreClr)
{
Assert.Equal(
@"Delta.2B: Epsilon: Test E
",
actual);
}
else
{
Assert.Equal(
@"Delta: Epsilon: Test E
",
actual);
}
}

[Fact]
public void AssemblyLoading_MultipleVersions_WorseMatchInSameDirectory()
{
StringBuilder sb = new StringBuilder();

var tempDir = Temp.CreateDirectory();
var epsilonFile = tempDir.CreateFile("Epsilon.dll").CopyContentFrom(_testFixture.Epsilon.Path);
var delta1File = tempDir.CreateFile("Delta.dll").CopyContentFrom(_testFixture.Delta1.Path);

// Epsilon wants Delta2, but since Delta1 is in the same directory, we prefer Delta1 over Delta2.
var loader = new DefaultAnalyzerAssemblyLoader();
loader.AddDependencyLocation(delta1File.Path);
loader.AddDependencyLocation(_testFixture.Delta2.Path);
loader.AddDependencyLocation(epsilonFile.Path);

Assembly epsilon = loader.LoadFromPath(epsilonFile.Path);
var e = epsilon.CreateInstance("Epsilon.E")!;
e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" });

#if NETCOREAPP
var alcs = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader);
Assert.Equal(1, alcs.Length);

VerifyAssemblies(
alcs[0].Assemblies,
("Delta", "1.0.0.0", delta1File.Path),
("Epsilon", "0.0.0.0", epsilonFile.Path));
#endif
Copy link
Member

Choose a reason for hiding this comment

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

Consider extracting a helper method: VerifyLoadContexts(new[] { ("Delta", ..., ...), ("Epsilon", ..., ...) });

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


var actual = sb.ToString();
Assert.Equal(
@"Delta: Epsilon: Test E
",
actual);
}

[Fact]
public void AssemblyLoading_MultipleVersions_MultipleLoaders()
{
Expand All @@ -228,18 +428,18 @@ public void AssemblyLoading_MultipleVersions_MultipleLoaders()
var alcs1 = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader1);
Assert.Equal(1, alcs1.Length);

Assert.Equal(new[] {
VerifyAssemblies(
alcs1[0].Assemblies,
("Delta", "1.0.0.0", _testFixture.Delta1.Path),
("Gamma", "0.0.0.0", _testFixture.Gamma.Path)
}, alcs1[0].Assemblies.Select(a => (a.GetName().Name!, a.GetName().Version!.ToString(), a.Location)).Order());
("Gamma", "0.0.0.0", _testFixture.Gamma.Path));

var alcs2 = DefaultAnalyzerAssemblyLoader.TestAccessor.GetOrderedLoadContexts(loader2);
Assert.Equal(1, alcs2.Length);

Assert.Equal(new[] {
VerifyAssemblies(
alcs2[0].Assemblies,
("Delta", "2.0.0.0", _testFixture.Delta2.Path),
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path)
}, alcs2[0].Assemblies.Select(a => (a.GetName().Name!, a.GetName().Version!.ToString(), a.Location)).Order());
("Epsilon", "0.0.0.0", _testFixture.Epsilon.Path));
#endif

var actual = sb.ToString();
Expand Down Expand Up @@ -280,19 +480,11 @@ public void AssemblyLoading_MultipleVersions_MissingVersion()
var eWrite = e.GetType().GetMethod("Write")!;

var actual = sb.ToString();
if (ExecutionConditionUtil.IsCoreClr)
{
var exception = Assert.Throws<TargetInvocationException>(() => eWrite.Invoke(e, new object[] { sb, "Test E" }));
Assert.IsAssignableFrom<FileNotFoundException>(exception.InnerException);
}
else
{
eWrite.Invoke(e, new object[] { sb, "Test E" });
Assert.Equal(
eWrite.Invoke(e, new object[] { sb, "Test E" });
Assert.Equal(
@"Delta: Gamma: Test G
",
actual);
}
actual);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,37 @@ public void AssemblyLoading_Delete()
",
actual);
}

[ConditionalFact(typeof(CoreClrOnly))]
public void AssemblyLoading_DependencyInDifferentDirectory_Delete()
{
StringBuilder sb = new StringBuilder();
var loader = new ShadowCopyAnalyzerAssemblyLoader();

var tempDir1 = Temp.CreateDirectory();
var tempDir2 = Temp.CreateDirectory();
var tempDir3 = Temp.CreateDirectory();

var delta1File = tempDir1.CreateFile("Delta.dll").CopyContentFrom(_testFixture.Delta1.Path);
var delta2File = tempDir2.CreateFile("Delta.dll").CopyContentFrom(_testFixture.Delta2.Path);
var gammaFile = tempDir3.CreateFile("Gamma.dll").CopyContentFrom(_testFixture.Gamma.Path);

loader.AddDependencyLocation(delta1File.Path);
loader.AddDependencyLocation(delta2File.Path);
loader.AddDependencyLocation(gammaFile.Path);
Assembly gamma = loader.LoadFromPath(gammaFile.Path);

var b = gamma.CreateInstance("Gamma.G")!;
var writeMethod = b.GetType().GetMethod("Write")!;
writeMethod.Invoke(b, new object[] { sb, "Test G" });

File.Delete(delta1File.Path);
File.Delete(delta2File.Path);
File.Delete(gammaFile.Path);

var actual = sb.ToString();
Assert.Equal(@"Delta: Gamma: Test G
", actual);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,21 +154,10 @@ private AssemblyIdentity AddToCache(string fullPath, AssemblyIdentity identity)
}

#nullable enable
protected bool IsKnownDependencyLocation(string fullPath)
protected HashSet<string>? GetPaths(string simpleName)
{
CompilerPathUtilities.RequireAbsolutePath(fullPath, nameof(fullPath));
var simpleName = PathUtilities.GetFileName(fullPath, includeExtension: false);
if (!_knownAssemblyPathsBySimpleName.TryGetValue(simpleName, out var paths))
{
return false;
}

if (!paths.Contains(fullPath))
{
return false;
}

return true;
_knownAssemblyPathsBySimpleName.TryGetValue(simpleName, out var paths);
return paths;
}

/// <summary>
Expand Down
Loading