Skip to content

Commit

Permalink
[andrewabestGH-87] Added new (Cecil based) convention to check that c…
Browse files Browse the repository at this point in the history
…ode doesn't directly call Guid.NewGuid()

A possible use-case is code written in a functional style (that wants to avoid non-deterministic behaviour)
  • Loading branch information
eddie.stanley committed Jan 6, 2024
1 parent 754f500 commit 4239440
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void
result.Failures.Should().HaveCount(1);
result.Failures.First().Should().Contain(nameof(DateTime));
}

private class GoodDateTimeOffsetCitizen
{
private DateTimeOffset _current;
Expand Down Expand Up @@ -150,7 +150,7 @@ public OffendingDateTimeOffsetCitizen()
_current = DateTimeOffset.Now;
}
}

private class AnotherOffendingDateTimeOffsetCitizen
{
private DateTimeOffset _current;
Expand Down Expand Up @@ -178,7 +178,7 @@ public void MustNotUseDateTimeOffsetNowConventionSpecification_FailsWhenACallToD
{
var result = new []
{
typeof (OffendingDateTimeOffsetCitizen),
typeof (OffendingDateTimeOffsetCitizen),
typeof(AnotherOffendingDateTimeOffsetCitizen),
typeof(AsyncOffendingDateTimeOffsetCitizen)
}
Expand Down Expand Up @@ -221,7 +221,7 @@ public BadExceptionThrower()
throw new Exception();
}
}


[Test]
public void ExceptionsThrownMustBeDerivedFromConventionSpecification_FailsIfExceptionDoesNotDeriveFromCorrectBase()
Expand Down Expand Up @@ -366,7 +366,7 @@ public void
result.IsSatisfied.Should().BeFalse();
result.Failures.Should().HaveCount(1);
}


private class HasMoreThanOneParameterizedConstructor
{
Expand All @@ -384,7 +384,7 @@ public HasMoreThanOneParameterizedConstructor(int id, string name)
public int Id { get; set; }
public string Name { get; set; }
}

private class HasNoConstructors
{
}
Expand Down Expand Up @@ -485,7 +485,7 @@ public void LibraryCodeShouldCallConfigureAwaitWhenAwaitingTasks_FailsWhenConfig
var expectedFailureMessage = "Libraries must call Task.ConfigureAwait(false) to prevent deadlocks"
+ Environment.NewLine
+ "- HasAnAsyncMethodThatAwaitsATaskAndDoesNotCallConfigureAwaitAndAnotherThatDoes.MethodThatAwaitsATaskAndDoesNotCallConfigureAwait";

var result = typeof(HasAnAsyncMethodThatAwaitsATaskAndDoesNotCallConfigureAwaitAndAnotherThatDoes)
.MustConformTo(Convention.LibraryCodeShouldCallConfigureAwaitWhenAwaitingTasks);

Expand Down Expand Up @@ -515,5 +515,63 @@ public void LibraryCodeShouldCallConfigureAwaitWhenAwaitingTasks_FailsWhenConfig
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be(expectedFailureMessage);
}

# region MustNotUseGuidNewGuid

private interface IGuidProvider
{
Guid CreateIdentifier();
}

private class GoodGuidCreationCitizen
{
public GoodGuidCreationCitizen(IGuidProvider guidProvider)
{
guidProvider.CreateIdentifier();
}
}

[Test]
public void MustNotUseGuidNewGuid_Success()
{
typeof(GoodGuidCreationCitizen)
.MustConformTo(Convention.MustNotUseGuidNewGuid)
.IsSatisfied
.Should()
.BeTrue();
}

private class OffendingGuidCreationCitizen
{
public OffendingGuidCreationCitizen()
{
Guid.NewGuid();
}
}

private class AsyncOffendingGuidCreationCitizen
{
public async Task<Guid> ResolveIdentifier()
{
await Task.Delay(1);
return Guid.NewGuid();
}
}

[Test]
public void MustNotUseGuidNewGuid_Failure()
{
var result = new []
{
typeof(OffendingGuidCreationCitizen),
typeof(AsyncOffendingGuidCreationCitizen)
}
.MustConformTo(Convention.MustNotUseGuidNewGuid);

result.Results.Should().OnlyContain(x => x.IsSatisfied == false);
result.Failures.Should().HaveCount(2);
}

# endregion
}
}
2 changes: 2 additions & 0 deletions src/Core/Conventional/Convention.Cecil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public static partial class Convention

public static MustNotUseDateTimeOffsetNowConventionSpecification MustNotUseDateTimeOffsetNow => new MustNotUseDateTimeOffsetNowConventionSpecification();

public static MustNotUseGuidNewGuidConventionSpecification MustNotUseGuidNewGuid => new MustNotUseGuidNewGuidConventionSpecification();

public static ExceptionsThrownMustBeDerivedFromConventionSpecification ExceptionsThrownMustBeDerivedFrom(Type baseType)
{
return new ExceptionsThrownMustBeDerivedFromConventionSpecification(baseType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Conventional.Conventions.Cecil
{
public class MustNotUseGuidNewGuidConventionSpecification :
MustNotCallMethodConventionSpecification
{
private static MethodInfo GetMethod()
{
Expression<Func<Guid>> expr = () => Guid.NewGuid();
return ((MethodCallExpression)expr.Body).Method;
}

public MustNotUseGuidNewGuidConventionSpecification()
: base(new[] { GetMethod() })
{
}
}
}

0 comments on commit 4239440

Please sign in to comment.