Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(2)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>> Should<TKey, TValue>(this System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>? value, string? expression = default)
where TKey : notnull
{
var source = global::TUnit.Assertions.Assert.That<TKey, TValue>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>(innerContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(2)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>> Should<TKey, TValue>(this System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>? value, string? expression = default)
where TKey : notnull
{
var source = global::TUnit.Assertions.Assert.That<TKey, TValue>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>(innerContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(2)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>> Should<TKey, TValue>(this System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>? value, string? expression = default)
where TKey : notnull
{
var source = global::TUnit.Assertions.Assert.That<TKey, TValue>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>>(innerContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(3)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>> Should<TItem>(this System.Collections.Generic.ISet<TItem>? value, string? expression = default)
{
var source = global::TUnit.Assertions.Assert.That<TItem>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.ISet<TItem>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>>(innerContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(3)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>> Should<TItem>(this System.Collections.Generic.ISet<TItem>? value, string? expression = default)
{
var source = global::TUnit.Assertions.Assert.That<TItem>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.ISet<TItem>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>>(innerContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <auto-generated />
#nullable enable

using System;
using System.Runtime.CompilerServices;
using TUnit.Assertions;
using TUnit.Assertions.Should.Core;

namespace TUnit.Assertions.Should;

public static partial class ShouldExtensions
{

[global::System.Runtime.CompilerServices.OverloadResolutionPriority(3)]
public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>> Should<TItem>(this System.Collections.Generic.ISet<TItem>? value, string? expression = default)
{
var source = global::TUnit.Assertions.Assert.That<TItem>(value, expression);
var innerContext = ((global::TUnit.Assertions.Core.IAssertionSource<System.Collections.Generic.ISet<TItem>>)source).Context;
innerContext.ExpressionBuilder.Clear();
innerContext.ExpressionBuilder.Append(expression ?? "?").Append(".Should()");
return new global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>>(innerContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,84 @@ public static MyBetweenAssertion<TValue> IsBetween<TValue>(
await Assert.That(output).Contains("CallerArgumentExpression(\"max\")");
}

[Test]
public async Task Assert_That_dictionary_specialization_emits_matching_Should_overload()
{
var output = await RunGenerator("""
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using TUnit.Assertions.Core;

namespace TUnit.Assertions;

public class DictionaryAssertion<TKey, TValue> : IAssertionSource<IReadOnlyDictionary<TKey, TValue>>
where TKey : notnull
{
public AssertionContext<IReadOnlyDictionary<TKey, TValue>> Context { get; }
public DictionaryAssertion(IReadOnlyDictionary<TKey, TValue>? value, string? expression)
=> Context = new AssertionContext<IReadOnlyDictionary<TKey, TValue>>(value!, new System.Text.StringBuilder());
public TypeOfAssertion<IReadOnlyDictionary<TKey, TValue>, TExpected> IsTypeOf<TExpected>() => throw new System.NotImplementedException();
public IsNotTypeOfAssertion<IReadOnlyDictionary<TKey, TValue>, TExpected> IsNotTypeOf<TExpected>() => throw new System.NotImplementedException();
public IsAssignableToAssertion<TExpected, IReadOnlyDictionary<TKey, TValue>> IsAssignableTo<TExpected>() => throw new System.NotImplementedException();
public IsNotAssignableToAssertion<TExpected, IReadOnlyDictionary<TKey, TValue>> IsNotAssignableTo<TExpected>() => throw new System.NotImplementedException();
public IsAssignableFromAssertion<TExpected, IReadOnlyDictionary<TKey, TValue>> IsAssignableFrom<TExpected>() => throw new System.NotImplementedException();
public IsNotAssignableFromAssertion<TExpected, IReadOnlyDictionary<TKey, TValue>> IsNotAssignableFrom<TExpected>() => throw new System.NotImplementedException();
}

public static class Assert
{
[OverloadResolutionPriority(2)]
public static DictionaryAssertion<TKey, TValue> That<TKey, TValue>(
IReadOnlyDictionary<TKey, TValue>? value,
[CallerArgumentExpression(nameof(value))] string? expression = null)
where TKey : notnull
=> new(value, expression);
}
""");

await Assert.That(output).Contains("public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>> Should<TKey, TValue>(this System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>? value");
await Assert.That(output).Contains("Assert.That<TKey, TValue>(value, expression)");
await Assert.That(output).Contains("Append(expression ?? \"?\").Append(\".Should()\")");
}

[Test]
public async Task Assert_That_set_specialization_emits_matching_Should_overload()
{
var output = await RunGenerator("""
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using TUnit.Assertions.Core;

namespace TUnit.Assertions;

public class SetAssertion<TItem> : IAssertionSource<ISet<TItem>>
{
public AssertionContext<ISet<TItem>> Context { get; }
public SetAssertion(ISet<TItem>? value, string? expression)
=> Context = new AssertionContext<ISet<TItem>>(value!, new System.Text.StringBuilder());
public TypeOfAssertion<ISet<TItem>, TExpected> IsTypeOf<TExpected>() => throw new System.NotImplementedException();
public IsNotTypeOfAssertion<ISet<TItem>, TExpected> IsNotTypeOf<TExpected>() => throw new System.NotImplementedException();
public IsAssignableToAssertion<TExpected, ISet<TItem>> IsAssignableTo<TExpected>() => throw new System.NotImplementedException();
public IsNotAssignableToAssertion<TExpected, ISet<TItem>> IsNotAssignableTo<TExpected>() => throw new System.NotImplementedException();
public IsAssignableFromAssertion<TExpected, ISet<TItem>> IsAssignableFrom<TExpected>() => throw new System.NotImplementedException();
public IsNotAssignableFromAssertion<TExpected, ISet<TItem>> IsNotAssignableFrom<TExpected>() => throw new System.NotImplementedException();
}

public static class Assert
{
[OverloadResolutionPriority(3)]
public static SetAssertion<TItem> That<TItem>(
ISet<TItem>? value,
[CallerArgumentExpression(nameof(value))] string? expression = null)
=> new(value, expression);
}
""");

await Assert.That(output).Contains("public static global::TUnit.Assertions.Should.Core.ShouldSource<System.Collections.Generic.ISet<TItem>> Should<TItem>(this System.Collections.Generic.ISet<TItem>? value");
await Assert.That(output).Contains("OverloadResolutionPriority(3)");
await Assert.That(output).Contains("Assert.That<TItem>(value, expression)");
}

/// <summary>
/// Compiles <paramref name="userSource"/> with the Should-generator's input dependencies,
/// runs <see cref="ShouldExtensionGenerator"/>, snapshots the full generated source via
Expand All @@ -251,9 +329,26 @@ public static MyBetweenAssertion<TValue> IsBetween<TValue>(
/// </summary>
private static async Task<string> RunGenerator(string userSource, [CallerMemberName] string testName = "")
{
// On net8.0 hosts, OverloadResolutionPriorityAttribute is missing from the BCL and the
// Polyfill copy compiled into this test assembly is internal — invisible to the synthetic
// GeneratorTest compilation. Inject a public copy so the attribute resolves consistently
// across all TFMs the test multi-targets.
var inputTrees = new List<SyntaxTree> { CSharpSyntaxTree.ParseText(userSource) };
#if NET8_0
inputTrees.Add(CSharpSyntaxTree.ParseText("""
namespace System.Runtime.CompilerServices;
[System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Constructor | System.AttributeTargets.Property, Inherited = false)]
public sealed class OverloadResolutionPriorityAttribute : System.Attribute
{
public OverloadResolutionPriorityAttribute(int priority) => Priority = priority;
public int Priority { get; }
}
"""));
#endif

var compilation = CSharpCompilation.Create(
assemblyName: "GeneratorTest",
syntaxTrees: [CSharpSyntaxTree.ParseText(userSource)],
syntaxTrees: inputTrees,
references: GetReferences(),
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

Expand All @@ -264,11 +359,11 @@ private static async Task<string> RunGenerator(string userSource, [CallerMemberN
await Assert.That(diagnostics.Length).IsEqualTo(0)
.Because("Generator should not emit diagnostics for valid input");

var trees = updatedCompilation.SyntaxTrees
.Where(t => t != compilation.SyntaxTrees[0])
var generatedTrees = updatedCompilation.SyntaxTrees
.Where(t => !compilation.SyntaxTrees.Contains(t))
.Select(t => t.ToString());

var combined = string.Join("\n//------\n", trees);
var combined = string.Join("\n//------\n", generatedTrees);

await Verify(combined)
.UseFileName($"{nameof(ShouldExtensionGeneratorTests)}.{testName}")
Expand Down
Loading
Loading