Permalink
Browse files

Very first implementation of an architecture rule engine

  • Loading branch information...
1 parent 69aa67d commit c3d2b1ad9f51f8fc71118d19d30756628a34599a @alexvictoor alexvictoor committed Feb 16, 2013
View
77 DependencyParser.Test/ArchitectureRuleEngineTest.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Mono.Cecil;
+using NUnit.Framework;
+
+namespace DependencyParser.Test {
+
+
+ [NUnit.Framework.TestFixture]
+ public class ArchitectureRuleEngineTest {
+
+ private ArchitectureRuleEngine engine;
+
+
+ [SetUp]
+ public void SetUp()
+ {
+ engine = new ArchitectureRuleEngine() {};
+ }
+
+ [Test]
+ public void Should_Find_Forbidden_Dependencies()
+ {
+ engine.Init("*:DependencyParser.*");
+ var dependencies = new TypeReference[] { typeof(Foo).GetCecilType() };
+ var result =
+ engine.FindArchitectureViolation(typeof(Bar).GetCecilType(), dependencies);
+ Assert.AreEqual(1, result.Count());
+ Assert.AreEqual("DependencyParser.Test.Foo", result.First().Dependency.FullName);
+ }
+
+ [Test]
+ public void Should_Not_Used_Unrelated_Rules_For_Source_Types()
+ {
+ engine.Init("Xyz.*:DependencyParser.*");
+ var dependencies = new TypeReference[] { typeof(Foo).GetCecilType() };
+ var result =
+ engine.FindArchitectureViolation(typeof(Bar).GetCecilType(), dependencies);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [Test]
+ public void Should_Not_Used_Unrelated_Rules_For_Destination_Types()
+ {
+ engine.Init("DependencyParser.*:xyz.*");
+ var dependencies = new TypeReference[] { typeof(Foo).GetCecilType() };
+ var result =
+ engine.FindArchitectureViolation(typeof(Bar).GetCecilType(), dependencies);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [Test]
+ public void Should_Not_Report_Twice_The_Same_Forbidden_Dependency()
+ {
+ engine.Init("*:DependencyParser.*,*:DependencyParser.Test.*");
+ var dependencies = new TypeReference[] { typeof(Foo).GetCecilType() };
+ var result =
+ engine.FindArchitectureViolation(typeof(Bar).GetCecilType(), dependencies);
+ Assert.AreEqual(1, result.Count());
+ Assert.AreEqual("DependencyParser.Test.Foo", result.First().Dependency.FullName);
+ }
+
+ }
+
+ public class Foo
+ {
+
+ }
+
+ public class Bar {
+
+ }
+
+}
View
20 DependencyParser.Test/CecilHelper.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Mono.Cecil;
+
+namespace DependencyParser.Test {
+
+ public static class CecilHelper {
+
+ public static TypeDefinition GetCecilType(this Type t)
+ {
+ string name = t.FullName;
+ string unit = Assembly.GetExecutingAssembly().Location;
+ var assembly = AssemblyDefinition.ReadAssembly(unit);
+ return assembly.MainModule.GetType(name);
+ }
+ }
+}
View
29 DependencyParser.Test/DependencyAnalyzerTest.cs
@@ -24,54 +24,45 @@ public void SetUp()
[Test]
public void Should_Find_Dependencies_From_Attributes()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(this.GetType()));
+ var dependencies = analyzer.FindTypeDependencies(this.GetType().GetCecilType());
Assert.AreEqual(1, dependencies.Count(n => n.FullName == typeof(TestAttribute).FullName));
}
[Test]
public void Should_Find_Dependencies_From_Fields()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(this.GetType()));
+ var dependencies = analyzer.FindTypeDependencies(this.GetType().GetCecilType());
Assert.AreEqual(1, dependencies.Count(n => n.FullName == typeof(DependencyAnalyzer).FullName));
}
[Test]
public void Should_Find_Dependencies_From_Properties()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(typeof(ClassWithProperty)));
- Assert.IsTrue(dependencies.Contains(GetType(typeof(Thread))));
+ var dependencies = analyzer.FindTypeDependencies(typeof(ClassWithProperty).GetCecilType());
+ Assert.IsTrue(dependencies.Contains(typeof(Thread).GetCecilType()));
}
[Test]
public void Should_Find_Dependencies_From_Parameters()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(typeof(ClassWithCallsOnParameters)));
- Assert.IsTrue(dependencies.Contains(GetType(typeof(Thread))));
+ var dependencies = analyzer.FindTypeDependencies(typeof(ClassWithCallsOnParameters).GetCecilType());
+ Assert.IsTrue(dependencies.Contains(typeof(Thread).GetCecilType()));
}
[Test]
public void Should_Find_Dependencies_From_Array()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(typeof(ClassWithArray)));
- Assert.IsTrue(dependencies.Contains(GetType(typeof(Thread))));
+ var dependencies = analyzer.FindTypeDependencies(typeof(ClassWithArray).GetCecilType());
+ Assert.IsTrue(dependencies.Contains(typeof(Thread).GetCecilType()));
}
[Test]
public void Should_Ignore_System_Dependencies()
{
- var dependencies = analyzer.FindTypeDependencies(GetType(typeof(ClassWithSystemDependency)));
+ var dependencies = analyzer.FindTypeDependencies(typeof(ClassWithSystemDependency).GetCecilType());
+ dependencies = analyzer.FilterSystemDependencies(dependencies);
Assert.AreEqual(0, dependencies.Count(n => n.FullName.Contains("Dictionary")));
}
-
- private TypeDefinition GetType(Type t)
- {
- string name = t.FullName;
- string unit = Assembly.GetExecutingAssembly().Location;
- var assembly = AssemblyDefinition.ReadAssembly(unit);
- return assembly.MainModule.GetType(name);
- }
-
-
}
public class ClassWithProperty {
View
6 DependencyParser.Test/DependencyParser.Test.csproj
@@ -56,13 +56,16 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ArchitectureRuleEngineTest.cs" />
+ <Compile Include="CecilHelper.cs" />
<Compile Include="DependencyAnalyzerTest.cs" />
<Compile Include="DependencyParserTest.cs" />
<Compile Include="DethOfInheritanceTreeAnalyzerTest.cs" />
<Compile Include="Lcom4AnalyzerTest.cs" />
<Compile Include="DesignMeasuresTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ResponseForClassAnalyzerTest.cs" />
+ <Compile Include="WildcardPatternMatcherTest.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="lcom4-expected.xml">
@@ -81,6 +84,9 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SubType>Designer</SubType>
</Content>
+ <Content Include="types-merged-expected.xml">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
View
52 DependencyParser.Test/DesignMeasuresTest.cs
@@ -13,29 +13,29 @@ namespace DependencyParser.Test {
[NUnit.Framework.TestFixture]
public class DesignMeasuresTest {
[Test]
- public void Should_Generate()
+ public void Should_Write_Correct_XML()
{
File.Delete("lcom4-generated.xml");
using (var stream = new FileStream("lcom4-generated.xml", FileMode.Create))
{
using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
{
writer.Formatting = Formatting.Indented;
- var designWriter = new DesignMeasures();
- var typeDefinition = GetType(typeof(SimpleClassWithTwoFields));
+ var measure = new DesignMeasures();
+ var typeDefinition = typeof(SimpleClassWithTwoFields).GetCecilType();
var blocks = new HashSet<HashSet<MemberReference>>();
foreach (var mth in typeDefinition.Methods)
{
var block = new HashSet<MemberReference> { mth, typeDefinition.Fields.First() };
blocks.Add(block);
}
- designWriter.Type = typeDefinition;
- designWriter.Lcom4Blocks = blocks;
- designWriter.ResponseForClass = 42;
- designWriter.DethOfInheritance = 17;
+ measure.Type = typeDefinition;
+ measure.Lcom4Blocks = blocks;
+ measure.ResponseForClass = 42;
+ measure.DethOfInheritance = 17;
- designWriter.Write(writer);
+ measure.Write(writer);
}
}
string expected = File.ReadAllText("lcom4-expected.xml");
@@ -44,12 +44,38 @@ public void Should_Generate()
Assert.AreEqual(expected, result);
}
- private TypeDefinition GetType(Type t)
+ [Test]
+ public void Should_Merge_Measures()
{
- string name = t.FullName;
- string unit = Assembly.GetExecutingAssembly().Location;
- var assembly = AssemblyDefinition.ReadAssembly(unit);
- return assembly.MainModule.GetType(name);
+ File.Delete("types-merged-generated.xml");
+ var measure = new DesignMeasures();
+ var typeDefinition = typeof(SimpleClassWithTwoFields).GetCecilType();
+ measure.Type = typeDefinition;
+ measure.ResponseForClass = 42;
+ measure.DethOfInheritance = 17;
+ measure.Lcom4Blocks = Enumerable.Empty<IEnumerable<MemberReference>>();
+
+ var measure2 = new DesignMeasures();
+ var typeDefinition2 = typeof(DesignMeasuresTest).GetCecilType();
+ measure2.Type = typeDefinition2;
+ measure2.ResponseForClass = 36;
+ measure2.DethOfInheritance = 42;
+ measure2.Lcom4Blocks = Enumerable.Empty<IEnumerable<MemberReference>>();
+
+ var mergedMeasures = measure.Merge(measure2);
+ using (var stream = new FileStream("types-merged-generated.xml", FileMode.Create))
+ {
+ using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
+ {
+ writer.Formatting = Formatting.Indented;
+ mergedMeasures.Write(writer);
+ }
+ }
+
+ string expected = File.ReadAllText("types-merged-expected.xml");
+ string result = File.ReadAllText("types-merged-generated.xml");
+ Assert.AreEqual(expected, result);
+
}
}
View
11 DependencyParser.Test/DethOfInheritanceTreeAnalyzerTest.cs
@@ -14,18 +14,9 @@ public class DethOfInheritanceTreeAnalyzerTest {
[Test]
public void Should_Compute_DIT()
{
- var t = GetType(typeof(DethOfInheritanceTreeAnalyzerTest));
+ var t = typeof(DethOfInheritanceTreeAnalyzerTest).GetCecilType();
var result = new DethOfInheritanceTreeAnalyzer().ComputeDIT(t);
Assert.AreEqual(1, result);
}
-
-
- private TypeDefinition GetType(Type t)
- {
- string name = t.FullName;
- string unit = Assembly.GetExecutingAssembly().Location;
- var assembly = AssemblyDefinition.ReadAssembly(unit);
- return assembly.MainModule.GetType(name);
- }
}
}
View
49 DependencyParser.Test/Lcom4AnalyzerTest.cs
@@ -18,23 +18,23 @@ public class Lcom4AnalyzerTest {
public void Should_Find_One_Block_On_A_Simple_Class()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClass).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Find_Two_Blocks_On_A_Simple_Stateless_Class()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleCalculator)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleCalculator).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
[Test]
public void Should_Find_One_Block_On_A_Class_With_Strong_Cohesion()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithManyMethodsAndStrongCohesion)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithManyMethodsAndStrongCohesion).GetCecilType());
Assert.AreEqual(1, blocks.Count);
Assert.AreEqual(5, blocks.ElementAt(0).Count);
}
@@ -43,111 +43,111 @@ public void Should_Find_One_Block_On_A_Class_With_Strong_Cohesion()
public void Should_Ignore_Simple_Accessors()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithProperties)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithProperties).GetCecilType());
Assert.AreEqual(0, blocks.Count);
}
[Test]
public void Should_Take_In_Account_Calls_To_Simple_Accessors()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithCallsToAccessors)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithCallsToAccessors).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Take_In_Account_Complex_Accessors()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithComplexProperties)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithComplexProperties).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
[Test]
public void Should_Ignore_Abstract_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(AbstractClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(AbstractClass).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Take_In_Account_Calls_To_Abstract_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(AbstractTemplateClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(AbstractTemplateClass).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Ignore_Empty_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithEmptyMethods)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithEmptyMethods).GetCecilType());
Assert.AreEqual(0, blocks.Count);
}
[Test]
public void Should_Take_In_Account_Calls_Ignore_Empty_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(ClassWithCallsToEmptyMethods)));
+ var blocks = analyzer.FindLcomBlocks(typeof(ClassWithCallsToEmptyMethods).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Ignore_Constructors()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClassWithCtr)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClassWithCtr).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
[Test]
public void Should_Not_Fail_On_Empty_Interface()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(IEmpty)));
+ var blocks = analyzer.FindLcomBlocks(typeof(IEmpty).GetCecilType());
Assert.AreEqual(0, blocks.Count);
}
[Test]
public void Should_Not_Fail_On_Empty_Class()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(EmptyClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(EmptyClass).GetCecilType());
Assert.AreEqual(0, blocks.Count);
}
[Test]
public void Should_Not_Take_In_Account_Dispose_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleDisposableClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleDisposableClass).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
[Test]
public void Should_Not_Take_In_Account_Equals_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClassWithEquals)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClassWithEquals).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
[Test]
public void Should_Not_Take_In_Account_ToString_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClassWithToString)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClassWithToString).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
[Test]
public void Should_Not_Take_In_Account_Methods_From_Wrapped_Objects()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClassWithDelegation)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClassWithDelegation).GetCecilType());
Assert.AreEqual(1, blocks.Count);
Assert.AreEqual(3, blocks.ElementAt(0).Count);
}
@@ -156,7 +156,7 @@ public void Should_Not_Take_In_Account_Methods_From_Wrapped_Objects()
public void Should_Not_Take_In_Account_Inherited_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(DerivedClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(DerivedClass).GetCecilType());
Assert.AreEqual(1, blocks.Count);
Assert.AreEqual(3, blocks.ElementAt(0).Count);
}
@@ -165,7 +165,7 @@ public void Should_Not_Take_In_Account_Inherited_Methods()
public void Should_Not_Take_In_Account_Static_Methods()
{
var analyzer = new Lcom4Analyzer();
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClassWithStaticMethod)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClassWithStaticMethod).GetCecilType());
Assert.AreEqual(1, blocks.Count);
}
@@ -196,18 +196,9 @@ public void Should_Take_In_Account_The_Ignorable_Field_Names()
{
var analyzer = new Lcom4Analyzer() { IgnorableFieldNames = new string[] {"Counter"} } ;
- var blocks = analyzer.FindLcomBlocks(GetType(typeof(SimpleClass)));
+ var blocks = analyzer.FindLcomBlocks(typeof(SimpleClass).GetCecilType());
Assert.AreEqual(2, blocks.Count);
}
-
- private TypeDefinition GetType(Type t)
- {
- string name = t.FullName;
- string unit = Assembly.GetExecutingAssembly().Location;
- var assembly = AssemblyDefinition.ReadAssembly(unit);
- return assembly.MainModule.GetType(name);
- }
-
}
public class EmptyClass
View
15 DependencyParser.Test/ResponseForClassAnalyzerTest.cs
@@ -20,33 +20,24 @@ public void Should_Compute_RFC()
{
var analyzer = new ResponseForClassAnalyzer();
- Assert.AreEqual(6, analyzer.ComputeRFC(GetType("DependencyParser.Test.ClassA")));
+ Assert.AreEqual(6, analyzer.ComputeRFC(typeof(ClassA).GetCecilType()));
}
[Test]
public void Should_Ignore_Property_Accessors()
{
var analyzer = new ResponseForClassAnalyzer();
- Assert.AreEqual(1, analyzer.ComputeRFC(GetType("DependencyParser.Test.Country")));
+ Assert.AreEqual(1, analyzer.ComputeRFC(typeof(Country).GetCecilType()));
}
[Test]
public void Should_Take_In_Account_Computed_Properties()
{
var analyzer = new ResponseForClassAnalyzer();
- Assert.AreEqual(3, analyzer.ComputeRFC(GetType("DependencyParser.Test.Employee")));
+ Assert.AreEqual(3, analyzer.ComputeRFC(typeof(Employee).GetCecilType()));
}
-
- private TypeDefinition GetType(string name)
- {
- string unit = Assembly.GetExecutingAssembly().Location;
- var assembly = AssemblyDefinition.ReadAssembly(unit);
- return assembly.MainModule.GetType(name);
- }
-
-
}
public class ClassA {
View
42 DependencyParser.Test/WildcardPatternMatcherTest.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace DependencyParser.Test {
+
+ [NUnit.Framework.TestFixture]
+ public class WildcardPatternMatcherTest {
+
+ [Test]
+ public void Should_Evaluate_Patterns()
+ {
+ // Positive Tests
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*", ""));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("?", " "));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*", "a"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*", "ab"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("?", "a"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*?", "abc"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("?*", "abc"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*abc", "abc"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*abc*", "abc"));
+ Assert.IsTrue(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "aXXXbc"));
+
+ // Negative Tests
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*a", ""));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("a*", ""));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("?", ""));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*b*", "a"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("b*a", "ab"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("??", "a"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*?", ""));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("??*", "a"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*abc", "abX"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*abc*", "Xbc"));
+ Assert.IsFalse(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "ac"));
+ }
+
+ }
+}
View
6 DependencyParser.Test/testdata/test.xml
@@ -9,17 +9,17 @@
<To fullname="Example.Core.Money" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
<To fullname="Example.Core.MoneyBag" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
</From>
- <From fullname="Example.Core.Money">
+ <From fullname="Example.Core.Money" path="c:\work\sonar-cs\dotnet\tools\dotnet-tools-commons\src\test\resources\solution\Example\Example.Core\Money.cs">
<To fullname="Example.Core.IMoney" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
<To fullname="Example.Core.MoneyBag" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
</From>
- <From fullname="Example.Core.MoneyBag">
+ <From fullname="Example.Core.MoneyBag" path="c:\work\sonar-cs\dotnet\tools\dotnet-tools-commons\src\test\resources\solution\Example\Example.Core\MoneyBag.cs">
<To fullname="Example.Core.IMoney" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
<To fullname="Example.Core.Money" assemblyname="Example.Core" assemblyversion="1.0.0.0" />
</From>
</TypeReferences>
<Design>
- <type fullName="Example.Core.Money" source="c:\work\sonar-cs\dotnet\tools\dotnet-tools-commons\src\test\resources\solution\Example\Example.Core\Money.cs" rfc="24" dit="1">
+ <type fullName="Example.Core.Money" mergedTypes="Example.Core.Money,Example.Core.Alex" source="c:\work\sonar-cs\dotnet\tools\dotnet-tools-commons\src\test\resources\solution\Example\Example.Core\Money.cs" rfc="24" dit="1">
<block>
<element type="Field" name="fAmount" />
<element type="Field" name="fCurrency" />
View
1 DependencyParser.Test/types-merged-expected.xml
@@ -0,0 +1 @@
+<type fullName="DependencyParser.Test.SimpleClassWithTwoFields" mergedTypes="DependencyParser.Test.SimpleClassWithTwoFields,DependencyParser.Test.DesignMeasuresTest" source="" rfc="42" dit="42" />
View
25 DependencyParser/ArchitectureRule.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Mono.Cecil;
+
+namespace DependencyParser {
+
+ public class ArchitectureRule {
+
+ public string FromPattern { get; set; }
+
+ public string ToPattern { get; set; }
+
+ public bool IsFromTypeApplies(TypeReference type)
+ {
+ return WildcardPatternMatcher.MatchWildcardString(FromPattern, type.FullName);
+ }
+
+ public bool IsToTypeApplies(TypeReference type)
+ {
+ return WildcardPatternMatcher.MatchWildcardString(ToPattern, type.FullName);
+ }
+ }
+}
View
57 DependencyParser/ArchitectureRuleEngine.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Mono.Cecil;
+
+namespace DependencyParser {
+
+
+ public class ArchitectureRuleEngine {
+
+ private IEnumerable<ArchitectureRule> rules = Enumerable.Empty<ArchitectureRule>();
+
+ public void Init(string s)
+ {
+ if (s == null) {
+ return;
+ }
+ var fromToPairs = s.Split(',');
+
+ rules =
+ from pair in fromToPairs
+ select new ArchitectureRule() {
+ FromPattern = pair.Split(':')[0],
+ ToPattern = pair.Split(':')[1]
+ };
+ }
+
+ public IEnumerable<ArchitectureViolation> FindArchitectureViolation(TypeDefinition fromType, IEnumerable<TypeReference> toTypes)
+ {
+ var applicableRules =
+ from r in rules
+ where r.IsFromTypeApplies(fromType)
+ select r;
+
+ var badDependencies =
+ from d in toTypes
+ from r in applicableRules
+ where r.IsToTypeApplies(d)
+ select new ArchitectureViolation() {Subject = fromType, Dependency = d, Rule = r};
+
+ return badDependencies.Distinct(new ViolationEqualityComparer());
+ }
+ }
+
+ public class ViolationEqualityComparer : IEqualityComparer<ArchitectureViolation> {
+ public bool Equals(ArchitectureViolation v1, ArchitectureViolation v2)
+ {
+ return v1.Dependency.FullName == v2.Dependency.FullName;
+ }
+
+ public int GetHashCode(ArchitectureViolation v)
+ {
+ return v.Dependency.FullName.GetHashCode();
+ }
+ }
+}
View
33 DependencyParser/ArchitectureViolation.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Mono.Cecil;
+
+namespace DependencyParser {
+
+ public class ArchitectureViolation {
+
+ public ArchitectureRule Rule { get; set; }
+
+ public TypeDefinition Subject { get; set; }
+
+ public TypeReference Dependency { get; set;}
+
+ public void Write(XmlTextWriter xml)
+ {
+ xml.WriteStartElement("Violation");
+ xml.WriteAttributeString("fullname", Subject.FullName);
+ var path = Subject.GetSourcePath();
+ if (path != null) {
+ xml.WriteAttributeString("path", path);
+ }
+ xml.WriteAttributeString("fromPattern", Rule.FromPattern);
+ xml.WriteAttributeString("toPattern", Rule.ToPattern);
+ xml.WriteAttributeString("dependency", Dependency.FullName);
+ xml.WriteEndElement();
+
+ }
+ }
+}
View
18 DependencyParser/DependencyAnalyzer.cs
@@ -9,7 +9,7 @@ namespace DependencyParser {
/// <summary>
- /// TODO: Update summary.
+ /// Type analyzer that provides dependencies to other classes
/// </summary>
public class DependencyAnalyzer {
@@ -21,6 +21,19 @@ public IEnumerable<TypeReference> FindTypeDependencies(TypeDefinition t)
return result;
}
+ /// <summary>
+ /// Remove dependencies to .Net framework
+ /// </summary>
+ /// <param name="unfilteredDependencies"></param>
+ /// <returns></returns>
+ public IEnumerable<TypeReference> FilterSystemDependencies(IEnumerable<TypeReference> unfilteredDependencies)
+ {
+ return from t in unfilteredDependencies
+ where
+ !(t.Scope.Name.Equals("mscorlib") || t.Scope.Name.StartsWith("System") || t.Scope.Name.StartsWith("Microsoft"))
+ select t;
+ }
+
public void FindTypeDependencies(ISet<TypeReference> result, TypeDefinition t)
{
// ignore generated types
@@ -86,8 +99,7 @@ public void FindTypeDependencies(ISet<TypeReference> result, TypeDefinition t)
private void AddDependency(ISet<TypeReference> result, TypeReference to)
{
// ignore generic parameters
- // and ignore types from .Net framework
- if (to.IsGenericParameter || to.Namespace.Equals(string.Empty) || to.Scope.Name.Equals("mscorlib") || to.Scope.Name.StartsWith("System") || to.Scope.Name.StartsWith("Microsoft")) {
+ if (to.IsGenericParameter || to.Namespace.Equals(string.Empty)) {
return;
}
View
4 DependencyParser/DependencyParser.csproj
@@ -78,6 +78,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ArchitectureRule.cs" />
+ <Compile Include="ArchitectureRuleEngine.cs" />
+ <Compile Include="ArchitectureViolation.cs" />
<Compile Include="DependencyAnalyzer.cs" />
<Compile Include="DethOfInheritanceTreeAnalyzer.cs" />
<Compile Include="Lcom4Analyzer.cs" />
@@ -87,6 +90,7 @@
<Compile Include="ResponseForClassAnalyzer.cs" />
<Compile Include="SourceHelper.cs" />
<Compile Include="SourceRegistry.cs" />
+ <Compile Include="WildcardPatternMatcher.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
View
26 DependencyParser/DesignMeasures.cs
@@ -24,10 +24,30 @@ public class DesignMeasures {
public IEnumerable<IEnumerable<MemberReference>> Lcom4Blocks { set; private get; }
+ private IEnumerable<string> mergedTypes;
+ private IEnumerable<string> MergedTypesNames
+ {
+ set
+ {
+ mergedTypes = value;
+ }
+
+ get
+ {
+ if (mergedTypes == null)
+ {
+ return new string[] { Type.FullName };
+ }
+ return mergedTypes;
+ }
+ }
+
+
public DesignMeasures Merge(DesignMeasures measures)
{
return new DesignMeasures() {
Type = Type,
+ MergedTypesNames = MergedTypesNames.Union(measures.MergedTypesNames),
ResponseForClass = Math.Max(ResponseForClass, measures.ResponseForClass),
DethOfInheritance = Math.Max(DethOfInheritance, measures.DethOfInheritance),
Lcom4Blocks = Lcom4Blocks.Union(measures.Lcom4Blocks)
@@ -38,6 +58,12 @@ public void Write(XmlTextWriter xml)
{
xml.WriteStartElement("type");
xml.WriteAttributeString("fullName", Type.FullName);
+
+ if (MergedTypesNames.Count()>1)
+ {
+ xml.WriteAttributeString("mergedTypes", string.Join(",", MergedTypesNames));
+ }
+
xml.WriteAttributeString("source", Type.GetSourcePath());
xml.WriteAttributeString("rfc", ResponseForClass.ToString());
xml.WriteAttributeString("dit", DethOfInheritance.ToString());
View
48 DependencyParser/Program.cs
@@ -25,6 +25,9 @@ public class Program
private static readonly DependencyAnalyzer dependencyAnalyzer = new DependencyAnalyzer();
+ private static readonly ArchitectureRuleEngine architectureRuleEngine = new ArchitectureRuleEngine();
+
+
private static bool designAnalysis = false;
public static void Main(string[] args)
@@ -40,6 +43,7 @@ public static void Main(string[] args)
{ "o|output=", "the path to the output XML", v => outputPath = v },
{ "h|help", "show this message and exit", v => showHelp = v != null },
{ "d|design", "flag that enables design analysis", d => designAnalysis = d != null },
+ { "r|rules=", "Architecture rules comma separated", r => architectureRuleEngine.Init(r) },
{ "i|ignorable_fields=", "When design analysis is enabled, comma list of names of fields that should not be taken in account for LCOM4 analysis", list => ignorableFieldNames = list.Split(',') }
};
@@ -264,25 +268,41 @@ private static void GenerateMultiTypeDesignMeasures(XmlTextWriter writer, Module
public static void ParseType(XmlTextWriter writer, TypeDefinition t)
{
var dependencies = dependencyAnalyzer.FindTypeDependencies(t);
+
if (dependencies != null && dependencies.Count() > 0) {
- writer.WriteStartElement("From");
- writer.WriteAttributeString("fullname", t.FullName);
-
- foreach (var to in dependencies) {
- writer.WriteStartElement("To");
- writer.WriteAttributeString("fullname", to.FullName);
- if (to.Scope is ModuleDefinition) {
- writer.WriteAttributeString("assemblyname", ((ModuleDefinition)to.Scope).Assembly.Name.Name);
- writer.WriteAttributeString("assemblyversion", ((ModuleDefinition)to.Scope).Assembly.Name.Version.ToString());
- } else if (to.Scope is AssemblyNameReference) {
- writer.WriteAttributeString("assemblyname", ((AssemblyNameReference)to.Scope).Name);
- writer.WriteAttributeString("assemblyversion", ((AssemblyNameReference)to.Scope).Version.ToString());
+ var filteredDependencies = dependencyAnalyzer.FilterSystemDependencies(dependencies);
+ if (filteredDependencies.Count() > 0)
+ {
+ writer.WriteStartElement("From");
+ writer.WriteAttributeString("fullname", t.FullName);
+
+ var path = t.GetSourcePath();
+ if (path != null)
+ {
+ writer.WriteAttributeString("path", path);
}
+ foreach (var to in filteredDependencies) {
+ writer.WriteStartElement("To");
+ writer.WriteAttributeString("fullname", to.FullName);
+
+ if (to.Scope is ModuleDefinition) {
+ writer.WriteAttributeString("assemblyname", ((ModuleDefinition)to.Scope).Assembly.Name.Name);
+ writer.WriteAttributeString("assemblyversion", ((ModuleDefinition)to.Scope).Assembly.Name.Version.ToString());
+ } else if (to.Scope is AssemblyNameReference) {
+ writer.WriteAttributeString("assemblyname", ((AssemblyNameReference)to.Scope).Name);
+ writer.WriteAttributeString("assemblyversion", ((AssemblyNameReference)to.Scope).Version.ToString());
+ }
+
+ writer.WriteEndElement();
+ }
writer.WriteEndElement();
}
-
- writer.WriteEndElement();
+ var violations = architectureRuleEngine.FindArchitectureViolation(t, dependencies);
+ foreach (var violation in violations)
+ {
+ violation.Write(writer);
+ }
}
}
View
48 DependencyParser/WildcardPatternMatcher.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DependencyParser {
+
+ /// <summary>
+ /// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings
+ ///
+ /// </summary>
+ public static class WildcardPatternMatcher {
+
+ public static Boolean MatchWildcardString(String pattern, String input)
+ {
+ if (String.Compare(pattern, input) == 0) {
+ return true;
+ } else if (String.IsNullOrEmpty(input)) {
+ if (String.IsNullOrEmpty(pattern.Trim(new Char[1] { '*' }))) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (pattern.Length == 0) {
+ return false;
+ } else if (pattern[0] == '?') {
+ return MatchWildcardString(pattern.Substring(1), input.Substring(1));
+ } else if (pattern[pattern.Length - 1] == '?') {
+ return MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input.Substring(0, input.Length - 1));
+ } else if (pattern[0] == '*') {
+ if (MatchWildcardString(pattern.Substring(1), input)) {
+ return true;
+ } else {
+ return MatchWildcardString(pattern, input.Substring(1));
+ }
+ } else if (pattern[pattern.Length - 1] == '*') {
+ if (MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input)) {
+ return true;
+ } else {
+ return MatchWildcardString(pattern, input.Substring(0, input.Length - 1));
+ }
+ } else if (pattern[0] == input[0]) {
+ return MatchWildcardString(pattern.Substring(1), input.Substring(1));
+ }
+ return false;
+ }
+ }
+}

0 comments on commit c3d2b1a

Please sign in to comment.