Skip to content

Commit d25700f

Browse files
authored
V1.2.0/service update (#3)
🔥 removed BenchmarkWorker and associated tests and benchmarks ⬆️ bump dependecies 🚨 CA2249 - update path filtering to use Contains for better readability 🚨 justification for CA1859 ♻️ simplified BenchmarkProgram inheriting from MinimalConsoleProgram 💬 updated community health pages 📦️ updated NuGet package definition
1 parent 6ba0635 commit d25700f

File tree

12 files changed

+143
-794
lines changed

12 files changed

+143
-794
lines changed

.nuget/Codebelt.Extensions.BenchmarkDotNet.Console/PackageReleaseNotes.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
Version: 1.2.0
2+
Availability: .NET 10 and .NET 9
3+
 
4+
# ALM
5+
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)
6+
 
7+
# Breaking Changes
8+
- REMOVED BenchmarkWorker class in the Codebelt.Extensions.BenchmarkDotNet.Console namespace and functionality has been merged into the BenchmarkProgram class to streamline the hosting and execution of benchmarks
9+
 
110
Version: 1.1.0
211
Availability: .NET 10 and .NET 9
312

.nuget/Codebelt.Extensions.BenchmarkDotNet/PackageReleaseNotes.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
Version: 1.2.0
2+
Availability: .NET 10 and .NET 9
3+
 
4+
# ALM
5+
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)
6+
 
17
Version: 1.1.0
28
Availability: .NET 10 and .NET 9
39

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
44

55
For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder.
66

7+
## [1.2.0] - 2025-12-19
8+
9+
Technically, this is a major release due to the removal of BenchmarkWorker class in the Codebelt.Extensions.BenchmarkDotNet.Console namespace. However, since this library is new and the external API remains unchanged, we have decided to label this release as a minor update.
10+
11+
### Removed
12+
13+
- `BenchmarkWorker` class in the Codebelt.Extensions.BenchmarkDotNet.Console namespace has been removed. Its functionality has been merged into the `BenchmarkProgram` class to streamline the hosting and execution of benchmarks.
14+
715
## [1.1.0] - 2025-12-14
816

17+
This release introduces several enhancements and fixes to improve the functionality and usability of the `Codebelt.Extensions.BenchmarkDotNet` and `Codebelt.Extensions.BenchmarkDotNet.Console` packages.
18+
919
### Changed
1020

1121
- `BenchmarkWorkspace` class in the Codebelt.Extensions.BenchmarkDotNet namespace was extended with two new static methods; `GetReportsResultsPath` and `GetReportsTuningPath` for retrieving the paths to the reports results and tuning directories respectively,

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<ItemGroup>
66
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
77
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.15.8" />
8-
<PackageVersion Include="Codebelt.Bootstrapper.Console" Version="5.0.1" />
9-
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.0.2" />
8+
<PackageVersion Include="Codebelt.Bootstrapper.Console" Version="5.0.2" />
9+
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.0.3" />
1010
<PackageVersion Include="Cuemon.Core" Version="10.1.1" />
1111
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
1212
<PackageVersion Include="MinVer" Version="6.0.0" />

reports/tuning/Codebelt.Extensions.BenchmarkDotNet.Console.BenchmarkWorkerBenchmark-report-github.md

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/Codebelt.Extensions.BenchmarkDotNet.Console/BenchmarkProgram.cs

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
using Codebelt.Bootstrapper.Console;
1+
using BenchmarkDotNet.Configs;
2+
using BenchmarkDotNet.Filters;
3+
using BenchmarkDotNet.Running;
4+
using Codebelt.Bootstrapper.Console;
25
using Cuemon;
36
using Cuemon.Reflection;
47
using Microsoft.Extensions.DependencyInjection;
58
using Microsoft.Extensions.Hosting;
69
using System;
10+
using System.Collections.Generic;
11+
using System.IO;
12+
using System.Linq;
713
using System.Reflection;
14+
using System.Threading;
15+
using System.Threading.Tasks;
816

917
namespace Codebelt.Extensions.BenchmarkDotNet.Console
1018
{
1119
/// <summary>
1220
/// Entry point helper for hosting and running benchmarks using BenchmarkDotNet.
1321
/// </summary>
14-
/// <seealso cref="ConsoleProgram{TStartup}"/>
15-
public class BenchmarkProgram : ConsoleProgram<BenchmarkWorker>
22+
/// <seealso cref="MinimalConsoleProgram{BenchmarkProgram}"/>
23+
public class BenchmarkProgram : MinimalConsoleProgram<BenchmarkProgram>
1624
{
1725
static BenchmarkProgram()
1826
{
@@ -86,15 +94,101 @@ public static void Run<TWorkspace>(string[] args, Action<BenchmarkWorkspaceOptio
8694
/// </remarks>
8795
public static void Run<TWorkspace>(string[] args, Action<IServiceCollection> serviceConfigurator = null, Action<BenchmarkWorkspaceOptions> setup = null) where TWorkspace : class, IBenchmarkWorkspace
8896
{
89-
var hostBuilder = CreateHostBuilder(args);
90-
hostBuilder.ConfigureServices(services =>
91-
{
92-
services.AddSingleton(new BenchmarkContext(args));
93-
services.AddBenchmarkWorkspace<TWorkspace>(setup);
94-
serviceConfigurator?.Invoke(services);
95-
});
96-
using var host = hostBuilder.Build();
97+
var builder = CreateHostBuilder(args);
98+
99+
builder.Services.Configure<ConsoleLifetimeOptions>(o => o.SuppressStatusMessages = !IsDebugBuild);
100+
builder.Services.AddSingleton(new BenchmarkContext(args));
101+
builder.Services.AddBenchmarkWorkspace<TWorkspace>(setup);
102+
serviceConfigurator?.Invoke(builder.Services);
103+
104+
using var host = builder.Build();
97105
host.Run();
98106
}
107+
108+
/// <summary>
109+
/// Runs the actual benchmarks as envisioned by BenchmarkDotNet.
110+
/// </summary>
111+
/// <param name="serviceProvider">The service provider.</param>
112+
/// <param name="cancellationToken">The cancellation token.</param>
113+
/// <returns>A completed task when benchmark execution has finished.</returns>
114+
/// <remarks>
115+
/// When arguments are provided, they are forwarded to <see cref="BenchmarkSwitcher"/> for selective execution.
116+
/// After execution completes, artifact post-processing is performed.
117+
/// </remarks>
118+
public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
119+
{
120+
var options = serviceProvider.GetRequiredService<BenchmarkWorkspaceOptions>();
121+
var workspace = serviceProvider.GetRequiredService<IBenchmarkWorkspace>();
122+
var assemblies = workspace.LoadBenchmarkAssemblies();
123+
var context = serviceProvider.GetRequiredService<BenchmarkContext>();
124+
125+
if (options.SkipBenchmarksWithReports) { ConfigureBenchmarkDotNetFiltersForExistingReports(options, assemblies); }
126+
127+
try
128+
{
129+
ExecuteBenchmarks(assemblies, context, options);
130+
}
131+
finally
132+
{
133+
workspace.PostProcessArtifacts();
134+
}
135+
136+
return Task.CompletedTask;
137+
}
138+
139+
private static void ConfigureBenchmarkDotNetFiltersForExistingReports(BenchmarkWorkspaceOptions options, Assembly[] assemblies)
140+
{
141+
var benchmarkTypes = assemblies
142+
.SelectMany(a => a.GetTypes().Where(t => t.Name.EndsWith("Benchmark", StringComparison.Ordinal)))
143+
.ToList();
144+
145+
options.ConfigureBenchmarkDotNet(c => ApplyReportFilters(c, options, benchmarkTypes));
146+
}
147+
148+
private static IConfig ApplyReportFilters(IConfig config, BenchmarkWorkspaceOptions options, List<Type> benchmarkTypes)
149+
{
150+
var tuningPath = BenchmarkWorkspace.GetReportsTuningPath(options);
151+
if (!Directory.Exists(tuningPath)) { return config; }
152+
153+
foreach (var report in Directory.EnumerateFiles(tuningPath))
154+
{
155+
var matchingType = FindMatchingBenchmarkType(report, benchmarkTypes);
156+
if (matchingType != null)
157+
{
158+
config = config.AddFilter(new SimpleFilter(bc => bc.Descriptor.Type != matchingType));
159+
}
160+
}
161+
162+
return config;
163+
}
164+
165+
private static Type FindMatchingBenchmarkType(string reportPath, List<Type> benchmarkTypes)
166+
{
167+
var filename = Path.GetFileNameWithoutExtension(reportPath);
168+
var potentialTypeFullName = filename.Split('-').FirstOrDefault();
169+
if (string.IsNullOrWhiteSpace(potentialTypeFullName)) { return null; }
170+
171+
var potentialTypeName = potentialTypeFullName.Split('.').LastOrDefault();
172+
if (string.IsNullOrWhiteSpace(potentialTypeName)) { return null; }
173+
174+
return benchmarkTypes.FirstOrDefault(t => t.Name.Equals(potentialTypeName, StringComparison.OrdinalIgnoreCase));
175+
}
176+
177+
private static void ExecuteBenchmarks(Assembly[] assemblies, BenchmarkContext context, BenchmarkWorkspaceOptions options)
178+
{
179+
if (context.Args.Length == 0)
180+
{
181+
foreach (var assembly in assemblies)
182+
{
183+
BenchmarkRunner.Run(assembly, options.Configuration);
184+
}
185+
}
186+
else
187+
{
188+
BenchmarkSwitcher
189+
.FromAssemblies(assemblies)
190+
.Run(context.Args, options.Configuration);
191+
}
192+
}
99193
}
100194
}

src/Codebelt.Extensions.BenchmarkDotNet.Console/BenchmarkWorker.cs

Lines changed: 0 additions & 112 deletions
This file was deleted.

src/Codebelt.Extensions.BenchmarkDotNet/BenchmarkWorkspace.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ private static IEnumerable<Assembly> LoadAssemblies(string repositoryPath, strin
121121

122122
var candidatePaths = Directory
123123
.EnumerateFiles(tuningDir, $"*.{benchmarkProjectSuffix}.dll", SearchOption.AllDirectories)
124-
.Where(path => path.IndexOf(buildSegment, StringComparison.OrdinalIgnoreCase) >= 0);
124+
.Where(path => path.Contains(buildSegment, StringComparison.OrdinalIgnoreCase));
125125

126126
foreach (var path in candidatePaths)
127127
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// This file is used by Code Analysis to maintain SuppressMessage
2+
// attributes that are applied to this project.
3+
// Project-level suppressions either have no target or are given
4+
// a specific target and scoped to a namespace, type, member, etc.
5+
6+
using System.Diagnostics.CodeAnalysis;
7+
8+
[assembly: SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Using abstractions in public APIs promotes encapsulation, testability, and long-term maintainability, as prescribed by the .NET Framework Design Guidelines.", Scope = "member", Target = "~M:Codebelt.Extensions.BenchmarkDotNet.BenchmarkWorkspace.LoadAssemblies(System.String,System.String,System.String,System.String,System.Boolean)~System.Collections.Generic.IEnumerable{System.Reflection.Assembly}")]

test/Codebelt.Extensions.BenchmarkDotNet.Console.Tests/BenchmarkProgramTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public void BenchmarkProgram_ShouldInheritFromConsoleProgram()
248248
// Assert
249249
Assert.NotNull(baseType);
250250
Assert.True(baseType.IsGenericType);
251-
Assert.Equal("ConsoleProgram`1", baseType.Name);
251+
Assert.Equal("MinimalConsoleProgram`1", baseType.Name);
252252

253253
TestOutput.WriteLine($"BenchmarkProgram correctly inherits from: {baseType.FullName}");
254254
}
@@ -263,7 +263,7 @@ public void BenchmarkProgram_ShouldUseCorrectGenericTypeParameter()
263263
// Assert
264264
Assert.NotNull(genericArguments);
265265
Assert.Single(genericArguments);
266-
Assert.Equal(typeof(BenchmarkWorker), genericArguments[0]);
266+
Assert.Equal(typeof(BenchmarkProgram), genericArguments[0]);
267267

268268
TestOutput.WriteLine($"BenchmarkProgram uses correct generic type parameter: {genericArguments[0].Name}");
269269
}

0 commit comments

Comments
 (0)