Skip to content
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
15 changes: 15 additions & 0 deletions ArchUnit.sln
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlicesTestAssembly", "TestA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptimizedAssembly", "TestAssemblies\OptimizedAssembly\OptimizedAssembly.csproj", "{74857E33-09C5-43C7-AA27-E9373DEC1380}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionPointerAssembly", "TestAssemblies\FunctionPointerAssembly\FunctionPointerAssembly.csproj", "{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A780B3D6-C525-4270-ABF1-63BA65EA1593}"
ProjectSection(SolutionItems) = preProject
ArchUnitNET.mtp.slnf = ArchUnitNET.mtp.slnf
Expand Down Expand Up @@ -473,6 +475,18 @@ Global
{74857E33-09C5-43C7-AA27-E9373DEC1380}.Release|x64.Build.0 = Release|Any CPU
{74857E33-09C5-43C7-AA27-E9373DEC1380}.Release|x86.ActiveCfg = Release|Any CPU
{74857E33-09C5-43C7-AA27-E9373DEC1380}.Release|x86.Build.0 = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|x64.ActiveCfg = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|x64.Build.0 = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|x86.ActiveCfg = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Debug|x86.Build.0 = Debug|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|Any CPU.Build.0 = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|x64.ActiveCfg = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|x64.Build.0 = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|x86.ActiveCfg = Release|Any CPU
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -492,6 +506,7 @@ Global
{2C04EB93-5AE0-474C-B328-145A734A5E2B} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{E421FA12-BBB9-4A8B-BD29-8757836079A5} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{74857E33-09C5-43C7-AA27-E9373DEC1380} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{B842CBE7-3508-416A-AF20-6CB8B1C00ED9} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{5B3E7A23-F91B-4506-9879-B8CAD13F4AE0} = {38699834-8620-4254-AB98-C19586AD3952}
{84BF2DBC-C8FA-46CE-9419-1CE4E3C4D175} = {38699834-8620-4254-AB98-C19586AD3952}
EndGlobalSection
Expand Down
8 changes: 4 additions & 4 deletions ArchUnitNET/Domain/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,22 +241,22 @@ public static Attribute GetAttributeOfType(this IType type, Class attributeClass

public static bool ResidesInNamespace(this IType e, string fullName)
{
return e.Namespace.FullNameEquals(fullName);
return e.Namespace != null && e.Namespace.FullNameEquals(fullName);
}

public static bool ResidesInNamespaceMatching(this IType e, string pattern)
{
return e.Namespace.FullNameMatches(pattern);
return e.Namespace != null && e.Namespace.FullNameMatches(pattern);
}

public static bool ResidesInAssembly(this IType e, string fullName)
{
return e.Assembly.FullNameEquals(fullName);
return e.Assembly != null && e.Assembly.FullNameEquals(fullName);
}

public static bool ResidesInAssemblyMatching(this IType e, string pattern)
{
return e.Assembly.FullNameMatches(pattern);
return e.Assembly != null && e.Assembly.FullNameMatches(pattern);
}

public static bool IsDeclaredAsFieldIn(this IType type, string fullName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public static IOrderedCondition<TRuleType> ResideInNamespace(string fullName)
{
return new SimpleCondition<TRuleType>(
type => type.ResidesInNamespace(fullName),
obj => "does reside in " + obj.Namespace.FullName,
obj => ResideInNamespaceFailDescription(obj),
"reside in namespace with full name \"" + fullName + "\""
);
}
Expand All @@ -309,7 +309,7 @@ public static IOrderedCondition<TRuleType> ResideInNamespaceMatching(string patt
{
return new SimpleCondition<TRuleType>(
type => type.ResidesInNamespaceMatching(pattern),
obj => "does reside in " + obj.Namespace.FullName,
obj => ResideInNamespaceFailDescription(obj),
"reside in namespace with full name matching \"" + pattern + "\""
);
}
Expand All @@ -318,7 +318,7 @@ public static IOrderedCondition<TRuleType> ResideInAssembly(string fullName)
{
return new SimpleCondition<TRuleType>(
type => type.ResidesInAssembly(fullName),
obj => "does reside in " + obj.Assembly.FullName,
obj => ResideInAssemblyFailDescription(obj),
"reside in assembly with full name \"" + fullName + "\""
);
}
Expand All @@ -327,7 +327,7 @@ public static IOrderedCondition<TRuleType> ResideInAssemblyMatching(string patte
{
return new SimpleCondition<TRuleType>(
type => type.ResidesInAssemblyMatching(pattern),
obj => "does reside in " + obj.Assembly.FullName,
obj => ResideInAssemblyFailDescription(obj),
"reside in assembly with full name matching \"" + pattern + "\""
);
}
Expand All @@ -339,9 +339,9 @@ params System.Reflection.Assembly[] moreAssemblies
{
bool Condition(TRuleType ruleType, Architecture architecture)
{
return ruleType.Assembly.Equals(architecture.GetAssemblyOfAssembly(assembly))
return Equals(ruleType.Assembly, architecture.GetAssemblyOfAssembly(assembly))
|| moreAssemblies.Any(asm =>
ruleType.Assembly.Equals(architecture.GetAssemblyOfAssembly(asm))
Equals(ruleType.Assembly, architecture.GetAssemblyOfAssembly(asm))
);
}

Expand All @@ -352,7 +352,7 @@ bool Condition(TRuleType ruleType, Architecture architecture)

return new OrderedArchitectureCondition<TRuleType>(
Condition,
(type, architecture) => "does reside in " + type.Assembly.FullName,
(type, architecture) => ResideInAssemblyFailDescription(type),
description
);
}
Expand All @@ -375,7 +375,7 @@ bool Condition(TRuleType ruleType)

return new SimpleCondition<TRuleType>(
Condition,
type => "does reside in " + type.Assembly.FullName,
type => ResideInAssemblyFailDescription(type),
description
);
}
Expand Down Expand Up @@ -706,7 +706,7 @@ public static IOrderedCondition<TRuleType> NotResideInNamespace(string fullName)
{
return new SimpleCondition<TRuleType>(
type => !type.ResidesInNamespace(fullName),
obj => "does reside in " + obj.Namespace.FullName,
obj => ResideInNamespaceFailDescription(obj),
"not reside in namespace with full name \"" + fullName + "\""
);
}
Expand All @@ -715,7 +715,7 @@ public static IOrderedCondition<TRuleType> NotResideInNamespaceMatching(string p
{
return new SimpleCondition<TRuleType>(
type => !type.ResidesInNamespaceMatching(pattern),
obj => "does reside in " + obj.Namespace.FullName,
obj => ResideInNamespaceFailDescription(obj),
"not reside in namespace with full name matching \"" + pattern + "\""
);
}
Expand All @@ -724,7 +724,7 @@ public static IOrderedCondition<TRuleType> NotResideInAssembly(string fullName)
{
return new SimpleCondition<TRuleType>(
type => !type.ResidesInAssembly(fullName),
obj => "does reside in " + obj.Assembly.FullName,
obj => ResideInAssemblyFailDescription(obj),
"not reside in assembly with full name \"" + fullName + "\""
);
}
Expand All @@ -733,7 +733,7 @@ public static IOrderedCondition<TRuleType> NotResideInAssemblyMatching(string pa
{
return new SimpleCondition<TRuleType>(
type => !type.ResidesInAssemblyMatching(pattern),
obj => "does reside in " + obj.Assembly.FullName,
obj => ResideInAssemblyFailDescription(obj),
"not reside in assembly with full name matching \"" + pattern + "\""
);
}
Expand All @@ -745,9 +745,9 @@ params System.Reflection.Assembly[] moreAssemblies
{
bool Condition(TRuleType ruleType, Architecture architecture)
{
return !ruleType.Assembly.Equals(architecture.GetAssemblyOfAssembly(assembly))
return !Equals(ruleType.Assembly, architecture.GetAssemblyOfAssembly(assembly))
&& !moreAssemblies.Any(asm =>
ruleType.Assembly.Equals(architecture.GetAssemblyOfAssembly(asm))
Equals(ruleType.Assembly, architecture.GetAssemblyOfAssembly(asm))
);
}

Expand All @@ -758,7 +758,7 @@ bool Condition(TRuleType ruleType, Architecture architecture)

return new OrderedArchitectureCondition<TRuleType>(
Condition,
(type, architecture) => "does reside in " + type.Assembly.FullName,
(type, architecture) => ResideInAssemblyFailDescription(type),
description
);
}
Expand All @@ -781,7 +781,7 @@ bool Condition(TRuleType ruleType)

return new SimpleCondition<TRuleType>(
Condition,
type => "does reside in " + type.Assembly.FullName,
type => ResideInAssemblyFailDescription(type),
description
);
}
Expand Down Expand Up @@ -848,5 +848,19 @@ public static RelationCondition<TRuleType, Interface> NotImplementAnyInterfacesT
"not implement any interfaces that",
"does implement any interfaces that"
);

private static string ResideInNamespaceFailDescription(IType type)
{
return type.Namespace == null
? "does not reside in a namespace"
: "does reside in " + type.Namespace.FullName;
}

private static string ResideInAssemblyFailDescription(IType type)
{
return type.Assembly == null
? "does not reside in an assembly"
: "does reside in " + type.Assembly.FullName;
}
}
}
1 change: 1 addition & 0 deletions ArchUnitNETTests/ArchUnitNETTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<ProjectReference Include="..\TestAssemblies\ClassAssembly\ClassAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\SlicesTestAssembly\SlicesTestAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\OptimizedAssembly\OptimizedAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\FunctionPointerAssembly\FunctionPointerAssembly.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
Expand Down
151 changes: 151 additions & 0 deletions ArchUnitNETTests/Domain/FunctionPointerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System.Linq;
using ArchUnitNET.Domain;
using ArchUnitNET.Domain.Extensions;
using ArchUnitNET.Fluent.Extensions;
using FunctionPointerNamespace;
using Xunit;
using static ArchUnitNET.Fluent.ArchRuleDefinition;

namespace ArchUnitNETTests.Domain
{
public class FunctionPointerTests
{
private static readonly Architecture Architecture =
StaticTestArchitectures.FunctionPointerArchitecture;

private readonly IType _functionPointer;

public FunctionPointerTests()
{
_functionPointer = Architecture
.GetClassOfType(typeof(ClassWithFunctionPointerField))
.GetFieldMembers()
.Single()
.Type;
}

[Fact]
public void FunctionPointerHasNoNamespaceOrAssembly()
{
Assert.IsType<FunctionPointer>(_functionPointer);
Assert.Contains(_functionPointer, Architecture.ReferencedTypes);
Assert.Null(_functionPointer.Namespace);
Assert.Null(_functionPointer.Assembly);
}

[Fact]
public void NamespaceExtensionsReturnFalseForTypeWithoutNamespace()
{
Assert.False(_functionPointer.ResidesInNamespace("System"));
Assert.False(_functionPointer.ResidesInNamespaceMatching(".*"));
}

[Fact]
public void AssemblyExtensionsReturnFalseForTypeWithoutAssembly()
{
Assert.False(_functionPointer.ResidesInAssembly("System"));
Assert.False(_functionPointer.ResidesInAssemblyMatching(".*"));
}

[Fact]
public void NamespacePredicatesHandleTypesWithoutNamespace()
{
var namespaceName = typeof(ClassWithFunctionPointerField).Namespace;
Assert.DoesNotContain(
_functionPointer,
Types(true).That().ResideInNamespace(namespaceName).GetObjects(Architecture)
);
Assert.DoesNotContain(
_functionPointer,
Types(true).That().ResideInNamespaceMatching(".*").GetObjects(Architecture)
);
Assert.Contains(
_functionPointer,
Types(true).That().DoNotResideInNamespace(namespaceName).GetObjects(Architecture)
);
Assert.Contains(
_functionPointer,
Types(true).That().DoNotResideInNamespaceMatching(".*").GetObjects(Architecture)
);
}

[Fact]
public void AssemblyPredicatesHandleTypesWithoutAssembly()
{
var assembly = typeof(ClassWithFunctionPointerField).Assembly;
Assert.DoesNotContain(
_functionPointer,
Types(true).That().ResideInAssembly(assembly.FullName).GetObjects(Architecture)
);
Assert.DoesNotContain(
_functionPointer,
Types(true).That().ResideInAssemblyMatching(".*").GetObjects(Architecture)
);
Assert.Contains(
_functionPointer,
Types(true).That().DoNotResideInAssembly(assembly.FullName).GetObjects(Architecture)
);
}

[Fact]
public void NamespaceConditionsReportTypesWithoutNamespace()
{
var functionPointers = Types(true).That().Are(_functionPointer);
AssertViolation(
functionPointers.Should().ResideInNamespace("System"),
"does not reside in a namespace"
);
AssertViolation(
functionPointers.Should().ResideInNamespaceMatching(".*"),
"does not reside in a namespace"
);
Assert.True(
functionPointers
.Should()
.NotResideInNamespace("System")
.HasNoViolations(Architecture)
);
}

[Fact]
public void AssemblyConditionsReportTypesWithoutAssembly()
{
var assembly = typeof(ClassWithFunctionPointerField).Assembly;
var functionPointers = Types(true).That().Are(_functionPointer);
AssertViolation(
functionPointers.Should().ResideInAssembly(assembly.FullName),
"does not reside in an assembly"
);
AssertViolation(
functionPointers.Should().ResideInAssemblyMatching(".*"),
"does not reside in an assembly"
);
AssertViolation(
functionPointers.Should().ResideInAssembly(assembly),
"does not reside in an assembly"
);
AssertViolation(
functionPointers
.Should()
.ResideInAssembly(Architecture.GetAssemblyOfAssembly(assembly)),
"does not reside in an assembly"
);
Assert.True(
functionPointers
.Should()
.NotResideInAssembly(assembly)
.HasNoViolations(Architecture)
);
}

private static void AssertViolation(
ArchUnitNET.Fluent.IArchRule rule,
string expectedDescription
)
{
var result = rule.Evaluate(Architecture).Single();
Assert.False(result.Passed);
Assert.Contains(expectedDescription, result.Description);
}
}
}
6 changes: 6 additions & 0 deletions ArchUnitNETTests/StaticTestArchitectures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ public static class StaticTestArchitectures
.LoadAssemblies(typeof(OptimizedAssembly.ClassWithAsyncMethod).Assembly)
.Build();

public static readonly Architecture FunctionPointerArchitecture = new ArchLoader()
.WithoutRuleEvaluationCache()
.WithoutArchitectureCache()
.LoadAssemblies(typeof(FunctionPointerNamespace.ClassWithFunctionPointerField).Assembly)
.Build();

public static readonly Architecture ArchUnitNETTestAssemblyArchitecture = new ArchLoader()
.LoadAssemblies(typeof(Class1).Assembly)
.Build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FunctionPointerNamespace;

// The field's type is a function pointer, which the loader resolves to a referenced type
// without a namespace or an assembly.
public unsafe class ClassWithFunctionPointerField
{
public delegate* <int, void> FunctionPointerField;
}
Loading
Loading