Skip to content

Commit

Permalink
refactor(#603): Add default console logger
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn committed Nov 22, 2022
1 parent 446f002 commit 543c2b4
Show file tree
Hide file tree
Showing 37 changed files with 140 additions and 136 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
### Changed

- 642 Expose container port bindings automatically
- 603 Add default logger that forwards messages to the console (does not support every test environment)

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ public virtual bool IsAvailable()
{
try
{
TaskFactory.StartNew(() => dockerClient.System.PingAsync()).Unwrap().GetAwaiter().GetResult();
TaskFactory.StartNew(() => dockerClient.System.PingAsync())
.Unwrap()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();

return true;
}
catch (Exception)
Expand Down
4 changes: 3 additions & 1 deletion src/Testcontainers/Clients/DockerImageOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ await this.DeleteAsync(image, ct)
await this.Docker.Images.BuildImageFromDockerfileAsync(buildParameters, dockerfileStream, Array.Empty<AuthConfig>(), new Dictionary<string, string>(), this.traceProgress, ct)
.ConfigureAwait(false);

var imageHasBeenCreated = await this.ExistsWithNameAsync(image.FullName, ct).ConfigureAwait(false);
var imageHasBeenCreated = await this.ExistsWithNameAsync(image.FullName, ct)
.ConfigureAwait(false);

if (!imageHasBeenCreated)
{
throw new InvalidOperationException($"Docker image {image.FullName} has not been created.");
Expand Down
3 changes: 1 addition & 2 deletions src/Testcontainers/Configurations/TestcontainersSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace DotNet.Testcontainers.Configurations
using DotNet.Testcontainers.Images;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

/// <summary>
/// This class represents the Testcontainers settings.
Expand Down Expand Up @@ -83,7 +82,7 @@ static TestcontainersSettings()
[PublicAPI]
[NotNull]
public static ILogger Logger { get; set; }
= NullLogger.Instance;
= new Logger();

/// <summary>
/// Gets or sets the host operating system.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers/Containers/TestcontainersContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ private void ThrowIfContainerHasNotBeenCreated()

private string GetContainerGateway()
{
const string localhost = "localhost";
const string localhost = "127.0.0.1";

if (!ContainerHasBeenCreatedStates.HasFlag(this.State))
{
Expand Down
112 changes: 112 additions & 0 deletions src/Testcontainers/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
namespace DotNet.Testcontainers
{
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;

/// <summary>
/// An <see cref="ILogger" /> implementation that forwards messages to the console. Not every test framework or environment supports this approach. Developers may still need to configure their own logging implementation.
/// If VSTest.Console.exe loads the test adapter in a deterministic order, we can write our own test adapter and intercept the IMessageLogger instance: https://github.com/microsoft/vstest/issues/4125#issuecomment-1320880502.
/// To debug the test host and runner set the environment variables VSTEST_HOST_DEBUG and VSTEST_RUNNER_DEBUG to 1. To enable VSTest logging set VSTEST_DIAG to 1 and VSTEST_DIAG_VERBOSITY to verbose.
/// The following example contains the ITestExecutor implementations. It is important that the assembly ends with TestAdapter.dll.
/// </summary>
/// <example>
/// <code>
/// [FileExtension(DllFileExtension)]
/// [FileExtension(ExeFileExtension)]
/// [DefaultExecutorUri(ExecutorUri)]
/// [ExtensionUri(ExecutorUri)]
/// [Category(Category)]
/// internal sealed class UssDiscovery : ITestDiscoverer, ITestExecutor
/// {
/// private const string DllFileExtension = &quot;.dll&quot;;
///
/// private const string ExeFileExtension = &quot;.exe&quot;;
///
/// private const string ExecutorUri = &quot;executor://testcontainers.org/v1&quot;;
///
/// private const string Category = &quot;managed&quot;;
///
/// public void DiscoverTests(IEnumerable&lt;string&gt; sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
/// {
/// }
///
/// public void RunTests(IEnumerable&lt;TestCase&gt; tests, IRunContext runContext, IFrameworkHandle frameworkHandle)
/// {
/// SetLogger(frameworkHandle);
/// }
///
/// public void RunTests(IEnumerable&lt;string&gt; sources, IRunContext runContext, IFrameworkHandle frameworkHandle)
/// {
/// SetLogger(frameworkHandle);
/// }
///
/// public void Cancel()
/// {
/// }
///
/// private static void SetLogger(IMessageLogger logger)
/// {
/// // Set the TestcontainersSettings.Logger. Use a semaphore to block the test execution until the logger is set.
/// }
/// }
/// </code>
/// </example>
internal sealed class Logger : ILogger, IDisposable
{
private readonly Stopwatch stopwatch = Stopwatch.StartNew();

public Logger()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Console.IsOutputRedirected && !Console.IsErrorRedirected)
{
Console.BufferWidth = short.MaxValue - 1;
}
}

public void Dispose()
{
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
TextWriter console;

switch (logLevel)
{
case LogLevel.Information:
console = Console.Out;
break;
case LogLevel.Warning:
console = Console.Out;
break;
case LogLevel.Error:
console = Console.Error;
break;
case LogLevel.Critical:
console = Console.Error;
break;
default:
console = TextWriter.Null;
break;
}

var message = string.Format(CultureInfo.CurrentCulture, "[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", this.stopwatch.Elapsed, formatter.Invoke(state, exception));
Debug.WriteLine(message);
console.WriteLine(message);
}

public bool IsEnabled(LogLevel logLevel)
{
return true;
}

public IDisposable BeginScope<TState>(TState state)
{
return this;
}
}
}
12 changes: 6 additions & 6 deletions src/Testcontainers/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ internal static class Logging
= LoggerMessage.Define<string>(LogLevel.Information, default, "Delete Docker container {Id}");

private static readonly Action<ILogger, string, string, Exception> _ExtractArchiveToDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Copy tar archive to {Path} at Docker container {Id}");
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Copy tar archive to \"{Path}\" at Docker container {Id}");

private static readonly Action<ILogger, string, string, Exception> _GetArchiveFromDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Read {Path} from Docker container {Id}");
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Read \"{Path}\" from Docker container {Id}");

private static readonly Action<ILogger, Type, string, Exception> _AttachToDockerContainer
= LoggerMessage.Define<Type, string>(LogLevel.Information, default, "Attach {OutputConsumer} at Docker container {Id}");

private static readonly Action<ILogger, IEnumerable<string>, string, Exception> _ExecuteCommandInDockerContainer
= LoggerMessage.Define<IEnumerable<string>, string>(LogLevel.Information, default, "Execute {Command} at Docker container {Id}");
private static readonly Action<ILogger, string, string, Exception> _ExecuteCommandInDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Execute \"{Command}\" at Docker container {Id}");

private static readonly Action<ILogger, string, Exception> _DockerImageCreated
= LoggerMessage.Define<string>(LogLevel.Information, default, "Docker image {FullName} created");
Expand Down Expand Up @@ -75,7 +75,7 @@ internal static class Logging
= LoggerMessage.Define<Guid, string>(LogLevel.Debug, default, "Lost connection to resource reaper {Id} at {Endpoint}");

private static readonly Action<ILogger, string, Exception> _DockerConfigFileNotFound
= LoggerMessage.Define<string>(LogLevel.Information, default, "Docker config {DockerConfigFilePath} not found");
= LoggerMessage.Define<string>(LogLevel.Information, default, "Docker config \"{DockerConfigFilePath}\" not found");

private static readonly Action<ILogger, string, Exception> _SearchingDockerRegistryCredential
= LoggerMessage.Define<string>(LogLevel.Information, default, "Searching Docker registry credential in {CredentialStore}");
Expand Down Expand Up @@ -144,7 +144,7 @@ public static void AttachToDockerContainer(this ILogger logger, string id, Type

public static void ExecuteCommandInDockerContainer(this ILogger logger, string id, IEnumerable<string> command)
{
_ExecuteCommandInDockerContainer(logger, command, id, null);
_ExecuteCommandInDockerContainer(logger, string.Join(" ", command), id, null);
}

public static void DockerImageCreated(this ILogger logger, IDockerImage image)
Expand Down
2 changes: 0 additions & 2 deletions src/Testcontainers/Networks/NonExistingDockerNetwork.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
namespace DotNet.Testcontainers.Networks
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;
Expand Down
19 changes: 0 additions & 19 deletions tests/Testcontainers.Tests/CustomSerilogLoggerFactory.cs

This file was deleted.

28 changes: 0 additions & 28 deletions tests/Testcontainers.Tests/Initialization.cs

This file was deleted.

39 changes: 0 additions & 39 deletions tests/Testcontainers.Tests/Initialized.cs

This file was deleted.

8 changes: 6 additions & 2 deletions tests/Testcontainers.Tests/SkipOnLinuxEngineAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ namespace DotNet.Testcontainers.Tests
{
using System;
using System.Diagnostics;
using DotNet.Testcontainers.Configurations;
using Xunit;

public sealed class SkipOnLinuxEngineAttribute : FactAttribute
{
private static readonly bool IsLinuxEngineEnabled = GetIsLinuxEngineEnabled();
static SkipOnLinuxEngineAttribute()
{
TestcontainersSettings.ResourceReaperEnabled = GetIsLinuxEngineEnabled();
}

public SkipOnLinuxEngineAttribute()
{
if (IsLinuxEngineEnabled)
if (TestcontainersSettings.ResourceReaperEnabled)
{
this.Skip = "Windows Docker engine is not available.";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ private sealed class AuthProviderTestData : List<object[]>
{
public AuthProviderTestData()
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var defaultConfiguration = new PropertiesFileConfiguration(Array.Empty<string>());
var dockerTlsConfiguration = new PropertiesFileConfiguration("docker.tls=true", $"docker.cert.path={CertificatesDirectoryPath}");
var dockerMTlsConfiguration = new PropertiesFileConfiguration("docker.tls.verify=true", $"docker.cert.path={CertificatesDirectoryPath}");
Expand All @@ -66,8 +65,8 @@ public AuthProviderTestData()
this.Add(new object[] { new EnvironmentEndpointAuthenticationProvider(DockerHostConfiguration), true });
this.Add(new object[] { new EnvironmentEndpointAuthenticationProvider(Array.Empty<ICustomConfiguration>()), false });
this.Add(new object[] { new EnvironmentEndpointAuthenticationProvider(defaultConfiguration, DockerHostConfiguration), true });
this.Add(new object[] { new NpipeEndpointAuthenticationProvider(), isWindows });
this.Add(new object[] { new UnixEndpointAuthenticationProvider(), !isWindows });
this.Add(new object[] { new NpipeEndpointAuthenticationProvider(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) });
this.Add(new object[] { new UnixEndpointAuthenticationProvider(), !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace DotNet.Testcontainers.Tests.Unit

public static class TestcontainersAccessInformationTest
{
[Collection(nameof(Testcontainers))]
public sealed class AccessDockerInformation
{
private const string DoesNotExist = nameof(TestcontainersAccessInformationTest);
Expand Down
Loading

0 comments on commit 543c2b4

Please sign in to comment.