Skip to content

Commit 4c7efc0

Browse files
authored
Add an optional experimental feature that allows the VS extension to let the the discovered bindings written out to a file. (#771)
* VsxBindingFormatter: An internal formatter that writes the discovered bindings to a JSON file for the Visual Studio extension. * Save binding registry directly * remove VsxBindingFormatter * change visibility * ignore writing out nulls
1 parent 6723982 commit 4c7efc0

5 files changed

Lines changed: 71 additions & 11 deletions

File tree

Reqnroll/Bindings/Discovery/RuntimeBindingRegistryBuilder.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Reflection;
1+
using Reqnroll.Bindings.Provider;
52
using Reqnroll.Bindings.Reflection;
63
using Reqnroll.Configuration;
74
using Reqnroll.Infrastructure;
85
using Reqnroll.PlatformCompatibility;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Reflection;
910

1011
namespace Reqnroll.Bindings.Discovery
1112
{
@@ -15,13 +16,15 @@ public class RuntimeBindingRegistryBuilder : IRuntimeBindingRegistryBuilder
1516
private readonly IReqnrollAttributesFilter _reqnrollAttributesFilter;
1617
private readonly IBindingAssemblyLoader _bindingAssemblyLoader;
1718
private readonly ReqnrollConfiguration _reqnrollConfiguration;
19+
private readonly IBindingProviderService _bindingProviderService;
1820

19-
public RuntimeBindingRegistryBuilder(IRuntimeBindingSourceProcessor bindingSourceProcessor, IReqnrollAttributesFilter reqnrollAttributesFilter, IBindingAssemblyLoader bindingAssemblyLoader, ReqnrollConfiguration reqnrollConfiguration)
21+
public RuntimeBindingRegistryBuilder(IRuntimeBindingSourceProcessor bindingSourceProcessor, IReqnrollAttributesFilter reqnrollAttributesFilter, IBindingAssemblyLoader bindingAssemblyLoader, ReqnrollConfiguration reqnrollConfiguration, IBindingProviderService bindingProviderService)
2022
{
2123
_bindingSourceProcessor = bindingSourceProcessor;
2224
_reqnrollAttributesFilter = reqnrollAttributesFilter;
2325
_bindingAssemblyLoader = bindingAssemblyLoader;
2426
_reqnrollConfiguration = reqnrollConfiguration;
27+
_bindingProviderService = bindingProviderService;
2528
}
2629

2730
public Assembly[] GetBindingAssemblies(Assembly testAssembly)
@@ -63,6 +66,7 @@ protected virtual Type[] GetAssemblyTypes(Assembly assembly, out string[] typeLo
6366
public void BuildingCompleted()
6467
{
6568
_bindingSourceProcessor.BuildingCompleted();
69+
_bindingProviderService.OnBindingRegistryBuildingCompleted();
6670
}
6771

6872
//internal - for testing

Reqnroll/Bindings/Provider/BindingJsonSourceGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
namespace Reqnroll.Bindings.Provider;
55

66
[JsonSourceGenerationOptions(
7-
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, // We specifiy the names explicitly
8-
UseStringEnumConverter = true)] // use strings instead of numbers for enums
7+
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, // We specify the names explicitly
8+
UseStringEnumConverter = true, // use strings instead of numbers for enums
9+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // Ignore null properties
10+
)]
911
[JsonSerializable(typeof(BindingData))]
1012
internal partial class BindingJsonSourceGenerator : JsonSerializerContext
1113
{

Reqnroll/Bindings/Provider/BindingProviderService.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,76 @@
22
using Reqnroll.Bindings.Provider.Data;
33
using Reqnroll.Bindings.Reflection;
44
using Reqnroll.BoDi;
5+
using Reqnroll.CommonModels;
56
using Reqnroll.Configuration;
7+
using Reqnroll.EnvironmentAccess;
68
using Reqnroll.Formatters.Configuration;
79
using Reqnroll.Infrastructure;
10+
using System;
11+
using System.Diagnostics;
12+
using System.IO;
813
using System.Linq;
914
using System.Reflection;
15+
using System.Text;
1016
using System.Text.Json;
1117

1218
namespace Reqnroll.Bindings.Provider;
1319

14-
public class BindingProviderService
20+
public interface IBindingProviderService
1521
{
22+
void OnBindingRegistryBuildingCompleted();
23+
}
24+
25+
public class BindingProviderService(IBindingRegistry bindingRegistry, ITestAssemblyProvider testAssemblyProvider, IEnvironmentWrapper environmentWrapper) : IBindingProviderService
26+
{
27+
/// <summary>
28+
/// Invoked by the Visual Studio extension connectors. Do not remove or change the signature.
29+
/// </summary>
1630
public static string DiscoverBindings(Assembly testAssembly, string jsonConfiguration)
1731
{
1832
if (string.IsNullOrWhiteSpace(jsonConfiguration)) jsonConfiguration = "{}";
1933
var globalContainer = CreateGlobalContainer(testAssembly, jsonConfiguration);
2034
var bindingRegistryBuilder = globalContainer.Resolve<IRuntimeBindingRegistryBuilder>();
2135
BuildBindingRegistry(testAssembly, bindingRegistryBuilder);
2236
var bindingRegistry = globalContainer.Resolve<IBindingRegistry>();
37+
return GetDiscoveredBindingsFromRegistry(bindingRegistry, testAssembly);
38+
}
39+
40+
private const string REQNROLL_BINDING_OUTPUT_ENVIRONMENT_VARIABLE = "REQNROLL_BINDING_OUTPUT";
41+
42+
public void OnBindingRegistryBuildingCompleted()
43+
{
44+
// Experimental feature: This code allows the Visual Studio extension to instruct saving the discovered bindings to a file
45+
// using an environment variable during the test run.
46+
// This might be used as an alternative to invoking the `DiscoverBindings` method, because that needs the test assembly to be
47+
// fully loaded and this is sometimes problematic.
48+
49+
if (environmentWrapper.GetEnvironmentVariable(REQNROLL_BINDING_OUTPUT_ENVIRONMENT_VARIABLE) is ISuccess<string> environmentVariable &&
50+
!string.IsNullOrWhiteSpace(environmentVariable.Result))
51+
{
52+
var outputFilePath = environmentVariable.Result.Equals("true", StringComparison.OrdinalIgnoreCase) ? "reqnroll_bindings.json" :
53+
environmentVariable.Result.Trim();
54+
55+
outputFilePath = Path.GetFullPath(outputFilePath);
56+
TrySaveBindings(outputFilePath);
57+
}
58+
}
59+
60+
private void TrySaveBindings(string outputFilePath)
61+
{
62+
try
63+
{
64+
var bindingsJson = GetDiscoveredBindingsFromRegistry(bindingRegistry, testAssemblyProvider.TestAssembly);
65+
File.WriteAllText(outputFilePath, bindingsJson, Encoding.UTF8);
66+
}
67+
catch (Exception ex)
68+
{
69+
Debug.WriteLine(ex, "Writing bindings failed.");
70+
}
71+
}
72+
73+
private static string GetDiscoveredBindingsFromRegistry(IBindingRegistry bindingRegistry, Assembly testAssembly)
74+
{
2375
var resultData = ParseDiscoveryResult(bindingRegistry, testAssembly);
2476
var jsonString = JsonSerializer.Serialize(resultData, BindingJsonSourceGenerator.Default.BindingData);
2577
return jsonString;
@@ -145,5 +197,4 @@ private static IObjectContainer CreateGlobalContainer(Assembly testAssembly, str
145197
var configurationProvider = new JsonStringRuntimeConfigurationProvider(jsonConfiguration);
146198
return containerBuilder.CreateGlobalContainer(testAssembly, configurationProvider);
147199
}
148-
149200
}

Reqnroll/Infrastructure/DefaultDependencyProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using System;
21
using Gherkin.CucumberMessages;
32
using Reqnroll.Analytics;
43
using Reqnroll.Analytics.AppInsights;
54
using Reqnroll.Analytics.UserId;
65
using Reqnroll.Bindings;
76
using Reqnroll.Bindings.CucumberExpressions;
87
using Reqnroll.Bindings.Discovery;
8+
using Reqnroll.Bindings.Provider;
99
using Reqnroll.BindingSkeletons;
1010
using Reqnroll.BoDi;
1111
using Reqnroll.Configuration;
@@ -27,6 +27,7 @@
2727
using Reqnroll.Time;
2828
using Reqnroll.Tracing;
2929
using Reqnroll.Utils;
30+
using System;
3031

3132
namespace Reqnroll.Infrastructure
3233
{
@@ -62,6 +63,7 @@ public virtual void RegisterGlobalContainerDefaults(ObjectContainer container)
6263
container.RegisterTypeAs<BindingInvoker, IAsyncBindingInvoker>();
6364
container.RegisterTypeAs<BindingDelegateInvoker, IBindingDelegateInvoker>();
6465
container.RegisterTypeAs<TestObjectResolver, ITestObjectResolver>();
66+
container.RegisterTypeAs<BindingProviderService, IBindingProviderService>();
6567

6668
container.RegisterTypeAs<StepDefinitionSkeletonProvider, IStepDefinitionSkeletonProvider>();
6769
container.RegisterTypeAs<DefaultSkeletonTemplateProvider, ISkeletonTemplateProvider>();

Tests/Reqnroll.RuntimeTests/RuntimeBindingRegistryBuilderTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Xunit;
66
using Reqnroll.Bindings;
77
using Reqnroll.Bindings.Discovery;
8+
using Reqnroll.Bindings.Provider;
89
using Reqnroll.Configuration;
910
using Reqnroll.Infrastructure;
1011

@@ -226,7 +227,7 @@ public void GivenAndWhen()
226227

227228
private RuntimeBindingRegistryBuilder CreateSut()
228229
{
229-
return new RuntimeBindingRegistryBuilder(bindingSourceProcessorStub, new ReqnrollAttributesFilter(), new Mock<IBindingAssemblyLoader>().Object, ConfigurationLoader.GetDefault());
230+
return new RuntimeBindingRegistryBuilder(bindingSourceProcessorStub, new ReqnrollAttributesFilter(), new Mock<IBindingAssemblyLoader>().Object, ConfigurationLoader.GetDefault(), new Mock<IBindingProviderService>().Object);
230231
}
231232

232233
[Fact]

0 commit comments

Comments
 (0)