Skip to content

Commit

Permalink
implement reference comparer, improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pwelter34 committed Sep 5, 2024
1 parent 92c5f8d commit 30c0151
Show file tree
Hide file tree
Showing 12 changed files with 490 additions and 38 deletions.
38 changes: 16 additions & 22 deletions src/Equatable.SourceGenerator/EquatableGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Reflection;
using System.Xml.Linq;

using Equatable.SourceGenerator.Models;
Expand Down Expand Up @@ -200,11 +201,15 @@ private static (ComparerTypes? comparerType, string? comparerName, string? compa

private static (ComparerTypes? comparerType, string? comparerName, string? comparerInstance) GetStringComparer(AttributeData? attribute)
{
var argument = attribute?.ConstructorArguments.FirstOrDefault();
if (argument == null || !argument.HasValue)
if (attribute == null || attribute.ConstructorArguments.Length != 1)
return (ComparerTypes.Default, null, null);

var argument = attribute.ConstructorArguments[0];

if (argument.Value is not int value)
return (ComparerTypes.String, "CurrentCulture", null);

var comparerName = argument?.Value switch
var comparerName = value switch
{
0 => "CurrentCulture",
1 => "CurrentCultureIgnoreCase",
Expand All @@ -220,30 +225,19 @@ private static (ComparerTypes? comparerType, string? comparerName, string? compa

private static (ComparerTypes? comparerType, string? comparerName, string? comparerInstance) GetEqualityComparer(AttributeData? attribute)
{
if (attribute == null)
if (attribute == null || attribute.ConstructorArguments.Length != 2)
return (ComparerTypes.Default, null, null);

// attribute constructor
var comparerType = attribute.ConstructorArguments.FirstOrDefault();
if (comparerType.Value is INamedTypeSymbol typeSymbol)
{
return (ComparerTypes.Custom, typeSymbol.ToDisplayString(), null);
}

// generic attribute
var attributeClass = attribute.AttributeClass;
if (attributeClass is { IsGenericType: true }
&& attributeClass.TypeArguments.Length == attributeClass.TypeParameters.Length
&& attributeClass.TypeArguments.Length == 1)
{
var typeArgument = attributeClass.TypeArguments[0];
var comparerName = typeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var comparerArgument = attribute.ConstructorArguments[0];
if (comparerArgument.Value is not INamedTypeSymbol typeSymbol)
return (ComparerTypes.Default, null, null); // invalid syntax found

return (ComparerTypes.Custom, comparerName, null);
}
var comparerName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

var instanceArgument = attribute.ConstructorArguments[1];
var comparerInstance = instanceArgument.Value as string;

return (ComparerTypes.Default, null, null);
return (ComparerTypes.Custom, comparerName, comparerInstance);
}


Expand Down
12 changes: 12 additions & 0 deletions src/Equatable.SourceGenerator/EquatableWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ private static void GenerateEquatable(IndentedStringBuilder codeBuilder, Equatab

break;
case ComparerTypes.Reference:
codeBuilder
.Append(" global::System.Object.ReferenceEquals(")
.Append(entityProperty.PropertyName)
.Append(", other.")
.Append(entityProperty.PropertyName)
.Append(")");

break;
case ComparerTypes.Sequence:
codeBuilder
Expand Down Expand Up @@ -395,6 +402,11 @@ private static void GenerateHashCode(IndentedStringBuilder codeBuilder, Equatabl
.AppendLine(");");
break;
case ComparerTypes.Reference:
codeBuilder
.Append("hashCode = (hashCode * -1521134295) + ")
.Append("global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(")
.Append(entityProperty.PropertyName)
.AppendLine("!);");
break;
case ComparerTypes.Sequence:
codeBuilder
Expand Down
4 changes: 3 additions & 1 deletion test/Equatable.Entities/Audit.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;

using Equatable.Attributes;

Expand All @@ -13,4 +12,7 @@ public partial class Audit : ModelBase
public int? TaskId { get; set; }
public string? Content { get; set; }
public string? UserName { get; set; }

[ReferenceEquality]
public object? Lock { get; set; }
}
38 changes: 38 additions & 0 deletions test/Equatable.Entities/CustomLength.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;

using Equatable.Attributes;

namespace Equatable.Entities;

[Equatable]
public partial class CustomLength
{
public int Id { get; set; }

public string Name { get; set; } = null!;

[EqualityComparer(typeof(LengthComparerDefault))]
public string? Key { get; set; }

[EqualityComparer(typeof(LengthComparerInstance), nameof(LengthComparerInstance.Instance))]
public string? Value { get; set; }

}

public static class LengthComparerDefault
{
public static readonly LengthEqualityComparer Default = new();
}

public static class LengthComparerInstance
{
public static readonly LengthEqualityComparer Instance = new();
}

public class LengthEqualityComparer : IEqualityComparer<string?>
{
public bool Equals(string? x, string? y) => x?.Length == y?.Length;

public int GetHashCode(string? obj) => obj?.Length.GetHashCode() ?? 0;
}

4 changes: 4 additions & 0 deletions test/Equatable.Entities/Equatable.Entities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Equatable.Generator\Equatable.Generator.csproj" />
<ProjectReference Include="..\..\src\Equatable.SourceGenerator\Equatable.SourceGenerator.csproj">
Expand Down
6 changes: 3 additions & 3 deletions test/Equatable.Entities/Nested.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

namespace Equatable.Entities;

public class Nested
public partial class Nested
{
//[Equatable]
public partial class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string? Name { get; set; }
public string? Type { get; set; }
}
}
4 changes: 4 additions & 0 deletions test/Equatable.Entities/UserImport.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

using Equatable.Attributes;

namespace Equatable.Entities;
Expand All @@ -10,6 +12,7 @@ public partial class UserImport
[StringEquality(StringComparison.OrdinalIgnoreCase)]
public string EmailAddress { get; set; } = null!;

[JsonPropertyName("name")]
public string? DisplayName { get; set; }

public string? FirstName { get; set; }
Expand All @@ -20,6 +23,7 @@ public partial class UserImport

public DateTimeOffset? LastLogin { get; set; }

[JsonIgnore]
[IgnoreEquality]
public string FullName => $"{FirstName} {LastName}";

Expand Down
122 changes: 122 additions & 0 deletions test/Equatable.Generator.Tests/Entities/AuditTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using Equatable.Entities;

namespace Equatable.Generator.Tests.Entities;

public class AuditTest
{
[Fact]
public void EqualAuditTrue()
{
var lockObject = new object();

var left = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = lockObject
};

var right = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = lockObject
};

var isEqual = left.Equals(right);
isEqual.Should().BeTrue();

// check operator ==
isEqual = left == right;
isEqual.Should().BeTrue();
}

[Fact]
public void NotEqualAudit()
{
var left = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = new object()
};

var right = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = new object()
};

var isEqual = left.Equals(right);
isEqual.Should().BeFalse();

// check operator !=
isEqual = left != right;
isEqual.Should().BeTrue();

}

[Fact]
public void HashCodeAuditTrue()
{
var lockObject = new object();
var left = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = lockObject
};

var right = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = lockObject
};

var leftCode = left.GetHashCode();
var rightCode = right.GetHashCode();

leftCode.Should().Be(rightCode);
}

[Fact]
public void HashCodeAuditNotEqual()
{
var left = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = new object()
};

var right = new Audit
{
Id = 1,
Date = new DateTime(2024, 9, 1),
UserId = 1,
TaskId = 2,
Lock = new object()
};

var leftCode = left.GetHashCode();
var rightCode = right.GetHashCode();

leftCode.Should().NotBe(rightCode);
}
}
Loading

0 comments on commit 30c0151

Please sign in to comment.