From 0889665ae2c76aeaeddde4740c6a0f2728159028 Mon Sep 17 00:00:00 2001 From: Tomas Arci Kouba Date: Tue, 9 Oct 2018 00:10:43 +0200 Subject: [PATCH 1/5] Localizable HelpText with infrastructure backend class and tests --- src/CommandLine/BaseAttribute.cs | 23 ++++++-- src/CommandLine/CommandLine.csproj | 13 ++++- .../LocalizableAttributeProperty.cs | 58 +++++++++++++++++++ .../CommandLine.Tests.Properties.csproj | 32 +++++----- .../CommandLine.Tests.csproj | 37 +++++++----- .../CommandLine.Tests/Fakes/ResourceFakes.cs | 31 ++++++++++ .../Unit/BaseAttributeTests.cs | 39 +++++++++++-- 7 files changed, 193 insertions(+), 40 deletions(-) create mode 100644 src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs create mode 100644 tests/CommandLine.Tests/Fakes/ResourceFakes.cs diff --git a/src/CommandLine/BaseAttribute.cs b/src/CommandLine/BaseAttribute.cs index e390e88c..a2bad2d7 100644 --- a/src/CommandLine/BaseAttribute.cs +++ b/src/CommandLine/BaseAttribute.cs @@ -12,8 +12,9 @@ public abstract class BaseAttribute : Attribute private int min; private int max; private object @default; - private string helpText; + private Infrastructure.LocalizableAttributeProperty helpText; private string metaValue; + private Type resourceType; /// /// Initializes a new instance of the class. @@ -22,8 +23,9 @@ protected internal BaseAttribute() { min = -1; max = -1; - helpText = string.Empty; + helpText = new Infrastructure.LocalizableAttributeProperty(nameof(HelpText)); metaValue = string.Empty; + resourceType = null; } /// @@ -90,7 +92,7 @@ public object Default /// public string HelpText { - get { return helpText; } + get { return helpText.Value; } set { if (value == null) @@ -98,7 +100,7 @@ public string HelpText throw new ArgumentNullException("value"); } - helpText = value; + helpText.Value = value; } } @@ -127,5 +129,18 @@ public bool Hidden get; set; } + + /// + /// Gets or sets the that contains the resources for . + /// + public Type ResourceType + { + get => resourceType; + set + { + resourceType = + helpText.ResourceType = value; + } + } } } diff --git a/src/CommandLine/CommandLine.csproj b/src/CommandLine/CommandLine.csproj index 8f829c9f..1f179fc5 100644 --- a/src/CommandLine/CommandLine.csproj +++ b/src/CommandLine/CommandLine.csproj @@ -84,6 +84,7 @@ + @@ -169,8 +170,16 @@ - - + + + + ..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll + True + True + + + + ..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll diff --git a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs new file mode 100644 index 00000000..0a462df1 --- /dev/null +++ b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace CommandLine.Infrastructure +{ + internal class LocalizableAttributeProperty + { + private string _propertyName; + private string _value; + private Type _type; + private PropertyInfo _localizationPropertyInfo; + + public LocalizableAttributeProperty(string propertyName) + { + _propertyName = propertyName; + } + + public string Value + { + get => GetLocalizedValue(); + set + { + _localizationPropertyInfo = null; + _value = value; + } + } + + public Type ResourceType + { + set + { + _localizationPropertyInfo = null; + _type = value; + } + } + + private string GetLocalizedValue() + { + if (String.IsNullOrEmpty(_value) || _type == null) + return _value; + if (_localizationPropertyInfo == null) + { + // Static class IsAbstract + if (!_type.IsVisible) + throw new ArgumentException("Invalid resource type", _propertyName); + PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static); + if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(string)) + throw new ArgumentException("Invalid resource property name", _propertyName); + _localizationPropertyInfo = propertyInfo; + } + return (string)_localizationPropertyInfo.GetValue(null, null); + } + } + +} diff --git a/tests/CommandLine.Tests.Properties/CommandLine.Tests.Properties.csproj b/tests/CommandLine.Tests.Properties/CommandLine.Tests.Properties.csproj index cb2b6b96..9940f4c6 100644 --- a/tests/CommandLine.Tests.Properties/CommandLine.Tests.Properties.csproj +++ b/tests/CommandLine.Tests.Properties/CommandLine.Tests.Properties.csproj @@ -58,12 +58,12 @@ - + <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio - + <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio @@ -94,7 +94,7 @@ <__paket__xunit_core_props>Xamarin.iOS\xunit.core - + <__paket__xunit_core_props>monoandroid\xunit.core @@ -109,7 +109,7 @@ <__paket__xunit_core_props>portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core - + <__paket__xunit_core_props>portable-win81+wpa81\xunit.core <__paket__xunit_core_targets>portable-win81+wpa81\xunit.core @@ -137,7 +137,7 @@ --> - + ..\..\packages\FluentAssertions\lib\monoandroid\FluentAssertions.Core.dll @@ -169,7 +169,7 @@ - + ..\..\packages\FluentAssertions\lib\net45\FluentAssertions.dll @@ -183,7 +183,7 @@ - + ..\..\packages\FluentAssertions\lib\portable-net40+sl5+win8+wp8+wpa81\FluentAssertions.dll @@ -197,7 +197,7 @@ - + ..\..\packages\FluentAssertions\lib\portable-win81+wpa81\FluentAssertions.dll @@ -255,7 +255,7 @@ - + ..\..\packages\FsCheck\lib\net45\FsCheck.dll @@ -264,7 +264,7 @@ - + ..\..\packages\FsCheck\lib\portable-net45+netcore45\FsCheck.dll @@ -302,7 +302,7 @@ - + ..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll @@ -311,7 +311,7 @@ - + ..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll @@ -349,7 +349,7 @@ - + ..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll @@ -358,7 +358,7 @@ - + ..\..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll @@ -369,7 +369,7 @@ - + ..\..\packages\xunit.assert\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll @@ -380,7 +380,7 @@ - + ..\..\packages\xunit.extensibility.core\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll diff --git a/tests/CommandLine.Tests/CommandLine.Tests.csproj b/tests/CommandLine.Tests/CommandLine.Tests.csproj index f96b4e2e..1d53d813 100644 --- a/tests/CommandLine.Tests/CommandLine.Tests.csproj +++ b/tests/CommandLine.Tests/CommandLine.Tests.csproj @@ -73,6 +73,7 @@ + @@ -143,12 +144,12 @@ - + <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio - + <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio @@ -179,7 +180,7 @@ <__paket__xunit_core_props>Xamarin.iOS\xunit.core - + <__paket__xunit_core_props>monoandroid\xunit.core @@ -194,7 +195,7 @@ <__paket__xunit_core_props>portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core - + <__paket__xunit_core_props>portable-win81+wpa81\xunit.core <__paket__xunit_core_targets>portable-win81+wpa81\xunit.core @@ -220,7 +221,7 @@ --> - + ..\..\packages\FluentAssertions\lib\monoandroid\FluentAssertions.Core.dll @@ -252,7 +253,7 @@ - + ..\..\packages\FluentAssertions\lib\net45\FluentAssertions.dll @@ -266,7 +267,7 @@ - + ..\..\packages\FluentAssertions\lib\portable-net40+sl5+win8+wp8+wpa81\FluentAssertions.dll @@ -280,7 +281,7 @@ - + ..\..\packages\FluentAssertions\lib\portable-win81+wpa81\FluentAssertions.dll @@ -347,8 +348,16 @@ - - + + + + ..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll + True + True + + + + ..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll @@ -386,7 +395,7 @@ - + ..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll @@ -395,7 +404,7 @@ - + ..\..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll @@ -406,7 +415,7 @@ - + ..\..\packages\xunit.assert\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll @@ -417,7 +426,7 @@ - + ..\..\packages\xunit.extensibility.core\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll diff --git a/tests/CommandLine.Tests/Fakes/ResourceFakes.cs b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs new file mode 100644 index 00000000..52e44b29 --- /dev/null +++ b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CommandLine.Tests.Fakes +{ + public static class StaticResource + { + public static string HelpText { get => "Localized HelpText"; } + } + + public class NonStaticResource + { + public static string HelpText { get => "Localized HelpText"; } + public static string WriteOnlyText { set => value?.ToString(); } + private static string PrivateHelpText { get => "Localized HelpText"; } + } + + public class NonStaticResource_WithNonStaticProperty + { + public string HelpText { get => "Localized HelpText"; } + } + + internal class InternalResource + { + public static string HelpText { get => "Localized HelpText"; } + } + +} diff --git a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs index 79d20c7b..9a2a3ae0 100644 --- a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs +++ b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace CommandLine.Tests.Unit @@ -20,6 +16,40 @@ public static void Default(object defaultValue) Assert.Equal(defaultValue, baseAttribute.Default); } + [Theory] + [InlineData("", null, "")] + [InlineData("", typeof(Fakes.StaticResource), "")] + [InlineData("Help text", null, "Help text")] + [InlineData("HelpText", typeof(Fakes.StaticResource), "Localized HelpText")] + [InlineData("HelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")] + public static void HelpText(string helpText, Type resourceType, string expected) + { + TestBaseAttribute baseAttribute = new TestBaseAttribute() + { + HelpText = helpText, + ResourceType = resourceType + }; + Assert.Equal(expected, baseAttribute.HelpText); + } + + [Theory] + [InlineData("HelpText", typeof(Fakes.NonStaticResource_WithNonStaticProperty))] + [InlineData("WriteOnlyText", typeof(Fakes.NonStaticResource))] + [InlineData("PrivateOnlyText", typeof(Fakes.NonStaticResource))] + [InlineData("HelpText", typeof(Fakes.InternalResource))] + public void ThrowsHelpText(string helpText, Type resourceType) + { + TestBaseAttribute baseAttribute = new TestBaseAttribute() + { + HelpText = helpText, + ResourceType = resourceType + }; + + // Verify exception + Assert.Throws(() => baseAttribute.HelpText.ToString()); + } + + private class TestBaseAttribute : BaseAttribute { public TestBaseAttribute() @@ -27,5 +57,6 @@ public TestBaseAttribute() // Do nothing } } + } } From 8eeff04e0b5db36027b3e414923c6427bd37c9fb Mon Sep 17 00:00:00 2001 From: Tomas Arci Kouba Date: Tue, 9 Oct 2018 00:27:32 +0200 Subject: [PATCH 2/5] Fixed expression bodied properties --- src/CommandLine/BaseAttribute.cs | 2 +- src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CommandLine/BaseAttribute.cs b/src/CommandLine/BaseAttribute.cs index a2bad2d7..bf179d78 100644 --- a/src/CommandLine/BaseAttribute.cs +++ b/src/CommandLine/BaseAttribute.cs @@ -135,7 +135,7 @@ public bool Hidden /// public Type ResourceType { - get => resourceType; + get { return resourceType; } set { resourceType = diff --git a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs index 0a462df1..25c2ae4d 100644 --- a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs +++ b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs @@ -20,7 +20,7 @@ public LocalizableAttributeProperty(string propertyName) public string Value { - get => GetLocalizedValue(); + get { return GetLocalizedValue(); } set { _localizationPropertyInfo = null; From 589869dd6273ca545f8c73c5a3ef120f26ccc1fc Mon Sep 17 00:00:00 2001 From: Tomas Arci Kouba Date: Tue, 9 Oct 2018 00:53:23 +0200 Subject: [PATCH 3/5] Fakes without expression bodied properties --- tests/CommandLine.Tests/Fakes/ResourceFakes.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/CommandLine.Tests/Fakes/ResourceFakes.cs b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs index 52e44b29..917d51bf 100644 --- a/tests/CommandLine.Tests/Fakes/ResourceFakes.cs +++ b/tests/CommandLine.Tests/Fakes/ResourceFakes.cs @@ -8,24 +8,24 @@ namespace CommandLine.Tests.Fakes { public static class StaticResource { - public static string HelpText { get => "Localized HelpText"; } + public static string HelpText { get { return "Localized HelpText"; } } } public class NonStaticResource { - public static string HelpText { get => "Localized HelpText"; } - public static string WriteOnlyText { set => value?.ToString(); } - private static string PrivateHelpText { get => "Localized HelpText"; } + public static string HelpText { get { return "Localized HelpText"; } } + public static string WriteOnlyText { set { value?.ToString(); } } + private static string PrivateHelpText { get { return "Localized HelpText"; } } } public class NonStaticResource_WithNonStaticProperty { - public string HelpText { get => "Localized HelpText"; } + public string HelpText { get { return "Localized HelpText"; } } } internal class InternalResource { - public static string HelpText { get => "Localized HelpText"; } + public static string HelpText { get { return "Localized HelpText"; } } } } From d45f14fd87d930e48916a35f56bb5095f84fe5af Mon Sep 17 00:00:00 2001 From: Tomas Arci Kouba Date: Tue, 9 Oct 2018 00:55:17 +0200 Subject: [PATCH 4/5] Refaktoring tests, removed initializers --- .../Unit/BaseAttributeTests.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs index 9a2a3ae0..72cf81b4 100644 --- a/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs +++ b/tests/CommandLine.Tests/Unit/BaseAttributeTests.cs @@ -24,11 +24,10 @@ public static void Default(object defaultValue) [InlineData("HelpText", typeof(Fakes.NonStaticResource), "Localized HelpText")] public static void HelpText(string helpText, Type resourceType, string expected) { - TestBaseAttribute baseAttribute = new TestBaseAttribute() - { - HelpText = helpText, - ResourceType = resourceType - }; + TestBaseAttribute baseAttribute = new TestBaseAttribute(); + baseAttribute.HelpText = helpText; + baseAttribute.ResourceType = resourceType; + Assert.Equal(expected, baseAttribute.HelpText); } @@ -39,11 +38,9 @@ public static void HelpText(string helpText, Type resourceType, string expected) [InlineData("HelpText", typeof(Fakes.InternalResource))] public void ThrowsHelpText(string helpText, Type resourceType) { - TestBaseAttribute baseAttribute = new TestBaseAttribute() - { - HelpText = helpText, - ResourceType = resourceType - }; + TestBaseAttribute baseAttribute = new TestBaseAttribute(); + baseAttribute.HelpText = helpText; + baseAttribute.ResourceType = resourceType; // Verify exception Assert.Throws(() => baseAttribute.HelpText.ToString()); From 0881d1b7e1277859a67fe93350f907c9ec377119 Mon Sep 17 00:00:00 2001 From: Tomas Arci Kouba Date: Wed, 31 Oct 2018 17:38:06 +0100 Subject: [PATCH 5/5] netstandard 1.5 fix --- .../Infrastructure/LocalizableAttributeProperty.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs index 25c2ae4d..3e622918 100644 --- a/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs +++ b/src/CommandLine/Infrastructure/LocalizableAttributeProperty.cs @@ -43,8 +43,12 @@ private string GetLocalizedValue() return _value; if (_localizationPropertyInfo == null) { - // Static class IsAbstract + // Static class IsAbstract +#if NETSTANDARD1_5 + if (!_type.GetTypeInfo().IsVisible) +#else if (!_type.IsVisible) +#endif throw new ArgumentException("Invalid resource type", _propertyName); PropertyInfo propertyInfo = _type.GetProperty(_value, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static); if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(string))