Skip to content

Commit 76d9815

Browse files
committed
BenMorris/NetArchTest#104 - add rule: HaveMatchingTypeWithName
1 parent e4498e2 commit 76d9815

File tree

7 files changed

+84
-17
lines changed

7 files changed

+84
-17
lines changed

src/NetArchTest.Rules/Condition_Special.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using NetArchTest.Functions;
1+
using System;
2+
using Mono.Cecil;
3+
using NetArchTest.Functions;
24

35
namespace NetArchTest.Rules
46
{
@@ -77,7 +79,7 @@ public ConditionList OnlyHaveNonNullableMembers()
7779

7880

7981
/// <summary>
80-
/// Check if the name of a type is consistent with its source file name
82+
/// For each type, check if the name is consistent with its source file name.
8183
/// </summary>
8284
/// <remarks>
8385
/// StringComparison.InvariantCultureIgnoreCase is used for comparing, can be changed through Options
@@ -90,7 +92,7 @@ public ConditionList HaveSourceFileNameMatchingName()
9092
}
9193

9294
/// <summary>
93-
/// Check if the namespace of a type is consistent with its source file path
95+
/// For each type, check if the namespace is consistent with its source file path.
9496
/// </summary>
9597
/// <remarks>
9698
/// StringComparison.InvariantCultureIgnoreCase is used for comparing, can be changed through Options
@@ -101,5 +103,15 @@ public ConditionList HaveSourceFilePathMatchingNamespace()
101103
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveFilePathMatchingTypeNamespace(context, inputTypes, true));
102104
return CreateConditionList();
103105
}
106+
107+
/// <summary>
108+
/// For each type, check if a matching type with the given name exists.
109+
/// </summary>
110+
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
111+
public ConditionList HaveMatchingTypeWithName(Func<TypeDefinition, string> getMatchingTypeName)
112+
{
113+
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveMatchingTypeWithName(context, inputTypes, getMatchingTypeName, true));
114+
return CreateConditionList();
115+
}
104116
}
105117
}

src/NetArchTest.Rules/Functions/FunctionDelegates_Special.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,18 @@ static bool IsMatching(string sourceFilePath, string @namespace, StringCompariso
112112
return pathAsNamespace.EndsWith(@namespace, comparer);
113113
}
114114
}
115+
116+
internal static IEnumerable<TypeSpec> HaveMatchingTypeWithName(FunctionSequenceExecutionContext context, IEnumerable<TypeSpec> input, Func<TypeDefinition, string> getMatchingTypeName, bool condition)
117+
{
118+
var exisitingTypes = new HashSet<string>(context.AllTypes.Select(x => x.Definition.GetName()));
119+
if (condition)
120+
{
121+
return input.Where(c => exisitingTypes.Contains(getMatchingTypeName(c.Definition)));
122+
}
123+
else
124+
{
125+
return input.Where(c => !exisitingTypes.Contains(getMatchingTypeName(c.Definition)));
126+
}
127+
}
115128
}
116129
}

src/NetArchTest.Rules/RuleEngine/FunctionSequence.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ public void CreateGroup()
2929
}
3030

3131

32-
public IEnumerable<TypeSpec> ExecuteToGetFailingTypes(IEnumerable<TypeSpec> inputTypes, bool selected, Options options)
32+
public IEnumerable<TypeSpec> ExecuteToGetFailingTypes(IEnumerable<TypeSpec> inputTypes, bool selected, Options options, IEnumerable<TypeSpec> allTypes)
3333
{
34-
return Execute(inputTypes, selected, true, options);
34+
return Execute(inputTypes, selected, true, options, allTypes);
3535
}
36-
public IEnumerable<TypeSpec> Execute(IEnumerable<TypeSpec> inputTypes, Options options)
36+
public IEnumerable<TypeSpec> Execute(IEnumerable<TypeSpec> inputTypes, Options options, IEnumerable<TypeSpec> allTypes)
3737
{
38-
return Execute(inputTypes, true, false, options);
38+
return Execute(inputTypes, true, false, options, allTypes);
3939
}
4040

41-
private IEnumerable<TypeSpec> Execute(IEnumerable<TypeSpec> inputTypes, bool selected, bool isFailPathRun, Options options)
41+
private IEnumerable<TypeSpec> Execute(IEnumerable<TypeSpec> inputTypes, bool selected, bool isFailPathRun, Options options, IEnumerable<TypeSpec> allTypes)
4242
{
4343
if (isEmpty) return inputTypes;
4444

45-
var context = new FunctionSequenceExecutionContext(isFailPathRun, options);
45+
var context = new FunctionSequenceExecutionContext(allTypes, isFailPathRun, options);
4646
MarkPassingTypes(context, inputTypes);
4747

4848
return inputTypes.Where(x => x.IsSelectedInMarkPhase == selected).ToArray();

src/NetArchTest.Rules/RuleEngine/FunctionSequenceExecutionContext.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
using NetArchTest.Rules;
1+
using System.Collections.Generic;
2+
using NetArchTest.Assemblies;
3+
using NetArchTest.Rules;
24

35
namespace NetArchTest.RuleEngine
4-
{
6+
{
57
internal class FunctionSequenceExecutionContext
68
{
9+
public IEnumerable<TypeSpec> AllTypes { get; }
710
public bool IsFailPathRun { get; }
811
public IDependencyFilter DependencyFilter { get; }
912
public Options UserOptions { get; }
1013

1114

12-
public FunctionSequenceExecutionContext(bool isFailPathRun, Options options = null)
15+
public FunctionSequenceExecutionContext(IEnumerable<TypeSpec> allTypes, bool isFailPathRun, Options options = null)
1316
{
17+
AllTypes = allTypes;
1418
UserOptions = options ?? Options.Default;
1519
IsFailPathRun = isFailPathRun;
1620
}

src/NetArchTest.Rules/RuleEngine/RuleContext.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ public RuleContext(IEnumerable<TypeSpec> inpuTypes)
2323
public IEnumerable<TypeSpec> Execute(Options options)
2424
{
2525
IEnumerable<TypeSpec> result = types;
26-
result = PredicateContext.Sequence.Execute(result, options);
27-
result = ConditionContext.Sequence.Execute(result, options);
26+
result = PredicateContext.Sequence.Execute(result, options, types);
27+
result = ConditionContext.Sequence.Execute(result, options, types);
2828
return result;
2929
}
3030

3131
public TestResult GetResult(Options options)
3232
{
3333
bool success;
3434

35-
var filteredTypes = PredicateContext.Sequence.Execute(types, options);
36-
var passingTypes = ConditionContext.Sequence.Execute(filteredTypes, options);
35+
var filteredTypes = PredicateContext.Sequence.Execute(types, options, types);
36+
var passingTypes = ConditionContext.Sequence.Execute(filteredTypes, options, types);
3737

3838
if (ConditionContext.Should)
3939
{
@@ -52,7 +52,7 @@ public TestResult GetResult(Options options)
5252
}
5353

5454
// If we've failed, get a collection of failing types so these can be reported in a failing test.
55-
var failedTypes = ConditionContext.Sequence.ExecuteToGetFailingTypes(filteredTypes, selected: !ConditionContext.Should, options);
55+
var failedTypes = ConditionContext.Sequence.ExecuteToGetFailingTypes(filteredTypes, selected: !ConditionContext.Should, options, types);
5656
return TestResult.Failure(failedTypes);
5757
}
5858

test/NetArchTest.Rules.UnitTests/ConditionTests_Special.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Reflection;
33
using NetArchTest.Rules;
44
using NetArchTest.TestStructure.File;
5+
using NetArchTest.TestStructure.File.Correct;
6+
using NetArchTest.TestStructure.File.Incorrect.Yabadabado;
57
using NetArchTest.TestStructure.NameMatching.Namespace1;
68
using NetArchTest.TestStructure.Stateless;
79
using Xunit;
@@ -186,5 +188,31 @@ public void HaveSourceFilePathMatchingTypeNamespace_ShouldNot()
186188

187189
Assert.True(result.IsSuccessful);
188190
}
191+
192+
[Fact(DisplayName = "HaveMatchingTypeWithName_Should")]
193+
public void HaveMatchingTypeWithName_Should()
194+
{
195+
var result = Types
196+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
197+
.That()
198+
.AreOfType(typeof(CorrectSourceFileNameType))
199+
.Should()
200+
.HaveMatchingTypeWithName(x => x.Name + "Tests").GetResult();
201+
202+
Assert.True(result.IsSuccessful);
203+
}
204+
205+
[Fact(DisplayName = "HaveMatchingTypeWithName_ShouldNot")]
206+
public void HaveMatchingTypeWithName_ShouldNot()
207+
{
208+
var result = Types
209+
.InAssembly(Assembly.GetAssembly(typeof(SourceFileNameType)))
210+
.That()
211+
.AreNotOfType(typeof(CorrectSourceFileNameType))
212+
.ShouldNot()
213+
.HaveMatchingTypeWithName(x => x.Name + "Tests").GetResult();
214+
215+
Assert.True(result.IsSuccessful);
216+
}
189217
}
190218
}
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.Tests
6+
{
7+
internal class CorrectSourceFileNameTypeTests
8+
{
9+
}
10+
}

0 commit comments

Comments
 (0)