Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions Reqnroll.MessagesLogger.TestLogger/FormatterLoggerBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;

namespace Reqnroll.FormatterTestLogger;

public abstract class FormatterLoggerBase : ITestLoggerWithParameters
{
public bool IsInitialized { get; private set; }
public Dictionary<string, string> Parameters { get; private set; } = new();
public string TestRunDirectory { get; private set; } = string.Empty;

private string _originalEnvValue;
private bool _envSetByLogger;
private bool _subscribed;
protected string FormatterName = null;

// Meta-keys to ignore for formatter environment variable
// ReSharper disable once UnusedMember.Global
protected static readonly HashSet<string> MetaKeys = new(StringComparer.OrdinalIgnoreCase)
{
"TestRunDirectory", "TargetFramework"
};

private void SetFormattersEnvironmentVariable(Dictionary<string, string> parameters, string testRunDirectory)
{
string effectiveTestRunDirectory = null;
if (parameters != null && parameters.TryGetValue("testRunDirectory", out var paramTestRunDir) && !string.IsNullOrEmpty(paramTestRunDir))
{
effectiveTestRunDirectory = paramTestRunDir;
}
else if (!string.IsNullOrEmpty(testRunDirectory))
{
effectiveTestRunDirectory = testRunDirectory;
}

if (parameters != null)
{
var outputFilePath = parameters.TryGetValue("outputFilePath", out string outputFilePathParameter) ? outputFilePathParameter : null;
var alternateFilePath = parameters.TryGetValue("LogFileName", out string alternateFilePathParameter) ? alternateFilePathParameter : null;
outputFilePath ??= alternateFilePath;

if (string.IsNullOrEmpty(FormatterName))
{
return; // No formatter name provided, nothing to do
}
if (!string.IsNullOrEmpty(effectiveTestRunDirectory) && !string.IsNullOrEmpty(outputFilePath))
{
outputFilePath = Path.Combine(effectiveTestRunDirectory, outputFilePath);
}
else if (!string.IsNullOrEmpty(effectiveTestRunDirectory) && string.IsNullOrEmpty(outputFilePath))
{
outputFilePath = effectiveTestRunDirectory;
}

// Use proper JSON serialization to handle escaping correctly
var configObject = new
{
formatters = new Dictionary<string, object>
{
[FormatterName] = new
{
outputFilePath
}
}
};

var json = JsonSerializer.Serialize(configObject);

if (_originalEnvValue == null)
{
_originalEnvValue = Environment.GetEnvironmentVariable($"REQNROLL_FORMATTERS_LOGGER_{FormatterName}");
}
Environment.SetEnvironmentVariable($"REQNROLL_FORMATTERS_LOGGER_{FormatterName}", json);
_envSetByLogger = true;
}
}

private void SubscribeToTestRunComplete(TestLoggerEvents events)
{
if (!_subscribed && events != null)
{
events.TestRunComplete += (_, _) =>
{
if (_envSetByLogger)
{
Environment.SetEnvironmentVariable($"REQNROLL_FORMATTERS_LOGGER_{FormatterName}", _originalEnvValue);
_envSetByLogger = false;
}
};
_subscribed = true;
}
}

public void Initialize(TestLoggerEvents events, Dictionary<string, string> parameters)
{
SubscribeToTestRunComplete(events);
IsInitialized = true;
Parameters = parameters;
SetFormattersEnvironmentVariable(parameters, TestRunDirectory);
}

public void Initialize(TestLoggerEvents events, string testRunDirectory)
{
SubscribeToTestRunComplete(events);
TestRunDirectory = testRunDirectory;
IsInitialized = true;
SetFormattersEnvironmentVariable(Parameters, testRunDirectory);
}
}
13 changes: 13 additions & 0 deletions Reqnroll.MessagesLogger.TestLogger/HtmlFormatterLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel;

namespace Reqnroll.FormatterTestLogger;

[FriendlyName("html-formatter")]
[ExtensionUri("logger://formatterhtmllogger")]
public class HtmlFormatterLogger : FormatterLoggerBase
{
public HtmlFormatterLogger()
{
FormatterName = "html";
}
}
13 changes: 13 additions & 0 deletions Reqnroll.MessagesLogger.TestLogger/MessageFormatterLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel;

namespace Reqnroll.FormatterTestLogger;

[FriendlyName("message-formatter")]
[ExtensionUri("logger://formattermessagelogger")]
public class MessageFormatterLogger : FormatterLoggerBase
{
public MessageFormatterLogger()
{
FormatterName = "message";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Reqnroll.FormatterTestLogger</AssemblyName>
<PackageId>Reqnroll.FormatterTestLogger</PackageId>
<AssemblyOriginatorKeyFile>$(Reqnroll_KeyFile)</AssemblyOriginatorKeyFile>
<SignAssembly>$(Reqnroll_EnableStrongNameSigning)</SignAssembly>
<PublicSign>$(Reqnroll_PublicSign)</PublicSign>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="17.14.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions Reqnroll.sln
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reqnroll.TUnit.Generator.Re
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reqnroll.Formatters.Tests", "Tests\Reqnroll.Formatters.Tests\Reqnroll.Formatters.Tests.csproj", "{90C5D62C-CE31-2F54-BEF9-F0DA12C8CE19}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reqnroll.FormatterTestLogger", "Reqnroll.MessagesLogger.TestLogger\Reqnroll.FormatterTestLogger.csproj", "{DA774F90-8287-4364-A736-B3AD94404A5B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -269,6 +271,10 @@ Global
{90C5D62C-CE31-2F54-BEF9-F0DA12C8CE19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90C5D62C-CE31-2F54-BEF9-F0DA12C8CE19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90C5D62C-CE31-2F54-BEF9-F0DA12C8CE19}.Release|Any CPU.Build.0 = Release|Any CPU
{DA774F90-8287-4364-A736-B3AD94404A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA774F90-8287-4364-A736-B3AD94404A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA774F90-8287-4364-A736-B3AD94404A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA774F90-8287-4364-A736-B3AD94404A5B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
17 changes: 16 additions & 1 deletion Reqnroll/EnvironmentAccess/EnvironmentWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using Reqnroll.CommonModels;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Reqnroll.EnvironmentAccess
{
Expand Down Expand Up @@ -36,5 +39,17 @@ public void SetEnvironmentVariable(string name, string value)
}

public string GetCurrentDirectory() => Environment.CurrentDirectory;

public IDictionary<string, string> GetEnvironmentVariables(string prefix)
{
if (string.IsNullOrEmpty(prefix))
throw new ArgumentException("Argument cannot be null or empty", nameof(prefix));

return Environment.GetEnvironmentVariables()
.OfType<DictionaryEntry>()
.Select(e => (Key: e.Key?.ToString() ?? "", Value: e.Value?.ToString() ?? ""))
.Where(e => e.Key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
.ToDictionary(e => e.Key, e => e.Value);
}
}
}
3 changes: 3 additions & 0 deletions Reqnroll/EnvironmentAccess/IEnvironmentWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Reqnroll.CommonModels;
using System.Collections.Generic;

namespace Reqnroll.EnvironmentAccess
{
Expand All @@ -10,6 +11,8 @@ public interface IEnvironmentWrapper

IResult<string> GetEnvironmentVariable(string name);

IDictionary<string,string> GetEnvironmentVariables(string prefix);

void SetEnvironmentVariable(string name, string value);

string GetCurrentDirectory();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,38 @@ public class EnvironmentConfigurationResolver : FormattersConfigurationResolverB
{
private readonly IEnvironmentWrapper _environmentWrapper;
private readonly IFormatterLog _log;
private readonly string _environmentVariableName;

public EnvironmentConfigurationResolver(
IEnvironmentWrapper environmentWrapper,
IFormatterLog log = null)
{
_environmentWrapper = environmentWrapper;
_log = log;
_environmentVariableName = FormattersConfigurationConstants.REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE;
}

internal EnvironmentConfigurationResolver(
IEnvironmentWrapper environmentWrapper,
string environmentVariableName,
IFormatterLog log = null)
{
_environmentWrapper = environmentWrapper ?? throw new ArgumentNullException(nameof(environmentWrapper));
_log = log;
_environmentVariableName = environmentVariableName ?? throw new ArgumentNullException(nameof(environmentVariableName));
}

protected override JsonDocument GetJsonDocument()
{
try
{
var formatters = _environmentWrapper.GetEnvironmentVariable(FormattersConfigurationConstants.REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE);
var formatters = _environmentWrapper.GetEnvironmentVariable(_environmentVariableName);

if (formatters is Success<string> formattersSuccess)
{
if (string.IsNullOrWhiteSpace(formattersSuccess.Result))
{
_log?.WriteMessage($"Environment variable {FormattersConfigurationConstants.REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE} is empty");
_log?.WriteMessage($"Environment variable {_environmentVariableName} is empty");
return null;
}

Expand All @@ -43,12 +55,12 @@ protected override JsonDocument GetJsonDocument()
}
catch (JsonException ex)
{
_log?.WriteMessage($"Failed to parse JSON from environment variable {FormattersConfigurationConstants.REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE}: {ex.Message}");
_log?.WriteMessage($"Failed to parse JSON from environment variable {_environmentVariableName}: {ex.Message}");
}
}
else if (formatters is Failure<string> failure)
{
_log?.WriteMessage($"Could not retrieve environment variable {FormattersConfigurationConstants.REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE}: {failure.Description}");
_log?.WriteMessage($"Could not retrieve environment variable {_environmentVariableName}: {failure.Description}");
}
}
catch (Exception ex) when (ex is not JsonException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public static class FormattersConfigurationConstants
{
public const string REQNROLL_FORMATTERS_ENVIRONMENT_VARIABLE = "REQNROLL_FORMATTERS";
public const string REQNROLL_FORMATTERS_DISABLED_ENVIRONMENT_VARIABLE = "REQNROLL_FORMATTERS_DISABLED";
public const string REQNROLL_FORMATTERS_LOGGER_ENVIRONMENT_VARIABLE_PREFIX = "REQNROLL_FORMATTERS_LOGGER_";
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ namespace Reqnroll.Formatters.Configuration;
/// </summary>
public class FormattersConfigurationProvider : IFormattersConfigurationProvider
{
private readonly IList<IFormattersConfigurationResolverBase> _resolvers;
private readonly List<IFormattersConfigurationResolverBase> _resolvers;
private readonly Lazy<FormattersConfiguration> _resolvedConfiguration;
private readonly IFormattersConfigurationDisableOverrideProvider _envVariableDisableFlagProvider;
public bool Enabled => _resolvedConfiguration.Value.Enabled;

public FormattersConfigurationProvider(IDictionary<string, IFormattersConfigurationResolver> resolvers, IFormattersEnvironmentOverrideConfigurationResolver environmentOverrideConfigurationResolver, IFormattersConfigurationDisableOverrideProvider envVariableDisableFlagProvider)
public FormattersConfigurationProvider(IDictionary<string, IFormattersConfigurationResolver> resolvers, IFormattersEnvironmentOverrideConfigurationResolver environmentOverrideConfigurationResolver, IFormattersLoggerConfigurationProvider formattersLoggerConfigurationProvider, IFormattersConfigurationDisableOverrideProvider envVariableDisableFlagProvider)
{
var fileResolver = resolvers["fileBasedResolver"];
_resolvers = [fileResolver, environmentOverrideConfigurationResolver];
_resolvers.AddRange(formattersLoggerConfigurationProvider.GetFormattersConfigurationResolvers());
_resolvedConfiguration = new Lazy<FormattersConfiguration>(ResolveConfiguration);
_envVariableDisableFlagProvider = envVariableDisableFlagProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ protected virtual void ProcessJsonDocument(JsonDocument jsonDocument, Dictionary
foreach(JsonProperty formatterProperty in formatters.EnumerateObject())
{
var configValues = new Dictionary<string, object>();

if (formatterProperty.Value.ValueKind == JsonValueKind.Object)
{
foreach (JsonProperty configProperty in formatterProperty.Value.EnumerateObject())
{
configValues.Add(configProperty.Name, GetConfigValue(configProperty.Value));
configValues.Add(configProperty.Name, GetConfigValue(configProperty.Value));
}
}

result.Add(formatterProperty.Name, configValues);
}
}
}

private object GetConfigValue(JsonElement valueElement)
{
switch (valueElement.ValueKind)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Reqnroll.CommonModels;
using Reqnroll.EnvironmentAccess;
using Reqnroll.Formatters.RuntimeSupport;
using System;
using System.Collections.Generic;

namespace Reqnroll.Formatters.Configuration;

public class FormattersLoggerConfigurationProvider : IFormattersLoggerConfigurationProvider
{
private readonly IEnvironmentWrapper _environmentWrapper;
private readonly IFormatterLog _log;

public FormattersLoggerConfigurationProvider(IEnvironmentWrapper environmentWrapper, IFormatterLog log = null)
{
_environmentWrapper = environmentWrapper ?? throw new ArgumentNullException(nameof(environmentWrapper));
_log = log;
}

public IEnumerable<IFormattersEnvironmentOverrideConfigurationResolver> GetFormattersConfigurationResolvers()
{
var formattersConfigurationResolvers = new List<IFormattersEnvironmentOverrideConfigurationResolver>();

var listOfFormattersResult = _environmentWrapper.GetEnvironmentVariables(FormattersConfigurationConstants.REQNROLL_FORMATTERS_LOGGER_ENVIRONMENT_VARIABLE_PREFIX);
foreach (var formatterEnvironmentVariable in listOfFormattersResult)
{
var resolver = new EnvironmentConfigurationResolver(_environmentWrapper, formatterEnvironmentVariable.Key, _log);
formattersConfigurationResolvers.Add(resolver);
}

return formattersConfigurationResolvers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;

namespace Reqnroll.Formatters.Configuration;

/// <summary>
/// Implementation of this interface provides a set of IFormattersConfigurationResolver instances each of which
/// is responsible for resolving the configuration of one formatter that has been configured via the --logger mechanism.
/// </summary>
public interface IFormattersLoggerConfigurationProvider
{
IEnumerable<IFormattersEnvironmentOverrideConfigurationResolver> GetFormattersConfigurationResolvers();
}
1 change: 1 addition & 0 deletions Reqnroll/Infrastructure/DefaultDependencyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public virtual void RegisterGlobalContainerDefaults(ObjectContainer container)
container.RegisterTypeAs<FormattersDisabledOverrideProvider, IFormattersConfigurationDisableOverrideProvider>();
container.RegisterTypeAs<FileBasedConfigurationResolver, IFormattersConfigurationResolver>("fileBasedResolver");
container.RegisterTypeAs<EnvironmentConfigurationResolver, IFormattersEnvironmentOverrideConfigurationResolver>();
container.RegisterTypeAs<FormattersLoggerConfigurationProvider, IFormattersLoggerConfigurationProvider>();
container.RegisterTypeAs<FormattersConfigurationProvider, IFormattersConfigurationProvider>();
container.RegisterTypeAs<MessageFormatter, ICucumberMessageFormatter>("message");
container.RegisterTypeAs<HtmlFormatter, ICucumberMessageFormatter>("html");
Expand Down
1 change: 1 addition & 0 deletions Reqnroll/Reqnroll.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Reqnroll.MessagesLogger.TestLogger\Reqnroll.FormatterTestLogger.csproj" />
<ProjectReference Include="..\Reqnroll.Utils\Reqnroll.Utils.csproj" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions Reqnroll/Reqnroll.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<file src="bin\$config$\netstandard2.0\Reqnroll.pdb" target="lib\netstandard2.0" />
<file src="bin\$config$\netstandard2.0\Reqnroll.xml" target="lib\netstandard2.0" />
<file src="bin\$config$\netstandard2.0\Reqnroll.Utils.*" target="lib\netstandard2.0" />
<file src="bin\$config$\netstandard2.0\Reqnroll.FormatterTestLogger.*" target="lib\netstandard2.0" />
<file src="Reqnroll.ExternalAnnotations.xml" target="lib\netstandard2.0" />

<file src="$SolutionDir$\Licenses\*" target="Licenses" />
Expand Down
Loading
Loading