diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs index ae72f0fb27..c588551f9c 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs @@ -299,23 +299,6 @@ public Task BuildImageAsync(IResource resource, CancellationToken cancellationTo } } - private sealed class TempDirectory : IDisposable - { - public TempDirectory() - { - Path = Directory.CreateTempSubdirectory(".aspire-compose").FullName; - } - - public string Path { get; } - public void Dispose() - { - if (File.Exists(Path)) - { - File.Delete(Path); - } - } - } - private sealed class TestProject : IProjectMetadata { public string ProjectPath => "another-path"; diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs index 4d6bdf9522..a3c6a866c9 100644 --- a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs +++ b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs @@ -105,19 +105,6 @@ await Verify(content, "yaml") .UseHelixAwareDirectory(); } - public sealed class TempDirectory : IDisposable - { - public string Path { get; } = Directory.CreateTempSubdirectory(".aspire-kubernetes").FullName; - - public void Dispose() - { - if (Directory.Exists(Path)) - { - Directory.Delete(Path, recursive: true); - } - } - } - private sealed class TestProject : IProjectMetadata { public string ProjectPath => "another-path"; diff --git a/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs b/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs index ee01ab43fa..d052c582f0 100644 --- a/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs +++ b/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs @@ -258,8 +258,8 @@ public void WithPhpMyAdminProducesValidServerConfigFile() { var builder = DistributedApplication.CreateBuilder(); - var tempStorePath = Directory.CreateTempSubdirectory().FullName; - builder.Configuration["Aspire:Store:Path"] = tempStorePath; + using var tempStore = new TempDirectory(); + builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var mysql1 = builder.AddMySql("mysql1").WithPhpMyAdmin(c => c.WithHostPort(8081)); var mysql2 = builder.AddMySql("mysql2").WithPhpMyAdmin(c => c.WithHostPort(8081)); @@ -286,15 +286,6 @@ public void WithPhpMyAdminProducesValidServerConfigFile() Assert.True(match1.Success); Match match2 = Regex.Match(fileContents, pattern2); Assert.True(match2.Success); - - try - { - Directory.Delete(tempStorePath, true); - } - catch - { - // Ignore. - } } [Fact] diff --git a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs index dd1e53fdf4..75da02c520 100644 --- a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs +++ b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs @@ -456,7 +456,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2); // Use the same path for both runs - var aspireStorePath = Directory.CreateTempSubdirectory().FullName; + using var aspireStore = new TempDirectory(); var before = await RunContainersAsync(); var after = await RunContainersAsync(); @@ -465,19 +465,10 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple Assert.All(after, Assert.NotNull); Assert.Equal(before, after); - try - { - Directory.Delete(aspireStorePath, true); - } - catch - { - // Don't fail test if we can't clean the temporary folder - } - async Task RunContainersAsync() { using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper) - .WithTempAspireStore(aspireStorePath) + .WithTempAspireStore(aspireStore.Path) .WithResourceCleanUp(false); var passwordParameter = builder.AddParameter("pwd", "p@ssw0rd1", secret: true); diff --git a/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs b/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs index a02adf90e1..d8b124d866 100644 --- a/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs +++ b/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs @@ -455,8 +455,8 @@ public async Task WithPostgresProducesValidServersJsonFile() { var builder = DistributedApplication.CreateBuilder(); - var tempStorePath = Directory.CreateTempSubdirectory().FullName; - builder.Configuration["Aspire:Store:Path"] = tempStorePath; + using var tempStore = new TempDirectory(); + builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var username = builder.AddParameter("pg-user", "myuser"); var pg1 = builder.AddPostgres("mypostgres1").WithPgAdmin(pga => pga.WithHostPort(8081)); @@ -508,15 +508,6 @@ public async Task WithPostgresProducesValidServersJsonFile() Assert.Equal("prefer", servers.GetProperty("2").GetProperty("SSLMode").GetString()); Assert.Equal("postgres", servers.GetProperty("2").GetProperty("MaintenanceDB").GetString()); Assert.Equal($"echo '{pg2.Resource.PasswordParameter.Value}'", servers.GetProperty("2").GetProperty("PasswordExecCommand").GetString()); - - try - { - Directory.Delete(tempStorePath, true); - } - catch - { - // Ignore. - } } [Fact] @@ -524,8 +515,8 @@ public async Task WithPgwebProducesValidBookmarkFiles() { var builder = DistributedApplication.CreateBuilder(); - var tempStorePath = Directory.CreateTempSubdirectory().FullName; - builder.Configuration["Aspire:Store:Path"] = tempStorePath; + using var tempStore = new TempDirectory(); + builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var pg1 = builder.AddPostgres("mypostgres1").WithPgWeb(pga => pga.WithHostPort(8081)); var pg2 = builder.AddPostgres("mypostgres2").WithPgWeb(pga => pga.WithHostPort(8081)); @@ -574,15 +565,6 @@ public async Task WithPgwebProducesValidBookmarkFiles() Assert.Equal(UnixFileMode.None, file.Mode); Assert.Equal(CreatePgWebBookmarkfileContent(db2.Resource), file.Contents); }); - - try - { - Directory.Delete(tempStorePath, true); - } - catch - { - // Ignore. - } } [Fact] diff --git a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs index 0b6a22a9b8..1553704fe8 100644 --- a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs +++ b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs @@ -446,7 +446,7 @@ public async Task Postgres_WithPersistentLifetime_ReusesContainers() var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10)); // Use the same path for both runs - var aspireStorePath = Directory.CreateTempSubdirectory().FullName; + using var aspireStore = new TempDirectory(); var before = await RunContainersAsync(); var after = await RunContainersAsync(); @@ -455,19 +455,10 @@ public async Task Postgres_WithPersistentLifetime_ReusesContainers() Assert.All(after, Assert.NotNull); Assert.Equal(before, after); - try - { - Directory.Delete(aspireStorePath, true); - } - catch - { - // Don't fail test if we can't clean the temporary folder - } - async Task RunContainersAsync() { using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper) - .WithTempAspireStore(aspireStorePath) + .WithTempAspireStore(aspireStore.Path) .WithResourceCleanUp(false); var passwordParameter = builder.AddParameter("pwd", "p@ssword1", secret: true); diff --git a/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj b/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj index a2d06c1735..c9a3641b60 100644 --- a/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj +++ b/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj @@ -33,6 +33,7 @@ + diff --git a/tests/Aspire.Hosting.Tests/MSBuildTests.cs b/tests/Aspire.Hosting.Tests/MSBuildTests.cs index 6f10a100ab..1db4e9347e 100644 --- a/tests/Aspire.Hosting.Tests/MSBuildTests.cs +++ b/tests/Aspire.Hosting.Tests/MSBuildTests.cs @@ -17,13 +17,12 @@ public class MSBuildTests public void EnsureWarningsAreEmittedWhenProjectReferencingLibraries() { var repoRoot = MSBuildUtils.GetRepoRoot(); - var tempDirectory = Directory.CreateTempSubdirectory("AspireHostingTests"); - try - { - var libraryDirectory = Path.Combine(tempDirectory.FullName, "Library"); - Directory.CreateDirectory(libraryDirectory); + using var tempDirectory = new TempDirectory(); + + var libraryDirectory = Path.Combine(tempDirectory.Path, "Library"); + Directory.CreateDirectory(libraryDirectory); - File.WriteAllText(Path.Combine(libraryDirectory, "Library.csproj"), """ + File.WriteAllText(Path.Combine(libraryDirectory, "Library.csproj"), """ @@ -34,7 +33,7 @@ public void EnsureWarningsAreEmittedWhenProjectReferencingLibraries() """); - File.WriteAllText(Path.Combine(libraryDirectory, "Class1.cs"), """ + File.WriteAllText(Path.Combine(libraryDirectory, "Class1.cs"), """ namespace Library; public class Class1 @@ -42,10 +41,10 @@ public class Class1 } """); - var appHostDirectory = Path.Combine(tempDirectory.FullName, "AppHost"); - Directory.CreateDirectory(appHostDirectory); + var appHostDirectory = Path.Combine(tempDirectory.Path, "AppHost"); + Directory.CreateDirectory(appHostDirectory); - File.WriteAllText(Path.Combine(appHostDirectory, "AppHost.csproj"), $""" + File.WriteAllText(Path.Combine(appHostDirectory, "AppHost.csproj"), $""" @@ -71,12 +70,12 @@ the Aspire.AppHost.SDK targets that will automatically add these references to p """); - File.WriteAllText(Path.Combine(appHostDirectory, "AppHost.cs"), """ + File.WriteAllText(Path.Combine(appHostDirectory, "AppHost.cs"), """ var builder = DistributedApplication.CreateBuilder(); builder.Build().Run(); """); - File.WriteAllText(Path.Combine(appHostDirectory, "Directory.Build.props"), $""" + File.WriteAllText(Path.Combine(appHostDirectory, "Directory.Build.props"), $""" true @@ -85,49 +84,44 @@ the Aspire.AppHost.SDK targets that will automatically add these references to p """); - File.WriteAllText(Path.Combine(appHostDirectory, "Directory.Build.targets"), $""" + File.WriteAllText(Path.Combine(appHostDirectory, "Directory.Build.targets"), $""" """); - var output = new StringBuilder(); - var outputDone = new ManualResetEvent(false); - using var process = new Process(); - // set '-nodereuse:false -p:UseSharedCompilation=false' so the MSBuild and Roslyn server processes don't hang around, which may hang the test in CI - process.StartInfo = new ProcessStartInfo("dotnet", $"build -nodereuse:false -p:UseSharedCompilation=false") + var output = new StringBuilder(); + var outputDone = new ManualResetEvent(false); + using var process = new Process(); + // set '-nodereuse:false -p:UseSharedCompilation=false' so the MSBuild and Roslyn server processes don't hang around, which may hang the test in CI + process.StartInfo = new ProcessStartInfo("dotnet", $"build -nodereuse:false -p:UseSharedCompilation=false") + { + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = appHostDirectory + }; + process.OutputDataReceived += (sender, e) => + { + if (e.Data == null) { - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - WorkingDirectory = appHostDirectory - }; - process.OutputDataReceived += (sender, e) => + outputDone.Set(); + } + else { - if (e.Data == null) - { - outputDone.Set(); - } - else - { - output.AppendLine(e.Data); - } - }; - process.Start(); - process.BeginOutputReadLine(); - - Assert.True(process.WaitForExit(milliseconds: 180_000), "dotnet build command timed out after 3 minutes."); - Assert.True(process.ExitCode == 0, $"Build failed: {Environment.NewLine}{output}"); - - Assert.True(outputDone.WaitOne(millisecondsTimeout: 60_000), "Timed out waiting for output to complete."); - - // Ensure a warning is emitted when an AppHost references a Library project - Assert.Contains("warning ASPIRE004", output.ToString()); - } - finally - { - tempDirectory.Delete(true); - } + output.AppendLine(e.Data); + } + }; + process.Start(); + process.BeginOutputReadLine(); + + Assert.True(process.WaitForExit(milliseconds: 180_000), "dotnet build command timed out after 3 minutes."); + Assert.True(process.ExitCode == 0, $"Build failed: {Environment.NewLine}{output}"); + + Assert.True(outputDone.WaitOne(millisecondsTimeout: 60_000), "Timed out waiting for output to complete."); + + // Ensure a warning is emitted when an AppHost references a Library project + Assert.Contains("warning ASPIRE004", output.ToString()); } } diff --git a/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs b/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs index a0d1d4243e..94b52c966e 100644 --- a/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs +++ b/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs @@ -16,7 +16,7 @@ public async Task PublishAsDockerFileConfiguresManifestWithoutBuildArgs() using var tempDir = CreateDirectoryWithDockerFile(); - var path = tempDir.Directory.FullName; + var path = tempDir.Path; var frontend = builder.AddNpmApp("frontend", path, "watch") .PublishAsDockerFile(); @@ -54,7 +54,7 @@ public async Task PublishAsDockerFileConfiguresManifestWithBuildArgs() using var tempDir = CreateDirectoryWithDockerFile(); - var path = tempDir.Directory.FullName; + var path = tempDir.Path; #pragma warning disable CS0618 // Type or member is obsolete var frontend = builder.AddNpmApp("frontend", path, "watch") @@ -107,7 +107,7 @@ public async Task PublishAsDockerFileConfiguresManifestWithBuildArgsThatHaveNoVa using var tempDir = CreateDirectoryWithDockerFile(); - var path = tempDir.Directory.FullName; + var path = tempDir.Path; #pragma warning disable CS0618 // Type or member is obsolete var frontend = builder.AddNpmApp("frontend", path, "watch") @@ -152,7 +152,7 @@ public async Task PublishAsDockerFileConfigureContainer() using var tempDir = CreateDirectoryWithDockerFile(); - var path = tempDir.Directory.FullName; + var path = tempDir.Path; var secret = builder.AddParameter("secret", secret: true); @@ -214,7 +214,7 @@ public async Task PublishProjectAsDockerFile() using var tempDir = CreateDirectoryWithDockerFile(); - var path = tempDir.Directory.FullName; + var path = tempDir.Path; var projectPath = Path.Combine(path, "project.csproj"); var project = builder.AddProject("project", projectPath, o => o.ExcludeLaunchProfile = true) @@ -265,21 +265,11 @@ public async Task PublishProjectAsDockerFile() Assert.Equal(expected, actual, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true); } - private static DisposableTempDirectory CreateDirectoryWithDockerFile() + private static TempDirectory CreateDirectoryWithDockerFile() { - var tempDir = Directory.CreateTempSubdirectory("aspire-docker-test"); - File.WriteAllText(Path.Join(tempDir.FullName, "Dockerfile"), "this does not matter"); - return new DisposableTempDirectory(tempDir); - } - - readonly struct DisposableTempDirectory(DirectoryInfo directory) : IDisposable - { - public DirectoryInfo Directory { get; } = directory; - - public void Dispose() - { - Directory.Delete(recursive: true); - } + var tempDir = new TempDirectory(); + File.WriteAllText(Path.Join(tempDir.Path, "Dockerfile"), "this does not matter"); + return tempDir; } private sealed class TestProject : IProjectMetadata diff --git a/tests/Shared/TempDirectory.cs b/tests/Shared/TempDirectory.cs new file mode 100644 index 0000000000..2bd417eeea --- /dev/null +++ b/tests/Shared/TempDirectory.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public sealed class TempDirectory : IDisposable +{ + public string Path { get; } = Directory.CreateTempSubdirectory(".aspire-tests").FullName; + + public void Dispose() + { + try + { + Directory.Delete(Path, recursive: true); + } + catch { } // Ignore errors during cleanup + } +}