Support generic methods and ref/out parameters
This commit also involves a major change in SimpleStubs Api. Properties that were used for stubbing have been replaced with setup methods that have the same name as the methods being stubbed. The new Api is fluent, simple and more readeable (no more _int_string naming style).
This commit is contained in:
Родитель
808afa7267
Коммит
8de7730ed6
|
@ -29,28 +29,19 @@ public interface IPhoneBook
|
|||
|
||||
### Stubbing methods
|
||||
```csharp
|
||||
var stub = new StubIPhoneBook
|
||||
{
|
||||
GetContactPhoneNumber_String_String = (fn, ln) =>
|
||||
{
|
||||
return 6041234567;
|
||||
}
|
||||
};
|
||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((firstName, lastName) => 6041234567);
|
||||
```
|
||||
|
||||
You can also copy and verify the parameters values:
|
||||
```csharp
|
||||
string firstName = null;
|
||||
string lastName = null;
|
||||
var stub = new StubIPhoneBook
|
||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((fn, ln) =>
|
||||
{
|
||||
GetContactPhoneNumber_String_String = (fn, ln) =>
|
||||
{
|
||||
firstName = fn;
|
||||
lastName = ln;
|
||||
return 6041234567;
|
||||
}
|
||||
};
|
||||
firstName = fn;
|
||||
lastName = ln;
|
||||
return number;
|
||||
});
|
||||
|
||||
ClassUnderTest obj = new ClassUnderTest(stub);
|
||||
|
||||
|
@ -60,18 +51,43 @@ Assert.AreEqual("John", firstName);
|
|||
Assert.AreEqual("Smith", lastName);
|
||||
```
|
||||
|
||||
### Out parameters
|
||||
```csharp
|
||||
object someObj = new Foo();
|
||||
var stub = new StubIContainer()
|
||||
.GetElement((int index, out object value) =>
|
||||
{
|
||||
value = someObj;
|
||||
return true;
|
||||
});
|
||||
```
|
||||
|
||||
### Ref parameters
|
||||
```csharp
|
||||
var stub = new StubIRefUtils()
|
||||
.Swap<int>((ref int v1, ref int v2) =>
|
||||
{
|
||||
int temp = v1;
|
||||
v1 = v2;
|
||||
v2 = temp;
|
||||
});
|
||||
```
|
||||
|
||||
### Generic methods
|
||||
```csharp
|
||||
int value = -1;
|
||||
var stub = new StubIContainer()
|
||||
.GetElement<int>(index => value)
|
||||
.SetElement<int>((i, v) => { value = v; });
|
||||
```
|
||||
|
||||
## Stubbing properties
|
||||
|
||||
```csharp
|
||||
long myNumber = 6041234567;
|
||||
var stub = new StubIPhoneBook
|
||||
{
|
||||
MyNumber_Get = () => myNumber,
|
||||
MyNumber_Set = num =>
|
||||
{
|
||||
myNumber = num;
|
||||
}
|
||||
};
|
||||
var stub = new StubIPhoneBook()
|
||||
.MyNumber_Get(() => myNumber)
|
||||
.MyNumber_Set(value => newNumber = value);
|
||||
```
|
||||
|
||||
## Stubbing events
|
||||
|
@ -99,139 +115,12 @@ var sequence = StubsUtils.Sequence<Func<string, string, int>>()
|
|||
.Repeat((p1, p2) => 11122233, 3) // next three calls will return 11122233
|
||||
.Forever((p1, p2) => 22233556); // any subsequent call will return 22233556
|
||||
|
||||
var stub = new StubIPhoneBook
|
||||
{
|
||||
// Get the next element from the sequence every time the method is called and invoke it
|
||||
GetContactPhoneNumber_String_String = (p1, p2) => sequence.Next(p1, p2)
|
||||
};
|
||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => sequence.Next(p1, p2));
|
||||
|
||||
// you can also verify how many times the sequence was called
|
||||
Assert.AreEqual(5, sequence.CallCount);
|
||||
```
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
Let's look at how we can unit test the following class (`LocationManager`) using SimpleStubs.
|
||||
|
||||
```csharp
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HelloApp
|
||||
{
|
||||
public class LocationManager
|
||||
{
|
||||
private readonly ILocationService _locationService;
|
||||
public LocationManager(ILocationService locationService)
|
||||
{
|
||||
_locationService = locationService;
|
||||
}
|
||||
|
||||
/// <returns>Current Location or null if the location could not be retrieved</returns>
|
||||
public async Task<Location> GetCurrentLocation()
|
||||
{
|
||||
try
|
||||
{
|
||||
string location = await _locationService.GetLocation();
|
||||
var ss = location.Split('/');
|
||||
return new Location(ss[0], ss[1]);
|
||||
}
|
||||
catch (LocationServiceUnavailableException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <returns>The current country code (e.g. US, CA) or null if the country code could not be retrieved</returns>
|
||||
public async Task<string> GetCurrentCountryCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
Location location = await GetCurrentLocation();
|
||||
string loc = $"{location.Country}/{location.City}";
|
||||
return await _locationService.GetCountryCode(loc);
|
||||
}
|
||||
catch (LocationServiceUnavailableException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ILocationService` interface is as follows:
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HelloApp
|
||||
{
|
||||
public interface ILocationService
|
||||
{
|
||||
/// <returns>
|
||||
/// the location in the format Country/City
|
||||
/// </returns>
|
||||
/// <exception cref="LocationServiceUnavailableException"></exception>
|
||||
Task<string> GetLocation();
|
||||
|
||||
/// <returns>the country code of the given location</returns>
|
||||
/// <exception cref="LocationServiceUnavailableException"></exception>
|
||||
Task<string> GetCountryCode(string location);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
SimpleStubs will automatically generate a stub for `ILocationService` called StubILocationService. The following tests show how the stub can be used to unit test the `LocationManager` class:
|
||||
|
||||
```csharp
|
||||
[TestMethod]
|
||||
public async Task TestGetCurrentLocation()
|
||||
{
|
||||
StubILocationService locationServiceStub = new StubILocationService
|
||||
{
|
||||
GetLocation = () => Task.FromResult("Canada/Vancouver")
|
||||
};
|
||||
|
||||
LocationManager locationManager = new LocationManager(locationServiceStub);
|
||||
Location location = await locationManager.GetCurrentLocation();
|
||||
|
||||
Assert.AreEqual("Canada", location.Country);
|
||||
Assert.AreEqual("Vancouver", location.City);
|
||||
Assert.AreEqual(1, locationServiceStub.ILocationService_GetLocation_CallCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestThatGetCurrentLocationReturnsNullIfLocationServiceIsUnavailable()
|
||||
{
|
||||
StubILocationService locationServiceStub = new StubILocationService
|
||||
{
|
||||
GetLocation = () =>
|
||||
{
|
||||
throw new LocationServiceUnavailableException();
|
||||
}
|
||||
};
|
||||
|
||||
LocationManager locationManager = new LocationManager(locationServiceStub);
|
||||
Assert.IsNull(await locationManager.GetCurrentLocation());
|
||||
Assert.AreEqual(1, locationServiceStub.ILocationService_GetLocation_CallCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGetCurrentCountryCode()
|
||||
{
|
||||
StubILocationService locationServiceStub = new StubILocationService
|
||||
{
|
||||
GetLocation = () => Task.FromResult("Canada/Vancouver"),
|
||||
GetCountryCode_String = location => Task.FromResult("CA")
|
||||
};
|
||||
|
||||
LocationManager locationManager = new LocationManager(locationServiceStub);
|
||||
Assert.AreEqual("CA", await locationManager.GetCurrentCountryCode());
|
||||
Assert.AreEqual(1, locationServiceStub.ILocationService_GetCountryCode_String_CallCount);
|
||||
}
|
||||
```
|
||||
## Configuration
|
||||
|
||||
SimpleStubs also supports an optional configuration file that can be added to the root of your test project. The configuration file (named `SimpleStubs.json`) has the following structure:
|
||||
|
@ -256,11 +145,9 @@ The configuration file allows you to instruct SimpleStubs to omit creating stubs
|
|||
It's also possible to instruct SimpleStubs to create stubs for internal interfaces (by default only public interfaces are stubbed) as shown in the configuration sample above.
|
||||
|
||||
## Current limitations
|
||||
* Methods signatures with pointers are not supported.
|
||||
* Generic methods are not supported (but generic interfaces are).
|
||||
* Only interfaces are stubbed.
|
||||
* Generic constrains are not supported.
|
||||
|
||||
## What if some stubs don't compile?
|
||||
|
||||
Exclude the interface that is causing the problem (using the `SimpleStubs.json` configuration file) and report the problem.
|
||||
|
||||
Exclude the interface that is causing the problem (using the `SimpleStubs.json` configuration file) and report the problem by opening an issue.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package >
|
||||
<metadata>
|
||||
<id>Etg.SimpleStubs</id>
|
||||
<version>1.0.3</version>
|
||||
<version>2.0.0</version>
|
||||
<title>SimpleStubs mocking framework</title>
|
||||
<authors>Microsoft Studios (BigPark)</authors>
|
||||
<owners>Microsoft Studios (BigPark)</owners>
|
||||
|
|
|
@ -9,90 +9,94 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
{
|
||||
using SF = SyntaxFactory;
|
||||
|
||||
class EventStubber : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
// only handle EventAdd and ignore EventRemove because we only need to stub the event once
|
||||
if (!methodSymbol.IsEventAdd())
|
||||
{
|
||||
return classDclr;
|
||||
}
|
||||
internal class EventStubber : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
// only handle EventAdd and ignore EventRemove because we only need to stub the event once
|
||||
if (!methodSymbol.IsEventAdd())
|
||||
{
|
||||
return classDclr;
|
||||
}
|
||||
|
||||
// add the event implementation to the stub
|
||||
IEventSymbol eventSymbol = (IEventSymbol)methodSymbol.AssociatedSymbol;
|
||||
EventFieldDeclarationSyntax eventDclr = ToEventDclr(eventSymbol);
|
||||
classDclr = classDclr.AddMembers(eventDclr);
|
||||
// add the event implementation to the stub
|
||||
IEventSymbol eventSymbol = (IEventSymbol) methodSymbol.AssociatedSymbol;
|
||||
EventFieldDeclarationSyntax eventDclr = ToEventDclr(eventSymbol);
|
||||
classDclr = classDclr.AddMembers(eventDclr);
|
||||
|
||||
string eventName = eventSymbol.Name;
|
||||
ParameterSyntax[] parameters = GetEventParameters(eventSymbol);
|
||||
string onEventArgs = "sender";
|
||||
string eventTriggerArgs = "sender";
|
||||
if (parameters.Count() == 2)
|
||||
{
|
||||
onEventArgs += ", args";
|
||||
eventTriggerArgs += ", args";
|
||||
}
|
||||
else if (parameters.Count() == 1)
|
||||
{
|
||||
onEventArgs += ", null";
|
||||
}
|
||||
string eventName = eventSymbol.Name;
|
||||
ParameterSyntax[] parameters = GetEventParameters(eventSymbol);
|
||||
string onEventArgs = "sender";
|
||||
string eventTriggerArgs = "sender";
|
||||
if (parameters.Count() == 2)
|
||||
{
|
||||
onEventArgs += ", args";
|
||||
eventTriggerArgs += ", args";
|
||||
}
|
||||
else if (parameters.Count() == 1)
|
||||
{
|
||||
onEventArgs += ", null";
|
||||
}
|
||||
|
||||
string eventType = GetEventType(eventSymbol);
|
||||
string onEventMethodName = "On_" + eventName;
|
||||
string eventType = GetEventType(eventSymbol);
|
||||
string onEventMethodName = "On_" + eventName;
|
||||
|
||||
// Create OnEvent method
|
||||
MethodDeclarationSyntax onEventMethodDclr = SF.MethodDeclaration(SF.ParseTypeName("void"), onEventMethodName)
|
||||
.AddModifiers(SF.Token(SyntaxKind.ProtectedKeyword))
|
||||
.AddParameterListParameters(parameters)
|
||||
.WithBody(SF.Block(
|
||||
SF.ParseStatement($"{eventType} handler = {eventName};\n"),
|
||||
SF.ParseStatement($"if (handler != null) {{ handler({onEventArgs}); }}\n")
|
||||
));
|
||||
// Create OnEvent method
|
||||
MethodDeclarationSyntax onEventMethodDclr = SF.MethodDeclaration(SF.ParseTypeName("void"), onEventMethodName)
|
||||
.AddModifiers(SF.Token(SyntaxKind.ProtectedKeyword))
|
||||
.AddParameterListParameters(parameters)
|
||||
.WithBody(SF.Block(
|
||||
SF.ParseStatement($"{eventType} handler = {eventName};\n"),
|
||||
SF.ParseStatement($"if (handler != null) {{ handler({onEventArgs}); }}\n")
|
||||
));
|
||||
|
||||
classDclr = classDclr.AddMembers(onEventMethodDclr);
|
||||
classDclr = classDclr.AddMembers(onEventMethodDclr);
|
||||
|
||||
// Create event trigger method
|
||||
string eventTriggerMethodName = eventName + "_Raise";
|
||||
MethodDeclarationSyntax eventTriggerMethod = SF.MethodDeclaration(SF.ParseTypeName("void"), eventTriggerMethodName)
|
||||
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
|
||||
.AddParameterListParameters(parameters)
|
||||
.WithBody(SF.Block(
|
||||
SF.ParseStatement($"{onEventMethodName}({eventTriggerArgs});\n")
|
||||
));
|
||||
classDclr = classDclr.AddMembers(eventTriggerMethod);
|
||||
// Create event trigger method
|
||||
string eventTriggerMethodName = eventName + "_Raise";
|
||||
MethodDeclarationSyntax eventTriggerMethod = SF.MethodDeclaration(SF.ParseTypeName("void"),
|
||||
eventTriggerMethodName)
|
||||
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword))
|
||||
.AddParameterListParameters(parameters)
|
||||
.WithBody(SF.Block(
|
||||
SF.ParseStatement($"{onEventMethodName}({eventTriggerArgs});\n")
|
||||
));
|
||||
classDclr = classDclr.AddMembers(eventTriggerMethod);
|
||||
|
||||
return classDclr;
|
||||
}
|
||||
return classDclr;
|
||||
}
|
||||
|
||||
private static ParameterSyntax[] GetEventParameters(IEventSymbol eventSymbol)
|
||||
{
|
||||
List<ParameterSyntax> parameters = new List<ParameterSyntax>
|
||||
{
|
||||
SF.Parameter(SF.Identifier("sender")).WithType(SF.ParseTypeName("object"))
|
||||
};
|
||||
INamedTypeSymbol type = (INamedTypeSymbol)(eventSymbol.Type);
|
||||
if (type.TypeArguments.Any())
|
||||
{
|
||||
parameters.Add(SF.Parameter(SF.Identifier("args")).WithType(SF.ParseTypeName(type.TypeArguments[0].Name)));
|
||||
}
|
||||
private static ParameterSyntax[] GetEventParameters(IEventSymbol eventSymbol)
|
||||
{
|
||||
List<ParameterSyntax> parameters = new List<ParameterSyntax>
|
||||
{
|
||||
SF.Parameter(SF.Identifier("sender")).WithType(SF.ParseTypeName("object"))
|
||||
};
|
||||
INamedTypeSymbol type = (INamedTypeSymbol) (eventSymbol.Type);
|
||||
if (type.TypeArguments.Any())
|
||||
{
|
||||
parameters.Add(SF.Parameter(SF.Identifier("args"))
|
||||
.WithType(SF.ParseTypeName(type.TypeArguments[0].Name)));
|
||||
}
|
||||
|
||||
return parameters.ToArray();
|
||||
}
|
||||
return parameters.ToArray();
|
||||
}
|
||||
|
||||
private static EventFieldDeclarationSyntax ToEventDclr(IEventSymbol eventSymbol)
|
||||
{
|
||||
string eventName = eventSymbol.Name;
|
||||
string eventType = GetEventType(eventSymbol);
|
||||
EventFieldDeclarationSyntax eventDclr = SF.EventFieldDeclaration(
|
||||
SF.VariableDeclaration(SF.IdentifierName(eventType),
|
||||
SF.SeparatedList(new[] { SF.VariableDeclarator(eventName) }))).AddModifiers(SF.Token(SyntaxKind.PublicKeyword));
|
||||
return eventDclr;
|
||||
}
|
||||
private static EventFieldDeclarationSyntax ToEventDclr(IEventSymbol eventSymbol)
|
||||
{
|
||||
string eventName = eventSymbol.Name;
|
||||
string eventType = GetEventType(eventSymbol);
|
||||
EventFieldDeclarationSyntax eventDclr = SF.EventFieldDeclaration(
|
||||
SF.VariableDeclaration(SF.IdentifierName(eventType),
|
||||
SF.SeparatedList(new[] {SF.VariableDeclarator(eventName)})))
|
||||
.AddModifiers(SF.Token(SyntaxKind.PublicKeyword));
|
||||
return eventDclr;
|
||||
}
|
||||
|
||||
private static string GetEventType(IEventSymbol eventSymbol)
|
||||
{
|
||||
return eventSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
}
|
||||
}
|
||||
private static string GetEventType(IEventSymbol eventSymbol)
|
||||
{
|
||||
return eventSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,9 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
interface IInterfaceStubber
|
||||
internal interface IInterfaceStubber
|
||||
{
|
||||
CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr, SemanticModel semanticModel);
|
||||
CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr,
|
||||
SemanticModel semanticModel);
|
||||
}
|
||||
}
|
|
@ -3,8 +3,9 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
interface IMethodStubber
|
||||
internal interface IMethodStubber
|
||||
{
|
||||
ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface);
|
||||
ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.CodeGen
|
||||
{
|
||||
interface IProjectStubber
|
||||
internal interface IProjectStubber
|
||||
{
|
||||
Task<StubProjectResult> StubProject(Project project, CompilationUnitSyntax cu);
|
||||
}
|
||||
|
|
|
@ -9,15 +9,16 @@ using Etg.SimpleStubs.CodeGen.Utils;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
class InterfaceStubber : IInterfaceStubber
|
||||
internal class InterfaceStubber : IInterfaceStubber
|
||||
{
|
||||
private readonly IEnumerable<IMethodStubber> _methodStubbers;
|
||||
|
||||
public InterfaceStubber(IEnumerable<IMethodStubber> methodStubbers)
|
||||
{
|
||||
_methodStubbers = new List<IMethodStubber>(methodStubbers);
|
||||
}
|
||||
|
||||
public CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr,
|
||||
public CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr,
|
||||
SemanticModel semanticModel)
|
||||
{
|
||||
INamedTypeSymbol interfaceType = semanticModel.GetDeclaredSymbol(interfaceDclr);
|
||||
|
@ -28,12 +29,24 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
.AddModifiers(SF.Token(RoslynUtils.GetVisibilityKeyword(interfaceType))
|
||||
//,SF.Token(SyntaxKind.PartialKeyword)
|
||||
)
|
||||
.WithBaseList(RoslynUtils.BaseList(interfaceName))
|
||||
.AddAttributeLists(AttributeListList(Attribute("CompilerGenerated")).ToArray());
|
||||
.WithBaseList(RoslynUtils.BaseList(interfaceName))
|
||||
.AddAttributeLists(AttributeListList(Attribute("CompilerGenerated")).ToArray());
|
||||
|
||||
classDclr = classDclr.AddMembers(
|
||||
SF.FieldDeclaration(
|
||||
SF.VariableDeclaration(SF.ParseTypeName("Dictionary<string, object>"),
|
||||
SF.SeparatedList(new[]
|
||||
{
|
||||
SF.VariableDeclarator(SF.Identifier("_stubs"), null,
|
||||
SF.EqualsValueClause(SF.ParseExpression("new Dictionary<string, object>()")))
|
||||
})))
|
||||
.AddModifiers(SF.Token(SyntaxKind.PrivateKeyword), SF.Token(SyntaxKind.ReadOnlyKeyword)));
|
||||
|
||||
|
||||
List<IMethodSymbol> methodsToStub = RoslynUtils.GetAllMethods(interfaceType);
|
||||
foreach (IMethodSymbol methodSymbol in methodsToStub)
|
||||
{
|
||||
foreach(IMethodStubber methodStubber in _methodStubbers)
|
||||
foreach (IMethodStubber methodStubber in _methodStubbers)
|
||||
{
|
||||
classDclr = methodStubber.StubMethod(classDclr, methodSymbol, interfaceType);
|
||||
}
|
||||
|
@ -58,29 +71,29 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
return namespaceNode;
|
||||
}
|
||||
|
||||
private static SyntaxList<AttributeListSyntax> AttributeListList(params AttributeSyntax[] attributes)
|
||||
{
|
||||
var list = new SyntaxList<AttributeListSyntax>();
|
||||
foreach (AttributeSyntax attributeSyntax in attributes)
|
||||
{
|
||||
list = list.Add(AttributeList(attributeSyntax));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
private static SyntaxList<AttributeListSyntax> AttributeListList(params AttributeSyntax[] attributes)
|
||||
{
|
||||
var list = new SyntaxList<AttributeListSyntax>();
|
||||
foreach (AttributeSyntax attributeSyntax in attributes)
|
||||
{
|
||||
list = list.Add(AttributeList(attributeSyntax));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static AttributeListSyntax AttributeList(params AttributeSyntax[] attributes)
|
||||
{
|
||||
SeparatedSyntaxList<AttributeSyntax> separatedList = SF.SeparatedList<AttributeSyntax>();
|
||||
foreach (var attributeSyntax in attributes)
|
||||
{
|
||||
separatedList = separatedList.Add(attributeSyntax);
|
||||
}
|
||||
return SF.AttributeList(separatedList);
|
||||
}
|
||||
private static AttributeListSyntax AttributeList(params AttributeSyntax[] attributes)
|
||||
{
|
||||
SeparatedSyntaxList<AttributeSyntax> separatedList = SF.SeparatedList<AttributeSyntax>();
|
||||
foreach (var attributeSyntax in attributes)
|
||||
{
|
||||
separatedList = separatedList.Add(attributeSyntax);
|
||||
}
|
||||
return SF.AttributeList(separatedList);
|
||||
}
|
||||
|
||||
private static AttributeSyntax Attribute(string attributeName)
|
||||
{
|
||||
return SF.Attribute(SF.IdentifierName(attributeName));
|
||||
}
|
||||
}
|
||||
private static AttributeSyntax Attribute(string attributeName)
|
||||
{
|
||||
return SF.Attribute(SF.IdentifierName(attributeName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,11 +7,12 @@ using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
class OrdinaryMethodStubber : IMethodStubber
|
||||
internal class OrdinaryMethodStubber : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface)
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
if(!methodSymbol.IsOrdinaryMethod())
|
||||
if (!methodSymbol.IsOrdinaryMethod())
|
||||
{
|
||||
return classDclr;
|
||||
}
|
||||
|
@ -22,35 +23,32 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
RoslynUtils.GetMethodParameterSyntaxList(methodSymbol).ToArray()));
|
||||
methodDclr = methodDclr.WithSemicolonToken(SF.Token(SyntaxKind.None))
|
||||
.WithExplicitInterfaceSpecifier(
|
||||
SF.ExplicitInterfaceSpecifier(
|
||||
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
||||
if (methodSymbol.IsGenericMethod)
|
||||
{
|
||||
StatementSyntax stmtSyntax;
|
||||
if (methodSymbol.ReturnsVoid)
|
||||
{
|
||||
stmtSyntax = SF.ParseStatement("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
stmtSyntax = SF.ParseStatement($"return default({methodSymbol.ReturnType.GetFullyQualifiedName()});\n");
|
||||
}
|
||||
SF.ExplicitInterfaceSpecifier(
|
||||
SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName())));
|
||||
|
||||
classDclr = classDclr.AddMembers(methodDclr.WithBody(SF.Block(stmtSyntax)));
|
||||
}
|
||||
else
|
||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
||||
string parameters = string.Join(", ", methodSymbol.Parameters.Select(p =>
|
||||
{
|
||||
string delegatePropertyName = NamingUtils.GetDelegatePropertyName(methodSymbol, stubbedInterface);
|
||||
string callDelegateStmt = $"{delegatePropertyName}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))});\n";
|
||||
if (!methodSymbol.ReturnsVoid)
|
||||
if (p.RefKind == RefKind.Out)
|
||||
{
|
||||
callDelegateStmt = callDelegateStmt.Insert(0, "return ");
|
||||
return $"out {p.Name}";
|
||||
}
|
||||
if (p.RefKind == RefKind.Ref)
|
||||
{
|
||||
return $"ref {p.Name}";
|
||||
}
|
||||
return p.Name;
|
||||
}));
|
||||
|
||||
classDclr = classDclr.AddMembers(
|
||||
methodDclr.WithBody(SF.Block(SF.ParseStatement(callDelegateStmt))));
|
||||
string callDelegateStmt = StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, parameters);
|
||||
if (!methodSymbol.ReturnsVoid)
|
||||
{
|
||||
callDelegateStmt = callDelegateStmt.Insert(0, "return ");
|
||||
}
|
||||
|
||||
classDclr = classDclr.AddMembers(
|
||||
methodDclr.WithBody(SF.Block(SF.ParseStatement(callDelegateStmt))));
|
||||
|
||||
return classDclr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.CodeGen
|
||||
{
|
||||
class ProjectStubber : IProjectStubber
|
||||
internal class ProjectStubber : IProjectStubber
|
||||
{
|
||||
private readonly IInterfaceStubber _interfaceStubber;
|
||||
private readonly SimpleStubsConfig _config;
|
||||
|
||||
public ProjectStubber(IInterfaceStubber interfaceStubber, SimpleStubsConfig config)
|
||||
{
|
||||
_interfaceStubber = interfaceStubber;
|
||||
|
@ -28,7 +29,11 @@ namespace Etg.SimpleStubs.CodeGen.CodeGen
|
|||
{
|
||||
SyntaxTree syntaxTree = await document.GetSyntaxTreeAsync();
|
||||
SemanticModel semanticModel = await document.GetSemanticModelAsync();
|
||||
IEnumerable<InterfaceDeclarationSyntax> interfaces = syntaxTree.GetRoot().DescendantNodes().OfType<InterfaceDeclarationSyntax>().Where(i => SatisfiesVisibilityConstraints(i));
|
||||
IEnumerable<InterfaceDeclarationSyntax> interfaces =
|
||||
syntaxTree.GetRoot()
|
||||
.DescendantNodes()
|
||||
.OfType<InterfaceDeclarationSyntax>()
|
||||
.Where(i => SatisfiesVisibilityConstraints(i));
|
||||
if (!interfaces.Any())
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -9,19 +9,22 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
using System.Linq;
|
||||
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
class PropertyStubber : IMethodStubber
|
||||
internal class PropertyStubber : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface)
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
if (!methodSymbol.IsPropertyAccessor())
|
||||
{
|
||||
return classDclr;
|
||||
}
|
||||
|
||||
string delegatePropertyName = NamingUtils.GetDelegatePropertyName(methodSymbol, stubbedInterface);
|
||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
||||
|
||||
string propName = methodSymbol.AssociatedSymbol.Name;
|
||||
string propType = ((IPropertySymbol)methodSymbol.AssociatedSymbol).Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
string propType =
|
||||
((IPropertySymbol) methodSymbol.AssociatedSymbol).Type.ToDisplayString(
|
||||
SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
var propDclr = GetPropDclr(classDclr, propName);
|
||||
if (propDclr == null)
|
||||
{
|
||||
|
@ -33,15 +36,19 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
if (methodSymbol.IsPropertyGetter())
|
||||
{
|
||||
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, SF.Block(
|
||||
SF.List(new[] {
|
||||
SF.ReturnStatement(SF.IdentifierName(delegatePropertyName + "()")) })));
|
||||
SF.List(new[]
|
||||
{
|
||||
SF.ParseStatement("return " + StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, ""))
|
||||
})));
|
||||
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
||||
}
|
||||
else if (methodSymbol.IsPropertySetter())
|
||||
{
|
||||
var accessorDclr = SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, SF.Block(
|
||||
SF.List(new[] {
|
||||
SF.ParseStatement($"{delegatePropertyName}(value);\n") })));
|
||||
SF.List(new[]
|
||||
{
|
||||
SF.ParseStatement(StubbingUtils.GenerateInvokeDelegateStmt(delegateTypeName, "value"))
|
||||
})));
|
||||
propDclr = propDclr.AddAccessorListAccessors(accessorDclr);
|
||||
}
|
||||
|
||||
|
@ -63,7 +70,10 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
|
||||
private static PropertyDeclarationSyntax GetPropDclr(ClassDeclarationSyntax classDclr, string propName)
|
||||
{
|
||||
return classDclr.DescendantNodes().OfType<PropertyDeclarationSyntax>().FirstOrDefault(p => p.Identifier.Text == propName);
|
||||
return
|
||||
classDclr.DescendantNodes()
|
||||
.OfType<PropertyDeclarationSyntax>()
|
||||
.FirstOrDefault(p => p.Identifier.Text == propName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,9 @@ namespace Etg.SimpleStubs.CodeGen
|
|||
{
|
||||
internal class SimpleStubsGenerator
|
||||
{
|
||||
IProjectStubber _projectStubber;
|
||||
SimpleStubsConfig _config;
|
||||
private IProjectStubber _projectStubber;
|
||||
private SimpleStubsConfig _config;
|
||||
|
||||
public SimpleStubsGenerator(IProjectStubber projectStubber, SimpleStubsConfig config)
|
||||
{
|
||||
_projectStubber = projectStubber;
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.CodeGen
|
||||
{
|
||||
class StubProjectResult
|
||||
internal class StubProjectResult
|
||||
{
|
||||
public StubProjectResult(CompilationUnitSyntax cu, IEnumerable<string> usings)
|
||||
{
|
||||
|
@ -11,14 +11,8 @@ namespace Etg.SimpleStubs.CodeGen.CodeGen
|
|||
Usings = usings;
|
||||
}
|
||||
|
||||
public CompilationUnitSyntax CompilationUnit
|
||||
{
|
||||
get;
|
||||
}
|
||||
public CompilationUnitSyntax CompilationUnit { get; }
|
||||
|
||||
public IEnumerable<string> Usings
|
||||
{
|
||||
get;
|
||||
}
|
||||
public IEnumerable<string> Usings { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Etg.SimpleStubs.CodeGen.Utils;
|
||||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Collections.Generic;
|
||||
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
internal class StubbingDelegateGenerator : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
if (methodSymbol.IsPropertyAccessor() || methodSymbol.IsOrdinaryMethod())
|
||||
{
|
||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface);
|
||||
string setupMethodName = NamingUtils.GetSetupMethodName(methodSymbol);
|
||||
|
||||
DelegateDeclarationSyntax delegateDclr = GenerateDelegateDclr(methodSymbol, delegateTypeName,
|
||||
stubbedInterface);
|
||||
MethodDeclarationSyntax propDclr = GenerateSetupMethod(setupMethodName, delegateTypeName,
|
||||
stubbedInterface, classDclr);
|
||||
classDclr = classDclr.AddMembers(delegateDclr, propDclr);
|
||||
}
|
||||
|
||||
return classDclr;
|
||||
}
|
||||
|
||||
private static MethodDeclarationSyntax GenerateSetupMethod(string setupMethodName, string delegateTypeName,
|
||||
INamedTypeSymbol stubbedInterface,
|
||||
ClassDeclarationSyntax stub)
|
||||
{
|
||||
SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface);
|
||||
return SF.MethodDeclaration(SF.ParseTypeName(stub.Identifier.Text), setupMethodName)
|
||||
.AddModifiers(SF.Token(visibility)).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))
|
||||
.AddParameterListParameters(
|
||||
SF.Parameter(SF.Identifier("del")).WithType(SF.ParseTypeName(delegateTypeName)))
|
||||
.WithBody(SF.Block(
|
||||
SF.ParseStatement($"_stubs[nameof({delegateTypeName})] = del;\n"),
|
||||
SF.ParseStatement("return this;\n")
|
||||
))
|
||||
.WithSemicolonToken(SF.Token(SyntaxKind.None));
|
||||
}
|
||||
|
||||
private static DelegateDeclarationSyntax GenerateDelegateDclr(IMethodSymbol methodSymbol, string delegateName,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface);
|
||||
List<ParameterSyntax> paramsSyntaxList = RoslynUtils.GetMethodParameterSyntaxList(methodSymbol);
|
||||
return SF.DelegateDeclaration(SF.ParseTypeName(methodSymbol.ReturnType.GetFullyQualifiedName()),
|
||||
delegateName)
|
||||
.AddModifiers(SF.Token(visibility)).AddParameterListParameters(paramsSyntaxList.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Etg.SimpleStubs.CodeGen.Utils;
|
||||
|
||||
namespace Etg.SimpleStubs.CodeGen
|
||||
{
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
class StubbingPropertiesGenerator : IMethodStubber
|
||||
{
|
||||
public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
if (methodSymbol.IsPropertyAccessor() || methodSymbol.IsOrdinaryMethod())
|
||||
{
|
||||
if (!methodSymbol.IsGenericMethod)
|
||||
{
|
||||
string delegatePropertyName = NamingUtils.GetDelegatePropertyName(methodSymbol, stubbedInterface);
|
||||
string delegateTypeName = NamingUtils.GetDelegateTypeName(delegatePropertyName);
|
||||
|
||||
DelegateDeclarationSyntax delegateDclr = GenerateDelegateDclr(methodSymbol, delegateTypeName, stubbedInterface);
|
||||
PropertyDeclarationSyntax propDclr = GenerateDelegatePropDclr(
|
||||
delegatePropertyName, delegateTypeName, stubbedInterface);
|
||||
classDclr = classDclr.AddMembers(delegateDclr, propDclr);
|
||||
}
|
||||
}
|
||||
|
||||
return classDclr;
|
||||
}
|
||||
|
||||
private static PropertyDeclarationSyntax GenerateDelegatePropDclr(string delegatePropertyName,
|
||||
string delegateName, INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface);
|
||||
return SF.PropertyDeclaration(SF.ParseTypeName(delegateName), delegatePropertyName)
|
||||
.AddModifiers(SF.Token(visibility)).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
private static DelegateDeclarationSyntax GenerateDelegateDclr(IMethodSymbol methodSymbol, string delegateName,
|
||||
INamedTypeSymbol stubbedInterface)
|
||||
{
|
||||
SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface);
|
||||
List<ParameterSyntax> paramsSyntaxList = RoslynUtils.GetMethodParameterSyntaxList(methodSymbol);
|
||||
return SF.DelegateDeclaration(SF.ParseTypeName(methodSymbol.ReturnType.GetFullyQualifiedName()), delegateName)
|
||||
.AddModifiers(SF.Token(visibility)).AddParameterListParameters(paramsSyntaxList.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using System.IO;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.Config
|
||||
{
|
||||
class ConfigLoader
|
||||
internal class ConfigLoader
|
||||
{
|
||||
public SimpleStubsConfig LoadConfig(string configFilePath)
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace Etg.SimpleStubs.CodeGen.Config
|
|||
return JsonConvert.DeserializeObject<SimpleStubsConfig>(File.ReadAllText(configFilePath));
|
||||
}
|
||||
|
||||
return new SimpleStubsConfig(new string[]{}, new string[]{}, false);
|
||||
return new SimpleStubsConfig(new string[] {}, new string[] {}, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.Config
|
||||
{
|
||||
class SimpleStubsConfig
|
||||
internal class SimpleStubsConfig
|
||||
{
|
||||
public SimpleStubsConfig(IEnumerable<string> ignoredProjects,
|
||||
public SimpleStubsConfig(IEnumerable<string> ignoredProjects,
|
||||
IEnumerable<string> ignoredInterfaces,
|
||||
bool stubInternalInterfaces)
|
||||
{
|
||||
|
@ -13,19 +13,10 @@ namespace Etg.SimpleStubs.CodeGen.Config
|
|||
StubInternalInterfaces = stubInternalInterfaces;
|
||||
}
|
||||
|
||||
public ISet<string> IgnoredProjects
|
||||
{
|
||||
get;
|
||||
}
|
||||
public ISet<string> IgnoredProjects { get; }
|
||||
|
||||
public ISet<string> IgnoredInterfaces
|
||||
{
|
||||
get;
|
||||
}
|
||||
public ISet<string> IgnoredInterfaces { get; }
|
||||
|
||||
public bool StubInternalInterfaces
|
||||
{
|
||||
get;
|
||||
}
|
||||
public bool StubInternalInterfaces { get; }
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using System.IO;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.DI
|
||||
{
|
||||
class DiModule
|
||||
internal class DiModule
|
||||
{
|
||||
private readonly IContainer _container;
|
||||
|
||||
|
@ -26,11 +26,13 @@ namespace Etg.SimpleStubs.CodeGen.DI
|
|||
cb.Register((c) =>
|
||||
{
|
||||
IInterfaceStubber interfaceStubber = new InterfaceStubber(
|
||||
new IMethodStubber[] {
|
||||
new OrdinaryMethodStubber(),
|
||||
new EventStubber(),
|
||||
new PropertyStubber(),
|
||||
new StubbingPropertiesGenerator() });
|
||||
new IMethodStubber[]
|
||||
{
|
||||
new OrdinaryMethodStubber(),
|
||||
new EventStubber(),
|
||||
new PropertyStubber(),
|
||||
new StubbingDelegateGenerator()
|
||||
});
|
||||
return interfaceStubber;
|
||||
}).As<IInterfaceStubber>().SingleInstance();
|
||||
|
||||
|
@ -42,4 +44,4 @@ namespace Etg.SimpleStubs.CodeGen.DI
|
|||
|
||||
public SimpleStubsGenerator StubsGenerator => _container.Resolve<SimpleStubsGenerator>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
|
||||
[assembly: AssemblyTitle("SimpleStubs")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
@ -17,9 +18,11 @@ using System.Runtime.InteropServices;
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
|
||||
[assembly: Guid("3e9c520a-94cf-46d0-864b-4293d439c92a")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
|
@ -32,6 +35,6 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.0.1.0")]
|
||||
|
||||
[assembly: InternalsVisibleTo("TestClassLibraryTest")]
|
||||
[assembly: AssemblyVersion("0.0.1.0")]
|
||||
[assembly: InternalsVisibleTo("TestClassLibraryTest")]
|
|
@ -103,6 +103,7 @@
|
|||
<Compile Include="CodeGen\InterfaceStubber.cs" />
|
||||
<Compile Include="CodeGen\IProjectStubber.cs" />
|
||||
<Compile Include="CodeGen\ProjectStubber.cs" />
|
||||
<Compile Include="Utils\StubbingUtils.cs" />
|
||||
<Compile Include="CodeGen\StubProjectResult.cs" />
|
||||
<Compile Include="Config\SimpleStubsConfig.cs" />
|
||||
<Compile Include="Config\ConfigLoader.cs" />
|
||||
|
@ -113,7 +114,7 @@
|
|||
<Compile Include="CodeGen\IMethodStubber.cs" />
|
||||
<Compile Include="CodeGen\OrdinaryMethodStubber.cs" />
|
||||
<Compile Include="CodeGen\PropertyStubber.cs" />
|
||||
<Compile Include="CodeGen\StubbingPropertiesGenerator.cs" />
|
||||
<Compile Include="CodeGen\StubbingDelegateGenerator.cs" />
|
||||
<Compile Include="CodeGen\SimpleStubsGenerator.cs" />
|
||||
<Compile Include="Utils\NamingUtils.cs" />
|
||||
<Compile Include="Utils\RoslynExtensions.cs" />
|
||||
|
|
|
@ -10,24 +10,16 @@ namespace Etg.SimpleStubs.CodeGen.Tasks
|
|||
public class GenerateStubsTask : Microsoft.Build.Utilities.AppDomainIsolatedTask
|
||||
{
|
||||
[Required]
|
||||
public string OutputPath
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string OutputPath { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ProjectPath
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string ProjectPath { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage("Generating stubs");
|
||||
LogMessage("Generating stubs");
|
||||
DiModule diModule = new DiModule(ProjectPath, OutputPath);
|
||||
File.WriteAllText(OutputPath, diModule.StubsGenerator.GenerateStubs(ProjectPath).Result);
|
||||
return true;
|
||||
|
|
|
@ -4,45 +4,62 @@ using System.Text;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.Utils
|
||||
{
|
||||
class NamingUtils
|
||||
internal class NamingUtils
|
||||
{
|
||||
public string GetInterfaceStubName(string interfaceName)
|
||||
{
|
||||
return "Stub" + interfaceName;
|
||||
}
|
||||
|
||||
public static string GetDelegatePropertyName(IMethodSymbol methodSymbol, INamedTypeSymbol targetInterface)
|
||||
public static string GetSetupMethodName(IMethodSymbol methodSymbol)
|
||||
{
|
||||
if (methodSymbol.IsPropertyGetter())
|
||||
{
|
||||
return methodSymbol.Name.Substring(4) + "_Get";
|
||||
}
|
||||
if (methodSymbol.IsPropertySetter())
|
||||
{
|
||||
return methodSymbol.Name.Substring(4) + "_Set";
|
||||
}
|
||||
return methodSymbol.GetGenericName();
|
||||
}
|
||||
|
||||
public static string GetDelegateTypeName(IMethodSymbol methodSymbol, INamedTypeSymbol targetInterface)
|
||||
{
|
||||
string methodName = methodSymbol.Name;
|
||||
if (methodSymbol.IsPropertyGetter())
|
||||
{
|
||||
methodName = methodName.Substring(4) + "_Get";
|
||||
}
|
||||
else if (methodSymbol.IsPropertySetter())
|
||||
{
|
||||
methodName = methodName.Substring(4) + "_Set";
|
||||
}
|
||||
string methodName = methodSymbol.Name;
|
||||
if (methodSymbol.IsPropertyGetter())
|
||||
{
|
||||
methodName = methodName.Substring(4) + "_Get";
|
||||
}
|
||||
else if (methodSymbol.IsPropertySetter())
|
||||
{
|
||||
methodName = methodName.Substring(4) + "_Set";
|
||||
}
|
||||
|
||||
// only prefix inherited members
|
||||
if (targetInterface.GetGenericName() != methodSymbol.ContainingSymbol.GetGenericName())
|
||||
{
|
||||
methodName = SerializeName(methodSymbol.ContainingSymbol) + "_" + methodName;
|
||||
}
|
||||
// only prefix inherited members
|
||||
if (targetInterface.GetGenericName() != methodSymbol.ContainingSymbol.GetGenericName())
|
||||
{
|
||||
methodName = SerializeName(methodSymbol.ContainingSymbol) + "_" + methodName;
|
||||
}
|
||||
|
||||
if(methodSymbol.IsOrdinaryMethod())
|
||||
{
|
||||
if (methodSymbol.Parameters.Any())
|
||||
{
|
||||
methodName = methodName + "_" + string.Join("_", methodSymbol.Parameters.Select(SerializeName));
|
||||
}
|
||||
}
|
||||
return methodName;
|
||||
if (methodSymbol.IsOrdinaryMethod())
|
||||
{
|
||||
if (methodSymbol.Parameters.Any())
|
||||
{
|
||||
methodName = methodName + "_" + string.Join("_", methodSymbol.Parameters.Select(SerializeName));
|
||||
}
|
||||
}
|
||||
|
||||
methodName += "_Delegate";
|
||||
|
||||
if (methodSymbol.IsGenericMethod)
|
||||
{
|
||||
methodName =
|
||||
$"{methodName}<{string.Join(",", methodSymbol.TypeParameters.Select(symbol => symbol.Name))}>";
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public static string GetDelegateTypeName(string delegatePropertyName)
|
||||
{
|
||||
return delegatePropertyName + "_Delegate";
|
||||
}
|
||||
|
||||
public static string SerializeName(ISymbol param)
|
||||
{
|
||||
|
@ -66,7 +83,6 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
{
|
||||
sb.Append(part.Symbol.Name);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -79,4 +95,4 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
return "Stub" + interfaceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,10 +7,10 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.Utils
|
||||
{
|
||||
static class RoslynExtensions
|
||||
internal static class RoslynExtensions
|
||||
{
|
||||
public static SymbolDisplayFormat QualifiedFormat = new SymbolDisplayFormat(
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
|
||||
|
||||
public static bool IsEvent(this IMethodSymbol methodSymbol)
|
||||
{
|
||||
|
@ -69,7 +69,10 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
|
||||
public static string GetQualifiedName(this ITypeSymbol symbol)
|
||||
{
|
||||
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Split(new[] { "::" }, StringSplitOptions.RemoveEmptyEntries).Last();
|
||||
return
|
||||
symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
.Split(new[] {"::"}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Last();
|
||||
}
|
||||
|
||||
public static string GetMinimallyQualifiedName(this ITypeSymbol symbol)
|
||||
|
@ -84,7 +87,9 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
|
||||
public static bool IsPublic(this TypeDeclarationSyntax typeDclr)
|
||||
{
|
||||
return typeDclr.Modifiers.Any(modifier => modifier.RawKind.Equals(SyntaxFactory.Token(SyntaxKind.PublicKeyword).RawKind));
|
||||
return
|
||||
typeDclr.Modifiers.Any(
|
||||
modifier => modifier.RawKind.Equals(SyntaxFactory.Token(SyntaxKind.PublicKeyword).RawKind));
|
||||
}
|
||||
|
||||
public static bool IsInternal(this TypeDeclarationSyntax typeDclr)
|
||||
|
@ -97,6 +102,5 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
};
|
||||
return !typeDclr.Modifiers.Any(modifier => nonInternalModifiers.Contains(modifier.RawKind));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|||
|
||||
namespace Etg.SimpleStubs.CodeGen.Utils
|
||||
{
|
||||
static class RoslynUtils
|
||||
internal static class RoslynUtils
|
||||
{
|
||||
public static UsingDirectiveSyntax UsingDirective(string nameSpace)
|
||||
{
|
||||
|
@ -21,12 +21,15 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
|
||||
public static ParameterSyntax CreateParameter(string type, string name)
|
||||
{
|
||||
return SF.Parameter(new SyntaxList<AttributeListSyntax>(), new SyntaxTokenList(), SF.IdentifierName(type), SF.Identifier(new SyntaxTriviaList().Add(SF.Space), name, new SyntaxTriviaList()), null);
|
||||
return SF.Parameter(new SyntaxList<AttributeListSyntax>(), new SyntaxTokenList(), SF.IdentifierName(type),
|
||||
SF.Identifier(new SyntaxTriviaList().Add(SF.Space), name, new SyntaxTriviaList()), null);
|
||||
}
|
||||
|
||||
public static BaseListSyntax BaseList(params string[] names)
|
||||
{
|
||||
return SF.BaseList(SF.SeparatedList<BaseTypeSyntax>(names.Select(name => SF.SimpleBaseType(SF.IdentifierName(name)))));
|
||||
return
|
||||
SF.BaseList(
|
||||
SF.SeparatedList<BaseTypeSyntax>(names.Select(name => SF.SimpleBaseType(SF.IdentifierName(name)))));
|
||||
}
|
||||
|
||||
public static List<ParameterSyntax> GetMethodParameterSyntaxList(IMethodSymbol methodSymbol)
|
||||
|
@ -34,7 +37,18 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
var paramsSyntaxList = new List<ParameterSyntax>();
|
||||
foreach (IParameterSymbol param in methodSymbol.Parameters)
|
||||
{
|
||||
ParameterSyntax paramSyntax = SF.Parameter(SF.Identifier(param.Name)).WithType(SF.ParseTypeName(param.Type.GetFullyQualifiedName()));
|
||||
ParameterSyntax paramSyntax = SF.Parameter(SF.Identifier(param.Name))
|
||||
.WithType(SF.ParseTypeName(param.Type.GetFullyQualifiedName()));
|
||||
|
||||
if (param.RefKind == RefKind.Out)
|
||||
{
|
||||
paramSyntax = paramSyntax.WithModifiers(SyntaxTokenList.Create(SF.Token(SyntaxKind.OutKeyword)));
|
||||
}
|
||||
else if (param.RefKind == RefKind.Ref)
|
||||
{
|
||||
paramSyntax = paramSyntax.WithModifiers(SyntaxTokenList.Create(SF.Token(SyntaxKind.RefKeyword)));
|
||||
}
|
||||
|
||||
paramsSyntaxList.Add(paramSyntax);
|
||||
}
|
||||
|
||||
|
@ -65,7 +79,9 @@ namespace Etg.SimpleStubs.CodeGen.Utils
|
|||
public static SyntaxKind GetVisibilityKeyword(ISymbol stubbedInterface)
|
||||
{
|
||||
return stubbedInterface.DeclaredAccessibility ==
|
||||
Accessibility.Internal ? SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword;
|
||||
Accessibility.Internal
|
||||
? SyntaxKind.InternalKeyword
|
||||
: SyntaxKind.PublicKeyword;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace Etg.SimpleStubs.CodeGen.Utils
|
||||
{
|
||||
internal class StubbingUtils
|
||||
{
|
||||
public static string GenerateInvokeDelegateStmt(string delegateTypeName, string parameters)
|
||||
{
|
||||
return $"(({delegateTypeName})_stubs[nameof({delegateTypeName})]).Invoke({parameters});\n";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="Autofac" version="3.5.2" targetFramework="net461" />
|
||||
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TestClassLibrary
|
||||
|
@ -20,7 +22,18 @@ namespace TestClassLibrary
|
|||
event EventHandler<long> PhoneNumberChanged;
|
||||
}
|
||||
|
||||
|
||||
public interface IContainer
|
||||
{
|
||||
T GetElement<T>(int index);
|
||||
void SetElement<T>(int index, T value);
|
||||
|
||||
bool GetElement(int index, out object value);
|
||||
}
|
||||
|
||||
public interface IRefUtils
|
||||
{
|
||||
void Swap<T>(ref T v1, ref T v2);
|
||||
}
|
||||
|
||||
public interface ITestInterface : IDisposable
|
||||
{
|
||||
|
@ -32,6 +45,8 @@ namespace TestClassLibrary
|
|||
|
||||
Task<List<int>> DoSomething(int parameter);
|
||||
|
||||
void SetDictionary(Dictionary<string, string> dict);
|
||||
|
||||
string Prop1 { get; }
|
||||
|
||||
string Prop2 { set; }
|
||||
|
@ -40,9 +55,9 @@ namespace TestClassLibrary
|
|||
|
||||
event EventHandler<EventArgs> Changed;
|
||||
|
||||
event EventHandler OtherEvent;
|
||||
event EventHandler OtherEvent;
|
||||
|
||||
List<T> GetGenericList<T, A>();
|
||||
List<T> GetGenericList<T>();
|
||||
|
||||
void SetGenericValue<T>(T value);
|
||||
}
|
||||
|
@ -51,7 +66,7 @@ namespace TestClassLibrary
|
|||
{
|
||||
}
|
||||
|
||||
interface IInternalInterface
|
||||
internal interface IInternalInterface
|
||||
{
|
||||
void DoSomethingInternal();
|
||||
}
|
||||
|
@ -61,4 +76,26 @@ namespace TestClassLibrary
|
|||
T GetX();
|
||||
}
|
||||
|
||||
}
|
||||
public interface IInterfaceWithGenericMethod
|
||||
{
|
||||
T GetFoo<T>();
|
||||
}
|
||||
|
||||
public class Stub : IInterfaceWithGenericMethod
|
||||
{
|
||||
private readonly Dictionary<string, object> _stubs = new Dictionary<string, object>();
|
||||
|
||||
public delegate T GetFooOfT_Delegate<T>();
|
||||
|
||||
public T GetFoo<T>()
|
||||
{
|
||||
return ((GetFooOfT_Delegate<T>) _stubs[nameof(GetFooOfT_Delegate<T>)]).Invoke();
|
||||
}
|
||||
|
||||
public Stub SetupGetFooOfT<T>(GetFooOfT_Delegate<T> del)
|
||||
{
|
||||
_stubs[nameof(GetFooOfT_Delegate<T>)] = del;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
|
||||
[assembly: AssemblyTitle("TestClassLibrary")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
@ -17,9 +18,11 @@ using System.Runtime.InteropServices;
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
|
||||
[assembly: Guid("ecbcbae6-949e-4e9c-84e1-614d97909b6c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
|
@ -32,7 +35,7 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
[assembly: InternalsVisibleTo("TestClassLibraryTest")]
|
||||
[assembly: InternalsVisibleTo("TestClassLibraryTest")]
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
|
||||
[assembly: AssemblyTitle("TestClassLibraryTest")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
|
@ -17,9 +18,11 @@ using System.Runtime.InteropServices;
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
|
||||
[assembly: Guid("cb81f60f-1374-4b46-bb64-d848b5103a58")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
|
@ -32,5 +35,6 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -18,15 +18,13 @@ namespace TestClassLibraryTest
|
|||
long number = 6041234567;
|
||||
string firstName = null;
|
||||
string lastName = null;
|
||||
var stub = new StubIPhoneBook
|
||||
var stub = new StubIPhoneBook();
|
||||
stub.GetContactPhoneNumber((fn, ln) =>
|
||||
{
|
||||
GetContactPhoneNumber_String_String = (fn, ln) =>
|
||||
{
|
||||
firstName = fn;
|
||||
lastName = ln;
|
||||
return number;
|
||||
}
|
||||
};
|
||||
firstName = fn;
|
||||
lastName = ln;
|
||||
return number;
|
||||
});
|
||||
IPhoneBook phoneBook = stub;
|
||||
long actualNumber = phoneBook.GetContactPhoneNumber("John", "Smith");
|
||||
Assert.AreEqual(number, actualNumber);
|
||||
|
@ -39,14 +37,11 @@ namespace TestClassLibraryTest
|
|||
{
|
||||
long myNumber = 6041234567;
|
||||
long newNumber = 0;
|
||||
var stub = new StubIPhoneBook
|
||||
{
|
||||
MyNumber_Get = () => myNumber,
|
||||
MyNumber_Set = num =>
|
||||
{
|
||||
newNumber = num;
|
||||
}
|
||||
};
|
||||
|
||||
var stub = new StubIPhoneBook()
|
||||
.MyNumber_Get(() => myNumber)
|
||||
.MyNumber_Set(value => newNumber = value);
|
||||
|
||||
IPhoneBook phoneBook = stub;
|
||||
Assert.AreEqual(myNumber, phoneBook.MyNumber);
|
||||
phoneBook.MyNumber = 13;
|
||||
|
@ -57,7 +52,7 @@ namespace TestClassLibraryTest
|
|||
public void TestPropertyStubWithGetterOnly()
|
||||
{
|
||||
int contactsCount = 55;
|
||||
var stub = new StubIPhoneBook { ContactsCount_Get = () => contactsCount };
|
||||
var stub = new StubIPhoneBook().ContactsCount_Get(() => contactsCount);
|
||||
IPhoneBook phoneBook = stub;
|
||||
Assert.AreEqual(contactsCount, phoneBook.ContactsCount);
|
||||
}
|
||||
|
@ -71,10 +66,9 @@ namespace TestClassLibraryTest
|
|||
stub.PhoneNumberChanged += (s, num) =>
|
||||
{
|
||||
sender = s;
|
||||
newNumber = num;
|
||||
newNumber = num;
|
||||
}
|
||||
|
||||
;
|
||||
;
|
||||
stub.PhoneNumberChanged_Raise(this, 55);
|
||||
Assert.AreEqual(55, newNumber);
|
||||
Assert.AreEqual(this, sender);
|
||||
|
@ -87,7 +81,7 @@ namespace TestClassLibraryTest
|
|||
.Once((p1, p2) => 12345678) // first call
|
||||
.Repeat((p1, p2) => 11122233, 2) // next two call
|
||||
.Forever((p1, p2) => 22233556); // rest of the calls
|
||||
var stub = new StubIPhoneBook { GetContactPhoneNumber_String_String = (p1, p2) => sequence.Next(p1, p2) };
|
||||
var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => sequence.Next(p1, p2));
|
||||
IPhoneBook phoneBook = stub;
|
||||
Assert.AreEqual(12345678, phoneBook.GetContactPhoneNumber("John", "Smith"));
|
||||
Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith"));
|
||||
|
@ -99,15 +93,64 @@ namespace TestClassLibraryTest
|
|||
Assert.AreEqual(6, sequence.CallCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGenericMethod()
|
||||
{
|
||||
int value = -1;
|
||||
var stub = new StubIContainer()
|
||||
.GetElement<int>(index => value)
|
||||
.SetElement<int>((i, v) => { value = v; });
|
||||
|
||||
IContainer container = stub;
|
||||
container.SetElement(0, 5);
|
||||
Assert.AreEqual(5, container.GetElement<int>(1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestOutParameter()
|
||||
{
|
||||
object someObj = "test";
|
||||
var stub = new StubIContainer()
|
||||
.GetElement((int index, out object value) =>
|
||||
{
|
||||
value = someObj;
|
||||
return true;
|
||||
});
|
||||
|
||||
IContainer container = stub;
|
||||
object result;
|
||||
container.GetElement(0, out result);
|
||||
Assert.AreEqual(someObj, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestRefParameter()
|
||||
{
|
||||
var stub = new StubIRefUtils()
|
||||
.Swap<int>((ref int v1, ref int v2) =>
|
||||
{
|
||||
int temp = v1;
|
||||
v1 = v2;
|
||||
v2 = temp;
|
||||
});
|
||||
|
||||
int i1 = 1;
|
||||
int i2 = 2;
|
||||
|
||||
((IRefUtils) stub).Swap<int>(ref i1, ref i2);
|
||||
Assert.AreEqual(2, i1);
|
||||
Assert.AreEqual(1, i2);
|
||||
}
|
||||
|
||||
// this test is only used for debugging
|
||||
[Ignore]
|
||||
[TestMethod]
|
||||
public async Task TestGenerateStubs()
|
||||
{
|
||||
string path = //@"C:\projects\JasperMain\Product\Jasper.Test\Jasper.Test.csproj";
|
||||
@"..\..\TestClassLibraryTest.csproj";
|
||||
//"..\\..\\SimpleStubsTest.csproj";
|
||||
SimpleStubsGenerator stubsGenerator = new DiModule(path, @"..\..\Properties\SimpleStubs.generated.cs").StubsGenerator;
|
||||
@"..\..\TestClassLibraryTest.csproj";
|
||||
//"..\\..\\SimpleStubsTest.csproj";
|
||||
SimpleStubsGenerator stubsGenerator =
|
||||
new DiModule(path, @"..\..\Properties\SimpleStubs.generated.cs").StubsGenerator;
|
||||
string stubs = await stubsGenerator.GenerateStubs(path);
|
||||
File.WriteAllText(@"..\..\Properties\SimpleStubs.generated.cs", stubs);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="Autofac" version="3.5.2" targetFramework="net461" />
|
||||
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче