mirror of
https://github.com/Smaug123/WoofWare.Whippet
synced 2025-10-06 08:18:39 +00:00
Import interface-mock generator (#14)
Some checks are pending
.NET / build (Release) (push) Waiting to run
.NET / analyzers (push) Waiting to run
.NET / build (Debug) (push) Waiting to run
.NET / check-dotnet-format (push) Waiting to run
.NET / check-nix-format (push) Waiting to run
.NET / Check links (push) Waiting to run
.NET / Check flake (push) Waiting to run
.NET / nuget-pack (push) Waiting to run
.NET / expected-pack (push) Blocked by required conditions
.NET / check-accurate-generations (push) Waiting to run
.NET / all-required-checks-complete (push) Blocked by required conditions
.NET / nuget-publish (push) Blocked by required conditions
.NET / nuget-publish-fantomas (push) Blocked by required conditions
.NET / nuget-publish-json-plugin (push) Blocked by required conditions
.NET / nuget-publish-json-attrs (push) Blocked by required conditions
.NET / nuget-publish-argparser-plugin (push) Blocked by required conditions
.NET / nuget-publish-argparser-attrs (push) Blocked by required conditions
.NET / nuget-publish-httpclient-plugin (push) Blocked by required conditions
.NET / nuget-publish-httpclient-attrs (push) Blocked by required conditions
.NET / nuget-publish-interfacemock-plugin (push) Blocked by required conditions
Some checks are pending
.NET / build (Release) (push) Waiting to run
.NET / analyzers (push) Waiting to run
.NET / build (Debug) (push) Waiting to run
.NET / check-dotnet-format (push) Waiting to run
.NET / check-nix-format (push) Waiting to run
.NET / Check links (push) Waiting to run
.NET / Check flake (push) Waiting to run
.NET / nuget-pack (push) Waiting to run
.NET / expected-pack (push) Blocked by required conditions
.NET / check-accurate-generations (push) Waiting to run
.NET / all-required-checks-complete (push) Blocked by required conditions
.NET / nuget-publish (push) Blocked by required conditions
.NET / nuget-publish-fantomas (push) Blocked by required conditions
.NET / nuget-publish-json-plugin (push) Blocked by required conditions
.NET / nuget-publish-json-attrs (push) Blocked by required conditions
.NET / nuget-publish-argparser-plugin (push) Blocked by required conditions
.NET / nuget-publish-argparser-attrs (push) Blocked by required conditions
.NET / nuget-publish-httpclient-plugin (push) Blocked by required conditions
.NET / nuget-publish-httpclient-attrs (push) Blocked by required conditions
.NET / nuget-publish-interfacemock-plugin (push) Blocked by required conditions
This commit is contained in:
47
.github/workflows/dotnet.yaml
vendored
47
.github/workflows/dotnet.yaml
vendored
@@ -190,6 +190,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: nuget-package-httpclient
|
name: nuget-package-httpclient
|
||||||
path: Plugins/HttpClient/WoofWare.Whippet.Plugin.HttpClient/bin/Release/WoofWare.Whippet.Plugin.HttpClient.*.nupkg
|
path: Plugins/HttpClient/WoofWare.Whippet.Plugin.HttpClient/bin/Release/WoofWare.Whippet.Plugin.HttpClient.*.nupkg
|
||||||
|
- name: Upload NuGet artifact (interfacemock plugin)
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-interfacemock
|
||||||
|
path: Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/bin/Release/WoofWare.Whippet.Plugin.InterfaceMock.*.nupkg
|
||||||
|
|
||||||
expected-pack:
|
expected-pack:
|
||||||
needs: [nuget-pack]
|
needs: [nuget-pack]
|
||||||
@@ -267,6 +272,14 @@ jobs:
|
|||||||
- name: Check NuGet contents
|
- name: Check NuGet contents
|
||||||
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
||||||
run: if [[ $(find packed-httpclient -maxdepth 1 -name 'WoofWare.Whippet.Plugin.HttpClient.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
run: if [[ $(find packed-httpclient -maxdepth 1 -name 'WoofWare.Whippet.Plugin.HttpClient.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
||||||
|
- name: Download NuGet artifact (interfacemock plugin)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-interfacemock
|
||||||
|
path: packed-interfacemock
|
||||||
|
- name: Check NuGet contents
|
||||||
|
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
||||||
|
run: if [[ $(find packed-interfacemock -maxdepth 1 -name 'WoofWare.Whippet.Plugin.InterfaceMock.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
||||||
|
|
||||||
check-accurate-generations:
|
check-accurate-generations:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -565,3 +578,37 @@ jobs:
|
|||||||
nuget-key: ${{ secrets.NUGET_API_KEY }}
|
nuget-key: ${{ secrets.NUGET_API_KEY }}
|
||||||
nupkg-dir: packed/
|
nupkg-dir: packed/
|
||||||
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
|
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
|
||||||
|
|
||||||
|
nuget-publish-interfacemock-plugin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
|
needs: [all-required-checks-complete]
|
||||||
|
environment: main-deploy
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v29
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download NuGet artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-interfacemock
|
||||||
|
path: packed
|
||||||
|
- name: Identify `dotnet`
|
||||||
|
id: dotnet-identify
|
||||||
|
run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
|
||||||
|
- name: Publish to NuGet
|
||||||
|
id: publish-success
|
||||||
|
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
|
||||||
|
with:
|
||||||
|
package-name: WoofWare.Whippet.Plugin.InterfaceMock
|
||||||
|
nuget-key: ${{ secrets.NUGET_API_KEY }}
|
||||||
|
nupkg-dir: packed/
|
||||||
|
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
|
||||||
|
|
||||||
|
@@ -7,6 +7,8 @@
|
|||||||
"./",
|
"./",
|
||||||
":/Plugins/Json/WoofWare.Whippet.Plugin.Json",
|
":/Plugins/Json/WoofWare.Whippet.Plugin.Json",
|
||||||
"!:/Plugins/Json/WoofWare.Whippet.Plugin.Json/WoofWare.Whippet.Plugin.Json.Test/",
|
"!:/Plugins/Json/WoofWare.Whippet.Plugin.Json/WoofWare.Whippet.Plugin.Json.Test/",
|
||||||
|
":/WoofWare.Whippet.Core/",
|
||||||
|
":/WoofWare.Whippet.Fantomas/",
|
||||||
":/global.json",
|
":/global.json",
|
||||||
":/Directory.Build.props"
|
":/Directory.Build.props"
|
||||||
]
|
]
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
namespace WoofWare.Whippet.Plugin.InterfaceMock.Consumer
|
||||||
|
|
||||||
|
[<assembly : System.Runtime.CompilerServices.InternalsVisibleTo("WoofWare.Whippet.Plugin.InterfaceMock.Test")>]
|
||||||
|
do ()
|
@@ -0,0 +1,195 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type public PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeInternalFalseNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InternalTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeInternalFalseNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 : 'a -> 'b
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VeryPublicTypeNoAttr<'a, 'b> with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 : int -> 'a -> string
|
||||||
|
Mem2 : int * string -> 'a -> string
|
||||||
|
Mem3 : (int * string) -> 'a -> string
|
||||||
|
Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
Mem5 : int * string -> ('a * int) -> string
|
||||||
|
Mem6 : int * string -> 'a * int -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
|
||||||
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
|
||||||
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedNoAttr<'a> with
|
||||||
|
member this.Mem1 arg_0_0 arg_1_0 = this.Mem1 (arg_0_0) (arg_1_0)
|
||||||
|
member this.Mem2 (arg_0_0, arg_0_1) arg_1_0 = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
member this.Mem3 ((arg_0_0, arg_0_1)) arg_1_0 = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
|
||||||
|
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem5 (arg_0_0, arg_0_1) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem5 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
|
||||||
|
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
/// Implementation of IDisposable.Dispose
|
||||||
|
Dispose : unit -> unit
|
||||||
|
Mem1 : string option -> string[] Async
|
||||||
|
Mem2 : unit -> string[] Async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun () -> ())
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithInterfaceNoAttr with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
member this.Mem2 () = this.Mem2 (())
|
||||||
|
|
||||||
|
interface System.IDisposable with
|
||||||
|
member this.Dispose () : unit = this.Dispose ()
|
@@ -0,0 +1,195 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal PublicTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicType with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type public PublicTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeInternalFalse with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal InternalTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : InternalTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InternalType with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateType with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeInternalFalse with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal VeryPublicTypeMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 : 'a -> 'b
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : VeryPublicTypeMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VeryPublicType<'a, 'b> with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal CurriedMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 : int -> 'a -> string
|
||||||
|
Mem2 : int * string -> 'a -> string
|
||||||
|
Mem3 : (int * string) -> 'a -> string
|
||||||
|
Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
Mem5 : int * string -> ('a * int) -> string
|
||||||
|
Mem6 : int * string -> 'a * int -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : CurriedMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
|
||||||
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
|
||||||
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Curried<'a> with
|
||||||
|
member this.Mem1 arg_0_0 arg_1_0 = this.Mem1 (arg_0_0) (arg_1_0)
|
||||||
|
member this.Mem2 (arg_0_0, arg_0_1) arg_1_0 = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
member this.Mem3 ((arg_0_0, arg_0_1)) arg_1_0 = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
|
||||||
|
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem5 (arg_0_0, arg_0_1) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem5 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
|
||||||
|
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithInterfaceMock =
|
||||||
|
{
|
||||||
|
/// Implementation of IDisposable.Dispose
|
||||||
|
Dispose : unit -> unit
|
||||||
|
Mem1 : string option -> string[] Async
|
||||||
|
Mem2 : unit -> string[] Async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : TypeWithInterfaceMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun () -> ())
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithInterface with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
member this.Mem2 () = this.Mem2 (())
|
||||||
|
|
||||||
|
interface System.IDisposable with
|
||||||
|
member this.Dispose () : unit = this.Dispose ()
|
@@ -0,0 +1,41 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
type IPublicTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type IPublicTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type internal InternalTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type VeryPublicTypeNoAttr<'a, 'b> =
|
||||||
|
abstract Mem1 : 'a -> 'b
|
||||||
|
|
||||||
|
type CurriedNoAttr<'a> =
|
||||||
|
abstract Mem1 : int -> 'a -> string
|
||||||
|
abstract Mem2 : int * string -> 'a -> string
|
||||||
|
abstract Mem3 : (int * string) -> 'a -> string
|
||||||
|
abstract Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
abstract Mem5 : x : int * string -> ('a * int) -> string
|
||||||
|
abstract Mem6 : int * string -> y : 'a * int -> string
|
||||||
|
|
||||||
|
type TypeWithInterfaceNoAttr =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Mem2 : unit -> string[] Async
|
@@ -0,0 +1,41 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
type IPublicType =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type IPublicTypeInternalFalse =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type internal InternalType =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateType =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeInternalFalse =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type VeryPublicType<'a, 'b> =
|
||||||
|
abstract Mem1 : 'a -> 'b
|
||||||
|
|
||||||
|
type Curried<'a> =
|
||||||
|
abstract Mem1 : int -> 'a -> string
|
||||||
|
abstract Mem2 : int * string -> 'a -> string
|
||||||
|
abstract Mem3 : (int * string) -> 'a -> string
|
||||||
|
abstract Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
abstract Mem5 : x : int * string -> ('a * int) -> string
|
||||||
|
abstract Mem6 : int * string -> y : 'a * int -> string
|
||||||
|
|
||||||
|
type TypeWithInterface =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Mem2 : unit -> string[] Async
|
@@ -0,0 +1,45 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="AssemblyInfo.fs" />
|
||||||
|
<Compile Include="MockExample.fs" />
|
||||||
|
<Compile Include="GeneratedMockExample.fs">
|
||||||
|
<WhippetFile>MockExample.fs</WhippetFile>
|
||||||
|
<WhippetParamIPublicTypeNoAttr>InterfaceMock</WhippetParamIPublicTypeNoAttr>
|
||||||
|
<WhippetParamIPublicTypeNoAttr>InterfaceMock</WhippetParamIPublicTypeNoAttr>
|
||||||
|
<WhippetParamIPublicTypeInternalFalseNoAttr>InterfaceMock(false)</WhippetParamIPublicTypeInternalFalseNoAttr>
|
||||||
|
<WhippetParamInternalTypeNoAttr>InterfaceMock</WhippetParamInternalTypeNoAttr>
|
||||||
|
<WhippetParamPrivateTypeNoAttr>InterfaceMock</WhippetParamPrivateTypeNoAttr>
|
||||||
|
<WhippetParamPrivateTypeInternalFalseNoAttr>InterfaceMock(false)</WhippetParamPrivateTypeInternalFalseNoAttr>
|
||||||
|
<WhippetParamVeryPublicTypeNoAttr>InterfaceMock</WhippetParamVeryPublicTypeNoAttr>
|
||||||
|
<WhippetParamCurriedNoAttr>InterfaceMock</WhippetParamCurriedNoAttr>
|
||||||
|
<WhippetParamTypeWithInterfaceNoAttr>InterfaceMock</WhippetParamTypeWithInterfaceNoAttr>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MockExample2.fs" />
|
||||||
|
<Compile Include="GeneratedMockExample2.fs">
|
||||||
|
<WhippetFile>MockExample2.fs</WhippetFile>
|
||||||
|
<WhippetParamIPublicType>InterfaceMock</WhippetParamIPublicType>
|
||||||
|
<WhippetParamIPublicTypeInternalFalse>InterfaceMock(false)</WhippetParamIPublicTypeInternalFalse>
|
||||||
|
<WhippetParamInternalType>InterfaceMock</WhippetParamInternalType>
|
||||||
|
<WhippetParamPrivateType>InterfaceMock</WhippetParamPrivateType>
|
||||||
|
<WhippetParamPrivateTypeInternalFalse>InterfaceMock(false)</WhippetParamPrivateTypeInternalFalse>
|
||||||
|
<WhippetParamVeryPublicType>InterfaceMock</WhippetParamVeryPublicType>
|
||||||
|
<WhippetParamCurried>InterfaceMock</WhippetParamCurried>
|
||||||
|
<WhippetParamTypeWithInterface>InterfaceMock</WhippetParamTypeWithInterface>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WoofWare.Whippet.Plugin.InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.fsproj" WhippetPlugin="true" />
|
||||||
|
<!-- Dance to get a binary dependency on a locally-built Whippet -->
|
||||||
|
<!-- ProjectReference Include="..\..\..\WoofWare.Whippet\WoofWare.Whippet.fsproj" PrivateAssets="all" -->
|
||||||
|
<PackageReference Include="WoofWare.Whippet" Version="*-*" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@@ -0,0 +1,11 @@
|
|||||||
|
namespace WoofWare.Whippet.Plugin.InterfaceMock
|
||||||
|
|
||||||
|
type internal DesiredGenerator =
|
||||||
|
| InterfaceMock of isInternal : bool option
|
||||||
|
|
||||||
|
static member Parse (s : string) =
|
||||||
|
match s with
|
||||||
|
| "InterfaceMock" -> DesiredGenerator.InterfaceMock None |> Some
|
||||||
|
| "InterfaceMock(true)" -> DesiredGenerator.InterfaceMock (Some true) |> Some
|
||||||
|
| "InterfaceMock(false)" -> DesiredGenerator.InterfaceMock (Some false) |> Some
|
||||||
|
| _ -> None
|
@@ -0,0 +1,333 @@
|
|||||||
|
namespace WoofWare.Whippet.Plugin.InterfaceMock
|
||||||
|
|
||||||
|
open System
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open WoofWare.Whippet.Core
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
|
type internal GenerateMockOutputSpec =
|
||||||
|
{
|
||||||
|
IsInternal : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal InterfaceMockGenerator =
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
let private getName (SynField (_, _, id, _, _, _, _, _, _)) =
|
||||||
|
match id with
|
||||||
|
| None -> failwith "Expected record field to have a name, but it was somehow anonymous"
|
||||||
|
| Some id -> id
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private KnownInheritance = | IDisposable
|
||||||
|
|
||||||
|
let createType
|
||||||
|
(spec : GenerateMockOutputSpec)
|
||||||
|
(name : string)
|
||||||
|
(interfaceType : InterfaceType)
|
||||||
|
(xmlDoc : PreXmlDoc)
|
||||||
|
(fields : SynField list)
|
||||||
|
: SynModuleDecl
|
||||||
|
=
|
||||||
|
let inherits =
|
||||||
|
interfaceType.Inherits
|
||||||
|
|> Seq.map (fun ty ->
|
||||||
|
match ty with
|
||||||
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (name, _, _)) ->
|
||||||
|
match name |> List.map _.idText with
|
||||||
|
| [] -> failwith "Unexpected empty identifier in inheritance declaration"
|
||||||
|
| [ "IDisposable" ]
|
||||||
|
| [ "System" ; "IDisposable" ] -> KnownInheritance.IDisposable
|
||||||
|
| _ -> failwithf "Unrecognised inheritance identifier: %+A" name
|
||||||
|
| x -> failwithf "Unrecognised type in inheritance: %+A" x
|
||||||
|
)
|
||||||
|
|> Set.ofSeq
|
||||||
|
|
||||||
|
let failwithFun (SynField (_, _, idOpt, _, _, _, _, _, _)) =
|
||||||
|
let failString =
|
||||||
|
match idOpt with
|
||||||
|
| None -> SynExpr.CreateConst "Unimplemented mock function"
|
||||||
|
| Some ident -> SynExpr.CreateConst $"Unimplemented mock function: %s{ident.idText}"
|
||||||
|
|
||||||
|
SynExpr.createLongIdent [ "System" ; "NotImplementedException" ]
|
||||||
|
|> SynExpr.applyTo failString
|
||||||
|
|> SynExpr.paren
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createIdent "raise")
|
||||||
|
|> SynExpr.createLambda "_"
|
||||||
|
|
||||||
|
let constructorReturnType =
|
||||||
|
match interfaceType.Generics with
|
||||||
|
| None -> SynType.createLongIdent' [ name ]
|
||||||
|
| Some generics ->
|
||||||
|
|
||||||
|
let generics =
|
||||||
|
generics.TyparDecls
|
||||||
|
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
|
||||||
|
|
||||||
|
SynType.app name generics
|
||||||
|
|
||||||
|
let constructorFields =
|
||||||
|
let extras =
|
||||||
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
|
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
|
||||||
|
|
||||||
|
[ SynLongIdent.createS "Dispose", unitFun ]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
let nonExtras =
|
||||||
|
fields
|
||||||
|
|> List.map (fun field -> SynLongIdent.createI (getName field), failwithFun field)
|
||||||
|
|
||||||
|
extras @ nonExtras
|
||||||
|
|
||||||
|
let constructor =
|
||||||
|
SynBinding.basic
|
||||||
|
[ Ident.create "Empty" ]
|
||||||
|
(if interfaceType.Generics.IsNone then
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[ SynPat.unit ])
|
||||||
|
(AstHelper.instantiateRecord constructorFields)
|
||||||
|
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|
||||||
|
|> SynBinding.withReturnAnnotation constructorReturnType
|
||||||
|
|> SynMemberDefn.staticMember
|
||||||
|
|
||||||
|
let fields =
|
||||||
|
let extras =
|
||||||
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
|
{
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some (Ident.create "Dispose")
|
||||||
|
Type = SynType.funFromDomain SynType.unit SynType.unit
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|
||||||
|
|> List.singleton
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
extras @ fields
|
||||||
|
|
||||||
|
let interfaceMembers =
|
||||||
|
let members =
|
||||||
|
interfaceType.Members
|
||||||
|
|> List.map (fun memberInfo ->
|
||||||
|
let headArgs =
|
||||||
|
memberInfo.Args
|
||||||
|
|> List.mapi (fun i tupledArgs ->
|
||||||
|
let args =
|
||||||
|
tupledArgs.Args
|
||||||
|
|> List.mapi (fun j ty ->
|
||||||
|
match ty.Type with
|
||||||
|
| UnitType -> SynPat.unit
|
||||||
|
| _ -> SynPat.named $"arg_%i{i}_%i{j}"
|
||||||
|
)
|
||||||
|
|
||||||
|
match args with
|
||||||
|
| [] -> failwith "somehow got no args at all"
|
||||||
|
| [ arg ] -> arg
|
||||||
|
| args -> SynPat.tuple args
|
||||||
|
|> fun i -> if tupledArgs.HasParen then SynPat.paren i else i
|
||||||
|
)
|
||||||
|
|
||||||
|
let body =
|
||||||
|
let tuples =
|
||||||
|
memberInfo.Args
|
||||||
|
|> List.mapi (fun i args ->
|
||||||
|
args.Args
|
||||||
|
|> List.mapi (fun j arg ->
|
||||||
|
match arg.Type with
|
||||||
|
| UnitType -> SynExpr.CreateConst ()
|
||||||
|
| _ -> SynExpr.createIdent $"arg_%i{i}_%i{j}"
|
||||||
|
)
|
||||||
|
|> SynExpr.tuple
|
||||||
|
)
|
||||||
|
|
||||||
|
match tuples |> List.rev with
|
||||||
|
| [] -> failwith "expected args but got none"
|
||||||
|
| last :: rest ->
|
||||||
|
|
||||||
|
(last, rest)
|
||||||
|
||> List.fold SynExpr.applyTo
|
||||||
|
|> SynExpr.applyFunction (
|
||||||
|
SynExpr.createLongIdent' [ Ident.create "this" ; memberInfo.Identifier ]
|
||||||
|
)
|
||||||
|
|
||||||
|
SynBinding.basic [ Ident.create "this" ; memberInfo.Identifier ] headArgs body
|
||||||
|
|> SynMemberDefn.memberImplementation
|
||||||
|
)
|
||||||
|
|
||||||
|
let interfaceName =
|
||||||
|
let baseName = SynType.createLongIdent interfaceType.Name
|
||||||
|
|
||||||
|
match interfaceType.Generics with
|
||||||
|
| None -> baseName
|
||||||
|
| Some generics ->
|
||||||
|
let generics =
|
||||||
|
match generics with
|
||||||
|
| SynTyparDecls.PostfixList (decls, _, _) -> decls
|
||||||
|
| SynTyparDecls.PrefixList (decls, _) -> decls
|
||||||
|
| SynTyparDecls.SinglePrefix (decl, _) -> [ decl ]
|
||||||
|
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
|
||||||
|
|
||||||
|
SynType.app' baseName generics
|
||||||
|
|
||||||
|
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
|
||||||
|
|
||||||
|
let access =
|
||||||
|
match interfaceType.Accessibility, spec.IsInternal with
|
||||||
|
| Some (SynAccess.Public _), true
|
||||||
|
| None, true -> SynAccess.Internal range0
|
||||||
|
| Some (SynAccess.Public _), false -> SynAccess.Public range0
|
||||||
|
| None, false -> SynAccess.Public range0
|
||||||
|
| Some (SynAccess.Internal _), _ -> SynAccess.Internal range0
|
||||||
|
| Some (SynAccess.Private _), _ -> SynAccess.Private range0
|
||||||
|
|
||||||
|
let extraInterfaces =
|
||||||
|
inherits
|
||||||
|
|> Seq.map (fun inheritance ->
|
||||||
|
match inheritance with
|
||||||
|
| KnownInheritance.IDisposable ->
|
||||||
|
let mem =
|
||||||
|
SynExpr.createLongIdent [ "this" ; "Dispose" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
|
|> SynBinding.basic [ Ident.create "this" ; Ident.create "Dispose" ] [ SynPat.unit ]
|
||||||
|
|> SynBinding.withReturnAnnotation SynType.unit
|
||||||
|
|> SynMemberDefn.memberImplementation
|
||||||
|
|
||||||
|
SynMemberDefn.Interface (
|
||||||
|
SynType.createLongIdent' [ "System" ; "IDisposable" ],
|
||||||
|
Some range0,
|
||||||
|
Some [ mem ],
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let record =
|
||||||
|
{
|
||||||
|
Name = Ident.create name
|
||||||
|
Fields = fields
|
||||||
|
Members = Some ([ constructor ; interfaceMembers ] @ extraInterfaces)
|
||||||
|
XmlDoc = Some xmlDoc
|
||||||
|
Generics = interfaceType.Generics
|
||||||
|
TypeAccessibility = Some access
|
||||||
|
ImplAccessibility = None
|
||||||
|
Attributes = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let typeDecl = RecordType.ToAst record
|
||||||
|
|
||||||
|
SynModuleDecl.Types ([ typeDecl ], range0)
|
||||||
|
|
||||||
|
let private buildType (x : ParameterInfo) : SynType =
|
||||||
|
if x.IsOptional then
|
||||||
|
SynType.app "option" [ x.Type ]
|
||||||
|
else
|
||||||
|
x.Type
|
||||||
|
|
||||||
|
let private constructMemberSinglePlace (tuple : TupledArg) : SynType =
|
||||||
|
tuple.Args
|
||||||
|
|> List.map buildType
|
||||||
|
|> SynType.tupleNoParen
|
||||||
|
|> Option.defaultWith (fun () -> failwith "no-arg functions not supported yet")
|
||||||
|
|> if tuple.HasParen then SynType.paren else id
|
||||||
|
|
||||||
|
let constructMember (mem : MemberInfo) : SynField =
|
||||||
|
let inputType = mem.Args |> List.map constructMemberSinglePlace
|
||||||
|
|
||||||
|
let funcType = SynType.toFun inputType mem.ReturnType
|
||||||
|
|
||||||
|
{
|
||||||
|
Type = funcType
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some mem.Identifier
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> SynField.withDocString (mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
|
||||||
|
|
||||||
|
let createRecord
|
||||||
|
(namespaceId : LongIdent)
|
||||||
|
(opens : SynOpenDeclTarget list)
|
||||||
|
(interfaceType : SynTypeDefn, spec : GenerateMockOutputSpec)
|
||||||
|
: SynModuleOrNamespace
|
||||||
|
=
|
||||||
|
let interfaceType = AstHelper.parseInterface interfaceType
|
||||||
|
let fields = interfaceType.Members |> List.map constructMember
|
||||||
|
let docString = PreXmlDoc.create "Mock record type for an interface"
|
||||||
|
|
||||||
|
let name =
|
||||||
|
List.last interfaceType.Name
|
||||||
|
|> _.idText
|
||||||
|
|> fun s ->
|
||||||
|
if s.StartsWith 'I' && s.Length > 1 && Char.IsUpper s.[1] then
|
||||||
|
s.Substring 1
|
||||||
|
else
|
||||||
|
s
|
||||||
|
|> fun s -> s + "Mock"
|
||||||
|
|
||||||
|
let typeDecl = createType spec name interfaceType docString fields
|
||||||
|
|
||||||
|
[ yield! opens |> List.map SynModuleDecl.openAny ; yield typeDecl ]
|
||||||
|
|> SynModuleOrNamespace.createNamespace namespaceId
|
||||||
|
|
||||||
|
/// Myriad generator that creates a record which implements the given interface,
|
||||||
|
/// but with every field mocked out.
|
||||||
|
[<WhippetGenerator>]
|
||||||
|
type InterfaceMockGenerator () =
|
||||||
|
|
||||||
|
interface IGenerateRawFromRaw with
|
||||||
|
member _.GenerateRawFromRaw (context : RawSourceGenerationArgs) =
|
||||||
|
if not (context.FilePath.EndsWith (".fs", StringComparison.Ordinal)) then
|
||||||
|
null
|
||||||
|
else
|
||||||
|
|
||||||
|
let targetedTypes =
|
||||||
|
context.Parameters
|
||||||
|
|> Seq.map (fun (KeyValue (k, v)) -> k, v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let ast = Ast.parse (System.Text.Encoding.UTF8.GetString context.FileContents)
|
||||||
|
|
||||||
|
let types = Ast.getTypes ast
|
||||||
|
|
||||||
|
let namespaceAndInterfaces =
|
||||||
|
types
|
||||||
|
|> List.choose (fun (ns, types) ->
|
||||||
|
types
|
||||||
|
|> List.choose (fun typeDef ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| Some (DesiredGenerator.InterfaceMock arg) ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
IsInternal = arg |> Option.defaultValue true
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
|> function
|
||||||
|
| [] -> None
|
||||||
|
| ty -> Some (ns, ty)
|
||||||
|
)
|
||||||
|
|
||||||
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
|
let modules =
|
||||||
|
namespaceAndInterfaces
|
||||||
|
|> List.collect (fun (ns, records) ->
|
||||||
|
records |> List.map (InterfaceMockGenerator.createRecord ns opens)
|
||||||
|
)
|
||||||
|
|
||||||
|
Ast.render modules |> Option.toObj
|
@@ -0,0 +1,82 @@
|
|||||||
|
# WoofWare.Whippet.Plugin.InterfaceMock
|
||||||
|
|
||||||
|
This is a [Whippet](https://github.com/Smaug123/WoofWare.Whippet) plugin for defining mocks for interfaces.
|
||||||
|
|
||||||
|
It is a copy of the corresponding [Myriad](https://github.com/MoiraeSoftware/myriad) HttpClient plugin in [WoofWare.Myriad](https://github.com/Smaug123/WoofWare.Myriad), taken from commit d59ebdfccb87a06579fb99008a15f58ea8be394e.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Define a file like `Client.fs`:
|
||||||
|
|
||||||
|
```fsharp
|
||||||
|
type IPublicType =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
```
|
||||||
|
|
||||||
|
In your fsproj:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Client.fs" />
|
||||||
|
<Compile Include="GeneratedClient.fs">
|
||||||
|
<WhippetFile>Client.fs</WhippetFile>
|
||||||
|
<WhippetParamIPublicType>InterfaceMock</WhippetParamIPublicType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Development dependencies, hence PrivateAssets="all". Note `WhippetPlugin="true"`. -->
|
||||||
|
<PackageReference Include="WoofWare.Whippet.Plugin.InterfaceMock" WhippetPlugin="true" Version="" />
|
||||||
|
<PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
```
|
||||||
|
|
||||||
|
(This plugin follows a standard convention taken by `WoofWare.Whippet.Plugin` plugins,
|
||||||
|
where you use Whippet parameters with the same name as each input type,
|
||||||
|
whose contents are a `!`-delimited list of the generators which you wish to apply to that input type.)
|
||||||
|
|
||||||
|
The generator produces a type like this:
|
||||||
|
|
||||||
|
```fsharp
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal PublicTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
static member Empty : PublicTypeMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicType with
|
||||||
|
member this.Mem1 (arg0, arg1) = this.Mem1 (arg0, arg1)
|
||||||
|
member this.Mem2 (arg0) = this.Mem2 (arg0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's the point?
|
||||||
|
|
||||||
|
Reflective mocking libraries like [Foq](https://github.com/fsprojects/Foq) in my experience are a rich source of flaky tests.
|
||||||
|
The [Grug-brained developer](https://grugbrain.dev/) would prefer to do this without reflection, and this reduces the rate of strange one-in-ten-thousand "failed to generate IL" errors.
|
||||||
|
But since F# does not let you partially update an interface definition, we instead stamp out a record,
|
||||||
|
thereby allowing the programmer to use F#'s record-update syntax.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
You may supply an `isInternal : bool` argument:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Compile Include="GeneratedClient.fs">
|
||||||
|
<WhippetFile>Client.fs</WhippetFile>
|
||||||
|
<WhippetParamIPublicType>InterfaceMock(false)</WhippetParamIPublicType>
|
||||||
|
</Compile>
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, we make the resulting record type at most internal (never public),
|
||||||
|
since this is intended only to be used in tests;
|
||||||
|
but you can instead make it public by setting the `false` boolean.
|
@@ -0,0 +1,36 @@
|
|||||||
|
namespace WoofWare.Whippet.Plugin.InterfaceMock.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open SomeNamespace
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMockGenerator =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: IPublicType`` () =
|
||||||
|
let mock : IPublicType =
|
||||||
|
{ PublicTypeMock.Empty with
|
||||||
|
Mem1 = fun (s, count) -> List.replicate count s
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
Assert.Throws<NotImplementedException> (fun () -> mock.Mem2 "hi" |> ignore<int>)
|
||||||
|
|
||||||
|
mock.Mem1 ("hi", 3) |> shouldEqual [ "hi" ; "hi" ; "hi" ]
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: curried args`` () =
|
||||||
|
let mock : Curried<_> =
|
||||||
|
{ CurriedMock.Empty () with
|
||||||
|
Mem1 = fun i c -> Array.replicate i c |> String
|
||||||
|
Mem2 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
Mem3 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
||||||
|
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
|
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
@@ -0,0 +1,36 @@
|
|||||||
|
namespace WoofWare.Whippet.Plugin.InterfaceMock.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open SomeNamespace
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMockGeneratorNoAttr =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: IPublicType`` () =
|
||||||
|
let mock : IPublicTypeNoAttr =
|
||||||
|
{ PublicTypeNoAttrMock.Empty with
|
||||||
|
Mem1 = fun (s, count) -> List.replicate count s
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
Assert.Throws<NotImplementedException> (fun () -> mock.Mem2 "hi" |> ignore<int>)
|
||||||
|
|
||||||
|
mock.Mem1 ("hi", 3) |> shouldEqual [ "hi" ; "hi" ; "hi" ]
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: curried args`` () =
|
||||||
|
let mock : CurriedNoAttr<_> =
|
||||||
|
{ CurriedNoAttrMock.Empty () with
|
||||||
|
Mem1 = fun i c -> Array.replicate i c |> String
|
||||||
|
Mem2 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
Mem3 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
||||||
|
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
|
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="TestMockGenerator.fs" />
|
||||||
|
<Compile Include="TestMockGeneratorNoAttr.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FsUnit" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
|
||||||
|
<PackageReference Include="NUnit" Version="4.2.2"/>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\WoofWare.Whippet.Plugin.InterfaceMock.Consumer\WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@@ -0,0 +1,38 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<Authors>Patrick Stevens</Authors>
|
||||||
|
<Copyright>Copyright (c) Patrick Stevens 2024</Copyright>
|
||||||
|
<Description>Whippet F# source generator plugin, for generating records that mock an interface.</Description>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<RepositoryUrl>https://github.com/Smaug123/WoofWare.Whippet</RepositoryUrl>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageTags>fsharp;source-generator;source-gen;whippet;mock</PackageTags>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<WarnOn>FS3559</WarnOn>
|
||||||
|
<PackageId>WoofWare.Whippet.Plugin.InterfaceMock</PackageId>
|
||||||
|
<DevelopmentDependency>true</DevelopmentDependency>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<NoWarn>NU5118</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="DesiredGenerator.fs" />
|
||||||
|
<Compile Include="InterfaceMockGenerator.fs" />
|
||||||
|
<None Include="README.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>/</PackagePath>
|
||||||
|
<Link>README.md</Link>
|
||||||
|
</None>
|
||||||
|
<EmbeddedResource Include="version.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\WoofWare.Whippet.Core\WoofWare.Whippet.Core.fsproj" />
|
||||||
|
<ProjectReference Include="..\..\..\WoofWare.Whippet.Fantomas\WoofWare.Whippet.Fantomas.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "0.1",
|
||||||
|
"publicReleaseRefSpec": [
|
||||||
|
"^refs/heads/main$"
|
||||||
|
],
|
||||||
|
"pathFilters": [
|
||||||
|
"./",
|
||||||
|
":/WoofWare.Whippet.Core/",
|
||||||
|
":/WoofWare.Whippet.Fantomas/",
|
||||||
|
":/global.json",
|
||||||
|
":/Directory.Build.props"
|
||||||
|
]
|
||||||
|
}
|
@@ -6,10 +6,10 @@ type internal DesiredGenerator =
|
|||||||
|
|
||||||
static member Parse (s : string) =
|
static member Parse (s : string) =
|
||||||
match s with
|
match s with
|
||||||
| "JsonParse" -> DesiredGenerator.JsonParse None
|
| "JsonParse" -> DesiredGenerator.JsonParse None |> Some
|
||||||
| "JsonParse(true)" -> DesiredGenerator.JsonParse (Some true)
|
| "JsonParse(true)" -> DesiredGenerator.JsonParse (Some true) |> Some
|
||||||
| "JsonParse(false)" -> DesiredGenerator.JsonParse (Some false)
|
| "JsonParse(false)" -> DesiredGenerator.JsonParse (Some false) |> Some
|
||||||
| "JsonSerialize" -> DesiredGenerator.JsonSerialize None
|
| "JsonSerialize" -> DesiredGenerator.JsonSerialize None |> Some
|
||||||
| "JsonSerialize(true)" -> DesiredGenerator.JsonSerialize (Some true)
|
| "JsonSerialize(true)" -> DesiredGenerator.JsonSerialize (Some true) |> Some
|
||||||
| "JsonSerialize(false)" -> DesiredGenerator.JsonSerialize (Some false)
|
| "JsonSerialize(false)" -> DesiredGenerator.JsonSerialize (Some false) |> Some
|
||||||
| _ -> failwith $"Failed to parse as a generator specification: %s{s}"
|
| _ -> None
|
||||||
|
@@ -739,7 +739,7 @@ type JsonParseGenerator () =
|
|||||||
desired
|
desired
|
||||||
|> List.tryPick (fun generator ->
|
|> List.tryPick (fun generator ->
|
||||||
match generator with
|
match generator with
|
||||||
| DesiredGenerator.JsonParse arg ->
|
| Some (DesiredGenerator.JsonParse arg) ->
|
||||||
let spec =
|
let spec =
|
||||||
{
|
{
|
||||||
ExtensionMethods =
|
ExtensionMethods =
|
||||||
|
@@ -555,7 +555,7 @@ type JsonSerializeGenerator () =
|
|||||||
desired
|
desired
|
||||||
|> List.tryPick (fun generator ->
|
|> List.tryPick (fun generator ->
|
||||||
match generator with
|
match generator with
|
||||||
| DesiredGenerator.JsonSerialize arg ->
|
| Some (DesiredGenerator.JsonSerialize arg) ->
|
||||||
let spec =
|
let spec =
|
||||||
{
|
{
|
||||||
ExtensionMethods =
|
ExtensionMethods =
|
||||||
|
@@ -36,6 +36,12 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.Htt
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.HttpClient.Test", "Plugins\HttpClient\WoofWare.Whippet.Plugin.HttpClient\WoofWare.Whippet.Plugin.HttpClient.Test\WoofWare.Whippet.Plugin.HttpClient.Test.fsproj", "{4DDD15F1-F273-441B-92F3-76BD9C089529}"
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.HttpClient.Test", "Plugins\HttpClient\WoofWare.Whippet.Plugin.HttpClient\WoofWare.Whippet.Plugin.HttpClient.Test\WoofWare.Whippet.Plugin.HttpClient.Test.fsproj", "{4DDD15F1-F273-441B-92F3-76BD9C089529}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.InterfaceMock", "Plugins\InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.fsproj", "{C69E6668-87D9-4371-ACB3-2E671938A598}"
|
||||||
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.InterfaceMock.Consumer", "Plugins\InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.Consumer\WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj", "{65C9BCE0-00CD-4CAE-AB0F-A9194B34ABAA}"
|
||||||
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.InterfaceMock.Test", "Plugins\InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.Test\WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj", "{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -114,5 +120,17 @@ Global
|
|||||||
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4DDD15F1-F273-441B-92F3-76BD9C089529}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C69E6668-87D9-4371-ACB3-2E671938A598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C69E6668-87D9-4371-ACB3-2E671938A598}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C69E6668-87D9-4371-ACB3-2E671938A598}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C69E6668-87D9-4371-ACB3-2E671938A598}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{65C9BCE0-00CD-4CAE-AB0F-A9194B34ABAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{65C9BCE0-00CD-4CAE-AB0F-A9194B34ABAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{65C9BCE0-00CD-4CAE-AB0F-A9194B34ABAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{65C9BCE0-00CD-4CAE-AB0F-A9194B34ABAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
Reference in New Issue
Block a user