Skip to content

Commit

Permalink
Adds various new conventions (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
flakey-bit committed Jan 7, 2024
1 parent 754f500 commit 451c3bd
Show file tree
Hide file tree
Showing 17 changed files with 628 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/Core/Conventional.Tests/AllAssembliesScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public void GivenAPattern_LocatesAndReturnsAllAssembliesForThatPattern()
{
var assemblySpecimen = AllAssemblies.WithNamesMatching("*");

assemblySpecimen.Should().HaveCount(5);
assemblySpecimen.Should().HaveCount(6);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Conventional.Extensions;
using FluentAssertions;
using NUnit.Framework;

Expand Down Expand Up @@ -185,5 +185,212 @@ public void MustHaveCertainFilesBeContentCopyIfNewer_Regex()
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().EndWith("copy-not.png");
}

private AssemblySpecimen[] TestProjects =>
AllAssemblies.WithNamesMatching("*")
.Where(specimen => specimen.ProjectFilePath.Contains("Tests"))
.ToArray();

// Note: In practice, this list of assemblies would be used to drive further convention tests (i.e. assembly.GetTypes())
private static readonly List<Assembly> TestAssemblies = new List<Assembly>
{
typeof(DogFoodConventions).Assembly
};

[Test]
public void MustBeIncludedInSetOfAssemblies_Success()
{
var result = TestProjects
.MustConformTo(Convention.MustBeIncludedInSetOfAssemblies(TestAssemblies, "TestAssemblies"));

// TODO: Use result.Should().AllSatisfy() once we've updated to fluentassertions 6.5.0+
result.Select(x => x.IsSatisfied).Distinct().Single().Should().BeTrue();
}

[Test]
public void MustBeIncludedInSetOfAssemblies_Failure()
{
// ReSharper disable once CollectionNeverUpdated.Local
var staleTestAssemblies = new List<Assembly>();

var result = TestProjects
.MustConformTo(Convention.MustBeIncludedInSetOfAssemblies(staleTestAssemblies, "TestAssemblies"));

// TODO: Use result.Should().AllSatisfy() once we've updated to fluentassertions 6.5.0+
result.Select(x => x.IsSatisfied).Distinct().Single().Should().BeFalse();
}

[Test]
public void MustNotIncludeProjectReferences_Success()
{
var result = TheAssembly
.WithNameMatching("TestProjectTwo")
.MustConformTo(Convention.MustNotIncludeProjectReferences);

// Note: TestProjectTwo doesn't import any other projects (at time of writing)
result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustNotIncludeProjectReferences_Failure()
{
var result = TheAssembly
.WithNameMatching("Conventional.Tests")
.MustConformTo(Convention.MustNotIncludeProjectReferences); // Note: Conventional.Tests of course includes a reference to Conventional

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().StartWith("Conventional.Tests includes reference to project");
}

[Test]
public void MustReferencePackage_Success()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustReferencePackage("coverlet.collector"));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustReferencePackage_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustReferencePackage("koverlet.kollector"));

result.IsSatisfied.Should().BeFalse();
}

[Test]
public void MustNotReferencePackage_Success()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustNotReferencePackage("foo.bar.baz"));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustNotReferencePackage_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustNotReferencePackage("coverlet.collector"));

result.IsSatisfied.Should().BeFalse();
}

[Test]
public void MustSetPropertyValue_SingleValue_Success()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustSetPropertyValue("TheUniversalAnswer", "42"));

result.IsSatisfied.Should().BeTrue();
}

[Theory]
[TestCase("Potato")]
[TestCase("Carrot")]
public void MustSetPropertyValue_MultipleValues_Success(string value)
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustSetPropertyValue("Vegetable", value));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustSetPropertyValue_SingleValue_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustSetPropertyValue("TheUniversalAnswer", "41.999"));

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property TheUniversalAnswer with value 41.999");
}

[Test]
public void MustSetPropertyValue_MultipleValues_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustSetPropertyValue("Vegetable", "Turnip")); // Note: Assumes no <Vegetable>Turnip</Vegetable> in the csproj

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property Vegetable with value Turnip");
}

[Test]
public void MustSetPropertyValue_NoValues_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustSetPropertyValue("ThisPropertyShouldNeverEverExist", "x")); // Note: Assumes no <ThisPropertyShouldNeverEverExist>x</ThisPropertyShouldNeverEverExist> in the csproj

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property ThisPropertyShouldNeverEverExist with value x");
}

[Theory]
[TestCase("CS0162")]
[TestCase("CS4014")]
public void MustTreatWarningAsError_MultipleWarnings_Success(string warning)
{
var result = TheAssembly
.WithNameMatching("TestProjectTwo")
.MustConformTo(Convention.MustTreatWarningAsError(warning));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustTreatWarningAsError_SingleWarning_Success()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustTreatWarningAsError("CS0162"));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustTreatWarningAsError_MultipleWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("TestProjectTwo")
.MustConformTo(Convention.MustTreatWarningAsError("XX9999"));

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly TestProjectTwo should treat warning XX9999 as an error but does not");
}

[Test]
public void MustTreatWarningAsError_SingleWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustTreatWarningAsError("XX9999"));

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly SdkClassLibrary1 should treat warning XX9999 as an error but does not");
}

[Test]
public void MustTreatWarningAsError_NoWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("TestSolution.TestProject")
.MustConformTo(Convention.MustTreatWarningAsError("CS0162"));

// Note: TestSolution.TestProject does not set the WarningsAsErrors property, at time of writing
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly TestSolution.TestProject should treat warning CS0162 as an error but does not");
}
}
}
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
}
}
Loading

0 comments on commit 451c3bd

Please sign in to comment.