Skip to content

Commit

Permalink
Cherry-pick performance improvement from Commit: 4b55051 [4b55051]
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveGilham committed Jul 26, 2024
1 parent 08aaceb commit b646d0a
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 31 deletions.
79 changes: 53 additions & 26 deletions AltCover.Engine/Cobertura.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace AltCover

open System
open System.Collections.Generic
open System.IO
open System.Xml.Linq
open System.Globalization
Expand Down Expand Up @@ -76,12 +77,15 @@ module internal Cobertura =
[<SuppressMessage("Gendarme.Rules.Performance",
"AvoidRepetitiveCallsToPropertiesRule",
Justification = "Compiler inlines")>]
let extractSource (values: (string * #(string seq)) seq) =
let extractSource (values: ((string * string seq) * string list) seq) =
let starter = values |> Seq.head

match Seq.length values with
| 1 -> starter |> fst
| _ -> // extract longest common prefix
let files =
values |> Seq.map fst |> Seq.collect snd

let l =
values
|> Seq.map (fun (_, split) -> Seq.length split)
Expand All @@ -107,10 +111,11 @@ module internal Cobertura =
|> Path.Combine

let trimmed = prefix.Length
let source = fst starter
let source = starter |> fst |> fst

source.Substring(0, trimmed).Trim('\\')
+ String([| Path.DirectorySeparatorChar |])
(source.Substring(0, trimmed).Trim('\\')
+ String([| Path.DirectorySeparatorChar |]),
files)

[<System.Diagnostics.CodeAnalysis.SuppressMessage("Gendarme.Rules.Maintainability",
"AvoidUnnecessarySpecializationRule",
Expand All @@ -122,19 +127,41 @@ module internal Cobertura =
(tag: string)
(attribute: string)
=
let rawsources =
let files =
report.Descendants(tag.X)
|> Seq.map (fun s -> s.Attribute(attribute.X).Value)
|> Seq.filter (String.IsNullOrWhiteSpace >> not)
|> Seq.map Path.GetDirectoryName
|> Seq.filter (String.IsNullOrWhiteSpace >> not)
|> Seq.distinct
|> Seq.sort

let groupable = // (directory, facet list)
rawsources |> Seq.map (fun x -> (x, splitPath x))
let table =
Dictionary<string option, string>()

let rawsources =
files
|> Seq.filter (fun a ->
let fail =
a |> String.IsNullOrWhiteSpace |> not

if fail then
table[Option.ofObj a] <- String.Empty

fail)
|> Seq.map (fun a -> a, Path.GetDirectoryName a)
|> Seq.filter (fun (a, d) ->
let fail =
d |> String.IsNullOrWhiteSpace |> not

if fail then
table[Option.ofObj a] <- a

fail)
|> Seq.groupBy snd
|> Seq.map (fun (a, s) -> a, s |> Seq.map fst)
|> Seq.sortBy fst // seq of (directory, files full names)

let groups = // lead facet, (directory, facet list) seq
let groupable = // seq of ((directory, files full names), facets)
rawsources
|> Seq.map (fun x -> (x, x |> fst |> splitPath))

let groups = // seq of (root, seq of ((directory, files full names), facets))
groupable |> Seq.groupBy (snd >> grouping)

let results = // throws away everything but the sources
Expand All @@ -143,10 +170,19 @@ module internal Cobertura =
results
|> Seq.iter (fun f ->
target.Descendants("sources".X)
|> Seq.iter _.Add(XElement("source".X, XText(f))))
|> Seq.iter _.Add(XElement("source".X, XText(fst f))))

results // TODO - make look-up table
|> Seq.map (fun p -> p.Replace('\\', '/').Trim('/'))
|> Seq.iter (fun (p, files) ->
let prefix =
1 + p.Replace('\\', '/').Trim('/').Length

files
|> Seq.iter (fun f -> table[Some f] <- f.Substring(prefix))

)

table

let internal nCover (report: XDocument) (packages: XElement) =

Expand Down Expand Up @@ -239,13 +275,8 @@ module internal Cobertura =
(hits, total)
((name, document: string), method)
=
let normalized = document.Replace('\\', '/')

let filename =
sources
|> Seq.tryFind (fun s -> normalized.StartsWith(s, StringComparison.Ordinal))
|> Option.map (fun start -> document.Substring(start.Length + 1))
|> Option.defaultValue document
sources[Option.ofObj document]

let ``class`` =
XElement(
Expand Down Expand Up @@ -511,13 +542,9 @@ module internal Cobertura =
((name, fileid), methodSet)
=
let document = files |> Map.find fileid
let normalized = document.Replace('\\', '/')

let filename =
sources
|> Seq.tryFind (fun s -> normalized.StartsWith(s, StringComparison.Ordinal))
|> Option.map (fun start -> document.Substring(start.Length + 1))
|> Option.defaultValue document
sources[Option.ofObj document]

let ``class`` =
XElement(
Expand Down
9 changes: 4 additions & 5 deletions AltCover.Tests/Runner.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6258,23 +6258,22 @@ module AltCoverRunnerTests =
let sep =
String([| Path.DirectorySeparatorChar |])

let s = [ "some dummy value" ] |> List.toSeq

let cases =
[ ([ "a" ], "a")
([ "a/b/"; "a/b/c" ], "a/b" + sep)
([ "a/b/x/y"; "a/c/d" ], "a" + sep)
([ "c:\\b\\x\\y"; "c:\\b\\c\\d" ], "c:\\b" + sep) ]
|> List.map (fun (inputs, expect) ->
(inputs
|> Seq.map (fun x -> (x, Cobertura.I.splitPath x))),
|> List.map (fun x -> ((x, s), Cobertura.I.splitPath x))),
expect)

Assert.Multiple(fun () ->
cases
|> Seq.iter (fun (case, expect) ->
test
<@ Cobertura.I.extractSource case = expect

@>))
test <@ case |> Cobertura.I.extractSource |> fst = expect @>))

[<Test>]
let DegenerateCasesShouldNotGenerateCobertura () =
Expand Down

0 comments on commit b646d0a

Please sign in to comment.