Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,116 @@

namespace BenchmarkDotNet.IntegrationTests.Diagnosers;

public sealed class MockInProcessDiagnoser : IInProcessDiagnoser
public abstract class BaseMockInProcessDiagnoser : IInProcessDiagnoser
{
public Dictionary<BenchmarkCase, string> Results { get; } = [];

public IEnumerable<string> Ids => [nameof(MockInProcessDiagnoser)];
public abstract string DiagnoserName { get; }
public abstract RunMode DiagnoserRunMode { get; }
public abstract string ExpectedResult { get; }

public IEnumerable<string> Ids => [DiagnoserName];

public IEnumerable<IExporter> Exporters => [];

public IEnumerable<IAnalyser> Analysers => [];

public void DisplayResults(ILogger logger) => logger.WriteLine($"{nameof(MockInProcessDiagnoser)} results: [{string.Join(", ", Results.Values)}]");
public void DisplayResults(ILogger logger) => logger.WriteLine($"{DiagnoserName} results: [{string.Join(", ", Results.Values)}]");

public RunMode GetRunMode(BenchmarkCase benchmarkCase) => RunMode.NoOverhead;
public RunMode GetRunMode(BenchmarkCase benchmarkCase) => DiagnoserRunMode;

public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { }

public IEnumerable<Metric> ProcessResults(DiagnoserResults results) => [];

public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters) => [];

public (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase)
=> (typeof(MockInProcessDiagnoserHandler), null);
public abstract (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase);

public IInProcessDiagnoserHandler? GetSameProcessHandler(BenchmarkCase benchmarkCase)
=> new MockInProcessDiagnoserHandler();
public virtual IInProcessDiagnoserHandler? GetSameProcessHandler(BenchmarkCase benchmarkCase)
{
var (handlerType, serializedConfig) = GetSeparateProcessHandlerTypeAndSerializedConfig(benchmarkCase);
if (handlerType == null)
return null;
var handler = (IInProcessDiagnoserHandler)Activator.CreateInstance(handlerType);
handler.Initialize(serializedConfig);
return handler;
}

public void DeserializeResults(BenchmarkCase benchmarkCase, string results) => Results.Add(benchmarkCase, results);
}

public sealed class MockInProcessDiagnoserHandler : IInProcessDiagnoserHandler
public abstract class BaseMockInProcessDiagnoserHandler : IInProcessDiagnoserHandler
{
public void Initialize(string? serializedConfig) { }
private string _result;

protected BaseMockInProcessDiagnoserHandler() { }

public void Initialize(string? serializedConfig)
{
_result = serializedConfig ?? string.Empty;
}

public void Handle(BenchmarkSignal signal, InProcessDiagnoserActionArgs args) { }

public string SerializeResults() => "MockResult";
public string SerializeResults() => _result;
}

public sealed class MockInProcessDiagnoser : BaseMockInProcessDiagnoser
{
public override string DiagnoserName => nameof(MockInProcessDiagnoser);
public override RunMode DiagnoserRunMode => RunMode.NoOverhead;
public override string ExpectedResult => "MockResult";

public override (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase)
=> (typeof(MockInProcessDiagnoserHandler), ExpectedResult);
}

public sealed class MockInProcessDiagnoserHandler : BaseMockInProcessDiagnoserHandler
{
}

public sealed class MockInProcessDiagnoserNoOverhead : BaseMockInProcessDiagnoser
{
public override string DiagnoserName => nameof(MockInProcessDiagnoserNoOverhead);
public override RunMode DiagnoserRunMode => RunMode.NoOverhead;
public override string ExpectedResult => "NoOverheadResult";

public override (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase)
=> (typeof(MockInProcessDiagnoserNoOverheadHandler), ExpectedResult);
}

public sealed class MockInProcessDiagnoserNoOverheadHandler : BaseMockInProcessDiagnoserHandler
{
}

public sealed class MockInProcessDiagnoserExtraRun : BaseMockInProcessDiagnoser
{
public override string DiagnoserName => nameof(MockInProcessDiagnoserExtraRun);
public override RunMode DiagnoserRunMode => RunMode.ExtraRun;
public override string ExpectedResult => "ExtraRunResult";

public override (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase)
=> (typeof(MockInProcessDiagnoserExtraRunHandler), ExpectedResult);
}

public sealed class MockInProcessDiagnoserExtraRunHandler : BaseMockInProcessDiagnoserHandler
{
}

public sealed class MockInProcessDiagnoserNone : BaseMockInProcessDiagnoser
{
public override string DiagnoserName => nameof(MockInProcessDiagnoserNone);
public override RunMode DiagnoserRunMode => RunMode.None;
public override string ExpectedResult => "NoneResult";

public override (Type? handlerType, string? serializedConfig) GetSeparateProcessHandlerTypeAndSerializedConfig(BenchmarkCase benchmarkCase)
=> default; // Returns default when RunMode is None

public override IInProcessDiagnoserHandler? GetSameProcessHandler(BenchmarkCase benchmarkCase)
=> null; // Returns null when RunMode is None
}

public sealed class MockInProcessDiagnoserNoneHandler : BaseMockInProcessDiagnoserHandler
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.IntegrationTests.Diagnosers;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Tests.Loggers;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using BenchmarkDotNet.Toolchains.InProcess.NoEmit;
using Xunit;
using Xunit.Abstractions;
using RunMode = BenchmarkDotNet.Diagnosers.RunMode;

namespace BenchmarkDotNet.IntegrationTests;

public class MultipleInProcessDiagnosersTests : BenchmarkTestExecutor
{
public MultipleInProcessDiagnosersTests(ITestOutputHelper output) : base(output) { }

private static readonly RunMode[] AllRunModes = { RunMode.NoOverhead, RunMode.ExtraRun, RunMode.None };

private static IEnumerable<BaseMockInProcessDiagnoser[]> GetDiagnoserCombinations(int count)
{
if (count == 1)
{
foreach (var runMode in AllRunModes)
{
yield return [CreateDiagnoser(runMode, 0)];
}
}
else if (count == 2)
{
foreach (var runMode1 in AllRunModes)
{
foreach (var runMode2 in AllRunModes)
{
yield return [CreateDiagnoser(runMode1, 0), CreateDiagnoser(runMode2, 1)];
}
}
}
else if (count == 3)
{
foreach (var runMode1 in AllRunModes)
{
foreach (var runMode2 in AllRunModes)
{
foreach (var runMode3 in AllRunModes)
{
yield return [CreateDiagnoser(runMode1, 0), CreateDiagnoser(runMode2, 1), CreateDiagnoser(runMode3, 2)];
}
}
}
}
}

private static BaseMockInProcessDiagnoser CreateDiagnoser(RunMode runMode, int index)
{
return runMode switch
{
RunMode.NoOverhead => index == 0 ? new MockInProcessDiagnoserNoOverhead() : new MockInProcessDiagnoser(),
RunMode.ExtraRun => new MockInProcessDiagnoserExtraRun(),
RunMode.None => new MockInProcessDiagnoserNone(),
_ => throw new ArgumentException($"Unsupported run mode: {runMode}")
};
}

public static IEnumerable<object[]> GetTestCombinations()
{
var toolchains = new IToolchain[]
{
InProcessEmitToolchain.DontLogOutput,
new InProcessNoEmitToolchain(TimeSpan.Zero, true),
null // Default toolchain
};

var counts = new[] { 1, 2, 3 };

foreach (var toolchain in toolchains)
{
foreach (var count in counts)
{
foreach (var diagnosers in GetDiagnoserCombinations(count))
{
yield return new object[] { diagnosers, toolchain };
}
}
}
}

[Theory]
[MemberData(nameof(GetTestCombinations))]
public void MultipleInProcessDiagnosersWork(BaseMockInProcessDiagnoser[] diagnosers, IToolchain toolchain)
{
var logger = new OutputLogger(Output);
var config = CreateConfig(logger, toolchain);

foreach (var diagnoser in diagnosers)
{
config = config.AddDiagnoser(diagnoser);
}

var summary = CanExecute<SimpleBenchmark>(config);

foreach (var diagnoser in diagnosers)
{
bool shouldHaveResults = diagnoser.DiagnoserRunMode != RunMode.None;

if (shouldHaveResults)
{
Assert.NotEmpty(diagnoser.Results);
Assert.Equal(summary.BenchmarksCases.Length, diagnoser.Results.Count);
Assert.All(diagnoser.Results.Values, result => Assert.Equal(diagnoser.ExpectedResult, result));
}
else
{
Assert.Empty(diagnoser.Results);
}
}
}

private IConfig CreateConfig(OutputLogger logger, IToolchain toolchain)
{
var job = Job.Dry;
if (toolchain != null)
{
job = job.WithToolchain(toolchain);
}

return new ManualConfig()
.AddJob(job)
.AddLogger(logger)
.AddColumnProvider(DefaultColumnProviders.Instance);
}

public class SimpleBenchmark
{
private int counter;

[Benchmark]
public void BenchmarkMethod()
{
Interlocked.Increment(ref counter);
}
}

public class MultipleBenchmarks
{
private int counter;

[Benchmark]
public void Benchmark1()
{
Interlocked.Increment(ref counter);
}

[Benchmark]
public void Benchmark2()
{
Interlocked.Increment(ref counter);
}

[Benchmark]
public void Benchmark3()
{
Interlocked.Increment(ref counter);
}
}
}