public void OnAllMutantsTested(IReadOnlyProjectComponent inputComponent) { foreach (var reporter in Reporters) { reporter.OnAllMutantsTested(inputComponent); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var files = reportComponent.GetAllFiles(); if (files.Any()) { // print empty line for readability _consoleWriter.WriteLine(); _consoleWriter.WriteLine(); _consoleWriter.WriteLine("All mutants have been tested, and your mutation score has been calculated"); var filePathLength = Math.Max(9, files.Max(f => f.RelativePath?.Length ?? 0) + 1); string dashes = new string('─', filePathLength); _consoleWriter.WriteLine($"┌─{dashes}┬──────────┬──────────┬───────────┬────────────┬──────────┬─────────┐"); _consoleWriter.WriteLine($"│ File{new string(' ', filePathLength - 4)}│ % score │ # killed │ # timeout │ # survived │ # no cov │ # error │"); _consoleWriter.WriteLine($"├─{dashes}┼──────────┼──────────┼───────────┼────────────┼──────────┼─────────┤"); DisplayComponent(reportComponent, filePathLength); foreach (var file in files) { DisplayComponent(file, filePathLength); } _consoleWriter.WriteLine($"└─{dashes}┴──────────┴──────────┴───────────┴────────────┴──────────┴─────────┘"); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var files = reportComponent.GetAllFiles(); if (files.Any()) { // print empty line for readability _console.WriteLine(); _console.WriteLine(); _console.WriteLine("All mutants have been tested, and your mutation score has been calculated"); var table = new Table() .RoundedBorder() .AddColumn("File", c => c.NoWrap()) .AddColumn("% score", c => c.Alignment(Justify.Right).NoWrap()) .AddColumn("# killed", c => c.Alignment(Justify.Right).NoWrap()) .AddColumn("# timeout", c => c.Alignment(Justify.Right).NoWrap()) .AddColumn("# survived", c => c.Alignment(Justify.Right).NoWrap()) .AddColumn("# no cov", c => c.Alignment(Justify.Right).NoWrap()) .AddColumn("# error", c => c.Alignment(Justify.Right).NoWrap()); DisplayComponent(reportComponent, table); foreach (var file in files) { DisplayComponent(file, table); } _console.Write(table); } }
private void DisplayComponent(IReadOnlyProjectComponent inputComponent) { var mutationScore = inputComponent.GetMutationScore(); // Convert the threshold integer values to decimal values _consoleWriter.Write($" [{ inputComponent.DetectedMutants().Count()}/{ inputComponent.TotalMutants().Count()} "); if (inputComponent.IsComponentExcluded(_options.Mutate)) { _consoleWriter.Write(Output.Bright.Black("(Excluded)")); } else if (double.IsNaN(mutationScore)) { _consoleWriter.Write(Output.Bright.Black("(N/A)")); } else { // print the score as a percentage string scoreText = string.Format("({0:P2})", mutationScore); if (inputComponent.CheckHealth(_options.Thresholds) is Health.Good) { _consoleWriter.Write(Output.Green(scoreText)); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Warning) { _consoleWriter.Write(Output.Yellow(scoreText)); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Danger) { _consoleWriter.Write(Output.Red(scoreText)); } } _consoleWriter.WriteLine("]"); }
private static List <bool> ParentContinuationLines(IReadOnlyProjectComponent current) { var continuationLines = new List <bool>(); var node = current; if (node.Parent != null) { var isRootFile = node.RelativePath == node.RelativePathToProjectFile; if (isRootFile) { continuationLines.Add(true); } else { while (node.Parent != null) { continuationLines.Add(node.Parent.Children.Last().ToReadOnlyInputComponent() == node); node = node.Parent.ToReadOnlyInputComponent(); } continuationLines.Reverse(); } } return(continuationLines); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { if (reportComponent.Mutants.Any()) { _progressBarReporter.ReportFinalState(); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var filename = _options.ReportFileName + ".html"; var reportPath = Path.Combine(_options.OutputPath, "reports", filename); reportPath = FilePathUtils.NormalizePathSeparators(reportPath); WriteHtmlReport(reportPath, mutationReport.ToJsonHtmlSafe()); // to make path clickable it should always start with: file:/// var reportUri = reportPath.Replace("\\", "/"); reportUri = reportUri.StartsWith("/") ? reportUri : "/" + reportUri; reportUri = "file://" + reportUri; if (_options.ReportTypeToOpen == Options.Inputs.ReportType.Html) { _processWrapper.Open(reportUri); } else { _consoleWriter.Write(Output.Cyan("Hint: by passing \"--open-report or -o\" the report will open automatically once Stryker is done.")); } _consoleWriter.WriteLine(Output.Green($"\nYour html report has been generated at:\n" + $"{reportUri}\n" + $"You can open it in your browser of choice.")); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var reportUri = _dashboardClient.PublishReport(mutationReport, _options.ProjectVersion).Result; if (reportUri != null) { if (_options.ReportTypeToOpen == Options.Inputs.ReportType.Dashboard) { _processWrapper.Open(reportUri); } else { _consoleWriter.Write(Output.Cyan("Hint: by passing \"--open-report:dashboard or -o:dashboard\" the report will open automatically once Stryker is done.")); } _logger.LogDebug("Your stryker report has been uploaded to: \n {0} \nYou can open it in your browser of choice.", reportUri); _consoleWriter.Write(Output.Green($"Your stryker report has been uploaded to: \n {reportUri} \nYou can open it in your browser of choice.")); } else { _logger.LogError("Uploading to stryker dashboard failed..."); } _consoleWriter.WriteLine(); _consoleWriter.WriteLine(); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { Tree root = null; var stack = new Stack <IHasTreeNodes>(); // setup display handlers reportComponent.DisplayFolder = (IReadOnlyProjectComponent current) => { var name = Path.GetFileName(current.RelativePath); if (root is null) { root = new Tree("All files" + DisplayComponent(current)); stack.Push(root); } else if (!string.IsNullOrWhiteSpace(name)) { stack.Push(stack.Peek().AddNode(name + DisplayComponent(current))); } }; reportComponent.DisplayFile = (IReadOnlyProjectComponent current) => { var name = Path.GetFileName(current.RelativePath); var fileNode = stack.Peek().AddNode(name + DisplayComponent(current)); if (current.FullPath == current.Parent.Children.Last().FullPath) { stack.Pop(); } var totalMutants = current.TotalMutants(); foreach (var mutant in totalMutants) { var status = mutant.ResultStatus switch { MutantStatus.Killed or MutantStatus.Timeout => $"[Green][[{mutant.ResultStatus}]][/]", MutantStatus.NoCoverage => $"[Yellow][[{mutant.ResultStatus}]][/]", _ => $"[Red][[{mutant.ResultStatus}]][/]", }; var mutantNode = fileNode.AddNode(status + $" {mutant.Mutation.DisplayName} on line {mutant.Line}"); mutantNode.AddNode(Markup.Escape($"[-] {mutant.Mutation.OriginalNode}")); mutantNode.AddNode(Markup.Escape($"[+] {mutant.Mutation.ReplacementNode}")); } }; // print empty line for readability _console.WriteLine(); _console.WriteLine(); _console.WriteLine("All mutants have been tested, and your mutation score has been calculated"); // start recursive invocation of handlers reportComponent.Display(); _console.Write(root); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var projectVersion = _gitInfoProvider.GetCurrentBranchName(); var baselineVersion = $"dashboard-compare/{projectVersion}"; _baselineProvider.Save(mutationReport, baselineVersion).Wait(); }
private JsonReport(StrykerOptions options, IReadOnlyProjectComponent mutationReport) { _options = options; Thresholds.Add("high", _options.Thresholds.High); Thresholds.Add("low", _options.Thresholds.Low); Merge(Files, GenerateReportComponents(mutationReport)); }
public void OnAllMutantsTested(IReadOnlyProjectComponent inputComponent) { // make sure all other console caches are flushed before writing final reports Thread.Sleep(TimeSpan.FromSeconds(1)); foreach (var reporter in Reporters) { reporter.OnAllMutantsTested(inputComponent); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent mutationTree) { var mutationReport = JsonReport.Build(_options, mutationTree); var reportPath = Path.Combine(_options.OutputPath, "reports", "mutation-report.json"); WriteReportToJsonFile(reportPath, mutationReport.ToJson()); _consoleWriter.Write(Output.Green($"\nYour json report has been generated at: \n " + $"{reportPath} \n")); }
public static JsonReport Build(IStrykerOptions options, IReadOnlyProjectComponent mutationReport) { // This should really only happen in unit tests. // We need this construct because in a unit test // we want to be able to generate different reports with different settings _report = _options == options ? _report : null; // If the report was already generated, return the existing report _report ??= new JsonReport(options, mutationReport); return(_report); }
public void OnMutantsCreated(IReadOnlyProjectComponent inputComponent) { foreach (var reporter in Reporters) { reporter.OnMutantsCreated(inputComponent); } // todo: refactor to lifecycle event if ((inputComponent.Mutants ?? Enumerable.Empty <IReadOnlyMutant>()).Any()) { new FilteredMutantsLogger().OnMutantsCreated(inputComponent); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent mutationTree) { var mutationReport = JsonReport.Build(_options, mutationTree); var reportPath = Path.Combine(_options.OutputPath, "reports", "mutation-report.html"); WriteHtmlReport(reportPath, mutationReport.ToJsonHtmlSafe()); _consoleWriter.Write(Output.Green($"\nYour html report has been generated at: \n " + $"{reportPath} \n" + $"You can open it in your browser of choice. \n")); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var filename = _options.ReportFileName + ".json"; var reportPath = Path.Combine(_options.OutputPath, "reports", filename); WriteReportToJsonFile(reportPath, mutationReport); var clickablePath = reportPath.Replace("\\", "/"); clickablePath = clickablePath.StartsWith("/") ? clickablePath : $"/{clickablePath}"; _consoleWriter.Write(Output.Green($"\nYour json report has been generated at: \n file://{clickablePath} \n")); }
public void OnAllMutantsTested(IReadOnlyProjectComponent mutationTree) { var mutationReport = JsonReport.Build(_options, mutationTree); var reportPath = Path.Combine(_options.OutputPath, "reports", "mutation-report.html"); WriteHtmlReport(reportPath, mutationReport.ToJsonHtmlSafe()); var clickablePath = reportPath.Replace("\\", "/"); clickablePath = clickablePath.StartsWith("/") ? clickablePath : $"/{clickablePath}"; _consoleWriter.Write(Output.Green($"\nYour html report has been generated at: \n " + $"file://{clickablePath} \n" + $"You can open it in your browser of choice. \n")); }
private void DisplayComponent(IReadOnlyProjectComponent inputComponent, Table table) { var columns = new List <IRenderable> { new Text(inputComponent.RelativePath ?? "All files") }; var mutationScore = inputComponent.GetMutationScore(); if (inputComponent.IsComponentExcluded(_options.Mutate)) { columns.Add(new Markup("[Gray]Excluded[/]")); } else if (double.IsNaN(mutationScore)) { columns.Add(new Markup("[Gray]N/A[/]")); } else { var scoreText = $"{mutationScore * 100:N2}"; var checkHealth = inputComponent.CheckHealth(_options.Thresholds); if (checkHealth is Health.Good) { columns.Add(new Markup($"[Green]{scoreText}[/]")); } else if (checkHealth is Health.Warning) { columns.Add(new Markup($"[Yellow]{scoreText}[/]")); } else if (checkHealth is Health.Danger) { columns.Add(new Markup($"[Red]{scoreText}[/]")); } } var mutants = inputComponent.Mutants.ToList(); columns.Add(new Text(mutants.Count(m => m.ResultStatus == MutantStatus.Killed).ToString())); columns.Add(new Text(mutants.Count(m => m.ResultStatus == MutantStatus.Timeout).ToString())); columns.Add(new Text((inputComponent.TotalMutants().Count() - inputComponent.DetectedMutants().Count()).ToString())); columns.Add(new Text(mutants.Count(m => m.ResultStatus == MutantStatus.NoCoverage).ToString())); columns.Add(new Text(mutants.Count(m => m.ResultStatus == MutantStatus.CompileError).ToString())); table.AddRow(columns); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var reportUrl = _dashboardClient.PublishReport(mutationReport.ToJson(), _options.ProjectVersion).Result; if (reportUrl != null) { _logger.LogDebug("Your stryker report has been uploaded to: \n {0} \nYou can open it in your browser of choice.", reportUrl); _consoleWriter.Write(Output.Green($"Your stryker report has been uploaded to: \n {reportUrl} \nYou can open it in your browser of choice.")); } else { _logger.LogError("Uploading to stryker dashboard failed..."); } _consoleWriter.WriteLine(); _consoleWriter.WriteLine(); }
private void DisplayComponent(IReadOnlyProjectComponent inputComponent, int filePathLength) { _consoleWriter.Write($"│ {(inputComponent.RelativePath ?? "All files").PadRight(filePathLength)}│ "); var mutationScore = inputComponent.GetMutationScore(); if (inputComponent.IsComponentExcluded(_options.Mutate)) { _consoleWriter.Write(Output.Bright.Black("Excluded")); } else if (double.IsNaN(mutationScore)) { _consoleWriter.Write(Output.Bright.Black(" N/A")); } else { var scoreText = $"{mutationScore * 100:N2}".PadLeft(8); var checkHealth = inputComponent.CheckHealth(_options.Thresholds); if (checkHealth is Health.Good) { _consoleWriter.Write(Output.Green(scoreText)); } else if (checkHealth is Health.Warning) { _consoleWriter.Write(Output.Yellow(scoreText)); } else if (checkHealth is Health.Danger) { _consoleWriter.Write(Output.Red(scoreText)); } } var mutants = inputComponent.Mutants.ToList(); _consoleWriter.Write($" │ {mutants.Count(m => m.ResultStatus == MutantStatus.Killed),8}"); _consoleWriter.Write($" │ {mutants.Count(m => m.ResultStatus == MutantStatus.Timeout),9}"); _consoleWriter.Write($" │ {inputComponent.TotalMutants().Count() - inputComponent.DetectedMutants().Count(),10}"); _consoleWriter.Write($" │ {mutants.Count(m => m.ResultStatus == MutantStatus.NoCoverage),8}"); _consoleWriter.Write($" │ {mutants.Count(m => m.ResultStatus == MutantStatus.CompileError),7}"); _consoleWriter.WriteLine($" │"); }
private static List <bool> ParentContinuationLines(IReadOnlyProjectComponent current) { var continuationLines = new List <bool>(); var node = current; if (node.Parent != null) { while (node.Parent != null) { continuationLines.Add(node.Parent.Children.Last().FullPath != node.FullPath); node = node.Parent; } continuationLines.Reverse(); } return(continuationLines); }
/// <summary> /// Checks with the given <see cref="ProjectComponent" />s whether all parts of the component are excluded. /// </summary> /// <param name="projectComponent">The file to check.</param> /// <param name="filePatterns">The file patters to check with.</param> /// <returns>If any parts of the file are included <c>false</c>; otherwise <c>true</c>.</returns> public static bool IsComponentExcluded(this IReadOnlyProjectComponent projectComponent, IEnumerable <FilePattern> filePatterns) { var includePattern = filePatterns.Where(x => !x.IsExclude).ToList(); var excludePattern = filePatterns.Where(x => x.IsExclude).ToList(); // Get in- and excluded spans var includedSpans = includePattern.Where(MatchesFilePattern).SelectMany(x => x.TextSpans).Reduce(); var excludedSpans = excludePattern.Where(MatchesFilePattern).SelectMany(x => x.TextSpans).Reduce(); // If there are only included spans, the file is not excluded. if (includedSpans.Any() && !excludedSpans.Any()) { return(false); } return(!includedSpans.RemoveOverlap(excludedSpans).Any()); bool MatchesFilePattern(FilePattern pattern) => pattern.Glob.IsMatch(projectComponent.FullPath) || pattern.Glob.IsMatch(projectComponent.RelativePathToProjectFile); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var reportUri = _dashboardClient.PublishReport(mutationReport, _options.ProjectVersion).Result; if (reportUri != null) { if (_options.ReportTypeToOpen == Options.Inputs.ReportType.Dashboard) { _processWrapper.Open(reportUri); } else { _console.Write("[Cyan]Hint: by passing \"--open-report:dashboard or -o:dashboard\" the report will open automatically once Stryker is done.[/]"); } _console.WriteLine(); _console.MarkupLine("[Green]Your report has been uploaded at:[/]"); if (_console.Profile.Capabilities.Links) { // We must print the report path as the link text because on some terminals links might be supported but not actually clickable: https://github.com/spectreconsole/spectre.console/issues/764 _console.MarkupLine($"[Green][link={reportUri}]{reportUri}[/][/]"); } else { _console.MarkupLine($"[Green]{reportUri}[/]"); } _console.MarkupLine("[Green]You can open it in your browser of choice.[/]"); } else { _logger.LogError("Uploading to stryker dashboard failed..."); } _console.WriteLine(); _console.WriteLine(); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var filename = _options.ReportFileName + ".json"; var reportPath = Path.Combine(_options.OutputPath, "reports", filename); var reportUri = "file://" + reportPath.Replace("\\", "/"); WriteReportToJsonFile(reportPath, mutationReport); _console.WriteLine(); _console.MarkupLine("[Green]Your json report has been generated at:[/]"); if (_console.Profile.Capabilities.Links) { // We must print the report path as the link text because on some terminals links might be supported but not actually clickable: https://github.com/spectreconsole/spectre.console/issues/764 _console.MarkupLine($"[Green][link={reportUri}]{reportPath}[/][/]"); } else { _console.MarkupLine($"[Green]{reportUri}[/]"); } }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var files = new List <ReadOnlyFileLeaf>(); ReadOnlyFolderComposite rootFolder = null; reportComponent.DisplayFolder = (IReadOnlyProjectComponent current) => { rootFolder ??= (ReadOnlyFolderComposite)current; }; reportComponent.DisplayFile = (IReadOnlyProjectComponent current) => { files.Add((ReadOnlyFileLeaf)current); }; // print empty line for readability _consoleWriter.WriteLine(); _consoleWriter.WriteLine(); _consoleWriter.WriteLine("All mutants have been tested, and your mutation score has been calculated"); // start recursive invocation of handlers reportComponent.Display(); var filePathLength = Math.Max(9, files.Max(f => f.RelativePath?.Length ?? 0) + 1); _consoleWriter.WriteLine($"┌─{new string('─', filePathLength)}┬──────────┬──────────┬───────────┬────────────┬──────────┬─────────┐"); _consoleWriter.WriteLine($"│ File{new string(' ', filePathLength - 4)}│ % score │ # killed │ # timeout │ # survived │ # no cov │ # error │"); _consoleWriter.WriteLine($"├─{new string('─', filePathLength)}┼──────────┼──────────┼───────────┼────────────┼──────────┼─────────┤"); DisplayComponent(rootFolder, filePathLength); foreach (var file in files) { DisplayComponent(file, filePathLength); } _consoleWriter.WriteLine($"└─{new string('─', filePathLength)}┴──────────┴──────────┴───────────┴────────────┴──────────┴─────────┘"); }
private string DisplayComponent(IReadOnlyProjectComponent inputComponent) { var mutationScore = inputComponent.GetMutationScore(); var stringBuilder = new StringBuilder(); // Convert the threshold integer values to decimal values stringBuilder.Append($" [[{ inputComponent.DetectedMutants().Count()}/{ inputComponent.TotalMutants().Count()} "); if (inputComponent.IsComponentExcluded(_options.Mutate)) { stringBuilder.Append("[Gray](Excluded)[/]"); } else if (double.IsNaN(mutationScore)) { stringBuilder.Append("[Gray](N/A)[/]"); } else { // print the score as a percentage var scoreText = string.Format("({0:P2})", mutationScore); if (inputComponent.CheckHealth(_options.Thresholds) is Health.Good) { stringBuilder.Append($"[Green]{scoreText}[/]"); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Warning) { stringBuilder.Append($"[Yellow]{scoreText}[/]"); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Danger) { stringBuilder.Append($"[Red]{scoreText}[/]"); } } stringBuilder.Append("]]"); return(stringBuilder.ToString()); }
public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); var filename = _options.ReportFileName + ".html"; var reportPath = Path.Combine(_options.OutputPath, "reports", filename); reportPath = FilePathUtils.NormalizePathSeparators(reportPath); WriteHtmlReport(reportPath, mutationReport.ToJsonHtmlSafe()); var reportUri = "file://" + reportPath.Replace("\\", "/"); if (_options.ReportTypeToOpen == Options.Inputs.ReportType.Html) { _processWrapper.Open(reportUri); } else { _console.MarkupLine("[Cyan]Hint: by passing \"--open-report or -o\" the report will open automatically once Stryker is done.[/]"); } _console.WriteLine(); _console.MarkupLine("[Green]Your html report has been generated at:[/]"); if (_console.Profile.Capabilities.Links) { // We must print the report path as the link text because on some terminals links might be supported but not actually clickable: https://github.com/spectreconsole/spectre.console/issues/764 _console.MarkupLine($"[Green][link={reportUri}]{reportPath}[/][/]"); } else { _console.MarkupLine($"[Green]{reportUri}[/]"); } _console.MarkupLine("[Green]You can open it in your browser of choice.[/]"); }
public void OnMutantsCreated(IReadOnlyProjectComponent reportComponent) { var skippedMutants = reportComponent.Mutants.Where(m => m.ResultStatus != MutantStatus.NotRun); var skippedMutantGroups = skippedMutants.GroupBy(x => new { x.ResultStatus, x.ResultStatusReason }).OrderBy(x => x.Key.ResultStatusReason); foreach (var skippedMutantGroup in skippedMutantGroups) { _logger.LogInformation( FormatStatusReasonLogString(skippedMutantGroup.Count(), skippedMutantGroup.Key.ResultStatus), skippedMutantGroup.Count(), skippedMutantGroup.Key.ResultStatus, skippedMutantGroup.Key.ResultStatusReason); } if (skippedMutants.Any()) { _logger.LogInformation( LeftPadAndFormatForMutantCount(skippedMutants.Count(), "total mutants are skipped for the above mentioned reasons"), skippedMutants.Count()); } var notRunMutantsWithResultStatusReason = reportComponent.Mutants .Where(m => m.ResultStatus == MutantStatus.NotRun && !string.IsNullOrEmpty(m.ResultStatusReason)) .GroupBy(x => x.ResultStatusReason); foreach (var notRunMutantReason in notRunMutantsWithResultStatusReason) { _logger.LogInformation( LeftPadAndFormatForMutantCount(notRunMutantReason.Count(), "mutants will be tested because: {1}"), notRunMutantReason.Count(), notRunMutantReason.Key); } var notRunCount = reportComponent.Mutants.Count(m => m.ResultStatus == MutantStatus.NotRun); _logger.LogInformation(LeftPadAndFormatForMutantCount(notRunCount, "total mutants will be tested"), notRunCount); }
private IDictionary <string, JsonReportFileComponent> GenerateReportComponents(IReadOnlyProjectComponent component) { var files = new Dictionary <string, JsonReportFileComponent>(); if (component is ReadOnlyFolderComposite folder) { Merge(files, GenerateFolderReportComponents(folder)); } else if (component is ReadOnlyFileLeaf file) { Merge(files, GenerateFileReportComponents(file)); } return(files); }