diff --git a/Directory.Packages.props b/Directory.Packages.props
index bae22338e..011ede47d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,11 +15,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/build.cake b/build.cake
index 7ce01ac0c..159e045df 100644
--- a/build.cake
+++ b/build.cake
@@ -84,6 +84,7 @@ Task("Tests")
Filter = param.TestFilter,
ResultsDirectory = param.Paths.Directories.TestResultsDirectoryPath,
ArgumentCustomization = args => args
+ .AppendSwitchQuoted("--blame-hang-timeout", "5m")
});
});
diff --git a/src/Testcontainers/Clients/DockerApiClient.cs b/src/Testcontainers/Clients/DockerApiClient.cs
index 122039994..f0436a287 100644
--- a/src/Testcontainers/Clients/DockerApiClient.cs
+++ b/src/Testcontainers/Clients/DockerApiClient.cs
@@ -4,6 +4,7 @@ namespace DotNet.Testcontainers.Clients
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
+ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -107,18 +108,14 @@ await RuntimeInitialized.WaitAsync(ct)
runtimeInfo.AppendLine(dockerInfo.OperatingSystem);
runtimeInfo.Append(" Total Memory: ");
- runtimeInfo.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0:F} {1}", dockerInfo.MemTotal / Math.Pow(1024, byteUnits.Length), byteUnits[byteUnits.Length - 1]));
+ runtimeInfo.AppendFormat(CultureInfo.InvariantCulture, "{0:F} {1}", dockerInfo.MemTotal / Math.Pow(1024, byteUnits.Length), byteUnits[byteUnits.Length - 1]);
var labels = dockerInfo.Labels;
if (labels != null && labels.Count > 0)
{
+ runtimeInfo.AppendLine();
runtimeInfo.AppendLine(" Labels: ");
-
- foreach (var label in labels)
- {
- runtimeInfo.Append(" ");
- runtimeInfo.AppendLine(label);
- }
+ runtimeInfo.Append(string.Join(Environment.NewLine, labels.Select(label => " " + label)));
}
Logger.LogInformation("{RuntimeInfo}", runtimeInfo);
}
diff --git a/src/Testcontainers/Logger.cs b/src/Testcontainers/Logger.cs
index e7028ec32..1b908c5d5 100644
--- a/src/Testcontainers/Logger.cs
+++ b/src/Testcontainers/Logger.cs
@@ -106,7 +106,8 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
{
if (IsEnabled(logLevel))
{
- Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception));
+ var message = exception == null ? formatter.Invoke(state, null) : string.Join(Environment.NewLine, formatter.Invoke(state, exception), exception);
+ Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, message);
}
}
diff --git a/tests/Testcontainers.Commons/CommonImages.cs b/tests/Testcontainers.Commons/CommonImages.cs
index 84e1090e2..5374858c8 100644
--- a/tests/Testcontainers.Commons/CommonImages.cs
+++ b/tests/Testcontainers.Commons/CommonImages.cs
@@ -3,7 +3,7 @@ namespace DotNet.Testcontainers.Commons;
[PublicAPI]
public static class CommonImages
{
- public static readonly IImage Ryuk = new DockerImage("testcontainers/ryuk:0.6.0");
+ public static readonly IImage Ryuk = new DockerImage("testcontainers/ryuk:0.9.0");
public static readonly IImage Alpine = new DockerImage("alpine:3.17");
diff --git a/tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs b/tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs
index 55a1ab3df..b3f05e8eb 100644
--- a/tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs
+++ b/tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs
@@ -2,37 +2,61 @@ namespace Testcontainers.Tests;
public sealed class DependsOnTest : IAsyncLifetime
{
- private const string DependsOnKey = "org.testcontainers.depends-on";
+ private readonly FilterByProperty _filters = new FilterByProperty();
- private const string DependsOnValue = "true";
+ private readonly IList _disposables = new List();
- private readonly IContainer _container = new ContainerBuilder()
- .DependsOn(new ContainerBuilder()
+ private readonly string _labelKey = Guid.NewGuid().ToString("D");
+
+ private readonly string _labelValue = Guid.NewGuid().ToString("D");
+
+ public DependsOnTest()
+ {
+ _filters.Add("label", string.Join("=", _labelKey, _labelValue));
+ }
+
+ public async Task InitializeAsync()
+ {
+ var childContainer1 = new ContainerBuilder()
.WithImage(CommonImages.Alpine)
- .WithLabel(DependsOnKey, DependsOnValue)
- .Build())
- .DependsOn(new ContainerBuilder()
+ .WithLabel(_labelKey, _labelValue)
+ .Build();
+
+ var childContainer2 = new ContainerBuilder()
.WithImage(CommonImages.Alpine)
- .WithLabel(DependsOnKey, DependsOnValue)
- .Build())
- .DependsOn(new NetworkBuilder()
- .WithLabel(DependsOnKey, DependsOnValue)
- .Build())
- .DependsOn(new VolumeBuilder()
- .WithLabel(DependsOnKey, DependsOnValue)
- .Build(), "/workdir")
- .WithImage(CommonImages.Alpine)
- .WithLabel(DependsOnKey, DependsOnValue)
- .Build();
-
- public Task InitializeAsync()
- {
- return _container.StartAsync();
+ .WithLabel(_labelKey, _labelValue)
+ .Build();
+
+ var network = new NetworkBuilder()
+ .WithLabel(_labelKey, _labelValue)
+ .Build();
+
+ var volume = new VolumeBuilder()
+ .WithLabel(_labelKey, _labelValue)
+ .Build();
+
+ var parentContainer = new ContainerBuilder()
+ .DependsOn(childContainer1)
+ .DependsOn(childContainer2)
+ .DependsOn(network)
+ .DependsOn(volume, "/workdir")
+ .WithImage(CommonImages.Alpine)
+ .WithLabel(_labelKey, _labelValue)
+ .Build();
+
+ await parentContainer.StartAsync()
+ .ConfigureAwait(false);
+
+ _disposables.Add(parentContainer);
+ _disposables.Add(childContainer1);
+ _disposables.Add(childContainer2);
+ _disposables.Add(network);
+ _disposables.Add(volume);
}
public Task DisposeAsync()
{
- return _container.DisposeAsync().AsTask();
+ return Task.WhenAll(_disposables.Select(disposable => disposable.DisposeAsync().AsTask()));
}
[Fact]
@@ -40,19 +64,15 @@ public Task DisposeAsync()
public async Task DependsOnCreatesDependentResources()
{
// Given
- using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(ResourceReaper.DefaultSessionId);
+ using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid());
using var client = clientConfiguration.CreateClient();
- var labelFilter = new Dictionary { { string.Join("=", DependsOnKey, DependsOnValue), true } };
-
- var filters = new Dictionary> { { "label", labelFilter } };
-
- var containersListParameters = new ContainersListParameters { All = true, Filters = filters };
+ var containersListParameters = new ContainersListParameters { All = true, Filters = _filters };
- var networksListParameters = new NetworksListParameters { Filters = filters };
+ var networksListParameters = new NetworksListParameters { Filters = _filters };
- var volumesListParameters = new VolumesListParameters { Filters = filters };
+ var volumesListParameters = new VolumesListParameters { Filters = _filters };
// When
var containers = await client.Containers.ListContainersAsync(containersListParameters)
@@ -61,12 +81,12 @@ public async Task DependsOnCreatesDependentResources()
var networks = await client.Networks.ListNetworksAsync(networksListParameters)
.ConfigureAwait(true);
- var volumesListResponse = await client.Volumes.ListAsync(volumesListParameters)
+ var response = await client.Volumes.ListAsync(volumesListParameters)
.ConfigureAwait(true);
// Then
Assert.Equal(3, containers.Count);
Assert.Single(networks);
- Assert.Single(volumesListResponse.Volumes);
+ Assert.Single(response.Volumes);
}
}
\ No newline at end of file
diff --git a/tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs b/tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs
index 747f85d6d..dad62493a 100644
--- a/tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs
+++ b/tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs
@@ -1,9 +1,16 @@
namespace Testcontainers.Tests;
-public sealed class ReusableResourceTest : IAsyncLifetime, IDisposable
+// We cannot run these tests in parallel because they interfere with the port
+// forwarding tests. When the port forwarding container is running, Testcontainers
+// automatically inject the necessary extra hosts into the builder configuration
+// using `WithPortForwarding()` internally. Depending on when the test framework
+// starts the port forwarding container, these extra hosts can lead to flakiness.
+// This happens because the reuse hash changes, resulting in two containers with
+// the same labels running instead of one.
+[CollectionDefinition(nameof(ReusableResourceTest), DisableParallelization = true)]
+[Collection(nameof(ReusableResourceTest))]
+public sealed class ReusableResourceTest : IAsyncLifetime
{
- private readonly DockerClient _dockerClient = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid()).CreateClient();
-
private readonly FilterByProperty _filters = new FilterByProperty();
private readonly IList _disposables = new List();
@@ -63,21 +70,26 @@ public Task DisposeAsync()
}));
}
- public void Dispose()
- {
- _dockerClient.Dispose();
- }
-
[Fact]
public async Task ShouldReuseExistingResource()
{
- var containers = await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters { Filters = _filters })
+ using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid());
+
+ using var client = clientConfiguration.CreateClient();
+
+ var containersListParameters = new ContainersListParameters { All = true, Filters = _filters };
+
+ var networksListParameters = new NetworksListParameters { Filters = _filters };
+
+ var volumesListParameters = new VolumesListParameters { Filters = _filters };
+
+ var containers = await client.Containers.ListContainersAsync(containersListParameters)
.ConfigureAwait(true);
- var networks = await _dockerClient.Networks.ListNetworksAsync(new NetworksListParameters { Filters = _filters })
+ var networks = await client.Networks.ListNetworksAsync(networksListParameters)
.ConfigureAwait(true);
- var response = await _dockerClient.Volumes.ListAsync(new VolumesListParameters { Filters = _filters })
+ var response = await client.Volumes.ListAsync(volumesListParameters)
.ConfigureAwait(true);
Assert.Single(containers);
@@ -85,9 +97,9 @@ public async Task ShouldReuseExistingResource()
Assert.Single(response.Volumes);
}
- public static class ReuseHash
+ public static class ReuseHashTest
{
- public sealed class NotEqual
+ public sealed class NotEqualTest
{
[Fact]
public void ForDifferentNames()