Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 73 additions & 76 deletions Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1888,82 +1888,93 @@ private static void AppendMockSubject_ImplementClass_AddMethod(StringBuilder sb,
.AppendLine();
}

if (hasOutParams)
{
foreach (MethodParameter parameter in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
sb.Append("\t\t\t").Append(parameter.Name).Append(" = default!;").AppendLine();
}
}

sb.Append("\t\t\tif (").Append(mockRegistry).Append(".Behavior.SkipInteractionRecording == false)").AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\t").Append(mockRegistry)
.Append(".RegisterInteraction(new global::Mockolate.Interactions.MethodInvocation");
if (method.Parameters.Count > 0)
{
sb.Append('<').Append(string.Join(", ", method.Parameters.Select(p => p.ToTypeOrWrapper()))).Append('>');
}

sb.Append("(").Append(method.GetUniqueNameString());
if (method.Parameters.Count > 0)
{
sb.Append(", ").Append(string.Join(", ", method.Parameters.Select(p => $"\"{p.Name}\", {p.ToNameOrWrapper()}")));
}

sb.Append("));").AppendLine();
sb.Append("\t\t\t}").AppendLine();

sb.Append("\t\t\ttry").AppendLine();
sb.Append("\t\t\t{").AppendLine();

if (supportsWrapping)
{
sb.Append("\t\t\tif (").Append(mockRegistry).Append(".Wraps is ").Append(className)
sb.Append("\t\t\t\tif (").Append(mockRegistry).Append(".Wraps is ").Append(className)
.Append(" wraps)").AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\t{").AppendLine();
if (method.ReturnType != Type.Void)
{
sb.Append("\t\t\t\t").Append(wrappedResult).Append(" = wraps").Append(".")
sb.Append("\t\t\t\t\t").Append(wrappedResult).Append(" = wraps").Append(".")
.Append(method.Name).Append('(')
.Append(FormatMethodParametersWithRefKind(method.Parameters))
.Append(");").AppendLine();
sb.Append("\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
sb.Append("\t\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
}
else
{
sb.Append("\t\t\t\twraps").Append(".")
sb.Append("\t\t\t\t\twraps").Append(".")
.Append(method.Name).Append('(')
.Append(FormatMethodParametersWithRefKind(method.Parameters))
.Append(");").AppendLine();
sb.Append("\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
sb.Append("\t\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
}

sb.Append("\t\t\t}").AppendLine();
if (hasOutParams)
{
sb.Append("\t\t\telse").AppendLine();
sb.Append("\t\t\t{").AppendLine();
foreach (MethodParameter parameter in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
sb.Append("\t\t\t\t").Append(parameter.Name).Append(" = default!;").AppendLine();
}

sb.Append("\t\t\t}").AppendLine();
}
}
else if (hasOutParams)
{
foreach (MethodParameter parameter in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
sb.Append("\t\t\t").Append(parameter.Name).Append(" = default!;").AppendLine();
}
sb.Append("\t\t\t\t}").AppendLine();
}

if (!isAbstractOrInterface)
{
if (method.Name.StartsWith("Send", StringComparison.Ordinal) &&
@class is { ClassFullName: "global::System.Net.Http.HttpClient", })
{
sb.Append("\t\t\t#if NETFRAMEWORK").AppendLine();
sb.Append("\t\t\t\t#if NETFRAMEWORK").AppendLine();
sb.Append(
"\t\t\t// Persist the HttpContent, because it gets automatically disposed on .NET Framework")
"\t\t\t\t// Persist the HttpContent, because it gets automatically disposed on .NET Framework")
.AppendLine();
sb.Append("\t\t\tif (request.Content != null)").AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\tif (request.Content != null)").AppendLine();
sb.Append("\t\t\t\t{").AppendLine();
sb.Append(
"\t\t\t\tvar stream = request.Content.ReadAsStreamAsync().ConfigureAwait(false).GetAwaiter().GetResult();")
"\t\t\t\t\tvar stream = request.Content.ReadAsStreamAsync().ConfigureAwait(false).GetAwaiter().GetResult();")
.AppendLine();
sb.Append("\t\t\t\tusing global::System.IO.MemoryStream ms = new();").AppendLine();
sb.Append("\t\t\t\tstream.CopyTo(ms);").AppendLine();
sb.Append("\t\t\t\tbyte[] bytes = ms.ToArray();").AppendLine();
sb.Append("\t\t\t\tstream.Position = 0L;").AppendLine();
sb.Append("\t\t\t\trequest.Properties.Add(\"Mockolate:HttpContent\", bytes);").AppendLine();
sb.Append("\t\t\t}").AppendLine();
sb.Append("\t\t\t#endif").AppendLine();
sb.Append("\t\t\t\t\tusing global::System.IO.MemoryStream ms = new();").AppendLine();
sb.Append("\t\t\t\t\tstream.CopyTo(ms);").AppendLine();
sb.Append("\t\t\t\t\tbyte[] bytes = ms.ToArray();").AppendLine();
sb.Append("\t\t\t\t\tstream.Position = 0L;").AppendLine();
sb.Append("\t\t\t\t\trequest.Properties.Add(\"Mockolate:HttpContent\", bytes);").AppendLine();
sb.Append("\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\t#endif").AppendLine();
}

sb.Append("\t\t\tif (!(").Append(methodSetup).Append("?.SkipBaseClass(").Append(mockRegistry)
sb.Append("\t\t\t\tif (!(").Append(methodSetup).Append("?.SkipBaseClass(").Append(mockRegistry)
.Append(".Behavior) ?? ").Append(mockRegistry).Append(".Behavior.SkipBaseClass)");
if (supportsWrapping)
{
sb.Append(" && !").Append(hasWrappedResult);
}

sb.Append(')').AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\t");
sb.Append("\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t");
if (method.ReturnType != Type.Void)
{
sb.Append(wrappedResult).Append(" = ");
Expand All @@ -1972,79 +1983,67 @@ private static void AppendMockSubject_ImplementClass_AddMethod(StringBuilder sb,
sb.Append("base.").Append(method.Name).Append('(')
.Append(FormatMethodParametersWithRefKind(method.Parameters))
.Append(");").AppendLine();
sb.Append("\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
sb.Append("\t\t\t}").AppendLine();
sb.Append("\t\t\t\t\t").Append(hasWrappedResult).Append(" = true;").AppendLine();
sb.Append("\t\t\t\t}").AppendLine();
}

if (hasOutParams || hasRefParams)
{
sb.Append("\t\t\tif (!").Append(hasWrappedResult).Append(" || ").Append(methodSetup).Append(" is ").Append(methodSetupType)
sb.Append("\t\t\t\tif (!").Append(hasWrappedResult).Append(" || ").Append(methodSetup).Append(" is ").Append(methodSetupType)
.Append(".WithParameterCollection)")
.AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\tif (").Append(methodSetup).Append(" is ").Append(methodSetupType)
.Append(".WithParameterCollection ").Append(wpc).Append(')').AppendLine();
sb.Append("\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\tif (").Append(methodSetup).Append(" is ").Append(methodSetupType)
.Append(".WithParameterCollection ").Append(wpc).Append(')').AppendLine();
sb.Append("\t\t\t\t\t{").AppendLine();
int parameterIndex = 0;
foreach (MethodParameter parameter in method.Parameters)
{
parameterIndex++;
if (parameter.RefKind == RefKind.Out)
{
sb.Append("\t\t\t\t\tif (").Append(wpc).Append(".Parameter").Append(parameterIndex)
sb.Append("\t\t\t\t\t\tif (").Append(wpc).Append(".Parameter").Append(parameterIndex)
.Append(" is not global::Mockolate.Parameters.IOutParameter<")
.Append(parameter.Type.ToTypeOrWrapper()).Append("> outParam").Append(parameterIndex)
.Append(" || !outParam").Append(parameterIndex).Append(".TryGetValue(out ")
.Append(parameter.Name).Append("))").AppendLine();
sb.Append("\t\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t\t").Append(parameter.Name).Append(" = ")
sb.Append("\t\t\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t\t\t").Append(parameter.Name).Append(" = ")
.AppendDefaultValueGeneratorFor(parameter.Type, $"{mockRegistry}.Behavior.DefaultValue")
.Append(';').AppendLine();
sb.Append("\t\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\t\t\t}").AppendLine();
}
else if (parameter.RefKind == RefKind.Ref)
{
sb.Append("\t\t\t\t\tif (").Append(wpc).Append(".Parameter").Append(parameterIndex)
sb.Append("\t\t\t\t\t\tif (").Append(wpc).Append(".Parameter").Append(parameterIndex)
.Append(" is global::Mockolate.Parameters.IRefParameter<")
.Append(parameter.Type.ToTypeOrWrapper()).Append("> refParam").Append(parameterIndex)
.Append(")").AppendLine();
sb.Append("\t\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t\t").Append(parameter.Name).Append(" = refParam").Append(parameterIndex)
sb.Append("\t\t\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t\t\t").Append(parameter.Name).Append(" = refParam").Append(parameterIndex)
.Append(".GetValue(").Append(parameter.Name).Append(");").AppendLine();
sb.Append("\t\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\t\t\t}").AppendLine();
}
}

sb.Append("\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\telse").AppendLine();
sb.Append("\t\t\t\t{").AppendLine();
sb.Append("\t\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\t\telse").AppendLine();
sb.Append("\t\t\t\t\t{").AppendLine();
foreach (MethodParameter parameter in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
sb.Append("\t\t\t\t\t").Append(parameter.Name).Append(" = ")
sb.Append("\t\t\t\t\t\t").Append(parameter.Name).Append(" = ")
.AppendDefaultValueGeneratorFor(parameter.Type, $"{mockRegistry}.Behavior.DefaultValue").Append(';')
.AppendLine();
}

sb.Append("\t\t\t\t\t}").AppendLine();
sb.Append("\t\t\t\t}").AppendLine();
sb.Append("\t\t\t}").AppendLine();
}

sb.Append("\t\t\tif (").Append(mockRegistry).Append(".Behavior.SkipInteractionRecording == false)").AppendLine();
sb.Append("\t\t\t}").AppendLine();
sb.Append("\t\t\tfinally").AppendLine();
sb.Append("\t\t\t{").AppendLine();
sb.Append("\t\t\t\t").Append(mockRegistry)
.Append(".RegisterInteraction(new global::Mockolate.Interactions.MethodInvocation");
if (method.Parameters.Count > 0)
{
sb.Append('<').Append(string.Join(", ", method.Parameters.Select(p => p.ToTypeOrWrapper()))).Append('>');
}

sb.Append("(").Append(method.GetUniqueNameString());
if (method.Parameters.Count > 0)
{
sb.Append(", ").Append(string.Join(", ", method.Parameters.Select(p => $"\"{p.Name}\", {p.ToNameOrWrapper()}")));
}

sb.Append("));").AppendLine();
AppendTriggerCallbacks(sb, "\t\t\t\t", methodSetup, method.Parameters);
sb.Append("\t\t\t}").AppendLine();

string displayMethodName = $"{method.ContainingType}.{method.Name}({string.Join(", ", method.Parameters.Select(p => p.Type.DisplayName))})";
Expand All @@ -2053,8 +2052,6 @@ private static void AppendMockSubject_ImplementClass_AddMethod(StringBuilder sb,
sb.Append("\t\t\t\tthrow new global::Mockolate.Exceptions.MockNotSetupException(\"The method '").Append(displayMethodName).Append("' was invoked without prior setup.\");").AppendLine();
sb.Append("\t\t\t}").AppendLine();

AppendTriggerCallbacks(sb, "\t\t\t", methodSetup, method.Parameters);

if (method.ReturnType != Type.Void)
{
sb.Append("\t\t\tif (").Append(methodSetup).Append("?.HasReturnCallbacks != true && ").Append(hasWrappedResult).Append(")").AppendLine();
Expand Down
33 changes: 29 additions & 4 deletions Source/Mockolate/MockRegistry.Interactions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.ExceptionServices;
using Mockolate.Exceptions;
using Mockolate.Interactions;
using Mockolate.Setup;
Expand Down Expand Up @@ -252,12 +253,36 @@ public TResult GetProperty<TResult>(string propertyName, Func<TResult> defaultVa
new PropertyGetterAccess(propertyName));
}

PropertySetup matchingSetup = ResolvePropertySetup(
propertyName, defaultValueGenerator, baseValueAccessor,
baseValueAccessor is not null);
PropertySetup matchingSetup;
if (baseValueAccessor is null)
{
matchingSetup = ResolvePropertySetup(propertyName, defaultValueGenerator, null, false);

return ((IInteractivePropertySetup)matchingSetup).InvokeGetter(interaction, Behavior,
return ((IInteractivePropertySetup)matchingSetup).InvokeGetter(interaction, Behavior,
defaultValueGenerator);
}
ExceptionDispatchInfo? capturedBaseException = null;
Func<TResult> safeBaseValueAccessor = () =>
{
try
{
return baseValueAccessor();
}
catch (Exception ex)
{
capturedBaseException = ExceptionDispatchInfo.Capture(ex);
return default!;
}
};

matchingSetup = ResolvePropertySetup(
propertyName, defaultValueGenerator, safeBaseValueAccessor, true);

TResult result = ((IInteractivePropertySetup)matchingSetup).InvokeGetter(interaction, Behavior,
defaultValueGenerator);

capturedBaseException?.Throw();
return result;
}

/// <summary>
Expand Down
18 changes: 12 additions & 6 deletions Tests/Mockolate.SourceGenerators.Tests/GeneralTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -797,20 +797,26 @@ public string MyMethod(string message)
var methodSetup = this.MockRegistry.GetMethodSetup<global::Mockolate.Setup.ReturnMethodSetup<string, string>>("global::MyCode.IMyService.MyMethod", m => m.Matches("message", message));
bool hasWrappedResult = false;
string wrappedResult = default!;
if (this.MockRegistry.Wraps is global::MyCode.IMyService wraps)
{
wrappedResult = wraps.MyMethod(message);
hasWrappedResult = true;
}
if (this.MockRegistry.Behavior.SkipInteractionRecording == false)
{
this.MockRegistry.RegisterInteraction(new global::Mockolate.Interactions.MethodInvocation<string>("global::MyCode.IMyService.MyMethod", "message", message));
}
try
{
if (this.MockRegistry.Wraps is global::MyCode.IMyService wraps)
{
wrappedResult = wraps.MyMethod(message);
hasWrappedResult = true;
}
}
finally
{
methodSetup?.TriggerCallbacks(message);
}
if (methodSetup is null && !hasWrappedResult && this.MockRegistry.Behavior.ThrowWhenNotSetup)
{
throw new global::Mockolate.Exceptions.MockNotSetupException("The method 'global::MyCode.IMyService.MyMethod(string)' was invoked without prior setup.");
}
methodSetup?.TriggerCallbacks(message);
if (methodSetup?.HasReturnCallbacks != true && hasWrappedResult)
{
return wrappedResult;
Expand Down
Loading