Skip to content

Commit 43c7b56

Browse files
authored
Log a warning when an empty benchmark is run (#2718)
* Add message and throw a validation error + add test * change the test to see if there is a log message * Update the logic to throw a validation error + update tests + remove redundant check for any benchmark * fix case of empty BenchmarkRunInfo array & added test & fix EventProcessorTests * Fix tests error when running an empty RunInfo array & fix wakeLock logic accordinly * Improve tests naming and precision * update tests to support benchmark switcher + add logger + add better logs * fix benchmarswitcher tests error
1 parent 602c5fe commit 43c7b56

File tree

6 files changed

+438
-19
lines changed

6 files changed

+438
-19
lines changed

src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs

+41-15
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
using BenchmarkDotNet.Jobs;
2020
using BenchmarkDotNet.Loggers;
2121
using BenchmarkDotNet.Mathematics;
22-
using BenchmarkDotNet.Portability;
2322
using BenchmarkDotNet.Reports;
2423
using BenchmarkDotNet.Toolchains;
2524
using BenchmarkDotNet.Toolchains.Parameters;
@@ -74,9 +73,6 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
7473
if (validationErrors.Any(validationError => validationError.IsCritical))
7574
return new[] { Summary.ValidationFailed(title, resultsFolderPath, logFilePath, validationErrors.ToImmutableArray()) };
7675

77-
if (!supportedBenchmarks.Any(benchmarks => benchmarks.BenchmarksCases.Any()))
78-
return new[] { Summary.ValidationFailed(title, resultsFolderPath, logFilePath) };
79-
8076
eventProcessor.OnEndValidationStage();
8177

8278
int totalBenchmarkCount = supportedBenchmarks.Sum(benchmarkInfo => benchmarkInfo.BenchmarksCases.Length);
@@ -579,20 +575,47 @@ private static void LogTotalTime(ILogger logger, TimeSpan time, int executedBenc
579575
private static (BenchmarkRunInfo[], List<ValidationError>) GetSupportedBenchmarks(BenchmarkRunInfo[] benchmarkRunInfos, IResolver resolver)
580576
{
581577
List<ValidationError> validationErrors = new ();
578+
List<BenchmarkRunInfo> runInfos = new (benchmarkRunInfos.Length);
579+
580+
if (benchmarkRunInfos.Length == 0)
581+
{
582+
validationErrors.Add(new ValidationError(true, $"No benchmarks were found."));
583+
return (Array.Empty<BenchmarkRunInfo>(), validationErrors);
584+
}
585+
586+
foreach (var benchmarkRunInfo in benchmarkRunInfos)
587+
{
588+
if (benchmarkRunInfo.BenchmarksCases.Length == 0)
589+
{
590+
validationErrors.Add(new ValidationError(true, $"No [Benchmark] attribute found on '{benchmarkRunInfo.Type.Name}' benchmark case."));
591+
continue;
592+
}
582593

583-
var runInfos = benchmarkRunInfos.Select(info => new BenchmarkRunInfo(
584-
info.BenchmarksCases.Where(benchmark =>
594+
var validBenchmarks = benchmarkRunInfo.BenchmarksCases
595+
.Where(benchmark =>
585596
{
586-
var errors = benchmark.GetToolchain().Validate(benchmark, resolver).ToArray();
597+
598+
var errors = benchmark.GetToolchain()
599+
.Validate(benchmark, resolver)
600+
.ToArray();
601+
587602
validationErrors.AddRange(errors);
588-
return !errors.Any();
589-
}).ToArray(),
590-
info.Type,
591-
info.Config))
592-
.Where(infos => infos.BenchmarksCases.Any())
593-
.ToArray();
594-
595-
return (runInfos, validationErrors);
603+
604+
return errors.Length == 0;
605+
})
606+
.ToArray();
607+
608+
runInfos.Add(
609+
new BenchmarkRunInfo(
610+
validBenchmarks,
611+
benchmarkRunInfo.Type,
612+
benchmarkRunInfo.Config
613+
614+
));
615+
616+
617+
}
618+
return (runInfos.ToArray(), validationErrors);
596619
}
597620

598621
private static string GetRootArtifactsFolderPath(BenchmarkRunInfo[] benchmarkRunInfos)
@@ -665,6 +688,9 @@ void AddLogger(ILogger logger)
665688
foreach (var logger in benchmarkRunInfo.Config.GetLoggers())
666689
AddLogger(logger);
667690

691+
if (benchmarkRunInfos.Length == 0)
692+
AddLogger(new ConsoleLogger());
693+
668694
AddLogger(streamLogger);
669695

670696
return new CompositeLogger(loggers.Values.ToImmutableHashSet());

src/BenchmarkDotNet/Running/TypeFilter.cs

+26
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@ public static (bool allTypesValid, IReadOnlyList<Type> runnable) GetTypesWithRun
1515
{
1616
var validRunnableTypes = new List<Type>();
1717

18+
bool hasRunnableTypeBenchmarks = types.Any(type => type.ContainsRunnableBenchmarks());
19+
bool hasRunnableAssemblyBenchmarks = assemblies.Any(assembly => GenericBenchmarksBuilder.GetRunnableBenchmarks(assembly.GetRunnableBenchmarks()).Length > 0);
20+
21+
if (!hasRunnableTypeBenchmarks && !hasRunnableAssemblyBenchmarks)
22+
{
23+
if (types.Any())
24+
{
25+
foreach (var type in types)
26+
{
27+
logger.WriteLineError($"No [Benchmark] attribute found on '{type.Name}' benchmark case.");
28+
}
29+
}
30+
else if (assemblies.Any())
31+
{
32+
foreach (var assembly in assemblies)
33+
{
34+
logger.WriteLineError($"No [Benchmark] attribute found on '{assembly.GetName().Name}' assembly.");
35+
}
36+
}
37+
else
38+
{
39+
logger.WriteLineError("No benchmarks were found.");
40+
}
41+
return (false, Array.Empty<Type>());
42+
}
43+
1844
foreach (var type in types)
1945
{
2046
if (type.ContainsRunnableBenchmarks())

src/BenchmarkDotNet/Running/WakeLock.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace BenchmarkDotNet.Running;
1111
internal partial class WakeLock
1212
{
1313
public static WakeLockType GetWakeLockType(BenchmarkRunInfo[] benchmarkRunInfos) =>
14-
benchmarkRunInfos.Select(static i => i.Config.WakeLock).Max();
14+
benchmarkRunInfos.Length == 0 ? WakeLockType.None : benchmarkRunInfos.Select(static i => i.Config.WakeLock).Max();
1515

1616
private static readonly bool OsVersionIsSupported =
1717
// Must be windows 7 or greater

tests/BenchmarkDotNet.IntegrationTests/BenchmarkSwitcherTest.cs

+9-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void WhenInvalidTypeIsProvidedAnErrorMessageIsDisplayedAndNoBenchmarksAre
6666
.Run(new[] { "--filter", "*" }, config);
6767

6868
Assert.Empty(summaries);
69-
Assert.Contains("Type BenchmarkDotNet.IntegrationTests.ClassC is invalid.", logger.GetLog());
69+
Assert.Contains(GetValidationErrorForType(typeof(ClassC)), logger.GetLog());
7070
}
7171

7272
[Fact]
@@ -80,7 +80,7 @@ public void WhenNoTypesAreProvidedAnErrorMessageIsDisplayedAndNoBenchmarksAreExe
8080
.Run(new[] { "--filter", "*" }, config);
8181

8282
Assert.Empty(summaries);
83-
Assert.Contains("No benchmarks to choose from. Make sure you provided public non-sealed non-static types with public [Benchmark] methods.", logger.GetLog());
83+
Assert.Contains("No benchmarks were found.", logger.GetLog());
8484
}
8585

8686
[Fact]
@@ -369,6 +369,11 @@ public IReadOnlyList<Type> AskUser(IReadOnlyList<Type> allTypes, ILogger logger)
369369
return returnValue;
370370
}
371371
}
372+
373+
private string GetValidationErrorForType(Type type)
374+
{
375+
return $"No [Benchmark] attribute found on '{type.Name}' benchmark case.";
376+
}
372377
}
373378
}
374379

@@ -424,6 +429,8 @@ public override void ExportToLog(Summary summary, ILogger logger)
424429
exported = true;
425430
}
426431
}
432+
433+
427434
}
428435

429436
namespace BenchmarkDotNet.NOTIntegrationTests

tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ public class EventProcessorTests
2323
public void WhenUsingEventProcessorAndNoBenchmarks()
2424
{
2525
var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassEmpty) });
26-
Assert.Single(events);
26+
Assert.Equal(2, events.Count);
2727
Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
28+
Assert.Equal(nameof(EventProcessor.OnValidationError), events[1].EventType);
2829
}
2930

3031
[Fact]

0 commit comments

Comments
 (0)