Skip to content

Commit 2076193

Browse files
Merge pull request #333 from TNG/325-ensure-no-calls-between-modules-in-a-modulith
2 parents 88f48ea + 0ccf818 commit 2076193

File tree

13 files changed

+193
-1
lines changed

13 files changed

+193
-1
lines changed

ArchUnit.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoaderTestAssembly", "TestA
3232
EndProject
3333
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherLoaderTestAssembly", "TestAssemblies\OtherLoaderTestAssembly\OtherLoaderTestAssembly.csproj", "{5A24529B-1794-4080-ADCC-77440BA0A0B3}"
3434
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilteredDirectoryLoaderTestAssembly", "TestAssemblies\FilteredDirectoryLoaderTestAssembly\FilteredDirectoryLoaderTestAssembly.csproj", "{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B}"
36+
EndProject
3537
Global
3638
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3739
Debug|Any CPU = Debug|Any CPU
@@ -94,6 +96,10 @@ Global
9496
{5A24529B-1794-4080-ADCC-77440BA0A0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
9597
{5A24529B-1794-4080-ADCC-77440BA0A0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
9698
{5A24529B-1794-4080-ADCC-77440BA0A0B3}.Release|Any CPU.Build.0 = Release|Any CPU
99+
{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
100+
{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B}.Debug|Any CPU.Build.0 = Debug|Any CPU
101+
{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B}.Release|Any CPU.ActiveCfg = Release|Any CPU
102+
{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B}.Release|Any CPU.Build.0 = Release|Any CPU
97103
EndGlobalSection
98104
GlobalSection(SolutionProperties) = preSolution
99105
HideSolutionNode = FALSE
@@ -104,5 +110,6 @@ Global
104110
{FBCD91F2-4DB9-44AC-8214-6F2FFF9178D5} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
105111
{0243F2D4-AC89-4561-A936-D647B6BB821F} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
106112
{5A24529B-1794-4080-ADCC-77440BA0A0B3} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
113+
{E6CB8C69-25F5-4C94-8EA3-D56E444EB46B} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
107114
EndGlobalSection
108115
EndGlobal

ArchUnitNET/Domain/UnavailableType.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2019 Florian Gather <[email protected]>
2+
// Copyright 2019 Fritz Brandhuber <[email protected]>
3+
// Copyright 2020 Pavel Fischer <[email protected]>
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using ArchUnitNET.Domain.Dependencies;
10+
11+
namespace ArchUnitNET.Domain
12+
{
13+
public class UnavailableType : IType
14+
{
15+
public UnavailableType(IType type)
16+
{
17+
Type = type;
18+
}
19+
20+
private IType Type { get; }
21+
public string Name => Type.Name;
22+
public string FullName => Type.FullName;
23+
24+
public Visibility Visibility => Type.Visibility;
25+
public bool IsNested => Type.IsNested;
26+
public bool IsGeneric => Type.IsGeneric;
27+
public bool IsGenericParameter => Type.IsGenericParameter;
28+
public bool IsStub => true;
29+
public bool IsCompilerGenerated => Type.IsCompilerGenerated;
30+
31+
public Namespace Namespace => Type.Namespace;
32+
public Assembly Assembly => Type.Assembly;
33+
34+
public IEnumerable<Attribute> Attributes =>
35+
AttributeInstances.Select(instance => instance.Type);
36+
public List<AttributeInstance> AttributeInstances => Type.AttributeInstances;
37+
38+
public List<ITypeDependency> Dependencies => Type.Dependencies;
39+
public List<ITypeDependency> BackwardsDependencies => Type.BackwardsDependencies;
40+
public IEnumerable<IType> ImplementedInterfaces => Type.ImplementedInterfaces;
41+
42+
public MemberList Members => Type.Members;
43+
public List<GenericParameter> GenericParameters => Type.GenericParameters;
44+
45+
public override string ToString()
46+
{
47+
return FullName;
48+
}
49+
50+
private bool Equals(Struct other)
51+
{
52+
return Equals(Type, other.Type);
53+
}
54+
55+
public override bool Equals(object obj)
56+
{
57+
if (ReferenceEquals(null, obj))
58+
{
59+
return false;
60+
}
61+
62+
if (ReferenceEquals(this, obj))
63+
{
64+
return true;
65+
}
66+
67+
return obj.GetType() == GetType() && Equals((Struct)obj);
68+
}
69+
70+
public override int GetHashCode()
71+
{
72+
return Type != null ? Type.GetHashCode() : 0;
73+
}
74+
}
75+
}

ArchUnitNET/Loader/TypeFactory.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,29 @@ bool isStub
229229
}
230230
if (typeDefinition == null)
231231
{
232-
throw new ArchLoaderException($"Could not resolve type {typeReference.FullName}");
232+
// When assemblies are loaded by path, there are cases where a dependent type cannot be resolved because
233+
// the assembly dependency is not loaded in the current application domain. In this case, we create a
234+
// stub type.
235+
return new TypeInstance<IType>(
236+
new UnavailableType(
237+
new Type(
238+
typeReference.BuildFullName(),
239+
typeReference.Name,
240+
_assemblyRegistry.GetOrCreateAssembly(
241+
typeReference.Module.Assembly.Name.FullName,
242+
typeReference.Module.Assembly.FullName,
243+
true,
244+
null
245+
),
246+
_namespaceRegistry.GetOrCreateNamespace(typeReference.Namespace),
247+
NotAccessible,
248+
typeReference.IsNested,
249+
typeReference.HasGenericParameters,
250+
true,
251+
typeReference.IsCompilerGenerated()
252+
)
253+
)
254+
);
233255
}
234256

235257
var typeName = typeDefinition.BuildFullName();

ArchUnitNETTests/ArchUnitNETTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<ProjectReference Include="..\TestAssembly\TestAssembly.csproj" />
1414
<ProjectReference Include="..\TestAssemblies\AttributeAssembly\AttributeAssembly.csproj" />
1515
<ProjectReference Include="..\TestAssemblies\DependencyAssembly\DependencyAssembly.csproj" />
16+
<ProjectReference Include="..\TestAssemblies\FilteredDirectoryLoaderTestAssembly\FilteredDirectoryLoaderTestAssembly.csproj" ReferenceOutputAssembly="false" />
1617
<ProjectReference Include="..\TestAssemblies\LoaderTestAssembly\LoaderTestAssembly.csproj" />
1718
<ProjectReference
1819
Include="..\TestAssemblies\OtherLoaderTestAssembly\OtherLoaderTestAssembly.csproj" />

ArchUnitNETTests/Loader/ArchLoaderTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
//
55
// SPDX-License-Identifier: Apache-2.0
66

7+
using System;
78
using System.Linq;
9+
using ArchUnitNET.Domain;
10+
using ArchUnitNET.Domain.Extensions;
811
using ArchUnitNET.Loader;
912
using ArchUnitNET.xUnit;
1013
using ArchUnitNETTests.Domain.Dependencies.Members;
@@ -96,5 +99,30 @@ public void TypesAreAssignedToCorrectAssemblies()
9699
.ResideInAssembly(GetType().Assembly)
97100
.Check(architecture);
98101
}
102+
103+
[Fact]
104+
public void UnavailableTypeTest()
105+
{
106+
// When loading an assembly from a file, there are situations where the assemblies dependencies are not
107+
// available in the current AppDomain. This test checks that the loader does not throw an exception in this
108+
// case.
109+
var assemblyPath = AppDomain.CurrentDomain.BaseDirectory[
110+
..AppDomain.CurrentDomain.BaseDirectory.IndexOf(
111+
@"ArchUnitNETTests",
112+
StringComparison.InvariantCulture
113+
)
114+
];
115+
var architecture = new ArchLoader()
116+
.LoadFilteredDirectory(
117+
assemblyPath,
118+
"FilteredDirectoryLoaderTestAssembly.dll",
119+
System.IO.SearchOption.AllDirectories
120+
)
121+
.Build();
122+
Assert.Single(architecture.Types);
123+
var loggerType = architecture.ReferencedTypes.WhereFullNameIs("Serilog.ILogger");
124+
Assert.NotNull(loggerType);
125+
Assert.True(loggerType is UnavailableType);
126+
}
99127
}
100128
}

TestAssemblies/AttributeAssembly/AttributeAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
89
</PropertyGroup>
910

1011
</Project>

TestAssemblies/DependencyAssembly/DependencyAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
89
</PropertyGroup>
910

1011
</Project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Serilog;
2+
3+
namespace FilteredDirectoryLoaderTestAssembly;
4+
5+
public class Class1
6+
{
7+
public ILogger logger;
8+
9+
public Class1()
10+
{
11+
this.logger = new LoggerConfiguration().CreateLogger();
12+
}
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Serilog" Version="4.2.0" />
13+
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.5.002.0
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilteredDirectoryLoaderTestAssembly", "FilteredDirectoryLoaderTestAssembly.csproj", "{8597C220-0EC2-42AF-9675-C56607614773}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{8597C220-0EC2-42AF-9675-C56607614773}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{8597C220-0EC2-42AF-9675-C56607614773}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{8597C220-0EC2-42AF-9675-C56607614773}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{8597C220-0EC2-42AF-9675-C56607614773}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {B166203B-6B5B-4BE6-B945-CA9FD03A6054}
24+
EndGlobalSection
25+
EndGlobal

TestAssemblies/LoaderTestAssembly/LoaderTestAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
89
</PropertyGroup>
910

1011
</Project>

TestAssemblies/OtherLoaderTestAssembly/OtherLoaderTestAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
89
</PropertyGroup>
910

1011
</Project>

TestAssemblies/VisibilityAssembly/VisibilityAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8+
<IsPackable>false</IsPackable>
89
</PropertyGroup>
910

1011
</Project>

0 commit comments

Comments
 (0)