Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds various new conventions #89

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
flakey-bit marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remark: I've introduced a region here to make it obvious that all of this is associated with the MustNotUseGuidNewGuid convention.

Might be worth splitting this file up.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too worried about splitting these test files up, they tend to be easy enough to peruse. Can we remove the #region though? It conflicts with my core beliefs 😆


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