Add support for indexers
This commit is contained in:
Родитель
f39e72bf43
Коммит
b93154c152
|
@ -105,6 +105,28 @@ var stub = new StubIPhoneBook()
|
||||||
.MyNumber_Set(value => newNumber = value);
|
.MyNumber_Set(value => newNumber = value);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stubbing indexers
|
||||||
|
```csharp
|
||||||
|
var stub = new StubIGenericContainer<int>();
|
||||||
|
|
||||||
|
// stubbing indexer getter
|
||||||
|
stub.Item_Get(index =>
|
||||||
|
{
|
||||||
|
// we're expecting the code under test to get index 5
|
||||||
|
if (index != 5) throw new IndexOutOfRangeException();
|
||||||
|
return 99;
|
||||||
|
});
|
||||||
|
|
||||||
|
// stubbing indexer setter
|
||||||
|
int res = -1;
|
||||||
|
stub.Item_Set((index, value) =>
|
||||||
|
{
|
||||||
|
// we're expecting the code under test to only set index 7
|
||||||
|
if (index != 7) throw new IndexOutOfRangeException();
|
||||||
|
res = value;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Stubbing events
|
## Stubbing events
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package >
|
<package >
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Etg.SimpleStubs</id>
|
<id>Etg.SimpleStubs</id>
|
||||||
<version>2.2.0</version>
|
<version>2.3.0</version>
|
||||||
<title>SimpleStubs mocking framework</title>
|
<title>SimpleStubs mocking framework</title>
|
||||||
<authors>Microsoft Studios (BigPark)</authors>
|
<authors>Microsoft Studios (BigPark)</authors>
|
||||||
<owners>Microsoft Studios (BigPark)</owners>
|
<owners>Microsoft Studios (BigPark)</owners>
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
namespace Etg.SimpleStubs.CodeGen
|
||||||
|
{
|
||||||
|
internal interface IPropertyStubber
|
||||||
|
{
|
||||||
|
ClassDeclarationSyntax StubProperty(ClassDeclarationSyntax classDclr, IPropertySymbol propertySymbol,
|
||||||
|
INamedTypeSymbol stubbedInterface);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,11 @@ namespace Etg.SimpleStubs.CodeGen
|
||||||
internal class InterfaceStubber : IInterfaceStubber
|
internal class InterfaceStubber : IInterfaceStubber
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<IMethodStubber> _methodStubbers;
|
private readonly IEnumerable<IMethodStubber> _methodStubbers;
|
||||||
|
private readonly IEnumerable<IPropertyStubber> _propertyStubbers;
|
||||||
|
|
||||||
public InterfaceStubber(IEnumerable<IMethodStubber> methodStubbers)
|
public InterfaceStubber(IEnumerable<IMethodStubber> methodStubbers, IEnumerable<IPropertyStubber> propertyStubbers)
|
||||||
{
|
{
|
||||||
|
_propertyStubbers = propertyStubbers;
|
||||||
_methodStubbers = new List<IMethodStubber>(methodStubbers);
|
_methodStubbers = new List<IMethodStubber>(methodStubbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +34,7 @@ namespace Etg.SimpleStubs.CodeGen
|
||||||
|
|
||||||
classDclr = RoslynUtils.CopyGenericConstraints(interfaceType, classDclr);
|
classDclr = RoslynUtils.CopyGenericConstraints(interfaceType, classDclr);
|
||||||
classDclr = AddStubContainerField(classDclr, stubName);
|
classDclr = AddStubContainerField(classDclr, stubName);
|
||||||
|
classDclr = StubProperties(interfaceType, classDclr);
|
||||||
classDclr = StubMethods(interfaceType, classDclr);
|
classDclr = StubMethods(interfaceType, classDclr);
|
||||||
|
|
||||||
string fullNameSpace = semanticModel.GetDeclaredSymbol(namespaceNode).ToString();
|
string fullNameSpace = semanticModel.GetDeclaredSymbol(namespaceNode).ToString();
|
||||||
|
@ -42,9 +45,22 @@ namespace Etg.SimpleStubs.CodeGen
|
||||||
return cu;
|
return cu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClassDeclarationSyntax StubProperties(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr)
|
||||||
|
{
|
||||||
|
IEnumerable<IPropertySymbol> propertiesToStub = RoslynUtils.GetAllMembers<IPropertySymbol>(interfaceType);
|
||||||
|
foreach (IPropertySymbol propertySymbol in propertiesToStub)
|
||||||
|
{
|
||||||
|
foreach (IPropertyStubber propertyStubber in _propertyStubbers)
|
||||||
|
{
|
||||||
|
classDclr = propertyStubber.StubProperty(classDclr, propertySymbol, interfaceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classDclr;
|
||||||
|
}
|
||||||
|
|
||||||
private ClassDeclarationSyntax StubMethods(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr)
|
private ClassDeclarationSyntax StubMethods(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr)
|
||||||
{
|
{
|
||||||
List<IMethodSymbol> methodsToStub = RoslynUtils.GetAllMethods(interfaceType);
|
IEnumerable<IMethodSymbol> methodsToStub = RoslynUtils.GetAllMembers<IMethodSymbol>(interfaceType);
|
||||||
foreach (IMethodSymbol methodSymbol in methodsToStub)
|
foreach (IMethodSymbol methodSymbol in methodsToStub)
|
||||||
{
|
{
|
||||||
foreach (IMethodStubber methodStubber in _methodStubbers)
|
foreach (IMethodStubber methodStubber in _methodStubbers)
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace Etg.SimpleStubs.CodeGen
|
||||||
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
||||||
|
|
||||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
||||||
string parameters = FormatParameters(methodSymbol);
|
string parameters = StubbingUtils.FormatParameters(methodSymbol);
|
||||||
|
|
||||||
string callDelegateStmt = StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, methodSymbol.GetGenericName(), parameters);
|
string callDelegateStmt = StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, methodSymbol.GetGenericName(), parameters);
|
||||||
if (!methodSymbol.ReturnsVoid)
|
if (!methodSymbol.ReturnsVoid)
|
||||||
|
@ -40,21 +40,5 @@ namespace Etg.SimpleStubs.CodeGen
|
||||||
|
|
||||||
return classDclr;
|
return classDclr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatParameters(IMethodSymbol methodSymbol)
|
|
||||||
{
|
|
||||||
return string.Join(", ", methodSymbol.Parameters.Select(p =>
|
|
||||||
{
|
|
||||||
if (p.RefKind == RefKind.Out)
|
|
||||||
{
|
|
||||||
return $"out {p.Name}";
|
|
||||||
}
|
|
||||||
if (p.RefKind == RefKind.Ref)
|
|
||||||
{
|
|
||||||
return $"ref {p.Name}";
|
|
||||||
}
|
|
||||||
return p.Name;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,79 +1,75 @@
|
||||||
using System;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using Etg.SimpleStubs.CodeGen.Utils;
|
using Etg.SimpleStubs.CodeGen.Utils;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
namespace Etg.SimpleStubs.CodeGen
|
namespace Etg.SimpleStubs.CodeGen
|
||||||
{
|
{
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using SF = SyntaxFactory;
|
||||||
using System.Linq;
|
|
||||||
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|
||||||
|
|
||||||
internal class PropertyStubber : IMethodStubber
|
internal class PropertyStubber : IPropertyStubber
|
||||||
{
|
{
|
||||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
public ClassDeclarationSyntax StubProperty(ClassDeclarationSyntax classDclr, IPropertySymbol propertySymbol,
|
||||||
INamedTypeSymbol stubbedInterface)
|
INamedTypeSymbol stubbedInterface)
|
||||||
{
|
{
|
||||||
if (!methodSymbol.IsPropertyAccessor())
|
string indexerType = propertySymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||||
{
|
BasePropertyDeclarationSyntax propDclr = null;
|
||||||
return classDclr;
|
|
||||||
}
|
|
||||||
|
|
||||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
if (propertySymbol.GetMethod != null)
|
||||||
|
|
||||||
string propName = methodSymbol.AssociatedSymbol.Name;
|
|
||||||
string propType =
|
|
||||||
((IPropertySymbol) methodSymbol.AssociatedSymbol).Type.ToDisplayString(
|
|
||||||
SymbolDisplayFormat.FullyQualifiedFormat);
|
|
||||||
var propDclr = GetPropDclr(classDclr, propName);
|
|
||||||
if (propDclr == null)
|
|
||||||
{
|
{
|
||||||
propDclr = SF.PropertyDeclaration(SF.ParseTypeName(propType), SF.Identifier(propName))
|
IMethodSymbol getMethodSymbol = propertySymbol.GetMethod;
|
||||||
.WithExplicitInterfaceSpecifier(SF.ExplicitInterfaceSpecifier(
|
string parameters = StubbingUtils.FormatParameters(getMethodSymbol);
|
||||||
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodSymbol.IsPropertyGetter())
|
string delegateTypeName = NamingUtils.GetDelegateTypeName(getMethodSymbol, stubbedInterface);
|
||||||
{
|
|
||||||
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, SF.Block(
|
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, SF.Block(
|
||||||
SF.List(new[]
|
SF.List(new[]
|
||||||
{
|
{
|
||||||
SF.ParseStatement("return " + StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, methodSymbol.Name, ""))
|
SF.ParseStatement("return " + StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, getMethodSymbol.Name, parameters))
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
propDclr = CreatePropertyDclr(getMethodSymbol, indexerType);
|
||||||
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (methodSymbol.IsPropertySetter())
|
if (propertySymbol.SetMethod != null)
|
||||||
{
|
{
|
||||||
|
IMethodSymbol setMethodSymbol = propertySymbol.SetMethod;
|
||||||
|
string parameters = $"{StubbingUtils.FormatParameters(setMethodSymbol)}";
|
||||||
|
string delegateTypeName = NamingUtils.GetDelegateTypeName(setMethodSymbol, stubbedInterface);
|
||||||
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, SF.Block(
|
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, SF.Block(
|
||||||
SF.List(new[]
|
SF.List(new[]
|
||||||
{
|
{
|
||||||
SF.ParseStatement(StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, methodSymbol.Name, "value"))
|
SF.ParseStatement(StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, setMethodSymbol.Name, parameters))
|
||||||
})));
|
})));
|
||||||
|
if (propDclr == null)
|
||||||
|
{
|
||||||
|
propDclr = CreatePropertyDclr(setMethodSymbol, indexerType);
|
||||||
|
}
|
||||||
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propDclr != null)
|
classDclr = classDclr.AddMembers(propDclr);
|
||||||
{
|
|
||||||
PropertyDeclarationSyntax existingPropDclr = GetPropDclr(classDclr, propName);
|
|
||||||
if (existingPropDclr != null)
|
|
||||||
{
|
|
||||||
classDclr = classDclr.ReplaceNode(existingPropDclr, propDclr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
classDclr = classDclr.AddMembers(propDclr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return classDclr;
|
return classDclr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PropertyDeclarationSyntax GetPropDclr(ClassDeclarationSyntax classDclr, string propName)
|
private BasePropertyDeclarationSyntax CreatePropertyDclr(IMethodSymbol methodSymbol, string propType)
|
||||||
{
|
{
|
||||||
return
|
if (methodSymbol.IsIndexerAccessor())
|
||||||
classDclr.DescendantNodes()
|
{
|
||||||
.OfType<PropertyDeclarationSyntax>()
|
IndexerDeclarationSyntax indexerDclr = SF.IndexerDeclaration(
|
||||||
.FirstOrDefault(p => p.Identifier.Text == propName);
|
SF.ParseTypeName(propType))
|
||||||
|
.WithExplicitInterfaceSpecifier(SF.ExplicitInterfaceSpecifier(
|
||||||
|
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
||||||
|
indexerDclr = indexerDclr.AddParameterListParameters(
|
||||||
|
RoslynUtils.GetMethodParameterSyntaxList(methodSymbol).ToArray());
|
||||||
|
return indexerDclr;
|
||||||
|
}
|
||||||
|
|
||||||
|
string propName = methodSymbol.AssociatedSymbol.Name;
|
||||||
|
PropertyDeclarationSyntax propDclr = SF.PropertyDeclaration(SF.ParseTypeName(propType), SF.Identifier(propName))
|
||||||
|
.WithExplicitInterfaceSpecifier(SF.ExplicitInterfaceSpecifier(
|
||||||
|
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
||||||
|
return propDclr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,8 +30,11 @@ namespace Etg.SimpleStubs.CodeGen.DI
|
||||||
{
|
{
|
||||||
new OrdinaryMethodStubber(),
|
new OrdinaryMethodStubber(),
|
||||||
new EventStubber(),
|
new EventStubber(),
|
||||||
new PropertyStubber(),
|
|
||||||
new StubbingDelegateGenerator()
|
new StubbingDelegateGenerator()
|
||||||
|
},
|
||||||
|
new IPropertyStubber[]
|
||||||
|
{
|
||||||
|
new PropertyStubber()
|
||||||
});
|
});
|
||||||
return interfaceStubber;
|
return interfaceStubber;
|
||||||
}).As<IInterfaceStubber>().SingleInstance();
|
}).As<IInterfaceStubber>().SingleInstance();
|
||||||
|
|
|
@ -102,6 +102,7 @@
|
||||||
<Compile Include="CodeGen\IInterfaceStubber.cs" />
|
<Compile Include="CodeGen\IInterfaceStubber.cs" />
|
||||||
<Compile Include="CodeGen\InterfaceStubber.cs" />
|
<Compile Include="CodeGen\InterfaceStubber.cs" />
|
||||||
<Compile Include="CodeGen\IProjectStubber.cs" />
|
<Compile Include="CodeGen\IProjectStubber.cs" />
|
||||||
|
<Compile Include="CodeGen\IPropertyStubber.cs" />
|
||||||
<Compile Include="CodeGen\ProjectStubber.cs" />
|
<Compile Include="CodeGen\ProjectStubber.cs" />
|
||||||
<Compile Include="Utils\StubbingUtils.cs" />
|
<Compile Include="Utils\StubbingUtils.cs" />
|
||||||
<Compile Include="CodeGen\StubProjectResult.cs" />
|
<Compile Include="CodeGen\StubProjectResult.cs" />
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
methodName = SerializeName(methodSymbol.ContainingSymbol) + "_" + methodName;
|
methodName = SerializeName(methodSymbol.ContainingSymbol) + "_" + methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methodSymbol.IsOrdinaryMethod())
|
if (methodSymbol.IsOrdinaryMethod() || methodSymbol.IsIndexerAccessor())
|
||||||
{
|
{
|
||||||
if (methodSymbol.Parameters.Any())
|
if (methodSymbol.Parameters.Any())
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,22 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
return methodSymbol.MethodKind == MethodKind.PropertyGet;
|
return methodSymbol.MethodKind == MethodKind.PropertyGet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsIndexerGetter(this IMethodSymbol methodSymbol)
|
||||||
|
{
|
||||||
|
return methodSymbol.Name == "get_Item";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIndexerSetter(this IMethodSymbol methodSymbol)
|
||||||
|
{
|
||||||
|
return methodSymbol.Name == "set_Item";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIndexerAccessor(this IMethodSymbol methodSymbol)
|
||||||
|
{
|
||||||
|
IPropertySymbol propertySymbol = methodSymbol.AssociatedSymbol as IPropertySymbol;
|
||||||
|
return propertySymbol != null && propertySymbol.IsIndexer;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetGenericName(this IMethodSymbol methodSymbol)
|
public static string GetGenericName(this IMethodSymbol methodSymbol)
|
||||||
{
|
{
|
||||||
string name = methodSymbol.Name;
|
string name = methodSymbol.Name;
|
||||||
|
@ -52,7 +68,7 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetContainingInterfaceGenericQualifiedName(this IMethodSymbol methodSymbol)
|
public static string GetContainingInterfaceGenericQualifiedName(this ISymbol methodSymbol)
|
||||||
{
|
{
|
||||||
return methodSymbol.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
return methodSymbol.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||||
}
|
}
|
||||||
|
@ -102,5 +118,21 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
};
|
};
|
||||||
return !typeDclr.Modifiers.Any(modifier => nonInternalModifiers.Contains(modifier.RawKind));
|
return !typeDclr.Modifiers.Any(modifier => nonInternalModifiers.Contains(modifier.RawKind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BasePropertyDeclarationSyntax AddAccessorListAccessors(this BasePropertyDeclarationSyntax baseDclr, params AccessorDeclarationSyntax[] accessors)
|
||||||
|
{
|
||||||
|
var propDclr = baseDclr as PropertyDeclarationSyntax;
|
||||||
|
if (propDclr != null)
|
||||||
|
{
|
||||||
|
return propDclr.AddAccessorListAccessors(accessors);
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexerDclr = baseDclr as IndexerDeclarationSyntax;
|
||||||
|
if (indexerDclr != null)
|
||||||
|
{
|
||||||
|
return indexerDclr.AddAccessorListAccessors(accessors);
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -55,21 +55,21 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
return paramsSyntaxList;
|
return paramsSyntaxList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IMethodSymbol> GetAllMethods(INamedTypeSymbol interfaceType)
|
public static List<T> GetAllMembers<T>(INamedTypeSymbol interfaceType)
|
||||||
{
|
{
|
||||||
var methodsToStub = new List<IMethodSymbol>(interfaceType.GetMembers().OfType<IMethodSymbol>());
|
var methodsToStub = new List<T>(interfaceType.GetMembers().OfType<T>());
|
||||||
methodsToStub.AddRange(GetAllInheritedMethods(interfaceType));
|
methodsToStub.AddRange(GetAllInheritedMethods<T>(interfaceType));
|
||||||
return methodsToStub;
|
return methodsToStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<IMethodSymbol> GetAllInheritedMethods(ITypeSymbol typeSymbol)
|
public static IEnumerable<T> GetAllInheritedMethods<T>(ITypeSymbol typeSymbol)
|
||||||
{
|
{
|
||||||
var methods = new List<IMethodSymbol>();
|
var methods = new List<T>();
|
||||||
if (typeSymbol.AllInterfaces.Any())
|
if (typeSymbol.AllInterfaces.Any())
|
||||||
{
|
{
|
||||||
foreach (var baseInterfaceType in typeSymbol.AllInterfaces)
|
foreach (var baseInterfaceType in typeSymbol.AllInterfaces)
|
||||||
{
|
{
|
||||||
methods.AddRange(baseInterfaceType.GetMembers().OfType<IMethodSymbol>());
|
methods.AddRange(baseInterfaceType.GetMembers().OfType<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,21 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
||||||
{
|
{
|
||||||
return $"_stubs.GetMethodStub<{delegateTypeName}>(\"{methodName}\").Invoke({parameters});\n";
|
return $"_stubs.GetMethodStub<{delegateTypeName}>(\"{methodName}\").Invoke({parameters});\n";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static string FormatParameters(IMethodSymbol methodSymbol)
|
||||||
|
{
|
||||||
|
return string.Join(", ", methodSymbol.Parameters.Select(p =>
|
||||||
|
{
|
||||||
|
if (p.RefKind == RefKind.Out)
|
||||||
|
{
|
||||||
|
return $"out {p.Name}";
|
||||||
|
}
|
||||||
|
if (p.RefKind == RefKind.Ref)
|
||||||
|
{
|
||||||
|
return $"ref {p.Name}";
|
||||||
|
}
|
||||||
|
return p.Name;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace TestClassLibrary
|
namespace TestClassLibrary
|
||||||
|
@ -20,12 +21,18 @@ namespace TestClassLibrary
|
||||||
event EventHandler<long> PhoneNumberChanged;
|
event EventHandler<long> PhoneNumberChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// just to make sure all inherited members are stubbed
|
||||||
|
public interface IPhoneBookSpecial : IPhoneBook
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public interface IContainer
|
public interface IContainer
|
||||||
{
|
{
|
||||||
T GetElement<T>(int index);
|
T GetElement<T>(int index);
|
||||||
void SetElement<T>(int index, T value);
|
void SetElement<T>(int index, T value);
|
||||||
|
|
||||||
bool GetElement(int index, out object value);
|
bool GetElement(int index, out object value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRefUtils
|
public interface IRefUtils
|
||||||
|
@ -74,6 +81,18 @@ namespace TestClassLibrary
|
||||||
T GetX();
|
T GetX();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IGenericContainer<T>
|
||||||
|
{
|
||||||
|
T this[int index] { get; set; }
|
||||||
|
|
||||||
|
T this[string key, int n] { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// just to make sure inherited indexers are stubbed
|
||||||
|
public interface IGenericContainerSubInterface : IGenericContainer<int>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public interface IInterfaceWithGenericMethod
|
public interface IInterfaceWithGenericMethod
|
||||||
{
|
{
|
||||||
T GetFoo<T>() where T : class;
|
T GetFoo<T>() where T : class;
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace TestClassLibraryTest
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(SimpleStubsException))]
|
[ExpectedException(typeof(SimpleStubsException))]
|
||||||
|
|
||||||
public void TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected()
|
public void TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected()
|
||||||
{
|
{
|
||||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678, Times.Once);
|
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678, Times.Once);
|
||||||
|
@ -115,7 +115,7 @@ namespace TestClassLibraryTest
|
||||||
public void TestThatMethodStubCanBeOverwritten()
|
public void TestThatMethodStubCanBeOverwritten()
|
||||||
{
|
{
|
||||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678);
|
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678);
|
||||||
stub.GetContactPhoneNumber((p1, p2) => 11122233, overwrite:true);
|
stub.GetContactPhoneNumber((p1, p2) => 11122233, overwrite: true);
|
||||||
|
|
||||||
IPhoneBook phoneBook = stub;
|
IPhoneBook phoneBook = stub;
|
||||||
Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith"));
|
Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith"));
|
||||||
|
@ -165,12 +165,63 @@ namespace TestClassLibraryTest
|
||||||
int i1 = 1;
|
int i1 = 1;
|
||||||
int i2 = 2;
|
int i2 = 2;
|
||||||
|
|
||||||
((IRefUtils) stub).Swap<int>(ref i1, ref i2);
|
((IRefUtils)stub).Swap<int>(ref i1, ref i2);
|
||||||
Assert.AreEqual(2, i1);
|
Assert.AreEqual(2, i1);
|
||||||
Assert.AreEqual(1, i2);
|
Assert.AreEqual(1, i2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestIndexerGet()
|
||||||
|
{
|
||||||
|
var stub = new StubIGenericContainer<int>();
|
||||||
|
stub.Item_Get(index =>
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return 13;
|
||||||
|
case 1:
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericContainer<int> container = stub;
|
||||||
|
Assert.AreEqual(13, container[0]);
|
||||||
|
Assert.AreEqual(5, container[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestIndexerSet()
|
||||||
|
{
|
||||||
|
var stub = new StubIGenericContainer<int>();
|
||||||
|
int res = -1;
|
||||||
|
stub.Item_Set((index, value) =>
|
||||||
|
{
|
||||||
|
if (index != 0) throw new IndexOutOfRangeException();
|
||||||
|
res = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericContainer<int> container = stub;
|
||||||
|
container[0] = 13;
|
||||||
|
|
||||||
|
Assert.AreEqual(13, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestThatMultipleIndexerDontConflict()
|
||||||
|
{
|
||||||
|
var stub = new StubIGenericContainer<int>();
|
||||||
|
stub.Item_Get(index => 12).Item_Get((key, i) => 3);
|
||||||
|
|
||||||
|
IGenericContainer<int> container = stub;
|
||||||
|
Assert.AreEqual(12, container[0]);
|
||||||
|
Assert.AreEqual(3, container["foo", 0]);
|
||||||
|
}
|
||||||
|
|
||||||
// this test is only used for debugging
|
// this test is only used for debugging
|
||||||
|
[Ignore]
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestGenerateStubs()
|
public async Task TestGenerateStubs()
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче