diff --git a/Reqnroll.Generator/DefaultDependencyProvider.cs b/Reqnroll.Generator/DefaultDependencyProvider.cs index cf9a95e90..7b09ff772 100644 --- a/Reqnroll.Generator/DefaultDependencyProvider.cs +++ b/Reqnroll.Generator/DefaultDependencyProvider.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Configuration; using Reqnroll.BoDi; using Reqnroll.Configuration; using Reqnroll.Generator.Configuration; @@ -21,6 +22,10 @@ public virtual void RegisterDefaults(ObjectContainer container) { container.RegisterTypeAs(); + container.RegisterTypeAs(); + container.RegisterTypeAs(); + + container.RegisterTypeAs(); container.RegisterTypeAs(); container.RegisterTypeAs(); diff --git a/Reqnroll.Generator/Interfaces/FeatureFileInput.cs b/Reqnroll.Generator/Interfaces/FeatureFileInput.cs index a78914000..dc7d68f35 100644 --- a/Reqnroll.Generator/Interfaces/FeatureFileInput.cs +++ b/Reqnroll.Generator/Interfaces/FeatureFileInput.cs @@ -3,16 +3,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - /// - /// - /// - /// Represents the information related to a feature file as an input of the generation - /// - [Serializable] public class FeatureFileInput { /// diff --git a/Reqnroll.Generator/Interfaces/GenerationSettings.cs b/Reqnroll.Generator/Interfaces/GenerationSettings.cs index e3ea2887b..71cd810c4 100644 --- a/Reqnroll.Generator/Interfaces/GenerationSettings.cs +++ b/Reqnroll.Generator/Interfaces/GenerationSettings.cs @@ -2,16 +2,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - /// - /// - /// - /// Settings for test generation - /// - [Serializable] public class GenerationSettings { /// diff --git a/Reqnroll.Generator/Interfaces/ITestGenerator.cs b/Reqnroll.Generator/Interfaces/ITestGenerator.cs index ab403e891..3cfadafae 100644 --- a/Reqnroll.Generator/Interfaces/ITestGenerator.cs +++ b/Reqnroll.Generator/Interfaces/ITestGenerator.cs @@ -3,10 +3,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains public interface ITestGenerator : IDisposable { TestGeneratorResult GenerateTestFile(FeatureFileInput featureFileInput, GenerationSettings settings); diff --git a/Reqnroll.Generator/Interfaces/ITestGeneratorFactory.cs b/Reqnroll.Generator/Interfaces/ITestGeneratorFactory.cs index 5eb042875..e357d55bd 100644 --- a/Reqnroll.Generator/Interfaces/ITestGeneratorFactory.cs +++ b/Reqnroll.Generator/Interfaces/ITestGeneratorFactory.cs @@ -3,10 +3,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization across AppDomains public interface ITestGeneratorFactory { Version GetGeneratorVersion(); diff --git a/Reqnroll.Generator/Interfaces/ProjectPlatformSettings.cs b/Reqnroll.Generator/Interfaces/ProjectPlatformSettings.cs index 3385a5dec..b6329506e 100644 --- a/Reqnroll.Generator/Interfaces/ProjectPlatformSettings.cs +++ b/Reqnroll.Generator/Interfaces/ProjectPlatformSettings.cs @@ -2,11 +2,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization across appdomains - [Serializable] public class ProjectPlatformSettings { /// diff --git a/Reqnroll.Generator/Interfaces/ProjectSettings.cs b/Reqnroll.Generator/Interfaces/ProjectSettings.cs index 62b4969fd..8c6b8c5af 100644 --- a/Reqnroll.Generator/Interfaces/ProjectSettings.cs +++ b/Reqnroll.Generator/Interfaces/ProjectSettings.cs @@ -3,11 +3,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - [Serializable] public class ProjectSettings { /// diff --git a/Reqnroll.Generator/Interfaces/ReqnrollConfigurationHolder.cs b/Reqnroll.Generator/Interfaces/ReqnrollConfigurationHolder.cs index d70ef6bf5..6bac0168b 100644 --- a/Reqnroll.Generator/Interfaces/ReqnrollConfigurationHolder.cs +++ b/Reqnroll.Generator/Interfaces/ReqnrollConfigurationHolder.cs @@ -4,11 +4,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - [Serializable] public class ReqnrollConfigurationHolder : IReqnrollConfigurationHolder { private readonly string xmlString; diff --git a/Reqnroll.Generator/Interfaces/TestGenerationError.cs b/Reqnroll.Generator/Interfaces/TestGenerationError.cs index 610197717..6c1ccbf5a 100644 --- a/Reqnroll.Generator/Interfaces/TestGenerationError.cs +++ b/Reqnroll.Generator/Interfaces/TestGenerationError.cs @@ -4,11 +4,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - [Serializable] public class TestGenerationError { /// diff --git a/Reqnroll.Generator/Interfaces/TestGeneratorFactoryAttribute.cs b/Reqnroll.Generator/Interfaces/TestGeneratorFactoryAttribute.cs index 8f8709f3c..44c21ee19 100644 --- a/Reqnroll.Generator/Interfaces/TestGeneratorFactoryAttribute.cs +++ b/Reqnroll.Generator/Interfaces/TestGeneratorFactoryAttribute.cs @@ -2,10 +2,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains [AttributeUsage(AttributeTargets.Assembly)] public class TestGeneratorFactoryAttribute : Attribute { diff --git a/Reqnroll.Generator/Interfaces/TestGeneratorResult.cs b/Reqnroll.Generator/Interfaces/TestGeneratorResult.cs index 2ace9841e..8b0c301d2 100644 --- a/Reqnroll.Generator/Interfaces/TestGeneratorResult.cs +++ b/Reqnroll.Generator/Interfaces/TestGeneratorResult.cs @@ -4,11 +4,6 @@ namespace Reqnroll.Generator.Interfaces { - /// IMPORTANT - /// This class is used for interop with the Visual Studio Extension - /// DO NOT REMOVE OR RENAME FIELDS! - /// This breaks binary serialization accross appdomains - [Serializable] public class TestGeneratorResult { /// diff --git a/Reqnroll.Tools.MsBuild.Generation/GenerateFeatureFileCodeBehindTaskContainerBuilder.cs b/Reqnroll.Tools.MsBuild.Generation/GenerateFeatureFileCodeBehindTaskContainerBuilder.cs index fafbf4afa..17cbb6bef 100644 --- a/Reqnroll.Tools.MsBuild.Generation/GenerateFeatureFileCodeBehindTaskContainerBuilder.cs +++ b/Reqnroll.Tools.MsBuild.Generation/GenerateFeatureFileCodeBehindTaskContainerBuilder.cs @@ -7,6 +7,8 @@ using Reqnroll.EnvironmentAccess; using Reqnroll.Generator.Configuration; using Reqnroll.Generator.Project; +using Microsoft.Extensions.Configuration; +using System.ComponentModel; namespace Reqnroll.Tools.MsBuild.Generation { @@ -47,6 +49,10 @@ public IObjectContainer BuildRootContainer( objectContainer.RegisterTypeAs(); objectContainer.RegisterTypeAs(); objectContainer.RegisterTypeAs(); + + objectContainer.RegisterTypeAs(); + objectContainer.RegisterTypeAs(); + objectContainer.RegisterTypeAs(); objectContainer.RegisterTypeAs(); objectContainer.RegisterTypeAs(); diff --git a/Reqnroll/Configuration/ConfigurationLoader.cs b/Reqnroll/Configuration/ConfigurationLoader.cs index 469018523..32b82ff7d 100644 --- a/Reqnroll/Configuration/ConfigurationLoader.cs +++ b/Reqnroll/Configuration/ConfigurationLoader.cs @@ -14,12 +14,14 @@ public class ConfigurationLoader : IConfigurationLoader { //private readonly ObjectContainer _objectContainer; private readonly JsonConfigurationLoader _jsonConfigurationLoader; + private readonly IMS_ConfigurationLoader _msConfigurationLoader; private readonly IReqnrollJsonLocator _reqnrollJsonLocator; - public ConfigurationLoader(IReqnrollJsonLocator reqnrollJsonLocator) + public ConfigurationLoader(IReqnrollJsonLocator reqnrollJsonLocator, IMS_ConfigurationLoader mS_ConfigurationLoader) { _reqnrollJsonLocator = reqnrollJsonLocator; _jsonConfigurationLoader = new JsonConfigurationLoader(); + _msConfigurationLoader = mS_ConfigurationLoader; } private static CultureInfo DefaultFeatureLanguage => CultureInfo.GetCultureInfo(ConfigDefaults.FeatureLanguage); @@ -137,14 +139,17 @@ public static ReqnrollConfiguration GetDefault() private ReqnrollConfiguration LoadJson(ReqnrollConfiguration reqnrollConfiguration) { - var jsonContent = File.ReadAllText(_reqnrollJsonLocator.GetReqnrollJsonFilePath()); + var jsonFilePath = _reqnrollJsonLocator.GetReqnrollJsonFilePath(); - return LoadJson(reqnrollConfiguration, jsonContent); + return _msConfigurationLoader.Load(reqnrollConfiguration); } private ReqnrollConfiguration LoadJson(ReqnrollConfiguration reqnrollConfiguration, string jsonContent) { - return _jsonConfigurationLoader.LoadJson(reqnrollConfiguration, jsonContent); + return LoadJson(reqnrollConfiguration); + + // The above ignores the previously read configuration content and uses the MS.Extensions.Configuration library to read the config file +// return _jsonConfigurationLoader.LoadJson(reqnrollConfiguration, jsonContent); } diff --git a/Reqnroll/Configuration/IMS_ConfigurationLoader.cs b/Reqnroll/Configuration/IMS_ConfigurationLoader.cs new file mode 100644 index 000000000..7c7b23907 --- /dev/null +++ b/Reqnroll/Configuration/IMS_ConfigurationLoader.cs @@ -0,0 +1,8 @@ +namespace Reqnroll.Configuration +{ + public interface IMS_ConfigurationLoader + { + public ReqnrollConfiguration Load(ReqnrollConfiguration reqnrollConfiguration); + + } +} \ No newline at end of file diff --git a/Reqnroll/Configuration/JsonConfig/JsonConfigurationLoader.cs b/Reqnroll/Configuration/JsonConfig/JsonConfigurationLoader.cs index ca237d2f8..2fef0f52a 100644 --- a/Reqnroll/Configuration/JsonConfig/JsonConfigurationLoader.cs +++ b/Reqnroll/Configuration/JsonConfig/JsonConfigurationLoader.cs @@ -15,6 +15,14 @@ public ReqnrollConfiguration LoadJson(ReqnrollConfiguration reqnrollConfiguratio var jsonConfig = jsonContent.FromJson(); + var configuration = ApplyJsonConfig(reqnrollConfiguration, jsonConfig); + configuration.ConfigSourceText = jsonContent; + + return configuration; + } + + public static ReqnrollConfiguration ApplyJsonConfig(ReqnrollConfiguration reqnrollConfiguration, JsonConfig jsonConfig) + { var containerRegistrationCollection = reqnrollConfiguration.CustomDependencies; var generatorContainerRegistrationCollection = reqnrollConfiguration.GeneratorCustomDependencies; var featureLanguage = reqnrollConfiguration.FeatureLanguage; @@ -122,10 +130,7 @@ public ReqnrollConfiguration LoadJson(ReqnrollConfiguration reqnrollConfiguratio addNonParallelizableMarkerForTags, obsoleteBehavior, coloredOutput - ) - { - ConfigSourceText = jsonContent - }; + ); } } } diff --git a/Reqnroll/Configuration/MicrosoftConfiguration_RuntimeConfigurationLoader.cs b/Reqnroll/Configuration/MicrosoftConfiguration_RuntimeConfigurationLoader.cs new file mode 100644 index 000000000..d6e6dd786 --- /dev/null +++ b/Reqnroll/Configuration/MicrosoftConfiguration_RuntimeConfigurationLoader.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Reqnroll.Configuration.JsonConfig; +using SpecFlow.Internal.Json; + +namespace Reqnroll.Configuration +{ + public class MicrosoftConfiguration_RuntimeConfigurationLoader : IMS_ConfigurationLoader + { + private readonly string _jsonConfigurationFilePath; + private readonly IConfigurationManager _configurationManager; + + public MicrosoftConfiguration_RuntimeConfigurationLoader(IReqnrollJsonLocator reqnrollJsonLocator, IConfigurationManager configurationManager) + { + if (configurationManager == null) throw new ArgumentNullException(nameof(configurationManager), "Microsoft.Extensions.Configuration configurationManager cannot be null."); + + configurationManager.AddEnvironmentVariables("DOTNET_"); + string envName = configurationManager["Environment"]; + + _jsonConfigurationFilePath = reqnrollJsonLocator.GetReqnrollJsonFilePath(); + + + if (_jsonConfigurationFilePath != null) + { + var pathWithoutExt = Path.Combine(Path.GetDirectoryName(_jsonConfigurationFilePath),Path.GetFileNameWithoutExtension(_jsonConfigurationFilePath)); + var envOverridePath = $"{pathWithoutExt}.{envName}.json"; + configurationManager.AddJsonFile(_jsonConfigurationFilePath, optional: true, reloadOnChange: false); + configurationManager.AddJsonFile(envOverridePath, optional: true, reloadOnChange: false); + } + configurationManager.AddEnvironmentVariables(prefix: "REQNROLL__"); + _configurationManager = configurationManager; + //configurationManager.Build(); + // Build not necessary as IConfigurationManager automatically rebuilds when sources are changed + } + + + public ReqnrollConfiguration Load(ReqnrollConfiguration reqnrollConfiguration) + { + var config = _configurationManager.Get(); + var configuration = JsonConfigurationLoader.ApplyJsonConfig(reqnrollConfiguration, config); + configuration.ConfigSourceText = config.ToJson(); + return configuration; + } + + } +} diff --git a/Reqnroll/Infrastructure/DefaultDependencyProvider.cs b/Reqnroll/Infrastructure/DefaultDependencyProvider.cs index eb489540e..299f14896 100644 --- a/Reqnroll/Infrastructure/DefaultDependencyProvider.cs +++ b/Reqnroll/Infrastructure/DefaultDependencyProvider.cs @@ -16,6 +16,7 @@ using Reqnroll.Time; using Reqnroll.Tracing; using Reqnroll.PlatformCompatibility; +using Microsoft.Extensions.Configuration; namespace Reqnroll.Infrastructure { @@ -27,6 +28,8 @@ public virtual void RegisterGlobalContainerDefaults(ObjectContainer container) { container.RegisterTypeAs(); container.RegisterTypeAs(); + container.RegisterTypeAs(); + container.RegisterTypeAs(); container.RegisterTypeAs(); diff --git a/Reqnroll/Reqnroll.csproj b/Reqnroll/Reqnroll.csproj index 22b524d82..4ebe74fb4 100644 --- a/Reqnroll/Reqnroll.csproj +++ b/Reqnroll/Reqnroll.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/Reqnroll/Reqnroll.nuspec b/Reqnroll/Reqnroll.nuspec index a69cad25d..78198a802 100644 --- a/Reqnroll/Reqnroll.nuspec +++ b/Reqnroll/Reqnroll.nuspec @@ -21,6 +21,10 @@ + + + + diff --git a/Tests/Reqnroll.GeneratorTests/MsBuildProjectReaderTests.cs b/Tests/Reqnroll.GeneratorTests/MsBuildProjectReaderTests.cs index 1e70b628d..4a5372c45 100644 --- a/Tests/Reqnroll.GeneratorTests/MsBuildProjectReaderTests.cs +++ b/Tests/Reqnroll.GeneratorTests/MsBuildProjectReaderTests.cs @@ -19,8 +19,9 @@ private void Should_parse_csproj_file_correctly(string csprojPath, string langua string projectFilePath = Path.Combine(directoryName, csprojPath); var reqnrollJsonLocatorMock = new Mock(); + var microsoftExtensionsConfigurationLoaderMock = new Mock(); - var configurationLoader = new ConfigurationLoader(reqnrollJsonLocatorMock.Object); + var configurationLoader = new ConfigurationLoader(reqnrollJsonLocatorMock.Object, microsoftExtensionsConfigurationLoaderMock.Object); var generatorConfigurationProvider = new GeneratorConfigurationProvider(configurationLoader); var projectLanguageReader = new ProjectLanguageReader(); var reader = new ProjectReader(generatorConfigurationProvider, projectLanguageReader); diff --git a/Tests/Reqnroll.RuntimeTests/Configuration/MSEConfigurationLoaderTests.cs b/Tests/Reqnroll.RuntimeTests/Configuration/MSEConfigurationLoaderTests.cs new file mode 100644 index 000000000..45f0a7872 --- /dev/null +++ b/Tests/Reqnroll.RuntimeTests/Configuration/MSEConfigurationLoaderTests.cs @@ -0,0 +1,62 @@ +using Reqnroll.Configuration.JsonConfig; +using Reqnroll.Configuration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Moq; +using FluentAssertions; +using System.IO; +using System.Reflection; +using Microsoft.Extensions.Configuration; + +namespace Reqnroll.RuntimeTests.Configuration +{ + public class MSEConfigurationLoaderTests + { + private IReqnrollJsonLocator GetStubJsonLocator() + { + var stubJsonLocatorMock = new Mock(); + stubJsonLocatorMock.Setup(x => x.GetReqnrollJsonFilePath()).Returns(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Configuration\\reqnroll_config_test.json")); + return stubJsonLocatorMock.Object; + } + //private string configFilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Configuration\\reqnroll_config_test.json"); + + [Fact] + public void Can_Load_From_JsonFile() + { + var runtimeConfig = new MicrosoftConfiguration_RuntimeConfigurationLoader(GetStubJsonLocator(), new Microsoft.Extensions.Configuration.ConfigurationManager()) + .Load(ConfigurationLoader.GetDefault()); + runtimeConfig.FeatureLanguage.TwoLetterISOLanguageName.Should().Be("hu"); + } + + [Fact] + public void Can_Read_EnvironmentDesignationFromEnvironment() + { + Environment.SetEnvironmentVariable("DOTNET_Environment", "Staging"); + var runtimeConfig = new MicrosoftConfiguration_RuntimeConfigurationLoader(GetStubJsonLocator(), new Microsoft.Extensions.Configuration.ConfigurationManager()) + .Load(ConfigurationLoader.GetDefault()); + runtimeConfig.FeatureLanguage.TwoLetterISOLanguageName.Should().Be("fr"); + + Environment.SetEnvironmentVariable("DOTNET_Environment", null); + + } + [Fact] + public void Can_Load_Override_From_Environment() + { + //string config = @"{ + // ""language"": { ""feature"": ""de"" } + // }"; + Environment.SetEnvironmentVariable("REQNROLL__language__feature", "de"); + var runtimeConfig = new MicrosoftConfiguration_RuntimeConfigurationLoader(GetStubJsonLocator(), new Microsoft.Extensions.Configuration.ConfigurationManager()) + .Load(ConfigurationLoader.GetDefault()); + + runtimeConfig.FeatureLanguage.TwoLetterISOLanguageName.Should().Be("de"); + Environment.SetEnvironmentVariable("REQNROLL__language__feature", null); + } + + + } +} diff --git a/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.Staging.json b/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.Staging.json new file mode 100644 index 000000000..9b1c2af89 --- /dev/null +++ b/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.Staging.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json", + "language": { + "feature": "fr-FR" + } +} diff --git a/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.json b/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.json new file mode 100644 index 000000000..ec47115b1 --- /dev/null +++ b/Tests/Reqnroll.RuntimeTests/Configuration/reqnroll_config_test.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json", + "language": { + "feature": "hu-HU" + }, + "bindingAssemblies": [ + ] +} diff --git a/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj b/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj index 995689edb..884f12509 100644 --- a/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj +++ b/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj @@ -38,6 +38,12 @@ + + Always + + + Always + PreserveNewest