diff --git a/Mutannot/Mutannot.fsproj b/Mutannot/Mutannot.fsproj
index 3329a09..cd3d012 100644
--- a/Mutannot/Mutannot.fsproj
+++ b/Mutannot/Mutannot.fsproj
@@ -9,4 +9,8 @@
+
+
+
+
diff --git a/Mutannot/Program.fs b/Mutannot/Program.fs
index 8342ab2..9baab14 100644
--- a/Mutannot/Program.fs
+++ b/Mutannot/Program.fs
@@ -1,4 +1,5 @@
open System
+open System.Collections.Generic
open System.Diagnostics
open System.IO
open System.Reflection
@@ -197,50 +198,81 @@ let ensureBuilt options project assemblyPath =
if not (File.Exists assemblyPath) then
fail $"Compiled test assembly not found at {assemblyPath}."
-let installAssemblyResolver (assemblyPath: string) =
+let requireConstructorArgumentString (args: IList) index name =
+ match args[index].Value with
+ | :? string as value when not (isNull value) -> value
+ | null -> fail $"MutationCaseAttribute constructor argument '{name}' must not be null."
+ | value ->
+ fail
+ $"MutationCaseAttribute constructor argument '{name}' had unexpected type '{value.GetType().FullName}'."
+
+let requireConstructorArgumentInt32 (args: IList) index name =
+ match args[index].Value with
+ | :? int as value -> value
+ | null -> fail $"MutationCaseAttribute constructor argument '{name}' must not be null."
+ | value ->
+ fail
+ $"MutationCaseAttribute constructor argument '{name}' had unexpected type '{value.GetType().FullName}'."
+
+let metadataLoadContextPaths (assemblyPath: string) =
let assemblyDir = Path.GetDirectoryName assemblyPath
- AppDomain.CurrentDomain.add_AssemblyResolve (
- ResolveEventHandler(fun _ args ->
- let name = AssemblyName(args.Name).Name + ".dll"
- let candidate = Path.Combine(assemblyDir, name)
+ let runtimeAssemblies =
+ match AppContext.GetData "TRUSTED_PLATFORM_ASSEMBLIES" with
+ | :? string as value when not (String.IsNullOrWhiteSpace value) ->
+ value.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
+ | _ -> fail "Unable to discover trusted platform assemblies for MetadataLoadContext."
- if File.Exists candidate then
- Assembly.LoadFrom candidate
- else
- null)
- )
+ let localAssemblies =
+ seq {
+ yield assemblyPath
+ yield! Directory.EnumerateFiles(assemblyDir, "*.dll")
+ yield! Directory.EnumerateFiles(assemblyDir, "*.exe")
+ }
+
+ Seq.append runtimeAssemblies localAssemblies
+ |> Seq.distinct
+ |> Seq.toArray
+
+let createMetadataLoadContext (assemblyPath: string) =
+ let resolver = PathAssemblyResolver(metadataLoadContextPaths assemblyPath)
+ let coreAssemblyName = typeof.Assembly.GetName().Name
+
+ new MetadataLoadContext(resolver, coreAssemblyName)
let mutationCases options project assemblyPath =
ensureBuilt options project assemblyPath
- installAssemblyResolver assemblyPath
- let asm = Assembly.LoadFrom assemblyPath
+ use mlc = createMetadataLoadContext assemblyPath
+ let asm = mlc.LoadFromAssemblyPath assemblyPath
asm.GetTypes()
|> Array.collect (fun t ->
+ let declaringType =
+ match t.FullName with
+ | null -> t.Name
+ | name -> name
+
t.GetMethods(BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.Static ||| BindingFlags.Instance)
|> Array.collect (fun m ->
m.GetCustomAttributesData()
- |> Seq.filter (fun attr -> attr.AttributeType.FullName = "Mutannot.MutationCaseAttribute")
- |> Seq.map (fun attr ->
- let args = attr.ConstructorArguments
+ |> Seq.choose (fun attr ->
+ if attr.AttributeType.FullName <> "Mutannot.MutationCaseAttribute" then
+ None
+ else
+ let args = attr.ConstructorArguments
- if args.Count <> 5 then
- failwithf "Unexpected MutationCaseAttribute shape on %s.%s" t.FullName m.Name
+ if args.Count <> 5 then
+ fail $"Unexpected MutationCaseAttribute shape on {declaringType}.{m.Name}"
- let declaringType =
- match t.FullName with
- | null -> t.Name
- | name -> name
-
- { Id = unbox args[0].Value
- File = unbox args[1].Value
- Line = unbox args[2].Value
- Find = unbox args[3].Value
- Replace = unbox args[4].Value
- TestName = m.Name
- DeclaringType = declaringType })
+ Some
+ { Id = requireConstructorArgumentString args 0 "id"
+ File = requireConstructorArgumentString args 1 "file"
+ Line = requireConstructorArgumentInt32 args 2 "line"
+ Find = requireConstructorArgumentString args 3 "find"
+ Replace = requireConstructorArgumentString args 4 "replace"
+ TestName = m.Name
+ DeclaringType = declaringType })
|> Seq.toArray))
|> Array.sortBy (fun mutation -> mutation.Id)