From 2bac5e360a7303cee6dd008cf7428010a81be3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 6 Jul 2020 14:26:46 +0200 Subject: [PATCH 1/5] Add environment variable directive parser --- .../EnvironmentVariableDirectiveTests.cs | 89 +++++++++++++++++++ .../Builder/CommandLineBuilderExtensions.cs | 25 ++++++ .../Invocation/MiddlewareOrder.cs | 1 + 3 files changed, 115 insertions(+) create mode 100644 src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs new file mode 100644 index 0000000000..91a71ed38b --- /dev/null +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.CommandLine.Builder; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; +using System.Text; +using System.Threading.Tasks; + +using Xunit; + +namespace System.CommandLine.Tests +{ + public class EnvironmentVariableDirectiveTests + { + private static readonly Random randomizer = new Random(Seed: 456476756); + + [Fact] + public static async Task Sets_environment_variable_to_value() + { + bool asserted = false; + string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); + + Assert.True(asserted); + } + + [Fact] + public static async Task Trims_environment_variable_name() + { + bool asserted = false; + string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env: {variable} ={value}]" }); + + Assert.True(asserted); + } + + [Fact] + public static async Task Trims_environment_variable_value() + { + bool asserted = false; + string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}= {value} ]" }); + + Assert.True(asserted); + } + } +} diff --git a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs index 60a10ae445..5b828604bf 100644 --- a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs +++ b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs @@ -228,11 +228,36 @@ public static CommandLineBuilder UseDebugDirective( return builder; } + public static CommandLineBuilder UseEnvironmentVariableDirective( + this CommandLineBuilder builder) + { + builder.AddMiddleware((context, next) => + { + if (context.ParseResult.Directives.TryGetValues("env", out var directives)) + { + foreach (var envDirective in directives) + { + var components = envDirective.Split(new[] { '=' }, count: 2); + var variable = components.Length > 0 ? components[0].Trim() : string.Empty; + if (string.IsNullOrEmpty(variable) || components.Length < 2) + continue; + var value = components[1].Trim(); + SetEnvironmentVariable(variable, value); + } + } + + return next(context); + }, MiddlewareOrderInternal.EnvironmentVariableDirective); + + return builder; + } + public static CommandLineBuilder UseDefaults(this CommandLineBuilder builder) { return builder .UseVersionOption() .UseHelp() + .UseEnvironmentVariableDirective() .UseParseDirective() .UseDebugDirective() .UseSuggestDirective() diff --git a/src/System.CommandLine/Invocation/MiddlewareOrder.cs b/src/System.CommandLine/Invocation/MiddlewareOrder.cs index b233e8042e..da921a6fad 100644 --- a/src/System.CommandLine/Invocation/MiddlewareOrder.cs +++ b/src/System.CommandLine/Invocation/MiddlewareOrder.cs @@ -15,6 +15,7 @@ internal enum MiddlewareOrderInternal { Startup = -4000, ExceptionHandler = -3000, + EnvironmentVariableDirective = -2600, ConfigureConsole = -2500, RegisterWithDotnetSuggest = -2400, DebugDirective = -2300, From 047eed193dbd7e5b65f5c48190b1b364884a31b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 6 Jul 2020 15:08:56 +0200 Subject: [PATCH 2/5] Add tests for special environment directive cases --- .../EnvironmentVariableDirectiveTests.cs | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs index 91a71ed38b..809a3ffa3d 100644 --- a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -3,6 +3,7 @@ using System.CommandLine.Builder; using System.CommandLine.Invocation; using System.CommandLine.Parsing; +using System.Linq; using System.Text; using System.Threading.Tasks; @@ -85,5 +86,100 @@ public static async Task Trims_environment_variable_value() Assert.True(asserted); } + + [Fact] + public static async Task Sets_environment_variable_value_containing_equals_sign() + { + bool asserted = false; + string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + const string value = "This is = a test containing equals"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); + + Assert.True(asserted); + } + + [Fact] + public static async Task Ignores_environment_directive_without_equals_sign() + { + bool asserted = false; + string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Assert.Null(Environment.GetEnvironmentVariable(variable)); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}]" }); + + Assert.True(asserted); + } + + [Fact] + public static async Task Ignores_environment_directive_with_empty_variable_name() + { + bool asserted = false; + string value = $"This is a test, random: {randomizer.Next()}"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + var env = Environment.GetEnvironmentVariables(); + Assert.DoesNotContain(value, env.Values.Cast().ToArray()); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:={value}]" }); + + Assert.True(asserted); + } + + [Fact] + public static async Task Ignores_environment_directive_with_whitespace_variable_name() + { + bool asserted = false; + string value = $"This is a test, random: {randomizer.Next()}"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + var env = Environment.GetEnvironmentVariables(); + Assert.DoesNotContain(value, env.Values.Cast().ToArray()); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env: ={value}]" }); + + Assert.True(asserted); + } } } From 0f7526158d03b846a725f335a09c12f4159eab4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Thu, 9 Jul 2020 00:53:51 +0200 Subject: [PATCH 3/5] Use FluentAssertions for Env-Directive unit-tests --- .../EnvironmentVariableDirectiveTests.cs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs index 809a3ffa3d..11f7e0e28c 100644 --- a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using FluentAssertions; + using System.CommandLine.Builder; using System.CommandLine.Invocation; using System.CommandLine.Parsing; using System.Linq; -using System.Text; using System.Threading.Tasks; using Xunit; @@ -26,7 +25,7 @@ public static async Task Sets_environment_variable_to_value() Handler = CommandHandler.Create(() => { asserted = true; - Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + Environment.GetEnvironmentVariable(variable).Should().Be(value); }) }; @@ -36,7 +35,7 @@ public static async Task Sets_environment_variable_to_value() await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -50,7 +49,7 @@ public static async Task Trims_environment_variable_name() Handler = CommandHandler.Create(() => { asserted = true; - Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + Environment.GetEnvironmentVariable(variable).Should().Be(value); }) }; @@ -60,7 +59,7 @@ public static async Task Trims_environment_variable_name() await parser.InvokeAsync(new[] { $"[env: {variable} ={value}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -74,7 +73,7 @@ public static async Task Trims_environment_variable_value() Handler = CommandHandler.Create(() => { asserted = true; - Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + Environment.GetEnvironmentVariable(variable).Should().Be(value); }) }; @@ -84,7 +83,7 @@ public static async Task Trims_environment_variable_value() await parser.InvokeAsync(new[] { $"[env:{variable}= {value} ]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -98,7 +97,7 @@ public static async Task Sets_environment_variable_value_containing_equals_sign( Handler = CommandHandler.Create(() => { asserted = true; - Assert.Equal(value, Environment.GetEnvironmentVariable(variable)); + Environment.GetEnvironmentVariable(variable).Should().Be(value); }) }; @@ -108,7 +107,7 @@ public static async Task Sets_environment_variable_value_containing_equals_sign( await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -121,7 +120,7 @@ public static async Task Ignores_environment_directive_without_equals_sign() Handler = CommandHandler.Create(() => { asserted = true; - Assert.Null(Environment.GetEnvironmentVariable(variable)); + Environment.GetEnvironmentVariable(variable).Should().BeNull(); }) }; @@ -131,7 +130,7 @@ public static async Task Ignores_environment_directive_without_equals_sign() await parser.InvokeAsync(new[] { $"[env:{variable}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -145,7 +144,7 @@ public static async Task Ignores_environment_directive_with_empty_variable_name( { asserted = true; var env = Environment.GetEnvironmentVariables(); - Assert.DoesNotContain(value, env.Values.Cast().ToArray()); + env.Values.Cast().Should().NotContain(value); }) }; @@ -155,7 +154,7 @@ public static async Task Ignores_environment_directive_with_empty_variable_name( await parser.InvokeAsync(new[] { $"[env:={value}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } [Fact] @@ -169,7 +168,7 @@ public static async Task Ignores_environment_directive_with_whitespace_variable_ { asserted = true; var env = Environment.GetEnvironmentVariables(); - Assert.DoesNotContain(value, env.Values.Cast().ToArray()); + env.Values.Cast().Should().NotContain(value); }) }; @@ -179,7 +178,7 @@ public static async Task Ignores_environment_directive_with_whitespace_variable_ await parser.InvokeAsync(new[] { $"[env: ={value}]" }); - Assert.True(asserted); + asserted.Should().BeTrue(); } } } From b7559244cd6b18efa6d03a8ef5bcef5629f6c024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Thu, 9 Jul 2020 00:54:29 +0200 Subject: [PATCH 4/5] Add brances in if-check for environment variables --- src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs index 5b828604bf..a6946c8703 100644 --- a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs +++ b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs @@ -240,7 +240,9 @@ public static CommandLineBuilder UseEnvironmentVariableDirective( var components = envDirective.Split(new[] { '=' }, count: 2); var variable = components.Length > 0 ? components[0].Trim() : string.Empty; if (string.IsNullOrEmpty(variable) || components.Length < 2) + { continue; + } var value = components[1].Trim(); SetEnvironmentVariable(variable, value); } From 51bd07b97835027f79b65384499bcb67fc502bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Thu, 9 Jul 2020 00:55:02 +0200 Subject: [PATCH 5/5] Construct test environment variable in test-constructor --- .../EnvironmentVariableDirectiveTests.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs index 11f7e0e28c..1464ab3511 100644 --- a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -13,12 +13,13 @@ namespace System.CommandLine.Tests public class EnvironmentVariableDirectiveTests { private static readonly Random randomizer = new Random(Seed: 456476756); + private readonly string test_variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; [Fact] - public static async Task Sets_environment_variable_to_value() + public async Task Sets_environment_variable_to_value() { bool asserted = false; - string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + string variable = test_variable; const string value = "This is a test"; var rootCommand = new RootCommand { @@ -39,10 +40,10 @@ public static async Task Sets_environment_variable_to_value() } [Fact] - public static async Task Trims_environment_variable_name() + public async Task Trims_environment_variable_name() { bool asserted = false; - string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + string variable = test_variable; const string value = "This is a test"; var rootCommand = new RootCommand { @@ -63,10 +64,10 @@ public static async Task Trims_environment_variable_name() } [Fact] - public static async Task Trims_environment_variable_value() + public async Task Trims_environment_variable_value() { bool asserted = false; - string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + string variable = test_variable; const string value = "This is a test"; var rootCommand = new RootCommand { @@ -87,10 +88,10 @@ public static async Task Trims_environment_variable_value() } [Fact] - public static async Task Sets_environment_variable_value_containing_equals_sign() + public async Task Sets_environment_variable_value_containing_equals_sign() { bool asserted = false; - string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + string variable = test_variable; const string value = "This is = a test containing equals"; var rootCommand = new RootCommand { @@ -111,10 +112,10 @@ public static async Task Sets_environment_variable_value_containing_equals_sign( } [Fact] - public static async Task Ignores_environment_directive_without_equals_sign() + public async Task Ignores_environment_directive_without_equals_sign() { bool asserted = false; - string variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + string variable = test_variable; var rootCommand = new RootCommand { Handler = CommandHandler.Create(() =>