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

String array + Comparer #63

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
17ca7b0
Refactor Generator: Remove requirement for compilation
JKamsker Oct 8, 2024
ceb45e2
SourceGen does transform and allows for better caching
JKamsker Oct 9, 2024
62a62cf
Fixes #60: String array + Comparer
JKamsker Oct 10, 2024
5797558
Remove throws and fix equality of the model
JKamsker Oct 10, 2024
b13b8d9
Merge branch 'JKamsker/real_incremental_reduced' into issues/60-strin…
JKamsker Oct 10, 2024
a48337e
Fix test
JKamsker Oct 10, 2024
71ee3af
Fix stringarray defaul comparison
JKamsker Oct 10, 2024
cdff4c9
Add EquatableImmutableDictionary
JKamsker Oct 10, 2024
d12bf22
Remove comments
JKamsker Oct 10, 2024
018b0d8
Comment out EmitCompilerGeneratedFiles
JKamsker Oct 11, 2024
afd36b0
Cleanup
JKamsker Oct 11, 2024
762c681
Remove useless test class
JKamsker Oct 11, 2024
1eae2be
Remove commented-out code
JKamsker Oct 11, 2024
60fc464
Remove comment
JKamsker Oct 11, 2024
cacd1dd
Resolved warnings
JKamsker Oct 11, 2024
ac6458b
Merge branch 'JKamsker/real_incremental_reduced' into issues/60-strin…
JKamsker Oct 11, 2024
2d50c57
Fix merge
JKamsker Oct 11, 2024
f86993b
Fix merge #2
JKamsker Oct 11, 2024
75f67a8
Using SymbolDisplayFormat.FullyQualifiedFormat for fullname
JKamsker Oct 11, 2024
c93ebeb
Merge branch 'JKamsker/real_incremental_reduced' into issues/60-strin…
JKamsker Oct 11, 2024
67374fe
Fix merge
JKamsker Oct 11, 2024
7e0e70c
SymbolExtensions made internal to get rid of warning
JKamsker Oct 14, 2024
927e2a7
Applied requested changes for pr
JKamsker Oct 14, 2024
25d158f
Merge branch 'JKamsker/real_incremental_reduced' into issues/60-strin…
JKamsker Oct 14, 2024
5901b89
Merge branch 'JKamsker/real_incremental_reduced' into issues/60-strin…
JKamsker Oct 16, 2024
2372096
Merge branch 'main' into issues/60-string-array
JKamsker Oct 16, 2024
6707932
Hide warnings in test
JKamsker Oct 16, 2024
fd4522f
Re-added Conditional
JKamsker Oct 16, 2024
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
82 changes: 82 additions & 0 deletions Generator.Equals.DynamicGenerationTests/Base_Assertions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
extern alias genEquals;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp;

namespace Generator.Equals.Tests.DynamicGeneration;

public class Base_Assertions
{
// Check if immutable arrays are comparable
[Fact]
public void Immutable_Arrays_Equatable()
{
var a = ImmutableArray.Create(1, 2, 3);
var b = ImmutableArray.Create(1, 2, 3);
Assert.Equal(a, b);
}

[Fact]
public void Immutable_Arrays_Comparable()
{
var a = ImmutableArray.Create(1, 2, 3);
var b = ImmutableArray.Create(1, 2, 3);
Assert.True(a.SequenceEqual(b));
}

// Check if equality type model is comparable by value
[Fact]
public void EqualityTypeModel_Equatable()
{
var a = CreateEqualityTypeModelMock();
var b = CreateEqualityTypeModelMock();
Assert.Equal(a, b);
}

[Fact]
public void AttributesMetadata_Equatable()
{
var a = AttributesMetadata.CreateDefault();
var b = AttributesMetadata.CreateDefault();
Assert.True(a.Equals(b));
}

internal static EqualityTypeModel CreateEqualityTypeModelMock()
{
var containingSymbols = ImmutableArray.Create<ContainingSymbol>(
new NamespaceContainingSymbol { Name = "Namespace1" },
new TypeContainingSymbol { Name = "ContainingType", Kind = null }
);

var attributesMetadata = AttributesMetadata.Instance;

var equalityMemberModels = ImmutableArray.Create(
new EqualityMemberModel
{
PropertyName = "Property1",
TypeName = "int",
EqualityType = EqualityType.DefaultEquality
},
new EqualityMemberModel
{
PropertyName = "Property2",
TypeName = "string",
EqualityType = EqualityType.StringEquality,
StringComparer = attributesMetadata.StringComparisonLookup[4]
}
);

return new EqualityTypeModel
{
TypeName = "MyType",
ContainingSymbols = containingSymbols,
AttributesMetadata = attributesMetadata,
ExplicitMode = false,
IgnoreInheritedMembers = false,
SyntaxKind = SyntaxKind.ClassDeclaration,
BaseTypeName = "BaseType",
IsSealed = true,
BuildEqualityModels = equalityMemberModels,
Fullname = "Namespace1.MyType"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="SourceGeneratorTestHelpers" Version="8.1.0" />

</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Generator.Equals.Runtime\Generator.Equals.Runtime.csproj" />
<ProjectReference Include="..\Generator.Equals\Generator.Equals.csproj">
<Aliases>genEquals</Aliases>
</ProjectReference>
</ItemGroup>


<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
4 changes: 4 additions & 0 deletions Generator.Equals.DynamicGenerationTests/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extern alias genEquals;

global using genEquals::Generator.Equals.Models;
global using genEquals::Generator.Equals;
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using SourceGeneratorTestHelpers;

namespace Generator.Equals.DynamicGenerationTests.Issues;

public class Issue_60_StringEquality_Enumerables
{
public static readonly List<PortableExecutableReference> References =
AppDomain.CurrentDomain.GetAssemblies()
.Where(x => !x.IsDynamic && !string.IsNullOrWhiteSpace(x.Location))
.Select(x => MetadataReference.CreateFromFile(x.Location))
.Append(MetadataReference.CreateFromFile(typeof(EquatableAttribute).Assembly.Location))
.ToList();

[Fact]
public void Comparison_is_correctly_generated()
{
var input = SourceText.CSharp(
"""
using System;
using System.Collections.Generic;
using Generator.Equals;

[Equatable]
public partial class Resource
{
[UnorderedEquality]
[StringEqualityAttribute(StringComparison.OrdinalIgnoreCase)]
public string[] Tags { get; set; } = Array.Empty<string>();

}
"""
);

var result = IncrementalGenerator.Run<EqualsGenerator>
(
input,
new CSharpParseOptions(),
References
);

var gensource = result.Results
.SelectMany(x => x.GeneratedSources)
.Select(x => x.SourceText)
.ToList()
;

Assert.NotNull(gensource);

var src = gensource.FirstOrDefault()?.ToString();

Assert.Contains(
"new global::Generator.Equals.UnorderedEqualityComparer<string>(global::System.StringComparer.OrdinalIgnoreCase)",
src);
}

[Fact]
public void Comparison_is_correctly_generated_without_attributes()
{
var input = SourceText.CSharp(
"""
using System;
using System.Collections.Generic;
using Generator.Equals;

[Equatable]
public partial class Resource
{
public string[] Tags { get; set; } = Array.Empty<string>();
}
"""
);

var result = IncrementalGenerator.Run<EqualsGenerator>
(
input,
new CSharpParseOptions(),
References
);

var gensource = result.Results
.SelectMany(x => x.GeneratedSources)
.Select(x => x.SourceText)
.ToList()
;

Assert.NotNull(gensource);

var src = gensource.FirstOrDefault()?.ToString();

Assert.Contains("new global::Generator.Equals.OrderedEqualityComparer<string>(global::System.StringComparer.Ordinal)",
src);
}
}
10 changes: 10 additions & 0 deletions Generator.Equals.DynamicGenerationTests/SourceText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extern alias genEquals;

using genEquals::System.Diagnostics.CodeAnalysis;

namespace Generator.Equals.DynamicGenerationTests;

internal static class SourceText
{
public static string CSharp([StringSyntax("c#-test")] string source) => source;
}
162 changes: 162 additions & 0 deletions Generator.Equals.Tests/Classes/StringArrayEquality.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
// ReSharper disable InconsistentNaming
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

namespace Generator.Equals.Tests.Classes;

public partial class StringArrayEquality
{
[Equatable]
public partial class Sample
{
// Order doesnt matter, case doesn't matter
[UnorderedEquality, StringEquality(StringComparison.OrdinalIgnoreCase)]
public string[] Unordered_Case_Insensitive { get; set; }

// Order matters, case doesn't matter
[OrderedEquality, StringEquality(StringComparison.OrdinalIgnoreCase)]
public string[] Ordered_Case_Insensitive { get; set; }

// Order doesn't matter, case matters
[UnorderedEquality, StringEquality(StringComparison.Ordinal)]
public string[] Unordered_Case_Sensitive { get; set; }

// Order matters, case matters
[OrderedEquality, StringEquality(StringComparison.Ordinal)]
public string[] Ordered_Case_Sensitive { get; set; }

// Default expectation: Order matters, case matters
public string[] DefaultBehaviour { get; set; }
}
}

public partial class StringArrayEquality
{
public class EqualsTests : EqualityTestCase
{
public override object Factory1()
{
return new Sample
{
Unordered_Case_Insensitive = new[] { "a", "b", "c" },
Ordered_Case_Insensitive = new[] { "a", "b", "c" },
Unordered_Case_Sensitive = new[] { "a", "b", "c" },
Ordered_Case_Sensitive = new[] { "a", "b", "c" },
DefaultBehaviour = new[] { "a", "b", "c" },
};
}

public override object Factory2()
{
return new Sample
{
Unordered_Case_Insensitive = new[] { "b", "A", "c" },
Ordered_Case_Insensitive = new[] { "A", "b", "c" },
Unordered_Case_Sensitive = new[] { "b", "a", "c" },
Ordered_Case_Sensitive = new[] { "a", "b", "c" },
DefaultBehaviour = new[] { "a", "b", "c" },
};
}

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}

// Now test if the equals operator can return false.
// Unordered_Case_Insensitive: "a", "b", "c" vs "b", "X", "c" (X is not in the first array)
public class NotEqualsTest_Unordered_Case_Insensitive : EqualityTestCase
{
public override bool Expected => false;

public override object Factory1() => new Sample
{
Unordered_Case_Insensitive = new[] { "a", "b", "c" },
};

public override object Factory2() => new Sample
{
Unordered_Case_Insensitive = new[] { "b", "X", "c" },
};

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}

// Ordered_Case_Insensitive: "a", "b", "c" vs "A", "b", "c" (a is different case)
public class NotEqualsTest_Ordered_Case_Insensitive : EqualityTestCase
{
public override bool Expected => false;

public override object Factory1() => new Sample
{
Ordered_Case_Insensitive = new[] { "a", "b", "c" },
};

public override object Factory2() => new Sample
{
Ordered_Case_Insensitive = new[] { "b", "A", "c" },
};

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}

// Unordered_Case_Sensitive: "a", "b", "c" vs "b", "a", "C" (C is different case)
public class NotEqualsTest_Unordered_Case_Sensitive : EqualityTestCase
{
public override bool Expected => false;

public override object Factory1() => new Sample
{
Unordered_Case_Sensitive = new[] { "a", "b", "c" },
};

public override object Factory2() => new Sample
{
Unordered_Case_Sensitive = new[] { "b", "a", "C" },
};

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}

// Ordered_Case_Sensitive: "a", "b", "c" vs "b", "a", "C" (C is different case)
public class NotEqualsTest_Ordered_Case_Sensitive : EqualityTestCase
{
public override bool Expected => false;

public override object Factory1() => new Sample
{
Ordered_Case_Sensitive = new[] { "a", "b", "c" },
};

public override object Factory2() => new Sample
{
Ordered_Case_Sensitive = new[] { "b", "a", "C" },
};

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}

// DefaultBehaviour: "a", "b", "c" vs "b", "a", "C" (C is different case)
public class NotEqualsTest_DefaultBehaviour : EqualityTestCase
{
public override bool Expected => false;

public override object Factory1() => new Sample
{
DefaultBehaviour = new[] { "a", "b", "c" },
};

public override object Factory2() => new Sample
{
DefaultBehaviour = new[] { "b", "a", "C" },
};

public override bool EqualsOperator(object value1, object value2) => (Sample)value1 == (Sample)value2;
public override bool NotEqualsOperator(object value1, object value2) => (Sample)value1 != (Sample)value2;
}
}

#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
Loading
Loading