Skip to content

Commit

Permalink
Move Empty(Test)Type attributes to contracts.
Browse files Browse the repository at this point in the history
  • Loading branch information
Corniel committed Feb 18, 2024
1 parent dd2bf68 commit 31ffa93
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Expand Up @@ -100,6 +100,7 @@ dotnet_diagnostic.SA1515.severity = suggestion # Single-line comment should be p

dotnet_diagnostic.S100.severity = none # Properties should be named in PascalCase
dotnet_diagnostic.S101.severity = none # Types should be named in PascalCase
dotnet_diagnostic.S1694.severity = none # An abstract class should have both abstract and concrete methods
dotnet_diagnostic.S2328.severity = none # GetHashCode() should not reference mutable fields
dotnet_diagnostic.S2178.severity = none # Short-circuit logic should be used in boolean contexts - '&' is used instead of && to accommodate expressions with nullable booleans too.
dotnet_diagnostic.S3376.severity = none # EventArgs, and Exception type names should end with the type being extended
Expand All @@ -122,7 +123,6 @@ dotnet_diagnostic.S1227.severity = warning # break statements should not be used
dotnet_diagnostic.S1479.severity = warning # Consider reworking this 'switch' to reduce the number of 'case's to at most 30
dotnet_diagnostic.S1541.severity = warning # Methods and properties should not be too complex
dotnet_diagnostic.S1858.severity = warning # "ToString()" calls should not be redundant
dotnet_diagnostic.S1694.severity = warning # An abstract class should have both abstract and concrete methods
dotnet_diagnostic.S2197.severity = warning # Modulus results should not be checked for direct equa
dotnet_diagnostic.S2302.severity = warning # "nameof" should be used
dotnet_diagnostic.S2342.severity = warning # Rename this enumeration to match naming convention
Expand Down
1 change: 0 additions & 1 deletion specs/Qowaiv.Benchmarks/UuidBenchmark.cs
@@ -1,6 +1,5 @@
using Qowaiv;
using Qowaiv.Identifiers;
using Qowaiv.TestTools;

namespace Benchmarks;

Expand Down
43 changes: 18 additions & 25 deletions specs/Qowaiv.Specs/Diagnostics/Contracts/Conditional_specs.cs
@@ -1,29 +1,22 @@
namespace Diagnostics.Contracts.Conditional_specs;

public class Is_decorated_with_condtional_CONTRACTS_FULL
public class Is_decorated_with
{
[Test]
public void Collection_mutation_attribute()
=> typeof(Qowaiv.Diagnostics.Contracts.CollectionMutationAttribute)
.Should().DecoratedWithConditionalAttribute("CONTRACTS_FULL");

[Test]
public void Fluent_syntax_attribute()
=> typeof(Qowaiv.Diagnostics.Contracts.FluentSyntaxAttribute)
.Should().DecoratedWithConditionalAttribute("CONTRACTS_FULL");

[Test]
public void Impure_attribute()
=> typeof(Qowaiv.Diagnostics.Contracts.ImpureAttribute)
.Should().DecoratedWithConditionalAttribute("CONTRACTS_FULL");

[Test]
public void Inheritable_attribute()
=> typeof(Qowaiv.Diagnostics.Contracts.InheritableAttribute)
.Should().DecoratedWithConditionalAttribute("CONTRACTS_FULL");

[Test]
public void Mutable_attribute()
=> typeof(Qowaiv.Diagnostics.Contracts.MutableAttribute)
.Should().DecoratedWithConditionalAttribute("CONTRACTS_FULL");
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.CollectionMutationAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.FluentSyntaxAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.ImpureAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.InheritableAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.MutableAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyTypeAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyClassAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyEnumAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyInterfaceAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyStructAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyTestClassAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyTestEnumAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyTestInterfaceAttribute))]
[TestCase(typeof(Qowaiv.Diagnostics.Contracts.EmptyTestStructAttribute))]
public void condtional_CONTRACTS_FULL(Type attribute)
=> attribute.Should().BeDecoratedWith<ConditionalAttribute>()
.Which.ConditionString.Should().Be("CONTRACTS_FULL");
}
@@ -0,0 +1,32 @@
namespace Diagnostics.Contracts.Empty_type_attribute_specs;

public class Can_decorate_empty
{
[Test]
public void Classes()
=> typeof(SomeEmptyClass).Should().BeDecoratedWith<EmptyTestClassAttribute>();

[Test]
public void Enums()
=> typeof(SomeEmptyEnumeration).Should().BeDecoratedWith<EmptyTestEnumAttribute>();

[Test]
public void Interfaces()
=> typeof(ISomeEmptyInterface).Should().BeDecoratedWith<EmptyTestInterfaceAttribute>();

[Test]
public void Structs()
=> typeof(SomeEmptyStruct).Should().BeDecoratedWith<EmptyTestStructAttribute>();
}

[EmptyTestClass]
internal class SomeEmptyClass { }

[EmptyTestEnum]
internal enum SomeEmptyEnumeration { }

[EmptyTestInterface]
internal interface ISomeEmptyInterface { }

[EmptyTestStruct]
internal struct SomeEmptyStruct { }
16 changes: 0 additions & 16 deletions specs/Qowaiv.Specs/FluentAssertions/System.Type.Assertions.cs

This file was deleted.

57 changes: 57 additions & 0 deletions src/Qowaiv.Diagnostics.Contracts/EmptyTypeAttribute.cs
@@ -0,0 +1,57 @@
namespace Qowaiv.Diagnostics.Contracts;

/// <summary>Indicates the type is empty by design.</summary>
[Conditional("CONTRACTS_FULL")]
public abstract class EmptyTypeAttribute(string justification) : Attribute

Check warning on line 5 in src/Qowaiv.Diagnostics.Contracts/EmptyTypeAttribute.cs

View workflow job for this annotation

GitHub Actions / Build

Specify AttributeUsage on 'EmptyTypeAttribute'. (https://rules.sonarsource.com/csharp/RSPEC-3993)

Check warning on line 5 in src/Qowaiv.Diagnostics.Contracts/EmptyTypeAttribute.cs

View workflow job for this annotation

GitHub Actions / Build

Specify AttributeUsage on 'EmptyTypeAttribute'. (https://rules.sonarsource.com/csharp/RSPEC-3993)

Check warning on line 5 in src/Qowaiv.Diagnostics.Contracts/EmptyTypeAttribute.cs

View workflow job for this annotation

GitHub Actions / Build

Specify AttributeUsage on 'EmptyTypeAttribute'. (https://rules.sonarsource.com/csharp/RSPEC-3993)
{
/// <summary>THe justification of this decoration.</summary>
public string Justification { get; } = justification;
}

/// <summary>Indicates the class is empty by design.</summary>
/// <remarks>
/// Using this attribute prevents S2094 (Classes should not be empty) from
/// showing up.
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public class EmptyClassAttribute(string justification) : EmptyTypeAttribute(justification) { }

/// <summary>Indicates the enum is empty by design.</summary>
[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public class EmptyEnumAttribute(string justification) : EmptyTypeAttribute(justification) { }

/// <summary>Indicates the class is empty by design.</summary>
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public class EmptyInterfaceAttribute(string justification) : EmptyTypeAttribute(justification) { }

/// <summary>Indicates the struct is empty by design.</summary>
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public class EmptyStructAttribute(string justification) : EmptyTypeAttribute(justification) { }

/// <summary>Indicates the class is empty by design.</summary>
/// <remarks>
/// Using this attribute prevents S2094 (Classes should not be empty) from
/// showing up.
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public sealed class EmptyTestClassAttribute(string? justification = null) : EmptyClassAttribute(justification ?? "For test purposes.") { }

/// <summary>Indicates the enum is empty by design.</summary>
[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public sealed class EmptyTestEnumAttribute(string? justification = null) : EmptyEnumAttribute(justification ?? "For test purposes.") { }

/// <summary>Indicates the class is empty by design.</summary>
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public sealed class EmptyTestInterfaceAttribute(string? justification = null) : EmptyInterfaceAttribute(justification ?? "For test purposes.") { }

/// <summary>Indicates the struct is empty by design.</summary>
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
[Conditional("CONTRACTS_FULL")]
public sealed class EmptyTestStructAttribute(string? justification = null) : EmptyStructAttribute(justification ?? "For test purposes.") { }
16 changes: 16 additions & 0 deletions src/Qowaiv.Diagnostics.Contracts/README.md
Expand Up @@ -39,6 +39,22 @@ changes due to this method call.
An attribute that inherits from `[Impure]` to indicate that the returned
instance is equal to self or of the parameters, just to allow a fluent syntax.
## Empty type attributes
Empty types (classes, enums, interfaces, structs) are generally seen as a bad
practice, or assumed to be unfinished code. Decorating them with an attribute
that explains the reason why the type is empty (for test cases for example)
can help:
* `[EmptyClass(Justification = "")]`
* `[EmptyEnum(Justification = "")]`
* `[EmptyInterface(Justification = "")]`
* `[EmptyStruct(Justification = "")]`
* `[EmptyTestClass]`
* `[EmptyTestEnum]`
* `[EmptyTestInterface]`
* `[EmptyTestStruct]`
## Inheritable attribute
The `[Inheritable]` attribute indicates that a class is designed to be
inheritable although no virtual or protected members have been defined.
Expand Down
16 changes: 0 additions & 16 deletions src/Qowaiv.TestTools/EmptyTestClassAttribute.cs

This file was deleted.

0 comments on commit 31ffa93

Please sign in to comment.