Skip to content

Commit 340a93a

Browse files
authored
fix: Do not run reusable resource tests in parallel (#1267)
1 parent 7174786 commit 340a93a

File tree

7 files changed

+91
-60
lines changed

7 files changed

+91
-60
lines changed

Directory.Packages.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
<PackageVersion Include="SSH.NET" Version="2023.0.0"/>
1616
<PackageVersion Include="System.Text.Json" Version="6.0.9"/>
1717
<!-- Unit and integration test dependencies: -->
18-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
19-
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="8.2.0"/>
20-
<PackageVersion Include="coverlet.collector" Version="6.0.1"/>
21-
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7"/>
22-
<PackageVersion Include="xunit" Version="2.7.0"/>
18+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
19+
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="8.9.1"/>
20+
<PackageVersion Include="coverlet.collector" Version="6.0.2"/>
21+
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2"/>
22+
<PackageVersion Include="xunit" Version="2.9.0"/>
2323
<!-- Third-party client dependencies to connect and interact with the containers: -->
2424
<PackageVersion Include="Apache.NMS.ActiveMQ" Version="2.1.0"/>
2525
<PackageVersion Include="ArangoDBNetStandard" Version="2.0.1"/>

build.cake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Task("Tests")
8484
Filter = param.TestFilter,
8585
ResultsDirectory = param.Paths.Directories.TestResultsDirectoryPath,
8686
ArgumentCustomization = args => args
87+
.AppendSwitchQuoted("--blame-hang-timeout", "5m")
8788
});
8889
});
8990

src/Testcontainers/Clients/DockerApiClient.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace DotNet.Testcontainers.Clients
44
using System.Collections.Concurrent;
55
using System.Collections.Generic;
66
using System.Globalization;
7+
using System.Linq;
78
using System.Text;
89
using System.Threading;
910
using System.Threading.Tasks;
@@ -107,18 +108,14 @@ await RuntimeInitialized.WaitAsync(ct)
107108
runtimeInfo.AppendLine(dockerInfo.OperatingSystem);
108109

109110
runtimeInfo.Append(" Total Memory: ");
110-
runtimeInfo.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0:F} {1}", dockerInfo.MemTotal / Math.Pow(1024, byteUnits.Length), byteUnits[byteUnits.Length - 1]));
111+
runtimeInfo.AppendFormat(CultureInfo.InvariantCulture, "{0:F} {1}", dockerInfo.MemTotal / Math.Pow(1024, byteUnits.Length), byteUnits[byteUnits.Length - 1]);
111112

112113
var labels = dockerInfo.Labels;
113114
if (labels != null && labels.Count > 0)
114115
{
116+
runtimeInfo.AppendLine();
115117
runtimeInfo.AppendLine(" Labels: ");
116-
117-
foreach (var label in labels)
118-
{
119-
runtimeInfo.Append(" ");
120-
runtimeInfo.AppendLine(label);
121-
}
118+
runtimeInfo.Append(string.Join(Environment.NewLine, labels.Select(label => " " + label)));
122119
}
123120
Logger.LogInformation("{RuntimeInfo}", runtimeInfo);
124121
}

src/Testcontainers/Logger.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
106106
{
107107
if (IsEnabled(logLevel))
108108
{
109-
Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception));
109+
var message = exception == null ? formatter.Invoke(state, null) : string.Join(Environment.NewLine, formatter.Invoke(state, exception), exception);
110+
Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, message);
110111
}
111112
}
112113

tests/Testcontainers.Commons/CommonImages.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace DotNet.Testcontainers.Commons;
33
[PublicAPI]
44
public static class CommonImages
55
{
6-
public static readonly IImage Ryuk = new DockerImage("testcontainers/ryuk:0.6.0");
6+
public static readonly IImage Ryuk = new DockerImage("testcontainers/ryuk:0.9.0");
77

88
public static readonly IImage Alpine = new DockerImage("alpine:3.17");
99

tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,77 @@ namespace Testcontainers.Tests;
22

33
public sealed class DependsOnTest : IAsyncLifetime
44
{
5-
private const string DependsOnKey = "org.testcontainers.depends-on";
5+
private readonly FilterByProperty _filters = new FilterByProperty();
66

7-
private const string DependsOnValue = "true";
7+
private readonly IList<IAsyncDisposable> _disposables = new List<IAsyncDisposable>();
88

9-
private readonly IContainer _container = new ContainerBuilder()
10-
.DependsOn(new ContainerBuilder()
9+
private readonly string _labelKey = Guid.NewGuid().ToString("D");
10+
11+
private readonly string _labelValue = Guid.NewGuid().ToString("D");
12+
13+
public DependsOnTest()
14+
{
15+
_filters.Add("label", string.Join("=", _labelKey, _labelValue));
16+
}
17+
18+
public async Task InitializeAsync()
19+
{
20+
var childContainer1 = new ContainerBuilder()
1121
.WithImage(CommonImages.Alpine)
12-
.WithLabel(DependsOnKey, DependsOnValue)
13-
.Build())
14-
.DependsOn(new ContainerBuilder()
22+
.WithLabel(_labelKey, _labelValue)
23+
.Build();
24+
25+
var childContainer2 = new ContainerBuilder()
1526
.WithImage(CommonImages.Alpine)
16-
.WithLabel(DependsOnKey, DependsOnValue)
17-
.Build())
18-
.DependsOn(new NetworkBuilder()
19-
.WithLabel(DependsOnKey, DependsOnValue)
20-
.Build())
21-
.DependsOn(new VolumeBuilder()
22-
.WithLabel(DependsOnKey, DependsOnValue)
23-
.Build(), "/workdir")
24-
.WithImage(CommonImages.Alpine)
25-
.WithLabel(DependsOnKey, DependsOnValue)
26-
.Build();
27-
28-
public Task InitializeAsync()
29-
{
30-
return _container.StartAsync();
27+
.WithLabel(_labelKey, _labelValue)
28+
.Build();
29+
30+
var network = new NetworkBuilder()
31+
.WithLabel(_labelKey, _labelValue)
32+
.Build();
33+
34+
var volume = new VolumeBuilder()
35+
.WithLabel(_labelKey, _labelValue)
36+
.Build();
37+
38+
var parentContainer = new ContainerBuilder()
39+
.DependsOn(childContainer1)
40+
.DependsOn(childContainer2)
41+
.DependsOn(network)
42+
.DependsOn(volume, "/workdir")
43+
.WithImage(CommonImages.Alpine)
44+
.WithLabel(_labelKey, _labelValue)
45+
.Build();
46+
47+
await parentContainer.StartAsync()
48+
.ConfigureAwait(false);
49+
50+
_disposables.Add(parentContainer);
51+
_disposables.Add(childContainer1);
52+
_disposables.Add(childContainer2);
53+
_disposables.Add(network);
54+
_disposables.Add(volume);
3155
}
3256

3357
public Task DisposeAsync()
3458
{
35-
return _container.DisposeAsync().AsTask();
59+
return Task.WhenAll(_disposables.Select(disposable => disposable.DisposeAsync().AsTask()));
3660
}
3761

3862
[Fact]
3963
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
4064
public async Task DependsOnCreatesDependentResources()
4165
{
4266
// Given
43-
using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(ResourceReaper.DefaultSessionId);
67+
using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid());
4468

4569
using var client = clientConfiguration.CreateClient();
4670

47-
var labelFilter = new Dictionary<string, bool> { { string.Join("=", DependsOnKey, DependsOnValue), true } };
48-
49-
var filters = new Dictionary<string, IDictionary<string, bool>> { { "label", labelFilter } };
50-
51-
var containersListParameters = new ContainersListParameters { All = true, Filters = filters };
71+
var containersListParameters = new ContainersListParameters { All = true, Filters = _filters };
5272

53-
var networksListParameters = new NetworksListParameters { Filters = filters };
73+
var networksListParameters = new NetworksListParameters { Filters = _filters };
5474

55-
var volumesListParameters = new VolumesListParameters { Filters = filters };
75+
var volumesListParameters = new VolumesListParameters { Filters = _filters };
5676

5777
// When
5878
var containers = await client.Containers.ListContainersAsync(containersListParameters)
@@ -61,12 +81,12 @@ public async Task DependsOnCreatesDependentResources()
6181
var networks = await client.Networks.ListNetworksAsync(networksListParameters)
6282
.ConfigureAwait(true);
6383

64-
var volumesListResponse = await client.Volumes.ListAsync(volumesListParameters)
84+
var response = await client.Volumes.ListAsync(volumesListParameters)
6585
.ConfigureAwait(true);
6686

6787
// Then
6888
Assert.Equal(3, containers.Count);
6989
Assert.Single(networks);
70-
Assert.Single(volumesListResponse.Volumes);
90+
Assert.Single(response.Volumes);
7191
}
7292
}

tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
namespace Testcontainers.Tests;
22

3-
public sealed class ReusableResourceTest : IAsyncLifetime, IDisposable
3+
// We cannot run these tests in parallel because they interfere with the port
4+
// forwarding tests. When the port forwarding container is running, Testcontainers
5+
// automatically inject the necessary extra hosts into the builder configuration
6+
// using `WithPortForwarding()` internally. Depending on when the test framework
7+
// starts the port forwarding container, these extra hosts can lead to flakiness.
8+
// This happens because the reuse hash changes, resulting in two containers with
9+
// the same labels running instead of one.
10+
[CollectionDefinition(nameof(ReusableResourceTest), DisableParallelization = true)]
11+
[Collection(nameof(ReusableResourceTest))]
12+
public sealed class ReusableResourceTest : IAsyncLifetime
413
{
5-
private readonly DockerClient _dockerClient = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid()).CreateClient();
6-
714
private readonly FilterByProperty _filters = new FilterByProperty();
815

916
private readonly IList<IAsyncDisposable> _disposables = new List<IAsyncDisposable>();
@@ -63,31 +70,36 @@ public Task DisposeAsync()
6370
}));
6471
}
6572

66-
public void Dispose()
67-
{
68-
_dockerClient.Dispose();
69-
}
70-
7173
[Fact]
7274
public async Task ShouldReuseExistingResource()
7375
{
74-
var containers = await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters { Filters = _filters })
76+
using var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(Guid.NewGuid());
77+
78+
using var client = clientConfiguration.CreateClient();
79+
80+
var containersListParameters = new ContainersListParameters { All = true, Filters = _filters };
81+
82+
var networksListParameters = new NetworksListParameters { Filters = _filters };
83+
84+
var volumesListParameters = new VolumesListParameters { Filters = _filters };
85+
86+
var containers = await client.Containers.ListContainersAsync(containersListParameters)
7587
.ConfigureAwait(true);
7688

77-
var networks = await _dockerClient.Networks.ListNetworksAsync(new NetworksListParameters { Filters = _filters })
89+
var networks = await client.Networks.ListNetworksAsync(networksListParameters)
7890
.ConfigureAwait(true);
7991

80-
var response = await _dockerClient.Volumes.ListAsync(new VolumesListParameters { Filters = _filters })
92+
var response = await client.Volumes.ListAsync(volumesListParameters)
8193
.ConfigureAwait(true);
8294

8395
Assert.Single(containers);
8496
Assert.Single(networks);
8597
Assert.Single(response.Volumes);
8698
}
8799

88-
public static class ReuseHash
100+
public static class ReuseHashTest
89101
{
90-
public sealed class NotEqual
102+
public sealed class NotEqualTest
91103
{
92104
[Fact]
93105
public void ForDifferentNames()

0 commit comments

Comments
 (0)