diff --git a/AltCover.Api.Tests/FSApiTests.fs b/AltCover.Api.Tests/FSApiTests.fs index c5602887a..e662b8002 100644 --- a/AltCover.Api.Tests/FSApiTests.fs +++ b/AltCover.Api.Tests/FSApiTests.fs @@ -1301,11 +1301,13 @@ module FSApiTests = |> AltCover.AltCover.CollectOptions.Primitive let prep2 = - { pprep with Verbosity = TraceLevel.Error } + { pprep with + Verbosity = TraceLevel.Error + Dependencies = [ "nonesuch.dll" ] } |> AltCover.AltCover.PrepareOptions.Primitive test - <@ DotNet.ToTestArguments prep2 coll2 combined = "/p:AltCover=\"true\" /p:AltCoverReportFormat=\"OpenCover\" /p:AltCoverShowStatic=\"-\" /p:AltCoverVerbosity=\"Error\" /p:AltCoverShowSummary=\"R\" /p:AltCoverForce=\"true\" /p:AltCoverFailFast=\"true\"" @> + <@ DotNet.ToTestArguments prep2 coll2 combined = "/p:AltCover=\"true\" /p:AltCoverDependencyList=\"nonesuch.dll|\" /p:AltCoverReportFormat=\"OpenCover\" /p:AltCoverShowStatic=\"-\" /p:AltCoverVerbosity=\"Error\" /p:AltCoverShowSummary=\"R\" /p:AltCoverForce=\"true\" /p:AltCoverFailFast=\"true\"" @> [] let MergeRejectsNonCoverage () = diff --git a/AltCover.DotNet/DotNet.fs b/AltCover.DotNet/DotNet.fs index 443bf365a..0f9fd645b 100644 --- a/AltCover.DotNet/DotNet.fs +++ b/AltCover.DotNet/DotNet.fs @@ -86,8 +86,21 @@ module DotNet = "AvoidMethodWithUnusedGenericTypeRule", Justification = "Compiler Generated")>] let internal toPrepareListArgumentList (prepare: Abstract.IPrepareOptions) = + let dependencies = prepare.Dependencies + + let extra = + if dependencies |> Seq.isEmpty then + 0 + else + 1 + + let suffix = + String.Empty |> Seq.replicate extra + + let d2 = suffix |> Seq.append dependencies + [ fromList, "SymbolDirectories", prepare.SymbolDirectories //=`"pipe `'|'` separated list of paths" - fromList, "DependencyList", prepare.Dependencies //=`"pipe `'|'` separated list of paths" + fromList, "DependencyList", d2 //=`"pipe `'|'` separated *AND TERMINATED* list of paths" fromList, "Keys", prepare.Keys //=`"pipe `'|'` separated list of paths to strong-name keys for re-signing assemblies" fromList, "FileFilter", prepare.FileFilter //=`"pipe `'|'` separated list of file name regexes" fromList, "AssemblyFilter", prepare.AssemblyFilter //=`"pipe `'|'` separated list of names" @@ -108,12 +121,12 @@ module DotNet = fromArg, "ShowStatic", prepare.ShowStatic ] //=-|+|++` to mark simple code like auto-properties in the coverage file let internal toPrepareArgArgumentList (prepare: Abstract.IPrepareOptions) = - [ (arg, "ZipFile", "false", prepare.ZipFile) //="true|false"` - set "true" to store the coverage report in a `.zip` archive - (arg, "MethodPoint", "false", prepare.MethodPoint) //="true|false"` - set "true" to record only the first point of each method - (arg, "Single", "false", prepare.SingleVisit) //="true|false"` - set "true" to record only the first visit to each point + [ (arg, "ZipFile", "true", prepare.ZipFile) //="true|false"` - set "true" to store the coverage report in a `.zip` archive + (arg, "MethodPoint", "true", prepare.MethodPoint) //="true|false"` - set "true" to record only the first point of each method + (arg, "Single", "true", prepare.SingleVisit) //="true|false"` - set "true" to record only the first visit to each point (arg, "LineCover", "true", prepare.LineCover) //="true|false"` - set "true" to record only line coverage in OpenCover format (arg, "BranchCover", "true", prepare.BranchCover) //="true|false"` - set "true" to record only branch coverage in OpenCover format - (arg, "SourceLink", "false", prepare.SourceLink) //=true|false` to opt for SourceLink document URLs for tracked files + (arg, "SourceLink", "true", prepare.SourceLink) //=true|false` to opt for SourceLink document URLs for tracked files (arg, "LocalSource", "true", prepare.LocalSource) //=true|false` to ignore assemblies with `.pdb`s that don't refer to local source (arg, "VisibleBranches", "true", prepare.VisibleBranches) //=true|false` to ignore compiler generated internal `switch`/`match` branches (arg, "ShowGenerated", "true", prepare.ShowGenerated) //=true|false` to mark generated code in the coverage file diff --git a/AltCover.Engine/AltCover.Engine.fsproj b/AltCover.Engine/AltCover.Engine.fsproj index 57e7b159d..fcca89139 100644 --- a/AltCover.Engine/AltCover.Engine.fsproj +++ b/AltCover.Engine/AltCover.Engine.fsproj @@ -30,6 +30,7 @@ + @@ -37,7 +38,6 @@ - diff --git a/AltCover.Engine/AltCover.fs b/AltCover.Engine/AltCover.fs index c2095adf8..bc85a8e4a 100644 --- a/AltCover.Engine/AltCover.fs +++ b/AltCover.Engine/AltCover.fs @@ -526,7 +526,7 @@ module AltCover = CommandLine.error <- String.Format( System.Globalization.CultureInfo.CurrentCulture, - CommandLine.resources.GetString "Incompatible", + Output.resources.GetString "Incompatible", "--branchcover", "--linecover" ) diff --git a/AltCover.Engine/CecilEx.fs b/AltCover.Engine/CecilEx.fs index 8a676c7e0..cc2754a0d 100644 --- a/AltCover.Engine/CecilEx.fs +++ b/AltCover.Engine/CecilEx.fs @@ -6,9 +6,131 @@ namespace AltCover open System +open System.Collections.Generic +open System.Diagnostics.CodeAnalysis +open System.IO +open System.Reflection + open Mono.Cecil open Mono.Cecil.Cil +module AssemblyConstants = + let internal nugetCache = + Path.Combine( + Path.Combine( + Environment.GetFolderPath Environment.SpecialFolder.UserProfile, + ".nuget" + ), + "packages" + ) + + let internal resolutionTable = + Dictionary() + + let internal findAssemblyName f = + try + (AssemblyName.GetAssemblyName f).ToString() + with + | :? ArgumentException + | :? FileNotFoundException + | :? System.Security.SecurityException + | :? BadImageFormatException + | :? FileLoadException -> String.Empty + +[] +[] +type internal AssemblyResolver() as self = + inherit DefaultAssemblyResolver() + + do + self.add_ResolveFailure + <| new AssemblyResolveEventHandler(AssemblyResolver.ResolveFromNugetCache) + + static member private AssemblyRegister (name: string) (path: string) = + let def = AssemblyResolver.ReadAssembly path // recursive + AssemblyConstants.resolutionTable.[name] <- def + def + + [] + static member Register (name: string) (path: string) = + AssemblyResolver.AssemblyRegister name path + |> ignore + + static member ReadAssembly(path: String) = + let reader = ReaderParameters() + reader.AssemblyResolver <- new AssemblyResolver() + AssemblyDefinition.ReadAssembly(path, reader) + + static member ReadAssembly(file: Stream) = + let reader = ReaderParameters() + reader.AssemblyResolver <- new AssemblyResolver() + AssemblyDefinition.ReadAssembly(file, reader) + + [] + static member internal ResolveFromNugetCache _ (y: AssemblyNameReference) = + let name = y.ToString() + + if AssemblyConstants.resolutionTable.ContainsKey name then + AssemblyConstants.resolutionTable.[name] + else + // Placate Gendarme here + let share = + "|usr|share" + .Replace('|', Path.DirectorySeparatorChar) + + let shared = + "dotnet|shared" + .Replace('|', Path.DirectorySeparatorChar) + + let sources = + [ Environment.GetEnvironmentVariable "NUGET_PACKAGES" + Path.Combine( + Environment.GetEnvironmentVariable "ProgramFiles" + |> Option.ofObj + |> (Option.defaultValue share), + shared + ) + Path.Combine(share, shared) + AssemblyConstants.nugetCache ] + + let candidate source = + source + |> List.filter (String.IsNullOrWhiteSpace >> not) + |> List.filter Directory.Exists + |> Seq.distinct + |> Seq.collect (fun dir -> + Directory.GetFiles(dir, y.Name + ".*", SearchOption.AllDirectories)) + |> Seq.sortDescending + |> Seq.filter (fun f -> + let x = Path.GetExtension f + + x.Equals(".exe", StringComparison.OrdinalIgnoreCase) + || x.Equals(".dll", StringComparison.OrdinalIgnoreCase)) + |> Seq.filter (fun f -> + y + .ToString() + .Equals(AssemblyConstants.findAssemblyName f, StringComparison.Ordinal)) + |> Seq.tryHead + + match candidate sources with + | None -> null + | Some x -> + String.Format( + System.Globalization.CultureInfo.CurrentCulture, + Output.resources.GetString "resolved", + y.ToString(), + x + ) + |> (Output.warnOn true) + + AssemblyResolver.AssemblyRegister name x + [] module internal CecilExtension = let internal scopesSeen = @@ -287,4 +409,15 @@ module internal CecilExtension = |> Seq.filter (fun i -> i.OpCode = OpCodes.Tail) |> Seq.iter (fun i -> i.OpCode <- OpCodes.Nop - i.Operand <- null) \ No newline at end of file + i.Operand <- null) + + let internal hookResolveHandler = + new AssemblyResolveEventHandler(AssemblyResolver.ResolveFromNugetCache) + + let internal hookResolver (resolver: IAssemblyResolver) = + if resolver.IsNotNull then + let hook = + resolver.GetType().GetMethod("add_ResolveFailure") + + hook.Invoke(resolver, [| hookResolveHandler :> obj |]) + |> ignore \ No newline at end of file diff --git a/AltCover.Engine/CommandLine.fs b/AltCover.Engine/CommandLine.fs index 942984fdd..0cc6878ba 100644 --- a/AltCover.Engine/CommandLine.fs +++ b/AltCover.Engine/CommandLine.fs @@ -111,9 +111,6 @@ module internal CommandLine = let internal dropReturnCode = ref false // ddFlag - let internal resources = - ResourceManager("AltCover.Strings", Assembly.GetExecutingAssembly()) - [] @@ -121,7 +118,7 @@ module internal CommandLine = [] // ~ Static class for methods with params array arguments type internal Format private () = static member Local(resource, [] args) = - String.Format(CultureInfo.CurrentCulture, resources.GetString resource, args) + String.Format(CultureInfo.CurrentCulture, Output.resources.GetString resource, args) module internal I = let internal conditionalOutput condition output = if condition () then output () @@ -274,16 +271,6 @@ module internal CommandLine = static member Throw<'T>(e: exn) : 'T = (e.Message, e) |> SecurityException |> raise - let internal findAssemblyName f = - try - (AssemblyName.GetAssemblyName f).ToString() - with - | :? ArgumentException - | :? FileNotFoundException - | :? System.Security.SecurityException - | :? BadImageFormatException - | :? FileLoadException -> String.Empty - let internal transformCryptographicException f = try f () @@ -365,7 +352,7 @@ module internal CommandLine = (fun () -> tag |> String.IsNullOrWhiteSpace |> not && error |> List.isEmpty |> not) - (fun () -> tag |> resources.GetString |> Output.error) + (fun () -> tag |> Output.resources.GetString |> Output.error) error |> List.iter Output.error @@ -412,7 +399,8 @@ module internal CommandLine = let internal validateAssembly assembly x = if I.validateFile assembly x then - let name = I.findAssemblyName x + let name = + AssemblyConstants.findAssemblyName x if String.IsNullOrWhiteSpace name then error <- @@ -447,9 +435,6 @@ module internal CommandLine = let internal doPathOperation = I.doPathOperation - let internal findAssemblyName = - I.findAssemblyName - let internal validateDirectory dir x = I.validateFileSystemEntity Directory.Exists I.dnf dir x @@ -475,25 +460,25 @@ module internal CommandLine = let internal usageBase u = I.writeColoured Console.Error ConsoleColor.Yellow (fun w -> - w.WriteLine(resources.GetString u.Intro) + w.WriteLine(Output.resources.GetString u.Intro) u.Options.WriteOptionDescriptions(w) if u.Options.Any() && u.Options2.Any() then - w.WriteLine(resources.GetString "orbinder") + w.WriteLine(Output.resources.GetString "orbinder") if u.Options2.Any() then w.WriteLine(" Runner") u.Options2.WriteOptionDescriptions(w) - w.WriteLine(resources.GetString "orbinder") - w.WriteLine(resources.GetString "ImportModule") - w.WriteLine(resources.GetString "orbinder") - w.WriteLine(resources.GetString "Version") - w.WriteLine(resources.GetString "orglobal") - w.WriteLine(resources.GetString "TargetsPath")) + w.WriteLine(Output.resources.GetString "orbinder") + w.WriteLine(Output.resources.GetString "ImportModule") + w.WriteLine(Output.resources.GetString "orbinder") + w.WriteLine(Output.resources.GetString "Version") + w.WriteLine(Output.resources.GetString "orglobal") + w.WriteLine(Output.resources.GetString "TargetsPath")) let internal writeResource = - resources.GetString >> Output.info + Output.resources.GetString >> Output.info let internal writeResourceWithFormatItems s x warn = Format.Local(s, x) |> (Output.warnOn warn) diff --git a/AltCover.Engine/Instrument.fs b/AltCover.Engine/Instrument.fs index 76ed79374..039cb2a38 100644 --- a/AltCover.Engine/Instrument.fs +++ b/AltCover.Engine/Instrument.fs @@ -75,7 +75,7 @@ type internal AsyncSupport = typeof .Assembly .Location - |> AssemblyDefinition.ReadAssembly + |> AssemblyResolver.ReadAssembly let task = def.MainModule.GetType("System.Threading.Tasks.Task") @@ -90,7 +90,7 @@ type internal AsyncSupport = typeof .Assembly .Location - |> AssemblyDefinition.ReadAssembly + |> AssemblyResolver.ReadAssembly let fsasync = def2.MainModule.GetType("Microsoft.FSharp.Control.FSharpAsync") @@ -138,15 +138,12 @@ module internal Instrument = .GetManifestResourceStream("AltCover.AltCover.Recorder.net20.dll") use def = - AssemblyDefinition.ReadAssembly stream + AssemblyResolver.ReadAssembly stream def.Name.Version.ToString() let version = recorderVersion () - let internal resolutionTable = - Dictionary() - let internal modules = List() module internal I = @@ -395,93 +392,10 @@ module internal Instrument = Justification = "Return confusing Gendarme -- TODO")>] let internal prepareAssembly (assembly: Stream) = let definition = - AssemblyDefinition.ReadAssembly(assembly) + AssemblyResolver.ReadAssembly(assembly) prepareAssemblyDefinition definition - let private nugetCache = - Path.Combine( - Path.Combine( - Environment.GetFolderPath Environment.SpecialFolder.UserProfile, - ".nuget" - ), - "packages" - ) - - [] - let internal resolveFromNugetCache _ (y: AssemblyNameReference) = - let name = y.ToString() - - if resolutionTable.ContainsKey name then - resolutionTable.[name] - else - // Placate Gendarme here - let share = - "|usr|share" - .Replace('|', Path.DirectorySeparatorChar) - - let shared = - "dotnet|shared" - .Replace('|', Path.DirectorySeparatorChar) - - let sources = - [ Environment.GetEnvironmentVariable "NUGET_PACKAGES" - Path.Combine( - Environment.GetEnvironmentVariable "ProgramFiles" - |> Option.ofObj - |> (Option.defaultValue share), - shared - ) - Path.Combine(share, shared) - nugetCache ] - - let candidate source = - source - |> List.filter (String.IsNullOrWhiteSpace >> not) - |> List.filter Directory.Exists - |> Seq.distinct - |> Seq.collect (fun dir -> - Directory.GetFiles(dir, y.Name + ".*", SearchOption.AllDirectories)) - |> Seq.sortDescending - |> Seq.filter (fun f -> - let x = Path.GetExtension f - - x.Equals(".exe", StringComparison.OrdinalIgnoreCase) - || x.Equals(".dll", StringComparison.OrdinalIgnoreCase)) - |> Seq.filter (fun f -> - y - .ToString() - .Equals(CommandLine.findAssemblyName f, StringComparison.Ordinal)) - |> Seq.tryHead - - match candidate sources with - | None -> null - | Some x -> - String.Format( - System.Globalization.CultureInfo.CurrentCulture, - CommandLine.resources.GetString "resolved", - y.ToString(), - x - ) - |> (Output.warnOn true) - - let a = AssemblyDefinition.ReadAssembly x - resolutionTable.[name] <- a - a - - let internal hookResolveHandler = - new AssemblyResolveEventHandler(resolveFromNugetCache) - - let internal hookResolver (resolver: IAssemblyResolver) = - if resolver.IsNotNull then - let hook = - resolver.GetType().GetMethod("add_ResolveFailure") - - hook.Invoke(resolver, [| hookResolveHandler :> obj |]) - |> ignore - // #if IDEMPOTENT_INSTRUMENT // let internal safeWait (mutex: System.Threading.WaitHandle) = // try @@ -903,7 +817,7 @@ module internal Instrument = String.Format( System.Globalization.CultureInfo.CurrentCulture, - CommandLine.resources.GetString "instrumented", + Output.resources.GetString "instrumented", definition, first ) @@ -918,7 +832,7 @@ module internal Instrument = String.Format( System.Globalization.CultureInfo.CurrentCulture, - CommandLine.resources.GetString "instrumented", + Output.resources.GetString "instrumented", definition, pathn ) @@ -1245,7 +1159,7 @@ module internal Instrument = .GetManifestResourceStream("AltCover.AltCover.Async.net46.dll") use delta = - AssemblyDefinition.ReadAssembly(stream) + AssemblyResolver.ReadAssembly(stream) // get a handle on the property let readCallTrackType (m: ModuleDefinition) = diff --git a/AltCover.Engine/Json.fs b/AltCover.Engine/Json.fs index d87466d1f..cef61dc68 100644 --- a/AltCover.Engine/Json.fs +++ b/AltCover.Engine/Json.fs @@ -99,7 +99,7 @@ module internal Json = Some path |> Option.filter File.Exists |> Option.map (fun p -> - let def = AssemblyDefinition.ReadAssembly p + let def = AssemblyResolver.ReadAssembly p ProgramDatabase.readSymbols def def) diff --git a/AltCover.Engine/Main.fs b/AltCover.Engine/Main.fs index ccec1455e..91c6e5cd1 100644 --- a/AltCover.Engine/Main.fs +++ b/AltCover.Engine/Main.fs @@ -39,7 +39,7 @@ module internal Main = CoverageParameters.theOutputDirectories.Clear() CoverageParameters.configurationHash <- None ProgramDatabase.symbolFolders.Clear() - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() Instrument.modules.Clear() CoverageParameters.keys.Clear() @@ -112,7 +112,7 @@ module internal Main = CommandLine.error <- String.Format( CultureInfo.CurrentCulture, - CommandLine.resources.GetString "InvalidValue", + Output.resources.GetString "InvalidValue", "--callContext", x ) @@ -184,7 +184,8 @@ module internal Main = CommandLine.validateAssembly "--dependency" path if ok then - Instrument.resolutionTable.[name] <- AssemblyDefinition.ReadAssembly path) + AssemblyResolver.Register name path) + () false)) @@ -327,7 +328,7 @@ module internal Main = :: CommandLine.error)) ] // default end stop |> List.fold (fun (o: OptionSet) (p, a) -> - o.Add(p, CommandLine.resources.GetString(p), new System.Action(a))) + o.Add(p, Output.resources.GetString(p), new System.Action(a))) (OptionSet()) let private echoDirectories (outputDirectory: string, inputDirectory: string) = @@ -544,7 +545,7 @@ module internal Main = use stream = File.OpenRead(fullName) use def = - AssemblyDefinition.ReadAssembly(stream) + AssemblyResolver.ReadAssembly(stream) ProgramDatabase.readSymbols def diff --git a/AltCover.Engine/Output.fs b/AltCover.Engine/Output.fs index 4d9ef0cbb..4f7976119 100644 --- a/AltCover.Engine/Output.fs +++ b/AltCover.Engine/Output.fs @@ -2,6 +2,8 @@ open System open System.IO +open System.Reflection +open System.Resources open AltCover.Shared open Mono.Options @@ -15,6 +17,9 @@ type internal UsageInfo = Options2: OptionSet } module internal Output = + let internal resources = + ResourceManager("AltCover.Strings", Assembly.GetExecutingAssembly()) + let mutable internal info: String -> unit = ignore diff --git a/AltCover.Engine/PostProcess.fs b/AltCover.Engine/PostProcess.fs index 12c16c965..8e98fef03 100644 --- a/AltCover.Engine/PostProcess.fs +++ b/AltCover.Engine/PostProcess.fs @@ -511,6 +511,6 @@ module internal PostProcess = [] () \ No newline at end of file diff --git a/AltCover.Engine/Runner.fs b/AltCover.Engine/Runner.fs index caf3893ce..318781e2a 100644 --- a/AltCover.Engine/Runner.fs +++ b/AltCover.Engine/Runner.fs @@ -340,7 +340,7 @@ module internal Runner = Justification = "AvoidSpeculativeGenerality too")>] let internal altSummary go extra (report: XDocument) = "Alternative" - |> CommandLine.resources.GetString + |> Output.resources.GetString |> if go || extra then write else ignore let classes = @@ -862,14 +862,14 @@ module internal Runner = :: CommandLine.error)) ] // default end stop |> List.fold (fun (o: OptionSet) (p, a) -> - o.Add(p, CommandLine.resources.GetString(p), new System.Action(a))) + o.Add(p, Output.resources.GetString(p), new System.Action(a))) (OptionSet()) let internal requireRecorderTest recordingDirectory success fail = match recordingDirectory with | None -> CommandLine.error <- - (CommandLine.resources.GetString "recorderRequired") + (Output.resources.GetString "recorderRequired") :: CommandLine.error fail @@ -901,7 +901,7 @@ module internal Runner = | (None, false) | (Some _, true) -> CommandLine.error <- - (CommandLine.resources.GetString "executableRequired") + (Output.resources.GetString "executableRequired") :: CommandLine.error Left("UsageError", options) @@ -944,7 +944,7 @@ module internal Runner = Path.Combine(Option.get recordingDirectory, recorderName) let definition = - AssemblyDefinition.ReadAssembly recorderPath + AssemblyResolver.ReadAssembly recorderPath (definition, definition.MainModule.GetType("AltCover.Recorder.Instance")) diff --git a/AltCover.Engine/Visitor.fs b/AltCover.Engine/Visitor.fs index ab2a7071b..9d0fb1bc4 100644 --- a/AltCover.Engine/Visitor.fs +++ b/AltCover.Engine/Visitor.fs @@ -692,7 +692,7 @@ module internal Visitor = Identity = path.Identity } path.AssemblyPath - |> (AssemblyDefinition.ReadAssembly + |> (AssemblyResolver.ReadAssembly >> makeInspection >> buildSequence)) diff --git a/AltCover.Tests/Tests2.fs b/AltCover.Tests/Tests2.fs index ed73f3ed4..0a441887e 100644 --- a/AltCover.Tests/Tests2.fs +++ b/AltCover.Tests/Tests2.fs @@ -382,7 +382,7 @@ module AltCoverTests2 = pdb |> Seq.iter (fun p -> - let a = CommandLine.findAssemblyName p + let a = AssemblyConstants.findAssemblyName p Assert.That(String.IsNullOrWhiteSpace a, p)) let dll = Directory.GetFiles(here, "*.dll") @@ -390,7 +390,7 @@ module AltCoverTests2 = dll |> Seq.iter (fun d -> - let a = CommandLine.findAssemblyName d + let a = AssemblyConstants.findAssemblyName d Assert.That(a |> String.IsNullOrWhiteSpace |> not, d)) [] @@ -445,13 +445,13 @@ module AltCoverTests2 = json |> Seq.iter (fun j -> - let a = CommandLine.findAssemblyName j + let a = AssemblyConstants.findAssemblyName j test' <@ String.IsNullOrWhiteSpace a @> j) #endif use raw = Mono.Cecil.AssemblyDefinition.ReadAssembly where - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() try raw.MainModule.AssemblyReferences @@ -460,7 +460,7 @@ module AltCoverTests2 = >= 0) |> Seq.iter (fun f -> let resolved = - Instrument.I.hookResolveHandler.Invoke(null, f) + CecilExtension.hookResolveHandler.Invoke(null, f) test' <@ resolved.IsNotNull @> <| f.ToString()) @@ -472,22 +472,24 @@ module AltCoverTests2 = f.Version <- System.Version("666.666.666.666") let resolved = - Instrument.I.hookResolveHandler.Invoke(null, f) + CecilExtension.hookResolveHandler.Invoke(null, f) test' <@ resolved |> isNull @> <| f.ToString()) let found = - Instrument.resolutionTable.Keys |> Seq.toList + AssemblyConstants.resolutionTable.Keys + |> Seq.toList found |> Seq.iter (fun k -> - let matched = Instrument.resolutionTable.[k] + let matched = + AssemblyConstants.resolutionTable.[k] let k2 = AssemblyNameReference.Parse(k.ToString()) k2.Version <- System.Version("666.666.666.666") - Instrument.resolutionTable.[k2.ToString()] <- matched) + AssemblyConstants.resolutionTable.[k2.ToString()] <- matched) raw.MainModule.AssemblyReferences |> Seq.filter (fun f -> @@ -497,11 +499,11 @@ module AltCoverTests2 = f.Version <- System.Version("666.666.666.666") let resolved = - Instrument.I.hookResolveHandler.Invoke(null, f) + CecilExtension.hookResolveHandler.Invoke(null, f) test' <@ resolved.IsNotNull @> <| f.ToString()) finally - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() [] let ShouldBeAbleToPrepareTheAssembly () = diff --git a/AltCover.Tests/Tests3.fs b/AltCover.Tests/Tests3.fs index ef9491daa..46bc95caf 100644 --- a/AltCover.Tests/Tests3.fs +++ b/AltCover.Tests/Tests3.fs @@ -1403,7 +1403,7 @@ module AltCoverTests3 = Main.init () try - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() let options = Main.I.declareOptions () let here = @@ -1423,8 +1423,8 @@ module AltCoverTests3 = Assert.That(x, Is.Empty) let expected = - Instrument.resolutionTable.Keys - |> Seq.map (fun a -> Instrument.resolutionTable.[a].Name.Name) + AssemblyConstants.resolutionTable.Keys + |> Seq.map (fun a -> AssemblyConstants.resolutionTable.[a].Name.Name) |> Seq.sort Assert.That( @@ -1432,7 +1432,7 @@ module AltCoverTests3 = Is.EqualTo("AltCover.Engine AltCover.Tests") ) finally - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() //let ParsingNoDependencyGivesFailure() = // Main.init() @@ -1453,7 +1453,7 @@ module AltCoverTests3 = Main.init () try - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() let options = Main.I.declareOptions () let unique = @@ -1469,7 +1469,7 @@ module AltCoverTests3 = Assert.That(y, Is.SameAs options) Assert.That(x, Is.EqualTo "UsageError") finally - Instrument.resolutionTable.Clear() + AssemblyConstants.resolutionTable.Clear() [] let ParsingNonDependencyGivesFailure () = @@ -3746,7 +3746,7 @@ module AltCoverTests3 = ) let helptext = - CommandLine + Output .resources .GetString("HelpText") .Replace("\r\n", "\n") diff --git a/Build/targets.fsx b/Build/targets.fsx index d68b07b04..9f7386d7b 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -304,6 +304,11 @@ let withWorkingDirectoryVN dir o = WorkingDirectory = Path.getFullName dir Verbosity = Some DotNet.Verbosity.Normal } +let withWorkingDirectoryVDet dir o = + { dotnetOptions o with + WorkingDirectory = Path.getFullName dir + Verbosity = Some DotNet.Verbosity.Detailed } + let withWorkingDirectoryOnly dir o = { dotnetOptions o with WorkingDirectory = Path.getFullName dir } @@ -6842,6 +6847,81 @@ _Target "Issue114" (fun _ -> Shell.mkdir folder Actions.CleanDir folder) +_Target "Issue156" (fun _ -> + try + Directory.ensure "./_Issue156" + Shell.cleanDir ("./_Issue156") + Directory.ensure "./_Issue156/Tests" + Directory.ensure "./_Issue156/ClassLibrary1" + + let config = XDocument.Load "./Build/NuGet.config.dotnettest" + + let repo = config.Descendants(XName.Get("add")) |> Seq.head + + repo.SetAttributeValue(XName.Get "value", Path.getFullName "./_Packaging") + config.Save "./_Issue156/NuGet.config" + + let csproj = XDocument.Load "./RegressionTesting/issue156/Tests/Tests.csproj" + + let pack = + csproj.Descendants(XName.Get("PackageReference")) + |> Seq.head + + let inject = + XElement( + XName.Get "PackageReference", + XAttribute(XName.Get "Include", "altcover"), + XAttribute(XName.Get "Version", Version.Value) + ) + + pack.AddBeforeSelf inject + csproj.Save "./_Issue156/Tests/Issue156.csproj" + Shell.copy "./_Issue156/Tests" (!! "./RegressionTesting/issue156/Tests/*.cs") + Shell.copy "./_Issue156/ClassLibrary1" (!! "./RegressionTesting/issue156/ClassLibrary1/*.cs*") + + DotNet.restore + (fun o -> + let tmp = o.WithCommon(withWorkingDirectoryVM "_Issue156/Tests") + + let mparams = { tmp.MSBuildParams with Properties = tmp.MSBuildParams.Properties } + + { tmp with MSBuildParams = mparams }) + "" + + let p0 = + { Primitive.PrepareOptions.Create() with + Dependencies = ["C:\\WINDOWS\\Microsoft.NET\\assembly\\GAC_MSIL\\WindowsBase\\v4.0_4.0.0.0__31bf3856ad364e35\\WindowsBase.dll" ] + //Dependencies = ["WindowsBase.dll"] + AssemblyFilter = + [| "nunit" + "Adapter" + "FSharp" + "AltCover" |] + SingleVisit = true + } + + let pp0 = AltCover.PrepareOptions.Primitive p0 + let c0 = Primitive.CollectOptions.Create() + let cc0 = AltCover.CollectOptions.Primitive c0 + + DotNet.test + (fun p -> + (({ p.WithCommon(withWorkingDirectoryVM "_Issue156/Tests") with + Configuration = DotNet.BuildConfiguration.Debug + NoBuild = false }) + .WithAltCoverOptions + pp0 + cc0 + ForceTrueOnly) + |> testWithCLIArguments) + "" + finally + let folder = (nugetCache @@ "altcover") @@ Version.Value + + Shell.mkdir folder + Actions.CleanDir folder) + + // AOB _Target "MakeDocumentation" (fun _ -> @@ -7348,6 +7428,8 @@ Target.activateFinal "ResetConsoleColours" "Unpack" ==> "Issue114" ==> "Deployment" +"Unpack" ==> "Issue156" + "Unpack" ==> "DotnetGlobalIntegration" ==> "Deployment" diff --git a/RegressionTesting/issue156/ClassLibrary1/Class1.cs b/RegressionTesting/issue156/ClassLibrary1/Class1.cs new file mode 100644 index 000000000..2a5185f1e --- /dev/null +++ b/RegressionTesting/issue156/ClassLibrary1/Class1.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq; +using System.Windows.Threading; + +namespace projectNamespace +{ + /// + /// Invoke the callback in the SynchronizationContext with given priority. + /// + public interface IUiSynchronizationContext + { + /// + /// Asynchronously invoke the callback in the SynchronizationContext with given priority. + /// + void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Synchronously invoke the callback in the SynchronizationContext with given priority. + /// + void Send(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + } +} diff --git a/RegressionTesting/issue156/ClassLibrary1/ClassLibrary1.csproj b/RegressionTesting/issue156/ClassLibrary1/ClassLibrary1.csproj new file mode 100644 index 000000000..c205b0d2f --- /dev/null +++ b/RegressionTesting/issue156/ClassLibrary1/ClassLibrary1.csproj @@ -0,0 +1,22 @@ + + + + net48 + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + \ No newline at end of file diff --git a/RegressionTesting/issue156/Tests/Class1.cs b/RegressionTesting/issue156/Tests/Class1.cs new file mode 100644 index 000000000..46a916b61 --- /dev/null +++ b/RegressionTesting/issue156/Tests/Class1.cs @@ -0,0 +1,32 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using projectNamespace; + +namespace Tests +{ + /// + /// Test fixrure + /// + [TestClass] + public class ConfigurationProviderTest + { + + /// + /// Test setup + /// + [TestInitialize] + public void TestInitialize() + { + + } + + /// + /// Test. + /// + [TestMethod] + public void UpdateConfiguration_ConfigurationChangedIsRun() + { + Assert.AreEqual(1, 1); + } + } +} diff --git a/RegressionTesting/issue156/Tests/Tests.csproj b/RegressionTesting/issue156/Tests/Tests.csproj new file mode 100644 index 000000000..18cd4ec06 --- /dev/null +++ b/RegressionTesting/issue156/Tests/Tests.csproj @@ -0,0 +1,25 @@ + + + + net48 + true + false + false + + + UnitTest + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + \ No newline at end of file diff --git a/RegressionTesting/issue156/Tests/Tests.sln b/RegressionTesting/issue156/Tests/Tests.sln new file mode 100644 index 000000000..70b24f31a --- /dev/null +++ b/RegressionTesting/issue156/Tests/Tests.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31205.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests.csproj", "{FBC3A8F8-39B4-4C3E-A7EC-C5D83F83AFBE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "..\ClassLibrary1\ClassLibrary1.csproj", "{830E0F7A-6740-46B0-B288-541DE57B060A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FBC3A8F8-39B4-4C3E-A7EC-C5D83F83AFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBC3A8F8-39B4-4C3E-A7EC-C5D83F83AFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBC3A8F8-39B4-4C3E-A7EC-C5D83F83AFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBC3A8F8-39B4-4C3E-A7EC-C5D83F83AFBE}.Release|Any CPU.Build.0 = Release|Any CPU + {830E0F7A-6740-46B0-B288-541DE57B060A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {830E0F7A-6740-46B0-B288-541DE57B060A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {830E0F7A-6740-46B0-B288-541DE57B060A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {830E0F7A-6740-46B0-B288-541DE57B060A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1A08953D-D88E-472F-BAA5-6078CCFD28F3} + EndGlobalSection +EndGlobal diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 812bee89f..22c36b809 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -4,10 +4,11 @@ A. Start with the Quick Start guide : https://github.com/SteveGilham/altcover/wi # 8.3.8xx (Habu series release 14) * [VISUALIZER] Fix off-by-one error in markup for the very last line of the file +* [BUGFIX] -- Fake API fix for ZipFile, MethodPoint, SingleVisit, SourceLink to enable their activation +* [BUGFIX] -- work around the behaviour of `dotnet test` with an argument ending `.dll` or `.exe`, directly through API and in docs for direct use. * Add a `--verbose`option for AltCover, the converse of `-q`. Away from the command line, adds meaning to the `Verbosity` option value `System.Diagnostics.TraceLevel.Verbose`. Currently `Verbose` output shows more of the selection of files for instrumentation, noting when files are excluded from the process. * Add a matching field, `ILoggingOptions.Verbose`, to the API as a sink to capture verbose output. -* Other minor build process asjustments for SDK 6.0.300/F# 6.0.4 - +* Other minor build process asjustments for SDK 6.0.300/F# 6.0.4 and later # 8.2.837 (Habu series release 13) * [VISUALIZER] Critical bug fixes for GTK and Avalonia : mismatched new icon names causing crashes diff --git a/nupkg/build/netstandard2.0/AltCover.targets b/nupkg/build/netstandard2.0/AltCover.targets index ec8ee9137..3ecddba23 100644 --- a/nupkg/build/netstandard2.0/AltCover.targets +++ b/nupkg/build/netstandard2.0/AltCover.targets @@ -85,7 +85,7 @@ OutputDirectories="@(AltCoverOutputDirectory)" InPlace="$(AltCoverInPlace)" SymbolDirectories="$(AltCoverSymbolDirectories.Split('|'))" - Dependencies="$(AltCoverDependencyList.Split('|'))" + Dependencies="$(AltCoverDependencyList.Trim('|').Split('|'))" Keys="$(AltCoverKeys.Split('|'))" StrongNameKey="$(AltCoverStrongNameKey)" Report="$(AltCoverReport2)"