Skip to content

Commit

Permalink
add rules: HaveNumberOfLinesOfCodeLowerThan / HaveNumberOfLinesOfCode…
Browse files Browse the repository at this point in the history
…GreaterThan
  • Loading branch information
NeVeSpl committed Dec 7, 2023
1 parent c5d8255 commit 1fa2adc
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ What **eNhancedEdition** has to offer, that is not available in the NetArchTest
- at the end, you get more information which should make reasoning about tests easier:


![revit-database-scripting-update-query](documentation/result.printscreen.png)
![revit-database-scripting-update-query](https://raw.githubusercontent.com/NeVeSpl/NetArchTest.eNhancedEdition/main/documentation/result.printscreen.png)



Expand Down
30 changes: 30 additions & 0 deletions sources/NetArchTest/Condition_Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
using NetArchTest.Functions;

namespace NetArchTest.Rules
{
public sealed partial class Condition
{
/// <summary>
/// Selects types that have more logical lines of code than a given number
/// </summary>
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public ConditionList HaveNumberOfLinesOfCodeGreaterThan(int number)
{
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveNumberOfLinesOfCodeFewerThan(context, inputTypes, number, false));
return CreateConditionList();
}

/// <summary>
/// Selects types that have fewer logical lines of code than a given number
/// </summary>
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public ConditionList HaveNumberOfLinesOfCodeLowerThan(int number)
{
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveNumberOfLinesOfCodeFewerThan(context, inputTypes, number, true));
return CreateConditionList();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.CodeDom.Compiler;
using System.Linq;
using System.Runtime.CompilerServices;
using Mono.Cecil;

namespace NetArchTest.Extensions.Mono.Cecil
{
internal static class MethodDefinitionExtensions
{
public static bool IsGeneratedCode(this MethodDefinition method)
{
if (method == null)
return false;

if (method.HasCustomAttributes == false)
return false;

return method.CustomAttributes.Any(x => x?.AttributeType?.FullName == typeof(CompilerGeneratedAttribute).FullName || x?.AttributeType?.FullName == typeof(GeneratedCodeAttribute).FullName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Mono.Cecil;

namespace NetArchTest.Extensions.Mono.Cecil
{
static internal partial class TypeDefinitionExtensions
{
public static int GetNumberOfLogicalLinesOfCode(this TypeDefinition type)
{
int count = 0;

if (type.HasMethods)
{
foreach (var method in type.Methods)
{
if (!method.HasBody) continue;
if (method.IsGeneratedCode()) continue;
if (method.DeclaringType.Module.HasSymbols == false) continue;

var methodLLOC = CountLogicalLinesOfCode(method);
count += methodLLOC;
}
}

return count;
}


private static int CountLogicalLinesOfCode(MethodDefinition method)
{
int count = 0;
int lastLine = int.MinValue;

foreach (var instruction in method.Body.Instructions)
{
var sequencePoint = method.DebugInformation.GetSequencePoint(instruction);
if (sequencePoint == null)
continue;

int line = sequencePoint.StartLine;
// special value for PDB (so that debuggers can ignore a line)
if (line == 0xFEEFEE)
continue;

if (line > lastLine)
count++;

lastLine = line;
}
return count;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Mono.Cecil
{
static internal class TypeDefinitionExtensions
static internal partial class TypeDefinitionExtensions
{
public static bool IsSubclassOf(this TypeReference child, TypeReference parent)
{
Expand Down
24 changes: 24 additions & 0 deletions sources/NetArchTest/Functions/FunctionDelegates_Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using NetArchTest.Assemblies;
using NetArchTest.Extensions.Mono.Cecil;
using NetArchTest.RuleEngine;

namespace NetArchTest.Functions
{
internal static partial class FunctionDelegates
{

internal static IEnumerable<TypeSpec> HaveNumberOfLinesOfCodeFewerThan(FunctionSequenceExecutionContext context, IEnumerable<TypeSpec> input, int number, bool condition)
{
if (condition)
{
return input.Where(c => c.Definition.GetNumberOfLogicalLinesOfCode() < number);
}
else
{
return input.Where(c => !(c.Definition.GetNumberOfLogicalLinesOfCode() < number));
}
}
}
}
30 changes: 30 additions & 0 deletions sources/NetArchTest/Predicate_Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
using NetArchTest.Functions;

namespace NetArchTest.Rules
{
public sealed partial class Predicate
{
/// <summary>
/// Selects types that have more logical lines of code than a given number
/// </summary>
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public PredicateList HaveNumberOfLinesOfCodeGreaterThan(int number)
{
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveNumberOfLinesOfCodeFewerThan(context, inputTypes, number, false));
return CreatePredicateList();
}

/// <summary>
/// Selects types that have fewer logical lines of code than a given number
/// </summary>
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
public PredicateList HaveNumberOfLinesOfCodeLowerThan(int number)
{
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveNumberOfLinesOfCodeFewerThan(context, inputTypes, number, true));
return CreatePredicateList();
}
}
}
44 changes: 44 additions & 0 deletions tests/NetArchTest.Rules.UnitTests/ConditionTests_Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NetArchTest.TestStructure.Metrics;
using NetArchTest.UnitTests.TestFixtures;
using Xunit;

namespace NetArchTest.UnitTests
{
public class ConditionTests_Metrics(AllTypesFixture fixture) : IClassFixture<AllTypesFixture>
{

[Fact(DisplayName = "HaveNumberOfLinesOfCodeLowerThan")]
public void HaveNumberOfLinesOfCodeLowerThan()
{
var result = fixture.Types
.That()
.ResideInNamespace(typeof(ClassSmall).Namespace)
.And()
.AreOfType(typeof(ClassSmall))
.Should()
.HaveNumberOfLinesOfCodeLowerThan(13).GetResult();

Assert.True(result.IsSuccessful);
}


[Fact(DisplayName = "HaveNumberOfLinesOfCodeGreaterThan")]
public void HaveNumberOfLinesOfCodeGreaterThan()
{
var result = fixture.Types
.That()
.ResideInNamespace(typeof(ClassSmall).Namespace)
.And()
.AreOfType(typeof(ClassLarge))
.Should()
.HaveNumberOfLinesOfCodeGreaterThan(13).GetResult();

Assert.True(result.IsSuccessful);
}
}
}
41 changes: 41 additions & 0 deletions tests/NetArchTest.Rules.UnitTests/PredicateTests_Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using NetArchTest.TestStructure.Metrics;
using NetArchTest.UnitTests.TestFixtures;
using Xunit;
using static NetArchTest.Utils;

namespace NetArchTest.UnitTests
{
public class PredicateTests_Metrics(AllTypesFixture fixture) : IClassFixture<AllTypesFixture>
{

[Fact(DisplayName = "HaveNumberOfLinesOfCodeLowerThan")]
public void HaveNumberOfLinesOfCodeLowerThan()
{
var result = fixture.Types
.That()
.ResideInNamespace(namespaceof<ClassSmall>())
.And()
.HaveNumberOfLinesOfCodeLowerThan(13)
.GetReflectionTypes();

Assert.Single(result);
Assert.Contains<Type>(typeof(ClassSmall), result);
}

[Fact(DisplayName = "HaveNumberOfLinesOfCodeGreaterThan")]
public void HaveNumberOfLinesOfCodeGreaterThan()
{
var result = fixture.Types
.That()
.ResideInNamespace(namespaceof<ClassSmall>())
.And()
.HaveNumberOfLinesOfCodeGreaterThan(13)
.GetReflectionTypes();

Assert.Single(result);
Assert.Contains<Type>(typeof(ClassLarge), result);
}

}
}
127 changes: 127 additions & 0 deletions tests/NetArchTest.TestStructure/Metrics/ClassLarge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace NetArchTest.TestStructure.Metrics
{
// This class was generated by GPT-4 Turbo :D
public class ClassLarge
{
// Fields
private string owner;
private double balance;
private double interestRate;

// Constructors
public ClassLarge()
{
owner = "Unknown";
balance = 0.0;
interestRate = 0.0;
}

public ClassLarge(string owner, double balance, double interestRate)
{
this.owner = owner;
this.balance = balance;
this.interestRate = interestRate;
}

// Methods
public void Deposit(double amount)
{
// Check if the amount is positive
if (amount > 0)
{
// Add the amount to the balance
balance += amount;
// Print a confirmation message
Console.WriteLine("You have deposited {0:C} to your account.", amount);
}
else
{
// Print an error message
Console.WriteLine("Invalid amount. Please enter a positive number.");
}
}

public void Withdraw(double amount)
{
// Check if the amount is positive
if (amount > 0)
{
// Check if the amount is less than or equal to the balance
if (amount <= balance)
{
// Subtract the amount from the balance
balance -= amount;
// Print a confirmation message
Console.WriteLine("You have withdrawn {0:C} from your account.", amount);
}
else
{
// Print an error message
Console.WriteLine("Insufficient funds. You cannot withdraw more than your balance.");
}
}
else
{
// Print an error message
Console.WriteLine("Invalid amount. Please enter a positive number.");
}
}

public void Transfer(double amount, ClassLarge other)
{
// Check if the other account is not null
if (other != null)
{
// Check if the amount is positive
if (amount > 0)
{
// Check if the amount is less than or equal to the balance
if (amount <= balance)
{
// Subtract the amount from the balance
balance -= amount;
// Add the amount to the other account's balance
other.balance += amount;
// Print a confirmation message
Console.WriteLine("You have transferred {0:C} to {1}'s account.", amount, other.owner);
}
else
{
// Print an error message
Console.WriteLine("Insufficient funds. You cannot transfer more than your balance.");
}
}
else
{
// Print an error message
Console.WriteLine("Invalid amount. Please enter a positive number.");
}
}
else
{
// Print an error message
Console.WriteLine("Invalid account. Please enter a valid account.");
}
}

public void ApplyInterest()
{
// Calculate the interest amount
double interest = balance * interestRate / 100;
// Add the interest amount to the balance
balance += interest;
// Print a confirmation message
Console.WriteLine("You have earned {0:C} in interest.", interest);
}

// Override ToString method
public override string ToString()
{
return $"Owner: {owner}, Balance: {balance:C}, Interest Rate: {interestRate}%";
}
}
}
Loading

0 comments on commit 1fa2adc

Please sign in to comment.