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
66 changes: 33 additions & 33 deletions Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Mockolate.SourceGenerators.Sources;
internal static partial class Sources
{
private const int MaxExplicitParameters = 4;

public static string MockClass(string name, Class @class)
{
EquatableArray<Method>? constructors = (@class as MockClass)?.Constructors;
Expand Down Expand Up @@ -1740,7 +1740,7 @@ private static void DefineSetupInterface(StringBuilder sb, Class @class, MemberT
property.MemberType == memberType;
foreach (Property property in @class.AllProperties().Where(propertyPredicate))
{
sb.AppendXmlSummary($"Setup for the {property.Type.Fullname.EscapeForXmlDoc()} property <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{property.Name.EscapeForXmlDoc()}\" />.");
sb.AppendXmlSummary($"Setup for the {property.Type.Fullname.EscapeForXmlDoc()} property <see cref=\"{property.ContainingType.EscapeForXmlDoc()}.{property.Name.EscapeForXmlDoc()}\" />.");
sb.Append("\t\tglobal::Mockolate.Setup.PropertySetup<").Append(property.Type.Fullname).Append("> ").Append(property.Name).Append(" { get; }").AppendLine();
sb.AppendLine();
}
Expand All @@ -1754,20 +1754,20 @@ private static void DefineSetupInterface(StringBuilder sb, Class @class, MemberT
indexer.MemberType == memberType;
foreach (Property indexer in @class.AllProperties().Where(indexerPredicate))
{
AppendIndexerSetupDefinition(sb, @class, indexer);
AppendIndexerSetupDefinition(sb, indexer);
if (indexer.IndexerParameters!.Value.Count <= MaxExplicitParameters)
{
foreach (bool[] valueFlags in GenerateValueFlagCombinations(indexer.IndexerParameters.Value))
{
AppendIndexerSetupDefinition(sb, @class, indexer, valueFlags);
AppendIndexerSetupDefinition(sb, indexer, valueFlags);
}
}
else
{
bool[] allValueFlags = indexer.IndexerParameters.Value.Select(p => p.CanBeExplicitValue()).ToArray();
if (allValueFlags.Any(f => f))
{
AppendIndexerSetupDefinition(sb, @class, indexer, allValueFlags);
AppendIndexerSetupDefinition(sb, indexer, allValueFlags);
}
}
}
Expand Down Expand Up @@ -1830,7 +1830,7 @@ private static void AppendMethodSetupDefinition(StringBuilder sb, Class @class,
if (methodNameOverride is null)
{
sb.Append("\t\t/// Setup for the method <see cref=\"")
.Append(@class.ClassFullName.EscapeForXmlDoc()).Append(".")
.Append(method.ContainingType.EscapeForXmlDoc()).Append(".")
.Append(method.Name.EscapeForXmlDoc()).Append("(")
.Append(string.Join(", ",
method.Parameters.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc())))
Expand Down Expand Up @@ -2004,7 +2004,7 @@ private static void ImplementSetupInterface(StringBuilder sb, Class @class, stri

Func<Method, bool> methodPredicate = method => method.ExplicitImplementation is null &&
method.MemberType == memberType;

List<IGrouping<string, Method>> methodGroups = @class.AllMethods().Where(methodPredicate).GroupBy(m => m.Name).ToList();
foreach (IGrouping<string, Method>? methodGroup in methodGroups)
{
Expand All @@ -2016,6 +2016,7 @@ private static void ImplementSetupInterface(StringBuilder sb, Class @class, stri
AppendMethodSetupImplementation(sb, method, mockRegistryName, setupName, true);
}
}

foreach (Method? method in methodGroup)
{
if (method.Parameters.Count == 0)
Expand Down Expand Up @@ -2193,10 +2194,10 @@ private static void AppendMethodSetupImplementation(StringBuilder sb, Method met
sb.AppendLine();
}

private static void AppendIndexerSetupDefinition(StringBuilder sb, Class @class, Property indexer, bool[]? valueFlags = null)
private static void AppendIndexerSetupDefinition(StringBuilder sb, Property indexer, bool[]? valueFlags = null)
{
sb.AppendXmlSummary(
$"Setup for the {indexer.Type.Fullname.EscapeForXmlDoc()} indexer <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.this[{string.Join(", ", indexer.IndexerParameters!.Value.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc()))}]\" />");
$"Setup for the {indexer.Type.Fullname.EscapeForXmlDoc()} indexer <see cref=\"{indexer.ContainingType.EscapeForXmlDoc()}.this[{string.Join(", ", indexer.IndexerParameters!.Value.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc()))}]\" />");
sb.Append("\t\tglobal::Mockolate.Setup.IndexerSetup<").AppendTypeOrWrapper(indexer.Type);
foreach (MethodParameter parameter in indexer.IndexerParameters!)
{
Expand Down Expand Up @@ -2320,10 +2321,10 @@ private static void AppendIndexerSetupImplementation(StringBuilder sb, Property
sb.AppendLine();
}

private static void AppendIndexerVerifyDefinition(StringBuilder sb, Class @class, Property indexer, string verifyName, bool[]? valueFlags = null)
private static void AppendIndexerVerifyDefinition(StringBuilder sb, Property indexer, string verifyName, bool[]? valueFlags = null)
{
sb.AppendXmlSummary(
$"Verify interactions with the {indexer.Type.Fullname.EscapeForXmlDoc()} indexer <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.this[{string.Join(", ", indexer.IndexerParameters!.Value.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc()))}]\" />");
$"Verify interactions with the {indexer.Type.Fullname.EscapeForXmlDoc()} indexer <see cref=\"{indexer.ContainingType.EscapeForXmlDoc()}.this[{string.Join(", ", indexer.IndexerParameters!.Value.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc()))}]\" />.");
sb.Append("\t\tglobal::Mockolate.Verify.VerificationIndexerResult<").Append(verifyName).Append(", ").AppendTypeOrWrapper(indexer.Type).Append("> this[");
int i = 0;
foreach (MethodParameter parameter in indexer.IndexerParameters!.Value)
Expand Down Expand Up @@ -2426,21 +2427,20 @@ private static void DefineRaiseInterface(StringBuilder sb, Class @class, MemberT
@event.MemberType == memberType;
foreach (Event @event in @class.AllEvents().Where(predicate))
{
sb.AppendXmlSummary($"Raise the <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{@event.Name.EscapeForXmlDoc()}\"/> event.");
sb.AppendXmlSummary($"Raise the <see cref=\"{@event.ContainingType.EscapeForXmlDoc()}.{@event.Name.EscapeForXmlDoc()}\"/> event.");
sb.Append("\t\tvoid ").Append(@event.Name).Append("(").Append(FormatParametersWithTypeAndName(@event.Delegate.Parameters)).Append(");").AppendLine();
sb.AppendLine();
}

foreach (string? eventName in @class.AllEvents()
foreach (Event @event in @class.AllEvents()
.Where(predicate)
.GroupBy(m => m.Name)
.Where(g => g.Count() == 1)
.Select(g => g.Single())
.Where(m => m.Delegate.Parameters.Count > 0)
.Select(x => x.Name))
.Where(m => m.Delegate.Parameters.Count > 0))
{
sb.AppendXmlSummary($"Raise the <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{eventName.EscapeForXmlDoc()}\"/> event.");
sb.Append("\t\tvoid ").Append(eventName).Append("(global::Mockolate.Parameters.IDefaultEventParameters parameters);").AppendLine();
sb.AppendXmlSummary($"Raise the <see cref=\"{@event.ContainingType.EscapeForXmlDoc()}.{@event.Name.EscapeForXmlDoc()}\"/> event.");
sb.Append("\t\tvoid ").Append(@event.Name).Append("(global::Mockolate.Parameters.IDefaultEventParameters parameters);").AppendLine();
sb.AppendLine();
}
}
Expand Down Expand Up @@ -2506,7 +2506,7 @@ private static void DefineVerifyInterface(StringBuilder sb, Class @class, string
property.MemberType == memberType;
foreach (Property property in @class.AllProperties().Where(propertyPredicate))
{
sb.AppendXmlSummary($"Verify interactions with the {property.Type.Fullname.EscapeForXmlDoc()} property <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{property.Name.EscapeForXmlDoc()}\" />.");
sb.AppendXmlSummary($"Verify interactions with the {property.Type.Fullname.EscapeForXmlDoc()} property <see cref=\"{property.ContainingType.EscapeForXmlDoc()}.{property.Name.EscapeForXmlDoc()}\" />.");
sb.Append("\t\tglobal::Mockolate.Verify.VerificationPropertyResult<").Append(verifyName).Append(", ").Append(property.Type.Fullname).Append("> ").Append(property.Name).Append(" { get; }").AppendLine();
sb.AppendLine();
}
Expand All @@ -2520,20 +2520,20 @@ private static void DefineVerifyInterface(StringBuilder sb, Class @class, string
indexer.MemberType == memberType;
foreach (Property indexer in @class.AllProperties().Where(indexerPredicate))
{
AppendIndexerVerifyDefinition(sb, @class, indexer, verifyName);
AppendIndexerVerifyDefinition(sb, indexer, verifyName);
if (indexer.IndexerParameters!.Value.Count <= MaxExplicitParameters)
{
foreach (bool[] valueFlags in GenerateValueFlagCombinations(indexer.IndexerParameters.Value))
{
AppendIndexerVerifyDefinition(sb, @class, indexer, verifyName, valueFlags);
AppendIndexerVerifyDefinition(sb, indexer, verifyName, valueFlags);
}
}
else
{
bool[] allValueFlags = indexer.IndexerParameters.Value.Select(p => p.CanBeExplicitValue()).ToArray();
if (allValueFlags.Any(f => f))
{
AppendIndexerVerifyDefinition(sb, @class, indexer, verifyName, allValueFlags);
AppendIndexerVerifyDefinition(sb, indexer, verifyName, allValueFlags);
}
}
}
Expand All @@ -2553,32 +2553,32 @@ private static void DefineVerifyInterface(StringBuilder sb, Class @class, string
Method? method = methodGroup.Single();
if (method.Parameters.Count > 0)
{
AppendMethodVerifyDefinition(sb, @class, method, verifyName, true);
AppendMethodVerifyDefinition(sb, method, verifyName, true);
}
}

foreach (Method? method in methodGroup)
{
if (method.Parameters.Count == 0)
{
AppendMethodVerifyDefinition(sb, @class, method, verifyName, false);
AppendMethodVerifyDefinition(sb, method, verifyName, false);
}
else
{
AppendMethodVerifyDefinition(sb, @class, method, verifyName, false);
AppendMethodVerifyDefinition(sb, method, verifyName, false);
if (method.Parameters.Count <= MaxExplicitParameters)
{
foreach (bool[] valueFlags in GenerateValueFlagCombinations(method.Parameters))
{
AppendMethodVerifyDefinition(sb, @class, method, verifyName, false, valueFlags: valueFlags);
AppendMethodVerifyDefinition(sb, method, verifyName, false, valueFlags: valueFlags);
}
}
else
{
bool[] allValueFlags = method.Parameters.Select(p => p.CanBeExplicitValue()).ToArray();
if (allValueFlags.Any(f => f))
{
AppendMethodVerifyDefinition(sb, @class, method, verifyName, false, valueFlags: allValueFlags);
AppendMethodVerifyDefinition(sb, method, verifyName, false, valueFlags: allValueFlags);
}
}
}
Expand All @@ -2591,23 +2591,23 @@ private static void DefineVerifyInterface(StringBuilder sb, Class @class, string

Func<Event, bool> eventPredicate = @event => @event.ExplicitImplementation is null &&
@event.MemberType == memberType;
foreach (string eventName in @class.AllEvents().Where(eventPredicate).Select(e => e.Name))
foreach (Event @event in @class.AllEvents().Where(eventPredicate))
{
sb.AppendXmlSummary($"Verify subscription on the {eventName} event <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{eventName}\" />.");
sb.Append("\t\tglobal::Mockolate.Verify.VerificationEventResult<").Append(verifyName).Append("> ").Append(eventName).Append(" { get; }").AppendLine();
sb.AppendXmlSummary($"Verify subscriptions on the {@event.Name} event of <see cref=\"{@event.ContainingType.EscapeForXmlDoc()}.{@event.Name}\" />.");
sb.Append("\t\tglobal::Mockolate.Verify.VerificationEventResult<").Append(verifyName).Append("> ").Append(@event.Name).Append(" { get; }").AppendLine();
sb.AppendLine();
}

#endregion
}

private static void AppendMethodVerifyDefinition(StringBuilder sb, Class @class, Method method, string verifyName,
private static void AppendMethodVerifyDefinition(StringBuilder sb, Method method, string verifyName,
bool useParameters, string? methodNameOverride = null, bool[]? valueFlags = null)
{
string methodName = methodNameOverride ?? method.Name;
Comment thread
vbreuss marked this conversation as resolved.
sb.Append("\t\t/// <summary>").AppendLine();
sb.Append("\t\t/// Validates the invocations for the method <see cref=\"")
.Append(@class.ClassFullName.EscapeForXmlDoc())
sb.Append("\t\t/// Verify invocations for the method <see cref=\"")
.Append(method.ContainingType.EscapeForXmlDoc())
.Append(".").Append(methodName.EscapeForXmlDoc()).Append("(");
sb.Append(string.Join(", ",
method.Parameters.Select(p => p.RefKind.GetString() + p.Type.Fullname.EscapeForXmlDoc())));
Expand Down Expand Up @@ -2779,7 +2779,7 @@ private static void ImplementVerifyInterface(StringBuilder sb, Class @class, str
@event.MemberType == memberType;
foreach (Event @event in @class.AllEvents().Where(eventPredicate))
{
sb.AppendXmlSummary($"Verify subscription on the {@event.Name} event <see cref=\"{@class.ClassFullName.EscapeForXmlDoc()}.{@event.Name}\" />.");
sb.AppendXmlSummary($"Verify subscriptions on the {@event.Name} event <see cref=\"{@event.ContainingType.EscapeForXmlDoc()}.{@event.Name}\" />.");
sb.Append("\t\t[global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Never)]").AppendLine();
sb.Append("\t\tglobal::Mockolate.Verify.VerificationEventResult<").Append(verifyName).Append("> ").Append(verifyName).Append('.').Append(@event.Name).AppendLine();
sb.Append("\t\t{").AppendLine();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ public static string MockDelegate(string name, MockClass @class, Method delegate

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

sb.Append("\t\t/// <inheritdoc />").AppendLine();
sb.Append("\t\tstring global::Mockolate.IMock.ToString()").AppendLine();
sb.Append("\t\t\t=> \"").Append(@class.DisplayString).Append(" mock\";").AppendLine();
sb.AppendLine();

AppendMethodSetupImplementation(sb, delegateMethod, mockRegistryName, $"IMockSetupFor{name}", false, "Setup");
if (delegateMethod.Parameters.Count > 0)
{
Expand Down Expand Up @@ -343,25 +343,25 @@ public static string MockDelegate(string name, MockClass @class, Method delegate
sb.AppendXmlSummary($"Verify interactions with the mock of <see cref=\"{escapedClassName}\" />.", "\t");
sb.Append("\tinternal interface IMockVerifyFor").Append(name).Append(" : global::Mockolate.Verify.IMockVerify<").Append(@class.ClassFullName).Append(">").AppendLine();
sb.Append("\t{").AppendLine();
AppendMethodVerifyDefinition(sb, @class, delegateMethod, $"IMockVerifyFor{name}", false, "Verify");
AppendMethodVerifyDefinition(sb, delegateMethod, $"IMockVerifyFor{name}", false, "Verify");
if (delegateMethod.Parameters.Count > 0)
{
AppendMethodVerifyDefinition(sb, @class, delegateMethod, $"IMockVerifyFor{name}", true, "Verify");
AppendMethodVerifyDefinition(sb, delegateMethod, $"IMockVerifyFor{name}", true, "Verify");
}

if (delegateMethod.Parameters.Count is > 0 and <= MaxExplicitParameters)
{
foreach (bool[] valueFlags in GenerateValueFlagCombinations(delegateMethod.Parameters))
{
AppendMethodVerifyDefinition(sb, @class, delegateMethod, $"IMockVerifyFor{name}", false, "Verify", valueFlags);
AppendMethodVerifyDefinition(sb, delegateMethod, $"IMockVerifyFor{name}", false, "Verify", valueFlags);
}
}
else if (delegateMethod.Parameters.Count > MaxExplicitParameters)
{
bool[] allValueFlags = delegateMethod.Parameters.Select(p => p.CanBeExplicitValue()).ToArray();
if (allValueFlags.Any(f => f))
{
AppendMethodVerifyDefinition(sb, @class, delegateMethod, $"IMockVerifyFor{name}", false, "Verify", allValueFlags);
AppendMethodVerifyDefinition(sb, delegateMethod, $"IMockVerifyFor{name}", false, "Verify", allValueFlags);
}
}

Expand Down
41 changes: 41 additions & 0 deletions Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,46 @@ await That(result.Sources).ContainsKey("Mock.MyService.g.cs").WhoseValue
.Contains("return baseResult1;")
.IgnoringNewlineStyle();
}

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

namespace MyCode;

public class Program
{
public static void Main(string[] args)
{
_ = IMyService.CreateMock();
}
}

public interface IMyService : IMyBaseService
{
}
public interface IMyBaseService
{
int BaseProperty { get; }
event EventHandler<int> BaseEvent;
int BaseMethod();
int this[string baseIndexer] { get; }
}
""");

await That(result.Sources).ContainsKey("Mock.IMyService.g.cs").WhoseValue
.Contains("Setup for the int property <see cref=\"global::MyCode.IMyBaseService.BaseProperty\" />.").And
.Contains("Verify interactions with the int property <see cref=\"global::MyCode.IMyBaseService.BaseProperty\" />.").And
.Contains("Setup for the int indexer <see cref=\"global::MyCode.IMyBaseService.this[string]\" />").And
.Contains("Verify interactions with the int indexer <see cref=\"global::MyCode.IMyBaseService.this[string]\" />.").And
.Contains("Setup for the method <see cref=\"global::MyCode.IMyBaseService.BaseMethod()\"/>.").And
.Contains("Verify invocations for the method <see cref=\"global::MyCode.IMyBaseService.BaseMethod()\"/>.").And
.Contains("Raise the <see cref=\"global::MyCode.IMyBaseService.BaseEvent\"/> event.").And
.Contains("Verify subscriptions on the BaseEvent event of <see cref=\"global::MyCode.IMyBaseService.BaseEvent\" />.");
}
}
}