Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Source/Mockolate.SourceGenerators/Entities/MethodParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ public MethodParameter(IParameterSymbol parameterSymbol)
Name = SyntaxFacts.GetKeywordKind(parameterSymbol.Name) != SyntaxKind.None ? "@" + parameterSymbol.Name : parameterSymbol.Name;
RefKind = parameterSymbol.RefKind;
IsNullableAnnotated = parameterSymbol.NullableAnnotation == NullableAnnotation.Annotated;
IsParams = parameterSymbol.IsParams;
HasExplicitDefaultValue = parameterSymbol.HasExplicitDefaultValue;
if (HasExplicitDefaultValue)
{
ExplicitDefaultValue = SymbolDisplay.FormatPrimitive(parameterSymbol.ExplicitDefaultValue, true, false);
Comment thread
vbreuss marked this conversation as resolved.
Outdated
}
}

public string? ExplicitDefaultValue { get; }
public bool HasExplicitDefaultValue { get; }
public bool IsParams { get; }

public bool IsNullableAnnotated { get; }
public Type Type { get; }
public string Name { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using Mockolate.SourceGenerators.Entities;
using Mockolate.SourceGenerators.Internals;
using Attribute = Mockolate.SourceGenerators.Entities.Attribute;
using Type = Mockolate.SourceGenerators.Entities.Type;

namespace Mockolate.SourceGenerators.Entities;
namespace Mockolate.SourceGenerators;

internal static class Helpers
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ private static void AppendMethodSetup(Class @class, StringBuilder sb, Method met
}

sb.Append(' ').Append(parameter.Name);
if (parameter.HasExplicitDefaultValue)
{
sb.Append(" = null");
}
}

sb.Append(")");
Expand Down Expand Up @@ -875,6 +879,10 @@ private static void AppendInvokedExtensions(StringBuilder sb, Class @class,
}

sb.Append(' ').Append(parameter.Name);
if (parameter.HasExplicitDefaultValue)
{
sb.Append(" = null");
}
}

sb.Append(")");
Expand Down
28 changes: 27 additions & 1 deletion Source/Mockolate.SourceGenerators/Sources/Sources.ForMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,17 @@ private static void AppendMockSubject_BaseClassConstructor(StringBuilder sb, Moc
sb.Append("\tpublic MockFor").Append(name).Append("(");
foreach (MethodParameter parameter in constructor.Parameters)
{
if (parameter.IsParams)
{
sb.Append("params ");
}

sb.Append(parameter.Type.Fullname).Append(' ').Append(parameter.Name);
if (parameter.HasExplicitDefaultValue)
{
sb.Append(" = ").Append(parameter.ExplicitDefaultValue);
}

sb.Append(", ");
}

Expand Down Expand Up @@ -647,7 +657,16 @@ private static void AppendMockSubject_ImplementClass_AddMethod(StringBuilder sb,
}

sb.Append(parameter.RefKind.GetString());
if (parameter.IsParams)
{
sb.Append("params ");
}

sb.Append(parameter.Type.Fullname).Append(' ').Append(parameter.Name);
if (parameter.HasExplicitDefaultValue)
{
sb.Append(" = ").Append(parameter.ExplicitDefaultValue);
}
}

sb.Append(')');
Expand Down Expand Up @@ -950,7 +969,14 @@ private static void AppendNamedParameter(StringBuilder sb, MethodParameter param
sb.Append(parameter.Name);
if (parameter.CanBeNullable())
{
sb.Append(" ?? global::Mockolate.It.IsNull<").Append(parameter.ToNullableType()).Append(">()");
if (parameter.HasExplicitDefaultValue)
{
sb.Append(" ?? global::Mockolate.It.Is<").Append(parameter.ToNullableType()).Append(">(").Append(parameter.ExplicitDefaultValue).Append(")");
}
else
{
sb.Append(" ?? global::Mockolate.It.IsNull<").Append(parameter.ToNullableType()).Append(">()");
}
}

sb.Append("))");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,90 @@ await That(result.Sources).ContainsKey("MockForIMyServiceExtensions.g.cs").Whose
""").IgnoringNewlineStyle();
}

[Fact]
public async Task Methods_ShouldSupportParamsParameters()
{
GeneratorResult result = Generator
.Run("""
using System;
using Mockolate;

namespace MyCode;
public class Program
{
public static void Main(string[] args)
{
_ = Mock.Create<IMyService>();
}
}

public interface IMyService
{
void MyMethod1(int a, params int[] b);
}
""");

await That(result.Sources).ContainsKey("MockForIMyService.g.cs").WhoseValue
.Contains("""
public void MyMethod1(int a, params int[] b)
""");
await That(result.Sources).ContainsKey("MockForIMyServiceExtensions.g.cs").WhoseValue
.Contains("""
public global::Mockolate.Setup.IVoidMethodSetup<int, int[]> MyMethod1(global::Mockolate.Parameters.IParameter<int>? a, global::Mockolate.Parameters.IParameter<int[]>? b)
{
var methodSetup = new global::Mockolate.Setup.VoidMethodSetup<int, int[]>("MyCode.IMyService.MyMethod1", new global::Mockolate.Parameters.NamedParameter("a", (global::Mockolate.Parameters.IParameter)(a ?? global::Mockolate.It.IsNull<int>())), new global::Mockolate.Parameters.NamedParameter("b", (global::Mockolate.Parameters.IParameter)(b ?? global::Mockolate.It.IsNull<int[]>())));
CastToMockRegistrationOrThrow(setup).SetupMethod(methodSetup);
return methodSetup;
}
""").IgnoringNewlineStyle().And
.Contains("""
public global::Mockolate.Verify.VerificationResult<MyCode.IMyService> MyMethod1(global::Mockolate.Parameters.IParameter<int>? a, global::Mockolate.Parameters.IParameter<int[]>? b)
=> CastToMockOrThrow(verifyInvoked).Method("MyCode.IMyService.MyMethod1", new global::Mockolate.Parameters.NamedParameter("a", (global::Mockolate.Parameters.IParameter)(a ?? global::Mockolate.It.IsNull<int>())), new global::Mockolate.Parameters.NamedParameter("b", (global::Mockolate.Parameters.IParameter)(b ?? global::Mockolate.It.IsNull<int[]>())));
""").IgnoringNewlineStyle();
}

[Fact]
public async Task Methods_ShouldSupportOptionalParameters()
{
GeneratorResult result = Generator
.Run("""
using System;
using Mockolate;

namespace MyCode;
public class Program
{
public static void Main(string[] args)
{
_ = Mock.Create<IMyService>();
}
}

public interface IMyService
{
void MyMethod1(int a, int b = 1, bool? c = null, string d = "default");
}
""");

await That(result.Sources).ContainsKey("MockForIMyService.g.cs").WhoseValue
.Contains("""
public void MyMethod1(int a, int b = 1, bool? c = null, string d = "default")
""");
await That(result.Sources).ContainsKey("MockForIMyServiceExtensions.g.cs").WhoseValue
.Contains("""
public global::Mockolate.Setup.IVoidMethodSetup<int, int, bool?, string> MyMethod1(global::Mockolate.Parameters.IParameter<int>? a, global::Mockolate.Parameters.IParameter<int>? b = null, global::Mockolate.Parameters.IParameter<bool?>? c = null, global::Mockolate.Parameters.IParameter<string>? d = null)
{
var methodSetup = new global::Mockolate.Setup.VoidMethodSetup<int, int, bool?, string>("MyCode.IMyService.MyMethod1", new global::Mockolate.Parameters.NamedParameter("a", (global::Mockolate.Parameters.IParameter)(a ?? global::Mockolate.It.IsNull<int>())), new global::Mockolate.Parameters.NamedParameter("b", (global::Mockolate.Parameters.IParameter)(b ?? global::Mockolate.It.Is<int>(1))), new global::Mockolate.Parameters.NamedParameter("c", (global::Mockolate.Parameters.IParameter)(c ?? global::Mockolate.It.Is<bool?>(null))), new global::Mockolate.Parameters.NamedParameter("d", (global::Mockolate.Parameters.IParameter)(d ?? global::Mockolate.It.Is<string>("default"))));
CastToMockRegistrationOrThrow(setup).SetupMethod(methodSetup);
return methodSetup;
}
""").IgnoringNewlineStyle().And
.Contains("""
public global::Mockolate.Verify.VerificationResult<MyCode.IMyService> MyMethod1(global::Mockolate.Parameters.IParameter<int>? a, global::Mockolate.Parameters.IParameter<int>? b = null, global::Mockolate.Parameters.IParameter<bool?>? c = null, global::Mockolate.Parameters.IParameter<string>? d = null)
=> CastToMockOrThrow(verifyInvoked).Method("MyCode.IMyService.MyMethod1", new global::Mockolate.Parameters.NamedParameter("a", (global::Mockolate.Parameters.IParameter)(a ?? global::Mockolate.It.IsNull<int>())), new global::Mockolate.Parameters.NamedParameter("b", (global::Mockolate.Parameters.IParameter)(b ?? global::Mockolate.It.Is<int>(1))), new global::Mockolate.Parameters.NamedParameter("c", (global::Mockolate.Parameters.IParameter)(c ?? global::Mockolate.It.Is<bool?>(null))), new global::Mockolate.Parameters.NamedParameter("d", (global::Mockolate.Parameters.IParameter)(d ?? global::Mockolate.It.Is<string>("default"))));
""").IgnoringNewlineStyle();
}

[Fact]
public async Task Properties_MultipleImplementations_ShouldOnlyHaveOneExplicitImplementation()
{
Expand Down
1 change: 1 addition & 0 deletions Tests/Mockolate.Tests/MatchExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public async Task OutParameterMonitor_MultipleMonitors_ShouldAllMonitorValues()
mock.MyMethodWithOutParam(out _);
mock.MyMethodWithOutParam(out _);
mock.MyMethodWithOutParam(out _);
mock.MyMethodWithOptionalParameters(5);
Comment thread
vbreuss marked this conversation as resolved.

await That(monitor1.Values).IsEqualTo([1, 2, 3,]);
await That(monitor2.Values).IsEqualTo([1, 2, 3,]);
Expand Down
32 changes: 32 additions & 0 deletions Tests/Mockolate.Tests/MockMethods/SetupMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,38 @@ public async Task WithInParameter_ShouldUseSetup()
await That(result).IsEqualTo(3);
}

[Fact]
public async Task WithOptionalParameters_ShouldUseOptionalValueWhenNotSet()
{
IMyService mock = Mock.Create<IMyService>();
mock.SetupMock.Method.MyMethodWithOptionalParameters(It.IsAny<int>()).Returns(true);

bool result1 = mock.MyMethodWithOptionalParameters(5);
bool result2 = mock.MyMethodWithOptionalParameters(5, 1);
bool result3 = mock.MyMethodWithOptionalParameters(5, c: "bar");

await That(result1).IsTrue();
await That(result2).IsFalse();
await That(result3).IsFalse();
}

[Fact]
public async Task WithParamsParameters_ShouldUseOptionalValueWhenNotSet()
{
Comment thread
vbreuss marked this conversation as resolved.
Outdated
IMyService mock = Mock.Create<IMyService>();
mock.SetupMock.Method.MyMethodWithParams(It.IsAny<int>(), It.Satisfies<bool[]>(x => x.Length > 1)).Returns(true);

bool result1 = mock.MyMethodWithParams(5);
bool result2 = mock.MyMethodWithParams(5, true);
bool result3 = mock.MyMethodWithParams(5, true, false);
bool result4 = mock.MyMethodWithParams(5, true, false, true);

await That(result1).IsFalse();
await That(result2).IsFalse();
await That(result3).IsTrue();
await That(result4).IsTrue();
}

[Fact]
public async Task WithRefReadonlyParameter_ShouldUseSetup()
{
Expand Down
2 changes: 2 additions & 0 deletions Tests/Mockolate.Tests/TestHelpers/IMyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ internal interface IMyService

void MyMethodWithOutParam(out int value);
void MyMethodWithRefParam(ref int value);
bool MyMethodWithParams(int a, params bool[] flags);
bool MyMethodWithOptionalParameters(int a, int b = 0, string c = "foo");

event EventHandler? MyEvent;

Expand Down
Loading