Skip to content

Commit

Permalink
Implement injection for events (fixes #56)
Browse files Browse the repository at this point in the history
  • Loading branch information
mchaloupka committed Jan 24, 2024
1 parent c6f1ae1 commit 6571dec
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFrameworks>net6.0;net452</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="Logger.fs" />
<Compile Include="NunitWiring.fs" />
<Compile Include="StockItem.fs" />
<EmbeddedResource Include="Stock.feature" />
Expand Down
23 changes: 23 additions & 0 deletions Examples/ByFeature/FunctionalInjection/Logger.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Logger

open TickSpec
open System

type LogMessages = string list

type LoggerContext = { Messages: LogMessages }

let [<BeforeScenario>] setup () =
{ Messages = "Before scenario" |> List.singleton }

let [<BeforeStep>] beforeStep (previousMessages: LoggerContext) =
{ Messages = "Before step" :: previousMessages.Messages }

let [<AfterStep>] afterStep (previousMessages: LoggerContext) =
{ Messages = "After step" :: previousMessages.Messages }

let [<AfterScenario>] afterScenario (previousMessages: LoggerContext) =
let allMessages =
"After scenario" :: previousMessages.Messages
|> List.rev
allMessages |> List.iter Console.WriteLine
61 changes: 40 additions & 21 deletions TickSpec/ScenarioGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,38 @@ let emitArgument
else
emitValue gen providerField parsers paramType arg

/// Emit arguments through injection
let emitInjectionArguments
(gen:ILGenerator)
(providerField:FieldBuilder)
(parameters: ParameterInfo array) =
parameters
|> Array.iter (fun p -> emitInstance gen providerField p.ParameterType)

let storeMethodResultInProvider
(gen:ILGenerator)
(providerField:FieldBuilder)
(mi:MethodInfo) =
if mi.ReturnType <> typeof<System.Void> then
gen.Emit(OpCodes.Box,mi.ReturnType)
let local0 = gen.DeclareLocal(typeof<Object>).LocalIndex
gen.Emit(OpCodes.Stloc, local0)

if FSharpType.IsTuple mi.ReturnType then
let types = FSharpType.GetTupleElements mi.ReturnType
for i = 0 to (types.Length - 1) do
let t = types.[i]
let local1 = gen.DeclareLocal(typeof<Object>).LocalIndex

gen.Emit(OpCodes.Ldloc, local0)
gen.Emit(OpCodes.Ldc_I4, i)
gen.EmitCall(OpCodes.Call, typeof<Microsoft.FSharp.Reflection.FSharpValue>.GetMethod("GetTupleField"), null)
gen.Emit(OpCodes.Stloc, local1)

emitRegisterInstanceCall gen t local1 providerField
else
emitRegisterInstanceCall gen (mi.ReturnType) local0 providerField

/// Defines step method
let defineStepMethod
doc
Expand Down Expand Up @@ -442,35 +474,17 @@ let defineStepMethod
let bulletsCount = line.Bullets |> Option.count
let docCount = line.Doc |> Option.count
args.Length + tableCount + bulletsCount + docCount

Array.sub ps a (ps.Length - a)
|> Array.iter (fun (p:ParameterInfo) ->
emitInstance gen providerField p.ParameterType)
|> emitInjectionArguments gen providerField

// Emit method invoke
if mi.IsStatic then
gen.EmitCall(OpCodes.Call, mi, null)
else
gen.Emit(OpCodes.Callvirt, mi)

if mi.ReturnType <> typeof<System.Void> then
gen.Emit(OpCodes.Box,mi.ReturnType)
let local0 = gen.DeclareLocal(typeof<Object>).LocalIndex
gen.Emit(OpCodes.Stloc, local0)

if FSharpType.IsTuple mi.ReturnType then
let types = FSharpType.GetTupleElements mi.ReturnType
for i = 0 to (types.Length - 1) do
let t = types.[i]
let local1 = gen.DeclareLocal(typeof<Object>).LocalIndex

gen.Emit(OpCodes.Ldloc, local0)
gen.Emit(OpCodes.Ldc_I4, i)
gen.EmitCall(OpCodes.Call, typeof<Microsoft.FSharp.Reflection.FSharpValue>.GetMethod("GetTupleField"), null)
gen.Emit(OpCodes.Stloc, local1)

emitRegisterInstanceCall gen t local1 providerField
else
emitRegisterInstanceCall gen (mi.ReturnType) local0 providerField
storeMethodResultInProvider gen providerField mi

// Emit return
gen.Emit(OpCodes.Ret)
Expand Down Expand Up @@ -498,11 +512,16 @@ let defineRunMethod
// Emit event methods
let emitEvents (ms:MethodInfo seq) =
ms |> Seq.iter (fun mi ->
mi.GetParameters()
|> emitInjectionArguments gen providerField

if mi.IsStatic then
gen.EmitCall(OpCodes.Call, mi, null)
else
emitInstance gen providerField mi.DeclaringType
gen.EmitCall(OpCodes.Callvirt, mi, null)

storeMethodResultInProvider gen providerField mi
)

beforeScenarioEvents |> emitEvents
Expand Down
24 changes: 13 additions & 11 deletions TickSpec/ScenarioRun.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ let getInstance (provider:IInstanceProvider) (m:MethodInfo) =
else provider.GetService m.DeclaringType

/// Invokes specified method with specified parameters
let invoke (provider:IInstanceProvider) (m:MethodInfo) ps =
let invoke (provider:IInstanceProvider) (m: MethodInfo) (ps: obj array) =
let injectionArgs =
let pars = m.GetParameters()
let a = ps.Length
Array.sub pars a (pars.Length - a)
|> Array.map (fun (p:ParameterInfo) -> provider.GetService(p.ParameterType))

let args = Array.append ps injectionArgs

let instance = getInstance provider m
let ret = m.Invoke(instance,ps)
let ret = m.Invoke(instance, args)
if m.ReturnType <> typeof<System.Void> then
if FSharpType.IsTuple m.ReturnType then
let types = FSharpType.GetTupleElements m.ReturnType
Expand Down Expand Up @@ -148,15 +156,9 @@ let invokeStep
else failwithf "Expected a Table or array argument at position %d" args.Length
| None,None,Some doc -> [|box doc|]
| _,_,_ -> [||]
let args =
let stArgs = Array.append args tail
let injectionArgs =
let pars = meth.GetParameters()
let a = stArgs.Length
Array.sub pars a (pars.Length - a)
|> Array.map (fun (p:ParameterInfo) -> provider.GetService(p.ParameterType))
Array.append stArgs injectionArgs
invoke provider meth args

Array.append args tail
|> invoke provider meth

/// Generate scenario execution function
let generate events parsers (scenario: ScenarioMetadata, lines) (serviceProviderFactory: Ref<unit -> IInstanceProvider>) =
Expand Down

0 comments on commit 6571dec

Please # to comment.