Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions .github/workflows/cloudshop-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CloudShop Example Tests

on:
pull_request:
branches: ["main"]
push:
branches: ["main"]
workflow_dispatch:

jobs:
integration-tests:
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 10

steps:
- uses: actions/checkout@v6

- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
continue-on-error: true
with:
path: ~/.nuget/packages
key: nuget-cloudshop-${{ hashFiles('examples/CloudShop/**/*.csproj') }}
restore-keys: |
nuget-cloudshop-

- name: Build
run: dotnet build examples/CloudShop/CloudShop.Tests/CloudShop.Tests.csproj -c Release

- name: Run integration tests
run: dotnet run --project examples/CloudShop/CloudShop.Tests/CloudShop.Tests.csproj -c Release --no-build
24 changes: 24 additions & 0 deletions TUnit.Assertions/Extensions/AssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,30 @@ public static WaitsForAssertion<TValue> WaitsFor<TValue>(
return new WaitsForAssertion<TValue>(source.Context, assertionBuilder, timeout, pollingInterval);
}

/// <summary>
/// Alias for <see cref="WaitsFor{TValue}"/> — asserts that an assertion passes within the specified timeout by polling repeatedly.
/// Reads more naturally for integration tests with eventually-consistent state.
/// Example: await Assert.That(getValue).Eventually(assert => assert.IsEqualTo(expected), timeout: TimeSpan.FromSeconds(10));
/// </summary>
/// <typeparam name="TValue">The type of value being asserted</typeparam>
/// <param name="source">The assertion source</param>
/// <param name="assertionBuilder">A function that builds the assertion to be evaluated on each poll</param>
/// <param name="timeout">The maximum time to wait for the assertion to pass</param>
/// <param name="pollingInterval">The interval between polling attempts (defaults to 10ms if not specified)</param>
/// <param name="timeoutExpression">Captured expression for the timeout parameter</param>
/// <param name="pollingIntervalExpression">Captured expression for the polling interval parameter</param>
/// <returns>An assertion that can be awaited or chained with And/Or</returns>
public static WaitsForAssertion<TValue> Eventually<TValue>(
this IAssertionSource<TValue> source,
Func<IAssertionSource<TValue>, Assertion<TValue>> assertionBuilder,
TimeSpan timeout,
TimeSpan? pollingInterval = null,
[CallerArgumentExpression(nameof(timeout))] string? timeoutExpression = null,
[CallerArgumentExpression(nameof(pollingInterval))] string? pollingIntervalExpression = null)
{
return source.WaitsFor(assertionBuilder, timeout, pollingInterval, timeoutExpression, pollingIntervalExpression);
}

private static Action GetActionFromDelegate(DelegateAssertion source)
{
return source.Action;
Expand Down
6 changes: 3 additions & 3 deletions TUnit.Engine/Scheduling/TestScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,10 +561,10 @@ private static int GetMaxParallelism(ILogger logger, ICommandLineOptions command
}
}

// Default: 8x CPU cores (empirically optimized for async/IO-bound workloads)
// Default: 4x CPU cores (empirically optimized for async/IO-bound workloads)
// Users can override via --maximum-parallel-tests or TUNIT_MAX_PARALLEL_TESTS
var defaultLimit = Environment.ProcessorCount * 8;
logger.LogDebug($"Maximum parallel tests limit defaulting to {defaultLimit} ({Environment.ProcessorCount} processors * 8)");
var defaultLimit = Environment.ProcessorCount * 4;
logger.LogDebug($"Maximum parallel tests limit defaulting to {defaultLimit} ({Environment.ProcessorCount} processors * 4)");
return defaultLimit;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,7 @@ namespace .Extensions
public static ..DoesNotHaveSameValueAsAssertion<TEnum> DoesNotHaveSameValueAs<TEnum>(this .<TEnum> source, otherEnumValue, [.("otherEnumValue")] string? expression = null)
where TEnum : struct, { }
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static .<TValue> Eventually<TValue>(this .<TValue> source, <.<TValue>, .<TValue>> assertionBuilder, timeout, ? pollingInterval = default, [.("timeout")] string? timeoutExpression = null, [.("pollingInterval")] string? pollingIntervalExpression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null)
where TEnum : struct, { }
[("Use Length() instead, which provides all numeric assertion methods. Example: Asse" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,7 @@ namespace .Extensions
public static ..DoesNotHaveSameValueAsAssertion<TEnum> DoesNotHaveSameValueAs<TEnum>(this .<TEnum> source, otherEnumValue, [.("otherEnumValue")] string? expression = null)
where TEnum : struct, { }
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static .<TValue> Eventually<TValue>(this .<TValue> source, <.<TValue>, .<TValue>> assertionBuilder, timeout, ? pollingInterval = default, [.("timeout")] string? timeoutExpression = null, [.("pollingInterval")] string? pollingIntervalExpression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null)
where TEnum : struct, { }
[("Use Length() instead, which provides all numeric assertion methods. Example: Asse" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,7 @@ namespace .Extensions
public static ..DoesNotHaveSameValueAsAssertion<TEnum> DoesNotHaveSameValueAs<TEnum>(this .<TEnum> source, otherEnumValue, [.("otherEnumValue")] string? expression = null)
where TEnum : struct, { }
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static .<TValue> Eventually<TValue>(this .<TValue> source, <.<TValue>, .<TValue>> assertionBuilder, timeout, ? pollingInterval = default, [.("timeout")] string? timeoutExpression = null, [.("pollingInterval")] string? pollingIntervalExpression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null)
where TEnum : struct, { }
[("Use Length() instead, which provides all numeric assertion methods. Example: Asse" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2186,6 +2186,7 @@ namespace .Extensions
public static ..DoesNotHaveSameValueAsAssertion<TEnum> DoesNotHaveSameValueAs<TEnum>(this .<TEnum> source, otherEnumValue, [.("otherEnumValue")] string? expression = null)
where TEnum : struct, { }
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static .<TValue> Eventually<TValue>(this .<TValue> source, <.<TValue>, .<TValue>> assertionBuilder, timeout, ? pollingInterval = default, [.("timeout")] string? timeoutExpression = null, [.("pollingInterval")] string? pollingIntervalExpression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null)
where TEnum : struct, { }
[("Use Length() instead, which provides all numeric assertion methods. Example: Asse" +
Expand Down
Loading
Loading