Skip to content

Commit e4498e2

Browse files
committed
BenMorris/NetArchTest#97 - add rules: HaveSourceFileNameMatchingName, HaveSourceFilePathMatchingNamespace
1 parent ec7788c commit e4498e2

File tree

6 files changed

+174
-1
lines changed

6 files changed

+174
-1
lines changed

src/NetArchTest.Rules/Condition_Special.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,32 @@ public ConditionList OnlyHaveNonNullableMembers()
7474
AddFunctionCall(x => FunctionDelegates.OnlyHaveNonNullableMembers(x, true));
7575
return CreateConditionList();
7676
}
77+
78+
79+
/// <summary>
80+
/// Check if the name of a type is consistent with its source file name
81+
/// </summary>
82+
/// <remarks>
83+
/// StringComparison.InvariantCultureIgnoreCase is used for comparing, can be changed through Options
84+
/// </remarks>
85+
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
86+
public ConditionList HaveSourceFileNameMatchingName()
87+
{
88+
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveFileNameMatchingTypeName(context, inputTypes, true));
89+
return CreateConditionList();
90+
}
91+
92+
/// <summary>
93+
/// Check if the namespace of a type is consistent with its source file path
94+
/// </summary>
95+
/// <remarks>
96+
/// StringComparison.InvariantCultureIgnoreCase is used for comparing, can be changed through Options
97+
/// </remarks>
98+
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
99+
public ConditionList HaveSourceFilePathMatchingNamespace()
100+
{
101+
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveFilePathMatchingTypeNamespace(context, inputTypes, true));
102+
return CreateConditionList();
103+
}
77104
}
78105
}

src/NetArchTest.Rules/Functions/FunctionDelegates_Special.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
24
using System.Linq;
35
using Mono.Cecil;
46
using NetArchTest.Assemblies;
7+
using NetArchTest.RuleEngine;
58

69
namespace NetArchTest.Functions
710
{
@@ -67,5 +70,47 @@ internal static IEnumerable<TypeSpec> BeStateless(IEnumerable<TypeSpec> input, b
6770
return input.Where(c => !c.Definition.IsStateless());
6871
}
6972
}
73+
74+
internal static IEnumerable<TypeSpec> HaveFileNameMatchingTypeName(FunctionSequenceExecutionContext context, IEnumerable<TypeSpec> input, bool condition)
75+
{
76+
if (condition)
77+
{
78+
return input.Where(c => IsMatching(c.SourceFilePath, c.Definition.GetName(), context.UserOptions.Comparer));
79+
}
80+
else
81+
{
82+
return input.Where(c => !IsMatching(c.SourceFilePath, c.Definition.GetName(), context.UserOptions.Comparer));
83+
}
84+
85+
static bool IsMatching(string sourceFilePath, string typeName, StringComparison comparer)
86+
{
87+
if (string.IsNullOrEmpty(sourceFilePath)) return true;
88+
89+
var fileName = Path.GetFileNameWithoutExtension(sourceFilePath);
90+
return fileName.Equals(typeName, comparer);
91+
}
92+
}
93+
94+
internal static IEnumerable<TypeSpec> HaveFilePathMatchingTypeNamespace(FunctionSequenceExecutionContext context, IEnumerable<TypeSpec> input, bool condition)
95+
{
96+
if (condition)
97+
{
98+
return input.Where(c => IsMatching(c.SourceFilePath, c.Definition.GetNamespace(), context.UserOptions.Comparer));
99+
}
100+
else
101+
{
102+
return input.Where(c => !IsMatching(c.SourceFilePath, c.Definition.GetNamespace(), context.UserOptions.Comparer));
103+
}
104+
105+
static bool IsMatching(string sourceFilePath, string @namespace, StringComparison comparer)
106+
{
107+
if (string.IsNullOrEmpty(sourceFilePath)) return true;
108+
109+
var fullPath = Path.GetDirectoryName(sourceFilePath);
110+
var pathAsNamespace = fullPath.Replace(Path.DirectorySeparatorChar, '.');
111+
112+
return pathAsNamespace.EndsWith(@namespace, comparer);
113+
}
114+
}
70115
}
71116
}

test/NetArchTest.Rules.UnitTests/ConditionTests_Special.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Linq;
22
using System.Reflection;
33
using NetArchTest.Rules;
4+
using NetArchTest.TestStructure.File;
45
using NetArchTest.TestStructure.NameMatching.Namespace1;
56
using NetArchTest.TestStructure.Stateless;
67
using Xunit;
@@ -123,5 +124,67 @@ public void OnlyHaveNonNullableMembers()
123124

124125
Assert.True(result.IsSuccessful);
125126
}
127+
128+
129+
[Fact(DisplayName = "HaveFileNameMatchingTypeName_Should")]
130+
public void HaveFileNameMatchingTypeName_Should()
131+
{
132+
var result = Types
133+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
134+
.That()
135+
.ResideInNamespace(namespaceof<SourceFileNameType>())
136+
.And()
137+
.DoNotHaveNameStartingWith("Incorrect")
138+
.Should()
139+
.HaveSourceFileNameMatchingName().GetResult();
140+
141+
Assert.True(result.IsSuccessful);
142+
}
143+
144+
[Fact(DisplayName = "HaveFileNameMatchingTypeName_ShouldNot")]
145+
public void HaveFileNameMatchingTypeName_ShouldNot()
146+
{
147+
var result = Types
148+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
149+
.That()
150+
.ResideInNamespace(namespaceof<SourceFileNameType>())
151+
.And()
152+
.HaveNameStartingWith("Incorrect")
153+
.ShouldNot()
154+
.HaveSourceFileNameMatchingName().GetResult();
155+
156+
Assert.True(result.IsSuccessful);
157+
}
158+
159+
160+
[Fact(DisplayName = "HaveSourceFilePathMatchingTypeNamespace_Should")]
161+
public void HaveSourceFilePathMatchingTypeNamespace_Should()
162+
{
163+
var result = Types
164+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
165+
.That()
166+
.ResideInNamespace(namespaceof<SourceFileNameType>())
167+
.And()
168+
.ResideInNamespaceContaining(@"\.Correct")
169+
.Should()
170+
.HaveSourceFilePathMatchingNamespace().GetResult();
171+
172+
Assert.True(result.IsSuccessful);
173+
}
174+
175+
[Fact(DisplayName = "HaveSourceFilePathMatchingTypeNamespace_ShouldNot")]
176+
public void HaveSourceFilePathMatchingTypeNamespace_ShouldNot()
177+
{
178+
var result = Types
179+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
180+
.That()
181+
.ResideInNamespace(namespaceof<SourceFileNameType>())
182+
.And()
183+
.ResideInNamespaceContaining(".Incorrect")
184+
.ShouldNot()
185+
.HaveSourceFilePathMatchingNamespace().GetResult();
186+
187+
Assert.True(result.IsSuccessful);
188+
}
126189
}
127190
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace NetArchTest.TestStructure.File.Correct
6+
{
7+
internal class IncorrectSourceFileNameTypeFoo
8+
{
9+
public IncorrectSourceFileNameTypeFoo()
10+
{
11+
12+
}
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace NetArchTest.TestStructure.File.Incorrect.Yabadabado
6+
{
7+
internal class CorrectSourceFileNameType
8+
{
9+
public CorrectSourceFileNameType()
10+
{
11+
12+
}
13+
}
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace NetArchTest.TestStructure.File
6+
{
7+
internal class SourceFileNameType
8+
{
9+
}
10+
}

0 commit comments

Comments
 (0)