Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Specflow.Internal.Json with System.Text.Json #373

Merged
merged 9 commits into from
Jan 10, 2025
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

## Bug fixes:

* Fix: Replace deprecated dependency `Specflow.Internal.Json` with `System.Text.Json`. The dependency was used for laoding `reqnroll.json`, for Visual Studio integration and for telemetry. (#373)

*Contributors of this release (in alphabetical order):* @clrudolphi, @obligaron, @olegKoshmeliuk

# v2.2.1 - 2024-11-08
Expand Down
24 changes: 22 additions & 2 deletions Reqnroll.Tools.MsBuild.Generation/AssemblyResolveLogger.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;

Expand All @@ -8,10 +9,12 @@ public sealed class AssemblyResolveLogger : IAssemblyResolveLogger
{
private readonly ITaskLoggingWrapper _taskLoggingWrapper;
private bool _isDisposed;
private readonly string _taskFolder;

public AssemblyResolveLogger(ITaskLoggingWrapper taskLoggingWrapper)
{
_taskLoggingWrapper = taskLoggingWrapper;
_taskFolder = Path.GetDirectoryName(typeof(AssemblyResolveLogger).Assembly.Location);
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}

Expand All @@ -22,13 +25,30 @@ public AssemblyResolveLogger(ITaskLoggingWrapper taskLoggingWrapper)

public Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
_taskLoggingWrapper.LogMessage(args.Name);
_taskLoggingWrapper.LogMessage($"Resolving {args.Name}");

try
{
var requestedAssemblyName = new AssemblyName(args.Name);

var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == requestedAssemblyName.Name);
if (loadedAssembly != null) return loadedAssembly;
if (loadedAssembly != null)
{
_taskLoggingWrapper.LogMessage($" Loading {args.Name} from loaded assembly ('{loadedAssembly.FullName}')");
return loadedAssembly;
}

if (_taskFolder != null)
{
var assemblyPath = Path.Combine(_taskFolder, requestedAssemblyName.Name + ".dll");
if (File.Exists(assemblyPath))
{
_taskLoggingWrapper.LogMessage($" Loading {args.Name} from {assemblyPath}");
return Assembly.LoadFrom(assemblyPath);
}
}

_taskLoggingWrapper.LogMessage($" {args.Name} is not in folder {_taskFolder}");
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,32 @@ public IResult<IReadOnlyCollection<ITaskItem>> Execute()
_processInfoDumper.DumpProcessInfo();
_taskLoggingWrapper.LogMessage("Starting GenerateFeatureFileCodeBehind");

var reqnrollProject = _reqnrollProjectProvider.GetReqnrollProject();

using (var generatorContainer = _wrappedGeneratorContainerBuilder.BuildGeneratorContainer(
reqnrollProject.ProjectSettings.ConfigurationHolder,
reqnrollProject.ProjectSettings,
_reqnrollProjectInfo.GeneratorPlugins,
_rootObjectContainer))
try
{
var projectCodeBehindGenerator = generatorContainer.Resolve<IProjectCodeBehindGenerator>();
var reqnrollProject = _reqnrollProjectProvider.GetReqnrollProject();

try
{
_ = Task.Run(_msbuildTaskAnalyticsTransmitter.TryTransmitProjectCompilingEventAsync);
using var generatorContainer = _wrappedGeneratorContainerBuilder.BuildGeneratorContainer(
reqnrollProject.ProjectSettings.ConfigurationHolder,
reqnrollProject.ProjectSettings,
_reqnrollProjectInfo.GeneratorPlugins,
_rootObjectContainer);
var projectCodeBehindGenerator = generatorContainer.Resolve<IProjectCodeBehindGenerator>();

var returnValue = projectCodeBehindGenerator.GenerateCodeBehindFilesForProject();
_ = Task.Run(_msbuildTaskAnalyticsTransmitter.TryTransmitProjectCompilingEventAsync);

if (_taskLoggingWrapper.HasLoggedErrors())
{
return Result<IReadOnlyCollection<ITaskItem>>.Failure("Feature file code-behind generation has failed with errors.");
}
var returnValue = projectCodeBehindGenerator.GenerateCodeBehindFilesForProject();

return Result.Success(returnValue);
}
catch (Exception e)
if (_taskLoggingWrapper.HasLoggedErrors())
{
_exceptionTaskLogger.LogException(e);
return Result<IReadOnlyCollection<ITaskItem>>.Failure(e);
return Result<IReadOnlyCollection<ITaskItem>>.Failure("Feature file code-behind generation has failed with errors.");
}

return Result.Success(returnValue);
}
catch (Exception e)
{
_exceptionTaskLogger.LogException(e);
return Result<IReadOnlyCollection<ITaskItem>>.Failure(e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
<file src="buildMultiTargeting\**\*" target="buildMultiTargeting" />

<file src="bin\$config$\net462\*.dll" target="tasks\$Reqnroll_FullFramework_Tools_TFM$" />
<file src="bin\$config$\netstandard2.0\*.dll"
exclude="bin\$config$\netstandard2.0\System.*;bin\$config$\netstandard2.0\Microsoft.*"
target="tasks\$Reqnroll_Core_Tools_TFM$" />
<file src="bin\$config$\netstandard2.0\*.dll" target="tasks\$Reqnroll_Core_Tools_TFM$" />
<file src="bin\$config$\netstandard2.0\*.deps.json" target="tasks\$Reqnroll_Core_Tools_TFM$" />

<file src="$SolutionDir$\Licenses\**\*" target="licenses" />
Expand Down
5 changes: 2 additions & 3 deletions Reqnroll/Analytics/AppInsights/AppInsightsEventSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Text;
using SpecFlow.Internal.Json;
using System.Text.Json;

namespace Reqnroll.Analytics.AppInsights
{
Expand All @@ -8,7 +7,7 @@ public class AppInsightsEventSerializer : IAppInsightsEventSerializer
public byte[] SerializeAnalyticsEvent(IAnalyticsEvent analyticsEvent, string instrumentationKey)
{
var eventTelemetry = new AppInsightsEventTelemetry(analyticsEvent, instrumentationKey);
return Encoding.UTF8.GetBytes(eventTelemetry.ToJson());
return JsonSerializer.SerializeToUtf8Bytes(eventTelemetry, AppInsightsEventTelemetryJsonSourceGenerator.Default.AppInsightsEventTelemetry);
}
}
}
22 changes: 11 additions & 11 deletions Reqnroll/Analytics/AppInsights/AppInsightsEventTelemetry.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace Reqnroll.Analytics.AppInsights
{
Expand All @@ -9,19 +9,19 @@ namespace Reqnroll.Analytics.AppInsights
/// </summary>
public class AppInsightsEventTelemetry
{
[DataMember(Name = "name")]
[JsonPropertyName("name")]
public string DataTypeName { get; set; }

[DataMember(Name = "time")]
[JsonPropertyName("time")]
public string EventDateTime { get; set; }

[DataMember(Name = "iKey")]
[JsonPropertyName("iKey")]
public string InstrumentationKey { get; set; }

[DataMember(Name = "data")]
[JsonPropertyName("data")]
public TelemetryData TelemetryData { get; set; }

[DataMember(Name = "tags")]
[JsonPropertyName("tags")]
public Dictionary<string, string> TelemetryTags { get; set; }

private const string DefaultValue = "undefined";
Expand Down Expand Up @@ -72,20 +72,20 @@ public AppInsightsEventTelemetry(IAnalyticsEvent analyticsEvent, string instrume
}
public class TelemetryData
{
[DataMember(Name = "baseType")]
[JsonPropertyName("baseType")]
public string ItemTypeName { get; set; }

[DataMember(Name = "baseData")]
[JsonPropertyName("baseData")]
public TelemetryDataItem TelemetryDataItem { get; set; }
}

public class TelemetryDataItem
{
[DataMember(Name = "ver")]
[JsonPropertyName("ver")]
public string EndPointSchemaVersion => "2";
[DataMember(Name = "name")]
[JsonPropertyName("name")]
public string EventName { get; set; }
[DataMember(Name = "properties")]
[JsonPropertyName("properties")]
public Dictionary<string, string> Properties { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;

namespace Reqnroll.Analytics.AppInsights
{
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, // We specifiy the names explicitly
UseStringEnumConverter = true)] // use strings instead of numbers for enums
[JsonSerializable(typeof(AppInsightsEventTelemetry))]
internal partial class AppInsightsEventTelemetryJsonSourceGenerator : JsonSerializerContext
{

}
}
13 changes: 13 additions & 0 deletions Reqnroll/Bindings/Provider/BindingJsonSourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Reqnroll.Bindings.Provider.Data;
using System.Text.Json.Serialization;

namespace Reqnroll.Bindings.Provider;

[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, // We specifiy the names explicitly
UseStringEnumConverter = true)] // use strings instead of numbers for enums
[JsonSerializable(typeof(BindingData))]
internal partial class BindingJsonSourceGenerator : JsonSerializerContext
{

}
14 changes: 7 additions & 7 deletions Reqnroll/Bindings/Provider/BindingProviderService.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System;
using System.Linq;
using System.Reflection;
using Reqnroll.BoDi;
using Reqnroll.Bindings.Discovery;
using Reqnroll.Bindings.Discovery;
using Reqnroll.Bindings.Provider.Data;
using Reqnroll.Bindings.Reflection;
using Reqnroll.BoDi;
using Reqnroll.Configuration;
using Reqnroll.Infrastructure;
using SpecFlow.Internal.Json;
using System.Linq;
using System.Reflection;
using System.Text.Json;

namespace Reqnroll.Bindings.Provider;

public class BindingProviderService
{
public static string DiscoverBindings(Assembly testAssembly, string jsonConfiguration)
Expand All @@ -20,7 +20,7 @@ public static string DiscoverBindings(Assembly testAssembly, string jsonConfigur
BuildBindingRegistry(testAssembly, bindingRegistryBuilder);
var bindingRegistry = globalContainer.Resolve<IBindingRegistry>();
var resultData = ParseDiscoveryResult(bindingRegistry, testAssembly);
var jsonString = resultData.ToJson();
var jsonString = JsonSerializer.Serialize(resultData, BindingJsonSourceGenerator.Default.BindingData);
return jsonString;
}

Expand Down
1 change: 1 addition & 0 deletions Reqnroll/Configuration/ConfigDefaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static class ConfigDefaults
public const bool TraceSuccessfulSteps = true;
public const bool TraceTimings = false;
public const string MinTracedDuration = "0:0:0.1";
public static TimeSpan MinTracedDurationAsTimeSpan { get; } = TimeSpan.Parse(MinTracedDuration);
public const StepDefinitionSkeletonStyle StepDefinitionSkeletonStyle = Reqnroll.BindingSkeletons.StepDefinitionSkeletonStyle.CucumberExpressionAttribute;
public const ObsoleteBehavior ObsoleteBehavior = Configuration.ObsoleteBehavior.Warn;
public const bool ColoredOutput = false;
Expand Down
4 changes: 1 addition & 3 deletions Reqnroll/Configuration/ConfigurationLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ConfigurationLoader(IReqnrollJsonLocator reqnrollJsonLocator)

private static bool DefaultTraceSuccessfulSteps => ConfigDefaults.TraceSuccessfulSteps;
private static bool DefaultTraceTimings => ConfigDefaults.TraceTimings;
private static TimeSpan DefaultMinTracedDuration => TimeSpan.Parse(ConfigDefaults.MinTracedDuration);
private static TimeSpan DefaultMinTracedDuration => ConfigDefaults.MinTracedDurationAsTimeSpan;

private static StepDefinitionSkeletonStyle DefaultStepDefinitionSkeletonStyle => ConfigDefaults.StepDefinitionSkeletonStyle;

Expand Down Expand Up @@ -146,7 +146,5 @@ private ReqnrollConfiguration LoadJson(ReqnrollConfiguration reqnrollConfigurati
{
return _jsonConfigurationLoader.LoadJson(reqnrollConfiguration, jsonContent);
}


}
}
7 changes: 2 additions & 5 deletions Reqnroll/Configuration/JsonConfig/BindingCultureElement.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System.Runtime.Serialization;

//using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace Reqnroll.Configuration.JsonConfig
{
// legacy config
public class BindingCultureElement
{
// legacy config
//[JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)]
[DataMember(Name="name")]
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
24 changes: 24 additions & 0 deletions Reqnroll/Configuration/JsonConfig/CustomTimeSpanConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Reqnroll.Configuration.JsonConfig
{
/// <summary>
/// Custom <see cref="TimeSpan"/> converter to stay compatible with old json format/parser
/// </summary>
sealed class CustomTimeSpanConverter : JsonConverter<TimeSpan>
{
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var content = reader.GetString();
return TimeSpan.Parse(content, CultureInfo.InvariantCulture);
}
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
{
var content = value.ToString(null, CultureInfo.InvariantCulture);
writer.WriteStringValue(content);
}
}
}
8 changes: 4 additions & 4 deletions Reqnroll/Configuration/JsonConfig/Dependency.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace Reqnroll.Configuration.JsonConfig
{
public class Dependency
{
[DataMember(Name = "type")]
[JsonPropertyName("type")]
public string ImplementationType { get; set; }
[DataMember(Name = "as")]
[JsonPropertyName("as")]
public string InterfaceType { get; set; }
[DataMember(Name = "name")]
[JsonPropertyName("name")]
public string Name { get; set; }
}
}
18 changes: 6 additions & 12 deletions Reqnroll/Configuration/JsonConfig/GeneratorElement.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace Reqnroll.Configuration.JsonConfig
{
public class GeneratorElement
{
//[JsonProperty("allowDebugGeneratedFiles", DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)]
[DataMember(Name = "allowDebugGeneratedFiles")]
[DefaultValue(ConfigDefaults.AllowDebugGeneratedFiles)]
public bool AllowDebugGeneratedFiles { get; set; }
[JsonPropertyName("allowDebugGeneratedFiles")]
public bool AllowDebugGeneratedFiles { get; set; } = ConfigDefaults.AllowDebugGeneratedFiles;

//[JsonProperty("allowRowTests", DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)]
[DefaultValue(ConfigDefaults.AllowRowTests)]
[DataMember(Name = "allowRowTests")]
public bool AllowRowTests { get; set; }
[JsonPropertyName("allowRowTests")]
public bool AllowRowTests { get; set; } = ConfigDefaults.AllowRowTests;

//[JsonProperty("addNonParallelizableMarkerForTags", NullValueHandling = NullValueHandling.Ignore)]
[DataMember(Name = "addNonParallelizableMarkerForTags")]
[JsonPropertyName("addNonParallelizableMarkerForTags")]
public List<string> AddNonParallelizableMarkerForTags { get; set; }
}
}
Loading
Loading