From e2bfdc81aafb2464bb4f7188e842c4f02a854b01 Mon Sep 17 00:00:00 2001 From: Avishai Dotan <108017307+AvishaiDotan@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:47:21 +0200 Subject: [PATCH 1/5] continue with implementing configs --- samples/BenchmarkDotNet.Samples/IntroRPlot.cs | 39 +++++++++++++++++ samples/BenchmarkDotNet.Samples/Program.cs | 3 +- .../Exporters/RPlotExporterAttribute.cs | 11 ++++- .../Configs/ConfigExtensions.cs | 4 ++ src/BenchmarkDotNet/Configs/DefaultConfig.cs | 2 +- .../Configs/ImmutableConfig.cs | 5 +++ .../Configs/ImmutableConfigBuilder.cs | 42 +++++++++++++++++- src/BenchmarkDotNet/Configs/ManualConfig.cs | 23 +++++++++- .../Exporters/IIntegratedExports.cs | 11 +++++ .../Exporters/IntegratedExport.cs | 36 ++++++++++++++++ .../Exporters/RPlotExporter.cs | 10 ++++- .../Running/BenchmarkConverter.cs | 43 ++++++++++++++++++- .../Running/BenchmarkRunnerClean.cs | 1 + 13 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 samples/BenchmarkDotNet.Samples/IntroRPlot.cs create mode 100644 src/BenchmarkDotNet/Exporters/IIntegratedExports.cs create mode 100644 src/BenchmarkDotNet/Exporters/IntegratedExport.cs diff --git a/samples/BenchmarkDotNet.Samples/IntroRPlot.cs b/samples/BenchmarkDotNet.Samples/IntroRPlot.cs new file mode 100644 index 0000000000..e72bde95ce --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroRPlot.cs @@ -0,0 +1,39 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Exporters; + + +namespace BenchmarkDotNet.Samples +{ + //[MemoryDiagnoser] + //[Config(typeof(Config))] + [RPlotExporter(IntegratedExportEnum.HtmlExporterWithRPlotExporter)] + public class IntroRPlot + { + [Benchmark] + public void Benchmark() + { + var result = Calculate(); + } + + private int Calculate() + { + int sum = 0; + for (int i = 0; i < 1000; i++) + { + sum += i; + } + return sum; + } + } + + public class Config : ManualConfig + { + public Config() + { + //AddExporter(CsvMeasurementsExporter.Default); + //AddExporter(RPlotExporter.Default); + } + } + +} \ No newline at end of file diff --git a/samples/BenchmarkDotNet.Samples/Program.cs b/samples/BenchmarkDotNet.Samples/Program.cs index 71daa668ba..230d26f2f2 100644 --- a/samples/BenchmarkDotNet.Samples/Program.cs +++ b/samples/BenchmarkDotNet.Samples/Program.cs @@ -4,6 +4,7 @@ namespace BenchmarkDotNet.Samples { public class Program { - public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + //public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + public static void Main(string[] args) => BenchmarkRunner.Run(); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs b/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs index 4b4f6d9c39..e9bb97efde 100644 --- a/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs @@ -1,10 +1,19 @@ using BenchmarkDotNet.Exporters; +using System; +using System.Collections.Generic; namespace BenchmarkDotNet.Attributes { public class RPlotExporterAttribute : ExporterConfigBaseAttribute { - public RPlotExporterAttribute() : base(DefaultExporters.RPlot) + /// + /// Determines whether Rplot exported images should be added to the HTML report (a.k.a HTML report). + /// False by default (rplotImages will not be displayed). + /// + protected RPlotExporterAttribute() + {} + + public RPlotExporterAttribute(params IntegratedExportEnum[] integratedExportEnums) : base(new RPlotExporter(integratedExportEnums)) { } } diff --git a/src/BenchmarkDotNet/Configs/ConfigExtensions.cs b/src/BenchmarkDotNet/Configs/ConfigExtensions.cs index 5177227c2a..3d6730f0ac 100644 --- a/src/BenchmarkDotNet/Configs/ConfigExtensions.cs +++ b/src/BenchmarkDotNet/Configs/ConfigExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Globalization; @@ -76,6 +77,9 @@ public static class ConfigExtensions [PublicAPI] public static ManualConfig WithUnionRule(this IConfig config, ConfigUnionRule unionRule) => config.With(m => m.WithUnionRule(unionRule)); [PublicAPI] public static ManualConfig WithCultureInfo(this IConfig config, CultureInfo cultureInfo) => config.With(m => m.CultureInfo = cultureInfo); + [PublicAPI] public static ManualConfig WithSetExporters(this IConfig config, List exporters) => config.With(m => m.WithSetExporters(exporters)); + [PublicAPI] public static ManualConfig WithSetIntegratedExporters(this IConfig config, List exporters) => config.With(m => m.WithSetIntegratedExporters(exporters)); + /// /// determines if all auto-generated files should be kept or removed after running the benchmarks /// diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index b70333cc1d..e1cf0e3b8a 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -77,7 +77,7 @@ public IEnumerable GetValidators() public IOrderer Orderer => null; public ICategoryDiscoverer? CategoryDiscoverer => null; - public ConfigUnionRule UnionRule => ConfigUnionRule.Union; + public ConfigUnionRule UnionRule { get; set; } = ConfigUnionRule.Union; public CultureInfo CultureInfo => null; diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index b6e03126fd..06ff27ee8e 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -24,6 +24,7 @@ public sealed class ImmutableConfig : IConfig // if something is an array here instead of hashset it means it must have a guaranteed order of elements private readonly ImmutableArray columnProviders; private readonly ImmutableArray exporters; + private readonly ImmutableArray integratedExports; private readonly ImmutableHashSet loggers; private readonly ImmutableHashSet diagnosers; private readonly ImmutableHashSet analysers; @@ -41,6 +42,7 @@ internal ImmutableConfig( ImmutableHashSet uniqueHardwareCounters, ImmutableHashSet uniqueDiagnosers, ImmutableArray uniqueExporters, + ImmutableArray uniqueIntegratedExports, ImmutableHashSet uniqueAnalyzers, ImmutableHashSet uniqueValidators, ImmutableHashSet uniqueFilters, @@ -63,6 +65,7 @@ internal ImmutableConfig( hardwareCounters = uniqueHardwareCounters; diagnosers = uniqueDiagnosers; exporters = uniqueExporters; + integratedExports = uniqueIntegratedExports; analysers = uniqueAnalyzers; validators = uniqueValidators; filters = uniqueFilters; @@ -92,6 +95,8 @@ internal ImmutableConfig( public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + + public IEnumerable GetIntegratedExports() => integratedExports; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index d7c0b5eb0f..cd7cb6611a 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -43,7 +43,10 @@ public static ImmutableConfig Create(IConfig source) var uniqueHardwareCounters = source.GetHardwareCounters().Where(counter => counter != HardwareCounter.NotSet).ToImmutableHashSet(); var uniqueDiagnosers = GetDiagnosers(source.GetDiagnosers(), uniqueHardwareCounters); - var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); + var allExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); + + // Check for integrated exporters + var (uniqueExporters, integratedExports) = ProcessIntegratedConfiguration(allExporters); var uniqueAnalyzers = GetAnalysers(source.GetAnalysers(), uniqueDiagnosers); var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options); @@ -61,6 +64,7 @@ public static ImmutableConfig Create(IConfig source) uniqueHardwareCounters, uniqueDiagnosers, uniqueExporters, + integratedExports, uniqueAnalyzers, uniqueValidators, uniqueFilters, @@ -254,5 +258,41 @@ private class TypeComparer : IEqualityComparer public int GetHashCode(TInterface obj) => obj.GetType().GetHashCode(); } + + private static IReadOnlyCollection ExtractIntegrationExportEnums(IEnumerable exporters) + { + return exporters + .OfType() + .Where(export => export.IntegratedExportEnums.Any()) + .SelectMany(export => export.IntegratedExportEnums) + .ToList(); + } + + private static (ImmutableArray UniqueExporters, ImmutableArray IntegratedExports) ProcessIntegratedConfiguration(ImmutableArray allExporters) + { + var integratedExportEnums = ExtractIntegrationExportEnums(allExporters); + var integratedExports = integratedExportEnums.Select(exportEnum => + { + var exporterTypesStr = IntegratedExportersMap.SplitEnumByWith(exportEnum); + return new IntegratedExport + { + ExportEnum = exportEnum, + Exporter = allExporters.FirstOrDefault(e => exporterTypesStr[0] == e.Name), + WithExporter = allExporters.FirstOrDefault(e => exporterTypesStr[1] == e.Name), + Dependencies = allExporters + .Where(exporter => exporter is IExporterDependencies) + .SelectMany(exporter => (exporter as IExporterDependencies).Dependencies) + .ToList() + }; + }).ToList(); + + var distinctExporters = allExporters + .Where(exporter => !integratedExports + .Any(integrated => integrated.Exporter?.Name == exporter.Name || + integrated.WithExporter?.Name == exporter.Name || !integrated.Dependencies.Any(d => d.Name == exporter.Name))) + .ToList(); + + return (distinctExporters.ToImmutableArray(), integratedExports.ToImmutableArray()); + } } } diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs index 5ea1be24e9..1734efb070 100644 --- a/src/BenchmarkDotNet/Configs/ManualConfig.cs +++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Globalization; using System.Linq; +using System.Reflection.Metadata; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Diagnosers; @@ -25,7 +26,6 @@ public class ManualConfig : IConfig private readonly static Conclusion[] emptyConclusion = Array.Empty(); private readonly List columnProviders = new List(); - private readonly List exporters = new List(); private readonly List loggers = new List(); private readonly List diagnosers = new List(); private readonly List analysers = new List(); @@ -37,8 +37,12 @@ public class ManualConfig : IConfig private readonly List eventProcessors = new List(); private readonly List columnHidingRules = new List(); + private List exporters = new List(); + private List integratedExporters = new List(); + public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + public IEnumerable GetIntegratedExporters() => integratedExporters; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; @@ -109,6 +113,21 @@ public ManualConfig WithBuildTimeout(TimeSpan buildTimeout) return this; } + public ManualConfig WithSetExporters(List updatedExporters) + { + exporters.Clear(); + exporters = updatedExporters; + return this; + } + + public ManualConfig WithSetIntegratedExporters(List updatedExporters) + { + integratedExporters.Clear(); + integratedExporters.AddRange(updatedExporters); + return this; + } + + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("This method will soon be removed, please start using .AddColumn() instead.")] public void Add(params IColumn[] newColumns) => AddColumn(newColumns); @@ -319,6 +338,8 @@ public static ManualConfig Union(IConfig globalConfig, IConfig localConfig) return manualConfig; } + + internal void RemoveAllJobs() => jobs.Clear(); internal void RemoveAllDiagnosers() => diagnosers.Clear(); diff --git a/src/BenchmarkDotNet/Exporters/IIntegratedExports.cs b/src/BenchmarkDotNet/Exporters/IIntegratedExports.cs new file mode 100644 index 0000000000..2f3992239a --- /dev/null +++ b/src/BenchmarkDotNet/Exporters/IIntegratedExports.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BenchmarkDotNet.Exporters +{ + internal interface IIntegratedExports + { + IEnumerable IntegratedExportEnums { get; set; } + } +} diff --git a/src/BenchmarkDotNet/Exporters/IntegratedExport.cs b/src/BenchmarkDotNet/Exporters/IntegratedExport.cs new file mode 100644 index 0000000000..40b1dce969 --- /dev/null +++ b/src/BenchmarkDotNet/Exporters/IntegratedExport.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BenchmarkDotNet.Exporters +{ + public enum IntegratedExportEnum + { + HtmlExporterWithRPlotExporter, + } + + public class IntegratedExport + { + public IntegratedExportEnum ExportEnum { get; set; } + public IExporter Exporter { get; set; } + public IExporter WithExporter { get; set; } + public List Dependencies { get; set; } + } + + public static class IntegratedExportersMap + { + private static readonly Dictionary> ExportTypesDictionary = new Dictionary> + { + { IntegratedExportEnum.HtmlExporterWithRPlotExporter, new List { nameof(RPlotExporter), nameof(HtmlExporter) } } + }; + + public static IReadOnlyDictionary> ExportTypes => ExportTypesDictionary; + + public static string[] SplitEnumByWith(IntegratedExportEnum enumValue) + { + string enumString = enumValue.ToString(); + + return enumString.Split(new string[] { "With" }, StringSplitOptions.None); + } + } +} diff --git a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs index c90338f490..e51d69bec4 100644 --- a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs +++ b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs @@ -12,20 +12,26 @@ namespace BenchmarkDotNet.Exporters { - public class RPlotExporter : IExporter, IExporterDependencies + public class RPlotExporter : IExporter, IExporterDependencies, IIntegratedExports { + public RPlotExporter(IntegratedExportEnum[] IntegratedExportEnums = null) + { + this.IntegratedExportEnums = IntegratedExportEnums; + } + public static readonly IExporter Default = new RPlotExporter(); public string Name => nameof(RPlotExporter); private const string ImageExtension = ".png"; private static readonly object BuildScriptLock = new object(); - public IEnumerable Dependencies { // R Plots depends on having the full measurements available get { yield return CsvMeasurementsExporter.Default; } } + public IEnumerable IntegratedExportEnums { get; set; } + public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) { const string scriptFileName = "BuildPlots.R"; diff --git a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs index 69837feec8..ee8d84cc02 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs @@ -7,8 +7,10 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Code; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Filters; +using BenchmarkDotNet.Order; using BenchmarkDotNet.Parameters; using BenchmarkDotNet.Reports; @@ -16,7 +18,7 @@ namespace BenchmarkDotNet.Running { public static partial class BenchmarkConverter { - private const BindingFlags AllMethodsFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + private const BindingFlags AllMethodsFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; public static BenchmarkRunInfo TypeToBenchmarks(Type type, IConfig? config = null) { @@ -93,6 +95,11 @@ private static ImmutableConfig GetFullTypeConfig(Type type, IConfig? config) foreach (var configFromAttribute in assemblyAttributes.Concat(typeAttributes)) config = ManualConfig.Union(config, configFromAttribute.Config); + // Check for integrated exporters + //IReadOnlyCollection integrationExportEnums = ExtractIntegrationExportEnums(config.GetExporters()); + //if (integrationExportEnums.Any()) + // config = ProcessIntegratedConfiguration(config, integrationExportEnums); + return ImmutableConfigBuilder.Create(config); } @@ -351,5 +358,39 @@ private static object[] GetAllValidValues(Type parameterType) return new object[] { Activator.CreateInstance(parameterType) }; } + + private static IReadOnlyCollection ExtractIntegrationExportEnums(IEnumerable exporters) + { + return exporters + .OfType() + .Where(export => export.IntegratedExportEnums.Any()) + .SelectMany(export => export.IntegratedExportEnums) + .ToList(); + } + + private static IConfig ProcessIntegratedConfiguration(IConfig config, IEnumerable integrationExportEnums) + { + var allExporters = config.GetExporters().ToList(); + var integratedExports = integrationExportEnums.Select(exportEnum => + { + var exporterTypesStr = IntegratedExportersMap.SplitEnumByWith(exportEnum); + return new IntegratedExport + { + ExportEnum = exportEnum, + Exporter = allExporters.FirstOrDefault(e => exporterTypesStr[0] == e.Name), + WithExporter = allExporters.FirstOrDefault(e => exporterTypesStr[1] == e.Name) + }; + }).ToList(); + + var distinctExporters = allExporters + .Where(exporter => !integratedExports + .Any(integrated => integrated.Exporter.Name == exporter.Name || + integrated.WithExporter.Name == exporter.Name)) + .ToList(); + + return config + .WithSetExporters(distinctExporters) + .WithSetIntegratedExporters(integratedExports); + } } } diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 6d9d48c3cc..f1311d4a34 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -292,6 +292,7 @@ private static void PrintSummary(ILogger logger, ImmutableConfig config, Summary logger.WriteLineHeader("// * Export *"); string currentDirectory = Directory.GetCurrentDirectory(); + List additionalData = new List(); foreach (string file in config.GetCompositeExporter().ExportToFiles(summary, logger)) { logger.WriteLineInfo($" {file.GetBaseName(currentDirectory)}"); From a81417edbc086a5c169a14dbb34071f512de06c1 Mon Sep 17 00:00:00 2001 From: Avishai Dotan <108017307+AvishaiDotan@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:35:33 +0200 Subject: [PATCH 2/5] integrate html with rplot --- .../Exporters/RPlotExporterAttribute.cs | 4 - .../Configs/ImmutableConfig.cs | 2 +- .../Configs/ImmutableConfigBuilder.cs | 2 +- .../Exporters/CompositeExporter.cs | 73 +++++++++-- src/BenchmarkDotNet/Exporters/ExporterBase.cs | 2 +- src/BenchmarkDotNet/Exporters/HtmlExporter.cs | 19 ++- .../Exporters/IIntegratedExports.cs | 7 +- .../Exporters/IntegratedExporterExtension.cs | 45 +++++++ .../Exporters/RPlotExporter.cs | 122 +++++++++++++++++- .../Running/BenchmarkRunnerClean.cs | 10 +- 10 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 src/BenchmarkDotNet/Exporters/IntegratedExporterExtension.cs diff --git a/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs b/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs index e9bb97efde..f48bd7c46f 100644 --- a/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/Exporters/RPlotExporterAttribute.cs @@ -6,10 +6,6 @@ namespace BenchmarkDotNet.Attributes { public class RPlotExporterAttribute : ExporterConfigBaseAttribute { - /// - /// Determines whether Rplot exported images should be added to the HTML report (a.k.a HTML report). - /// False by default (rplotImages will not be displayed). - /// protected RPlotExporterAttribute() {} diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index 06ff27ee8e..e14b076871 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -95,7 +95,6 @@ internal ImmutableConfig( public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; - public IEnumerable GetIntegratedExports() => integratedExports; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; @@ -110,6 +109,7 @@ internal ImmutableConfig( public ILogger GetCompositeLogger() => new CompositeLogger(loggers); public IExporter GetCompositeExporter() => new CompositeExporter(exporters); + public IExporter GetCompositeIntegratedExporter() => new CompositeExporter(integratedExports); public IValidator GetCompositeValidator() => new CompositeValidator(validators); public IAnalyser GetCompositeAnalyser() => new CompositeAnalyser(analysers); public IDiagnoser GetCompositeDiagnoser() => new CompositeDiagnoser(diagnosers); diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index cd7cb6611a..e3d416d529 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -263,7 +263,7 @@ private static IReadOnlyCollection ExtractIntegrationExpor { return exporters .OfType() - .Where(export => export.IntegratedExportEnums.Any()) + .Where(export => export?.IntegratedExportEnums?.Any() ?? false) .SelectMany(export => export.IntegratedExportEnums) .ToList(); } diff --git a/src/BenchmarkDotNet/Exporters/CompositeExporter.cs b/src/BenchmarkDotNet/Exporters/CompositeExporter.cs index 95d3bde9d3..7e70a05a82 100644 --- a/src/BenchmarkDotNet/Exporters/CompositeExporter.cs +++ b/src/BenchmarkDotNet/Exporters/CompositeExporter.cs @@ -11,9 +11,12 @@ namespace BenchmarkDotNet.Exporters public class CompositeExporter : IExporter { private readonly ImmutableArray exporters; + private readonly ImmutableArray integratedExports; public CompositeExporter(ImmutableArray exporters) => this.exporters = exporters; + public CompositeExporter(ImmutableArray integratedExports) => this.integratedExports = integratedExports; + public string Name => nameof(CompositeExporter); public void ExportToLog(Summary summary, ILogger logger) @@ -35,18 +38,68 @@ public void ExportToLog(Summary summary, ILogger logger) } public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) - => exporters.SelectMany(exporter => + { + if (exporters != null && exporters.Any()) { - var files = new List(); - try + return exporters.SelectMany(exporter => { - files.AddRange(exporter.ExportToFiles(summary, consoleLogger)); - } - catch (Exception e) + var files = new List(); + try + { + files.AddRange(exporter.ExportToFiles(summary, consoleLogger)); + } + catch (Exception e) + { + consoleLogger.WriteLineError(e.ToString()); + } + return files; + }); + } + else if (integratedExports != null && integratedExports.Any()) + { + return integratedExports.SelectMany(exporter => { - consoleLogger.WriteLineError(e.ToString()); - } - return files; - }); + var files = new List(); + IEnumerable dependencyFilePaths = new List(); + try + { + if (exporter.Dependencies.Any()) + { + exporter.Dependencies.ForEach(d => + { + files.AddRange(d.ExportToFiles(summary, consoleLogger)); + }); + } + if (exporter.WithExporter != null) + { + files.AddRange(exporter.WithExporter.ExportToFiles(summary, consoleLogger)); + } + if (exporter.Exporter != null && exporter.Exporter is IntegratedExporterExtension exporterBase) + { + object? payload = null; + switch (exporter.ExportEnum) + { + case IntegratedExportEnum.HtmlExporterWithRPlotExporter: + if (exporter.WithExporter is RPlotExporter rPlotExporter) + { + payload = rPlotExporter.GetExpectedPngPaths(summary, consoleLogger); + } + break; + } + files.AddRange(exporterBase.IntegratedExportToFiles(summary, consoleLogger, payload)); + } + } + catch (Exception e) + { + consoleLogger.WriteLineError(e.ToString()); + } + return files; + }); + } + else + { + return Array.Empty(); + } + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Exporters/ExporterBase.cs b/src/BenchmarkDotNet/Exporters/ExporterBase.cs index 17a8d432a6..527798b2ae 100644 --- a/src/BenchmarkDotNet/Exporters/ExporterBase.cs +++ b/src/BenchmarkDotNet/Exporters/ExporterBase.cs @@ -54,7 +54,7 @@ internal string GetArtifactFullName(Summary summary) return $"{Path.Combine(summary.ResultsDirectoryPath, fileName)}-{FileCaption}{FileNameSuffix}.{FileExtension}"; } - private static string GetFileName(Summary summary) + protected static string GetFileName(Summary summary) { // we can't use simple name here, because user might be running benchmarks for a library, which defines few types with the same name // and reports the results per type, so every summary is going to contain just single benchmark diff --git a/src/BenchmarkDotNet/Exporters/HtmlExporter.cs b/src/BenchmarkDotNet/Exporters/HtmlExporter.cs index 8fe3068e14..eb290c87a3 100644 --- a/src/BenchmarkDotNet/Exporters/HtmlExporter.cs +++ b/src/BenchmarkDotNet/Exporters/HtmlExporter.cs @@ -1,10 +1,12 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Reports; +using System; +using System.Collections.Generic; namespace BenchmarkDotNet.Exporters { - public class HtmlExporter : ExporterBase + public class HtmlExporter : IntegratedExporterExtension { private const string CssDefinition = @"