Skip to content

Commit

Permalink
Little Cleanup and Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
justindbaur committed Nov 19, 2023
1 parent 923445a commit 94458e6
Show file tree
Hide file tree
Showing 19 changed files with 303 additions and 38 deletions.
15 changes: 15 additions & 0 deletions pretender.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{DA78B66F-E
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Comparison", "perf\Comparison\Comparison.csproj", "{96C653E3-D10B-47A0-8E42-0B93119AE145}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pretender.Tests", "test\Pretender.Tests\Pretender.Tests.csproj", "{17552274-CC28-438A-80BB-4A161F95AB11}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -95,6 +97,18 @@ Global
{96C653E3-D10B-47A0-8E42-0B93119AE145}.Release|x64.Build.0 = Release|Any CPU
{96C653E3-D10B-47A0-8E42-0B93119AE145}.Release|x86.ActiveCfg = Release|Any CPU
{96C653E3-D10B-47A0-8E42-0B93119AE145}.Release|x86.Build.0 = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|x64.ActiveCfg = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|x64.Build.0 = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|x86.ActiveCfg = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Debug|x86.Build.0 = Debug|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|Any CPU.Build.0 = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|x64.ActiveCfg = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|x64.Build.0 = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|x86.ActiveCfg = Release|Any CPU
{17552274-CC28-438A-80BB-4A161F95AB11}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -104,6 +118,7 @@ Global
{90CC0802-39BB-4390-8A06-C7FD0C14D5C7} = {DFB5E6EE-B017-40FD-BC67-CE0471060A68}
{09EB76D6-C82D-48A9-A0F7-B9BCC10B7621} = {2667A3D7-30CA-4DF5-B2F4-A7554C6D3ADD}
{96C653E3-D10B-47A0-8E42-0B93119AE145} = {DA78B66F-EE75-46B5-8EC8-6F498A8AFC64}
{17552274-CC28-438A-80BB-4A161F95AB11} = {2667A3D7-30CA-4DF5-B2F4-A7554C6D3ADD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FD826CBD-9A31-44A4-B352-E637B9FABD6B}
Expand Down
23 changes: 23 additions & 0 deletions src/Pretender.SourceGenerator/Parser/PretendParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using static Pretender.SourceGenerator.PretenderSourceGenerator;

namespace Pretender.SourceGenerator.Parser
{
internal class PretendParser
{

public PretendParser(PretendInvocation pretendInvocation, CompilationData compilationData)
{
PretendInvocation = pretendInvocation;
}

public PretendInvocation PretendInvocation { get; }

public (object? Emitter, ImmutableArray<Diagnostic>? Diagnostics) GetEmitter(CancellationToken cancellationToken)
{

return (null, null);
}
}
}
74 changes: 74 additions & 0 deletions src/Pretender.SourceGenerator/PretendInvocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Pretender.SourceGenerator.Parser;

namespace Pretender.SourceGenerator
{
internal class PretendInvocation
{
public PretendInvocation(ITypeSymbol pretendType, Location location, bool fillExisting)
{
PretendType = pretendType;
Location = location;
FillExisting = fillExisting;
}

public ITypeSymbol PretendType { get; }
public Location Location { get; }
public bool FillExisting { get; }

public static bool IsCandidateSyntaxNode(SyntaxNode node)
{
// Pretend.That<T>();
if (node is InvocationExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
// TODO: Will this work with a using static Pretender.Pretend
// ...
// That<IInterface>();
Expression: IdentifierNameSyntax { Identifier.ValueText: "Pretend" },
Name: GenericNameSyntax { Identifier.ValueText: "That", TypeArgumentList.Arguments.Count: 1 },
}
})
{
return true;
}

// TODO: Also do Attribute

return false;
}

public static PretendInvocation? Create(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
Debug.Assert(IsCandidateSyntaxNode(context.Node));
var operation = context.SemanticModel.GetOperation(context.Node, cancellationToken);
if (operation is IInvocationOperation invocation)
{
cancellationToken.ThrowIfCancellationRequested();
return CreateFromGeneric(invocation);
}
// TODO: Support attribute

return null;
}

private static PretendInvocation? CreateFromGeneric(IInvocationOperation operation)
{
if (operation.TargetMethod is not IMethodSymbol
{
Name: "That",
ContainingType: INamedTypeSymbol namedTypeSymbol,
TypeArguments.Length: 1,
} || !KnownTypeSymbols.IsPretend(namedTypeSymbol))
{
return null;
}

return new PretendInvocation(operation.TargetMethod.TypeArguments[0], operation.Syntax.GetLocation(), false);
}
}
}
22 changes: 1 addition & 21 deletions src/Pretender.SourceGenerator/PretenderSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
#region Pretend
IncrementalValuesProvider<PretendEntrypoint> pretendsWithDiagnostics =
context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (node, token) =>
{
// Pretend.That<T>();
if (node is InvocationExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
// TODO: Will this work with a using static Pretender.Pretend
// ...
// That<IInterface>();
Expression: IdentifierNameSyntax { Identifier.ValueText: "Pretend" },
Name: GenericNameSyntax { Identifier.ValueText: "That", TypeArgumentList.Arguments.Count: 1 },
},
})
{
return true;
}

// TODO: Allow constructor and shortcut Pretend.Of<T>();
return false;
},
predicate: (node, _) => PretendInvocation.IsCandidateSyntaxNode(node),
transform: static (context, token) =>
{
var operation = context.SemanticModel.GetOperation(context.Node, token);
Expand Down
3 changes: 1 addition & 2 deletions src/Pretender.SourceGenerator/SetupCreationSpec.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down
8 changes: 3 additions & 5 deletions src/Pretender/Behaviors/CallbackBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
namespace Pretender.Behaviors
{
public delegate void Callback(ref CallInfo callInfo);

internal class CallbackBehavior : Behavior
{
private readonly Callback _action;
private readonly Action<CallInfo> _action;

public CallbackBehavior(Callback action)
public CallbackBehavior(Action<CallInfo> action)
{
_action = action;
}

public override void Execute(CallInfo callInfo)
{
_action(ref callInfo);
_action(callInfo);
}
}
}
38 changes: 36 additions & 2 deletions src/Pretender/Called.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Pretender
using System.Diagnostics;

namespace Pretender
{
public readonly struct Called
{
Expand All @@ -15,15 +17,31 @@ private Called(int from, int to, CalledKind calledKind)

enum CalledKind
{
Exact
Exact,
AtLeast,
Range,
}

public static Called Exactly(int expectedCalls)
=> new(expectedCalls, expectedCalls, CalledKind.Exact);

public static Called AtLeastOnce()
=> new(1, int.MaxValue, CalledKind.AtLeast);

public static implicit operator Called(Range range)
{
if (range.Start.IsFromEnd || range.End.IsFromEnd)
{
throw new ArgumentException();
}

return new(range.Start.Value, range.End.Value, CalledKind.Range);
}

public static implicit operator Called(int expectedCalls)
=> new(expectedCalls, expectedCalls, CalledKind.Exact);

[StackTraceHidden]
public void Validate(int callCount)
{
switch (_calledKind)
Expand All @@ -35,10 +53,26 @@ public void Validate(int callCount)
throw new Exception("It was not called exactly that many times.");
}
break;
case CalledKind.AtLeast:
if (callCount < _from)
{
throw new Exception($"It was not called at least {_from} time(s)");
}
break;
case CalledKind.Range:
if (callCount < _from || callCount >= _to)
{
throw new Exception($"It was not between the range {_from}..{_to}");
}
break;
default:
throw new Exception("Invalid call kind.");
}
}

public override string ToString()
{
return $"From = {_from}, To = {_to}, Kind = {_calledKind}";
}
}
}
2 changes: 1 addition & 1 deletion src/Pretender/It.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public static class It
[Matcher<AnyMatcher>]
public static T IsAny<T>()
{
// This method is never normally invoked during its normal usage inside an expression
// This method is never normally invoked during its normal usage
return default!;
}

Expand Down
2 changes: 0 additions & 2 deletions src/Pretender/Matchers/AnyMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
{
public sealed class AnyMatcher : IMatcher
{
public static AnyMatcher Instance = new();

public bool Matches(object? argument)
{
return true;
Expand Down
7 changes: 7 additions & 0 deletions src/Pretender/Matchers/IMatcher.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
namespace Pretender.Matchers
{
/// <summary>
///
/// </summary>
/// <remarks>
/// Matchers don't actually need to implement this interface, matchers are used by duck-typing.
/// So as long as they implement a `Matches` method taking one argument and returning a bool it will be used.
/// </remarks>
public interface IMatcher
{
bool Matches(object? argument);
Expand Down
5 changes: 2 additions & 3 deletions src/Pretender/Pretend.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using Pretender.Internals;

namespace Pretender;

[DebuggerDisplay("{DebuggerToString(),nq}")]
public partial class Pretend<T>
public sealed partial class Pretend<T>
{
// TODO: Should we minimize allocations for rarely called mocks?
private List<CallInfo>? _calls;
Expand Down Expand Up @@ -44,6 +42,7 @@ public void Verify<TReturn>(Func<T, TReturn> verifyExpression, Called called)

[EditorBrowsable(EditorBrowsableState.Never)]
// TODO: Make this obsolete
[StackTraceHidden]
public void Verify(IPretendSetup<T> pretendSetup, Called called)
{
// Right now we can't trust that this setup was created before, loop over all the calls and check it
Expand Down
12 changes: 11 additions & 1 deletion src/Pretender/PretendSetupExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,20 @@ public static Pretend<T> Throws<T, TException>(this IPretendSetup<T> pretendSetu
return pretendSetup.Pretend;
}

public static Pretend<T> Callback<T>(this IPretendSetup<T> pretendSetup, Callback callback)
public static Pretend<T> Callback<T>(this IPretendSetup<T> pretendSetup, Action<CallInfo> callback)
{
pretendSetup.SetBehavior(new CallbackBehavior(callback));
return pretendSetup.Pretend;
}

public static Pretend<T> Does<T, T1>(this IPretendSetup<T> pretendSetup, Action<T1> callback)
{
pretendSetup.SetBehavior(new CallbackBehavior(callInfo =>
{
var firstArg = (T1)callInfo.Arguments[0]!;
callback(firstArg);
}));
return pretendSetup.Pretend;
}
}
}
2 changes: 1 addition & 1 deletion src/Pretender/Pretender.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>0.1.2</VersionPrefix>
<VersionPrefix>0.1.3</VersionPrefix>
<VersionSuffix>prerelease</VersionSuffix>
<Description>A mocking framework that makes use of source generators an interceptors to be fast and give you control.</Description>
<PackageId>Pretender</PackageId>
Expand Down
Loading

0 comments on commit 94458e6

Please sign in to comment.