From bbb04b9b31b42275e1114efa06aa131af328ef8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 29 Apr 2026 05:33:56 +0200 Subject: [PATCH 1/2] refactor: simplify source generator tests assertions --- .../GeneralTests.cs | 94 ++++++++++++------- .../IndexerSetupsTests.cs | 8 +- .../MethodSetupsTests.cs | 11 ++- .../MockBehaviorExtensionsTests.cs | 20 ++-- .../MockGenerator.AggregationTests.cs | 9 +- .../MockGeneratorTests.cs | 77 ++++++++++----- .../MockTests.ClassTests.EventsTests.cs | 15 ++- .../MockTests.ClassTests.IndexerTests.cs | 15 ++- .../MockTests.ClassTests.MethodTests.cs | 65 ++++++++----- .../MockTests.ClassTests.PropertiesTests.cs | 14 ++- .../MockTests.ClassTests.cs | 24 +++-- .../MockTests.CombinationTests.cs | 18 ++-- .../MockTests.DelegateTests.cs | 36 +++---- .../MockTests.RefStructTests.cs | 54 +++++++---- .../MockTests.cs | 51 ++++++---- .../ReturnsThrowsAsyncExtensionsTests.cs | 8 +- 16 files changed, 334 insertions(+), 185 deletions(-) diff --git a/Tests/Mockolate.SourceGenerators.Tests/GeneralTests.cs b/Tests/Mockolate.SourceGenerators.Tests/GeneralTests.cs index 77a154c2..e479c427 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/GeneralTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/GeneralTests.cs @@ -176,35 +176,50 @@ public interface IMyInterface """, typeof(IEnumerable<>)); - await That(result.Sources).ContainsKey("Mock.IMyInterface_object.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_object.g.cs"); + await That(result.Sources["Mock.IMyInterface_object.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_bool.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_bool.g.cs"); + await That(result.Sources["Mock.IMyInterface_bool.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_string.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_string.g.cs"); + await That(result.Sources["Mock.IMyInterface_string.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_char.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_char.g.cs"); + await That(result.Sources["Mock.IMyInterface_char.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_byte.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_byte.g.cs"); + await That(result.Sources["Mock.IMyInterface_byte.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_sbyte.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_sbyte.g.cs"); + await That(result.Sources["Mock.IMyInterface_sbyte.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_short.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_short.g.cs"); + await That(result.Sources["Mock.IMyInterface_short.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_ushort.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_ushort.g.cs"); + await That(result.Sources["Mock.IMyInterface_ushort.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_int.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_int.g.cs"); + await That(result.Sources["Mock.IMyInterface_int.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_uint.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_uint.g.cs"); + await That(result.Sources["Mock.IMyInterface_uint.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_long.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_long.g.cs"); + await That(result.Sources["Mock.IMyInterface_long.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_ulong.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_ulong.g.cs"); + await That(result.Sources["Mock.IMyInterface_ulong.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_float.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_float.g.cs"); + await That(result.Sources["Mock.IMyInterface_float.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_double.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_double.g.cs"); + await That(result.Sources["Mock.IMyInterface_double.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); - await That(result.Sources).ContainsKey("Mock.IMyInterface_decimal.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface_decimal.g.cs"); + await That(result.Sources["Mock.IMyInterface_decimal.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable MyEnumerable"); } @@ -229,7 +244,8 @@ public static void Main(string[] args) } """, typeof(HttpMessageHandler)); - await That(result.Sources).ContainsKey("Mock.HttpMessageHandler.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.HttpMessageHandler.g.cs"); + await That(result.Sources["Mock.HttpMessageHandler.g.cs"]) .Contains("protected override void Dispose(bool disposing)").And .DoesNotContain("void Dispose()"); } @@ -255,7 +271,8 @@ public static void Main(string[] args) """, typeof(IList<>)); - await That(result.Sources).ContainsKey("Mock.IList_int.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IList_int.g.cs"); + await That(result.Sources["Mock.IList_int.g.cs"]) .Contains("global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator()").And .Contains("public global::System.Collections.Generic.IEnumerator GetEnumerator()"); } @@ -286,7 +303,8 @@ public record MyRecord(int Id, string Name); """, typeof(IList<>)); - await That(result.Sources).ContainsKey("Mock.IList_MyRecord.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IList_MyRecord.g.cs"); + await That(result.Sources["Mock.IList_MyRecord.g.cs"]) .Contains(""" internal class IList_MyRecord : global::System.Collections.Generic.IList @@ -334,7 +352,8 @@ public interface IMyInterface """, typeof(IEnumerable<>)); - await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("public global::System.Collections.Generic.IEnumerable EnumerableOfObject").And .Contains("public global::System.Collections.Generic.IEnumerable EnumerableOfBool").And .Contains("public global::System.Collections.Generic.IEnumerable EnumerableOfString").And @@ -458,8 +477,8 @@ public virtual int SomeMethod() """, typeof(ObsoleteAttribute)); - await That(result.Sources) - .ContainsKey("Mock.MyBaseClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs"); + await That(result.Sources["Mock.MyBaseClass.g.cs"]) .Contains(""" [global::System.Obsolete] public MyBaseClass(global::Mockolate.MockRegistry mockRegistry) @@ -520,7 +539,8 @@ public interface IMyInterface2 await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.IMyInterface1__IMyInterface2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface1__IMyInterface2.g.cs"); + await That(result.Sources["Mock.IMyInterface1__IMyInterface2.g.cs"]) .Contains("public void MyMethod(int v1)").And .Contains("void global::MyCode.IMyInterface2.MyMethod(int v1)"); } @@ -558,7 +578,8 @@ public interface IMyInterface2 await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.IMyInterface1.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface1.g.cs"); + await That(result.Sources["Mock.IMyInterface1.g.cs"]) .Contains("public void MyMethod(int v1)").And .Contains("void global::MyCode.IMyInterface2.MyMethod(int v1)"); } @@ -589,8 +610,8 @@ public class MyService // A mock subclass declaring a property `MockRegistry` would hide the inherited field // (CS0108). The dedup pipeline must skip past the conflicting member name. - await That(result.Sources) - .ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("MockolateMockRegistry") .IgnoringNewlineStyle().And .DoesNotContain("private global::Mockolate.MockRegistry MockRegistry { get; }") @@ -621,8 +642,8 @@ public class MockRegistry { } } """); - await That(result.Sources) - .ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("MockolateMockRegistry") .IgnoringNewlineStyle().And .DoesNotContain("private global::Mockolate.MockRegistry MockRegistry { get; }") @@ -654,8 +675,8 @@ public interface IMyInterface // `Mock` is a method on the type, so the extension `Mock` property would shadow access. // CreateUniquePropertyName must now skip past it to a Mockolate_-prefixed alternative. - await That(result.Sources) - .ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("Mockolate_Mock") .IgnoringNewlineStyle(); } @@ -684,8 +705,8 @@ public interface IMyInterface } """); - await That(result.Sources) - .ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("Mockolate_Mock__1") .IgnoringNewlineStyle(); } @@ -715,8 +736,8 @@ public interface IMyInterface } """); - await That(result.Sources) - .ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("Mockolate_Mock__2") .IgnoringNewlineStyle(); } @@ -747,8 +768,8 @@ public interface IMyInterface } """, typeof(EventHandler)); - await That(result.Sources) - .ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("MockRegistry_2") .IgnoringNewlineStyle().And .DoesNotContain("private global::Mockolate.MockRegistry MockRegistry_1 { get; }") @@ -880,7 +901,8 @@ public CustomAttribute( """, typeof(AllowNullAttribute), typeof(IDataParameter), typeof(LocalizableAttribute), typeof(AttributeUsageAttribute)); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// [global::System.Diagnostics.CodeAnalysis.AllowNull] diff --git a/Tests/Mockolate.SourceGenerators.Tests/IndexerSetupsTests.cs b/Tests/Mockolate.SourceGenerators.Tests/IndexerSetupsTests.cs index fbac52d7..efa2477e 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/IndexerSetupsTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/IndexerSetupsTests.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace Mockolate.SourceGenerators.Tests; @@ -31,7 +31,8 @@ public interface IMyInterface } """, typeof(CancellationToken)); - await That(result.Sources).ContainsKey("IndexerSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("IndexerSetups.g.cs"); + await That(result.Sources["IndexerSetups.g.cs"]) .Contains( "internal class IndexerSetup(global::Mockolate.MockRegistry mockRegistry, global::Mockolate.Parameters.IParameterMatch parameter1, global::Mockolate.Parameters.IParameterMatch parameter2, global::Mockolate.Parameters.IParameterMatch parameter3, global::Mockolate.Parameters.IParameterMatch parameter4, global::Mockolate.Parameters.IParameterMatch parameter5) : global::Mockolate.Setup.IndexerSetup(mockRegistry)"); } @@ -96,7 +97,8 @@ public interface IMyInterface } """); - await That(result.Sources).ContainsKey("IndexerSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("IndexerSetups.g.cs"); + await That(result.Sources["IndexerSetups.g.cs"]) .Contains("class IndexerSetup(").And .DoesNotContain("class IndexerSetup(").And .DoesNotContain("class IndexerSetup("); diff --git a/Tests/Mockolate.SourceGenerators.Tests/MethodSetupsTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MethodSetupsTests.cs index c06dab60..b2e438c6 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MethodSetupsTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MethodSetupsTests.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace Mockolate.SourceGenerators.Tests; @@ -34,7 +34,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task), typeof(CancellationToken)); - await That(result.Sources).ContainsKey("MethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MethodSetups.g.cs"); + await That(result.Sources["MethodSetups.g.cs"]) .Contains( "internal abstract class ReturnMethodSetup : global::Mockolate.Setup.MethodSetup") .And @@ -182,7 +183,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task), typeof(CancellationToken)); - await That(result.Sources).ContainsKey("MethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MethodSetups.g.cs"); + await That(result.Sources["MethodSetups.g.cs"]) .Contains("class ReturnMethodSetup").And .DoesNotContain("class VoidMethodSetup").And .DoesNotContain("class ReturnMethodSetup").And @@ -216,7 +218,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task), typeof(CancellationToken)); - await That(result.Sources).ContainsKey("MethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MethodSetups.g.cs"); + await That(result.Sources["MethodSetups.g.cs"]) .Contains("class VoidMethodSetup").And .DoesNotContain("class ReturnMethodSetup").And .DoesNotContain("class VoidMethodSetup").And diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockBehaviorExtensionsTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockBehaviorExtensionsTests.cs index 5b7ef237..4e52b8c4 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockBehaviorExtensionsTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockBehaviorExtensionsTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Mockolate.SourceGenerators.Tests; @@ -31,7 +31,8 @@ public interface IMyInterface } """); - await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs"); + await That(result.Sources["Mock.IMyInterface.g.cs"]) .Contains("(b.DefaultValue.Generate(default(int)!), b.DefaultValue.Generate(default(string)!))").And .Contains( "(b.DefaultValue.Generate(default(int)!), b.DefaultValue.Generate(default(string)!), b.DefaultValue.Generate(default(int)!), b.DefaultValue.Generate(default(string)!), b.DefaultValue.Generate(default(int)!), b.DefaultValue.Generate(default(string)!), b.DefaultValue.Generate(default(int)!), b.DefaultValue.Generate(default(string)!))") @@ -39,7 +40,8 @@ await That(result.Sources).ContainsKey("Mock.IMyInterface.g.cs").WhoseValue .Contains( "(this.MockRegistry.Behavior.DefaultValue.Generate(default(int)!), this.MockRegistry.Behavior.DefaultValue.Generate(default(T1)!), this.MockRegistry.Behavior.DefaultValue.Generate(default(T2)!))"); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .DoesNotContain("Generate((T1, T2)").And .DoesNotContain("Generate((T1, T2, T3)").And .DoesNotContain("Generate"); @@ -72,7 +74,8 @@ public interface IMyInterface } """); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .Contains(""" public T[] Generate(T[] nullValue, params object?[] parameters) => global::System.Array.Empty(); @@ -119,7 +122,8 @@ public interface IMyInterface } """, typeof(IEnumerable<>)); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .Contains(""" public global::System.Collections.Generic.IEnumerable Generate(global::System.Collections.Generic.IEnumerable nullValue, params object?[] parameters) => global::System.Array.Empty(); @@ -158,7 +162,8 @@ public interface IMyInterface } """, typeof(Task)); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .Contains(""" public global::System.Threading.Tasks.Task Generate(global::System.Threading.Tasks.Task nullValue, T value, params object?[] parameters) { @@ -203,7 +208,8 @@ public interface IMyInterface } """, typeof(ValueTask)); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .Contains(""" public global::System.Threading.Tasks.ValueTask Generate(global::System.Threading.Tasks.ValueTask nullValue, T value, params object?[] parameters) { diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs index 2f3f35a1..eb458ba0 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs @@ -56,7 +56,8 @@ public interface IA { } public interface IB { } """); - await That(result.Sources).ContainsKey("Mock.AsExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.AsExtensions.g.cs"); + await That(result.Sources["Mock.AsExtensions.g.cs"]) // (IBase ↔ IB): bridge from a typed-as-IBase mock to IMockForIB. .Contains("internal static partial class MockExtensionsForIB").And .Contains("extension(global::Mockolate.Mock.IMockForIBase mock)").And @@ -86,7 +87,8 @@ public static void Main(string[] args) } """, typeof(HttpClient)); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .Contains("HttpResponseMessageFactory").And .Contains("new HttpResponseMessageFactory(global::System.Net.HttpStatusCode.NotImplemented)"); } @@ -114,7 +116,8 @@ public interface IService } """); - await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("MockBehaviorExtensions.g.cs"); + await That(result.Sources["MockBehaviorExtensions.g.cs"]) .DoesNotContain("HttpResponseMessageFactory"); } diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockGeneratorTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockGeneratorTests.cs index ac73f7b0..c024112e 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockGeneratorTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockGeneratorTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; @@ -38,7 +38,8 @@ public interface IInterface2 await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.IInterface1__IInterface2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IInterface1__IInterface2.g.cs"); + await That(result.Sources["Mock.IInterface1__IInterface2.g.cs"]) .Contains("public void Method(string? value)").And .Contains("void global::MyCode.IInterface2.Method(string value)"); } @@ -97,7 +98,8 @@ public MyService(int value) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("public static global::MyCode.MyService CreateMock(int value)") .IgnoringNewlineStyle().And .Contains("=> CreateMock(null, null, new object?[] { value });") @@ -137,7 +139,8 @@ public MyService(int number, string text) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("public static global::MyCode.MyService CreateMock(string text)") .IgnoringNewlineStyle().And .Contains("public static global::MyCode.MyService CreateMock(int number, string text)") @@ -167,7 +170,8 @@ public class MyService { } await That(result.Diagnostics).IsEmpty(); // No typed overloads beyond the existing hand-written ones; the existing parameterless // CreateMock() overload is still there and no typed overload is emitted for it. - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("public static global::MyCode.MyService CreateMock()") .IgnoringNewlineStyle(); } @@ -196,7 +200,8 @@ public MyService(char c = '\n') { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(char c = '\\n')") .IgnoringNewlineStyle(); } @@ -225,7 +230,8 @@ public MyService(decimal price = 19.95m) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(decimal price = 19.95m)") .IgnoringNewlineStyle(); } @@ -256,7 +262,8 @@ public MyService(MyKind kind = MyKind.Beta) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(global::MyCode.MyKind kind = (global::MyCode.MyKind)1)") .IgnoringNewlineStyle(); } @@ -285,7 +292,8 @@ public MyService(float factor = 3.14f) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(float factor = 3.14f)") .IgnoringNewlineStyle(); } @@ -315,7 +323,8 @@ public MyService(in int value) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .DoesNotContain("CreateMock(in int value)") .IgnoringNewlineStyle().And .DoesNotContain("CreateMock(int value)") @@ -346,7 +355,8 @@ public MyService(long big = 9999999999L, double pi = 3.14) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(long big = 9999999999, double pi = 3.14)") .IgnoringNewlineStyle(); } @@ -376,7 +386,8 @@ public MyService(decimal? price = 19.95m) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(decimal? price = 19.95m)") .IgnoringNewlineStyle(); } @@ -406,7 +417,8 @@ public MyService(string? text = null) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(string? text = null)") .IgnoringNewlineStyle(); } @@ -435,7 +447,8 @@ public MyService(params int[] values) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .DoesNotContain("public static global::MyCode.MyService CreateMock(int[] values)") .IgnoringNewlineStyle().And .DoesNotContain("public static global::MyCode.MyService CreateMock(params int[] values)") @@ -466,7 +479,8 @@ public MyService(string text = "has \"quotes\"") { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(string text = \"has \\\"quotes\\\"\")") .IgnoringNewlineStyle(); } @@ -497,7 +511,8 @@ public MyService(MyToken token = default) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(global::MyCode.MyToken token = default)") .IgnoringNewlineStyle(); } @@ -561,7 +576,8 @@ public MyService(Action callback) { } await That(result.Diagnostics).IsEmpty(); // Action does not collide with the setup action type (Action), // so the generator should emit the typed overload. - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("CreateMock(global::System.Action callback)") .IgnoringNewlineStyle(); } @@ -593,7 +609,8 @@ public MyService(int? value) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains("public static global::MyCode.MyService CreateMock(int value)") .IgnoringNewlineStyle().And .Contains("public static global::MyCode.MyService CreateMock(int? value)") @@ -629,7 +646,8 @@ public DerivedService(string text) : base(text) { } """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.DerivedService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.DerivedService.g.cs"); + await That(result.Sources["Mock.DerivedService.g.cs"]) .Contains("public static global::MyCode.DerivedService CreateMock(string text)") .IgnoringNewlineStyle(); } @@ -736,7 +754,8 @@ public interface IMyInterface await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs"); + await That(result.Sources["Mock.MyService__IMyInterface.g.cs"]) .Contains("static bool TryCastWithDefaultValue(object?[] values, int index, TValue defaultValue, global::Mockolate.MockBehavior behavior, out TValue result)") .IgnoringNewlineStyle().And .Contains("mock.MockRegistry.ConstructorParameters.Length >= 0 && mock.MockRegistry.ConstructorParameters.Length <= 1") @@ -773,7 +792,8 @@ public interface IMyInterface await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs"); + await That(result.Sources["Mock.MyService__IMyInterface.g.cs"]) .Contains("static bool TryCast(object?[] values, int index, global::Mockolate.MockBehavior behavior, out TValue result)") .IgnoringNewlineStyle().And .Contains("No parameterless constructor found for 'MyCode.MyService'") @@ -811,7 +831,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task)); - await That(result.Sources).ContainsKey("ActionFunc.g.cs").WhoseValue + await That(result.Sources).ContainsKey("ActionFunc.g.cs"); + await That(result.Sources["ActionFunc.g.cs"]) .Contains( "public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);") .And @@ -1060,7 +1081,8 @@ public interface IInterface2 await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.IInterface1__IInterface2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IInterface1__IInterface2.g.cs"); + await That(result.Sources["Mock.IInterface1__IInterface2.g.cs"]) .Contains("public void Method(ref int value)").And .Contains("void global::MyCode.IInterface2.Method(in int value)"); } @@ -1164,7 +1186,8 @@ public MyService(int value, string text) { } } """, DocumentationMode.Diagnose); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains($"to invoke the MyService(int, string) constructor.") .IgnoringNewlineStyle(); @@ -1195,7 +1218,8 @@ public static void Main(string[] args) } """, DocumentationMode.Diagnose, typeof(HttpClient)); - await That(result.Sources).ContainsKey("Mock.HttpClient.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.HttpClient.g.cs"); + await That(result.Sources["Mock.HttpClient.g.cs"]) .Contains("") .IgnoringNewlineStyle().And .DoesNotContain("HttpClient.HttpClient(") @@ -1233,7 +1257,8 @@ public MyService(T value) { } // Closed-generic constructor crefs (e.g. MyService{int}.MyService(int)) aren't valid C# // cref syntax, so the generator falls back to the unlinked phrasing for generic classes // rather than emit something that would surface CS1584 on the consumer side. - await That(result.Sources).ContainsKey("Mock.MyService_int.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService_int.g.cs"); + await That(result.Sources["Mock.MyService_int.g.cs"]) .Contains("to invoke the base-class constructor.") .IgnoringNewlineStyle().And .DoesNotContain("MyService.MyService(") diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.EventsTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.EventsTests.cs index 1c0d8a92..4f6ffa93 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.EventsTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.EventsTests.cs @@ -39,7 +39,8 @@ public interface IMyServiceBase2 } """); - await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"); + await That(result.Sources["Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"]) .Contains("public event global::System.EventHandler? SomeEvent").Once().And .Contains("event global::System.EventHandler? global::MyCode.IMyServiceBase1.SomeEvent").Once().And .Contains("event global::System.EventHandler? global::MyCode.IMyServiceBase2.SomeEvent").Once(); @@ -68,7 +69,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// /// Setup for the event SomeEvent. @@ -114,7 +116,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" private global::System.EventHandler? _mockolateEvent_global__MyCode_IMyService_SomeEvent; /// @@ -217,7 +220,8 @@ public interface IMyServiceBase3 } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" private global::System.EventHandler? _mockolateEvent_global__MyCode_IMyService_MyDirectEvent; /// @@ -380,7 +384,8 @@ public interface IMyOtherService } """); - await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs"); + await That(result.Sources["Mock.MyService__IMyOtherService.g.cs"]) .Contains(""" private global::System.EventHandler? _mockolateEvent_global__MyCode_MyService_SomeEvent; /// diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs index fe1dfb06..9bdc966b 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs @@ -29,7 +29,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .DoesNotContain("global::Mockolate.Interactions.IndexerGetterAccess access = new(\"access\",") .IgnoringNewlineStyle(); } @@ -57,7 +58,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) // The wrap-base pattern-match cast must not collide with the user's `wraps` // indexer parameter. .DoesNotContain("global::MyCode.IMyService wraps)") @@ -93,7 +95,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public int this[int index] @@ -277,7 +280,8 @@ public interface IMyOtherService } """); - await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs"); + await That(result.Sources["Mock.MyService__IMyOtherService.g.cs"]) .Contains(""" /// public override int this[int index] @@ -418,7 +422,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public int this[global::System.Span buffer] diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs index 95256e87..1dbe640b 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs @@ -29,7 +29,8 @@ public class MyService } """); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) // The pattern-match cast must not bind a local named `wraps` because the user's // parameter already occupies that name. .DoesNotContain("global::MyCode.MyService wraps)") @@ -69,7 +70,8 @@ public interface IMyOtherService } """); - await That(result.Sources).ContainsKey("Mock.IMyService__IMyOtherService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService__IMyOtherService.g.cs"); + await That(result.Sources["Mock.IMyService__IMyOtherService.g.cs"]) .Contains(""" /// global::System.Threading.Tasks.Task global::MyCode.IMyOtherService.DoSomethingAsync() @@ -126,7 +128,8 @@ public class MyClass } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains($$""" /// public bool MyMethod1(int index) @@ -168,7 +171,8 @@ public class MyClass } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public bool MyMethod1(int index) @@ -200,7 +204,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains( "foreach (global::Mockolate.Setup.ReturnMethodSetup s_methodSetup in this.MockRegistry.GetMethodSetups>(\"global::MyCode.IMyService.ProcessData\"))") .IgnoringNewlineStyle().And @@ -234,7 +239,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) // The numbered cast variable must not reuse the parameter name; the base is // renamed so `outParam1`/`outParam2` (parameters) and `outParam_1` (cast) // don't collide. @@ -267,7 +273,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains( "foreach (global::Mockolate.Setup.ReturnMethodSetup s_methodSetup in this.MockRegistry.GetMethodSetups>(\"global::MyCode.IMyService.ProcessResult\"))") .IgnoringNewlineStyle().And @@ -300,7 +307,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .DoesNotContain("out var returnValue)") .IgnoringNewlineStyle().And .Contains("out var returnValue1) == true ? returnValue1 :") @@ -332,8 +340,8 @@ public interface IService } """); - await That(result.Sources) - .ContainsKey("Mock.IService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IService.g.cs"); + await That(result.Sources["Mock.IService.g.cs"]) .Contains("(global::MyCode.Status)1") .IgnoringNewlineStyle(); } @@ -362,8 +370,8 @@ public interface IService } """, typeof(DateTime)); - await That(result.Sources) - .ContainsKey("Mock.IService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IService.g.cs"); + await That(result.Sources["Mock.IService.g.cs"]) .Contains("DateTime time = default") .IgnoringNewlineStyle(); } @@ -403,7 +411,8 @@ public interface IMyServiceBase2 } """); - await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"); + await That(result.Sources["Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"]) .Contains("public string Value()").Once().And .Contains("public string Value(string p1)").Once().And .Contains("int global::MyCode.IMyServiceBase1.Value()").Once().And @@ -435,7 +444,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public bool MyMethod1(int index) @@ -547,7 +557,8 @@ public interface IMyServiceBase3 } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public int MyDirectMethod(int value) @@ -751,7 +762,8 @@ public interface IMyOtherService } """); - await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs"); + await That(result.Sources["Mock.MyService__IMyOtherService.g.cs"]) .Contains(""" /// public override void MyMethod1(int index, ref int value1, out bool flag) @@ -923,11 +935,13 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" public void MyMethod1(int a, int b = 1, bool? c = null, string d = "default") """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" global::Mockolate.Setup.IVoidMethodSetupWithCallback global::Mockolate.Mock.IMockSetupForIMyService.MyMethod1(global::Mockolate.Parameters.IParameter? a, global::Mockolate.Parameters.IParameter? b, global::Mockolate.Parameters.IParameter? c, global::Mockolate.Parameters.IParameter? d) { @@ -964,11 +978,13 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" public void MyMethod1(int a, params int[] b) """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" global::Mockolate.Setup.IVoidMethodSetupWithCallback global::Mockolate.Mock.IMockSetupForIMyService.MyMethod1(global::Mockolate.Parameters.IParameter? a, global::Mockolate.Parameters.IParameter? b) { @@ -1013,7 +1029,8 @@ public readonly struct MyReadonlyStruct } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public void MyMethod1(ref int index) @@ -1209,7 +1226,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" global::Mockolate.Setup.IVoidMethodSetupWithCallback> global::Mockolate.Mock.IMockSetupForIMyService.MyMethod1(global::Mockolate.Parameters.ISpanParameter buffer) { @@ -1265,7 +1283,8 @@ public virtual bool MyMethod(T entity) } """); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains(""" /// public override bool MyMethod(T entity) diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs index cf68ff70..54c1ce89 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.PropertiesTests.cs @@ -1,4 +1,4 @@ -namespace Mockolate.SourceGenerators.Tests; +namespace Mockolate.SourceGenerators.Tests; public sealed partial class MockTests { @@ -39,7 +39,8 @@ public interface IMyServiceBase2 } """); - await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"); + await That(result.Sources["Mock.IMyService__IMyServiceBase1__IMyServiceBase2.g.cs"]) .Contains("public string Value").Once().And .Contains("int global::MyCode.IMyServiceBase1.Value").Once().And .Contains("long global::MyCode.IMyServiceBase2.Value").Once(); @@ -73,7 +74,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public int SomeProperty @@ -210,7 +212,8 @@ public interface IMyServiceBase3 } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" /// public int MyDirectProperty @@ -324,7 +327,8 @@ public interface IMyOtherService } """); - await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyOtherService.g.cs"); + await That(result.Sources["Mock.MyService__IMyOtherService.g.cs"]) .Contains(""" /// public override int SomeProperty1 diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.cs index db194c16..7e7c2aab 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.cs @@ -27,7 +27,8 @@ public class MyService } """); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .Contains( "foreach (global::Mockolate.Setup.ReturnMethodSetup s_methodSetup in this.MockRegistry.GetMethodSetups>(\"global::MyCode.MyService.ProcessData\"))") .IgnoringNewlineStyle().And @@ -73,7 +74,8 @@ protected AnnotatedShape() { } // One occurrence per generator-emitted public ctor: the (MockRegistry, ...) overload and // the typed (MockBehavior, ...) overload that subclasses use. The base ctor already carries // the attribute, and the dedup check still suppresses an injected duplicate on each. - await That(result.Sources).ContainsKey("Mock.AnnotatedShape.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.AnnotatedShape.g.cs"); + await That(result.Sources["Mock.AnnotatedShape.g.cs"]) .Contains("[global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers]").Exactly(2); } @@ -106,7 +108,8 @@ public abstract class DerivedShape : BaseShape """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.DerivedShape.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.DerivedShape.g.cs"); + await That(result.Sources["Mock.DerivedShape.g.cs"]) .Contains("[global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers]"); } @@ -135,7 +138,8 @@ public abstract class PlainShape """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.PlainShape.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.PlainShape.g.cs"); + await That(result.Sources["Mock.PlainShape.g.cs"]) .DoesNotContain("SetsRequiredMembers"); } @@ -164,7 +168,8 @@ public abstract class RequiredShape """); await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.RequiredShape.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.RequiredShape.g.cs"); + await That(result.Sources["Mock.RequiredShape.g.cs"]) .Contains(""" [global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers] public RequiredShape(global::Mockolate.MockRegistry mockRegistry) @@ -202,7 +207,8 @@ public interface IMyBaseService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains( "Setup for the int property BaseProperty.") .And @@ -250,7 +256,8 @@ public interface IHasEvent } """); - await That(result.Sources).ContainsKey("Mock.IHasEvent.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IHasEvent.g.cs"); + await That(result.Sources["Mock.IHasEvent.g.cs"]) .Contains("Raise - trigger events declared on the mocked type.").And .Contains(".Mock.Raise triggers events declared on the mocked type.").And .DoesNotContain("SetupProtected / VerifyProtected / RaiseProtected").And @@ -280,7 +287,8 @@ public interface IPlain } """); - await That(result.Sources).ContainsKey("Mock.IPlain.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IPlain.g.cs"); + await That(result.Sources["Mock.IPlain.g.cs"]) .DoesNotContain("Raise - trigger events declared on the mocked type.").And .DoesNotContain("SetupProtected / VerifyProtected / RaiseProtected").And .DoesNotContain("SetupStatic / VerifyStatic / RaiseStatic").And diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs index 4248fcef..da154864 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs @@ -38,7 +38,8 @@ public interface IMyInterface await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs"); + await That(result.Sources["Mock.MyService__IMyInterface.g.cs"]) .Contains("static bool TryCast(object?[] values, int index, global::Mockolate.MockBehavior behavior, out TValue result)").And .Contains("static bool TryCastWithDefaultValue(object?[] values, int index, TValue defaultValue, global::Mockolate.MockBehavior behavior, out TValue result)").And // Parameterless dispatch. @@ -77,7 +78,8 @@ public interface IMyInterface await That(result.Diagnostics).IsEmpty(); - await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IMyInterface.g.cs"); + await That(result.Sources["Mock.MyService__IMyInterface.g.cs"]) .Contains("No parameterless constructor found for 'MyCode.MyService'").And .Contains("static bool TryCast(object?[] values, int index, global::Mockolate.MockBehavior behavior, out TValue result)").And .DoesNotContain("static bool TryCastWithDefaultValue"); @@ -112,7 +114,8 @@ public interface IExtra } """); - await That(result.Sources).ContainsKey("Mock.MyService__IExtra.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IExtra.g.cs"); + await That(result.Sources["Mock.MyService__IExtra.g.cs"]) .Contains("IMockProtectedRaiseOnMyService").And .Contains("#region IMockProtectedRaiseOnMyService"); } @@ -146,7 +149,8 @@ public interface IExtra } """); - await That(result.Sources).ContainsKey("Mock.MyService__IExtra.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService__IExtra.g.cs"); + await That(result.Sources["Mock.MyService__IExtra.g.cs"]) // AppendMockSubject_BaseClassConstructor stamps SetsRequiredMembers on the generated // constructor when the base class declares any `required` member. .Contains("[global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers]"); @@ -181,7 +185,8 @@ public interface IStaticEvents } """); - await That(result.Sources).ContainsKey("Mock.IBase__IStaticEvents.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IBase__IStaticEvents.g.cs"); + await That(result.Sources["Mock.IBase__IStaticEvents.g.cs"]) .Contains("IMockStaticRaiseOnIStaticEvents").And .Contains("#region IMockStaticRaiseOnIStaticEvents").And .Contains("internal static readonly global::System.Threading.AsyncLocal MockRegistryProvider"); @@ -216,7 +221,8 @@ public interface IStaticAware } """); - await That(result.Sources).ContainsKey("Mock.IBase__IStaticAware.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IBase__IStaticAware.g.cs"); + await That(result.Sources["Mock.IBase__IStaticAware.g.cs"]) .Contains("IMockStaticSetupForIStaticAware").And .Contains("IMockStaticVerifyForIStaticAware").And .Contains("#region IMockStaticSetupForIStaticAware").And diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.DelegateTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.DelegateTests.cs index 1aa0cada..dabc1bc4 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.DelegateTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.DelegateTests.cs @@ -25,8 +25,8 @@ public static void Main(string[] args) } """); - await That(result.Sources) - .ContainsKey("Mock.Program_DoSomething.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_DoSomething.g.cs"); + await That(result.Sources["Mock.Program_DoSomething.g.cs"]) .Contains(""" global::Mockolate.Setup.IReturnMethodSetupWithCallback global::Mockolate.Mock.IMockSetupForProgram_DoSomething.Setup(global::Mockolate.Parameters.IParameter? x, global::Mockolate.Parameters.IParameter? y, global::Mockolate.Parameters.IOutParameter success) { @@ -74,8 +74,8 @@ public static void Main(string[] args) } """); - await That(result.Sources) - .ContainsKey("Mock.Program_DoSomething1.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_DoSomething1.g.cs"); + await That(result.Sources["Mock.Program_DoSomething1.g.cs"]) .Contains(""" public global::MyCode.Program.DoSomething1 Object => new(Invoke); private global::System.Span Invoke(int x) @@ -98,8 +98,8 @@ await That(result.Sources) return methodSetup?.TryGetReturnValue(x, out var returnValue) == true ? returnValue : this.MockRegistry.Behavior.DefaultValue.Generate(default(global::Mockolate.Setup.SpanWrapper)!); } """).IgnoringNewlineStyle(); - await That(result.Sources) - .ContainsKey("Mock.Program_DoSomething2.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_DoSomething2.g.cs"); + await That(result.Sources["Mock.Program_DoSomething2.g.cs"]) .Contains(""" public global::MyCode.Program.DoSomething2 Object => new(Invoke); private global::System.ReadOnlySpan Invoke(int x) @@ -145,8 +145,8 @@ public static void Main(string[] args) } """); - await That(result.Sources) - .ContainsKey("Mock.Program_DoSomething.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_DoSomething.g.cs"); + await That(result.Sources["Mock.Program_DoSomething.g.cs"]) .Contains(""" global::Mockolate.Setup.IVoidMethodSetupWithCallback global::Mockolate.Mock.IMockSetupForProgram_DoSomething.Setup(global::Mockolate.Parameters.IParameter? x, global::Mockolate.Parameters.IRefParameter y, global::Mockolate.Parameters.IOutParameter z) { @@ -192,7 +192,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.Program_DoSomething.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_DoSomething.g.cs"); + await That(result.Sources["Mock.Program_DoSomething.g.cs"]) .Contains("Verify invocations for the delegate DoSomething with the given , .").And .Contains("Verify invocations for the delegate DoSomething with the given .").And .DoesNotContain("Verify invocations for the method s_methodSetup in this.MockRegistry.GetMethodSetups>(\"global::System.Func.Invoke\"))") .IgnoringNewlineStyle().And @@ -244,8 +246,8 @@ public static void Main(string[] args) } """); - await That(result.Sources) - .ContainsKey("Mock.Program_ProcessAll.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_ProcessAll.g.cs"); + await That(result.Sources["Mock.Program_ProcessAll.g.cs"]) .Contains("Setup(int a, int b, int c, int d, int e)") .IgnoringNewlineStyle().And .Contains("Verify(int a, int b, int c, int d, int e)") @@ -275,8 +277,8 @@ public static void Main(string[] args) } """); - await That(result.Sources) - .ContainsKey("Mock.Program_ProcessAll.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_ProcessAll.g.cs"); + await That(result.Sources["Mock.Program_ProcessAll.g.cs"]) .Contains( "Setup(global::Mockolate.Parameters.IOutParameter a, global::Mockolate.Parameters.IOutParameter b, global::Mockolate.Parameters.IOutParameter c, global::Mockolate.Parameters.IOutParameter d, global::Mockolate.Parameters.IOutParameter e)") .IgnoringNewlineStyle().And @@ -305,7 +307,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.Program_ProcessResult.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_ProcessResult.g.cs"); + await That(result.Sources["Mock.Program_ProcessResult.g.cs"]) .Contains("foreach (global::Mockolate.Setup.ReturnMethodSetup s_methodSetup1 in this.MockRegistry.GetMethodSetups>(") .IgnoringNewlineStyle().And .Contains("methodSetup1?.TriggerCallbacks(methodSetup);") @@ -335,7 +338,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.Program_ProcessResult.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.Program_ProcessResult.g.cs"); + await That(result.Sources["Mock.Program_ProcessResult.g.cs"]) .Contains("foreach (global::Mockolate.Setup.VoidMethodSetup s_methodSetup1 in this.MockRegistry.GetMethodSetups>(") .IgnoringNewlineStyle().And .Contains("methodSetup1?.TriggerCallbacks(methodSetup, value);") diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs index 231e06fd..2067d950 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs @@ -30,7 +30,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructVoidMethodSetup").And .Contains("public sealed class RefStructVoidMethodSetup").And .Contains("where T16 : allows ref struct").And @@ -63,7 +64,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerSetup").And .Contains("public sealed class RefStructIndexerSetup").And // Combined facade exposes the inner Getter / Setter properties. @@ -102,7 +104,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerGetterSetup").And .Contains("public sealed class RefStructIndexerGetterSetup").And // Storage / projection plumbing must be present at arity 5. @@ -139,7 +142,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerSetterSetup").And .Contains("public sealed class RefStructIndexerSetterSetup").And // BoundGetter property is emitted on the setter so combined setups can forward writes. @@ -175,7 +179,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructReturnMethodSetup").And .Contains("public sealed class RefStructReturnMethodSetup").And .Contains("public bool HasReturnValue => _returnFactory is not null;").And @@ -213,12 +218,14 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IBigSink.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IBigSink.g.cs"); + await That(result.Sources["Mock.IBigSink.g.cs"]) .Contains("RefStructVoidMethodSetup").And // The method body must not bail out to NotSupportedException for this arity. .Contains("matched = true"); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("#if NET9_0_OR_GREATER").And .Contains("public interface IRefStructVoidMethodSetup").And .Contains("public sealed class RefStructVoidMethodSetup").And @@ -252,7 +259,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructReturnMethodSetup").And .Contains("public sealed class RefStructReturnMethodSetup").And .Contains("public bool HasReturnValue"); @@ -284,7 +292,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructVoidMethodSetup").And .Contains("public sealed class RefStructVoidMethodSetup").And .Contains("where T1 : allows ref struct").And @@ -320,7 +329,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructReturnMethodSetup").And .Contains("public sealed class RefStructReturnMethodSetup").And .Contains("public bool HasReturnValue => _returnFactory is not null;").And @@ -354,7 +364,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs").WhoseValue + await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); + await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructVoidMethodSetup").And .Contains("public sealed class RefStructVoidMethodSetup").And .Contains("where T8 : allows ref struct").And @@ -388,7 +399,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IRefStructLookup.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IRefStructLookup.g.cs"); + await That(result.Sources["Mock.IRefStructLookup.g.cs"]) // Body uses the ref-struct dispatch, not NotSupportedException. .Contains("RefStructIndexerGetterSetup").And .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructLookup.get_Item\", \"key\")").And @@ -424,7 +436,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IRefStructStore.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IRefStructStore.g.cs"); + await That(result.Sources["Mock.IRefStructStore.g.cs"]) // Combined facade type is the public setup surface. .Contains("IRefStructIndexerSetup").And // Both accessors now dispatch through the ref-struct pipeline, not NSE. @@ -459,7 +472,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IRefStructWriter.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IRefStructWriter.g.cs"); + await That(result.Sources["Mock.IRefStructWriter.g.cs"]) .Contains("IRefStructIndexerSetterSetup").And .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructWriter.set_Item\", \"key\", \"value\")"); } @@ -490,7 +504,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IPacketWriter.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IPacketWriter.g.cs"); + await That(result.Sources["Mock.IPacketWriter.g.cs"]) // Two-arg ref-struct setup with the int flowing through as T2 (allows ref struct // is satisfied by any type, so int works). .Contains("RefStructVoidMethodSetup").And @@ -523,7 +538,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IBoringService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IBoringService.g.cs"); + await That(result.Sources["Mock.IBoringService.g.cs"]) // Regular pipeline markers. .Contains("global::Mockolate.Interactions.MethodInvocation").And .Contains("global::Mockolate.Setup.VoidMethodSetup").And @@ -586,7 +602,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IPacketParser.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IPacketParser.g.cs"); + await That(result.Sources["Mock.IPacketParser.g.cs"]) .Contains("RefStructReturnMethodSetup").And .Contains("IRefStructReturnMethodSetup").And // The return-side HasReturnValue gate is there so Throws-only setups still fall @@ -620,7 +637,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.IPacketSink.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IPacketSink.g.cs"); + await That(result.Sources["Mock.IPacketSink.g.cs"]) .Contains("#if NET9_0_OR_GREATER").And // Method body: records a RefStructMethodInvocation (names only, no value). .Contains("RefStructMethodInvocation(\"global::MyCode.IPacketSink.Consume\", \"packet\")").And diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs index 62e1f3af..28cd65c4 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.cs @@ -65,7 +65,8 @@ public static void Main(string[] args) } """); - await That(result.Sources).ContainsKey("Mock.OuterClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.OuterClass.g.cs"); + await That(result.Sources["Mock.OuterClass.g.cs"]) .Contains("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.OuterValue").And .Contains("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.BaseClassValue").And .DoesNotContain("global::Mockolate.Setup.PropertySetup global::Mockolate.Mock.IMockSetupForOuterClass.DirectValue").And @@ -132,7 +133,8 @@ public static void Main(string[] args) } """, typeof(IEnumerator), typeof(IEnumerable)); - await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); + await That(result.Sources["Mock.MyService.g.cs"]) .DoesNotContain("private global::System.Collections.IEnumerator GetEnumerator()"); } @@ -161,7 +163,8 @@ protected MyBaseClass(int value, bool flag) { } } """); - await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs"); + await That(result.Sources["Mock.MyBaseClass.g.cs"]) .DoesNotContain("No parameterless constructor found"); } @@ -189,7 +192,8 @@ protected MyBaseClass(int value, bool flag) { } } """); - await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs"); + await That(result.Sources["Mock.MyBaseClass.g.cs"]) .Contains(""" if (constructorParameters.Length == 1 && TryCast(constructorParameters, 0, mockRegistry.Behavior, out int c1p1)) @@ -264,7 +268,8 @@ public MyBaseClass() { } } """); - await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs"); + await That(result.Sources["Mock.MyBaseClass.g.cs"]) .DoesNotContain("CreateMock(object?[] constructorParameters)").And .DoesNotContain("CreateMock(global::Mockolate.MockBehavior mockBehavior, object?[] constructorParameters)").And .DoesNotContain("object?[] constructorParameters)").And @@ -294,7 +299,8 @@ public class MyBaseClass } """); - await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyBaseClass.g.cs"); + await That(result.Sources["Mock.MyBaseClass.g.cs"]) .DoesNotContain("CreateMock(object?[] constructorParameters)").And .DoesNotContain("object?[] constructorParameters)").And .Contains( @@ -356,7 +362,8 @@ public class MySubClass } """); - await That(result.Sources).ContainsKey("Mock.MyClassWithSealedEvents.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyClassWithSealedEvents.g.cs"); + await That(result.Sources["Mock.MyClassWithSealedEvents.g.cs"]) .DoesNotContain("event System.EventHandler? SomeEvent"); } @@ -389,7 +396,8 @@ public class MySubClass } """); - await That(result.Sources).ContainsKey("Mock.MyClassWithSealedIndexers.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyClassWithSealedIndexers.g.cs"); + await That(result.Sources["Mock.MyClassWithSealedIndexers.g.cs"]) .DoesNotContain("override int this[int index]"); } @@ -423,7 +431,8 @@ public virtual void MyMethod(int value) { } } """); - await That(result.Sources).ContainsKey("Mock.MyClassWithSealedMethods.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyClassWithSealedMethods.g.cs"); + await That(result.Sources["Mock.MyClassWithSealedMethods.g.cs"]) .DoesNotContain("override void MyMethod(int value)"); } @@ -456,7 +465,8 @@ public class MySubClass } """); - await That(result.Sources).ContainsKey("Mock.MyClassWithSealedProperties.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyClassWithSealedProperties.g.cs"); + await That(result.Sources["Mock.MyClassWithSealedProperties.g.cs"]) .DoesNotContain("override int MyProperty"); } @@ -488,7 +498,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains("public int @class").And .Contains("public string @return()").And .Contains("public void @event(int @params)").And @@ -524,7 +535,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" public string this[int @true] """).And @@ -532,7 +544,8 @@ public string this[int @true] public void DoSomething(int @event) """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" global::Mockolate.Setup.IVoidMethodSetupWithCallback global::Mockolate.Mock.IMockSetupForIMyService.DoSomething(global::Mockolate.Parameters.IParameter? @event) { @@ -597,7 +610,8 @@ protected virtual void ProtectedMethod() { } } """); - await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs"); + await That(result.Sources["Mock.MyDerivedClass.g.cs"]) .DoesNotContain("override void SealedMethod").And .Contains("ProtectedInternalMethod").And .Contains("InternalMethod").And @@ -639,7 +653,8 @@ public virtual void SomeMethod() { } } """); - await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs"); + await That(result.Sources["Mock.MyDerivedClass.g.cs"]) .DoesNotContain("override bool Equals").And .DoesNotContain("override int GetHashCode").And .DoesNotContain("override string ToString"); @@ -679,7 +694,8 @@ public virtual void SomeMethod() { } // Even though MyMiddleClass.Equals has non-nullable parameter (object), // it should still match and filter out object.Equals with nullable parameter (object?) - await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.MyDerivedClass.g.cs"); + await That(result.Sources["Mock.MyDerivedClass.g.cs"]) .DoesNotContain("override bool Equals"); } @@ -706,7 +722,8 @@ public interface IMyService } """); - await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue + await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); + await That(result.Sources["Mock.IMyService.g.cs"]) .Contains(""" public void MyMethod(object v1, bool v2, string v3, char v4, byte v5, sbyte v6, short v7, ushort v8, int v9, uint v10, long v11, ulong v12, float v13, double v14, decimal v15) { diff --git a/Tests/Mockolate.SourceGenerators.Tests/ReturnsThrowsAsyncExtensionsTests.cs b/Tests/Mockolate.SourceGenerators.Tests/ReturnsThrowsAsyncExtensionsTests.cs index c55142c9..79f1a058 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/ReturnsThrowsAsyncExtensionsTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/ReturnsThrowsAsyncExtensionsTests.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace Mockolate.SourceGenerators.Tests; @@ -61,7 +61,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task), typeof(CancellationToken)); - await That(result.Sources).ContainsKey("ReturnsThrowsAsyncExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("ReturnsThrowsAsyncExtensions.g.cs"); + await That(result.Sources["ReturnsThrowsAsyncExtensions.g.cs"]) .Contains( "public static global::Mockolate.Setup.IReturnMethodSetupReturnBuilder, T1, T2, T3, T4, T5> ReturnsAsync(this global::Mockolate.Setup.IReturnMethodSetup, T1, T2, T3, T4, T5> setup, TReturn returnValue)") .And @@ -126,7 +127,8 @@ public interface IMyInterface } """, typeof(DateTime), typeof(Task), typeof(CancellationToken)); - await That(result.Sources).ContainsKey("ReturnsThrowsAsyncExtensions.g.cs").WhoseValue + await That(result.Sources).ContainsKey("ReturnsThrowsAsyncExtensions.g.cs"); + await That(result.Sources["ReturnsThrowsAsyncExtensions.g.cs"]) .Contains("ReturnsAsync(this").And .Contains("ThrowsAsync(this").And .DoesNotContain("ReturnsAsync(this").And From 2f58889e200c67b22cd63ed5a4b8a06390d7ab6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 29 Apr 2026 05:53:31 +0200 Subject: [PATCH 2/2] Move comments to `.Because` --- .../MockGenerator.AggregationTests.cs | 12 +-- .../MockTests.ClassTests.IndexerTests.cs | 5 +- .../MockTests.ClassTests.MethodTests.cs | 15 ++-- .../MockTests.CombinationTests.cs | 17 ++-- .../MockTests.RefStructTests.cs | 86 +++++++++---------- 5 files changed, 64 insertions(+), 71 deletions(-) diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs index eb458ba0..412de347 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockGenerator.AggregationTests.cs @@ -58,13 +58,13 @@ public interface IB { } await That(result.Sources).ContainsKey("Mock.AsExtensions.g.cs"); await That(result.Sources["Mock.AsExtensions.g.cs"]) - // (IBase ↔ IB): bridge from a typed-as-IBase mock to IMockForIB. - .Contains("internal static partial class MockExtensionsForIB").And + .Contains("internal static partial class MockExtensionsForIB") + .Because("the (IBase ↔ IB) bridge from a typed-as-IBase mock to IMockForIB must be emitted").And .Contains("extension(global::Mockolate.Mock.IMockForIBase mock)").And - // (IA ↔ IB): bridge from a typed-as-IA mock to IMockForIB. - .Contains("extension(global::Mockolate.Mock.IMockForIA mock)").And - // Reverse direction is also emitted. - .Contains("internal static partial class MockExtensionsForIBase").And + .Contains("extension(global::Mockolate.Mock.IMockForIA mock)") + .Because("the (IA ↔ IB) bridge from a typed-as-IA mock to IMockForIB must be emitted").And + .Contains("internal static partial class MockExtensionsForIBase") + .Because("the reverse direction must also be emitted").And .Contains("internal static partial class MockExtensionsForIA"); } diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs index 9bdc966b..dbdc2f44 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.IndexerTests.cs @@ -60,10 +60,9 @@ public interface IMyService await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); await That(result.Sources["Mock.IMyService.g.cs"]) - // The wrap-base pattern-match cast must not collide with the user's `wraps` - // indexer parameter. .DoesNotContain("global::MyCode.IMyService wraps)") - .IgnoringNewlineStyle().And + .IgnoringNewlineStyle() + .Because("the wrap-base pattern-match cast must not collide with the user's `wraps` indexer parameter").And .DoesNotContain("global::MyCode.IMyService wraps ?") .IgnoringNewlineStyle().And .Contains("global::MyCode.IMyService wraps1") diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs index 1dbe640b..2f147d00 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs @@ -31,15 +31,14 @@ public class MyService await That(result.Sources).ContainsKey("Mock.MyService.g.cs"); await That(result.Sources["Mock.MyService.g.cs"]) - // The pattern-match cast must not bind a local named `wraps` because the user's - // parameter already occupies that name. .DoesNotContain("global::MyCode.MyService wraps)") - .IgnoringNewlineStyle().And + .IgnoringNewlineStyle() + .Because("the pattern-match cast must not bind a local named `wraps` while the user's parameter already occupies that name").And .Contains("global::MyCode.MyService wraps1)") .IgnoringNewlineStyle().And - // The forwarding call must use the deduped name, not the parameter. .Contains("wraps1.Run(wraps);") - .IgnoringNewlineStyle(); + .IgnoringNewlineStyle() + .Because("the forwarding call must use the deduped name, not the parameter"); } [Fact] @@ -241,11 +240,9 @@ public interface IMyService await That(result.Sources).ContainsKey("Mock.IMyService.g.cs"); await That(result.Sources["Mock.IMyService.g.cs"]) - // The numbered cast variable must not reuse the parameter name; the base is - // renamed so `outParam1`/`outParam2` (parameters) and `outParam_1` (cast) - // don't collide. .Contains("IOutParameter outParam_11") - .IgnoringNewlineStyle().And + .IgnoringNewlineStyle() + .Because("the numbered cast variable must not reuse the parameter name (the base is renamed so `outParam1`/`outParam2` parameters and `outParam_1` cast don't collide)").And .Contains("IOutParameter outParam_12") .IgnoringNewlineStyle(); } diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs index da154864..dfc10d7f 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.CombinationTests.cs @@ -42,10 +42,10 @@ public interface IMyInterface await That(result.Sources["Mock.MyService__IMyInterface.g.cs"]) .Contains("static bool TryCast(object?[] values, int index, global::Mockolate.MockBehavior behavior, out TValue result)").And .Contains("static bool TryCastWithDefaultValue(object?[] values, int index, TValue defaultValue, global::Mockolate.MockBehavior behavior, out TValue result)").And - // Parameterless dispatch. - .Contains("if (mock.MockRegistry.ConstructorParameters is null || mock.MockRegistry.ConstructorParameters.Length == 0)").And - // 'else if' arity dispatch chain (mixed-required + defaulted ctor produces the optional-range branch). - .Contains("else if (mock.MockRegistry.ConstructorParameters.Length >= 1 && mock.MockRegistry.ConstructorParameters.Length <= 2"); + .Contains("if (mock.MockRegistry.ConstructorParameters is null || mock.MockRegistry.ConstructorParameters.Length == 0)") + .Because("the parameterless dispatch branch must be emitted").And + .Contains("else if (mock.MockRegistry.ConstructorParameters.Length >= 1 && mock.MockRegistry.ConstructorParameters.Length <= 2") + .Because("the 'else if' arity dispatch chain must include the optional-range branch for a mixed-required + defaulted ctor"); } [Fact] @@ -151,9 +151,8 @@ public interface IExtra await That(result.Sources).ContainsKey("Mock.MyService__IExtra.g.cs"); await That(result.Sources["Mock.MyService__IExtra.g.cs"]) - // AppendMockSubject_BaseClassConstructor stamps SetsRequiredMembers on the generated - // constructor when the base class declares any `required` member. - .Contains("[global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers]"); + .Contains("[global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers]") + .Because("AppendMockSubject_BaseClassConstructor must stamp SetsRequiredMembers on the generated constructor when the base class declares any `required` member"); } [Fact] @@ -227,8 +226,8 @@ await That(result.Sources["Mock.IBase__IStaticAware.g.cs"]) .Contains("IMockStaticVerifyForIStaticAware").And .Contains("#region IMockStaticSetupForIStaticAware").And .Contains("#region IMockStaticVerifyForIStaticAware").And - // AsyncLocal field is required so static accessors can find the registry. - .Contains("internal static readonly global::System.Threading.AsyncLocal MockRegistryProvider"); + .Contains("internal static readonly global::System.Threading.AsyncLocal MockRegistryProvider") + .Because("the AsyncLocal field is required so static accessors can find the registry"); } } } diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs index 2067d950..fa498b8f 100644 --- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs +++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.RefStructTests.cs @@ -68,11 +68,11 @@ public static void Main(string[] args) await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerSetup").And .Contains("public sealed class RefStructIndexerSetup").And - // Combined facade exposes the inner Getter / Setter properties. - .Contains("Getter { get; }").And + .Contains("Getter { get; }") + .Because("the combined facade must expose the inner Getter / Setter properties").And .Contains("Setter { get; }").And - // Storage plumbing visible on the getter-only emission. - .Contains("TryResolveKey").And + .Contains("TryResolveKey") + .Because("the storage plumbing must be visible on the getter-only emission").And .Contains("GetChildDispatch").And .Contains("StoreValue").And .Contains("BoundGetter"); @@ -108,11 +108,11 @@ public static void Main(string[] args) await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerGetterSetup").And .Contains("public sealed class RefStructIndexerGetterSetup").And - // Storage / projection plumbing must be present at arity 5. - .Contains("TryResolveKey").And + .Contains("TryResolveKey") + .Because("the storage / projection plumbing must be present at arity 5").And .Contains("GetChildDispatch").And - // Setter-only and combined surfaces must NOT be emitted for a getter-only indexer. - .DoesNotContain("public sealed class RefStructIndexerSetterSetup").And + .DoesNotContain("public sealed class RefStructIndexerSetterSetup") + .Because("setter-only and combined surfaces must not be emitted for a getter-only indexer").And .DoesNotContain("public sealed class RefStructIndexerSetup"); } @@ -146,10 +146,10 @@ public static void Main(string[] args) await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public interface IRefStructIndexerSetterSetup").And .Contains("public sealed class RefStructIndexerSetterSetup").And - // BoundGetter property is emitted on the setter so combined setups can forward writes. - .Contains("BoundGetter").And - // No getter-only or combined surface for a setter-only indexer. - .DoesNotContain("public sealed class RefStructIndexerGetterSetup").And + .Contains("BoundGetter") + .Because("the BoundGetter property must be emitted on the setter so combined setups can forward writes").And + .DoesNotContain("public sealed class RefStructIndexerGetterSetup") + .Because("no getter-only or combined surface should be emitted for a setter-only indexer").And .DoesNotContain("public sealed class RefStructIndexerSetup"); } @@ -186,8 +186,8 @@ await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public bool HasReturnValue => _returnFactory is not null;").And .Contains(".Returns(TReturn returnValue)").And .Contains(".Returns(global::System.Func returnFactory)").And - // default! fallback when neither return factory nor defaultFactory is present. - .Contains("return defaultFactory is not null ? defaultFactory() : default!;"); + .Contains("return defaultFactory is not null ? defaultFactory() : default!;") + .Because("a `default!` fallback must be emitted when neither return factory nor defaultFactory is present"); } [Fact] @@ -221,16 +221,16 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IBigSink.g.cs"); await That(result.Sources["Mock.IBigSink.g.cs"]) .Contains("RefStructVoidMethodSetup").And - // The method body must not bail out to NotSupportedException for this arity. - .Contains("matched = true"); + .Contains("matched = true") + .Because("the method body must not bail out to NotSupportedException for this arity"); await That(result.Sources).ContainsKey("RefStructMethodSetups.g.cs"); await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("#if NET9_0_OR_GREATER").And .Contains("public interface IRefStructVoidMethodSetup").And .Contains("public sealed class RefStructVoidMethodSetup").And - // Every generic parameter carries the allows-ref-struct anti-constraint. - .Contains("where T5 : allows ref struct"); + .Contains("where T5 : allows ref struct") + .Because("every generic parameter must carry the allows-ref-struct anti-constraint"); } [Fact] @@ -298,8 +298,8 @@ await That(result.Sources["RefStructMethodSetups.g.cs"]) .Contains("public sealed class RefStructVoidMethodSetup").And .Contains("where T1 : allows ref struct").And .Contains("where T6 : allows ref struct").And - // _returnAction body present (matches the emitted void-setup throw machinery). - .Contains("private global::System.Func? _returnAction;").And + .Contains("private global::System.Func? _returnAction;") + .Because("the _returnAction body must be present to match the emitted void-setup throw machinery").And .Contains("_returnAction = static () => new TException();"); } @@ -401,11 +401,11 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IRefStructLookup.g.cs"); await That(result.Sources["Mock.IRefStructLookup.g.cs"]) - // Body uses the ref-struct dispatch, not NotSupportedException. - .Contains("RefStructIndexerGetterSetup").And + .Contains("RefStructIndexerGetterSetup") + .Because("the body must use the ref-struct dispatch, not NotSupportedException").And .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructLookup.get_Item\", \"key\")").And - // The setup facade exposes the narrow IRefStructIndexerGetterSetup surface. - .Contains("IRefStructIndexerGetterSetup"); + .Contains("IRefStructIndexerGetterSetup") + .Because("the setup facade must expose the narrow IRefStructIndexerGetterSetup surface"); } [Fact] @@ -438,10 +438,10 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IRefStructStore.g.cs"); await That(result.Sources["Mock.IRefStructStore.g.cs"]) - // Combined facade type is the public setup surface. - .Contains("IRefStructIndexerSetup").And - // Both accessors now dispatch through the ref-struct pipeline, not NSE. - .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructStore.get_Item\", \"key\")").And + .Contains("IRefStructIndexerSetup") + .Because("the combined facade type is the public setup surface").And + .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructStore.get_Item\", \"key\")") + .Because("both accessors must dispatch through the ref-struct pipeline, not NotSupportedException").And .Contains("RefStructMethodInvocation(\"global::MyCode.IRefStructStore.set_Item\", \"key\", \"value\")").And .Contains("RefStructIndexerSetterSetup"); } @@ -506,9 +506,8 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IPacketWriter.g.cs"); await That(result.Sources["Mock.IPacketWriter.g.cs"]) - // Two-arg ref-struct setup with the int flowing through as T2 (allows ref struct - // is satisfied by any type, so int works). - .Contains("RefStructVoidMethodSetup").And + .Contains("RefStructVoidMethodSetup") + .Because("the two-arg ref-struct setup must let the int flow through as T2 (allows ref struct is satisfied by any type, so int works)").And .Contains("setup.Matches(packet, priority)"); } @@ -540,11 +539,11 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IBoringService.g.cs"); await That(result.Sources["Mock.IBoringService.g.cs"]) - // Regular pipeline markers. - .Contains("global::Mockolate.Interactions.MethodInvocation").And + .Contains("global::Mockolate.Interactions.MethodInvocation") + .Because("the regular pipeline markers must be emitted").And .Contains("global::Mockolate.Setup.VoidMethodSetup").And - // Must NOT use the ref-struct pipeline. - .DoesNotContain("RefStructMethodInvocation").And + .DoesNotContain("RefStructMethodInvocation") + .Because("the ref-struct pipeline must not be used here").And .DoesNotContain("RefStructVoidMethodSetup"); } @@ -606,9 +605,8 @@ public static void Main(string[] args) await That(result.Sources["Mock.IPacketParser.g.cs"]) .Contains("RefStructReturnMethodSetup").And .Contains("IRefStructReturnMethodSetup").And - // The return-side HasReturnValue gate is there so Throws-only setups still fall - // through to the framework default. - .Contains("if (setup.HasReturnValue)"); + .Contains("if (setup.HasReturnValue)") + .Because("the return-side HasReturnValue gate must be present so Throws-only setups still fall through to the framework default"); } [Fact] @@ -640,13 +638,13 @@ public static void Main(string[] args) await That(result.Sources).ContainsKey("Mock.IPacketSink.g.cs"); await That(result.Sources["Mock.IPacketSink.g.cs"]) .Contains("#if NET9_0_OR_GREATER").And - // Method body: records a RefStructMethodInvocation (names only, no value). - .Contains("RefStructMethodInvocation(\"global::MyCode.IPacketSink.Consume\", \"packet\")").And - // Dispatch: iterates matching setups via the GetMethodSetups(name) API. - .Contains("GetMethodSetups>").And + .Contains("RefStructMethodInvocation(\"global::MyCode.IPacketSink.Consume\", \"packet\")") + .Because("the method body must record a RefStructMethodInvocation (names only, no value)").And + .Contains("GetMethodSetups>") + .Because("dispatch must iterate matching setups via the GetMethodSetups(name) API").And .Contains("RefStructVoidMethodSetup").And - // Setup facade entry point uses the narrow IRefStructVoidMethodSetup surface. - .Contains("IRefStructVoidMethodSetup").And + .Contains("IRefStructVoidMethodSetup") + .Because("the setup facade entry point must use the narrow IRefStructVoidMethodSetup surface").And .Contains("global::Mockolate.Parameters.IParameter? packet"); } }