コード例 #1
0
 public static CoverageResult GetCoverageResult(string filePath)
 {
     using (var result = new FileStream(filePath, FileMode.Open))
     {
         CoveragePrepareResult coveragePrepareResultLoaded = CoveragePrepareResult.Deserialize(result);
         Coverage coverage = new Coverage(coveragePrepareResultLoaded, new Mock <ILogger>().Object);
         return(coverage.GetCoverageResult());
     }
 }
コード例 #2
0
        public static CoverageResult GetCoverageResult(string filePath)
        {
            SetTestContainer();
            using var result = new FileStream(filePath, FileMode.Open);
            var logger = new Mock <ILogger>();

            logger.Setup(l => l.LogVerbose(It.IsAny <string>())).Callback((string message) =>
            {
                Assert.DoesNotContain("not found for module: ", message);
            });
            _processWideContainer.GetRequiredService <IInstrumentationHelper>().SetLogger(logger.Object);
            CoveragePrepareResult coveragePrepareResultLoaded = CoveragePrepareResult.Deserialize(result);
            Coverage coverage = new Coverage(coveragePrepareResultLoaded, logger.Object, _processWideContainer.GetService <IInstrumentationHelper>(), new FileSystem(), new SourceRootTranslator(new Mock <ILogger>().Object, new FileSystem()));

            return(coverage.GetCoverageResult());
        }
コード例 #3
0
        public override bool Execute()
        {
            try
            {
                Console.WriteLine("\nCalculating coverage result...");

                if (InstrumenterState is null || !File.Exists(InstrumenterState.ItemSpec))
                {
                    _logger.LogError("Result of instrumentation task not found");
                    return(false);
                }

                var coverage = new Coverage(CoveragePrepareResult.Deserialize(new FileStream(InstrumenterState.ItemSpec, FileMode.Open)), this._logger);
                var result   = coverage.GetCoverageResult();

                var directory = Path.GetDirectoryName(_output);
                if (directory == string.Empty)
                {
                    directory = Directory.GetCurrentDirectory();
                }
                else if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                var formats = _format.Split(',');
                foreach (var format in formats)
                {
                    var reporter = new ReporterFactory(format).CreateReporter();
                    if (reporter == null)
                    {
                        throw new Exception($"Specified output format '{format}' is not supported");
                    }

                    if (reporter.OutputType == ReporterOutputType.Console)
                    {
                        // Output to console
                        Console.WriteLine("  Outputting results to console");
                        Console.WriteLine(reporter.Report(result));
                    }
                    else
                    {
                        // Output to file
                        var filename = Path.GetFileName(_output);
                        filename = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
                        filename = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";

                        var report = Path.Combine(directory, filename);
                        Console.WriteLine($"  Generating report '{report}'");
                        File.WriteAllText(report, reporter.Report(result));
                    }
                }

                var thresholdTypeFlags = ThresholdTypeFlags.None;
                var thresholdStat      = ThresholdStatistic.Minimum;

                foreach (var thresholdType in _thresholdType.Split(',').Select(t => t.Trim()))
                {
                    if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Line;
                    }
                    else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Branch;
                    }
                    else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Method;
                    }
                }

                if (_thresholdStat.Equals("average", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Average;
                }
                else if (_thresholdStat.Equals("total", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Total;
                }

                var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
                var summary       = new CoverageSummary();
                int numModules    = result.Modules.Count;

                var linePercentCalculation   = summary.CalculateLineCoverage(result.Modules);
                var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
                var methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);

                var totalLinePercent   = linePercentCalculation.Percent;
                var totalBranchPercent = branchPercentCalculation.Percent;
                var totalMethodPercent = methodPercentCalculation.Percent;

                var averageLinePercent   = linePercentCalculation.AverageModulePercent;
                var averageBranchPercent = branchPercentCalculation.AverageModulePercent;
                var averageMethodPercent = methodPercentCalculation.AverageModulePercent;

                foreach (var module in result.Modules)
                {
                    var linePercent   = summary.CalculateLineCoverage(module.Value).Percent;
                    var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent;
                    var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent;

                    coverageTable.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%");
                }

                Console.WriteLine();
                Console.WriteLine(coverageTable.ToStringAlternative());

                coverageTable.Columns.Clear();
                coverageTable.Rows.Clear();

                coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
                coverageTable.AddRow("Total", $"{totalLinePercent}%", $"{totalBranchPercent}%", $"{totalMethodPercent}%");
                coverageTable.AddRow("Average", $"{averageLinePercent}%", $"{averageBranchPercent}%", $"{averageMethodPercent}%");

                Console.WriteLine(coverageTable.ToStringAlternative());

                thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, _threshold, thresholdTypeFlags, thresholdStat);
                if (thresholdTypeFlags != ThresholdTypeFlags.None)
                {
                    var exceptionMessageBuilder = new StringBuilder();
                    if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {_threshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {_threshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {_threshold}");
                    }

                    throw new Exception(exceptionMessageBuilder.ToString());
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }
コード例 #4
0
        public void CoveragePrepareResult_SerializationRoundTrip()
        {
            CoveragePrepareResult cpr = new CoveragePrepareResult();

            cpr.Identifier        = "Identifier";
            cpr.MergeWith         = "MergeWith";
            cpr.ModuleOrDirectory = "Module";
            cpr.UseSourceLink     = true;

            InstrumenterResult ir = new InstrumenterResult();

            ir.HitsFilePath = "HitsFilePath";
            ir.Module       = "Module";
            ir.ModulePath   = "ModulePath";
            ir.SourceLink   = "SourceLink";

            ir.HitCandidates.Add(new HitCandidate(true, 1, 2, 3));
            ir.HitCandidates.Add(new HitCandidate(false, 4, 5, 6));

            var doc = new Document()
            {
                Index = 0,
                Path  = "Path0"
            };

            doc.Lines.Add(0, new Line()
            {
                Class  = "Class0",
                Hits   = 0,
                Method = "Method0",
                Number = 0
            });
            doc.Branches.Add(new BranchKey(0, 0),
                             new Branch()
            {
                Class     = "Class0",
                EndOffset = 0,
                Hits      = 0,
                Method    = "Method",
                Number    = 0,
                Offset    = 0,
                Ordinal   = 0,
                Path      = 0
            });

            var doc2 = new Document()
            {
                Index = 1,
                Path  = "Path1"
            };

            doc2.Lines.Add(1, new Line()
            {
                Class  = "Class1",
                Hits   = 1,
                Method = "Method1",
                Number = 1
            });
            doc2.Branches.Add(new BranchKey(1, 1),
                              new Branch()
            {
                Class     = "Class1",
                EndOffset = 1,
                Hits      = 1,
                Method    = "Method1",
                Number    = 1,
                Offset    = 1,
                Ordinal   = 1,
                Path      = 1
            });

            ir.Documents.Add("key", doc);
            ir.Documents.Add("key2", doc2);
            cpr.Results = new InstrumenterResult[] { ir };

            CoveragePrepareResult roundTrip = CoveragePrepareResult.Deserialize(CoveragePrepareResult.Serialize(cpr));

            Assert.Equal(cpr.Identifier, roundTrip.Identifier);
            Assert.Equal(cpr.MergeWith, roundTrip.MergeWith);
            Assert.Equal(cpr.ModuleOrDirectory, roundTrip.ModuleOrDirectory);
            Assert.Equal(cpr.UseSourceLink, roundTrip.UseSourceLink);

            for (int i = 0; i < cpr.Results.Length; i++)
            {
                Assert.Equal(cpr.Results[i].HitsFilePath, roundTrip.Results[i].HitsFilePath);
                Assert.Equal(cpr.Results[i].Module, roundTrip.Results[i].Module);
                Assert.Equal(cpr.Results[i].ModulePath, roundTrip.Results[i].ModulePath);
                Assert.Equal(cpr.Results[i].SourceLink, roundTrip.Results[i].SourceLink);

                for (int k = 0; k < cpr.Results[i].HitCandidates.Count; k++)
                {
                    Assert.Equal(cpr.Results[i].HitCandidates[k].start, roundTrip.Results[i].HitCandidates[k].start);
                    Assert.Equal(cpr.Results[i].HitCandidates[k].isBranch, roundTrip.Results[i].HitCandidates[k].isBranch);
                    Assert.Equal(cpr.Results[i].HitCandidates[k].end, roundTrip.Results[i].HitCandidates[k].end);
                    Assert.Equal(cpr.Results[i].HitCandidates[k].docIndex, roundTrip.Results[i].HitCandidates[k].docIndex);
                }

                for (int k = 0; k < cpr.Results[i].Documents.Count; k++)
                {
                    var documents          = cpr.Results[i].Documents.ToArray();
                    var documentsRoundTrip = roundTrip.Results[i].Documents.ToArray();
                    for (int j = 0; j < documents.Length; j++)
                    {
                        Assert.Equal(documents[j].Key, documentsRoundTrip[j].Key);
                        Assert.Equal(documents[j].Value.Index, documentsRoundTrip[j].Value.Index);
                        Assert.Equal(documents[j].Value.Path, documentsRoundTrip[j].Value.Path);

                        for (int v = 0; v < documents[j].Value.Lines.Count; v++)
                        {
                            var lines          = documents[j].Value.Lines.ToArray();
                            var linesRoundTrip = documentsRoundTrip[j].Value.Lines.ToArray();

                            Assert.Equal(lines[v].Key, linesRoundTrip[v].Key);
                            Assert.Equal(lines[v].Value.Class, lines[v].Value.Class);
                            Assert.Equal(lines[v].Value.Hits, lines[v].Value.Hits);
                            Assert.Equal(lines[v].Value.Method, lines[v].Value.Method);
                            Assert.Equal(lines[v].Value.Number, lines[v].Value.Number);
                        }

                        for (int v = 0; v < documents[j].Value.Branches.Count; v++)
                        {
                            var branches          = documents[j].Value.Branches.ToArray();
                            var branchesRoundTrip = documentsRoundTrip[j].Value.Branches.ToArray();

                            Assert.Equal(branches[v].Key, branchesRoundTrip[v].Key);
                            Assert.Equal(branches[v].Value.Class, branchesRoundTrip[v].Value.Class);
                            Assert.Equal(branches[v].Value.EndOffset, branchesRoundTrip[v].Value.EndOffset);
                            Assert.Equal(branches[v].Value.Hits, branchesRoundTrip[v].Value.Hits);
                            Assert.Equal(branches[v].Value.Method, branchesRoundTrip[v].Value.Method);
                            Assert.Equal(branches[v].Value.Number, branchesRoundTrip[v].Value.Number);
                            Assert.Equal(branches[v].Value.Offset, branchesRoundTrip[v].Value.Offset);
                            Assert.Equal(branches[v].Value.Ordinal, branchesRoundTrip[v].Value.Ordinal);
                            Assert.Equal(branches[v].Value.Path, branchesRoundTrip[v].Value.Path);
                        }
                    }
                }
            }
        }
コード例 #5
0
ファイル: CoverageResultTask.cs プロジェクト: wdolek/coverlet
        public override bool Execute()
        {
            try
            {
                Console.WriteLine("\nCalculating coverage result...");

                IFileSystem fileSystem = ServiceProvider.GetService <IFileSystem>();
                if (InstrumenterState is null || !fileSystem.Exists(InstrumenterState.ItemSpec))
                {
                    _logger.LogError("Result of instrumentation task not found");
                    return(false);
                }

                Coverage coverage = null;
                using (Stream instrumenterStateStream = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open))
                {
                    IInstrumentationHelper instrumentationHelper = ServiceProvider.GetService <IInstrumentationHelper>();
                    // Task.Log is teared down after a task and thus the new MSBuildLogger must be passed to the InstrumentationHelper
                    // https://github.com/microsoft/msbuild/issues/5153
                    instrumentationHelper.SetLogger(_logger);
                    coverage = new Coverage(CoveragePrepareResult.Deserialize(instrumenterStateStream), _logger, ServiceProvider.GetService <IInstrumentationHelper>(), fileSystem, ServiceProvider.GetService <ISourceRootTranslator>());
                }

                try
                {
                    fileSystem.Delete(InstrumenterState.ItemSpec);
                }
                catch (Exception ex)
                {
                    // We don't want to block coverage for I/O errors
                    _logger.LogWarning($"Exception during instrument state deletion, file name '{InstrumenterState.ItemSpec}' exception message '{ex.Message}'");
                }

                CoverageResult result = coverage.GetCoverageResult();

                string directory = Path.GetDirectoryName(Output);
                if (directory == string.Empty)
                {
                    directory = Directory.GetCurrentDirectory();
                }
                else if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                string[] formats             = OutputFormat.Split(',');
                var      coverageReportPaths = new List <ITaskItem>(formats.Length);
                ISourceRootTranslator sourceRootTranslator = ServiceProvider.GetService <ISourceRootTranslator>();
                foreach (string format in formats)
                {
                    IReporter reporter = new ReporterFactory(format).CreateReporter();
                    if (reporter == null)
                    {
                        throw new Exception($"Specified output format '{format}' is not supported");
                    }

                    if (reporter.OutputType == ReporterOutputType.Console)
                    {
                        // Output to console
                        Console.WriteLine("  Outputting results to console");
                        Console.WriteLine(reporter.Report(result, sourceRootTranslator));
                    }
                    else
                    {
                        ReportWriter writer = new(CoverletMultiTargetFrameworksCurrentTFM,
                                                  directory,
                                                  Output,
                                                  reporter,
                                                  fileSystem,
                                                  ServiceProvider.GetService <IConsole>(),
                                                  result,
                                                  sourceRootTranslator);
                        string path     = writer.WriteReport();
                        var    metadata = new Dictionary <string, string> {
                            ["Format"] = format
                        };
                        coverageReportPaths.Add(new TaskItem(path, metadata));
                    }
                }

                ReportItems = coverageReportPaths.ToArray();

                var thresholdTypeFlagQueue = new Queue <ThresholdTypeFlags>();

                foreach (string thresholdType in ThresholdType.Split(',').Select(t => t.Trim()))
                {
                    if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line);
                    }
                    else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch);
                    }
                    else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method);
                    }
                }

                var thresholdTypeFlagValues = new Dictionary <ThresholdTypeFlags, double>();
                if (Threshold.Contains(','))
                {
                    IEnumerable <string> thresholdValues = Threshold.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim());
                    if (thresholdValues.Count() != thresholdTypeFlagQueue.Count)
                    {
                        throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match");
                    }

                    foreach (string threshold in thresholdValues)
                    {
                        if (double.TryParse(threshold, out double value))
                        {
                            thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = value;
                        }
                        else
                        {
                            throw new Exception($"Invalid threshold value must be numeric");
                        }
                    }
                }
                else
                {
                    double thresholdValue = double.Parse(Threshold);

                    while (thresholdTypeFlagQueue.Any())
                    {
                        thresholdTypeFlagValues[thresholdTypeFlagQueue.Dequeue()] = thresholdValue;
                    }
                }

                ThresholdStatistic thresholdStat = ThresholdStatistic.Minimum;
                if (ThresholdStat.Equals("average", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Average;
                }
                else if (ThresholdStat.Equals("total", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Total;
                }

                var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
                var summary       = new CoverageSummary();

                CoverageDetails linePercentCalculation   = summary.CalculateLineCoverage(result.Modules);
                CoverageDetails branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
                CoverageDetails methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);

                double totalLinePercent   = linePercentCalculation.Percent;
                double totalBranchPercent = branchPercentCalculation.Percent;
                double totalMethodPercent = methodPercentCalculation.Percent;

                double averageLinePercent   = linePercentCalculation.AverageModulePercent;
                double averageBranchPercent = branchPercentCalculation.AverageModulePercent;
                double averageMethodPercent = methodPercentCalculation.AverageModulePercent;

                foreach (KeyValuePair <string, Documents> module in result.Modules)
                {
                    double linePercent   = summary.CalculateLineCoverage(module.Value).Percent;
                    double branchPercent = summary.CalculateBranchCoverage(module.Value).Percent;
                    double methodPercent = summary.CalculateMethodCoverage(module.Value).Percent;

                    coverageTable.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{InvariantFormat(linePercent)}%", $"{InvariantFormat(branchPercent)}%", $"{InvariantFormat(methodPercent)}%");
                }

                Console.WriteLine();
                Console.WriteLine(coverageTable.ToStringAlternative());

                coverageTable.Columns.Clear();
                coverageTable.Rows.Clear();

                coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
                coverageTable.AddRow("Total", $"{InvariantFormat(totalLinePercent)}%", $"{InvariantFormat(totalBranchPercent)}%", $"{InvariantFormat(totalMethodPercent)}%");
                coverageTable.AddRow("Average", $"{InvariantFormat(averageLinePercent)}%", $"{InvariantFormat(averageBranchPercent)}%", $"{InvariantFormat(averageMethodPercent)}%");

                Console.WriteLine(coverageTable.ToStringAlternative());

                ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat);
                if (thresholdTypeFlags != ThresholdTypeFlags.None)
                {
                    var exceptionMessageBuilder = new StringBuilder();
                    if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine(
                            $"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine(
                            $"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine(
                            $"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}");
                    }

                    throw new Exception(exceptionMessageBuilder.ToString());
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }
コード例 #6
0
        public override bool Execute()
        {
            try
            {
                Console.WriteLine("\nCalculating coverage result...");

                IFileSystem fileSystem = ServiceProvider.GetService <IFileSystem>();
                if (InstrumenterState is null || !fileSystem.Exists(InstrumenterState.ItemSpec))
                {
                    _logger.LogError("Result of instrumentation task not found");
                    return(false);
                }

                Coverage coverage = null;
                using (Stream instrumenterStateStream = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open))
                {
                    var instrumentationHelper = ServiceProvider.GetService <IInstrumentationHelper>();
                    // Task.Log is teared down after a task and thus the new MSBuildLogger must be passed to the InstrumentationHelper
                    // https://github.com/microsoft/msbuild/issues/5153
                    instrumentationHelper.SetLogger(_logger);
                    coverage = new Coverage(CoveragePrepareResult.Deserialize(instrumenterStateStream), this._logger, ServiceProvider.GetService <IInstrumentationHelper>(), fileSystem, ServiceProvider.GetService <ISourceRootTranslator>());
                }

                try
                {
                    fileSystem.Delete(InstrumenterState.ItemSpec);
                }
                catch (Exception ex)
                {
                    // We don't want to block coverage for I/O errors
                    _logger.LogWarning($"Exception during instrument state deletion, file name '{InstrumenterState.ItemSpec}' exception message '{ex.Message}'");
                }

                CoverageResult result = coverage.GetCoverageResult();

                var directory = Path.GetDirectoryName(_output);
                if (directory == string.Empty)
                {
                    directory = Directory.GetCurrentDirectory();
                }
                else if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                var formats = _format.Split(',');
                foreach (var format in formats)
                {
                    var reporter = new ReporterFactory(format).CreateReporter();
                    if (reporter == null)
                    {
                        throw new Exception($"Specified output format '{format}' is not supported");
                    }

                    if (reporter.OutputType == ReporterOutputType.Console)
                    {
                        // Output to console
                        Console.WriteLine("  Outputting results to console");
                        Console.WriteLine(reporter.Report(result));
                    }
                    else
                    {
                        ReportWriter writer = new ReportWriter(_coverletMultiTargetFrameworksCurrentTFM,
                                                               directory,
                                                               _output,
                                                               reporter,
                                                               fileSystem,
                                                               ServiceProvider.GetService <IConsole>(),
                                                               result);
                        writer.WriteReport();
                    }
                }

                var thresholdTypeFlags = ThresholdTypeFlags.None;
                var thresholdStat      = ThresholdStatistic.Minimum;

                foreach (var thresholdType in _thresholdType.Split(',').Select(t => t.Trim()))
                {
                    if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Line;
                    }
                    else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Branch;
                    }
                    else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Method;
                    }
                }

                if (_thresholdStat.Equals("average", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Average;
                }
                else if (_thresholdStat.Equals("total", StringComparison.OrdinalIgnoreCase))
                {
                    thresholdStat = ThresholdStatistic.Total;
                }

                var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
                var summary       = new CoverageSummary();
                int numModules    = result.Modules.Count;

                var linePercentCalculation   = summary.CalculateLineCoverage(result.Modules);
                var branchPercentCalculation = summary.CalculateBranchCoverage(result.Modules);
                var methodPercentCalculation = summary.CalculateMethodCoverage(result.Modules);

                var totalLinePercent   = linePercentCalculation.Percent;
                var totalBranchPercent = branchPercentCalculation.Percent;
                var totalMethodPercent = methodPercentCalculation.Percent;

                var averageLinePercent   = linePercentCalculation.AverageModulePercent;
                var averageBranchPercent = branchPercentCalculation.AverageModulePercent;
                var averageMethodPercent = methodPercentCalculation.AverageModulePercent;

                foreach (var module in result.Modules)
                {
                    var linePercent   = summary.CalculateLineCoverage(module.Value).Percent;
                    var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent;
                    var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent;

                    coverageTable.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%");
                }

                Console.WriteLine();
                Console.WriteLine(coverageTable.ToStringAlternative());

                coverageTable.Columns.Clear();
                coverageTable.Rows.Clear();

                coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
                coverageTable.AddRow("Total", $"{totalLinePercent}%", $"{totalBranchPercent}%", $"{totalMethodPercent}%");
                coverageTable.AddRow("Average", $"{averageLinePercent}%", $"{averageBranchPercent}%", $"{averageMethodPercent}%");

                Console.WriteLine(coverageTable.ToStringAlternative());

                thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, _threshold, thresholdTypeFlags, thresholdStat);
                if (thresholdTypeFlags != ThresholdTypeFlags.None)
                {
                    var exceptionMessageBuilder = new StringBuilder();
                    if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {_threshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {_threshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {_threshold}");
                    }

                    throw new Exception(exceptionMessageBuilder.ToString());
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }