diff --git a/src/app/Fake.Testing.ReportGenerator/Fake.Testing.ReportGenerator.fsproj b/src/app/Fake.Testing.ReportGenerator/Fake.Testing.ReportGenerator.fsproj index e78775fbac5..e5fbcea2406 100644 --- a/src/app/Fake.Testing.ReportGenerator/Fake.Testing.ReportGenerator.fsproj +++ b/src/app/Fake.Testing.ReportGenerator/Fake.Testing.ReportGenerator.fsproj @@ -13,6 +13,7 @@ + diff --git a/src/app/Fake.Testing.ReportGenerator/ReportGenerator.fs b/src/app/Fake.Testing.ReportGenerator/ReportGenerator.fs index aa2d0293584..b07916b9277 100644 --- a/src/app/Fake.Testing.ReportGenerator/ReportGenerator.fs +++ b/src/app/Fake.Testing.ReportGenerator/ReportGenerator.fs @@ -16,20 +16,27 @@ module Fake.Testing.ReportGenerator open System -open System.Text open System.IO open Fake.Core open Fake.IO +open Fake.IO.Globbing +open Fake.IO.FileSystemOperators type ReportType = - | Html = 0 - | HtmlSummary = 1 - | Xml = 2 - | XmlSummary = 3 - | Latex = 4 - | LatexSummary = 5 - | Badges = 6 + | Html + | HtmlChart + | HtmlInline + | HtmlSummary + | MHtml + | PngChart + | TextSummary + | Xml + | XmlSummary + | Latex + | LatexSummary + | Badges + | CsvSummary type LogVerbosity = | Verbose = 0 @@ -38,7 +45,8 @@ type LogVerbosity = /// ReportGenerator parameters, for more details see: https://github.com/danielpalme/ReportGenerator. type ReportGeneratorParams = - { /// (Required) Path to the ReportGenerator exe file. + { + /// (Required) Path to the ReportGenerator exe file. ExePath : string /// (Required) The directory where the generated report should be saved. TargetDir : string @@ -50,9 +58,22 @@ type ReportGeneratorParams = /// Can be used in future reports to show coverage evolution. HistoryDir : string /// Optional list of assemblies that should be included or excluded - /// in the report. Exclusion filters take precedence over inclusion + /// in the report e.g. "-Foo.Test" (default is "+*") + /// Exclusion filters take precedence over inclusion /// filters. Wildcards are allowed. Filters : string list + /// Optional list of files that should be included or excluded + /// in the report e.g. "-*.xaml.cs" or "+*.cs" (default is "+*") + /// Exclusion filters take precedence over inclusion + /// filters. Wildcards are allowed. + FileFilters: string list + /// Optional list of classes that should be included or excluded + /// in the report e.g. "-*Tests" (default is "+*") + /// Exclusion filters take precedence over inclusion + /// filters. Wildcards are allowed. + ClassFilters: string list + /// Optional tag or build version + Tag: string option /// The verbosity level of the log messages. LogVerbosity : LogVerbosity /// The directory where the ReportGenerator process will be started. @@ -61,35 +82,71 @@ type ReportGeneratorParams = TimeOut : TimeSpan } let private currentDirectory = Directory.GetCurrentDirectory () +let private toolname = "ReportGenerator.exe" /// ReportGenerator default parameters let private ReportGeneratorDefaultParams = - { ExePath = "./tools/ReportGenerator/bin/ReportGenerator.exe" + { ExePath = Tools.findToolInSubPath toolname (currentDirectory "tools" "ReportGenerator") TargetDir = currentDirectory ReportTypes = [ ReportType.Html ] SourceDirs = [] - HistoryDir = String.Empty + HistoryDir = null Filters = [] + FileFilters = [] + ClassFilters = [] + Tag = None LogVerbosity = LogVerbosity.Verbose WorkingDir = currentDirectory TimeOut = TimeSpan.FromMinutes 5. } -/// Builds the report generator command line arguments from the given parameters and reports +/// Builds the report generator command line arguments and process from the given parameters and reports /// [omit] -let private buildReportGeneratorArgs parameters (reports : string seq) = - let reportTypes = parameters.ReportTypes |> List.map (fun rt -> rt.ToString()) - let sourceDirs = sprintf "-sourcedirs:%s" (String.Join(";", parameters.SourceDirs)) - let filters = sprintf "-filters:%s" (String.Join(";", parameters.Filters)) - - new StringBuilder() - |> StringBuilder.append (sprintf "-reports:%s" (String.Join(";", reports))) - |> StringBuilder.append (sprintf "-targetdir:%s" parameters.TargetDir) - |> StringBuilder.appendWithoutQuotes (sprintf "-reporttypes:%s" (String.Join(";", reportTypes))) - |> StringBuilder.appendIfTrue (parameters.SourceDirs.Length > 0) sourceDirs - |> StringBuilder.appendStringIfValueIsNotNullOrEmpty (parameters.HistoryDir) (sprintf "-historydir:%s" parameters.HistoryDir) - |> StringBuilder.appendIfTrue (parameters.Filters.Length > 0) filters - |> StringBuilder.appendWithoutQuotes (sprintf "-verbosity:%s" (parameters.LogVerbosity.ToString())) - |> StringBuilder.toText +let internal createProcess setParams (reports : string seq) = + let parameters = setParams ReportGeneratorDefaultParams + let tool = parameters.ExePath + + let joinWithSemicolon (xs: string seq) = String.Join(";", xs) + + let yieldIfSome paramName (value: string option) = + seq { match value with + | None -> () + | Some v -> yield sprintf "-%s:%s" paramName v } + + let yieldIfNotEmpty paramName (value: string seq) = + seq { match value |> Seq.toList with + | [] -> () + | xs -> yield sprintf "-%s:%s" paramName (xs |> joinWithSemicolon) } + + let yieldIfNotNullOrEmpty paramName value = + seq { if String.isNotNullOrEmpty value + then yield sprintf "-%s:%s" paramName value } + + let args = + [ + yield! reports |> yieldIfNotEmpty "reports" + yield sprintf "-targetdir:%s" parameters.TargetDir + + yield! parameters.ReportTypes + |> List.map (fun rt -> rt.ToString()) + |> yieldIfNotEmpty "reporttypes" + + yield! parameters.SourceDirs |> yieldIfNotEmpty "sourcedirs" + yield! parameters.HistoryDir |> yieldIfNotNullOrEmpty "historydir" + yield! parameters.Filters |> yieldIfNotEmpty "assemblyfilters" + yield! parameters.ClassFilters |> yieldIfNotEmpty "classfilters" + yield! parameters.FileFilters |> yieldIfNotEmpty "filefilters" + yield! parameters.Tag |> yieldIfSome "tag" + yield sprintf "-verbosity:%s" (parameters.LogVerbosity.ToString()) + ] + |> Arguments.OfArgs + + CreateProcess.fromCommand (RawCommand(tool, args)) + |> CreateProcess.withFramework + |> CreateProcess.withWorkingDirectory parameters.WorkingDir + |> CreateProcess.ensureExitCode + |> fun command -> + Trace.trace command.CommandLine + command /// Runs ReportGenerator on one or more coverage reports. /// ## Parameters @@ -97,20 +154,15 @@ let private buildReportGeneratorArgs parameters (reports : string seq) = /// - `setParams` - Function used to overwrite the default ReportGenerator parameters. /// - `reports` - Coverage reports. let generateReports setParams (reports : string list) = - let taskName = "ReportGenerator" - let description = "Generating reports" - - use __ = Trace.traceTask taskName description - let param = setParams ReportGeneratorDefaultParams - - let processArgs = buildReportGeneratorArgs param reports - Trace.tracefn "ReportGenerator command\n%s %s" param.ExePath processArgs - - let processStartInfo info = - { info with FileName = param.ExePath - WorkingDirectory = if param.WorkingDir |> String.isNullOrEmpty then info.WorkingDirectory else param.WorkingDir - Arguments = processArgs } |> Process.withFramework - match Process.execSimple processStartInfo param.TimeOut with - | 0 -> () - | v -> failwithf "ReportGenerator reported errors: %i" v - __.MarkSuccess() \ No newline at end of file + use __ = Trace.traceTask "ReportGenerator" "Generating reports" + + match reports with + | [] -> + Trace.trace "No reports given. Ignoring task" + | reports -> + reports + |> createProcess setParams + |> Proc.run + |> ignore + + __.MarkSuccess() diff --git a/src/app/Fake.Testing.ReportGenerator/VisibleTo.fs b/src/app/Fake.Testing.ReportGenerator/VisibleTo.fs new file mode 100644 index 00000000000..9a497f59ddc --- /dev/null +++ b/src/app/Fake.Testing.ReportGenerator/VisibleTo.fs @@ -0,0 +1,7 @@ + +namespace System +open System.Runtime.CompilerServices + +[] +[] +do () \ No newline at end of file diff --git a/src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj b/src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj index 9d26ed5688d..9a4b46e464b 100644 --- a/src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj +++ b/src/test/Fake.Core.UnitTests/Fake.Core.UnitTests.fsproj @@ -15,6 +15,7 @@ + @@ -30,6 +31,7 @@ + diff --git a/src/test/Fake.Core.UnitTests/Fake.Testing.ReportGenerator.fs b/src/test/Fake.Core.UnitTests/Fake.Testing.ReportGenerator.fs new file mode 100644 index 00000000000..a2705acc239 --- /dev/null +++ b/src/test/Fake.Core.UnitTests/Fake.Testing.ReportGenerator.fs @@ -0,0 +1,51 @@ +module Fake.Testing.ReportGeneratorTests + +open System.IO +open Fake.Core +open Fake.Testing +open Expecto + +let runCreateProcess setParams = + let cp = + ["report1.xml"; "report2.xml"] + |> Fake.Testing.ReportGenerator.createProcess (fun param -> + { setParams param with + ExePath = Path.Combine("reportgenerator", "ReportGenerator.exe") + TargetDir = "targetDir"}) + + let file, args = + match cp.Command with + | RawCommand(file, args) -> file, args + | _ -> failwithf "expected RawCommand" + |> ArgumentHelper.checkIfMono + + let expectedPath = Path.Combine("reportgenerator", "ReportGenerator.exe") + Expect.equal file expectedPath "Expected ReportGenerator.exe" + expectedPath, cp.Command.CommandLine + +[] +let tests = + testList "Fake.Testing.ReportGenerator.Tests" [ + testCase "Test that new argument generation with default parameters" <| fun _ -> + let expectedPath, commandLine = + runCreateProcess id + + Expect.equal commandLine + (sprintf "%s -reports:report1.xml;report2.xml -targetdir:targetDir -reporttypes:Html -verbosity:Verbose" expectedPath) "expected proper command line" + + testCase "Test that new argument generation with all parameters" <| fun _ -> + let expectedPath, commandLine = + runCreateProcess (fun p -> + { p with + ReportTypes = [ ReportGenerator.ReportType.Html + ReportGenerator.ReportType.MHtml ] + SourceDirs = [ "source1"; "source2" ] + HistoryDir = "history" + Filters = [ "+a1*"; "-a2*" ] + ClassFilters = [ "+c1*"; "-c2*" ] + FileFilters = [ "+f1*"; "-f2*" ] + Tag = Some "mytag" }) + + Expect.equal commandLine + (sprintf "%s -reports:report1.xml;report2.xml -targetdir:targetDir -reporttypes:Html;MHtml -sourcedirs:source1;source2 -historydir:history -assemblyfilters:+a1*;-a2* -classfilters:+c1*;-c2* -filefilters:+f1*;-f2* -tag:mytag -verbosity:Verbose" expectedPath) "expected proper command line" + ]