From cdb53725e6a7bd630ea7813d010e65bcab237df6 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Mon, 16 Apr 2018 08:40:05 +0100 Subject: [PATCH 01/48] Restructure before doing --- AltCover/AltCover.fsproj | 2 + AltCover/Cobertura.fs | 5 + AltCover/LCov.fs | 248 ++++++++++++++++++++++++++++++++++ AltCover/Runner.fs | 247 +-------------------------------- AltCover/altcover.core.fsproj | 2 + Tests/Runner.Tests.fs | 34 ++--- 6 files changed, 278 insertions(+), 260 deletions(-) create mode 100644 AltCover/Cobertura.fs create mode 100644 AltCover/LCov.fs diff --git a/AltCover/AltCover.fsproj b/AltCover/AltCover.fsproj index 6b93fa155..c2cb3eda6 100644 --- a/AltCover/AltCover.fsproj +++ b/AltCover/AltCover.fsproj @@ -83,6 +83,8 @@ + + diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs new file mode 100644 index 000000000..abd4928cc --- /dev/null +++ b/AltCover/Cobertura.fs @@ -0,0 +1,5 @@ +namespace AltCover + +module Cobertura = + let internal path : Option ref = ref None + diff --git a/AltCover/LCov.fs b/AltCover/LCov.fs new file mode 100644 index 000000000..3716b488f --- /dev/null +++ b/AltCover/LCov.fs @@ -0,0 +1,248 @@ +namespace AltCover + +open System +open System.Globalization +open System.IO +open System.Xml.Linq + +module LCov = + let internal path : Option ref = ref None + + let DoWithFile (create: unit -> FileStream) (action : Stream -> unit) = + use stream = create() + action stream + + let X = OpenCover.X + + let lineOfMethod (m : XElement) = + (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "line").Value |> Int32.TryParse |> snd + + let multiSort (by : 'a -> int) (l : (string * 'a seq) seq) = + l + |> Seq.map (fun (f, ms) -> (f, ms + |> Seq.sortBy by + |> Seq.toList)) + |> Seq.sortBy fst + + let multiSortByNameAndStartLine (l : (string * XElement seq) seq) = + multiSort lineOfMethod l + + let Summary (report:XDocument) (format:Base.ReportFormat) result = + DoWithFile + (fun () -> File.OpenWrite(!path |> Option.get)) + (fun stream -> + use writer = new StreamWriter(stream) + //If available, a tracefile begins with the testname which + // is stored in the following format: + // + // TN: + writer.WriteLine "TN:" + + match format with + | Base.ReportFormat.NCover -> + report.Descendants(X "method") + |> Seq.filter (fun m -> m.Descendants(X "seqpnt") |> Seq.isEmpty |> not) + |> Seq.groupBy (fun m -> (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "document").Value) + |> multiSortByNameAndStartLine + |> Seq.iter (fun (f, methods) -> + // For each source file referenced in the .da file, there is a section + // containing filename and coverage data: + // + // SF: + writer.WriteLine ("SF:" + f) + + // Following is a list of line numbers for each function name found in the + // source file: + // + // FN:, + methods + |> Seq.iter (fun m -> + let l = (lineOfMethod m).ToString(CultureInfo.InvariantCulture) + let name = m.Attribute(X "fullname").Value + writer.WriteLine ("FN:" + l + "," + name)) + + // Next, there is a list of execution counts for each instrumented function: + // + // FNDA:, + let hit = methods + |> Seq.fold (fun n m -> + let v = (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "visitcount").Value + let name = m.Attribute(X "fullname").Value + writer.WriteLine ("FNDA:" + v + "," + name) + n + (if v = "0" then 0 else 1) + ) 0 + // This list is followed by two lines containing the number of functions + // found and hit: + // + // FNF: + // FNH: + writer.WriteLine ("FNF:" + methods.Length.ToString(CultureInfo.InvariantCulture)) + writer.WriteLine ("FNH:" + hit.ToString(CultureInfo.InvariantCulture)) + + // Branch coverage information is stored which one line per branch: + // + // BRDA:,,, + // + // Block number and branch number are gcc internal IDs for the branch. + // Taken is either '-' if the basic block containing the branch was never + // executed or a number indicating how often that branch was taken. + + // Branch coverage summaries are stored in two lines: + // + // BRF: + // BRH: + writer.WriteLine ("BRF:0") + writer.WriteLine ("BRH:0") + + // Then there is a list of execution counts for each instrumented line + // (i.e. a line which resulted in executable code): + // + // DA:,[,] + // + // Note that there may be an optional checksum present for each instru‐ + // mented line. The current geninfo implementation uses an MD5 hash as + // checksumming algorithm. + let (lf, lh) = methods + |> Seq.collect (fun m -> m.Descendants(X "seqpnt")) + |> Seq.filter (fun b -> b.Attribute(X "line").Value |> String.IsNullOrWhiteSpace |> not) + |> Seq.fold (fun (f,h) b -> let sl = b.Attribute(X "line").Value + let v = b.Attribute(X "visitcount") + let vc = if v |> isNull then "0" else v.Value + writer.WriteLine ("DA:" + sl + "," + vc) + (f+1, h + if vc = "0" then 0 else 1)) + (0,0) + // At the end of a section, there is a summary about how many lines were + // found and how many were actually instrumented: + // + // LH: + // LF: + writer.WriteLine ("LH:" + lh.ToString(CultureInfo.InvariantCulture)) + writer.WriteLine ("LF:" + lf.ToString(CultureInfo.InvariantCulture)) + + // Each sections ends with: + // + // end_of_record + writer.WriteLine "end_of_record" + + ) + + | _ -> + // For each source file referenced in the .da file, there is a section + // containing filename and coverage data: + // + // SF: + + report.Descendants(X "File") + |> Seq.iter (fun f -> + writer.WriteLine ("SF:" + f.Attribute(X "fullPath").Value) + let uid = f.Attribute(X "uid").Value + let p = f.Parent.Parent + + // Following is a list of line numbers for each function name found in the + // source file: + // + // FN:, + let methods = p.Descendants(X "Method") + |> Seq.filter (fun m -> m.Descendants(X "FileRef") + |> Seq.exists (fun r -> r.Attribute(X "uid").Value = uid)) + |> Seq.toList + + let FN (ms : XElement list) = + ms + |> Seq.iter (fun m -> + m.Descendants(X "SequencePoint") |> Seq.tryHead + |> Option.iter (fun s -> let n = (m.Descendants(X "Name") + |> Seq.head).Value + let mp = m.Descendants(X "MethodPoint") |> Seq.head + let sl = s.Attribute(X "sl").Value + if sl |> String.IsNullOrWhiteSpace |> not then + writer.WriteLine ("FN:" + s.Attribute(X "sl").Value + + "," + n))) + FN methods + // Next, there is a list of execution counts for each instrumented function: + // + // FNDA:, + let FNDA (ms : XElement list) = + ms + |> Seq.iter (fun m -> + m.Descendants(X "SequencePoint") |> Seq.tryHead + |> Option.iter (fun s -> let n = (m.Descendants(X "Name") + |> Seq.head).Value + let mp = m.Descendants(X "MethodPoint") |> Seq.head + let sl = s.Attribute(X "sl").Value + if sl |> String.IsNullOrWhiteSpace |> not then + writer.WriteLine ("FNDA:" + mp.Attribute(X "vc").Value + + "," + n))) + + FNDA methods + // This list is followed by two lines containing the number of functions + // found and hit: + // + // FNF: + // FNH: + writer.WriteLine ("FNF:" + methods.Length.ToString(CultureInfo.InvariantCulture)) + let hit = methods + |> List.filter (fun m -> m.Attribute(X "visited").Value = "true") + writer.WriteLine ("FNH:" + hit.Length.ToString(CultureInfo.InvariantCulture)) + + // Branch coverage information is stored which one line per branch: + // + // BRDA:,,, + // + // Block number and branch number are gcc internal IDs for the branch. + // Taken is either '-' if the basic block containing the branch was never + // executed or a number indicating how often that branch was taken. + let Branch (ms : XElement list) = + let (brf, brh, _) = ms + |> Seq.collect (fun m -> m.Descendants(X "BranchPoint")) + |> Seq.filter (fun b -> b.Attribute(X "sl").Value |> String.IsNullOrWhiteSpace |> not) + |> Seq.fold (fun (f,h,(o,u)) b -> let sl = b.Attribute(X "sl").Value + let off = b.Attribute(X "offset").Value + let usp = b.Attribute(X "uspid").Value + let path = b.Attribute(X "path").Value + let vc = b.Attribute(X "vc").Value + writer.WriteLine ("BRDA:" + sl + "," + + (if o = off then u else usp) + + "," + path + "," + + (if vc = "0" then "-" else vc)) + (f+1, h + (if vc = "0" then 0 else 1), if o = off then (o,u) else (off,usp))) + (0,0,("?","?")) + // Branch coverage summaries are stored in two lines: + // + // BRF: + // BRH: + writer.WriteLine ("BRF:" + brf.ToString(CultureInfo.InvariantCulture)) + writer.WriteLine ("BRH:" + brh.ToString(CultureInfo.InvariantCulture)) + + Branch methods + + // Then there is a list of execution counts for each instrumented line + // (i.e. a line which resulted in executable code): + // + // DA:,[,] + // + // Note that there may be an optional checksum present for each instru‐ + // mented line. The current geninfo implementation uses an MD5 hash as + // checksumming algorithm. + let (lf, lh) = methods + |> Seq.collect (fun m -> m.Descendants(X "SequencePoint")) + |> Seq.filter (fun b -> b.Attribute(X "sl").Value |> String.IsNullOrWhiteSpace |> not) + |> Seq.fold (fun (f,h) b -> let sl = b.Attribute(X "sl").Value + let vc = b.Attribute(X "vc").Value + writer.WriteLine ("DA:" + sl + "," + vc) + (f+1, h + if vc = "0" then 0 else 1)) + (0,0) + // At the end of a section, there is a summary about how many lines were + // found and how many were actually instrumented: + // + // LH: + // LF: + writer.WriteLine ("LH:" + lh.ToString(CultureInfo.InvariantCulture)) + writer.WriteLine ("LF:" + lf.ToString(CultureInfo.InvariantCulture)) + + // Each sections ends with: + // + // end_of_record + writer.WriteLine "end_of_record" + )) + result diff --git a/AltCover/Runner.fs b/AltCover/Runner.fs index 5fb92d92f..1ac58693b 100644 --- a/AltCover/Runner.fs +++ b/AltCover/Runner.fs @@ -35,249 +35,10 @@ module Runner = let mutable internal recordingDirectory : Option = None let mutable internal workingDirectory : Option = None let internal executable : Option ref = ref None - let internal lcov : Option ref = ref None let mutable internal collect = false let mutable internal threshold : Option = None - - let DoWithFile (create: unit -> FileStream) (action : Stream -> unit) = - use stream = create() - action stream - let X = OpenCover.X - let lineOfMethod (m : XElement) = - (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "line").Value |> Int32.TryParse |> snd - - let multiSort (by : 'a -> int) (l : (string * 'a seq) seq) = - l - |> Seq.map (fun (f, ms) -> (f, ms - |> Seq.sortBy by - |> Seq.toList)) - |> Seq.sortBy fst - - let multiSortByNameAndStartLine (l : (string * XElement seq) seq) = - multiSort lineOfMethod l - - let LCovSummary (report:XDocument) (format:Base.ReportFormat) result = - DoWithFile - (fun () -> File.OpenWrite(!lcov |> Option.get)) - (fun stream -> - use writer = new StreamWriter(stream) - //If available, a tracefile begins with the testname which - // is stored in the following format: - // - // TN: - writer.WriteLine "TN:" - - match format with - | Base.ReportFormat.NCover -> - report.Descendants(X "method") - |> Seq.filter (fun m -> m.Descendants(X "seqpnt") |> Seq.isEmpty |> not) - |> Seq.groupBy (fun m -> (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "document").Value) - |> multiSortByNameAndStartLine - |> Seq.iter (fun (f, methods) -> - // For each source file referenced in the .da file, there is a section - // containing filename and coverage data: - // - // SF: - writer.WriteLine ("SF:" + f) - - // Following is a list of line numbers for each function name found in the - // source file: - // - // FN:, - methods - |> Seq.iter (fun m -> - let l = (lineOfMethod m).ToString(CultureInfo.InvariantCulture) - let name = m.Attribute(X "fullname").Value - writer.WriteLine ("FN:" + l + "," + name)) - - // Next, there is a list of execution counts for each instrumented function: - // - // FNDA:, - let hit = methods - |> Seq.fold (fun n m -> - let v = (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "visitcount").Value - let name = m.Attribute(X "fullname").Value - writer.WriteLine ("FNDA:" + v + "," + name) - n + (if v = "0" then 0 else 1) - ) 0 - // This list is followed by two lines containing the number of functions - // found and hit: - // - // FNF: - // FNH: - writer.WriteLine ("FNF:" + methods.Length.ToString(CultureInfo.InvariantCulture)) - writer.WriteLine ("FNH:" + hit.ToString(CultureInfo.InvariantCulture)) - - // Branch coverage information is stored which one line per branch: - // - // BRDA:,,, - // - // Block number and branch number are gcc internal IDs for the branch. - // Taken is either '-' if the basic block containing the branch was never - // executed or a number indicating how often that branch was taken. - - // Branch coverage summaries are stored in two lines: - // - // BRF: - // BRH: - writer.WriteLine ("BRF:0") - writer.WriteLine ("BRH:0") - - // Then there is a list of execution counts for each instrumented line - // (i.e. a line which resulted in executable code): - // - // DA:,[,] - // - // Note that there may be an optional checksum present for each instru‐ - // mented line. The current geninfo implementation uses an MD5 hash as - // checksumming algorithm. - let (lf, lh) = methods - |> Seq.collect (fun m -> m.Descendants(X "seqpnt")) - |> Seq.filter (fun b -> b.Attribute(X "line").Value |> String.IsNullOrWhiteSpace |> not) - |> Seq.fold (fun (f,h) b -> let sl = b.Attribute(X "line").Value - let v = b.Attribute(X "visitcount") - let vc = if v |> isNull then "0" else v.Value - writer.WriteLine ("DA:" + sl + "," + vc) - (f+1, h + if vc = "0" then 0 else 1)) - (0,0) - // At the end of a section, there is a summary about how many lines were - // found and how many were actually instrumented: - // - // LH: - // LF: - writer.WriteLine ("LH:" + lh.ToString(CultureInfo.InvariantCulture)) - writer.WriteLine ("LF:" + lf.ToString(CultureInfo.InvariantCulture)) - - // Each sections ends with: - // - // end_of_record - writer.WriteLine "end_of_record" - - ) - - | _ -> - // For each source file referenced in the .da file, there is a section - // containing filename and coverage data: - // - // SF: - - report.Descendants(X "File") - |> Seq.iter (fun f -> - writer.WriteLine ("SF:" + f.Attribute(X "fullPath").Value) - let uid = f.Attribute(X "uid").Value - let p = f.Parent.Parent - - // Following is a list of line numbers for each function name found in the - // source file: - // - // FN:, - let methods = p.Descendants(X "Method") - |> Seq.filter (fun m -> m.Descendants(X "FileRef") - |> Seq.exists (fun r -> r.Attribute(X "uid").Value = uid)) - |> Seq.toList - - let FN (ms : XElement list) = - ms - |> Seq.iter (fun m -> - m.Descendants(X "SequencePoint") |> Seq.tryHead - |> Option.iter (fun s -> let n = (m.Descendants(X "Name") - |> Seq.head).Value - let mp = m.Descendants(X "MethodPoint") |> Seq.head - let sl = s.Attribute(X "sl").Value - if sl |> String.IsNullOrWhiteSpace |> not then - writer.WriteLine ("FN:" + s.Attribute(X "sl").Value + - "," + n))) - FN methods - // Next, there is a list of execution counts for each instrumented function: - // - // FNDA:, - let FNDA (ms : XElement list) = - ms - |> Seq.iter (fun m -> - m.Descendants(X "SequencePoint") |> Seq.tryHead - |> Option.iter (fun s -> let n = (m.Descendants(X "Name") - |> Seq.head).Value - let mp = m.Descendants(X "MethodPoint") |> Seq.head - let sl = s.Attribute(X "sl").Value - if sl |> String.IsNullOrWhiteSpace |> not then - writer.WriteLine ("FNDA:" + mp.Attribute(X "vc").Value + - "," + n))) - - FNDA methods - // This list is followed by two lines containing the number of functions - // found and hit: - // - // FNF: - // FNH: - writer.WriteLine ("FNF:" + methods.Length.ToString(CultureInfo.InvariantCulture)) - let hit = methods - |> List.filter (fun m -> m.Attribute(X "visited").Value = "true") - writer.WriteLine ("FNH:" + hit.Length.ToString(CultureInfo.InvariantCulture)) - - // Branch coverage information is stored which one line per branch: - // - // BRDA:,,, - // - // Block number and branch number are gcc internal IDs for the branch. - // Taken is either '-' if the basic block containing the branch was never - // executed or a number indicating how often that branch was taken. - let Branch (ms : XElement list) = - let (brf, brh, _) = ms - |> Seq.collect (fun m -> m.Descendants(X "BranchPoint")) - |> Seq.filter (fun b -> b.Attribute(X "sl").Value |> String.IsNullOrWhiteSpace |> not) - |> Seq.fold (fun (f,h,(o,u)) b -> let sl = b.Attribute(X "sl").Value - let off = b.Attribute(X "offset").Value - let usp = b.Attribute(X "uspid").Value - let path = b.Attribute(X "path").Value - let vc = b.Attribute(X "vc").Value - writer.WriteLine ("BRDA:" + sl + "," + - (if o = off then u else usp) + - "," + path + "," + - (if vc = "0" then "-" else vc)) - (f+1, h + (if vc = "0" then 0 else 1), if o = off then (o,u) else (off,usp))) - (0,0,("?","?")) - // Branch coverage summaries are stored in two lines: - // - // BRF: - // BRH: - writer.WriteLine ("BRF:" + brf.ToString(CultureInfo.InvariantCulture)) - writer.WriteLine ("BRH:" + brh.ToString(CultureInfo.InvariantCulture)) - - Branch methods - - // Then there is a list of execution counts for each instrumented line - // (i.e. a line which resulted in executable code): - // - // DA:,[,] - // - // Note that there may be an optional checksum present for each instru‐ - // mented line. The current geninfo implementation uses an MD5 hash as - // checksumming algorithm. - let (lf, lh) = methods - |> Seq.collect (fun m -> m.Descendants(X "SequencePoint")) - |> Seq.filter (fun b -> b.Attribute(X "sl").Value |> String.IsNullOrWhiteSpace |> not) - |> Seq.fold (fun (f,h) b -> let sl = b.Attribute(X "sl").Value - let vc = b.Attribute(X "vc").Value - writer.WriteLine ("DA:" + sl + "," + vc) - (f+1, h + if vc = "0" then 0 else 1)) - (0,0) - // At the end of a section, there is a summary about how many lines were - // found and how many were actually instrumented: - // - // LH: - // LF: - writer.WriteLine ("LH:" + lh.ToString(CultureInfo.InvariantCulture)) - writer.WriteLine ("LF:" + lf.ToString(CultureInfo.InvariantCulture)) - - // Each sections ends with: - // - // end_of_record - writer.WriteLine "end_of_record" - )) - result - let NCoverSummary (report:XDocument) = let summarise v n key = let pc = if n = 0 then "n/a" else @@ -455,13 +216,13 @@ module Runner = collect <- true)) ("l|lcovReport=", (fun x -> if not (String.IsNullOrWhiteSpace(x)) then - if Option.isSome !lcov then + if Option.isSome !LCov.path then CommandLine.error <- String.Format(CultureInfo.CurrentCulture, CommandLine.resources.GetString "MultiplesNotAllowed", "--lcovReport") :: CommandLine.error else - lcov := Some x - Summaries <- LCovSummary :: Summaries + LCov.path := x |> Path.GetFullPath |> Some + Summaries <- LCov.Summary :: Summaries else CommandLine.error <- String.Format(CultureInfo.CurrentCulture, CommandLine.resources.GetString "InvalidValue", "--lcovReport", @@ -558,7 +319,7 @@ module Runner = String.Format (CultureInfo.CurrentCulture, s |> CommandLine.resources.GetString, x) |> Output.Info let internal SetRecordToFile report = - DoWithFile (fun () -> + LCov.DoWithFile (fun () -> let binpath = report + ".acv" File.Create(binpath)) ignore diff --git a/AltCover/altcover.core.fsproj b/AltCover/altcover.core.fsproj index 7c54b7996..410e8bd3f 100644 --- a/AltCover/altcover.core.fsproj +++ b/AltCover/altcover.core.fsproj @@ -36,6 +36,8 @@ + + diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index 2371f3a19..0d73c7531 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -530,9 +530,9 @@ type AltCoverTests() = class [] member self.ParsingLcovGivesLcov() = - lock Runner.lcov (fun () -> + lock LCov.path (fun () -> try - Runner.lcov := None + LCov.path := None Runner.Summaries <- [Runner.StandardSummary] let options = Runner.DeclareOptions () let unique = "some exe" @@ -543,20 +543,20 @@ type AltCoverTests() = class | Right (x, y) -> Assert.That (y, Is.SameAs options) Assert.That (x, Is.Empty) - match !Runner.lcov with + match !LCov.path with | None -> Assert.Fail() | Some x -> Assert.That(Path.GetFileName x, Is.EqualTo unique) Assert.That (Runner.Summaries.Length, Is.EqualTo 2) finally Runner.Summaries <- [Runner.StandardSummary] - Runner.lcov := None) + LCov.path := None) [] member self.ParsingMultipleLcovGivesFailure() = - lock Runner.lcov (fun () -> + lock LCov.path (fun () -> try - Runner.lcov := None + LCov.path := None Runner.Summaries <- [Runner.StandardSummary] let options = Runner.DeclareOptions () let unique = Guid.NewGuid().ToString() @@ -568,13 +568,13 @@ type AltCoverTests() = class Assert.That (x, Is.EqualTo "UsageError") finally Runner.Summaries <- [Runner.StandardSummary] - Runner.lcov := None) + LCov.path := None) [] member self.ParsingNoLcovGivesFailure() = - lock Runner.lcov (fun () -> + lock LCov.path (fun () -> try - Runner.lcov := None + LCov.path := None Runner.Summaries <- [Runner.StandardSummary] let options = Runner.DeclareOptions () let blank = " " @@ -586,7 +586,7 @@ type AltCoverTests() = class Assert.That (x, Is.EqualTo "UsageError") finally Runner.Summaries <- [Runner.StandardSummary] - Runner.lcov := None) + LCov.path := None) [] member self.ParsingThresholdGivesThreshold() = @@ -1447,11 +1447,11 @@ or let baseline = XDocument.Load(stream) let unique = Path.Combine(Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName, Guid.NewGuid().ToString() + "/OpenCover.lcov") - Runner.lcov := Some unique + LCov.path := Some unique unique |> Path.GetDirectoryName |> Directory.CreateDirectory |> ignore try - let r = Runner.LCovSummary baseline Base.ReportFormat.OpenCover 0 + let r = LCov.Summary baseline Base.ReportFormat.OpenCover 0 Assert.That (r, Is.EqualTo 0) let result = File.ReadAllText unique @@ -1464,7 +1464,7 @@ or let expected = reader.ReadToEnd().Replace("\r", String.Empty) Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) finally - Runner.lcov := None + LCov.path := None [] member self.NCoverShouldGeneratePlausibleLcov() = @@ -1476,11 +1476,11 @@ or let baseline = XDocument.Load(stream) let unique = Path.Combine(Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName, Guid.NewGuid().ToString() + "/NCover.lcov") - Runner.lcov := Some unique + LCov.path := Some unique unique |> Path.GetDirectoryName |> Directory.CreateDirectory |> ignore try - let r = Runner.LCovSummary baseline Base.ReportFormat.NCover 0 + let r = LCov.Summary baseline Base.ReportFormat.NCover 0 Assert.That (r, Is.EqualTo 0) let result = File.ReadAllText unique @@ -1493,7 +1493,7 @@ or let expected = reader.ReadToEnd().Replace("\r", String.Empty) Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) finally - Runner.lcov := None + LCov.path := None [] member self.MultiSortDoesItsThing() = @@ -1506,7 +1506,7 @@ or |> List.map (fun x -> x.Descendants(XName.Get "x") |> Seq.head) |> List.toSeq)) |> List.toSeq - let result = Runner.multiSortByNameAndStartLine input + let result = LCov.multiSortByNameAndStartLine input |> Seq.map (fun (f,ms) -> (f, ms |> Seq.map (fun m -> m.ToString().Replace("\r",String.Empty).Replace("\n",String.Empty).Replace(" <", "<")) |> Seq.toList)) From 2283a889ef10b3371c90a4492c114020ca690015 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Mon, 16 Apr 2018 09:02:59 +0100 Subject: [PATCH 02/48] And the rest --- Build/actions.fsx | 4 ++-- Build/targets.fsx | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Build/actions.fsx b/Build/actions.fsx index 1022a6a41..39c02875c 100644 --- a/Build/actions.fsx +++ b/Build/actions.fsx @@ -4,7 +4,7 @@ open System.Reflection open System.Xml open System.Xml.Linq -open Fake.Core.Environment +open Fake.Core open Fake.DotNet open Fake.IO.FileSystemOperators open Fake.IO @@ -33,7 +33,7 @@ module Actions = |> Seq.iter (fun n -> printfn "Deleting %s" n Directory.Delete(n, true)) - let temp = environVar "TEMP" + let temp = Environment.environVar "TEMP" if not <| String.IsNullOrWhiteSpace temp then Directory.GetFiles(temp, "*.tmp.dll.mdb") |> Seq.iter File.Delete diff --git a/Build/targets.fsx b/Build/targets.fsx index c4304ecb7..5589e91f6 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -5,7 +5,7 @@ open System.Xml.Linq open Actions -open Fake.Core.Environment +open Fake.Core open Fake.Core.Target open Fake.Core.TargetOperators open Fake.DotNet @@ -29,15 +29,15 @@ let AltCoverFilter= @" -s=Adapter -s=Mono -s=\.Recorder -s=Sample -s=nunit -e=Te let AltCoverFilterX= @" -s=Adapter --s=Mono -s=\.Recorder -s=Sample -s=nunit -t=System\. -t=Sample3\.Class2 " let AltCoverFilterG= @" -s=Adapter --s=Mono -s=\.Recorder\.g -s=Sample -s=nunit -e=Tests -t=System. -t=Sample3\.Class2 " -let programFiles = environVar "ProgramFiles" -let programFiles86 = environVar "ProgramFiles(x86)" +let programFiles = Environment.environVar "ProgramFiles" +let programFiles86 = Environment.environVar "ProgramFiles(x86)" let dotnetPath = "dotnet" |> Fake.Core.Process.tryFindFileOnPath let dotnetOptions (o:DotNet.Options) = match dotnetPath with | Some f -> {o with DotNetCliPath = f} | None -> o -let monoOnWindows = if isWindows then +let monoOnWindows = if Environment.isWindows then [programFiles; programFiles86] |> List.filter (String.IsNullOrWhiteSpace >> not) |> List.map (fun s -> s @@ "Mono/bin/mono.exe") @@ -45,7 +45,7 @@ let monoOnWindows = if isWindows then |> List.tryFind (fun _ -> true) else None -let dotnetPath86 = if isWindows then +let dotnetPath86 = if Environment.isWindows then let perhaps = [programFiles86] |> List.filter (String.IsNullOrWhiteSpace >> not) |> List.map (fun s -> s @@ "dotnet\dotnet.EXE") @@ -79,8 +79,8 @@ Target "Clean" (fun _ -> ) Target "SetVersion" (fun _ -> - let appveyor = environVar "APPVEYOR_BUILD_VERSION" - let travis = environVar "TRAVIS_JOB_NUMBER" + let appveyor = Environment.environVar "APPVEYOR_BUILD_VERSION" + let travis = Environment.environVar "TRAVIS_JOB_NUMBER" let version = Actions.GetVersionFromYaml () let ci = if String.IsNullOrWhiteSpace appveyor then if String.IsNullOrWhiteSpace travis then @@ -201,7 +201,7 @@ Target "Gendarme" (fun _ -> // Needs debug because release is compiled --standal Target "FxCop" (fun _ -> // Needs debug because release is compiled --standalone which contaminates everything Directory.ensure "./_Reports" - let vsInstallPath = if isWindows then + let vsInstallPath = if Environment.isWindows then use hklmKey = Microsoft.Win32.RegistryKey.OpenBaseKey( Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry32) @@ -540,7 +540,7 @@ Target "UnitTestWithAltCoverRunner" (fun _ -> File.WriteAllLines(coverage, Seq.concat [cover1; cover2; cover3] |> Seq.toArray) - if not <| String.IsNullOrWhiteSpace (environVar "APPVEYOR_BUILD_NUMBER") then + if not <| String.IsNullOrWhiteSpace (Environment.environVar "APPVEYOR_BUILD_NUMBER") then Actions.Run (fun info -> { info with FileName = findToolInSubPath "coveralls.net.exe" nugetCache @@ -938,7 +938,7 @@ Target "Packaging" (fun _ -> Publish = false ReleaseNotes = Path.getFullName "ReleaseNotes.md" |> File.ReadAllText - ToolPath = if isWindows then p.ToolPath else "/usr/bin/nuget" + ToolPath = if Environment.isWindows then p.ToolPath else "/usr/bin/nuget" }) "./Build/AltCover.nuspec" ) @@ -1508,7 +1508,7 @@ activateFinal "ResetConsoleColours" "Compilation" ==> "FxCop" -=?> ("Analysis", isWindows) // not supported +=?> ("Analysis", Environment.isWindows) // not supported "Compilation" ==> "Gendarme" @@ -1527,7 +1527,7 @@ activateFinal "ResetConsoleColours" "Compilation" ==> "UnitTestWithOpenCover" -=?> ("UnitTest", isWindows) // OpenCover Mono support +=?> ("UnitTest", Environment.isWindows) // OpenCover Mono support "Compilation" ==> "UnitTestWithAltCover" @@ -1594,14 +1594,14 @@ activateFinal "ResetConsoleColours" "Compilation" ==> "SelfTest" -=?> ("OperationalTest", isWindows) // OpenCover Mono support AND Mono + F# + Fake build => no symbols +=?> ("OperationalTest", Environment.isWindows) // OpenCover Mono support AND Mono + F# + Fake build => no symbols "Compilation" ?=> "Packaging" "Compilation" ==> "PrepareFrameworkBuild" -=?> ("Packaging", isWindows) // can't ILMerge +=?> ("Packaging", Environment.isWindows) // can't ILMerge "Compilation" ==> "PrepareDotNetBuild" From 0f4316c461727e40ca7558f50daeabe0a542b6d0 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Mon, 16 Apr 2018 11:31:51 +0100 Subject: [PATCH 03/48] UI for Cobertura report --- AltCover/Cobertura.fs | 7 +++++ AltCover/Runner.fs | 13 ++++++++ AltCover/Strings.eo.resx | 5 ++- AltCover/Strings.resx | 5 ++- AltCover/Tasks.fs | 2 ++ Tests/Runner.Tests.fs | 66 +++++++++++++++++++++++++++++++++++++++- Tests/Tests.fs | 2 ++ 7 files changed, 97 insertions(+), 3 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index abd4928cc..1a22dd314 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -1,5 +1,12 @@ namespace AltCover +open System +open System.Xml.Linq + module Cobertura = let internal path : Option ref = ref None + let Summary //(report:XDocument) (format:Base.ReportFormat) result = + _ _ _ = + NotImplementedException() |> raise + diff --git a/AltCover/Runner.fs b/AltCover/Runner.fs index 1ac58693b..1dfc5cbd9 100644 --- a/AltCover/Runner.fs +++ b/AltCover/Runner.fs @@ -242,6 +242,19 @@ module Runner = CommandLine.resources.GetString "InvalidValue", "--threshold", x) :: CommandLine.error)) + ("c|cobertura=", + (fun x -> if not (String.IsNullOrWhiteSpace(x)) then + if Option.isSome !Cobertura.path then + CommandLine.error <- String.Format(CultureInfo.CurrentCulture, + CommandLine.resources.GetString "MultiplesNotAllowed", + "--cobertura") :: CommandLine.error + else + Cobertura.path := x |> Path.GetFullPath |> Some + Summaries <- Cobertura.Summary :: Summaries + else CommandLine.error <- String.Format(CultureInfo.CurrentCulture, + CommandLine.resources.GetString "InvalidValue", + "--cobertura", + x) :: CommandLine.error)) ("?|help|h", (fun x -> CommandLine.help <- not (isNull x))) ("<>", (fun x -> CommandLine.error <- String.Format(CultureInfo.CurrentCulture, CommandLine.resources.GetString "InvalidValue", diff --git a/AltCover/Strings.eo.resx b/AltCover/Strings.eo.resx index 5b8e12680..2dbd333a2 100644 --- a/AltCover/Strings.eo.resx +++ b/AltCover/Strings.eo.resx @@ -156,7 +156,7 @@ AltCover [/i[nputDirectory]=VALO] [/o[utputDirectory]=VALO] [/y|symbolDirectory=VALO] [/sn|strongNameKey=VALO] [/k[ey]=VALO] [/x[mlReport]=VALO] [/f[ileFilter]=VALO] [/s|assemblyFilter=VALO] [/t|typeFilter=VALO] [/m|methodFilter=VALO] [/a|attributeFilter=VALO] [/c[allContext]=VALO] [--opencover] [--inplace] [--save] [/?|h[elp]] [-- ] [...] aŭ -AltCover Runner [/r[ecordingDirectory]=VALO] [/w[orkingDirectory]=VALO] [/x||executable=VALO] [--collect] [/l[covReport]=VALO] [/t[hreshold]=VALO] [/?|h[elp]] [-- ] [...] +AltCover Runner [/r[ecordingDirectory]=VALO] [/w[orkingDirectory]=VALO] [/x||executable=VALO] [--collect] [/l[covReport]=VALO] [/t[hreshold]=VALO] [/c[obertura]=VALO] [/?|h[elp]] [-- ] [...] Vidu https://github.com/SteveGilham/altcover/wiki/Usage por plenaj detaloj. @@ -312,4 +312,7 @@ Detaloj skribitaj al {0} Laŭvola: minimuma akceptebla kovra procento (entjero, 0 ĝis 100). Se la rezulto de kovraĵo estas sub sojlo, la rondkodo de la procezo estas (sojlo - reala) rondigita ĝis la plej proksima entjero. + + Laŭvola: Dosiero por la versio de formato Cobertura de la datumoj kolektitaj + \ No newline at end of file diff --git a/AltCover/Strings.resx b/AltCover/Strings.resx index b2afe8029..8a9e3fbeb 100644 --- a/AltCover/Strings.resx +++ b/AltCover/Strings.resx @@ -156,7 +156,7 @@ AltCover [/i[nputDirectory]=VALUE] [/o[utputDirectory]=VALUE] [/y|symbolDirectory=VALUE] [/sn|strongNameKey=VALUE] [/k[ey]=VALUE] [/x[mlReport]=VALUE] [/f[ileFilter]=VALUE] [/s|assemblyFilter=VALUE] [/t|typeFilter=VALUE] [/m|methodFilter=VALUE] [/a|attributeFilter=VALUE] [/c[allContext]=VALUE] [--opencover] [--inplace] [--save] [/?|h[elp]] [-- ] [...] or -AltCover Runner [/r[ecordingDirectory]=VALUE] [/w[orkingDirectory]=VALUE] [/x|executable=VALUE] [--collect] [/l[covReport]=VALUE] [/t[hreshold]=VALUE] [/?|h[elp]] [-- ] [...] +AltCover Runner [/r[ecordingDirectory]=VALUE] [/w[orkingDirectory]=VALUE] [/x|executable=VALUE] [--collect] [/l[covReport]=VALUE] [/t[hreshold]=VALUE] [/c[obertura]=VALUE] [/?|h[elp]] [-- ] [...] See https://github.com/SteveGilham/altcover/wiki/Usage for full details. @@ -313,4 +313,7 @@ Details written to {0} Optional: minimum acceptable coverage percentage (integer, 0 to 100). If the coverage result is below threshold, the return code of the process is (threshold - actual) rounded up to the nearest integer. + + Optional: File for Cobertura format version of the collected data + \ No newline at end of file diff --git a/AltCover/Tasks.fs b/AltCover/Tasks.fs index 642c4e8e0..9f323229f 100644 --- a/AltCover/Tasks.fs +++ b/AltCover/Tasks.fs @@ -85,6 +85,7 @@ type Collect () = member val Executable = String.Empty with get, set member val LcovReport = String.Empty with get, set member val Threshold = String.Empty with get, set + member val Cobertura = String.Empty with get, set member val CommandLine = String.Empty with get, set @@ -101,6 +102,7 @@ type Collect () = Args.Item "-x" self.Executable; Args.Item "-l" self.LcovReport; Args.Item "-t" self.Threshold; + Args.Item "-c" self.Cobertura; Args.Flag "--collect" (self.Executable |> String.IsNullOrWhiteSpace) diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index 0d73c7531..52e3b5f79 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -193,6 +193,8 @@ type AltCoverTests() = class below threshold, the return code of the process is (threshold - actual) rounded up to the nearest integer. + -c, --cobertura=VALUE Optional: File for Cobertura format version of the + collected data -?, --help, -h Prints out the options. """ @@ -251,7 +253,7 @@ type AltCoverTests() = class [] member self.ShouldHaveExpectedOptions() = let options = Runner.DeclareOptions () - Assert.That (options.Count, Is.EqualTo 8) + Assert.That (options.Count, Is.EqualTo 9) Assert.That(options |> Seq.filter (fun x -> x.Prototype <> "<>") |> Seq.forall (fun x -> (String.IsNullOrWhiteSpace >> not) x.Description)) Assert.That (options |> Seq.filter (fun x -> x.Prototype = "<>") |> Seq.length, Is.EqualTo 1) @@ -634,6 +636,66 @@ type AltCoverTests() = class finally Runner.threshold <- None + [] + member self.ParsingCoberturaGivesCobertura() = + lock Cobertura.path (fun () -> + try + Cobertura.path := None + Runner.Summaries <- [Runner.StandardSummary] + let options = Runner.DeclareOptions () + let unique = "some exe" + let input = [| "-c"; unique |] + let parse = CommandLine.ParseCommandLine input options + match parse with + | Left _ -> Assert.Fail() + | Right (x, y) -> Assert.That (y, Is.SameAs options) + Assert.That (x, Is.Empty) + + match !Cobertura.path with + | None -> Assert.Fail() + | Some x -> Assert.That(Path.GetFileName x, Is.EqualTo unique) + + Assert.That (Runner.Summaries.Length, Is.EqualTo 2) + finally + Runner.Summaries <- [Runner.StandardSummary] + Cobertura.path := None) + + [] + member self.ParsingMultipleCoberturaGivesFailure() = + lock Cobertura.path (fun () -> + try + Cobertura.path := None + Runner.Summaries <- [Runner.StandardSummary] + let options = Runner.DeclareOptions () + let unique = Guid.NewGuid().ToString() + let input = [| "-c"; unique; "/c"; unique.Replace("-", "+") |] + let parse = CommandLine.ParseCommandLine input options + match parse with + | Right _ -> Assert.Fail() + | Left (x, y) -> Assert.That (y, Is.SameAs options) + Assert.That (x, Is.EqualTo "UsageError") + finally + Runner.Summaries <- [Runner.StandardSummary] + Cobertura.path := None) + + [] + member self.ParsingNoCoberturaGivesFailure() = + lock Cobertura.path (fun () -> + try + Cobertura.path := None + Runner.Summaries <- [Runner.StandardSummary] + let options = Runner.DeclareOptions () + let blank = " " + let input = [| "-c"; blank; |] + let parse = CommandLine.ParseCommandLine input options + match parse with + | Right _ -> Assert.Fail() + | Left (x, y) -> Assert.That (y, Is.SameAs options) + Assert.That (x, Is.EqualTo "UsageError") + finally + Runner.Summaries <- [Runner.StandardSummary] + Cobertura.path := None) + [] member self.ParsingNoThresholdGivesFailure() = try @@ -945,6 +1007,8 @@ or below threshold, the return code of the process is (threshold - actual) rounded up to the nearest integer. + -c, --cobertura=VALUE Optional: File for Cobertura format version of the + collected data -?, --help, -h Prints out the options. """ diff --git a/Tests/Tests.fs b/Tests/Tests.fs index 7a5c40348..0c8501b6b 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -4869,6 +4869,8 @@ or below threshold, the return code of the process is (threshold - actual) rounded up to the nearest integer. + -c, --cobertura=VALUE Optional: File for Cobertura format version of the + collected data -?, --help, -h Prints out the options. """ From d0ce6de984578a88eb9af6ce0c70af102c5427cb Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Mon, 16 Apr 2018 11:39:01 +0100 Subject: [PATCH 04/48] Expand the stub (Gendarme doesn't like this step) --- AltCover/Cobertura.fs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 1a22dd314..c16966a40 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -6,7 +6,15 @@ open System.Xml.Linq module Cobertura = let internal path : Option ref = ref None - let Summary //(report:XDocument) (format:Base.ReportFormat) result = - _ _ _ = + let NCover _ = //(report:XDocument) NotImplementedException() |> raise + let OpenCover _ = //(report:XDocument) + NotImplementedException() |> raise + + let Summary (report:XDocument) (format:Base.ReportFormat) result = + match format with + | Base.ReportFormat.NCover -> NCover report + | _ -> OpenCover report + result + From b701cb88fed83d90e7efcf3aec3d000a5ae415d9 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Wed, 18 Apr 2018 17:53:51 +0100 Subject: [PATCH 05/48] Proof and prepare release notes --- ReleaseNotes.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 2efab4201..035f8d25c 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,8 +1,10 @@ +# 3.0.4xx (Caba series release 8) + # 3.0.433 (Caba series release 7) * `-t|threshold` option in `runner` mode (parameter `Threshold` for the `AltCover.Collect` task) to fail the build (non-zero return code or MSBuild error state) if coverage falls below the specified percentage * [BUGFIX] -- Parameters weren't being re-initialised on the second or subsequent use of the MSBuild tasks, leading to failures about arguments being specified more than once (Issue #10) -* FIXED AGAIN : reinstate earlier change [f61f951] to write .mdb out for .mdb in (and pdb out for pdb in) -- at least on .net core and full .net (long story) * [BUGFIX] -- ArgumentNullException when relaying output from a launched process: filter null/empty messages +* FIXED AGAIN : reinstate earlier change [f61f951] to write .mdb out for .mdb in (and pdb out for pdb in) -- at least on .net core and full .net (long story) * Moving some of the unit tests to XUnit for reasons related to the above # 3.0.422 (Caba series release 6) @@ -13,7 +15,7 @@ # 3.0.416 (Caba series release 5) * Exclude constructors on compiler generated types as being simply noise -- they will be exercised if you use any of the real code they represent, so nothing of importance is lost * C# compiler generated types for lambdas, `async`/`await` and `yield return` are mapped to their containing methods for the purpose of filtering by method name or method level attributes -* F# compiler generated types for lambdas, nested named functions and computation expressions are mapped to their containing methods (and their containing methods, if relevant) for the purpose of filtering by method name or method level attributes to that a filter at any level will be picked up by deeply nested inner functions +* F# compiler generated types for lambdas, nested named functions and computation expressions are mapped to their containing methods (and their containing methods, if relevant) for the purpose of filtering by method name or method level attributes so that a filter at any level will be picked up by deeply nested inner functions * Even more feedback on error, including logging exception detail to file. * [BUGFIX] Mono.Cecil can give `ArgumentException` when given an arbitrary file input (while detecting which files are instrumentable assemblies); handle that case From 5a405e5bdc24c96e232d512a5292deb26a73991e Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 08:40:37 +0100 Subject: [PATCH 06/48] Add red test for the issue --- Build/targets.fsx | 27 ++++++++++++++ Sample7/Library.fs | 72 +++++++++++++++++++++++++++++++++++++ Sample7/sample7.core.fsproj | 33 +++++++++++++++++ altcover.core.sln | 12 ++++++- 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 Sample7/Library.fs create mode 100644 Sample7/sample7.core.fsproj diff --git a/Build/targets.fsx b/Build/targets.fsx index 0bd9b498a..cc3071cd9 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -810,6 +810,29 @@ Target "FSharpTypesDotNet" ( fun _ -> Actions.ValidateFSharpTypesCoverage simpleReport ) +Target "FSharpTests" ( fun _ -> + Directory.ensure "./_Reports" + let altcover = Path.getFullName "./_Binaries/AltCover/Release+AnyCPU/netcoreapp2.0/AltCover.dll" + let simpleReport = (Path.getFullName "./_Reports") @@ ( "AltCoverFSharpTests.xml") + let sampleRoot = Path.getFullName "Sample7/_Binaries/Sample7/Debug+AnyCPU/netcoreapp2.0" + + // Test the --inplace operation + Shell.CleanDir sampleRoot + Actions.RunDotnet (fun o -> {dotnetOptions o with WorkingDirectory = Path.getFullName "Sample7"}) "test" + ("--configuration Debug sample7.core.fsproj") + "sample initial test returned with a non-zero exit code" + + // inplace instrument + Actions.RunDotnet (fun o -> {dotnetOptions o with WorkingDirectory = sampleRoot}) "" + (altcover + " --inplace -c=[Test] -s=Adapter -t \"System\\.\" -t \"Microsoft\\.\" -x \"" + simpleReport + "\" ") + "FSharpTypesDotNet" + + printfn "Execute the instrumented tests" + Actions.RunDotnet (fun o -> {dotnetOptions o with WorkingDirectory = Path.getFullName "Sample7"}) "test" + ("--no-build --configuration Debug sample7.core.fsproj") + "sample coverage test returned with a non-zero exit code" +) + Target "FSharpTypesDotNetRunner" ( fun _ -> Directory.ensure "./_Reports" let altcover = Path.getFullName "./_Binaries/AltCover/Release+AnyCPU/netcoreapp2.0/AltCover.dll" @@ -1650,6 +1673,10 @@ activateFinal "ResetConsoleColours" ==> "FSharpTypes" ==> "OperationalTest" +"Compilation" +==> "FSharpTests" +//==> "OperationalTest" + "Compilation" ==> "FSharpTypesDotNet" // ==> "OperationalTest" diff --git a/Sample7/Library.fs b/Sample7/Library.fs new file mode 100644 index 000000000..70253fff4 --- /dev/null +++ b/Sample7/Library.fs @@ -0,0 +1,72 @@ +namespace Tests + +open NUnit.Framework +open FsUnitTyped + +type Size(width : int, height : int) = + struct + member __.Width = width + member __.Height = height + end + +type Thing(size : Size) = + member val Height = size.Height + member val Width = size.Width + member __.Rectangle (x, y) = + System.Drawing.Rectangle(x, y, x, y) + +module Tested = + let add6To n = + n + 6 + +open Tested + +module Problematic = + [] + let ``Using FsUnit`` () = + add6To 3 |> shouldEqual 9 + + [] + let ``Using FsUnit bis`` () = + try + add6To 3 |> shouldEqual 9 + finally + System.Console.WriteLine("Finally") + + //let add6To n = + // n + 6 + + //let inline shouldBeOdd x = + // if x % 2 = 0 then Assert.Fail "Not odd enough" + + //[] + //let ``add6To3 should be odd`` () = + // add6To 3 |> shouldBeOdd + + //[] + //let ``add6To3 should be odd bis`` () = + // try + // add6To 3 |> shouldBeOdd + // finally + // System.Console.WriteLine("Finally") + + //[] + //let ``Thing Rectangle`` () = + // let g = Thing (Size(11, 21)) + // for row = 0 to g.Height do + // for col = 0 to g.Width do + // Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) + + //[] + //let ``Thing Rectangle bis`` () = + // try + // let g = Thing (Size(11, 21)) + // for row = 0 to g.Height do + // for col = 0 to g.Width do + // Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) + // finally + // System.Console.WriteLine("Finally") + +#if NETCOREAPP2_0 +module Program = let [] main _ = 0 +#endif \ No newline at end of file diff --git a/Sample7/sample7.core.fsproj b/Sample7/sample7.core.fsproj new file mode 100644 index 000000000..dfdb50ed7 --- /dev/null +++ b/Sample7/sample7.core.fsproj @@ -0,0 +1,33 @@ + + + + netcoreapp2.0 + Sample7 + $(AssetTargetFallback);netcoreapp1.0; + + + + $(SolutionDir)_Binaries/$(AssemblyName)/$(Configuration)+$(Platform)/ + $(SolutionDir)_Intermediate/$(AssemblyName)/$(Configuration)+$(Platform)/ + + + $(SolutionDir)_Binaries/$(AssemblyName)/$(Configuration)+$(Platform)/ + $(SolutionDir)_Intermediate/$(AssemblyName)/$(Configuration)+$(Platform)/ + + + + + + + + + + + + + + + + + + diff --git a/altcover.core.sln b/altcover.core.sln index f8b75d3b3..5f97a60e7 100644 --- a/altcover.core.sln +++ b/altcover.core.sln @@ -10,7 +10,7 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "altcover.shadow.core", "Alt EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "altcover.tests.core", "Tests/altcover.tests.core.fsproj", "{440A79BE-7038-44EA-B683-3ED8EFAEC39C}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "altcover.x.tests.core", "XTests/altcover.x.tests.core.fsproj", "{6EC68908-866F-453F-B38E-30C76A438CBC}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "altcover.x.tests.core", "XTests/altcover.x.tests.core.fsproj", "{6EC68908-866F-453F-B38E-30C76A438CBC}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "altcover.adapter.core", "Shadow.Adapter/altcover.adapter.core.fsproj", "{DDF0D5A2-370B-4827-B5D3-2A258E6677F1}" EndProject @@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sample5.core", "Sample5/sam EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "sample6.core", "Sample6/sample6.core.fsproj", "{54F49C6B-AF20-42E4-95A0-386296CB9125}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "sample7.core", "Sample7/sample7.core.fsproj", "{8E0DC7BC-C322-466C-8409-B69A12062AB6}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{2837CE07-B91F-4B8A-89B5-E7BE39A8F340}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{97367D43-64EF-48E1-B6B4-D951C783E6E1}" @@ -152,6 +154,13 @@ Global {54F49C6B-AF20-42E4-95A0-386296CB9125}.Release|Any CPU.ActiveCfg = Release|Any CPU {54F49C6B-AF20-42E4-95A0-386296CB9125}.Release|x64.ActiveCfg = Release|Any CPU {54F49C6B-AF20-42E4-95A0-386296CB9125}.Release|x86.ActiveCfg = Release|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Release|x64.ActiveCfg = Release|Any CPU + {8E0DC7BC-C322-466C-8409-B69A12062AB6}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -163,6 +172,7 @@ Global {358265A1-C7B3-48E8-9707-2F344ECAAE9F} = {2837CE07-B91F-4B8A-89B5-E7BE39A8F340} {8510ED92-510C-42D7-9E4D-85810B4DE86B} = {2837CE07-B91F-4B8A-89B5-E7BE39A8F340} {54F49C6B-AF20-42E4-95A0-386296CB9125} = {2837CE07-B91F-4B8A-89B5-E7BE39A8F340} + {8E0DC7BC-C322-466C-8409-B69A12062AB6} = {2837CE07-B91F-4B8A-89B5-E7BE39A8F340} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C31111CF-68A2-403F-9B06-9625FCBD48E3} From e21e1b14c3ea16ae448a9911759cb1570a5d03ba Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 09:47:11 +0100 Subject: [PATCH 07/48] Remove tail instructions where found --- AltCover/Instrument.fs | 17 +++++++++++++---- Tests/Tests.fs | 5 ++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/AltCover/Instrument.fs b/AltCover/Instrument.fs index 3ab56db45..ea7691262 100644 --- a/AltCover/Instrument.fs +++ b/AltCover/Instrument.fs @@ -403,19 +403,19 @@ module Instrument = MethodWorker = body.GetILProcessor() } | _ -> state - let private UpdateBranchReferences state instruction injected = + let private UpdateBranchReferences (body:MethodBody) instruction injected = // Change references in operands from "instruction" to first counter invocation instruction (instrLoadModuleId) let subs = SubstituteInstruction (instruction, injected) - state.MethodBody.Instructions + body.Instructions |> Seq.iter subs.SubstituteInstructionOperand - state.MethodBody.ExceptionHandlers + body.ExceptionHandlers |> Seq.iter subs.SubstituteExceptionBoundary let private VisitMethodPoint (state : Context) instruction point included = if included then // by construction the sequence point is included let instrLoadModuleId = InsertVisit instruction state.MethodWorker state.RecordingMethodRef.Visit state.ModuleId point - UpdateBranchReferences state instruction instrLoadModuleId + UpdateBranchReferences state.MethodBody instruction instrLoadModuleId state let internal VisitBranchPoint (state:Context) branch = @@ -504,6 +504,10 @@ module Instrument = |> Seq.filter (fun i -> i.OpCode = OpCodes.Ret) |> Seq.toList + let tailcalls = instructions + |> Seq.filter (fun i -> i.OpCode = OpCodes.Tail) + |> Seq.toList + let tail = instructions |> Seq.last let popper = methodWorker.Create(OpCodes.Call, state.RecordingMethodRef.Pop) methodWorker.InsertAfter(tail, popper) @@ -514,8 +518,13 @@ module Instrument = rets |> Seq.iter (fun i -> let leave = methodWorker.Create(OpCodes.Leave, ret) + UpdateBranchReferences body i leave methodWorker.Replace (i, leave) ) + tailcalls + |> Seq.iter (fun i -> UpdateBranchReferences body i i.Next + methodWorker.Remove i) + let handler = ExceptionHandler(ExceptionHandlerType.Finally) handler.TryStart <- instructions |> Seq.head handler.TryEnd <- popper diff --git a/Tests/Tests.fs b/Tests/Tests.fs index 512ad86cc..b2dd26382 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -2320,10 +2320,13 @@ type AltCoverTests() = class Push = recorder.[1] Pop = recorder.[2] }} let countBefore = recorder.Head.Body.Instructions.Count + let tailsBefore = recorder.Head.Body.Instructions + |> Seq.filter (fun i -> i.OpCode = OpCodes.Tail) + |> Seq.length let handlersBefore = recorder.Head.Body.ExceptionHandlers.Count AltCover.Instrument.Track state recorder.Head Inspect.Track <| Some(42, "hello") - Assert.That (recorder.Head.Body.Instructions.Count, Is.EqualTo (countBefore + 5)) + Assert.That (recorder.Head.Body.Instructions.Count, Is.EqualTo (countBefore + 5 - tailsBefore)) Assert.That (recorder.Head.Body.ExceptionHandlers.Count, Is.EqualTo (handlersBefore + 1)) [] From d61cc3f64772a3dc3eb184f321e6435b98f3e041 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 11:05:39 +0100 Subject: [PATCH 08/48] Add operational testing to the partial unit -test support (.net core test has a tail., but the framework build doesn't) --- Build/targets.fsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build/targets.fsx b/Build/targets.fsx index cc3071cd9..21b830511 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -1675,7 +1675,7 @@ activateFinal "ResetConsoleColours" "Compilation" ==> "FSharpTests" -//==> "OperationalTest" +==> "OperationalTest" "Compilation" ==> "FSharpTypesDotNet" From 8d86b3cc80ea24827d6ec64fa767dffa20ec1f7e Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 13:26:55 +0100 Subject: [PATCH 09/48] Properly integrate the XUnit tests --- Build/actions.fsx | 1 + Build/targets.fsx | 14 ++++++++++++-- XTests/XTests.fs | 45 +++++++++++++++++++++++++++++++------------- XTests/XTests.fsproj | 5 ++++- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Build/actions.fsx b/Build/actions.fsx index 051577efc..3e092b311 100644 --- a/Build/actions.fsx +++ b/Build/actions.fsx @@ -22,6 +22,7 @@ module Actions = |> Seq.filter (fun n -> match n.Name with | "obj" -> Path.Combine(n.FullName, "dotnet-fake.fsproj.nuget.g.props") |> File.Exists |> not | _ -> true) + |> Seq.filter (fun n -> "packages" |> Path.GetFullPath |> n.FullName.StartsWith |> not) |> Seq.map (fun x -> x.FullName) |> Seq.distinct // arrange so leaves get deleted first, avoiding "does not exist" warnings diff --git a/Build/targets.fsx b/Build/targets.fsx index 0bd9b498a..b955d1679 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -104,6 +104,14 @@ Target "SetVersion" (fun _ -> AssemblyInfo.Trademark "" AssemblyInfo.Copyright copy ] + + let hack = """namespace AltCover +module SolutionRoot = + let location = """ + "\"\"\"" + (Path.getFullName ".") + "\"\"\"" + let path = "_Generated/SolutionRoot.fs" + // Update the file only if it would change + let old = if File.Exists(path) then File.ReadAllText(path) else String.Empty + if not (old.Equals(hack)) then File.WriteAllText(path, hack) ) // Basic compilation @@ -299,7 +307,8 @@ Target "JustUnitTest" (fun _ -> ShadowCopy = false}) !! (@"_Binaries/*Tests/Debug+AnyCPU/*Test*.dll") - |> Seq.filter (fun f -> Path.GetFileName(f) <> "AltCover.XTests.dll") + |> Seq.filter (fun f -> Path.GetFileName(f) <> "AltCover.XTests.dll" && + Path.GetFileName(f) <> "xunit.runner.visualstudio.testadapter.dll") |> NUnit3.run (fun p -> { p with ToolPath = findToolInSubPath "nunit3-console.exe" "." WorkingDir = "." Labels = LabelsLevel.All @@ -325,7 +334,8 @@ Target "UnitTestDotNet" (fun _ -> Target "UnitTestWithOpenCover" (fun _ -> Directory.ensure "./_Reports/_UnitTestWithOpenCover" let testFiles = !! (@"_Binaries/*Tests/Debug+AnyCPU/*Test*.dll") - |> Seq.filter (fun f -> Path.GetFileName(f) <> "AltCover.XTests.dll") + |> Seq.filter (fun f -> Path.GetFileName(f) <> "AltCover.XTests.dll" && + Path.GetFileName(f) <> "xunit.runner.visualstudio.testadapter.dll") let xtestFiles = !! (@"_Binaries/*Tests/Debug+AnyCPU/*XTest*.dll") let coverage = Path.getFullName "_Reports/UnitTestWithOpenCover.xml" diff --git a/XTests/XTests.fs b/XTests/XTests.fs index d3947a054..e75a37616 100644 --- a/XTests/XTests.fs +++ b/XTests/XTests.fs @@ -19,6 +19,14 @@ module XTests = | 0 -> "/.." | _ -> String.Empty + let SolutionDir() = +#if NETCOREAPP2_0 + let where = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + where.Substring(0, where.IndexOf("_Binaries")) +#else + SolutionRoot.location +#endif + #if NETCOREAPP2_0 let sample1 = "Sample1.dll" let monoSample1 = "../_Mono/Sample1" @@ -125,8 +133,9 @@ module XTests = [] let ADotNetDryRunLooksAsExpected() = let where = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - let path = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), "_Binaries/Sample4/Debug+AnyCPU/netcoreapp2.0") - let key0 = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), "Build/SelfTest.snk") + let here = SolutionDir() + let path = Path.Combine(here, "_Binaries/Sample4/Debug+AnyCPU/netcoreapp2.0") + let key0 = Path.Combine(here, "Build/SelfTest.snk") #if NETCOREAPP2_0 let input = if Directory.Exists path then path else Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), "../_Binaries/Sample4/Debug+AnyCPU/netcoreapp2.0") @@ -295,8 +304,9 @@ module XTests = [] let ADryRunLooksAsExpected() = let where = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - let path = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), "_Mono/Sample1") - let key0 = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), "Build/SelfTest.snk") + let here = SolutionDir() + let path = Path.Combine(here, "_Mono/Sample1") + let key0 = Path.Combine(here, "Build/SelfTest.snk") #if NETCOREAPP2_0 let input = if Directory.Exists path then path else Path.Combine(where.Substring(0, where.IndexOf("_Binaries")), monoSample1) @@ -417,8 +427,10 @@ module XTests = [] let AfterAssemblyCommitsThatAssembly () = - let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(Path.GetDirectoryName(where) + Hack(), "Sample4.dll") + let hack = Path.Combine(SolutionDir(), "_Binaries/AltCover.XTests/Debug+AnyCPU") + let local = Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName + let where = if local.IndexOf("_Binaries") > 0 then local else hack + let path = Path.Combine(where + Hack(), "Sample4.dll") let def = Mono.Cecil.AssemblyDefinition.ReadAssembly path ProgramDatabase.ReadSymbols def @@ -444,7 +456,8 @@ module XTests = let AfterAssemblyCommitsThatAssemblyForMono () = // Hack for running while instrumented let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + "_Mono/Sample1", "Sample1.exe") + let here = SolutionDir() + let path = Path.Combine(here, "_Mono/Sample1/Sample1.exe") #if NETCOREAPP2_0 let path' = if File.Exists path then path else Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + monoSample1, "Sample1.exe") @@ -481,8 +494,10 @@ module XTests = [] let FinishCommitsTheRecordingAssembly () = - let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(Path.GetDirectoryName(where) + Hack(), "Sample4.dll") + let hack = Path.Combine(SolutionDir(), "_Binaries/AltCover.XTests/Debug+AnyCPU") + let local = Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName + let where = if local.IndexOf("_Binaries") > 0 then local else hack + let path = Path.Combine(where + Hack(), "Sample4.dll") let def = Mono.Cecil.AssemblyDefinition.ReadAssembly path ProgramDatabase.ReadSymbols def @@ -510,7 +525,9 @@ module XTests = [] let ShouldDoCoverage() = let start = Directory.GetCurrentDirectory() - let here = (Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName) + let hack = Path.Combine(SolutionDir(), "_Binaries/AltCover.XTests/Debug+AnyCPU") + let local = Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName + let here = if local.IndexOf("_Binaries") > 0 then local else hack let where = Path.Combine(here, Guid.NewGuid().ToString()) Directory.CreateDirectory(where) |> ignore Directory.SetCurrentDirectory where @@ -568,7 +585,8 @@ module XTests = let visitor, document = Report.ReportGenerator() // Hack for running while instrumented let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + "_Mono/Sample1", "Sample1.exe") + let here = SolutionDir() + let path = Path.Combine(here, "_Mono/Sample1/Sample1.exe") #if NETCOREAPP2_0 let path' = if File.Exists path then path else Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + monoSample1, "Sample1.exe") @@ -587,9 +605,10 @@ module XTests = let ShouldGenerateExpectedXmlReportFromMonoOpenCoverStyle() = let visitor, document = OpenCover.ReportGenerator() // Hack for running while instrumented - let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + "_Mono/Sample1", "Sample1.exe") + let here = SolutionDir() + let path = Path.Combine(here, "_Mono/Sample1/Sample1.exe") #if NETCOREAPP2_0 + let where = Assembly.GetExecutingAssembly().Location let path' = if File.Exists path then path else Path.Combine(where.Substring(0, where.IndexOf("_Binaries")) + monoSample1, "Sample1.exe") #else diff --git a/XTests/XTests.fsproj b/XTests/XTests.fsproj index 42ff83f11..801295751 100644 --- a/XTests/XTests.fsproj +++ b/XTests/XTests.fsproj @@ -1,6 +1,6 @@ - + @@ -67,6 +67,9 @@ AssemblyVersion.fs + + SolutionRoot.fs + From cf72f18061412133244cd393ec29fda5d842acb2 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 13:51:01 +0100 Subject: [PATCH 10/48] Bump up the unit test in the framework tests by examining the .net core build --- Tests/Tests.fs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/Tests.fs b/Tests/Tests.fs index b2dd26382..ddff76859 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -2310,7 +2310,13 @@ type AltCoverTests() = class [] member self.ShouldBeAbleToTrackAMethod () = let where = Assembly.GetExecutingAssembly().Location - let path = Path.Combine(Path.GetDirectoryName(where) + AltCoverTests.Hack(), "AltCover.Recorder.dll") +#if NETCOREAPP2_0 + let shift = String.Empty +#else + let shift = "\\netcoreapp2.0" +#endif + let path = Path.Combine(Path.GetDirectoryName(where) + AltCoverTests.Hack() + + shift, "AltCover.Recorder.dll") let def = Mono.Cecil.AssemblyDefinition.ReadAssembly path let recorder = AltCover.Instrument.RecordingMethod def let raw = AltCover.Instrument.Context.Build([]) From 383d5ed19c2936efd4a7e9fb536f6f57cb99504b Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 13:55:01 +0100 Subject: [PATCH 11/48] Purge broken caches --- .travis.yml | 1 - appveyor.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b533df4a4..607470b47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ if: NOT branch =~ ^develop/.*$ OR branch =~ ^develop/travis/.*$ cache: directories: - - packages - Demo/Service/packages - $HOME/.nuget diff --git a/appveyor.yml b/appveyor.yml index 4b55f80e6..a917b7ec7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,6 @@ environment: COVERALLS_REPO_TOKEN: secure: qgIIfE76i59qTVO64mT7QwKcAUUYCQLie4XdUdlnt7MXLhrwqTbcfGUsCss/TmLg cache: - - packages -> **\packages.config - Demo\Service\packages -> Demo\Service\Service\packages.config - '%USERPROFILE%\.nuget\packages -> **\*.*proj' nuget: From 5dccad750f98da0151dcfd1932ae4847a0217ab0 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 13:55:23 +0100 Subject: [PATCH 12/48] Revert "Purge broken caches" This reverts commit 383d5ed19c2936efd4a7e9fb536f6f57cb99504b. --- .travis.yml | 1 + appveyor.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 607470b47..b533df4a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ if: NOT branch =~ ^develop/.*$ OR branch =~ ^develop/travis/.*$ cache: directories: + - packages - Demo/Service/packages - $HOME/.nuget diff --git a/appveyor.yml b/appveyor.yml index a917b7ec7..4b55f80e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,7 @@ environment: COVERALLS_REPO_TOKEN: secure: qgIIfE76i59qTVO64mT7QwKcAUUYCQLie4XdUdlnt7MXLhrwqTbcfGUsCss/TmLg cache: + - packages -> **\packages.config - Demo\Service\packages -> Demo\Service\Service\packages.config - '%USERPROFILE%\.nuget\packages -> **\*.*proj' nuget: From e448d6a6ac369208cc57f427e672e24f2dfca8c9 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 14:11:42 +0100 Subject: [PATCH 13/48] Fix *mix build --- Tests/Tests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Tests.fs b/Tests/Tests.fs index ddff76859..e69522d43 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -2313,7 +2313,7 @@ type AltCoverTests() = class #if NETCOREAPP2_0 let shift = String.Empty #else - let shift = "\\netcoreapp2.0" + let shift = "/netcoreapp2.0" #endif let path = Path.Combine(Path.GetDirectoryName(where) + AltCoverTests.Hack() + shift, "AltCover.Recorder.dll") From dd36fffaf83ce00d57fa064190ad86a9ebbf7b92 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 14:26:11 +0100 Subject: [PATCH 14/48] The branch retargeting seems to have fixed this one as well. --- Sample7/Library.fs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Sample7/Library.fs b/Sample7/Library.fs index 70253fff4..fe627760b 100644 --- a/Sample7/Library.fs +++ b/Sample7/Library.fs @@ -33,22 +33,22 @@ module Problematic = finally System.Console.WriteLine("Finally") - //let add6To n = - // n + 6 + let add6To n = + n + 6 - //let inline shouldBeOdd x = - // if x % 2 = 0 then Assert.Fail "Not odd enough" + let inline shouldBeOdd x = + if x % 2 = 0 then Assert.Fail "Not odd enough" - //[] - //let ``add6To3 should be odd`` () = - // add6To 3 |> shouldBeOdd + [] + let ``add6To3 should be odd`` () = + add6To 3 |> shouldBeOdd - //[] - //let ``add6To3 should be odd bis`` () = - // try - // add6To 3 |> shouldBeOdd - // finally - // System.Console.WriteLine("Finally") + [] + let ``add6To3 should be odd bis`` () = + try + add6To 3 |> shouldBeOdd + finally + System.Console.WriteLine("Finally") //[] //let ``Thing Rectangle`` () = From 22564bde6886649fe768ae887814bc070acaa4d2 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Thu, 19 Apr 2018 15:28:27 +0100 Subject: [PATCH 15/48] Enable the final test, and watch it pass --- Build/targets.fsx | 2 +- Sample7/Library.fs | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Build/targets.fsx b/Build/targets.fsx index cc3071cd9..07b6707bb 100644 --- a/Build/targets.fsx +++ b/Build/targets.fsx @@ -824,7 +824,7 @@ Target "FSharpTests" ( fun _ -> // inplace instrument Actions.RunDotnet (fun o -> {dotnetOptions o with WorkingDirectory = sampleRoot}) "" - (altcover + " --inplace -c=[Test] -s=Adapter -t \"System\\.\" -t \"Microsoft\\.\" -x \"" + simpleReport + "\" ") + (altcover + " --opencover --inplace -c=[Test] -s=Adapter -t \"System\\.\" -t \"Microsoft\\.\" -x \"" + simpleReport + "\" ") "FSharpTypesDotNet" printfn "Execute the instrumented tests" diff --git a/Sample7/Library.fs b/Sample7/Library.fs index fe627760b..33a88feab 100644 --- a/Sample7/Library.fs +++ b/Sample7/Library.fs @@ -50,22 +50,22 @@ module Problematic = finally System.Console.WriteLine("Finally") - //[] - //let ``Thing Rectangle`` () = - // let g = Thing (Size(11, 21)) - // for row = 0 to g.Height do - // for col = 0 to g.Width do - // Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) + [] + let ``Thing Rectangle`` () = + let g = Thing (Size(11, 21)) + for row = 0 to g.Height do + for col = 0 to g.Width do + Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) - //[] - //let ``Thing Rectangle bis`` () = - // try - // let g = Thing (Size(11, 21)) - // for row = 0 to g.Height do - // for col = 0 to g.Width do - // Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) - // finally - // System.Console.WriteLine("Finally") + [] + let ``Thing Rectangle bis`` () = + try + let g = Thing (Size(11, 21)) + for row = 0 to g.Height do + for col = 0 to g.Width do + Assert.AreEqual(System.Drawing.Rectangle(col, row, col, row), g.Rectangle(col, row)) + finally + System.Console.WriteLine("Finally") #if NETCOREAPP2_0 module Program = let [] main _ = 0 From fc5e7fdeac85d226e8b109637b634bf31dda1830 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 08:57:16 +0100 Subject: [PATCH 16/48] Update release notes with bugfix information -- issues #13, #14, #15 --- ReleaseNotes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 035f8d25c..d88c2f5fb 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,7 @@ # 3.0.4xx (Caba series release 8) +* [BUGFIX] for `-c|callContext` option where the conext function completes with a tail call +* [BUGFIX] for `-c|callContext` option where the context function contains a branch directly to the return instruction + # 3.0.433 (Caba series release 7) * `-t|threshold` option in `runner` mode (parameter `Threshold` for the `AltCover.Collect` task) to fail the build (non-zero return code or MSBuild error state) if coverage falls below the specified percentage From 3bc07d4e57984c660531e7d4be53d904de3e63c5 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 10:06:24 +0100 Subject: [PATCH 17/48] Initial stages of Cobertura-style output --- AltCover/Cobertura.fs | 44 +++++++++++++++++++--- Tests/NCover.cob | 6 +++ Tests/OpenCover.cob | 6 +++ Tests/Runner.Tests.fs | 63 ++++++++++++++++++++++++++++++++ Tests/Tests.fsproj | 2 + Tests/altcover.tests.core.fsproj | 2 + 6 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 Tests/NCover.cob create mode 100644 Tests/OpenCover.cob diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index c16966a40..ee352b7ec 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -1,20 +1,52 @@ namespace AltCover open System +open System.IO open System.Xml.Linq module Cobertura = let internal path : Option ref = ref None + let X = OpenCover.X - let NCover _ = //(report:XDocument) - NotImplementedException() |> raise + let NCover (report:XDocument) (packages:XElement) = + report.Descendants(X "module") + |> Seq.iter (fun m -> let package = XElement(X "package", + XAttribute(X "name", m.Attribute(X "name").Value)) + packages.Add(package) + ) + packages.Parent.SetAttributeValue(X "branch-rate", null) - let OpenCover _ = //(report:XDocument) - NotImplementedException() |> raise + + + + let OpenCover (report:XDocument) (packages:XElement) = + report.Descendants(X "Module") + |> Seq.filter(fun m -> m.Descendants(X "Class") |> Seq.isEmpty |> not) + |> Seq.iter (fun m -> let package = XElement(X "package", + XAttribute(X "name", + m.Descendants(X "ModuleName") + |> Seq.map (fun x -> x.Value) + |> Seq.head)) + packages.Add(package) + ) let Summary (report:XDocument) (format:Base.ReportFormat) result = + let rewrite = XDocument(XDeclaration("1.0", "utf-8", "yes"), [||]) + let element = XElement(X "coverage", + XAttribute(X "line-rate", 0), + XAttribute(X "branch-rate", 0), + XAttribute(X "version", AssemblyVersionInformation.AssemblyVersion), + XAttribute(X "timestamp", int((DateTime.UtcNow - DateTime(1970,1,1,0,0,0,DateTimeKind.Utc)).TotalSeconds)) + ) + + rewrite.Add(element) + let packages = XElement(X "packages") + element.Add(packages) + match format with - | Base.ReportFormat.NCover -> NCover report - | _ -> OpenCover report + | Base.ReportFormat.NCover -> NCover report packages + | _ -> OpenCover report packages + + rewrite.Save(!path |> Option.get) result diff --git a/Tests/NCover.cob b/Tests/NCover.cob new file mode 100644 index 000000000..fd95aa766 --- /dev/null +++ b/Tests/NCover.cob @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob new file mode 100644 index 000000000..9b5b14213 --- /dev/null +++ b/Tests/OpenCover.cob @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index 062c6e0de..cd6b47c88 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -5,6 +5,7 @@ open System.Collections.Generic open System.IO open System.IO.Compression open System.Reflection +open System.Text.RegularExpressions open System.Threading open System.Xml open System.Xml.Linq @@ -1543,4 +1544,66 @@ or ("z", [ """""" """""" ]) ]) + [] + member self.NCoverShouldGeneratePlausibleCobertura() = + let resource = Assembly.GetExecutingAssembly().GetManifestResourceNames() + |> Seq.find (fun n -> n.EndsWith("SimpleCoverage.xml", StringComparison.Ordinal)) + + use stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource) + + let baseline = XDocument.Load(stream) + let unique = Path.Combine(Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + "/NCover.cob") + Cobertura.path := Some unique + unique |> Path.GetDirectoryName |> Directory.CreateDirectory |> ignore + + try + let r = Cobertura.Summary baseline Base.ReportFormat.NCover 0 + Assert.That (r, Is.EqualTo 0) + + let result = Regex.Replace(File.ReadAllText unique, + """timestamp=\"\d*\">""", + """timestamp="xx">""") + + let resource2 = Assembly.GetExecutingAssembly().GetManifestResourceNames() + |> Seq.find (fun n -> n.EndsWith("NCover.cob", StringComparison.Ordinal)) + + use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) + use reader = new StreamReader(stream2) + let expected = reader.ReadToEnd().Replace("\r", String.Empty) + Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + finally + Cobertura.path := None + + [] + member self.OpenCoverShouldGeneratePlausibleCobertura() = + let resource = Assembly.GetExecutingAssembly().GetManifestResourceNames() + |> Seq.find (fun n -> n.EndsWith("Sample1WithOpenCover.xml", StringComparison.Ordinal)) + + use stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource) + + let baseline = XDocument.Load(stream) + let unique = Path.Combine(Assembly.GetExecutingAssembly().Location |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + "/OpenCover.cob") + Cobertura.path := Some unique + unique |> Path.GetDirectoryName |> Directory.CreateDirectory |> ignore + + try + let r = Cobertura.Summary baseline Base.ReportFormat.OpenCover 0 + Assert.That (r, Is.EqualTo 0) + + let result = Regex.Replace(File.ReadAllText unique, + """timestamp=\"\d*\">""", + """timestamp="xx">""") + + let resource2 = Assembly.GetExecutingAssembly().GetManifestResourceNames() + |> Seq.find (fun n -> n.EndsWith("OpenCover.cob", StringComparison.Ordinal)) + + use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) + use reader = new StreamReader(stream2) + let expected = reader.ReadToEnd().Replace("\r", String.Empty) + Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + finally + Cobertura.path := None + end \ No newline at end of file diff --git a/Tests/Tests.fsproj b/Tests/Tests.fsproj index 834dbbc60..6c4a1c296 100644 --- a/Tests/Tests.fsproj +++ b/Tests/Tests.fsproj @@ -86,6 +86,8 @@ + + diff --git a/Tests/altcover.tests.core.fsproj b/Tests/altcover.tests.core.fsproj index 05ffc55c4..dc312ac94 100644 --- a/Tests/altcover.tests.core.fsproj +++ b/Tests/altcover.tests.core.fsproj @@ -31,6 +31,8 @@ + + From 6f0984fe1883cd98744396190744f79b24e12ae5 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 08:57:16 +0100 Subject: [PATCH 18/48] Update release notes with bugfix information -- issues #13, #14, #15 --- ReleaseNotes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 035f8d25c..d88c2f5fb 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,7 @@ # 3.0.4xx (Caba series release 8) +* [BUGFIX] for `-c|callContext` option where the conext function completes with a tail call +* [BUGFIX] for `-c|callContext` option where the context function contains a branch directly to the return instruction + # 3.0.433 (Caba series release 7) * `-t|threshold` option in `runner` mode (parameter `Threshold` for the `AltCover.Collect` task) to fail the build (non-zero return code or MSBuild error state) if coverage falls below the specified percentage From 11cdfac655c11417f7faae15e6223cb10c26e7b0 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 10:09:35 +0100 Subject: [PATCH 19/48] Expand release notes --- ConsoleApplication1Coverage.xml | 247 ++++++++++++++++++++++++++++++++ ReleaseNotes.md | 4 +- 2 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 ConsoleApplication1Coverage.xml diff --git a/ConsoleApplication1Coverage.xml b/ConsoleApplication1Coverage.xml new file mode 100644 index 000000000..c18ae2540 --- /dev/null +++ b/ConsoleApplication1Coverage.xml @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + c: + f: + + diff --git a/ReleaseNotes.md b/ReleaseNotes.md index d88c2f5fb..d7bc5cda3 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,6 +1,6 @@ # 3.0.4xx (Caba series release 8) -* [BUGFIX] for `-c|callContext` option where the conext function completes with a tail call -* [BUGFIX] for `-c|callContext` option where the context function contains a branch directly to the return instruction +* [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function completes with a tail call +* [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function contains a branch directly to a return instruction # 3.0.433 (Caba series release 7) From f071843fba38ae714e6a4e59b8c0f94d22891735 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 10:29:58 +0100 Subject: [PATCH 20/48] Next level --- AltCover/Cobertura.fs | 15 +++++++-------- Tests/NCover.cob | 4 +++- Tests/OpenCover.cob | 4 +++- Tests/Runner.Tests.fs | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index ee352b7ec..a3e1d4d52 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -1,7 +1,6 @@ namespace AltCover open System -open System.IO open System.Xml.Linq module Cobertura = @@ -13,21 +12,22 @@ module Cobertura = |> Seq.iter (fun m -> let package = XElement(X "package", XAttribute(X "name", m.Attribute(X "name").Value)) packages.Add(package) + let classes = XElement(X "classes") + package.Add(classes) ) packages.Parent.SetAttributeValue(X "branch-rate", null) - - - let OpenCover (report:XDocument) (packages:XElement) = report.Descendants(X "Module") |> Seq.filter(fun m -> m.Descendants(X "Class") |> Seq.isEmpty |> not) |> Seq.iter (fun m -> let package = XElement(X "package", - XAttribute(X "name", + XAttribute(X "name", m.Descendants(X "ModuleName") |> Seq.map (fun x -> x.Value) |> Seq.head)) packages.Add(package) + let classes = XElement(X "classes") + package.Add(classes) ) let Summary (report:XDocument) (format:Base.ReportFormat) result = @@ -43,10 +43,9 @@ module Cobertura = let packages = XElement(X "packages") element.Add(packages) - match format with + match format with | Base.ReportFormat.NCover -> NCover report packages | _ -> OpenCover report packages rewrite.Save(!path |> Option.get) - result - + result \ No newline at end of file diff --git a/Tests/NCover.cob b/Tests/NCover.cob index fd95aa766..0b14ef0b9 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -1,6 +1,8 @@ - + + + \ No newline at end of file diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index 9b5b14213..e47c6fee0 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -1,6 +1,8 @@ - + + + \ No newline at end of file diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index cd6b47c88..a5b15b726 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -1571,7 +1571,7 @@ or use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) let expected = reader.ReadToEnd().Replace("\r", String.Empty) - Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected, result) finally Cobertura.path := None @@ -1602,7 +1602,7 @@ or use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) let expected = reader.ReadToEnd().Replace("\r", String.Empty) - Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected, result) finally Cobertura.path := None From bd77682d992f5e7c59c643fce57068b50ad6a864 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 12:27:24 +0100 Subject: [PATCH 21/48] Next level --- AltCover/Cobertura.fs | 27 ++++++++++++++++++++++++++- Tests/NCover.cob | 4 +++- Tests/OpenCover.cob | 4 +++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index a3e1d4d52..335527a0c 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -14,7 +14,17 @@ module Cobertura = packages.Add(package) let classes = XElement(X "classes") package.Add(classes) - ) + m.Descendants(X "method") + |> Seq.groupBy(fun mx -> (mx.Attribute(X "class").Value, + mx.Descendants(X "seqpnt") + |> Seq.map (fun s -> s.Attribute(X "document").Value) + |> Seq.head)) + |> Seq.sortBy fst + |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", + XAttribute(X "name", n), + XAttribute(X "filename", s)) + classes.Add(cx)) + ) packages.Parent.SetAttributeValue(X "branch-rate", null) let OpenCover (report:XDocument) (packages:XElement) = @@ -25,9 +35,24 @@ module Cobertura = m.Descendants(X "ModuleName") |> Seq.map (fun x -> x.Value) |> Seq.head)) + let files = m.Descendants(X "File") + |> Seq.fold(fun m x -> m |> + Map.add (x.Attribute(X "uid").Value) (x.Attribute(X "fullPath").Value)) Map.empty packages.Add(package) let classes = XElement(X "classes") package.Add(classes) + m.Descendants(X "Method") + |> Seq.filter(fun m -> m.Descendants(X "FileRef") |> Seq.isEmpty |> not) + |> Seq.groupBy(fun mx -> ((mx.Parent.Parent.Descendants(X "FullName") |> Seq.head).Value, + mx.Descendants(X "FileRef") + |> Seq.map (fun s -> files + |> Map.find (s.Attribute(X "uid").Value)) + |> Seq.head)) + |> Seq.sortBy fst + |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", + XAttribute(X "name", n), + XAttribute(X "filename", s)) + classes.Add(cx)) ) let Summary (report:XDocument) (format:Base.ReportFormat) result = diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 0b14ef0b9..5dcf63ec9 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -2,7 +2,9 @@ - + + + \ No newline at end of file diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index e47c6fee0..6e4585c41 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -2,7 +2,9 @@ - + + + \ No newline at end of file From c5a1954d5b2ff99927b4c2deb0f654a7a485d824 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 15:15:27 +0100 Subject: [PATCH 22/48] Next level --- AltCover/Cobertura.fs | 8 ++++++-- Tests/NCover.cob | 4 +++- Tests/OpenCover.cob | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 335527a0c..80c54284e 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -23,7 +23,9 @@ module Cobertura = |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", XAttribute(X "name", n), XAttribute(X "filename", s)) - classes.Add(cx)) + classes.Add(cx) + let mxx = XElement(X "methods") + cx.Add(mxx)) ) packages.Parent.SetAttributeValue(X "branch-rate", null) @@ -52,7 +54,9 @@ module Cobertura = |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", XAttribute(X "name", n), XAttribute(X "filename", s)) - classes.Add(cx)) + classes.Add(cx) + let mxx = XElement(X "methods") + cx.Add(mxx)) ) let Summary (report:XDocument) (format:Base.ReportFormat) result = diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 5dcf63ec9..fcf855092 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -3,7 +3,9 @@ - + + + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index 6e4585c41..434d2870e 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -3,7 +3,9 @@ - + + + From 904a8893a69c935659b6ea7f7cc93ee6d8321830 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 15:56:30 +0100 Subject: [PATCH 23/48] Next level again --- AltCover/Cobertura.fs | 25 +++++++++++++++++++++++-- Tests/NCover.cob | 4 +++- Tests/OpenCover.cob | 4 +++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 80c54284e..5b54f8c96 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -25,7 +25,18 @@ module Cobertura = XAttribute(X "filename", s)) classes.Add(cx) let mxx = XElement(X "methods") - cx.Add(mxx)) + cx.Add(mxx) + mx + |> Seq.map(fun mt -> let fn = mt.Attribute(X "fullname").Value.Split([| ' '; '(' |]) |> Array.toList + let key = fn.[1].Substring(n.Length + 1) + let signa = fn.[0] + " " + fn.[2] + (key, (signa, mt))) + |> Seq.sortBy fst + |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signa)) + mxx.Add(mtx))) + ) packages.Parent.SetAttributeValue(X "branch-rate", null) @@ -56,7 +67,17 @@ module Cobertura = XAttribute(X "filename", s)) classes.Add(cx) let mxx = XElement(X "methods") - cx.Add(mxx)) + cx.Add(mxx) + mx + |> Seq.map(fun mt -> let fn = (mt.Descendants(X "Name") |> Seq.head).Value.Split([| ' '; '(' |]) |> Array.toList + let key = fn.[1].Substring(n.Length + 2) + let signa = fn.[0] + " " + fn.[2] + (key, (signa, mt))) + |> Seq.sortBy fst + |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signa)) + mxx.Add(mtx))) ) let Summary (report:XDocument) (format:Base.ReportFormat) result = diff --git a/Tests/NCover.cob b/Tests/NCover.cob index fcf855092..1a3d86533 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -4,7 +4,9 @@ - + + + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index 434d2870e..eaf58dfdf 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -4,7 +4,9 @@ - + + + From e1c7c503482ba56a20c0dedb6dfed419dfff2267 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 16:01:14 +0100 Subject: [PATCH 24/48] And again --- AltCover/Cobertura.fs | 8 ++++++-- Tests/NCover.cob | 4 +++- Tests/OpenCover.cob | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 5b54f8c96..ee27d9be9 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -35,7 +35,9 @@ module Cobertura = |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", XAttribute(X "name", key), XAttribute(X "signature", signa)) - mxx.Add(mtx))) + mxx.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines))) ) packages.Parent.SetAttributeValue(X "branch-rate", null) @@ -77,7 +79,9 @@ module Cobertura = |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", XAttribute(X "name", key), XAttribute(X "signature", signa)) - mxx.Add(mtx))) + mxx.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines))) ) let Summary (report:XDocument) (format:Base.ReportFormat) result = diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 1a3d86533..ff6be96a4 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -5,7 +5,9 @@ - + + + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index eaf58dfdf..c1d90484f 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -5,7 +5,9 @@ - + + + From 3d158bf169754515f47672a3551cc03aadd0403a Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 16:57:54 +0100 Subject: [PATCH 25/48] Finish NCover to Cobertura --- AltCover/Cobertura.fs | 79 +++++++++++++++++++++++++------------------ Tests/NCover.cob | 21 +++++++++--- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index ee27d9be9..bd653c7b8 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -8,38 +8,53 @@ module Cobertura = let X = OpenCover.X let NCover (report:XDocument) (packages:XElement) = - report.Descendants(X "module") - |> Seq.iter (fun m -> let package = XElement(X "package", - XAttribute(X "name", m.Attribute(X "name").Value)) - packages.Add(package) - let classes = XElement(X "classes") - package.Add(classes) - m.Descendants(X "method") - |> Seq.groupBy(fun mx -> (mx.Attribute(X "class").Value, - mx.Descendants(X "seqpnt") - |> Seq.map (fun s -> s.Attribute(X "document").Value) - |> Seq.head)) - |> Seq.sortBy fst - |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", - XAttribute(X "name", n), - XAttribute(X "filename", s)) - classes.Add(cx) - let mxx = XElement(X "methods") - cx.Add(mxx) - mx - |> Seq.map(fun mt -> let fn = mt.Attribute(X "fullname").Value.Split([| ' '; '(' |]) |> Array.toList - let key = fn.[1].Substring(n.Length + 1) - let signa = fn.[0] + " " + fn.[2] - (key, (signa, mt))) - |> Seq.sortBy fst - |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", - XAttribute(X "name", key), - XAttribute(X "signature", signa)) - mxx.Add(mtx) - let lines = XElement(X "lines") - mtx.Add(lines))) - - ) + let (hxxx, txxx) = + report.Descendants(X "module") + |> Seq.fold (fun (h0, t0) m -> let package = XElement(X "package", + XAttribute(X "name", m.Attribute(X "name").Value)) + packages.Add(package) + let classes = XElement(X "classes") + package.Add(classes) + let (h0x, t0x) = m.Descendants(X "method") + |> Seq.groupBy(fun mx -> (mx.Attribute(X "class").Value, + mx.Descendants(X "seqpnt") + |> Seq.map (fun s -> s.Attribute(X "document").Value) + |> Seq.head)) + |> Seq.sortBy fst + |> Seq.fold (fun (h0', t0') ((n,s),mx) -> let cx = XElement(X "class", + XAttribute(X "name", n), + XAttribute(X "filename", s)) + classes.Add(cx) + let mxx = XElement(X "methods") + cx.Add(mxx) + let (hxx, txx) = mx + |> Seq.map(fun mt -> let fn = mt.Attribute(X "fullname").Value.Split([| ' '; '(' |]) |> Array.toList + let key = fn.[1].Substring(n.Length + 1) + let signa = fn.[0] + " " + fn.[2] + (key, (signa, mt))) + |> Seq.sortBy fst + |> Seq.fold(fun (h', t') (key, (signa, mt)) -> let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signa)) + mxx.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines) + let (hx,tx) = mt.Descendants(X "seqpnt") + |> Seq.fold (fun (h,t) s -> let vc = s.Attribute(X "visitcount") + let vx = if isNull vc then "0" else vc.Value + let line = XElement(X "line", + XAttribute(X "number", s.Attribute(X "line").Value), + XAttribute(X "hits", if isNull vc then "0" else vc.Value), + XAttribute(X "branch", "false")) + lines.Add line + (h + (if vx = "0" then 0 else 1), t + 1)) (0,0) + if tx > 0 then mtx.SetAttributeValue(X "line-rate", (float hx)/(float tx)) + (h' + hx, t' + tx)) (0,0) + if txx > 0 then cx.SetAttributeValue(X "line-rate", (float hxx)/(float txx)) + (h0'+hxx, t0'+txx)) (0,0) + if t0x > 0 then package.SetAttributeValue(X "line-rate", (float h0x)/(float t0x)) + (h0 + h0x, t0 + t0x)) (0,0) + if txxx > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hxxx)/(float txxx)) packages.Parent.SetAttributeValue(X "branch-rate", null) let OpenCover (report:XDocument) (packages:XElement) = diff --git a/Tests/NCover.cob b/Tests/NCover.cob index ff6be96a4..78afb5730 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -1,12 +1,23 @@ - + - + - + - - + + + + + + + + + + + + + From 096657ef54c1d335f751aa2ee653e2bf0e592253 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 18:06:56 +0100 Subject: [PATCH 26/48] OpenCover summary information --- AltCover/Cobertura.fs | 55 ++++++++++++++++++++++++++++++------------- Tests/OpenCover.cob | 8 +++---- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index bd653c7b8..5c890ffbb 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -8,7 +8,7 @@ module Cobertura = let X = OpenCover.X let NCover (report:XDocument) (packages:XElement) = - let (hxxx, txxx) = + let (hxxx, txxx) = report.Descendants(X "module") |> Seq.fold (fun (h0, t0) m -> let package = XElement(X "package", XAttribute(X "name", m.Attribute(X "name").Value)) @@ -53,20 +53,27 @@ module Cobertura = if txx > 0 then cx.SetAttributeValue(X "line-rate", (float hxx)/(float txx)) (h0'+hxx, t0'+txx)) (0,0) if t0x > 0 then package.SetAttributeValue(X "line-rate", (float h0x)/(float t0x)) - (h0 + h0x, t0 + t0x)) (0,0) + (h0 + h0x, t0 + t0x)) (0,0) if txxx > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hxxx)/(float txxx)) packages.Parent.SetAttributeValue(X "branch-rate", null) let OpenCover (report:XDocument) (packages:XElement) = + let extract (x:XElement) n = + Math.Round((x.Attribute(X n).Value + |> Double.TryParse + |> snd) / 100.0, 4) report.Descendants(X "Module") |> Seq.filter(fun m -> m.Descendants(X "Class") |> Seq.isEmpty |> not) - |> Seq.iter (fun m -> let package = XElement(X "package", + |> Seq.iter (fun m -> let summary = m.Descendants(X "Summary") |> Seq.head + let package = XElement(X "package", XAttribute(X "name", m.Descendants(X "ModuleName") |> Seq.map (fun x -> x.Value) |> Seq.head)) + package.SetAttributeValue(X "line-rate", extract summary "sequenceCoverage") + package.SetAttributeValue(X "branch-rate", extract summary "branchCoverage") let files = m.Descendants(X "File") - |> Seq.fold(fun m x -> m |> + |> Seq.fold(fun m x -> m |> Map.add (x.Attribute(X "uid").Value) (x.Attribute(X "fullPath").Value)) Map.empty packages.Add(package) let classes = XElement(X "classes") @@ -85,19 +92,35 @@ module Cobertura = classes.Add(cx) let mxx = XElement(X "methods") cx.Add(mxx) - mx - |> Seq.map(fun mt -> let fn = (mt.Descendants(X "Name") |> Seq.head).Value.Split([| ' '; '(' |]) |> Array.toList - let key = fn.[1].Substring(n.Length + 2) - let signa = fn.[0] + " " + fn.[2] - (key, (signa, mt))) - |> Seq.sortBy fst - |> Seq.iter(fun (key, (signa, mt)) -> let mtx = XElement(X "method", - XAttribute(X "name", key), - XAttribute(X "signature", signa)) - mxx.Add(mtx) - let lines = XElement(X "lines") - mtx.Add(lines))) + let q = mx + |> Seq.map(fun mt -> let fn = (mt.Descendants(X "Name") |> Seq.head).Value.Split([| ' '; '(' |]) |> Array.toList + let key = fn.[1].Substring(n.Length + 2) + let signa = fn.[0] + " " + fn.[2] + (key, (signa, mt))) + |> Seq.sortBy fst + |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) + |> Seq.fold(fun (b,bv,s,sv) (key, (signa, mt)) -> let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signa), + XAttribute(X "line-rate", extract mt "sequenceCoverage"), + XAttribute(X "branch-rate", extract mt "branchCoverage") + ) + mxx.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines) + let summary = mt.Descendants(X "Summary") |> Seq.head + ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), + bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), + s + (summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd), + sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd))) (0,0,0,0) + let (b,bv,s,sv) = q + if s > 0 then cx.SetAttributeValue(X "line-rate", (float sv)/(float s)) + if b > 0 then cx.SetAttributeValue(X "branch-rate", (float bv)/(float b)) + ) ) + let summary = report.Descendants(X "Summary") |> Seq.head + packages.Parent.SetAttributeValue(X "line-rate", extract summary "sequenceCoverage") + packages.Parent.SetAttributeValue(X "branch-rate", extract summary "branchCoverage") let Summary (report:XDocument) (format:Base.ReportFormat) result = let rewrite = XDocument(XDeclaration("1.0", "utf-8", "yes"), [||]) diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index c1d90484f..ae0e6803f 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -1,11 +1,11 @@ - + - + - + - + From ea321a34db76b2cb9a7cc1ba1eff35cf6baa4387 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 18:54:06 +0100 Subject: [PATCH 27/48] Standardize the coverage summaries --- AltCover/Cobertura.fs | 37 +++++++++++++++++++------------------ Tests/OpenCover.cob | 6 +++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 5c890ffbb..677c62848 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -58,26 +58,30 @@ module Cobertura = packages.Parent.SetAttributeValue(X "branch-rate", null) let OpenCover (report:XDocument) (packages:XElement) = - let extract (x:XElement) n = - Math.Round((x.Attribute(X n).Value - |> Double.TryParse - |> snd) / 100.0, 4) + let extract (owner:XElement) (target:XElement) = + let summary = owner.Descendants(X "Summary") |> Seq.head + let b = summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd + let bv = summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd + let s = summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd + let sv = summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd + if s > 0 then target.SetAttributeValue(X "line-rate", (float sv)/(float s)) + if b > 0 then target.SetAttributeValue(X "branch-rate", (float bv)/(float b)) report.Descendants(X "Module") |> Seq.filter(fun m -> m.Descendants(X "Class") |> Seq.isEmpty |> not) - |> Seq.iter (fun m -> let summary = m.Descendants(X "Summary") |> Seq.head + |> Seq.iter (fun m -> let mname = m.Descendants(X "ModuleName") + |> Seq.map (fun x -> x.Value) + |> Seq.head let package = XElement(X "package", - XAttribute(X "name", - m.Descendants(X "ModuleName") - |> Seq.map (fun x -> x.Value) - |> Seq.head)) - package.SetAttributeValue(X "line-rate", extract summary "sequenceCoverage") - package.SetAttributeValue(X "branch-rate", extract summary "branchCoverage") + XAttribute(X "name", mname)) let files = m.Descendants(X "File") |> Seq.fold(fun m x -> m |> Map.add (x.Attribute(X "uid").Value) (x.Attribute(X "fullPath").Value)) Map.empty packages.Add(package) let classes = XElement(X "classes") package.Add(classes) + + extract m package + m.Descendants(X "Method") |> Seq.filter(fun m -> m.Descendants(X "FileRef") |> Seq.isEmpty |> not) |> Seq.groupBy(fun mx -> ((mx.Parent.Parent.Descendants(X "FullName") |> Seq.head).Value, @@ -101,10 +105,8 @@ module Cobertura = |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) |> Seq.fold(fun (b,bv,s,sv) (key, (signa, mt)) -> let mtx = XElement(X "method", XAttribute(X "name", key), - XAttribute(X "signature", signa), - XAttribute(X "line-rate", extract mt "sequenceCoverage"), - XAttribute(X "branch-rate", extract mt "branchCoverage") - ) + XAttribute(X "signature", signa)) + extract mt mtx mxx.Add(mtx) let lines = XElement(X "lines") mtx.Add(lines) @@ -118,9 +120,8 @@ module Cobertura = if b > 0 then cx.SetAttributeValue(X "branch-rate", (float bv)/(float b)) ) ) - let summary = report.Descendants(X "Summary") |> Seq.head - packages.Parent.SetAttributeValue(X "line-rate", extract summary "sequenceCoverage") - packages.Parent.SetAttributeValue(X "branch-rate", extract summary "branchCoverage") + + extract (report.Descendants(X "CoverageSession") |> Seq.head) packages.Parent let Summary (report:XDocument) (format:Base.ReportFormat) result = let rewrite = XDocument(XDeclaration("1.0", "utf-8", "yes"), [||]) diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index ae0e6803f..bc6c78236 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -1,11 +1,11 @@ - + - + - + From dadf674f841c3d7360fdae80773ca39ad4a73159 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 19:12:06 +0100 Subject: [PATCH 28/48] And that's it. bar refactoring --- AltCover/Cobertura.fs | 25 ++++++++++++++++++++++++- Tests/OpenCover.cob | 17 ++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 677c62848..369aa84bd 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -44,7 +44,7 @@ module Cobertura = let vx = if isNull vc then "0" else vc.Value let line = XElement(X "line", XAttribute(X "number", s.Attribute(X "line").Value), - XAttribute(X "hits", if isNull vc then "0" else vc.Value), + XAttribute(X "hits", vx), XAttribute(X "branch", "false")) lines.Add line (h + (if vx = "0" then 0 else 1), t + 1)) (0,0) @@ -110,6 +110,29 @@ module Cobertura = mxx.Add(mtx) let lines = XElement(X "lines") mtx.Add(lines) + mt.Descendants(X "SequencePoint") + |> Seq.iter(fun s -> let vc = s.Attribute(X "vc") + let vx = if isNull vc then "0" else vc.Value + let bec = s.Attribute(X "bec").Value |> Int32.TryParse |> snd + let bev = s.Attribute(X "bev").Value |> Int32.TryParse |> snd + let line = XElement(X "line", + XAttribute(X "number", s.Attribute(X "sl").Value), + XAttribute(X "hits", vx), + XAttribute(X "branch", if bec = 0 then "false" else "true")) + if bec > 0 then + let pc = Math.Round(100.0 * (float bev)/ (float bec)) |> int + line.SetAttributeValue(X "condition-coverage", + sprintf "%d%% (%d/%d)" pc bev bec) + let cc = XElement(X "conditions") + line.Add cc + let co = XElement(X "condition", + XAttribute(X "number", 0), + XAttribute(X "type", "jump"), + XAttribute(X "coverage", sprintf "%d%%" pc)) + cc.Add co + + lines.Add line + ) let summary = mt.Descendants(X "Summary") |> Seq.head ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index bc6c78236..b751d7183 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -6,7 +6,22 @@ - + + + + + + + + + + + + + + + + From 790f0f39ac02f081a6c67c34360ccbc3badd3ccc Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 19:15:49 +0100 Subject: [PATCH 29/48] Release note for the new feature --- ReleaseNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index d7bc5cda3..86c209dfe 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,6 +1,7 @@ # 3.0.4xx (Caba series release 8) * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function completes with a tail call * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function contains a branch directly to a return instruction +* `-c|cobertura` option on "runner"/Collect mode -- Cobertura format output to teh given file name, based on sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml # 3.0.433 (Caba series release 7) From 8fd330118855b3b5f3182e43ef2530aa771b4d3d Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 19:55:55 +0100 Subject: [PATCH 30/48] Tidy release notes --- ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 86c209dfe..62c22651d 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,7 +1,7 @@ # 3.0.4xx (Caba series release 8) * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function completes with a tail call * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function contains a branch directly to a return instruction -* `-c|cobertura` option on "runner"/Collect mode -- Cobertura format output to teh given file name, based on sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml +* `-c|cobertura` option on "runner"/Collect mode -- Cobertura format output to the given file name, with format composition based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml # 3.0.433 (Caba series release 7) From 5ef0eacb0f03b82dd27786897e5c174ef60f1b98 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 19:57:37 +0100 Subject: [PATCH 31/48] Make the wording consistent --- ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 62c22651d..9d8720dd9 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,7 +1,7 @@ # 3.0.4xx (Caba series release 8) * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function completes with a tail call * [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function contains a branch directly to a return instruction -* `-c|cobertura` option on "runner"/Collect mode -- Cobertura format output to the given file name, with format composition based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml +* `-c|cobertura` option in `runner` mode (parameter `Cobertura` for the `AltCover.Collect` task) -- Cobertura format output to the given file name, with format composition based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml # 3.0.433 (Caba series release 7) From ee6bde92dce34a65f15497d930d7df90a364d9ca Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 20:38:09 +0100 Subject: [PATCH 32/48] Make the wording of the help text more systematic. --- AltCover/Strings.eo.resx | 12 +++++----- AltCover/Strings.resx | 12 +++++----- Tests/Runner.Tests.fs | 26 ++++++++++---------- Tests/Tests.fs | 52 ++++++++++++++++++++-------------------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/AltCover/Strings.eo.resx b/AltCover/Strings.eo.resx index 5b8e12680..9ba3980b0 100644 --- a/AltCover/Strings.eo.resx +++ b/AltCover/Strings.eo.resx @@ -133,19 +133,19 @@ Laŭvola: La eliga raporta ŝablono-dosiero (defaŭlte: 'coverage.xml' en la aktuala dosierujo) - Laŭvola: fonta dosiernomo por ekskludi de instrumentado (eble ripeti) + Laŭvola, multobla: fonta dosiernomo por ekskludi de instrumentado - Laŭvola: ensambla nomo por ekskludi de instrumentado (eble ripeti) + Laŭvola, multobla: ensambla nomo por ekskludi de instrumentado - Laŭvola: tajpu nomon por ekskludi de instrumentado (eble ripeti) + Laŭvola, multobla: tajpu nomon por ekskludi de instrumentado - Laŭvola: metodo nomo por ekskludi de instrumentado (eble ripeti) + Laŭvola, multobla: metodo nomo por ekskludi de instrumentado - Laŭvola: atribuka nomo por ekskludi de instrumentado (eble ripeti) + Laŭvola, multobla: atribuka nomo por ekskludi de instrumentado Elmetas ĉi tiujn eblojn. @@ -181,7 +181,7 @@ Kovra Raporto: {0} - Laŭvola: asembleo, kiu kunligas aliajn agordajn asembleojn, sed por kiuj internaĵoj povas esti ekskluditaj (eble ripeti) + Laŭvola, multobla: asembleo, kiu kunligas aliajn agordajn asembleojn, sed por kiuj internaĵoj povas esti ekskluditaj La dosierujo enhavanta la instrumentan kodon por kontroli (inkluzive de la AltCover.Recorder.g.dll generita per antaŭa uzo de la ".net core" AltCover). diff --git a/AltCover/Strings.resx b/AltCover/Strings.resx index b2afe8029..273355bd8 100644 --- a/AltCover/Strings.resx +++ b/AltCover/Strings.resx @@ -133,19 +133,19 @@ Optional: The output report template file (default: coverage.xml in the current directory) - Optional: source file name to exclude from instrumentation (may repeat) + Optional, multiple: source file name to exclude from instrumentation - Optional: assembly name to exclude from instrumentation (may repeat) + Optional, multiple: assembly name to exclude from instrumentation - Optional: type name to exclude from instrumentation (may repeat) + Optional, multiple: type name to exclude from instrumentation - Optional: method name to exclude from instrumentation (may repeat) + Optional, multiple: method name to exclude from instrumentation - Optional: attribute name to exclude from instrumentation (may repeat) + Optional, multiple: attribute name to exclude from instrumentation Prints out the options. @@ -182,7 +182,7 @@ Coverage Report: {0} - Optional: assembly which links other instrumented assemblies but for which internal details may be excluded (may repeat) + Optional, multiple: assembly which links other instrumented assemblies but for which internal details may be excluded The folder containing the instrumented code to monitor (including the AltCover.Recorder.g.dll generated by previous a use of the .net core AltCover). diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index 46f23ef22..96425c4d6 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -902,21 +902,21 @@ type AltCoverTests() = class #endif + """ -x, --xmlReport=VALUE Optional: The output report template file (default: coverage.xml in the current directory) - -f, --fileFilter=VALUE Optional: source file name to exclude from - instrumentation (may repeat) - -s, --assemblyFilter=VALUE Optional: assembly name to exclude from - instrumentation (may repeat) + -f, --fileFilter=VALUE Optional, multiple: source file name to exclude + from instrumentation + -s, --assemblyFilter=VALUE Optional, multiple: assembly name to exclude from + instrumentation -e, --assemblyExcludeFilter=VALUE - Optional: assembly which links other instrumented - assemblies but for which internal details may be - excluded (may repeat) - -t, --typeFilter=VALUE Optional: type name to exclude from - instrumentation (may repeat) - -m, --methodFilter=VALUE Optional: method name to exclude from - instrumentation (may repeat) + Optional, multiple: assembly which links other + instrumented assemblies but for which internal + details may be excluded + -t, --typeFilter=VALUE Optional, multiple: type name to exclude from + instrumentation + -m, --methodFilter=VALUE Optional, multiple: method name to exclude from + instrumentation -a, --attributeFilter=VALUE - Optional: attribute name to exclude from - instrumentation (may repeat) + Optional, multiple: attribute name to exclude from + instrumentation -c, --callContext=VALUE Optional, multiple: Tracking either times of visits in ticks or designated method calls leading to the visits. diff --git a/Tests/Tests.fs b/Tests/Tests.fs index ddff76859..f8e9d3911 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -4308,21 +4308,21 @@ type AltCoverTests() = class #endif + """ -x, --xmlReport=VALUE Optional: The output report template file (default: coverage.xml in the current directory) - -f, --fileFilter=VALUE Optional: source file name to exclude from - instrumentation (may repeat) - -s, --assemblyFilter=VALUE Optional: assembly name to exclude from - instrumentation (may repeat) + -f, --fileFilter=VALUE Optional, multiple: source file name to exclude + from instrumentation + -s, --assemblyFilter=VALUE Optional, multiple: assembly name to exclude from + instrumentation -e, --assemblyExcludeFilter=VALUE - Optional: assembly which links other instrumented - assemblies but for which internal details may be - excluded (may repeat) - -t, --typeFilter=VALUE Optional: type name to exclude from - instrumentation (may repeat) - -m, --methodFilter=VALUE Optional: method name to exclude from - instrumentation (may repeat) + Optional, multiple: assembly which links other + instrumented assemblies but for which internal + details may be excluded + -t, --typeFilter=VALUE Optional, multiple: type name to exclude from + instrumentation + -m, --methodFilter=VALUE Optional, multiple: method name to exclude from + instrumentation -a, --attributeFilter=VALUE - Optional: attribute name to exclude from - instrumentation (may repeat) + Optional, multiple: attribute name to exclude from + instrumentation -c, --callContext=VALUE Optional, multiple: Tracking either times of visits in ticks or designated method calls leading to the visits. @@ -4388,21 +4388,21 @@ type AltCoverTests() = class #endif + """ -x, --xmlReport=VALUE Optional: The output report template file (default: coverage.xml in the current directory) - -f, --fileFilter=VALUE Optional: source file name to exclude from - instrumentation (may repeat) - -s, --assemblyFilter=VALUE Optional: assembly name to exclude from - instrumentation (may repeat) + -f, --fileFilter=VALUE Optional, multiple: source file name to exclude + from instrumentation + -s, --assemblyFilter=VALUE Optional, multiple: assembly name to exclude from + instrumentation -e, --assemblyExcludeFilter=VALUE - Optional: assembly which links other instrumented - assemblies but for which internal details may be - excluded (may repeat) - -t, --typeFilter=VALUE Optional: type name to exclude from - instrumentation (may repeat) - -m, --methodFilter=VALUE Optional: method name to exclude from - instrumentation (may repeat) + Optional, multiple: assembly which links other + instrumented assemblies but for which internal + details may be excluded + -t, --typeFilter=VALUE Optional, multiple: type name to exclude from + instrumentation + -m, --methodFilter=VALUE Optional, multiple: method name to exclude from + instrumentation -a, --attributeFilter=VALUE - Optional: attribute name to exclude from - instrumentation (may repeat) + Optional, multiple: attribute name to exclude from + instrumentation -c, --callContext=VALUE Optional, multiple: Tracking either times of visits in ticks or designated method calls leading to the visits. From 5eca396b21f3504679e96771227621cef75c8444 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 20:53:35 +0100 Subject: [PATCH 33/48] Refactor a little, but still way too wide --- AltCover/Cobertura.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 369aa84bd..5f5109174 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -3,6 +3,8 @@ open System open System.Xml.Linq +// based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml + module Cobertura = let internal path : Option ref = ref None let X = OpenCover.X @@ -119,7 +121,7 @@ module Cobertura = XAttribute(X "number", s.Attribute(X "sl").Value), XAttribute(X "hits", vx), XAttribute(X "branch", if bec = 0 then "false" else "true")) - if bec > 0 then + let doBranch () = let pc = Math.Round(100.0 * (float bev)/ (float bec)) |> int line.SetAttributeValue(X "condition-coverage", sprintf "%d%% (%d/%d)" pc bev bec) @@ -131,6 +133,8 @@ module Cobertura = XAttribute(X "coverage", sprintf "%d%%" pc)) cc.Add co + if bec > 0 then doBranch() + lines.Add line ) let summary = mt.Descendants(X "Summary") |> Seq.head From b499d24e9965c8eecdcba49345fe9c1c330fc357 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 21:06:41 +0100 Subject: [PATCH 34/48] Final stage of the cobertura format -- class level line reports --- AltCover/Cobertura.fs | 2 ++ Tests/NCover.cob | 12 ++++++++++++ Tests/OpenCover.cob | 16 ++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 5f5109174..524b02d9a 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -4,6 +4,8 @@ open System open System.Xml.Linq // based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml +// TODO -- refactor away from the arrow anti-pattern +// TODO -- class level lines summaries module Cobertura = let internal path : Option ref = ref None diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 78afb5730..7d7ae5afa 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -20,6 +20,18 @@ + + + + + + + + + + + + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index b751d7183..6416dd261 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -24,6 +24,22 @@ + + + + + + + + + + + + + + + + From 012941acac31b14f7d26a36279e16218dc7f15fe Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sat, 21 Apr 2018 21:13:09 +0100 Subject: [PATCH 35/48] Fix the Unix build --- Tests/Tests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Tests.fs b/Tests/Tests.fs index f8e9d3911..c83f2c8f2 100644 --- a/Tests/Tests.fs +++ b/Tests/Tests.fs @@ -2313,7 +2313,7 @@ type AltCoverTests() = class #if NETCOREAPP2_0 let shift = String.Empty #else - let shift = "\\netcoreapp2.0" + let shift = "/netcoreapp2.0" #endif let path = Path.Combine(Path.GetDirectoryName(where) + AltCoverTests.Hack() + shift, "AltCover.Recorder.dll") From fa43c7fc89cf3c4319b5267f04920ddcf09d9b2f Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 09:22:08 +0100 Subject: [PATCH 36/48] Lines reprise stanze at class level --- AltCover/Cobertura.fs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 524b02d9a..1898edd4a 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -5,7 +5,6 @@ open System.Xml.Linq // based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml // TODO -- refactor away from the arrow anti-pattern -// TODO -- class level lines summaries module Cobertura = let internal path : Option ref = ref None @@ -169,5 +168,17 @@ module Cobertura = | Base.ReportFormat.NCover -> NCover report packages | _ -> OpenCover report packages + // lines reprise + packages.Descendants(X "class") + |> Seq.iter(fun c -> let reprise = XElement(X "lines") + c.Add reprise + let lines = c.Descendants(X "line") + |> Seq.sortBy(fun l -> l.Attribute(X "number").Value + |> Int32.TryParse |> snd) + |> Seq.toList + lines + |> List.iter (fun l -> let copy = XElement(l) + reprise.Add copy)) + rewrite.Save(!path |> Option.get) result \ No newline at end of file From 130b4ac34702588d68a6620298b9e10d179ff5a3 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 09:25:23 +0100 Subject: [PATCH 37/48] Extract Method the arrow anti-pattern --- AltCover/Cobertura.fs | 112 ++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 1898edd4a..cbfc372cc 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -11,53 +11,71 @@ module Cobertura = let X = OpenCover.X let NCover (report:XDocument) (packages:XElement) = - let (hxxx, txxx) = - report.Descendants(X "module") - |> Seq.fold (fun (h0, t0) m -> let package = XElement(X "package", - XAttribute(X "name", m.Attribute(X "name").Value)) - packages.Add(package) - let classes = XElement(X "classes") - package.Add(classes) - let (h0x, t0x) = m.Descendants(X "method") - |> Seq.groupBy(fun mx -> (mx.Attribute(X "class").Value, - mx.Descendants(X "seqpnt") - |> Seq.map (fun s -> s.Attribute(X "document").Value) - |> Seq.head)) - |> Seq.sortBy fst - |> Seq.fold (fun (h0', t0') ((n,s),mx) -> let cx = XElement(X "class", - XAttribute(X "name", n), - XAttribute(X "filename", s)) - classes.Add(cx) - let mxx = XElement(X "methods") - cx.Add(mxx) - let (hxx, txx) = mx - |> Seq.map(fun mt -> let fn = mt.Attribute(X "fullname").Value.Split([| ' '; '(' |]) |> Array.toList - let key = fn.[1].Substring(n.Length + 1) - let signa = fn.[0] + " " + fn.[2] - (key, (signa, mt))) - |> Seq.sortBy fst - |> Seq.fold(fun (h', t') (key, (signa, mt)) -> let mtx = XElement(X "method", - XAttribute(X "name", key), - XAttribute(X "signature", signa)) - mxx.Add(mtx) - let lines = XElement(X "lines") - mtx.Add(lines) - let (hx,tx) = mt.Descendants(X "seqpnt") - |> Seq.fold (fun (h,t) s -> let vc = s.Attribute(X "visitcount") - let vx = if isNull vc then "0" else vc.Value - let line = XElement(X "line", - XAttribute(X "number", s.Attribute(X "line").Value), - XAttribute(X "hits", vx), - XAttribute(X "branch", "false")) - lines.Add line - (h + (if vx = "0" then 0 else 1), t + 1)) (0,0) - if tx > 0 then mtx.SetAttributeValue(X "line-rate", (float hx)/(float tx)) - (h' + hx, t' + tx)) (0,0) - if txx > 0 then cx.SetAttributeValue(X "line-rate", (float hxx)/(float txx)) - (h0'+hxx, t0'+txx)) (0,0) - if t0x > 0 then package.SetAttributeValue(X "line-rate", (float h0x)/(float t0x)) - (h0 + h0x, t0 + t0x)) (0,0) - if txxx > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hxxx)/(float txxx)) + let ProcessSeqPnts (``method``:XElement) (lines:XElement) = + ``method``.Descendants(X "seqpnt") + |> Seq.fold (fun (h,t) s -> let vc = s.Attribute(X "visitcount") + let vx = if isNull vc then "0" else vc.Value + let line = XElement(X "line", + XAttribute(X "number", s.Attribute(X "line").Value), + XAttribute(X "hits", vx), + XAttribute(X "branch", "false")) + lines.Add line + (h + (if vx = "0" then 0 else 1), t + 1)) (0,0) + + let ProcessMethod (methods:XElement) (hits, total) (key, (signature, ``method``)) = + let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signature)) + methods.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines) + let (mHits, mTotal) = ProcessSeqPnts ``method`` lines + if mTotal > 0 then mtx.SetAttributeValue(X "line-rate", (float mHits)/(float mTotal)) + (hits + mHits, total + mTotal) + + let SortMethod (n:String) (methods:XElement) (``method``: XElement seq) = + ``method`` + |> Seq.map(fun m -> let fn = m.Attribute(X "fullname").Value.Split([| ' '; '(' |]) + |> Array.toList + let key = fn.[1].Substring(n.Length + 1) + let signature = fn.[0] + " " + fn.[2] + (key, (signature, m))) + |> Seq.sortBy fst + |> Seq.fold (ProcessMethod methods) (0,0) + + let ProcessClass (classes:XElement) (hits, total) ((name,signature),``method``) = + let ``class`` = XElement(X "class", + XAttribute(X "name", name), + XAttribute(X "filename", signature)) + classes.Add(``class``) + let methods = XElement(X "methods") + ``class``.Add(methods) + let (mHits, mTotal) = SortMethod name methods ``method`` + if mTotal > 0 then ``class``.SetAttributeValue(X "line-rate", (float mHits)/(float mTotal)) + (hits + mHits, total + mTotal) + + let ExtractClasses (``module``:XElement) classes = + ``module``.Descendants(X "method") + |> Seq.groupBy(fun ``method`` -> (``method``.Attribute(X "class").Value, + ``method``.Descendants(X "seqpnt") + |> Seq.map (fun s -> s.Attribute(X "document").Value) + |> Seq.head)) + |> Seq.sortBy fst + |> Seq.fold (ProcessClass classes) (0,0) + + let ProcessModule (hits, total) (``module``:XElement) = + let package = XElement(X "package", + XAttribute(X "name", ``module``.Attribute(X "name").Value)) + packages.Add(package) + let classes = XElement(X "classes") + package.Add(classes) + let (cHits, cTotal) = ExtractClasses ``module`` classes + if cTotal > 0 then package.SetAttributeValue(X "line-rate", (float cHits)/(float cTotal)) + (hits + cHits, total + cTotal) + + let (hits, total) = report.Descendants(X "module") + |> Seq.fold ProcessModule (0,0) + if total > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hits)/(float total)) packages.Parent.SetAttributeValue(X "branch-rate", null) let OpenCover (report:XDocument) (packages:XElement) = From 8f4b41cbd4a0d6aa8fbe257f62826cf4e58a85d0 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 09:26:23 +0100 Subject: [PATCH 38/48] These don't need to be public. Neither does one of the generated class constructors, but I can't do anything about that save switch off another Gendarme rule --- AltCover/Cobertura.fs | 6 +++--- Build/rules.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index cbfc372cc..ebb569111 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -10,7 +10,7 @@ module Cobertura = let internal path : Option ref = ref None let X = OpenCover.X - let NCover (report:XDocument) (packages:XElement) = + let internal NCover (report:XDocument) (packages:XElement) = let ProcessSeqPnts (``method``:XElement) (lines:XElement) = ``method``.Descendants(X "seqpnt") |> Seq.fold (fun (h,t) s -> let vc = s.Attribute(X "visitcount") @@ -78,7 +78,7 @@ module Cobertura = if total > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hits)/(float total)) packages.Parent.SetAttributeValue(X "branch-rate", null) - let OpenCover (report:XDocument) (packages:XElement) = + let internal OpenCover (report:XDocument) (packages:XElement) = let extract (owner:XElement) (target:XElement) = let summary = owner.Descendants(X "Summary") |> Seq.head let b = summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd @@ -169,7 +169,7 @@ module Cobertura = extract (report.Descendants(X "CoverageSession") |> Seq.head) packages.Parent - let Summary (report:XDocument) (format:Base.ReportFormat) result = + let internal Summary (report:XDocument) (format:Base.ReportFormat) result = let rewrite = XDocument(XDeclaration("1.0", "utf-8", "yes"), [||]) let element = XElement(X "coverage", XAttribute(X "line-rate", 0), diff --git a/Build/rules.xml b/Build/rules.xml index 760552232..b56745de2 100644 --- a/Build/rules.xml +++ b/Build/rules.xml @@ -8,7 +8,7 @@ exclude="CheckParametersNullityInVisibleMethodsRule | MethodCanBeMadeStaticRule | EnsureLocalDisposalRule" from="Gendarme.Rules.Correctness.dll"/> Date: Sun, 22 Apr 2018 10:07:06 +0100 Subject: [PATCH 39/48] Refactor and other tidying up --- AltCover/Cobertura.fs | 200 +++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 89 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index ebb569111..638eb0293 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -4,12 +4,14 @@ open System open System.Xml.Linq // based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml -// TODO -- refactor away from the arrow anti-pattern module Cobertura = let internal path : Option ref = ref None let X = OpenCover.X + let SetRate hits total rate (target:XElement) = + if total > 0 then target.SetAttributeValue(X rate, (float hits)/(float total)) + let internal NCover (report:XDocument) (packages:XElement) = let ProcessSeqPnts (``method``:XElement) (lines:XElement) = ``method``.Descendants(X "seqpnt") @@ -22,7 +24,7 @@ module Cobertura = lines.Add line (h + (if vx = "0" then 0 else 1), t + 1)) (0,0) - let ProcessMethod (methods:XElement) (hits, total) (key, (signature, ``method``)) = + let ProcessMethod (methods:XElement) (hits, total) (key, (signature, ``method``)) = let mtx = XElement(X "method", XAttribute(X "name", key), XAttribute(X "signature", signature)) @@ -30,12 +32,12 @@ module Cobertura = let lines = XElement(X "lines") mtx.Add(lines) let (mHits, mTotal) = ProcessSeqPnts ``method`` lines - if mTotal > 0 then mtx.SetAttributeValue(X "line-rate", (float mHits)/(float mTotal)) + SetRate mHits mTotal "line-rate" mtx (hits + mHits, total + mTotal) let SortMethod (n:String) (methods:XElement) (``method``: XElement seq) = ``method`` - |> Seq.map(fun m -> let fn = m.Attribute(X "fullname").Value.Split([| ' '; '(' |]) + |> Seq.map(fun m -> let fn = m.Attribute(X "fullname").Value.Split([| ' '; '(' |]) |> Array.toList let key = fn.[1].Substring(n.Length + 1) let signature = fn.[0] + " " + fn.[2] @@ -51,7 +53,7 @@ module Cobertura = let methods = XElement(X "methods") ``class``.Add(methods) let (mHits, mTotal) = SortMethod name methods ``method`` - if mTotal > 0 then ``class``.SetAttributeValue(X "line-rate", (float mHits)/(float mTotal)) + SetRate mHits mTotal "line-rate" ``class`` (hits + mHits, total + mTotal) let ExtractClasses (``module``:XElement) classes = @@ -63,19 +65,19 @@ module Cobertura = |> Seq.sortBy fst |> Seq.fold (ProcessClass classes) (0,0) - let ProcessModule (hits, total) (``module``:XElement) = + let ProcessModule (hits, total) (``module``:XElement) = let package = XElement(X "package", XAttribute(X "name", ``module``.Attribute(X "name").Value)) packages.Add(package) let classes = XElement(X "classes") package.Add(classes) let (cHits, cTotal) = ExtractClasses ``module`` classes - if cTotal > 0 then package.SetAttributeValue(X "line-rate", (float cHits)/(float cTotal)) + SetRate cHits cTotal "line-rate" package (hits + cHits, total + cTotal) let (hits, total) = report.Descendants(X "module") |> Seq.fold ProcessModule (0,0) - if total > 0 then packages.Parent.SetAttributeValue(X "line-rate", (float hits)/(float total)) + SetRate hits total "line-rate" packages.Parent packages.Parent.SetAttributeValue(X "branch-rate", null) let internal OpenCover (report:XDocument) (packages:XElement) = @@ -85,87 +87,105 @@ module Cobertura = let bv = summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd let s = summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd let sv = summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd - if s > 0 then target.SetAttributeValue(X "line-rate", (float sv)/(float s)) - if b > 0 then target.SetAttributeValue(X "branch-rate", (float bv)/(float b)) + SetRate sv s "line-rate" target + SetRate bv b "branch-rate" target + + let doBranch bec bev (line:XElement) = + let pc = Math.Round(100.0 * (float bev)/ (float bec)) |> int + line.SetAttributeValue(X "condition-coverage", + sprintf "%d%% (%d/%d)" pc bev bec) + let cc = XElement(X "conditions") + line.Add cc + let co = XElement(X "condition", + XAttribute(X "number", 0), + XAttribute(X "type", "jump"), + XAttribute(X "coverage", sprintf "%d%%" pc)) + cc.Add co + + let ProcessSeqPnt (lines:XElement) (s:XElement) = + let vc = s.Attribute(X "vc") + let vx = if isNull vc then "0" else vc.Value + let bec = s.Attribute(X "bec").Value |> Int32.TryParse |> snd + let bev = s.Attribute(X "bev").Value |> Int32.TryParse |> snd + let line = XElement(X "line", + XAttribute(X "number", s.Attribute(X "sl").Value), + XAttribute(X "hits", vx), + XAttribute(X "branch", if bec = 0 then "false" else "true")) + + if bec > 0 then doBranch bec bev line + lines.Add line + + let ProcessMethod (methods:XElement) (b,bv,s,sv) (key, (signature, ``method``)) = + let mtx = XElement(X "method", + XAttribute(X "name", key), + XAttribute(X "signature", signature)) + extract ``method`` mtx + methods.Add(mtx) + let lines = XElement(X "lines") + mtx.Add(lines) + ``method``.Descendants(X "SequencePoint") + |> Seq.iter(ProcessSeqPnt lines) + let summary = ``method``.Descendants(X "Summary") |> Seq.head + ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), + bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), + s + (summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd), + sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd)) + + let ArrangeMethods (name:String) (methods:XElement) (methodSet:XElement seq) = + methodSet + |> Seq.map(fun ``method`` -> let fn = (``method``.Descendants(X "Name") + |> Seq.head).Value.Split([| ' '; '(' |]) + |> Array.toList + let key = fn.[1].Substring(name.Length + 2) + let signature = fn.[0] + " " + fn.[2] + (key, (signature, ``method``))) + |> Seq.sortBy fst + |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) + |> Seq.fold(ProcessMethod methods) (0,0,0,0) + + let ProcessClass (classes:XElement) ((name, source), methodSet) = + let ``class`` = XElement(X "class", + XAttribute(X "name", name), + XAttribute(X "filename", source)) + classes.Add(``class``) + let methods = XElement(X "methods") + ``class``.Add(methods) + let (b,bv,s,sv) = ArrangeMethods name methods methodSet + SetRate sv s "line-rate" ``class`` + SetRate bv b "branch-rate" ``class`` + + let ProcessModule files classes (``module``:XElement) = + ``module``.Descendants(X "Method") + |> Seq.filter(fun m -> m.Descendants(X "FileRef") |> Seq.isEmpty |> not) + |> Seq.groupBy(fun ``method`` -> ((``method``.Parent.Parent.Descendants(X "FullName") + |> Seq.head).Value, + ``method``.Descendants(X "FileRef") + |> Seq.map (fun s -> files + |> Map.find (s.Attribute(X "uid").Value)) + |> Seq.head)) + |> Seq.sortBy fst + |> Seq.iter (ProcessClass classes) + + let lookUpFiles (``module``:XElement) = + ``module``.Descendants(X "File") + |> Seq.fold(fun m x -> m |> + Map.add (x.Attribute(X "uid").Value) + (x.Attribute(X "fullPath").Value)) + Map.empty report.Descendants(X "Module") |> Seq.filter(fun m -> m.Descendants(X "Class") |> Seq.isEmpty |> not) - |> Seq.iter (fun m -> let mname = m.Descendants(X "ModuleName") - |> Seq.map (fun x -> x.Value) - |> Seq.head - let package = XElement(X "package", - XAttribute(X "name", mname)) - let files = m.Descendants(X "File") - |> Seq.fold(fun m x -> m |> - Map.add (x.Attribute(X "uid").Value) (x.Attribute(X "fullPath").Value)) Map.empty - packages.Add(package) - let classes = XElement(X "classes") - package.Add(classes) - - extract m package - - m.Descendants(X "Method") - |> Seq.filter(fun m -> m.Descendants(X "FileRef") |> Seq.isEmpty |> not) - |> Seq.groupBy(fun mx -> ((mx.Parent.Parent.Descendants(X "FullName") |> Seq.head).Value, - mx.Descendants(X "FileRef") - |> Seq.map (fun s -> files - |> Map.find (s.Attribute(X "uid").Value)) - |> Seq.head)) - |> Seq.sortBy fst - |> Seq.iter (fun ((n,s),mx) -> let cx = XElement(X "class", - XAttribute(X "name", n), - XAttribute(X "filename", s)) - classes.Add(cx) - let mxx = XElement(X "methods") - cx.Add(mxx) - let q = mx - |> Seq.map(fun mt -> let fn = (mt.Descendants(X "Name") |> Seq.head).Value.Split([| ' '; '(' |]) |> Array.toList - let key = fn.[1].Substring(n.Length + 2) - let signa = fn.[0] + " " + fn.[2] - (key, (signa, mt))) - |> Seq.sortBy fst - |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) - |> Seq.fold(fun (b,bv,s,sv) (key, (signa, mt)) -> let mtx = XElement(X "method", - XAttribute(X "name", key), - XAttribute(X "signature", signa)) - extract mt mtx - mxx.Add(mtx) - let lines = XElement(X "lines") - mtx.Add(lines) - mt.Descendants(X "SequencePoint") - |> Seq.iter(fun s -> let vc = s.Attribute(X "vc") - let vx = if isNull vc then "0" else vc.Value - let bec = s.Attribute(X "bec").Value |> Int32.TryParse |> snd - let bev = s.Attribute(X "bev").Value |> Int32.TryParse |> snd - let line = XElement(X "line", - XAttribute(X "number", s.Attribute(X "sl").Value), - XAttribute(X "hits", vx), - XAttribute(X "branch", if bec = 0 then "false" else "true")) - let doBranch () = - let pc = Math.Round(100.0 * (float bev)/ (float bec)) |> int - line.SetAttributeValue(X "condition-coverage", - sprintf "%d%% (%d/%d)" pc bev bec) - let cc = XElement(X "conditions") - line.Add cc - let co = XElement(X "condition", - XAttribute(X "number", 0), - XAttribute(X "type", "jump"), - XAttribute(X "coverage", sprintf "%d%%" pc)) - cc.Add co - - if bec > 0 then doBranch() - - lines.Add line - ) - let summary = mt.Descendants(X "Summary") |> Seq.head - ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), - bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), - s + (summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd), - sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd))) (0,0,0,0) - let (b,bv,s,sv) = q - if s > 0 then cx.SetAttributeValue(X "line-rate", (float sv)/(float s)) - if b > 0 then cx.SetAttributeValue(X "branch-rate", (float bv)/(float b)) - ) - ) + |> Seq.iter (fun ``module`` -> let mname = ``module``.Descendants(X "ModuleName") + |> Seq.map (fun x -> x.Value) + |> Seq.head + let package = XElement(X "package", + XAttribute(X "name", mname)) + let files = lookUpFiles ``module`` + packages.Add(package) + let classes = XElement(X "classes") + package.Add(classes) + + extract ``module`` package + ProcessModule files classes ``module``) extract (report.Descendants(X "CoverageSession") |> Seq.head) packages.Parent @@ -175,7 +195,9 @@ module Cobertura = XAttribute(X "line-rate", 0), XAttribute(X "branch-rate", 0), XAttribute(X "version", AssemblyVersionInformation.AssemblyVersion), - XAttribute(X "timestamp", int((DateTime.UtcNow - DateTime(1970,1,1,0,0,0,DateTimeKind.Utc)).TotalSeconds)) + XAttribute(X "timestamp", + int((DateTime.UtcNow - + DateTime(1970,1,1,0,0,0,DateTimeKind.Utc)).TotalSeconds)) ) rewrite.Add(element) @@ -191,7 +213,7 @@ module Cobertura = |> Seq.iter(fun c -> let reprise = XElement(X "lines") c.Add reprise let lines = c.Descendants(X "line") - |> Seq.sortBy(fun l -> l.Attribute(X "number").Value + |> Seq.sortBy(fun l -> l.Attribute(X "number").Value |> Int32.TryParse |> snd) |> Seq.toList lines From d08e2a8606057b62e9d74407cce0613d1001defc Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 10:46:17 +0100 Subject: [PATCH 40/48] Factor more common code --- AltCover/Cobertura.fs | 8 ++++---- AltCover/LCov.fs | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 638eb0293..30d95360c 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -42,7 +42,7 @@ module Cobertura = let key = fn.[1].Substring(n.Length + 1) let signature = fn.[0] + " " + fn.[2] (key, (signature, m))) - |> Seq.sortBy fst + |> LCov.SortByFirst |> Seq.fold (ProcessMethod methods) (0,0) let ProcessClass (classes:XElement) (hits, total) ((name,signature),``method``) = @@ -62,7 +62,7 @@ module Cobertura = ``method``.Descendants(X "seqpnt") |> Seq.map (fun s -> s.Attribute(X "document").Value) |> Seq.head)) - |> Seq.sortBy fst + |> LCov.SortByFirst |> Seq.fold (ProcessClass classes) (0,0) let ProcessModule (hits, total) (``module``:XElement) = @@ -139,7 +139,7 @@ module Cobertura = let key = fn.[1].Substring(name.Length + 2) let signature = fn.[0] + " " + fn.[2] (key, (signature, ``method``))) - |> Seq.sortBy fst + |> LCov.SortByFirst |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) |> Seq.fold(ProcessMethod methods) (0,0,0,0) @@ -163,7 +163,7 @@ module Cobertura = |> Seq.map (fun s -> files |> Map.find (s.Attribute(X "uid").Value)) |> Seq.head)) - |> Seq.sortBy fst + |> LCov.SortByFirst |> Seq.iter (ProcessClass classes) let lookUpFiles (``module``:XElement) = diff --git a/AltCover/LCov.fs b/AltCover/LCov.fs index 3716b488f..24e96f295 100644 --- a/AltCover/LCov.fs +++ b/AltCover/LCov.fs @@ -17,12 +17,17 @@ module LCov = let lineOfMethod (m : XElement) = (m.Descendants(X "seqpnt") |> Seq.head).Attribute(X "line").Value |> Int32.TryParse |> snd + let SortByFirst s = + s + |> Seq.sortBy fst + + let multiSort (by : 'a -> int) (l : (string * 'a seq) seq) = l |> Seq.map (fun (f, ms) -> (f, ms |> Seq.sortBy by |> Seq.toList)) - |> Seq.sortBy fst + |> SortByFirst let multiSortByNameAndStartLine (l : (string * XElement seq) seq) = multiSort lineOfMethod l From f6acbc21008a943ba5b31731a147791348604043 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 11:08:34 +0100 Subject: [PATCH 41/48] Extract Method --- AltCover/Cobertura.fs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 30d95360c..44ef95de6 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -115,14 +115,18 @@ module Cobertura = if bec > 0 then doBranch bec bev line lines.Add line - let ProcessMethod (methods:XElement) (b,bv,s,sv) (key, (signature, ``method``)) = + let AddMethod (methods:XElement) (key, signature) = let mtx = XElement(X "method", XAttribute(X "name", key), XAttribute(X "signature", signature)) - extract ``method`` mtx methods.Add(mtx) let lines = XElement(X "lines") mtx.Add(lines) + (mtx, lines) + + let ProcessMethod (methods:XElement) (b,bv,s,sv) (key, (signature, ``method``)) = + let mtx, lines = AddMethod (methods:XElement) (key, signature) + extract ``method`` mtx ``method``.Descendants(X "SequencePoint") |> Seq.iter(ProcessSeqPnt lines) let summary = ``method``.Descendants(X "Summary") |> Seq.head From bccb46d3d4bec646f4cdce5133a07c8ffd1ad6ff Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 13:00:07 +0100 Subject: [PATCH 42/48] Explicitly report the coverage defect too, if relevant --- AltCover/Runner.fs | 11 +++++++++-- AltCover/Strings.eo.resx | 3 +++ AltCover/Strings.resx | 3 +++ Tests/Runner.Tests.fs | 22 ++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/AltCover/Runner.fs b/AltCover/Runner.fs index ca3dbbdf5..44781bbce 100644 --- a/AltCover/Runner.fs +++ b/AltCover/Runner.fs @@ -341,6 +341,9 @@ module Runner = let WriteResourceWithFormatItems s x = String.Format (CultureInfo.CurrentCulture, s |> CommandLine.resources.GetString, x) |> Output.Info + let WriteErrorResourceWithFormatItems s x = + String.Format (CultureInfo.CurrentCulture, s |> CommandLine.resources.GetString, x) |> Output.Error + let internal SetRecordToFile report = LCov.DoWithFile (fun () -> let binpath = report + ".acv" @@ -558,8 +561,12 @@ module Runner = let mutable internal DoReport = WriteReportBase let DoSummaries (document:XDocument) (format:Base.ReportFormat) result = - Summaries - |> List.fold (fun r summary -> summary document format r) result + let code = Summaries + |> List.fold (fun r summary -> summary document format r) result + if (code > 0 && code <> result) + then WriteErrorResourceWithFormatItems "threshold" [| code :> obj ; + (Option.get threshold) :> obj|] + code let DoCoverage arguments options1 = let check1 = DeclareOptions () diff --git a/AltCover/Strings.eo.resx b/AltCover/Strings.eo.resx index e0af5a5e5..11abb7f07 100644 --- a/AltCover/Strings.eo.resx +++ b/AltCover/Strings.eo.resx @@ -315,4 +315,7 @@ Detaloj skribitaj al {0} Laŭvola: Dosiero por la versio de formato Cobertura de la datumoj kolektitaj + + Kovra procento atingita estas {0}% sub la sojlo de {1}%. + \ No newline at end of file diff --git a/AltCover/Strings.resx b/AltCover/Strings.resx index 62ed4b2ef..0a03c3640 100644 --- a/AltCover/Strings.resx +++ b/AltCover/Strings.resx @@ -316,4 +316,7 @@ Details written to {0} Optional: File for Cobertura format version of the collected data + + Coverage percentage achieved is {0}% below the threshold of {1}%. + \ No newline at end of file diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index 4ac84b9a4..a4f8b496f 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -1606,4 +1606,26 @@ or finally Cobertura.path := None + [] + member self.ThresholdViolationShouldBeReported() = + let saveErr = Output.Error + let saveSummaries = Runner.Summaries + let builder = System.Text.StringBuilder() + let saved = Runner.threshold + + try + Runner.Summaries <- [ (fun _ _ _ -> 23) ] + Output.Error <- (fun s -> builder.Append(s).Append("|") |> ignore) + Runner.threshold <- Some 42 + + let delta = Runner.DoSummaries (XDocument()) Base.ReportFormat.NCover 0 + Assert.That (delta, Is.EqualTo 23) + Assert.That(builder.ToString(), + Is.EqualTo "Coverage percentage achieved is 23% below the threshold of 42%.|") + + finally + Output.Error <- saveErr + Runner.Summaries <- saveSummaries + Runner.threshold <- saved + end \ No newline at end of file From cdf38f9526de0afe6dafdd2c7bbfb458609e007e Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 14:02:51 +0100 Subject: [PATCH 43/48] Add sources as per https://github.com/cobertura/cobertura/blob/master/cobertura/src/main/java/net/sourceforge/cobertura/reporting/xml/XMLReport.java#L124 --- AltCover/Cobertura.fs | 16 +++++++++++++++- Shadow.Tests/SimpleCoverage.xml | 20 ++++++++++---------- Tests/NCover.cob | 5 ++++- Tests/OpenCover.cob | 5 ++++- Tests/Runner.Tests.fs | 16 ++++++++-------- Tests/Sample1WithOpenCover.xml | 2 +- 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 44ef95de6..7715e4297 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -1,6 +1,7 @@ namespace AltCover open System +open System.IO open System.Xml.Linq // based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml @@ -12,6 +13,16 @@ module Cobertura = let SetRate hits total rate (target:XElement) = if total > 0 then target.SetAttributeValue(X rate, (float hits)/(float total)) + let AddSources (report:XDocument) (target:XElement) tag attribute = + report.Descendants(X tag) + |> Seq.map (fun s -> s.Attribute(X attribute).Value |> Path.GetDirectoryName) + |> Seq.fold (fun s f -> s |> Set.add f) Set.empty + |> Seq.sort + |> Seq.iter (fun f -> target.Descendants(X "sources") + |> Seq.iter (fun s -> s.Add(XElement(X "source", + XText(f))))) + + let internal NCover (report:XDocument) (packages:XElement) = let ProcessSeqPnts (``method``:XElement) (lines:XElement) = ``method``.Descendants(X "seqpnt") @@ -79,6 +90,7 @@ module Cobertura = |> Seq.fold ProcessModule (0,0) SetRate hits total "line-rate" packages.Parent packages.Parent.SetAttributeValue(X "branch-rate", null) + AddSources report packages.Parent "seqpnt" "document" let internal OpenCover (report:XDocument) (packages:XElement) = let extract (owner:XElement) (target:XElement) = @@ -125,7 +137,7 @@ module Cobertura = (mtx, lines) let ProcessMethod (methods:XElement) (b,bv,s,sv) (key, (signature, ``method``)) = - let mtx, lines = AddMethod (methods:XElement) (key, signature) + let mtx, lines = AddMethod (methods:XElement) (key, signature) extract ``method`` mtx ``method``.Descendants(X "SequencePoint") |> Seq.iter(ProcessSeqPnt lines) @@ -192,6 +204,7 @@ module Cobertura = ProcessModule files classes ``module``) extract (report.Descendants(X "CoverageSession") |> Seq.head) packages.Parent + AddSources report packages.Parent "File" "fullPath" let internal Summary (report:XDocument) (format:Base.ReportFormat) result = let rewrite = XDocument(XDeclaration("1.0", "utf-8", "yes"), [||]) @@ -205,6 +218,7 @@ module Cobertura = ) rewrite.Add(element) + element.Add(XElement(X "sources")) let packages = XElement(X "packages") element.Add(packages) diff --git a/Shadow.Tests/SimpleCoverage.xml b/Shadow.Tests/SimpleCoverage.xml index 68455a653..eaa60aa66 100644 --- a/Shadow.Tests/SimpleCoverage.xml +++ b/Shadow.Tests/SimpleCoverage.xml @@ -3,16 +3,16 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 7d7ae5afa..300a505fa 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -1,9 +1,12 @@ + + altcover/Sample1 + - + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index 6416dd261..c20068618 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -1,9 +1,12 @@ + + altcover\Sample1 + - + diff --git a/Tests/Runner.Tests.fs b/Tests/Runner.Tests.fs index a4f8b496f..ea2408e01 100644 --- a/Tests/Runner.Tests.fs +++ b/Tests/Runner.Tests.fs @@ -1484,8 +1484,8 @@ or use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) - let expected = reader.ReadToEnd().Replace("\r", String.Empty) - Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + let expected = reader.ReadToEnd().Replace("\r", String.Empty).Replace("\\","/") + Assert.That (result.Replace("\r", String.Empty).Replace("\\","/"), Is.EqualTo expected) finally LCov.path := None @@ -1513,8 +1513,8 @@ or use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) - let expected = reader.ReadToEnd().Replace("\r", String.Empty) - Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected) + let expected = reader.ReadToEnd().Replace("\r", String.Empty).Replace("\\","/") + Assert.That (result.Replace("\r", String.Empty).Replace("\\","/"), Is.EqualTo expected) finally LCov.path := None @@ -1563,14 +1563,14 @@ or let result = Regex.Replace(File.ReadAllText unique, """timestamp=\"\d*\">""", - """timestamp="xx">""") + """timestamp="xx">""").Replace("\\","/") let resource2 = Assembly.GetExecutingAssembly().GetManifestResourceNames() |> Seq.find (fun n -> n.EndsWith("NCover.cob", StringComparison.Ordinal)) use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) - let expected = reader.ReadToEnd().Replace("\r", String.Empty) + let expected = reader.ReadToEnd().Replace("\r", String.Empty).Replace("\\","/") Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected, result) finally Cobertura.path := None @@ -1601,8 +1601,8 @@ or use stream2 = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource2) use reader = new StreamReader(stream2) - let expected = reader.ReadToEnd().Replace("\r", String.Empty) - Assert.That (result.Replace("\r", String.Empty), Is.EqualTo expected, result) + let expected = reader.ReadToEnd().Replace("\r", String.Empty).Replace("\\","/") + Assert.That (result.Replace("\r", String.Empty).Replace("\\","/"), Is.EqualTo expected, result) finally Cobertura.path := None diff --git a/Tests/Sample1WithOpenCover.xml b/Tests/Sample1WithOpenCover.xml index d937d4b2e..775cf3956 100644 --- a/Tests/Sample1WithOpenCover.xml +++ b/Tests/Sample1WithOpenCover.xml @@ -20,7 +20,7 @@ 2018-02-22T08:50:13.1034471Z Sample1 - + From a5b6d2ef511387dc17233ad691ce415e90617f04 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 14:27:35 +0100 Subject: [PATCH 44/48] Compute Cyclomatic Complexity averages as per https://github.com/cobertura/cobertura/blob/master/cobertura/src/main/java/net/sourceforge/cobertura/reporting/ComplexityCalculator.java#L62 --- AltCover/Cobertura.fs | 26 ++++++++++++++++++-------- Tests/NCover.cob | 8 ++++---- Tests/OpenCover.cob | 4 ++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index 7715e4297..bc898e4a8 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -44,6 +44,7 @@ module Cobertura = mtx.Add(lines) let (mHits, mTotal) = ProcessSeqPnts ``method`` lines SetRate mHits mTotal "line-rate" mtx + SetRate 1 1 "branch-rate" mtx (hits + mHits, total + mTotal) let SortMethod (n:String) (methods:XElement) (``method``: XElement seq) = @@ -65,6 +66,8 @@ module Cobertura = ``class``.Add(methods) let (mHits, mTotal) = SortMethod name methods ``method`` SetRate mHits mTotal "line-rate" ``class`` + SetRate 1 1 "branch-rate" ``class`` + SetRate 1 1 "complexity" ``class`` (hits + mHits, total + mTotal) let ExtractClasses (``module``:XElement) classes = @@ -84,12 +87,14 @@ module Cobertura = package.Add(classes) let (cHits, cTotal) = ExtractClasses ``module`` classes SetRate cHits cTotal "line-rate" package + SetRate 1 1 "branch-rate" package + SetRate 1 1 "complexity" package (hits + cHits, total + cTotal) let (hits, total) = report.Descendants(X "module") |> Seq.fold ProcessModule (0,0) SetRate hits total "line-rate" packages.Parent - packages.Parent.SetAttributeValue(X "branch-rate", null) + SetRate 1 1 "branch-rate" packages.Parent AddSources report packages.Parent "seqpnt" "document" let internal OpenCover (report:XDocument) (packages:XElement) = @@ -136,7 +141,7 @@ module Cobertura = mtx.Add(lines) (mtx, lines) - let ProcessMethod (methods:XElement) (b,bv,s,sv) (key, (signature, ``method``)) = + let ProcessMethod (methods:XElement) (b,bv,s,sv,c,cv) (key, (signature, ``method``)) = let mtx, lines = AddMethod (methods:XElement) (key, signature) extract ``method`` mtx ``method``.Descendants(X "SequencePoint") @@ -145,7 +150,9 @@ module Cobertura = ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), s + (summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd), - sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd)) + sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd), + c + 1, + cv + (``method``.Attribute(X "cyclomaticComplexity").Value |> Int32.TryParse |> snd)) let ArrangeMethods (name:String) (methods:XElement) (methodSet:XElement seq) = methodSet @@ -157,18 +164,20 @@ module Cobertura = (key, (signature, ``method``))) |> LCov.SortByFirst |> Seq.filter (fun (_,(_,mt)) -> mt.Descendants(X "SequencePoint") |> Seq.isEmpty |> not) - |> Seq.fold(ProcessMethod methods) (0,0,0,0) + |> Seq.fold(ProcessMethod methods) (0,0,0,0,0,0) - let ProcessClass (classes:XElement) ((name, source), methodSet) = + let ProcessClass (classes:XElement) (cvcum, ccum) ((name, source), methodSet) = let ``class`` = XElement(X "class", XAttribute(X "name", name), XAttribute(X "filename", source)) classes.Add(``class``) let methods = XElement(X "methods") ``class``.Add(methods) - let (b,bv,s,sv) = ArrangeMethods name methods methodSet + let (b,bv,s,sv,c,cv) = ArrangeMethods name methods methodSet SetRate sv s "line-rate" ``class`` SetRate bv b "branch-rate" ``class`` + SetRate cv c "complexity" ``class`` + (cv + cvcum, c + ccum) let ProcessModule files classes (``module``:XElement) = ``module``.Descendants(X "Method") @@ -180,7 +189,7 @@ module Cobertura = |> Map.find (s.Attribute(X "uid").Value)) |> Seq.head)) |> LCov.SortByFirst - |> Seq.iter (ProcessClass classes) + |> Seq.fold (ProcessClass classes) (0,0) let lookUpFiles (``module``:XElement) = ``module``.Descendants(X "File") @@ -201,7 +210,8 @@ module Cobertura = package.Add(classes) extract ``module`` package - ProcessModule files classes ``module``) + let (cv,c) = ProcessModule files classes ``module`` + SetRate cv c "complexity" package) extract (report.Descendants(X "CoverageSession") |> Seq.head) packages.Parent AddSources report packages.Parent "File" "fullPath" diff --git a/Tests/NCover.cob b/Tests/NCover.cob index 300a505fa..0f5bd0c38 100644 --- a/Tests/NCover.cob +++ b/Tests/NCover.cob @@ -1,14 +1,14 @@ - + altcover/Sample1 - + - + - + diff --git a/Tests/OpenCover.cob b/Tests/OpenCover.cob index c20068618..63c040ead 100644 --- a/Tests/OpenCover.cob +++ b/Tests/OpenCover.cob @@ -4,9 +4,9 @@ altcover\Sample1 - + - + From 7aba28e7e4a0ed25df3c01bb7a94bcd01da0beee Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 14:33:06 +0100 Subject: [PATCH 45/48] Tracking what has been done so far --- ReleaseNotes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 9d8720dd9..04919969e 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,8 +1,8 @@ # 3.0.4xx (Caba series release 8) -* [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function completes with a tail call -* [BUGFIX] for `-c|callContext` option -- generate valid IL where the context function contains a branch directly to a return instruction -* `-c|cobertura` option in `runner` mode (parameter `Cobertura` for the `AltCover.Collect` task) -- Cobertura format output to the given file name, with format composition based on the sample file at https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml - +* [BUGFIX] for `-c|callContext` option -- generate valid IL where the function being tracked completes with a tail call +* [BUGFIX] for `-c|callContext` option -- generate valid IL where the function being tracked contains a branch directly to a return instruction +* `-c|cobertura` option in `runner` mode (parameter `Cobertura` for the `AltCover.Collect` task) -- Cobertura format output to the given file name (more complete for OpenCover format coverage gathering than NCover, inevitably) +* Signal failure explicitly for `-t|threshold` violations, as well as through the return code # 3.0.433 (Caba series release 7) * `-t|threshold` option in `runner` mode (parameter `Threshold` for the `AltCover.Collect` task) to fail the build (non-zero return code or MSBuild error state) if coverage falls below the specified percentage From 4a15b73b5804c9129b27a5ccfb4a488b47c966fa Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 15:07:46 +0100 Subject: [PATCH 46/48] Refactore common code --- AltCover/Cobertura.fs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index bc898e4a8..ba1368455 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -141,18 +141,21 @@ module Cobertura = mtx.Add(lines) (mtx, lines) + let AddAttributeValue (element:XElement) name v= + v + (element.Attribute(X name).Value |> Int32.TryParse |> snd) + let ProcessMethod (methods:XElement) (b,bv,s,sv,c,cv) (key, (signature, ``method``)) = let mtx, lines = AddMethod (methods:XElement) (key, signature) extract ``method`` mtx ``method``.Descendants(X "SequencePoint") |> Seq.iter(ProcessSeqPnt lines) let summary = ``method``.Descendants(X "Summary") |> Seq.head - ( b + (summary.Attribute(X "numBranchPoints").Value |> Int32.TryParse |> snd), - bv + (summary.Attribute(X "visitedBranchPoints").Value |> Int32.TryParse |> snd), - s + (summary.Attribute(X "numSequencePoints").Value |> Int32.TryParse |> snd), - sv + (summary.Attribute(X "visitedSequencePoints").Value |> Int32.TryParse |> snd), + ( b |> AddAttributeValue summary "numBranchPoints", + bv |> AddAttributeValue summary "visitedBranchPoints", + s |> AddAttributeValue summary "numSequencePoints", + sv |> AddAttributeValue summary "visitedSequencePoints", c + 1, - cv + (``method``.Attribute(X "cyclomaticComplexity").Value |> Int32.TryParse |> snd)) + cv |> AddAttributeValue ``method``"cyclomaticComplexity") let ArrangeMethods (name:String) (methods:XElement) (methodSet:XElement seq) = methodSet From 81350f05fcaa14e758e802650021476d37a87540 Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 15:16:00 +0100 Subject: [PATCH 47/48] Whitespace --- AltCover/AltCover.fs | 2 +- AltCover/Cobertura.fs | 3 +-- AltCover/CommandLine.fs | 6 +++--- AltCover/LCov.fs | 3 +-- AltCover/Runner.fs | 4 ++-- Sample5/Properties/AssemblyInfo.cs | 3 +-- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/AltCover/AltCover.fs b/AltCover/AltCover.fs index 7b57655f4..e1c6f834d 100644 --- a/AltCover/AltCover.fs +++ b/AltCover/AltCover.fs @@ -327,7 +327,7 @@ module Main = | Right (rest, fromInfo, toInfo, targetInfo) -> let report = Visitor.ReportPath() let result = CommandLine.doPathOperation( fun () -> - report + report |> Path.GetDirectoryName |> CommandLine.ensureDirectory let (assemblies, assemblyNames) = PrepareTargetFiles fromInfo toInfo targetInfo diff --git a/AltCover/Cobertura.fs b/AltCover/Cobertura.fs index ba1368455..19c0a3087 100644 --- a/AltCover/Cobertura.fs +++ b/AltCover/Cobertura.fs @@ -22,7 +22,6 @@ module Cobertura = |> Seq.iter (fun s -> s.Add(XElement(X "source", XText(f))))) - let internal NCover (report:XDocument) (packages:XElement) = let ProcessSeqPnts (``method``:XElement) (lines:XElement) = ``method``.Descendants(X "seqpnt") @@ -141,7 +140,7 @@ module Cobertura = mtx.Add(lines) (mtx, lines) - let AddAttributeValue (element:XElement) name v= + let AddAttributeValue (element:XElement) name v = v + (element.Attribute(X name).Value |> Int32.TryParse |> snd) let ProcessMethod (methods:XElement) (b,bv,s,sv,c,cv) (key, (signature, ``method``)) = diff --git a/AltCover/CommandLine.fs b/AltCover/CommandLine.fs index ffc5fa864..571456c74 100644 --- a/AltCover/CommandLine.fs +++ b/AltCover/CommandLine.fs @@ -50,7 +50,7 @@ module CommandLine = let internal resources = ResourceManager(resource , Assembly.GetExecutingAssembly()) let conditionalOutput condition output = - if condition() + if condition() then output() let ensureDirectory directory = @@ -92,7 +92,7 @@ module CommandLine = Write Console.Out ConsoleColor.White line let internal Filter line f = - if line |> String.IsNullOrEmpty |> not + if line |> String.IsNullOrEmpty |> not then f line let internal Launch (cmd:string) args toDirectory = @@ -176,7 +176,7 @@ module CommandLine = |> Output.Error let ReportErrors (tag:string) = - conditionalOutput(fun () -> tag |> String.IsNullOrWhiteSpace |> not && + conditionalOutput(fun () -> tag |> String.IsNullOrWhiteSpace |> not && error |> List.isEmpty |> not) (fun () -> tag |> resources.GetString |> Output.Error) diff --git a/AltCover/LCov.fs b/AltCover/LCov.fs index 24e96f295..7a9c472b7 100644 --- a/AltCover/LCov.fs +++ b/AltCover/LCov.fs @@ -21,7 +21,6 @@ module LCov = s |> Seq.sortBy fst - let multiSort (by : 'a -> int) (l : (string * 'a seq) seq) = l |> Seq.map (fun (f, ms) -> (f, ms @@ -250,4 +249,4 @@ module LCov = // end_of_record writer.WriteLine "end_of_record" )) - result + result \ No newline at end of file diff --git a/AltCover/Runner.fs b/AltCover/Runner.fs index 44781bbce..13f384d1b 100644 --- a/AltCover/Runner.fs +++ b/AltCover/Runner.fs @@ -563,8 +563,8 @@ module Runner = let DoSummaries (document:XDocument) (format:Base.ReportFormat) result = let code = Summaries |> List.fold (fun r summary -> summary document format r) result - if (code > 0 && code <> result) - then WriteErrorResourceWithFormatItems "threshold" [| code :> obj ; + if (code > 0 && code <> result) + then WriteErrorResourceWithFormatItems "threshold" [| code :> obj ; (Option.get threshold) :> obj|] code diff --git a/Sample5/Properties/AssemblyInfo.cs b/Sample5/Properties/AssemblyInfo.cs index 33f792779..6f1831ea8 100644 --- a/Sample5/Properties/AssemblyInfo.cs +++ b/Sample5/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file From 804882864fd2236697a2fe50156417ed133f3b7e Mon Sep 17 00:00:00 2001 From: Steve Gilham Date: Sun, 22 Apr 2018 16:21:09 +0100 Subject: [PATCH 48/48] Go for release --- ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 04919969e..74371f566 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,4 @@ -# 3.0.4xx (Caba series release 8) +# 3.0.444 (Caba series release 8) * [BUGFIX] for `-c|callContext` option -- generate valid IL where the function being tracked completes with a tail call * [BUGFIX] for `-c|callContext` option -- generate valid IL where the function being tracked contains a branch directly to a return instruction * `-c|cobertura` option in `runner` mode (parameter `Cobertura` for the `AltCover.Collect` task) -- Cobertura format output to the given file name (more complete for OpenCover format coverage gathering than NCover, inevitably)