diff --git a/tests/Aspire.Hosting.MySql.Tests/Aspire.Hosting.MySql.Tests.csproj b/tests/Aspire.Hosting.MySql.Tests/Aspire.Hosting.MySql.Tests.csproj
index 3790d425768..04844332767 100644
--- a/tests/Aspire.Hosting.MySql.Tests/Aspire.Hosting.MySql.Tests.csproj
+++ b/tests/Aspire.Hosting.MySql.Tests/Aspire.Hosting.MySql.Tests.csproj
@@ -12,4 +12,8 @@
+
+
+
+
diff --git a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs
index ea0165cc0e2..e489c761f7e 100644
--- a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs
+++ b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs
@@ -6,6 +6,7 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Tests.Utils;
using Aspire.Hosting.Utils;
+using Microsoft.AspNetCore.InternalTesting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
@@ -27,7 +28,7 @@ public class MySqlFunctionalTests(ITestOutputHelper testOutputHelper)
[RequiresDocker]
public async Task VerifyWaitForOnMySqlBlocksDependentResources()
{
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan);
using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper);
var healthCheckTcs = new TaskCompletionSource();
@@ -58,14 +59,14 @@ public async Task VerifyWaitForOnMySqlBlocksDependentResources()
await pendingStart;
- await app.StopAsync();
+ await app.StopAsync(cts.Token);
}
[Fact]
[RequiresDocker]
public async Task VerifyMySqlResource()
{
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2), ShouldHandle = new PredicateBuilder().Handle() })
.Build();
@@ -79,9 +80,9 @@ public async Task VerifyMySqlResource()
using var app = builder.Build();
- await app.StartAsync();
+ await app.StartAsync(cts.Token);
- await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
+ await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
var hb = Host.CreateApplicationBuilder();
@@ -94,7 +95,7 @@ public async Task VerifyMySqlResource()
using var host = hb.Build();
- await host.StartAsync();
+ await host.StartAsync(cts.Token);
await pipeline.ExecuteAsync(async token =>
{
using var connection = host.Services.GetRequiredService();
@@ -119,7 +120,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
string? volumeName = null;
string? bindMountPath = null;
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2) })
.Build();
@@ -151,9 +152,9 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
using (var app = builder1.Build())
{
- await app.StartAsync();
+ await app.StartAsync(cts.Token);
- await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
+ await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
try
{
@@ -168,7 +169,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
using (var host = hb.Build())
{
- await host.StartAsync();
+ await host.StartAsync(cts.Token);
// Wait until the database is available
await pipeline.ExecuteAsync(async token =>
@@ -199,7 +200,7 @@ await pipeline.ExecuteAsync(async token =>
finally
{
// Stops the container, or the Volume/mount would still be in use
- await app.StopAsync();
+ await app.StopAsync(cts.Token);
}
}
@@ -220,9 +221,9 @@ await pipeline.ExecuteAsync(async token =>
using (var app = builder2.Build())
{
- await app.StartAsync();
+ await app.StartAsync(cts.Token);
- await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
+ await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
try
{
@@ -230,14 +231,14 @@ await pipeline.ExecuteAsync(async token =>
hb.Configuration.AddInMemoryCollection(new Dictionary
{
- [$"ConnectionStrings:{db2.Resource.Name}"] = await db2.Resource.ConnectionStringExpression.GetValueAsync(default)
+ [$"ConnectionStrings:{db2.Resource.Name}"] = await db2.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
});
hb.AddMySqlDataSource(db2.Resource.Name);
using (var host = hb.Build())
{
- await host.StartAsync();
+ await host.StartAsync(cts.Token);
// Wait until the database is available
await pipeline.ExecuteAsync(async token =>
@@ -264,7 +265,7 @@ await pipeline.ExecuteAsync(async token =>
finally
{
// Stops the container, or the Volume/mount would still be in use
- await app.StopAsync();
+ await app.StopAsync(cts.Token);
}
}
@@ -296,7 +297,7 @@ public async Task VerifyWithInitBindMount()
{
// Creates a script that should be executed when the container is initialized.
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2), ShouldHandle = new PredicateBuilder().Handle() })
.Build();
@@ -323,22 +324,22 @@ public async Task VerifyWithInitBindMount()
using var app = builder.Build();
- await app.StartAsync();
+ await app.StartAsync(cts.Token);
- await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
+ await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
var hb = Host.CreateApplicationBuilder();
hb.Configuration.AddInMemoryCollection(new Dictionary
{
- [$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(default)
+ [$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
});
hb.AddMySqlDataSource(db.Resource.Name);
using var host = hb.Build();
- await host.StartAsync();
+ await host.StartAsync(cts.Token);
// Wait until the database is available
await pipeline.ExecuteAsync(async token =>
@@ -380,7 +381,7 @@ await pipeline.ExecuteAsync(async token =>
[QuarantinedTest("https://github.com/dotnet/aspire/issues/7340")]
public async Task VerifyEfMySql()
{
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(1), ShouldHandle = new PredicateBuilder().Handle() })
.Build();
@@ -394,22 +395,22 @@ public async Task VerifyEfMySql()
using var app = builder.Build();
- await app.StartAsync();
+ await app.StartAsync(cts.Token);
- await app.WaitForTextAsync(s_mySqlReadyText).WaitAsync(TimeSpan.FromMinutes(2));
+ await app.WaitForTextAsync(s_mySqlReadyText, cts.Token).WaitAsync(cts.Token);
var hb = Host.CreateApplicationBuilder();
hb.Configuration.AddInMemoryCollection(new Dictionary
{
- [$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(default)
+ [$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(cts.Token)
});
hb.AddMySqlDbContext(db.Resource.Name);
using var host = hb.Build();
- await host.StartAsync();
+ await host.StartAsync(cts.Token);
// Wait until the database is available
await pipeline.ExecuteAsync(async token =>
@@ -453,7 +454,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple
// it generates and mounts a config.user.inc.php file instead of using environment variables.
// For this reason we need to test with and without multiple instances to cover both scenarios.
- var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
+ using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2);
// Use the same path for both runs
var aspireStorePath = Directory.CreateTempSubdirectory().FullName;
@@ -511,7 +512,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple
resourceEvent = await rns.WaitForResourceHealthyAsync("resource-phpmyadmin", cts.Token);
var phpMyAdminId = GetContainerId(resourceEvent);
- await app.StopAsync(cts.Token).WaitAsync(TimeSpan.FromMinutes(1), cts.Token);
+ await app.StopAsync(cts.Token).WaitAsync(cts.Token);
return [mySqlId, mySqlId2, phpMyAdminId];
}
diff --git a/tests/Aspire.Hosting.Tests/Utils/LoggerNotificationExtensions.cs b/tests/Aspire.Hosting.Tests/Utils/LoggerNotificationExtensions.cs
index af977ea4571..3bfe91b39ce 100644
--- a/tests/Aspire.Hosting.Tests/Utils/LoggerNotificationExtensions.cs
+++ b/tests/Aspire.Hosting.Tests/Utils/LoggerNotificationExtensions.cs
@@ -28,7 +28,7 @@ public static Task WaitForTextAsync(this DistributedApplication app, string logT
return WaitForTextAsync(app, (log) => log.Contains(logText), resourceName, cancellationToken);
}
- public static async Task WaitForHealthyAsync(this DistributedApplication app, IResourceBuilder resource, CancellationToken cancellationToken = default) where T: IResource
+ public static async Task WaitForHealthyAsync(this DistributedApplication app, IResourceBuilder resource, CancellationToken cancellationToken = default) where T : IResource
{
ArgumentNullException.ThrowIfNull(app);
ArgumentNullException.ThrowIfNull(resource);
@@ -52,6 +52,16 @@ public static Task WaitForTextAsync(this DistributedApplication app, IEnumerable
return app.WaitForTextAsync((log) => logTexts.Any(x => log.Contains(x)), resourceName, cancellationToken);
}
+ ///
+ /// Waits for the specified text to be logged.
+ ///
+ /// The instance to watch.
+ /// A predicate checking the text to wait for.
+ /// The cancellation token.
+ ///
+ public static Task WaitForTextAsync(this DistributedApplication app, Predicate predicate, CancellationToken cancellationToken = default)
+ => app.WaitForTextAsync(predicate, resourceName: null, cancellationToken);
+
///
/// Waits for the specified text to be logged.
///
diff --git a/tests/Shared/AsyncTestHelpers.cs b/tests/Shared/AsyncTestHelpers.cs
index c72cc383dd8..d3941eebd2b 100644
--- a/tests/Shared/AsyncTestHelpers.cs
+++ b/tests/Shared/AsyncTestHelpers.cs
@@ -20,11 +20,13 @@ internal static class TestConstants
// Less time waiting for hang unit tests to fail in aspnetcore solution.
public static readonly int DefaultTimeoutDuration = 5 * 1000 * (PlatformDetection.IsRunningOnCI ? 6 : 1); // 5 sec, 30 sec in CI
public static readonly int LongTimeoutDuration = 60 * 1000 * (PlatformDetection.IsRunningOnCI ? 3 : 1); // 60 sec, 180 sec in CI
+ public static readonly int ExtraLongTimeoutDuration = 60 * 1000 * 3 * (PlatformDetection.IsRunningOnCI ? 2 : 1); // 180 sec, 360 sec in CI -- useful when a docker image might need pulling
public static readonly int DefaultOrchestratorTestTimeout = 15 * 1000 * (PlatformDetection.IsRunningOnCI ? 2 : 1); // 15 sec, 30 sec in CI
public static readonly int DefaultOrchestratorTestLongTimeout = 45 * 1000 * (PlatformDetection.IsRunningOnCI ? 4 : 1); // 45 sec, 180 sec in CI
public static TimeSpan DefaultTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(DefaultTimeoutDuration);
public static TimeSpan LongTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(LongTimeoutDuration);
+ public static TimeSpan ExtraLongTimeoutTimeSpan { get; } = TimeSpan.FromMilliseconds(ExtraLongTimeoutDuration);
}
internal static class AsyncTestHelpers