diff --git a/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs new file mode 100644 index 0000000..275d046 --- /dev/null +++ b/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs @@ -0,0 +1,32 @@ + +namespace OptionStudy.Next +{ + /// + /// Chained 配置源 + /// + public class ChainedConfigurationSourceTest: IDisposable + { + private readonly ITestOutputHelper testOutput; + + public ChainedConfigurationSourceTest(ITestOutputHelper testOutputHelper) + { + this.testOutput = testOutputHelper; + } + + /// + /// 使用 Chained配置源 + /// + [Fact] + public void Use_Test() + { + + + testOutput.WriteLine("使用 Chained 配置源!"); + } + + public void Dispose() + { + + } + } +} diff --git a/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs new file mode 100644 index 0000000..c109c40 --- /dev/null +++ b/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs @@ -0,0 +1,302 @@ + +using OptionStudy.UnitApp.Next; + +namespace OptionStudy.Next +{ + /// + /// 命令行 配置源 + /// 命令行参数分为 + /// 单参数:用=将参数名和参数值按以下形式指定 + /// {name}={value} + /// {prefix}{name}={value} prefix目前支持三种 “/”“--”“-”,其中“-”需配合 命令行参数映射一起使用 + /// 双参数:使用 空格 将参数名和参数值隔开的形式,形如 {name}={value} 或 {prefix}{name}={value} + /// + public class CommandLineConfigurationSourceTest : IDisposable + { + private readonly ITestOutputHelper testOutput; + + public CommandLineConfigurationSourceTest(ITestOutputHelper testOutputHelper) + { + this.testOutput = testOutputHelper; + } + + /// + /// 创建 CommandLineConfigurationProvider + /// + [Fact] + public void Create_CommandLineConfigurationProvider_Test() + { + var args = new string[] + { + "AppName=argAppName", + "/AppVersion=1.1.1.1" + }; + var cmdLineProvier = new CommandLineConfigurationProvider(args); + cmdLineProvier.Load(); + + var childKeys = cmdLineProvier.GetChildKeys(new string[0],null); + cmdLineProvier.TryGet("AppName", out string? appName); + cmdLineProvier.TryGet("AppVersion", out string? appVersion); + + Assert.Equal(2,childKeys.Count()); + Assert.Equal("argAppName", appName); + Assert.Equal("1.1.1.1", appVersion); + } + + /// + /// 使用 CommandLineConfiguration + /// + [Fact] + public void Build_CommandLineConfiguration_Test() + { + var args = new string[] + { + "AppName=argAppName", + "/AppVersion=1.1.1.1", + "--EMail:ReceiveAddress=arg@163.com", + "--EMail:Recipient=arg", + }; + + var root = new ConfigurationBuilder().AddCommandLine(args).Build(); + + var option = root.Get(); + + Assert.NotNull(option); + Assert.Equal("argAppName",option.AppName); + Assert.Equal(new Version(1,1,1,1), option.AppVersion); + Assert.Equal("arg@163.com", option.EMail?.ReceiveAddress); + Assert.Equal("arg", option.EMail?.Recipient); + } + + /// + /// 忽略中间不规范的值 + /// + [Fact] + public void IgnoreValuesInMiddle_Test() + { + var args = new string[] + { + "Key1=Value1", + "--Key2=Value2", + "/Key3=Value3", + "Bogus1", //忽略,只有一个名没有值 + "--Key4", "Value4", + "Bogus2", //忽略,只有一个名没有值 + "/Key5", "Value5", + "Bogus3" //忽略,只有一个名没有值 + }; + + var root = new ConfigurationBuilder().AddCommandLine(args).Build(); + + Assert.Equal("Value1", root.GetValue("Key1")); + Assert.Equal("Value2", root.GetValue("Key2")); + Assert.Equal("Value3", root.GetValue("Key3")); + Assert.Equal("Value4", root.GetValue("Key4")); + Assert.Equal("Value5", root.GetValue("Key5")); + Assert.Equal(5, root.GetChildren().Count()); + } + + /// + /// 使用参数映射:-前辍必需配合参数映射一起使用 + /// + [Fact] + public void Use_SwitchMappings_Test() + { + var args = new string[] + { + "-K1=Value1", + "--Key2=Value2", + "/Key3=Value3", + "--Key4", "Value4", + "/Key5", "Value5", + "/Key6=Value6" + }; + var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "-K1", "LongKey1" }, + { "--Key2", "SuperLongKey2" }, + { "--Key6", "SuchALongKey6"} + }; + + var root = new ConfigurationBuilder().AddCommandLine(args,switchMappings).Build(); + + Assert.Equal("Value1", root.GetValue("LongKey1")); + Assert.Equal("Value2", root.GetValue("SuperLongKey2")); + Assert.Equal("Value3", root.GetValue("Key3")); + Assert.Equal("Value4", root.GetValue("Key4")); + Assert.Equal("Value5", root.GetValue("Key5")); + Assert.Equal("Value6", root.GetValue("SuchALongKey6")); + } + + /// + /// 参数映射错误时,抛出异常 + /// + [Fact] + public void ThrowException_SwitchMappings_HasError_Test() + { + // Arrange + var args = new string[] + { + "-K1=Value1", //映射被错过 + "--Key2=Value2", + "/Key3=Value3", + "--Key4", "Value4", + "/Key5", "Value5" + }; + var switchMappings = new Dictionary(StringComparer.Ordinal) + { + { "--KEY1", "LongKey1" }, + { "--key1", "SuperLongKey1" }, + { "-Key2", "LongKey2" }, + { "-KEY2", "LongKey2"} + }; + + // Find out the duplicate expected be be reported + var expectedDup = string.Empty; + var set = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var mapping in switchMappings) + { + if (set.Contains(mapping.Key)) + { + expectedDup = mapping.Key; + break; + } + + set.Add(mapping.Key); + } + + var expectedMsg = new ArgumentException("switchMappings").Message; + + // Act + var exception = Assert.Throws + ( + () => new CommandLineConfigurationProvider(args, switchMappings) + ); + + // Assert + Assert.Contains(expectedMsg, exception.Message); + } + + /// + /// 包含无效键名时,抛出异常 + /// + [Fact] + public void ThrowException_SwitchMappings_ContainInvalidKey_Test() + { + var args = new string[] + { + "-K1=Value1", + "--Key2=Value2", + "/Key3=Value3", + "--Key4", "Value4", + "/Key5", "Value5" + }; + var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "-K1", "LongKey1" }, + { "--Key2", "SuperLongKey2" }, + { "/Key3", "AnotherSuperLongKey3" } + }; + + Assert.Throws + ( + () => new CommandLineConfigurationProvider(args, switchMappings) + ); + } + + /// + /// 命令行参数为null,抛出异常 + /// + [Fact] + public void ThrowException_NullIsPassedToConstructorAsArgs_Test() + { + string[] args = null; + var expectedMsg = new ArgumentNullException("args").Message; + + var exception = Assert.Throws(() => new CommandLineConfigurationProvider(args)); + + Assert.Equal(expectedMsg, exception.Message); + } + + /// + /// 键重复时覆盖值 + /// + [Fact] + public void OverrideValueWhenKeyIsDuplicated_Test() + { + var args = new string[] + { + "/Key1=Value1", + "--Key1=Value2" + }; + var cmdLineConfig = new CommandLineConfigurationProvider(args); + + cmdLineConfig.Load(); + + cmdLineConfig.TryGet("Key1", out string? value1); + + Assert.Equal("Value2", value1); + } + + /// + /// 忽略缺少Key的值 + /// + [Fact] + public void IgnoreWhenValueForAKeyIsMissing_Test() + { + var args = new string[] + { + "--Key1", "Value1", + "/Key2" /* The value for Key2 is missing here */ + }; + + var cmdLineConfig = new CommandLineConfigurationProvider(args); + cmdLineConfig.Load(); + + cmdLineConfig.TryGet("Key1", out string? value1); + + Assert.Single(cmdLineConfig.GetChildKeys(new string[0], null)); + Assert.Equal("Value1", value1); + } + + /// + /// 忽略无法识别的参数 + /// + [Fact] + public void IgnoreWhenAnArgumentCannotBeRecognized_Test() + { + var args = new string[] + { + "ArgWithoutPrefixAndEqualSign" + }; + var cmdLineConfig = new CommandLineConfigurationProvider(args); + cmdLineConfig.Load(); + Assert.Empty(cmdLineConfig.GetChildKeys(new string[0], null)); + } + + /// + /// 忽略未被映射的参数 + /// 如果没有映射,则异常;有映射,但key不在映射中时,则忽略 + /// + [Fact] + public void IgnoreWhenShortSwitchNotDefined_Test() + { + var args = new string[] + { + "-Key1", "Value1", + }; + var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "-Key2", "LongKey2" } + }; + var cmdLineConfig = new CommandLineConfigurationProvider(args, switchMappings); + cmdLineConfig.Load(); + Assert.Empty(cmdLineConfig.GetChildKeys(new string[0], "")); + } + + public void Dispose() + { + + } + } +} diff --git a/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs new file mode 100644 index 0000000..bded6c1 --- /dev/null +++ b/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs @@ -0,0 +1,76 @@ + + + +using OptionStudy.UnitApp.Next; + +namespace OptionStudy.Next +{ + /// + /// 环境变量 配置源 + /// 环境变量分类:系统环境变量、用户环境变量、进程环境变量 + /// 环境变量设置:系统图形工具、编程、启动文件 launchSettings.json(VS环境比较方便) + /// + public class EnvironmentVariablesConfigurationSourceTest : IDisposable + { + private readonly ITestOutputHelper testOutput; + + public EnvironmentVariablesConfigurationSourceTest(ITestOutputHelper testOutputHelper) + { + this.testOutput = testOutputHelper; + } + + /// + /// 设置的进程环境变量,在一次测试运行中会保留 + /// 因为一次测试运行中的多个测试方法,是在一个进程中运行的。 + /// + [Theory] + [InlineData("xUnit-First")] + [InlineData("xUnit-Second")] + public void ProcessEnvironmentVariables_Set_Test(string value) + { + var envName = "xUnit:AppName"; + var env = Environment.GetEnvironmentVariable(envName); + //一次运行多个测试用例时: + //第一个测试用例运行时,是null值,但第二个用例运行时就有值,其值为第一个用例设置的值 + if (env == null) + { + testOutput.WriteLine($"当前用例参数为 {value}, 环境变量 {nameof(envName)} 的值为 null "); + } + else + { + testOutput.WriteLine($"当前用例参数为 {value}, 环境变量 {nameof(envName)} 的值为 {env} "); + } + + //设置环境变量,看看在另一个测试工里会不会保留设置的环境变量值 + Environment.SetEnvironmentVariable(envName, value, EnvironmentVariableTarget.Process); + } + + /// + /// 设置进程环境变量 + /// + [Fact] + public void Load_Set_ProcessEnvironmentVariables_Test() + { + // 设置进程环境变量 + Environment.SetEnvironmentVariable("xUnit:AppName", "xunitAppName", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("xUnit:AppVersion", "2.2.2.2", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("xUnit:EMail:ReceiveAddress", "xunit@163.com", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("xUnit:Email:Recipient", "xunit", EnvironmentVariableTarget.Process); + + // 获取配置对象 + var root = new ConfigurationBuilder().AddEnvironmentVariables("xUnit:").Build(); + var option = root.Get(); + + Assert.NotNull(option); + Assert.Equal("xunitAppName",option.AppName); + Assert.Equal(new Version(2,2,2,2),option.AppVersion); + Assert.Equal("xunit@163.com",option.EMail?.ReceiveAddress); + Assert.Equal("xunit",option.EMail?.Recipient); + } + + public void Dispose() + { + + } + } +} diff --git a/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs new file mode 100644 index 0000000..91f6095 --- /dev/null +++ b/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs @@ -0,0 +1,51 @@ + +using OptionStudy.UnitApp.Next; + +namespace OptionStudy.Next +{ + /// + /// 内存 配置源 + /// + public class MemoryConfigurationSourceTest : IDisposable + { + private readonly ITestOutputHelper testOutput; + + public MemoryConfigurationSourceTest(ITestOutputHelper testOutputHelper) + { + this.testOutput = testOutputHelper; + } + + /// + /// 使用内存配置源 + /// + [Fact] + public void Use_Test() + { + IDictionary memoryData = new Dictionary() + { + ["AppName"] = "MemoryAppName", + ["AppVersion"] = "0.0.0.1", + ["EMail:ReceiveAddress"] = "memory@163.com", + ["EMail:Recipient"] = "memory", + }; + + var root = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build(); + var configOption = root.Get(); + + //MemoryConfigurationProvider 可以执行添加、设置等操作 + var provider = root.Providers.First() as MemoryConfigurationProvider; + provider?.Add("MyAdd", "MyValue"); + provider?.Set("AppVersion", "2.0.0.0"); + + Assert.NotNull(configOption); + Assert.Equal("memory", configOption.EMail?.Recipient); + + testOutput.WriteLine("使用 内存配置源!"); + } + + public void Dispose() + { + + } + } +} diff --git a/OptionStudy.Next/OptionStudy.Next.csproj b/OptionStudy.Next/OptionStudy.Next.csproj index 9fa8b26..e62b5b5 100644 --- a/OptionStudy.Next/OptionStudy.Next.csproj +++ b/OptionStudy.Next/OptionStudy.Next.csproj @@ -59,8 +59,4 @@ - - - - diff --git a/OptionStudy.Next/Usings.cs b/OptionStudy.Next/Usings.cs index 88a592b..ce36fb1 100644 --- a/OptionStudy.Next/Usings.cs +++ b/OptionStudy.Next/Usings.cs @@ -2,7 +2,10 @@ global using System; global using System.Linq; global using System.Text; global using System.Threading.Tasks; +global using System.Collections; global using System.Collections.Generic; +global using System.Collections.Concurrent; +global using System.Collections.Specialized; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration.Memory;