Skip to content

Commit 429f5b0

Browse files
committed
Add an attribute for discovering custom test locators
1 parent 5410c10 commit 429f5b0

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

Expecto.TestLocator.CompilerService/TestLocator.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ module FCSTestLocator =
326326
return locations |> astLocationsToNameLocationMap
327327
}
328328

329+
[<TestLocator>]
329330
let testLocator (assembly: System.Reflection.Assembly) (sourceFilePath: string) (test: FlatTest) : SourceLocation option =
330331
let locationMap = getTestNameToLocationMapForFile sourceFilePath |> Async.RunSynchronously
331332
locationMap |> Map.tryFind test.name

Expecto/Expecto.fs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,4 +685,33 @@ module TopLevelDefaults =
685685
/// Path to the f# file the test is defined in
686686
type SourceFilePath = string
687687
type TestLocator = System.Reflection.Assembly -> SourceFilePath -> FlatTest -> SourceLocation option
688-
let mutable testLocator : TestLocator = Expecto.Impl.CodeLocation.testLocator
688+
let mutable testLocator : TestLocator = Expecto.Impl.CodeLocation.testLocator
689+
690+
/// Mark the custom TestLocator so the test adapter (i.e. YoloDev.Expecto.TestSdk) can find and use different testLocator implementations
691+
[<AttributeUsage(AttributeTargets.Method ||| AttributeTargets.Property ||| AttributeTargets.Field)>]
692+
type TestLocatorAttribute() = inherit Attribute()
693+
694+
module TestLocatorAttribute =
695+
open System.Reflection
696+
697+
let private methodInfoToTestLocator (mi: MethodInfo) : TopLevelDefaults.TestLocator =
698+
(fun (assembly: Assembly) (sourceFilePath: string) (test: FlatTest) ->
699+
mi.Invoke(null, [|box assembly; box sourceFilePath; box test|]) |> unbox
700+
)
701+
702+
let tryFindLocatorInSingleAssembly (assembly: Assembly) : TopLevelDefaults.TestLocator option =
703+
assembly
704+
|> _.GetExportedTypes()
705+
|> Array.collect (fun t -> t.GetMethods(BindingFlags.Public ||| BindingFlags.Static))
706+
|> Array.filter (fun m -> m.GetCustomAttributes<TestLocatorAttribute>() |> (not << Seq.isEmpty))
707+
|> Array.tryHead
708+
|> Option.map methodInfoToTestLocator
709+
710+
let tryFindTestLocator (assembly: Assembly) : TopLevelDefaults.TestLocator option =
711+
// Prioritize a test locator found in the top assembly
712+
tryFindLocatorInSingleAssembly assembly
713+
|> Option.orElseWith (fun () ->
714+
// otherwise, look for it in referenced assemblies
715+
assembly.GetReferencedAssemblies()
716+
|> Array.tryPick (Assembly.Load >> tryFindLocatorInSingleAssembly)
717+
)

0 commit comments

Comments
 (0)