Skip to content

Commit

Permalink
feat(testcontainers#569): Support relative base directories other tha…
Browse files Browse the repository at this point in the history
…n the working directory with WithDockerfileDirectory
  • Loading branch information
HofmeisterAn committed Sep 4, 2022
1 parent 0eaea8b commit fcbd4df
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
- 528 Do not require the Docker host configuration (`DockerEndpointAuthConfig`) on `TestcontainersSettings` initialization
- 538 Support optional username and password in MongoDB connection string (@the-avid-engineer)
- 540 Add Docker registry authentication provider for `DOCKER_AUTH_CONFIG` environment variable (@vova-lantsov-dev)
- 541 Allow MsSqlTestcontainerConfiguration custom database names (@enginexon)
- 541 Allow `MsSqlTestcontainerConfiguration` custom database names (@enginexon)
- 558 Support relative base directories other than the working directory with `WithDockerfileDirectory`.

### Changed

Expand Down
124 changes: 124 additions & 0 deletions src/Testcontainers/Builders/CommonDirectoryPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
namespace DotNet.Testcontainers.Builders
{
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;

/// <summary>
/// Resolves common directory paths.
/// </summary>
[PublicAPI]
public readonly struct CommonDirectoryPath
{
private static readonly string WorkingDirectoryPath = Directory.GetCurrentDirectory();

/// <summary>
/// Initializes a new instance of the <see cref="CommonDirectoryPath" /> struct.
/// </summary>
/// <param name="directoryPath">The directory path.</param>
[PublicAPI]
public CommonDirectoryPath(string directoryPath)
{
this.DirectoryPath = directoryPath;
}

/// <summary>
/// Gets the directory path.
/// </summary>
[PublicAPI]
public string DirectoryPath { get; }

/// <summary>
/// Resolves the first bin directory upwards the directory tree.
/// </summary>
/// <returns>The first bin directory upwards the directory tree.</returns>
/// <exception cref="DirectoryNotFoundException">Thrown when the bin directory was not found upwards the directory tree.</exception>
[PublicAPI]
public static CommonDirectoryPath GetBinDirectory()
{
var indexOfBinDirectory = WorkingDirectoryPath.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase);

if (indexOfBinDirectory > -1)
{
return new CommonDirectoryPath(WorkingDirectoryPath.Substring(0, indexOfBinDirectory));
}

const string message = "Cannot find 'bin' and resolve the base directory in the directory tree.";
throw new DirectoryNotFoundException(message);
}

/// <summary>
/// Resolves the first Git directory upwards the directory tree.
/// </summary>
/// <remarks>
/// Start node is the caller file path directory. End node is the root directory.
/// </remarks>
/// <param name="filePath">The caller file path.</param>
/// <returns>The first Git directory upwards the directory tree.</returns>
/// <exception cref="DirectoryNotFoundException">Thrown when the Git directory was not found upwards the directory tree.</exception>
[PublicAPI]
public static CommonDirectoryPath GetGitDirectory([CallerFilePath, NotNull] string filePath = "")
{
return new CommonDirectoryPath(GetDirectoryPath(Path.GetDirectoryName(filePath), ".git"));
}

/// <summary>
/// Resolves the first Visual Studio solution file upwards the directory tree.
/// </summary>
/// <remarks>
/// Start node is the caller file path directory. End node is the root directory.
/// </remarks>
/// <param name="filePath">The caller file path.</param>
/// <returns>The first Visual Studio solution file upwards the directory tree.</returns>
/// <exception cref="DirectoryNotFoundException">Thrown when the Visual Studio solution file was not found upwards the directory tree.</exception>
[PublicAPI]
public static CommonDirectoryPath GetSolutionDirectory([CallerFilePath, NotNull] string filePath = "")
{
return new CommonDirectoryPath(GetDirectoryPath(Path.GetDirectoryName(filePath), "*.sln"));
}

/// <summary>
/// Resolves the first CSharp project file upwards the directory tree.
/// </summary>
/// <remarks>
/// Start node is the caller file path directory. End node is the root directory.
/// </remarks>
/// <param name="filePath">The caller file path.</param>
/// <returns>The first CSharp project file upwards the directory tree.</returns>
/// <exception cref="DirectoryNotFoundException">Thrown when the CSharp project file was not found upwards the directory tree.</exception>
[PublicAPI]
public static CommonDirectoryPath GetProjectDirectory([CallerFilePath, NotNull] string filePath = "")
{
return new CommonDirectoryPath(GetDirectoryPath(Path.GetDirectoryName(filePath), "*.csproj"));
}

/// <summary>
/// Resolves the caller file path directory.
/// </summary>
/// <param name="filePath">The caller file path.</param>
/// <returns>The caller file path directory.</returns>
[PublicAPI]
public static CommonDirectoryPath GetCallerFileDirectory([CallerFilePath, NotNull] string filePath = "")
{
return new CommonDirectoryPath(Path.GetDirectoryName(filePath));
}

private static string GetDirectoryPath(string path, string searchPattern)
{
return GetDirectoryPath(Directory.Exists(path) ? new DirectoryInfo(path) : null, searchPattern);
}

private static string GetDirectoryPath(DirectoryInfo path, string searchPattern)
{
if (path != null)
{
return path.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly).Any() ? path.FullName : GetDirectoryPath(path.Parent, searchPattern);
}

var message = $"Cannot find '{searchPattern}' and resolve the base directory in the directory tree.";
throw new DirectoryNotFoundException(message);
}
}
}
9 changes: 9 additions & 0 deletions src/Testcontainers/Builders/IImageFromDockerfileBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ public interface IImageFromDockerfileBuilder : IAbstractBuilder<IImageFromDocker
[PublicAPI]
IImageFromDockerfileBuilder WithDockerfileDirectory(string dockerfileDirectory);

/// <summary>
/// Sets the base directory of the Dockerfile.
/// </summary>
/// <param name="commonDirectoryPath">Common directory path that contains the Dockerfile base directory.</param>
/// <param name="dockerfileDirectory">Dockerfile base directory.</param>
/// <returns>A configured instance of <see cref="IImageFromDockerfileBuilder" />.</returns>
[PublicAPI]
IImageFromDockerfileBuilder WithDockerfileDirectory(CommonDirectoryPath commonDirectoryPath, string dockerfileDirectory);

/// <summary>
/// If true, Testcontainer will remove the existing Docker image. Otherwise, Testcontainer will keep the Docker image.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Testcontainers/Builders/ImageFromDockerfileBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace DotNet.Testcontainers.Builders
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using DotNet.Testcontainers.Clients;
using DotNet.Testcontainers.Configurations;
Expand Down Expand Up @@ -59,6 +60,13 @@ public IImageFromDockerfileBuilder WithDockerfileDirectory(string dockerfileDire
return this.MergeNewConfiguration(new ImageFromDockerfileConfiguration(dockerfileDirectory: dockerfileDirectory));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithDockerfileDirectory(CommonDirectoryPath commonDirectoryPath, string dockerfileDirectory)
{
var baseDirectoryPath = Path.Combine(commonDirectoryPath.DirectoryPath, dockerfileDirectory);
return this.WithDockerfileDirectory(baseDirectoryPath);
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithDeleteIfExists(bool deleteIfExists)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace DotNet.Testcontainers.Tests.Unit
{
using System;
using System.Collections.Generic;
using System.IO;
using DotNet.Testcontainers.Builders;
using Xunit;

public sealed class CommonDirectoryPathTest
{
public static IEnumerable<object[]> CommonDirectoryPaths { get; }
= new[]
{
new[] { (object)CommonDirectoryPath.GetBinDirectory() },
new[] { (object)CommonDirectoryPath.GetGitDirectory() },
new[] { (object)CommonDirectoryPath.GetProjectDirectory() },
new[] { (object)CommonDirectoryPath.GetSolutionDirectory() },
new[] { (object)CommonDirectoryPath.GetCallerFileDirectory() },
};

[Theory]
[MemberData(nameof(CommonDirectoryPaths))]
public void CommonDirectoryPathExists(CommonDirectoryPath commonDirectoryPath)
{
Assert.True(Directory.Exists(commonDirectoryPath.DirectoryPath));
}

[Fact]
public void CommonDirectoryPathNotExists()
{
var callerFilePath = Path.GetPathRoot(Directory.GetCurrentDirectory());
Assert.Throws<DirectoryNotFoundException>(() => CommonDirectoryPath.GetGitDirectory(callerFilePath));
}
}
}

0 comments on commit fcbd4df

Please sign in to comment.