diff --git a/src/BenchmarkDotNet/Configs/DebugConfig.cs b/src/BenchmarkDotNet/Configs/DebugConfig.cs index 7c832b9cd3..06e4730dec 100644 --- a/src/BenchmarkDotNet/Configs/DebugConfig.cs +++ b/src/BenchmarkDotNet/Configs/DebugConfig.cs @@ -52,7 +52,6 @@ public override IEnumerable GetJobs() public abstract class DebugConfig : IConfig { - private readonly static Conclusion[] emptyConclusion = Array.Empty(); public abstract IEnumerable GetJobs(); public IEnumerable GetValidators() => Array.Empty(); @@ -85,7 +84,5 @@ public string ArtifactsPath public IEnumerable GetLogicalGroupRules() => Array.Empty(); public ConfigOptions Options => ConfigOptions.KeepBenchmarkFiles | ConfigOptions.DisableOptimizationsValidator; - - public IReadOnlyList ConfigAnalysisConclusion => emptyConclusion; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index 2ee08c8169..c69975f838 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -20,7 +20,6 @@ namespace BenchmarkDotNet.Configs public class DefaultConfig : IConfig { public static readonly IConfig Instance = new DefaultConfig(); - private readonly static Conclusion[] emptyConclusion = Array.Empty(); private DefaultConfig() { @@ -68,6 +67,7 @@ public IEnumerable GetValidators() yield return GenericBenchmarksValidator.DontFailOnError; yield return DeferredExecutionValidator.FailOnError; yield return ParamsAllValuesValidator.FailOnError; + yield return RPlotExporterValidator.FailOnError; } public IOrderer Orderer => null; @@ -93,8 +93,6 @@ public string ArtifactsPath } } - public IReadOnlyList ConfigAnalysisConclusion => emptyConclusion; - public IEnumerable GetJobs() => Array.Empty(); public IEnumerable GetLogicalGroupRules() => Array.Empty(); diff --git a/src/BenchmarkDotNet/Configs/IConfig.cs b/src/BenchmarkDotNet/Configs/IConfig.cs index f3847a6f4e..78f181b9b0 100644 --- a/src/BenchmarkDotNet/Configs/IConfig.cs +++ b/src/BenchmarkDotNet/Configs/IConfig.cs @@ -52,10 +52,5 @@ public interface IConfig /// the auto-generated project build timeout /// TimeSpan BuildTimeout { get; } - - /// - /// Collect any errors or warnings when composing the configuration - /// - IReadOnlyList ConfigAnalysisConclusion { get; } } } diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index 83b7b9374a..0f2a926751 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -52,8 +52,7 @@ internal ImmutableConfig( IOrderer orderer, SummaryStyle summaryStyle, ConfigOptions options, - TimeSpan buildTimeout, - IReadOnlyList configAnalysisConclusion) + TimeSpan buildTimeout) { columnProviders = uniqueColumnProviders; loggers = uniqueLoggers; @@ -73,7 +72,6 @@ internal ImmutableConfig( SummaryStyle = summaryStyle; Options = options; BuildTimeout = buildTimeout; - ConfigAnalysisConclusion = configAnalysisConclusion; } public ConfigUnionRule UnionRule { get; } @@ -116,7 +114,5 @@ public IDiagnoser GetCompositeDiagnoser(BenchmarkCase benchmarkCase, RunMode run return diagnosersForGivenMode.Any() ? new CompositeDiagnoser(diagnosersForGivenMode) : null; } - - public IReadOnlyList ConfigAnalysisConclusion { get; private set; } } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index 677574bf86..15ebd044b0 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -27,6 +27,7 @@ public static class ImmutableConfigBuilder ShadowCopyValidator.DontFailOnError, JitOptimizationsValidator.DontFailOnError, DeferredExecutionValidator.DontFailOnError, + RPlotExporterValidator.FailOnError, ParamsAllValuesValidator.FailOnError }; @@ -37,11 +38,10 @@ public static ImmutableConfig Create(IConfig source) { var uniqueColumnProviders = source.GetColumnProviders().Distinct().ToImmutableArray(); var uniqueLoggers = source.GetLoggers().ToImmutableHashSet(); - var configAnalyse = new List(); var uniqueHardwareCounters = source.GetHardwareCounters().ToImmutableHashSet(); var uniqueDiagnosers = GetDiagnosers(source.GetDiagnosers(), uniqueHardwareCounters); - var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); + var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers); var uniqueAnalyzers = GetAnalysers(source.GetAnalysers(), uniqueDiagnosers); var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options); @@ -70,8 +70,7 @@ public static ImmutableConfig Create(IConfig source) source.Orderer ?? DefaultOrderer.Instance, source.SummaryStyle ?? SummaryStyle.Default, source.Options, - source.BuildTimeout, - configAnalyse.AsReadOnly() + source.BuildTimeout ); } @@ -95,83 +94,36 @@ private static ImmutableHashSet GetDiagnosers(IEnumerable GetExporters(IEnumerable exporters, - ImmutableHashSet uniqueDiagnosers, - IList configAnalyse) + private static ImmutableArray GetExporters(IEnumerable exporters, ImmutableHashSet uniqueDiagnosers) { - - void AddWarning(string message) - { - var conclusion = Conclusion.CreateWarning("Configuration", message); - configAnalyse.Add(conclusion); - } - - var mergeDictionary = new Dictionary(); + var allExporters = new List(); foreach (var exporter in exporters) - { - var exporterType = exporter.GetType(); - if (mergeDictionary.ContainsKey(exporterType)) - { - AddWarning($"The exporter {exporterType} is already present in configuration. There may be unexpected results."); - } - mergeDictionary[exporterType] = exporter; - } - + allExporters.Add(exporter); foreach (var diagnoser in uniqueDiagnosers) - foreach (var exporter in diagnoser.Exporters) - { - var exporterType = exporter.GetType(); - if (mergeDictionary.ContainsKey(exporterType)) - { - AddWarning($"The exporter {exporterType} of {diagnoser.GetType().Name} is already present in configuration. There may be unexpected results."); - } - mergeDictionary[exporterType] = exporter; - }; - - var result = mergeDictionary.Values.ToList(); - + foreach (var exporter in diagnoser.Exporters) + allExporters.Add(exporter); var hardwareCounterDiagnoser = uniqueDiagnosers.OfType().SingleOrDefault(); var disassemblyDiagnoser = uniqueDiagnosers.OfType().SingleOrDefault(); // we can use InstructionPointerExporter only when we have both IHardwareCountersDiagnoser and DisassemblyDiagnoser - if (hardwareCounterDiagnoser != default(IHardwareCountersDiagnoser) && disassemblyDiagnoser != default(DisassemblyDiagnoser)) - result.Add(new InstructionPointerExporter(hardwareCounterDiagnoser, disassemblyDiagnoser)); + if (hardwareCounterDiagnoser != null && disassemblyDiagnoser != null) + allExporters.Add(new InstructionPointerExporter(hardwareCounterDiagnoser, disassemblyDiagnoser)); - for (int i = result.Count - 1; i >=0; i--) - if (result[i] is IExporterDependencies exporterDependencies) + for (int i = allExporters.Count - 1; i >= 0; i--) + if (allExporters[i] is IExporterDependencies exporterDependencies) foreach (var dependency in exporterDependencies.Dependencies) - /* - * When exporter that depends on an other already present in the configuration print warning. - * - * Example: - * - * // Global Current Culture separator is Semicolon; - * [CsvMeasurementsExporter(CsvSeparator.Comma)] // force use Comma - * [RPlotExporter] - * public class MyBanch - * { - * - * } - * - * RPlotExporter is depend from CsvMeasurementsExporter.Default - * - * On active logger will by print: - * "The CsvMeasurementsExporter is already present in the configuration. There may be unexpected results of RPlotExporter. - * - */ - if (!result.Any(exporter=> exporter.GetType() == dependency.GetType())) - result.Insert(i, dependency); // All the exporter dependencies should be added before the exporter - else - { - AddWarning($"The {dependency.Name} is already present in the configuration. There may be unexpected results of {result[i].GetType().Name}."); - } - - result.Sort((left, right) => (left is IExporterDependencies).CompareTo(right is IExporterDependencies)); // the case when they were defined by user in wrong order ;) - - return result.ToImmutableArray(); + allExporters.Insert(i, dependency); // All the exporter dependencies should be added before the exporter + + var uniqueExporters = new List(); + + foreach (var exporter in allExporters) + if (!uniqueExporters.Contains(exporter, ExporterComparer.Instance)) + uniqueExporters.Add(exporter); + + return uniqueExporters.ToImmutableArray(); } private static ImmutableHashSet GetAnalysers(IEnumerable analysers, ImmutableHashSet uniqueDiagnosers) @@ -249,5 +201,14 @@ private class TypeComparer : IEqualityComparer public int GetHashCode(TInterface obj) => obj.GetType().GetHashCode(); } + + private class ExporterComparer : IEqualityComparer + { + public static readonly ExporterComparer Instance = new ExporterComparer(); + + public bool Equals(IExporter x, IExporter y) => x.GetType() == y.GetType() && x.Id == y.Id; + + public int GetHashCode(IExporter obj) => obj.GetType().GetHashCode(); + } } } diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs index 9f632c03d0..6b254b4fc1 100644 --- a/src/BenchmarkDotNet/Configs/ManualConfig.cs +++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs @@ -20,8 +20,6 @@ namespace BenchmarkDotNet.Configs { 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(); @@ -54,8 +52,6 @@ public class ManualConfig : IConfig [PublicAPI] public SummaryStyle SummaryStyle { get; set; } [PublicAPI] public TimeSpan BuildTimeout { get; set; } = DefaultConfig.Instance.BuildTimeout; - public IReadOnlyList ConfigAnalysisConclusion => emptyConclusion; - public ManualConfig WithOption(ConfigOptions option, bool value) { Options = Options.Set(value, option); diff --git a/src/BenchmarkDotNet/Exporters/AsciiDocExporter.cs b/src/BenchmarkDotNet/Exporters/AsciiDocExporter.cs index 9a1d60cbd8..a0c39e16a1 100644 --- a/src/BenchmarkDotNet/Exporters/AsciiDocExporter.cs +++ b/src/BenchmarkDotNet/Exporters/AsciiDocExporter.cs @@ -6,10 +6,10 @@ namespace BenchmarkDotNet.Exporters { public class AsciiDocExporter : ExporterBase { - protected override string FileExtension => "asciidoc"; - public static readonly IExporter Default = new AsciiDocExporter(); + protected override string FileExtension => "asciidoc"; + private AsciiDocExporter() { } diff --git a/src/BenchmarkDotNet/Exporters/CompositeExporter.cs b/src/BenchmarkDotNet/Exporters/CompositeExporter.cs index 95d3bde9d3..e415a9a7aa 100644 --- a/src/BenchmarkDotNet/Exporters/CompositeExporter.cs +++ b/src/BenchmarkDotNet/Exporters/CompositeExporter.cs @@ -14,7 +14,7 @@ public class CompositeExporter : IExporter public CompositeExporter(ImmutableArray exporters) => this.exporters = exporters; - public string Name => nameof(CompositeExporter); + public string Id => nameof(CompositeExporter); public void ExportToLog(Summary summary, ILogger logger) { diff --git a/src/BenchmarkDotNet/Exporters/Csv/CsvExporter.cs b/src/BenchmarkDotNet/Exporters/Csv/CsvExporter.cs index e6ab71ab58..c2c7da4420 100644 --- a/src/BenchmarkDotNet/Exporters/Csv/CsvExporter.cs +++ b/src/BenchmarkDotNet/Exporters/Csv/CsvExporter.cs @@ -6,12 +6,12 @@ namespace BenchmarkDotNet.Exporters.Csv { public class CsvExporter : ExporterBase { + public static readonly IExporter Default = new CsvExporter(CsvSeparator.CurrentCulture, SummaryStyle.Default.WithZeroMetricValuesInContent()); + private readonly SummaryStyle style; private readonly CsvSeparator separator; protected override string FileExtension => "csv"; - public static readonly IExporter Default = new CsvExporter(CsvSeparator.CurrentCulture, SummaryStyle.Default.WithZeroMetricValuesInContent()); - public CsvExporter(CsvSeparator separator) : this (separator, SummaryStyle.Default.WithZeroMetricValuesInContent()) { } diff --git a/src/BenchmarkDotNet/Exporters/ExporterBase.cs b/src/BenchmarkDotNet/Exporters/ExporterBase.cs index 17a8d432a6..ca8258abc4 100644 --- a/src/BenchmarkDotNet/Exporters/ExporterBase.cs +++ b/src/BenchmarkDotNet/Exporters/ExporterBase.cs @@ -10,7 +10,7 @@ namespace BenchmarkDotNet.Exporters { public abstract class ExporterBase : IExporter { - public string Name => $"{GetType().Name}{FileNameSuffix}"; + public string Id => $"{GetType().Name}{FileNameSuffix}"; protected virtual string FileExtension => "txt"; protected virtual string FileNameSuffix => string.Empty; diff --git a/src/BenchmarkDotNet/Exporters/IExporter.cs b/src/BenchmarkDotNet/Exporters/IExporter.cs index 19d55b687b..2e5a0bb10b 100644 --- a/src/BenchmarkDotNet/Exporters/IExporter.cs +++ b/src/BenchmarkDotNet/Exporters/IExporter.cs @@ -6,7 +6,7 @@ namespace BenchmarkDotNet.Exporters { public interface IExporter { - string Name { get; } + string Id { get; } void ExportToLog(Summary summary, ILogger logger); IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger); diff --git a/src/BenchmarkDotNet/Exporters/InstructionPointerExporter.cs b/src/BenchmarkDotNet/Exporters/InstructionPointerExporter.cs index 9bd783b88f..720128cfa4 100644 --- a/src/BenchmarkDotNet/Exporters/InstructionPointerExporter.cs +++ b/src/BenchmarkDotNet/Exporters/InstructionPointerExporter.cs @@ -31,7 +31,7 @@ internal InstructionPointerExporter(IHardwareCountersDiagnoser hardwareCountersD this.disassemblyDiagnoser = disassemblyDiagnoser; } - public string Name => nameof(InstructionPointerExporter); + public string Id => nameof(InstructionPointerExporter); public void ExportToLog(Summary summary, ILogger logger) { } diff --git a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs index 602f299653..90d1c83386 100644 --- a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs +++ b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs @@ -14,7 +14,7 @@ namespace BenchmarkDotNet.Exporters public class RPlotExporter : IExporter, IExporterDependencies { public static readonly IExporter Default = new RPlotExporter(); - public string Name => nameof(RPlotExporter); + public string Id => nameof(RPlotExporter); private const string ImageExtension = ".png"; private static readonly object BuildScriptLock = new object(); diff --git a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs index 754cf9f9d1..9f6f5a7330 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs @@ -89,7 +89,7 @@ private static ImmutableConfig GetFullTypeConfig(Type type, IConfig config) var typeAttributes = type.GetCustomAttributes(true).OfType(); var assemblyAttributes = type.Assembly.GetCustomAttributes().OfType(); - foreach (var configFromAttribute in assemblyAttributes.Concat(typeAttributes)) + foreach (var configFromAttribute in typeAttributes.Concat(assemblyAttributes)) config = ManualConfig.Union(config, configFromAttribute.Config); return ImmutableConfigBuilder.Create(config); diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 0de7f8c7a9..1fb77cb86c 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -272,12 +272,6 @@ private static void PrintSummary(ILogger logger, ImmutableConfig config, Summary // TODO: make exporter ConclusionHelper.Print(logger, config.GetCompositeAnalyser().Analyse(summary).Distinct().ToList()); - if (config.ConfigAnalysisConclusion.Any()) - { - logger.WriteLineHeader("// * Config Issues *"); - ConclusionHelper.Print(logger, config.ConfigAnalysisConclusion); - } - // TODO: move to conclusions var columnWithLegends = summary.Table.Columns.Where(c => c.NeedToShow && !string.IsNullOrEmpty(c.OriginalColumn.Legend)).Select(c => c.OriginalColumn).ToArray(); diff --git a/src/BenchmarkDotNet/Validators/RPlotExporterValidator.cs b/src/BenchmarkDotNet/Validators/RPlotExporterValidator.cs new file mode 100644 index 0000000000..0156cd2a21 --- /dev/null +++ b/src/BenchmarkDotNet/Validators/RPlotExporterValidator.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; + +namespace BenchmarkDotNet.Validators +{ + public class RPlotExporterValidator : IValidator + { + public static readonly RPlotExporterValidator FailOnError = new RPlotExporterValidator(); + + public bool TreatsWarningsAsErrors => true; + + public IEnumerable Validate(ValidationParameters validationParameters) + { + var exporters = validationParameters.Config.GetExporters(); + if (!ContainRPlotExporter(exporters)) + yield break; + + if (!ValidateCsvExporter(exporters)) + yield return new ValidationError(TreatsWarningsAsErrors, "RPlotExporter requires CsvMeasurementsExporter.Default. Do not override CsvMeasurementsExporter"); + } + + private static bool ContainRPlotExporter(IEnumerable exporters) + { + return exporters.Any(exporter => exporter.GetType() == typeof(RPlotExporter)); + } + + private static bool ValidateCsvExporter(IEnumerable exporters) + { + var exporter = exporters.OfType().FirstOrDefault(); + if (exporter == null) + return false; + + return exporter.Separator == CsvMeasurementsExporter.Default.Separator; + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/BaselineRatioColumnTest.cs b/tests/BenchmarkDotNet.IntegrationTests/BaselineRatioColumnTest.cs index 3da377ae6c..feb3529cee 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BaselineRatioColumnTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/BaselineRatioColumnTest.cs @@ -90,7 +90,7 @@ public class TestExporter : IExporter public string Description => "For Testing Only!"; - public string Name => "TestBenchmarkExporter"; + public string Id => "TestBenchmarkExporter"; public void ExportToLog(Summary summary, BenchmarkDotNet.Loggers.ILogger logger) => ExportCalled = true; diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index 88bff1070e..9e282a4cc1 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -7,6 +7,8 @@ using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Exporters.Xml; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; @@ -349,7 +351,7 @@ private static ImmutableConfig[] AddLeftToTheRightAndRightToTheLef(ManualConfig var leftAddedToTheRight = ManualConfig.Create(right); leftAddedToTheRight.Add(left); - return new[]{ rightAddedToLeft.CreateImmutableConfig(), leftAddedToTheRight.CreateImmutableConfig() }; + return new[] { rightAddedToLeft.CreateImmutableConfig(), leftAddedToTheRight.CreateImmutableConfig() }; } public class TestExporter : IExporter, IExporterDependencies @@ -363,7 +365,7 @@ public IEnumerable Dependencies public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) => Enumerable.Empty(); - public string Name => nameof(TestExporter); + public string Id => nameof(TestExporter); public void ExportToLog(Summary summary, ILogger logger) { } } @@ -373,40 +375,54 @@ public class TestExporterDependency : IExporter public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) => Enumerable.Empty(); - public string Name => nameof(TestExporterDependency); + public string Id => nameof(TestExporterDependency); public void ExportToLog(Summary summary, ILogger logger) { } } [Fact] - public void GenerateWarningWhenExporterDependencyAlreadyExistInConfig() + public void TheFirstCsvExporterHasPrecedence() { - System.Globalization.CultureInfo currentCulture = default; - System.Globalization.CultureInfo currentUICulture = default; - { - var ct = System.Threading.Thread.CurrentThread; - currentCulture = ct.CurrentCulture; - currentUICulture = ct.CurrentUICulture; - ct.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; - ct.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; - } - try - { - var mutable = ManualConfig.CreateEmpty(); - mutable.AddExporter(new BenchmarkDotNet.Exporters.Csv.CsvMeasurementsExporter(BenchmarkDotNet.Exporters.Csv.CsvSeparator.Comma)); - mutable.AddExporter(RPlotExporter.Default); + var config = ManualConfig.CreateEmpty(); + config.AddExporter(CsvMeasurementsExporter.Default); + config.AddExporter(new CsvMeasurementsExporter(CsvSeparator.Comma)); - var final = ImmutableConfigBuilder.Create(mutable); + var immutableConfig = config.CreateImmutableConfig(); + var exporters = immutableConfig.GetExporters().Cast().Where(e => e != null).ToArray(); - Assert.Equal(1, final.ConfigAnalysisConclusion.Count); - } - finally - { - var ct = System.Threading.Thread.CurrentThread; - ct.CurrentCulture = currentCulture; - ct.CurrentUICulture = currentUICulture; + Assert.Single(exporters); + Assert.True(exporters.First() == CsvMeasurementsExporter.Default); + } - } + [Fact] + public void DifferentMarkdownExportersAreNotRemoved() + { + var config = ManualConfig.CreateEmpty(); + config.AddExporter(MarkdownExporter.Default); + config.AddExporter(MarkdownExporter.GitHub); + + var immutableConfig = config.CreateImmutableConfig(); + var exporters = immutableConfig.GetExporters().Cast().Where(e => e != null).ToArray(); + + Assert.Equal(2, exporters.Length); + Assert.True(exporters[0] == MarkdownExporter.Default); + Assert.True(exporters[1] == MarkdownExporter.GitHub); + } + + [Fact] + public void DifferentXmlExportersAreNotRemoved() + { + var config = ManualConfig.CreateEmpty(); + config.AddExporter(XmlExporter.Default); + config.AddExporter(XmlExporter.Full); + config.AddExporter(XmlExporter.FullCompressed); + + var immutableConfig = config.CreateImmutableConfig(); + var exporters = immutableConfig.GetExporters().Cast().Where(e => e != null).ToArray(); + Assert.Equal(3, exporters.Length); + Assert.True(exporters[0] == XmlExporter.Default); + Assert.True(exporters[1] == XmlExporter.Full); + Assert.True(exporters[2] == XmlExporter.FullCompressed); } } -} +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterApprovalTests.cs b/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterApprovalTests.cs index 0b784bb77a..779cac8f58 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterApprovalTests.cs +++ b/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterApprovalTests.cs @@ -66,7 +66,7 @@ public void Exporters(string cultureInfoName) private static void PrintTitle(AccumulationLogger logger, IExporter exporter) { logger.WriteLine("############################################"); - logger.WriteLine($"{exporter.Name}"); + logger.WriteLine($"{exporter.Id}"); logger.WriteLine("############################################"); } diff --git a/tests/BenchmarkDotNet.Tests/Validators/RPlotValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/RPlotValidatorTests.cs new file mode 100644 index 0000000000..594ab1039e --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Validators/RPlotValidatorTests.cs @@ -0,0 +1,36 @@ +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; +using Xunit; + +namespace BenchmarkDotNet.Tests.Validators +{ + public class RPlotValidatorTests + { + [Fact] + public void RPlotExporterRequiresDefaultCsvExporter() + { + var actualSeparator = Thread.CurrentThread.CurrentCulture.GetActualListSeparator(); + + var alternativeSeparator = actualSeparator == CsvSeparator.Comma.ToRealSeparator() + ? CsvSeparator.Semicolon + : CsvSeparator.Comma; + + var config = ManualConfig.CreateEmpty(); + config.AddExporter(new CsvMeasurementsExporter(alternativeSeparator)); + config.AddExporter(RPlotExporter.Default); + + var validationParameters = new ValidationParameters(ImmutableArray.Empty, config.CreateImmutableConfig()); + var validationErrors = RPlotExporterValidator.FailOnError.Validate(validationParameters).ToArray(); + + Assert.Single(validationErrors); + Assert.Equal("RPlotExporter requires CsvMeasurementsExporter.Default. Do not override CsvMeasurementsExporter", validationErrors.First().Message); + } + } +} \ No newline at end of file