From c653d7ce95bf1a05cc2d577af9434b2c31484dfe Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sat, 25 Oct 2025 20:16:21 +0100
Subject: [PATCH 1/2] feat: add SkipIfEmpty property to data source attributes
to control test execution behavior
---
.../Attributes/TestData/ArgumentsAttribute.cs | 6 ++
...typedDataSourceSourceGeneratorAttribute.cs | 3 +
.../TestData/DelegateDataSourceAttribute.cs | 7 +-
.../TestData/EmptyDataSourceAttribute.cs | 3 +
.../TestData/IDataSourceAttribute.cs | 5 +
.../TestData/MethodDataSourceAttribute.cs | 12 ++-
.../Attributes/TestData/NoDataSource.cs | 5 +-
.../TestData/StaticDataSourceAttribute.cs | 7 +-
.../TestData/TypedDataSourceAttribute.cs | 5 +-
TUnit.Engine/Building/TestBuilder.cs | 98 +++++++++++++++++++
TUnit.Engine/EmptyDataSourceAttribute.cs | 3 +
...Has_No_API_Changes.DotNet10_0.verified.txt | 16 ++-
..._Has_No_API_Changes.DotNet8_0.verified.txt | 16 ++-
..._Has_No_API_Changes.DotNet9_0.verified.txt | 16 ++-
...ary_Has_No_API_Changes.Net4_7.verified.txt | 16 ++-
TUnit.UnitTests/EmptyDataSourceTests.cs | 13 +++
16 files changed, 202 insertions(+), 29 deletions(-)
diff --git a/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs b/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
index 62f3be1d06..66bc973a79 100644
--- a/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
@@ -36,6 +36,9 @@ public sealed class ArgumentsAttribute : Attribute, IDataSourceAttribute, ITestR
public string? Skip { get; set; }
+ ///
+ public bool SkipIfEmpty { get; set; }
+
public ArgumentsAttribute(params object?[]? values)
{
if (values == null || values.Length == 0)
@@ -73,6 +76,9 @@ public sealed class ArgumentsAttribute(T value) : TypedDataSourceAttribute
{
public string? Skip { get; set; }
+ ///
+ public override bool SkipIfEmpty { get; set; }
+
public override async IAsyncEnumerable>> GetTypedDataRowsAsync(DataGeneratorMetadata dataGeneratorMetadata)
{
yield return () => Task.FromResult(value);
diff --git a/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
index d2bf7ce4c8..033f74c33f 100644
--- a/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
@@ -5,6 +5,9 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
public abstract class AsyncUntypedDataSourceGeneratorAttribute : Attribute, IAsyncUntypedDataSourceGeneratorAttribute
{
+ ///
+ public virtual bool SkipIfEmpty { get; set; }
+
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
public async IAsyncEnumerable>> GenerateAsync(DataGeneratorMetadata dataGeneratorMetadata)
diff --git a/TUnit.Core/Attributes/TestData/DelegateDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/DelegateDataSourceAttribute.cs
index e38c290027..45a07b9795 100644
--- a/TUnit.Core/Attributes/TestData/DelegateDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/DelegateDataSourceAttribute.cs
@@ -9,13 +9,16 @@ internal sealed class DelegateDataSourceAttribute : Attribute, IDataSourceAttrib
private readonly Func> _factory;
private readonly bool _isShared;
private List>>? _cachedFactories;
-
+
+ ///
+ public bool SkipIfEmpty { get; set; }
+
public DelegateDataSourceAttribute(Func> factory, bool isShared = false)
{
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
_isShared = isShared;
}
-
+
public async IAsyncEnumerable>> GetDataRowsAsync(DataGeneratorMetadata dataGeneratorMetadata)
{
if (_isShared && _cachedFactories != null)
diff --git a/TUnit.Core/Attributes/TestData/EmptyDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/EmptyDataSourceAttribute.cs
index 9a1361503d..a80ea2f2d2 100644
--- a/TUnit.Core/Attributes/TestData/EmptyDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/EmptyDataSourceAttribute.cs
@@ -6,6 +6,9 @@ namespace TUnit.Core;
///
internal sealed class EmptyDataSourceAttribute : Attribute, IDataSourceAttribute
{
+ ///
+ public bool SkipIfEmpty { get; set; }
+
public async IAsyncEnumerable>> GetDataRowsAsync(DataGeneratorMetadata dataGeneratorMetadata)
{
yield return () => Task.FromResult