Skip to content

Commit

Permalink
Types.InAssembly - add option to load referenced assemblies
Browse files Browse the repository at this point in the history
  • Loading branch information
NeVeSpl committed Dec 1, 2023
1 parent f0c1cac commit 5e164c4
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<ItemGroup>
<ProjectReference Include="..\..\sources\NetArchTest\NetArchTest.csproj" />
<ProjectReference Include="..\SampleApp.API\SampleApp.API.csproj" />
<ProjectReference Include="..\SampleApp.ModuleAlpha\SampleApp.ModuleAlpha.csproj" />
<ProjectReference Include="..\SampleApp.ModuleOmega\SampleApp.ModuleOmega.csproj" />
</ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions samples/SampleApp.API/SampleApp.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\SampleApp.ModuleAlpha\SampleApp.ModuleAlpha.csproj" />
<ProjectReference Include="..\SampleApp.ModuleOmega\SampleApp.ModuleOmega.csproj" />
</ItemGroup>

</Project>
9 changes: 9 additions & 0 deletions sources/NetArchTest/Assemblies/AssemblySpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ internal sealed class AssemblySpec
{
private AssemblyDefinition assemblyDefinition;
private IReadOnlyList<TypeDefinition> typeDefinitions;
private List<AssemblySpec> referenced = new List<AssemblySpec>();

public string FullName => assemblyDefinition.FullName;



public AssemblySpec(AssemblyDefinition assemblyDefinition, IEnumerable<TypeDefinition> typeDefinitions)
Expand All @@ -18,13 +22,18 @@ public AssemblySpec(AssemblyDefinition assemblyDefinition, IEnumerable<TypeDefin
this.typeDefinitions = typeDefinitions.ToArray();
}

public void AddRef(AssemblySpec assemblySpec)
{
referenced.Add(assemblySpec);
}

public IEnumerable<TypeSpec> GetTypes()
{
return typeDefinitions.Select(x => new TypeSpec(x));
}



public IAssembly CreateWrapper()
{
return new AssemblyContainer(assemblyDefinition);
Expand Down
47 changes: 38 additions & 9 deletions sources/NetArchTest/Assemblies/DataLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static class DataLoader
"netstandard",
"NuGet",
"Newtonsoft",
"Xunit",
"xunit",
"Internal.Microsoft",
"Mono.Cecil",
"NetArchTest.Assemblies",
Expand All @@ -38,36 +38,65 @@ public static LoadedData LoadFromCurrentDomain()

return LoadFromAssemblies(selectedAssemblies);
}
public static LoadedData LoadFromAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null)
public static LoadedData LoadFromAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
{
var files = assemblies.Select(x => x.Location);

return LoadFromFiles(files, searchDirectories);
return LoadFromFiles(files, searchDirectories, loadReferencedAssemblies);
}
public static LoadedData LoadFromFiles(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null)
public static LoadedData LoadFromFiles(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
{
var assemblies = Load(fileNames, searchDirectories);
var assemblies = Load(fileNames, searchDirectories, loadReferencedAssemblies);

return new LoadedData(assemblies);
}


private static IEnumerable<AssemblySpec> Load(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null)
private static IEnumerable<AssemblySpec> Load(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories, bool loadReferencedAssemblies)
{
var readerParameters = CreateReaderParameters(searchDirectories);
var definitions = new Dictionary<string, AssemblySpec>();

foreach (var fileName in fileNames)
{
var assemblyDefinition = ReadAssemblyDefinition(fileName, readerParameters);
if (assemblyDefinition == null) continue;
ProcessAssemblyDefinition(null, assemblyDefinition);
}

if (exclusionTree.GetAllMatchingNames(assemblyDefinition.Name.Name).Any() == true) continue;
void ProcessAssemblyDefinition(AssemblySpec parent, AssemblyDefinition assemblyDefinition)
{
if (assemblyDefinition == null) return;
if (exclusionTree.GetAllMatchingNames(assemblyDefinition.Name.Name).Any() == true) return;
if (definitions.TryGetValue(assemblyDefinition.FullName, out var existingSpec))
{
parent?.AddRef(existingSpec);
return;
}

var typeDefinitions = ReadTypes(assemblyDefinition);
var spec = new AssemblySpec(assemblyDefinition, typeDefinitions);

definitions.Add(assemblyDefinition.FullName, spec);
parent?.AddRef(spec);

yield return new AssemblySpec(assemblyDefinition, typeDefinitions);
if (loadReferencedAssemblies == false) return;

foreach (var module in assemblyDefinition.Modules)
{
if (module.HasAssemblyReferences)
{
foreach (var reference in module.AssemblyReferences)
{
var refAssembly = module.AssemblyResolver.Resolve(reference);
ProcessAssemblyDefinition(spec, refAssembly);
}
}
}
}

return definitions.Values;
}

private static ReaderParameters CreateReaderParameters(IEnumerable<string> searchDirectories, bool readSymbols = true)
{
DefaultAssemblyResolver assemblyResolver = null;
Expand Down
8 changes: 4 additions & 4 deletions sources/NetArchTest/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public static Types InCurrentDomain()
/// <param name="assembly">The assembly to base the list on.</param>
/// <param name="searchDirectories">An optional list of search directories to allow resolution of referenced assemblies.</param>
/// <returns>A list of types that can have predicates and conditions applied to it.</returns>
public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDirectories = null)
public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)

Check warning on line 44 in sources/NetArchTest/Types.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'loadReferencedAssemblies' has no matching param tag in the XML comment for 'Types.InAssembly(Assembly, IEnumerable<string>, bool)' (but other parameters do)
{
return Types.InAssemblies(new List<Assembly> { assembly }, searchDirectories);
return Types.InAssemblies(new List<Assembly> { assembly }, searchDirectories, loadReferencedAssemblies);
}

/// <summary>
Expand All @@ -52,9 +52,9 @@ public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDire
/// <param name="assemblies">The assemblies to base the list on.</param>
/// <param name="searchDirectories">An optional list of search directories to allow resolution of referenced assemblies.</param>
/// <returns>A list of types that can have predicates and conditions applied to it.</returns>
public static Types InAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null)
public static Types InAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)

Check warning on line 55 in sources/NetArchTest/Types.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'loadReferencedAssemblies' has no matching param tag in the XML comment for 'Types.InAssemblies(IEnumerable<Assembly>, IEnumerable<string>, bool)' (but other parameters do)
{
return new Types(DataLoader.LoadFromAssemblies(assemblies, searchDirectories));
return new Types(DataLoader.LoadFromAssemblies(assemblies, searchDirectories, loadReferencedAssemblies));
}

/// <summary>
Expand Down
27 changes: 27 additions & 0 deletions tests/NetArchTest.Rules.UnitTests/TypesTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using NetArchTest.CrossAssemblyTest.A;
using NetArchTest.Rules;
using Xunit;

Expand Down Expand Up @@ -98,5 +99,31 @@ public void FromFile_BadImage_CaughtAndEmptyListReturned()
// Assert
Assert.Empty(result);
}


[Fact(DisplayName = "InAssembly can load types from referenced assemblies")]
public void InAssembly_LoadReferencedAssemblies_True()
{
// Act
var result = Types.InAssembly(typeof(TypesTests).Assembly, loadReferencedAssemblies: true)
.GetTypes()
.Select(x => x.ReflectionType)
.ToArray();

// Assert
Assert.Contains(typeof(BaseClassFromA), result);
}
[Fact(DisplayName = "InAssembly can not load types from referenced assemblies")]
public void InAssembly_LoadReferencedAssemblies_False()
{
// Act
var result = Types.InAssembly(typeof(TypesTests).Assembly, loadReferencedAssemblies: false)
.GetTypes()
.Select(x => x.ReflectionType)
.ToArray();

// Assert
Assert.DoesNotContain(typeof(BaseClassFromA), result);
}
}
}

0 comments on commit 5e164c4

Please sign in to comment.