From c7df4e9fb3a69e5de1d91e3cb231677353d1b23b Mon Sep 17 00:00:00 2001 From: Michael Handschuh Date: Mon, 29 Jul 2019 02:25:25 -0400 Subject: [PATCH] Add StringExtensions and Parse utilizing CultureInfo.InvariantCulture Adds static methods of the form Parse.(string str) that use CultureInfo.InvariantCulture. These are to be used when parsing strings. It's still safe (from the CA1304/CA1305 perspective) to use the ToDecimal extension method for decimals. Adds string extension methods for common operations that will now require CultureInfo.InvariantCulture. These are to be used when converting values to strings, such as ToStringInvariant()/ToStringInvariant(format), but also useful for searching within strings, StartsWithInvariant, EndsWithInvariant and IndexOfInvariant. --- .../QuantConnect.Algorithm.CSharp.csproj | 3 + Algorithm.CSharp/packages.config | 1 + .../QuantConnect.Algorithm.Framework.csproj | 3 + Algorithm.Framework/packages.config | 1 + .../QuantConnect.Algorithm.Python.csproj | 5 + Algorithm.Python/packages.config | 1 + Algorithm/QuantConnect.Algorithm.csproj | 3 + Algorithm/packages.config | 1 + .../QuantConnect.AlgorithmFactory.csproj | 3 + AlgorithmFactory/packages.config | 1 + Api/QuantConnect.Api.csproj | 3 + Api/packages.config | 1 + Brokerages/QuantConnect.Brokerages.csproj | 3 + Brokerages/packages.config | 1 + Common/Parse.cs | 112 ++++++++++ Common/QuantConnect.csproj | 5 + Common/StringExtensions.cs | 209 ++++++++++++++++++ Common/packages.config | 1 + Compression/QuantConnect.Compression.csproj | 3 + Compression/packages.config | 1 + .../QuantConnect.Configuration.csproj | 3 + Configuration/packages.config | 1 + Engine/QuantConnect.Lean.Engine.csproj | 3 + Engine/packages.config | 1 + Indicators/QuantConnect.Indicators.csproj | 3 + Indicators/packages.config | 1 + Jupyter/QuantConnect.Jupyter.csproj | 3 + Jupyter/packages.config | 1 + Launcher/QuantConnect.Lean.Launcher.csproj | 3 + Launcher/packages.config | 1 + Logging/QuantConnect.Logging.csproj | 3 + Logging/packages.config | 1 + Messaging/QuantConnect.Messaging.csproj | 3 + Messaging/packages.config | 1 + Queues/QuantConnect.Queues.csproj | 3 + Queues/packages.config | 1 + Tests/Common/ParseTests.cs | 104 +++++++++ Tests/Common/StringExtensionsTests.cs | 185 ++++++++++++++++ Tests/QuantConnect.Tests.csproj | 5 + Tests/packages.config | 1 + ToolBox/QuantConnect.ToolBox.csproj | 3 + ToolBox/packages.config | 1 + UserInterface/QuantConnect.Views.csproj | 3 + UserInterface/packages.config | 17 ++ .../QuantConnect.VisualStudio17Plugin.csproj | 3 + ...s.QuantConnect.VisualStudio17Plugin.config | 1 + 46 files changed, 716 insertions(+) create mode 100644 Common/Parse.cs create mode 100644 Common/StringExtensions.cs create mode 100644 Tests/Common/ParseTests.cs create mode 100644 Tests/Common/StringExtensionsTests.cs create mode 100644 UserInterface/packages.config diff --git a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj index 9cac5a2fbdfc..b35bdc031347 100644 --- a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj +++ b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj @@ -80,6 +80,9 @@ ..\packages\R.NET.Community.1.6.5\lib\net40\RDotNet.NativeLibrary.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Algorithm.CSharp/packages.config b/Algorithm.CSharp/packages.config index 487a6a74b0bd..73f801143d87 100644 --- a/Algorithm.CSharp/packages.config +++ b/Algorithm.CSharp/packages.config @@ -16,4 +16,5 @@ + \ No newline at end of file diff --git a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj index e06f2ae45820..5982fd163392 100644 --- a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj +++ b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj @@ -62,6 +62,9 @@ ..\packages\NodaTime.1.3.4\lib\net35-Client\NodaTime.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Algorithm.Framework/packages.config b/Algorithm.Framework/packages.config index c7964159d7fd..410a815c356c 100644 --- a/Algorithm.Framework/packages.config +++ b/Algorithm.Framework/packages.config @@ -11,4 +11,5 @@ + \ No newline at end of file diff --git a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj index a49de9386898..f9561e09c93b 100644 --- a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj +++ b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj @@ -231,6 +231,11 @@ + + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + + false true diff --git a/Algorithm.Python/packages.config b/Algorithm.Python/packages.config index 708b9d836408..bb47afba52ee 100644 --- a/Algorithm.Python/packages.config +++ b/Algorithm.Python/packages.config @@ -6,4 +6,5 @@ + \ No newline at end of file diff --git a/Algorithm/QuantConnect.Algorithm.csproj b/Algorithm/QuantConnect.Algorithm.csproj index 4895d8e4a931..9904c5d2dd27 100644 --- a/Algorithm/QuantConnect.Algorithm.csproj +++ b/Algorithm/QuantConnect.Algorithm.csproj @@ -83,6 +83,9 @@ ..\packages\NodaTime.1.3.4\lib\net35-Client\NodaTime.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Algorithm/packages.config b/Algorithm/packages.config index 8b13e7b27f41..98e83fd66fd4 100644 --- a/Algorithm/packages.config +++ b/Algorithm/packages.config @@ -9,4 +9,5 @@ + \ No newline at end of file diff --git a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj index 9e2e69cc9ec4..4b3ccab16e91 100644 --- a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj +++ b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj @@ -48,6 +48,9 @@ ..\packages\NodaTime.1.3.4\lib\net35-Client\NodaTime.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/AlgorithmFactory/packages.config b/AlgorithmFactory/packages.config index 80b3e7292fa4..d013d994e350 100644 --- a/AlgorithmFactory/packages.config +++ b/AlgorithmFactory/packages.config @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/Api/QuantConnect.Api.csproj b/Api/QuantConnect.Api.csproj index c87fe72b06c2..b889dfa96b76 100644 --- a/Api/QuantConnect.Api.csproj +++ b/Api/QuantConnect.Api.csproj @@ -104,6 +104,9 @@ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Api/packages.config b/Api/packages.config index 5d562c043b3d..25bca3df4d53 100644 --- a/Api/packages.config +++ b/Api/packages.config @@ -12,5 +12,6 @@ + \ No newline at end of file diff --git a/Brokerages/QuantConnect.Brokerages.csproj b/Brokerages/QuantConnect.Brokerages.csproj index d94a2065eadc..609caa17a889 100644 --- a/Brokerages/QuantConnect.Brokerages.csproj +++ b/Brokerages/QuantConnect.Brokerages.csproj @@ -162,6 +162,9 @@ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Brokerages/packages.config b/Brokerages/packages.config index 67fa01b0b090..998d40e328ce 100644 --- a/Brokerages/packages.config +++ b/Brokerages/packages.config @@ -11,6 +11,7 @@ + \ No newline at end of file diff --git a/Common/Parse.cs b/Common/Parse.cs new file mode 100644 index 000000000000..6c802717a46a --- /dev/null +++ b/Common/Parse.cs @@ -0,0 +1,112 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System; +using System.Globalization; + +namespace QuantConnect +{ + /// + /// Provides methods for parsing strings using + /// + public static class Parse + { + /// + /// Parses the provided value as a using + /// with + /// + public static TimeSpan TimeSpan(string value) + { + return System.TimeSpan.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// with + /// + public static DateTime DateTime(string value) + { + return System.DateTime.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// with the specified and + /// + public static DateTime DateTimeExact(string value, string format) + { + return System.DateTime.ParseExact(value, format, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// with the specified , and + /// + public static DateTime DateTimeExact(string value, string format, DateTimeStyles dateTimeStyles) + { + return System.DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, dateTimeStyles); + } + + /// + /// Parses the provided value as a using + /// + public static double Double(string value) + { + return double.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// + public static decimal Decimal(string value) + { + return decimal.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using the specified + /// and + /// + public static decimal Decimal(string value, NumberStyles numberStyles) + { + return decimal.Parse(value, numberStyles, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// + public static int Int(string value) + { + return int.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// + public static long Long(string value) + { + return long.Parse(value, CultureInfo.InvariantCulture); + } + + /// + /// Parses the provided value as a using + /// and the specified + /// + public static long Long(string value, NumberStyles numberStyles) + { + return long.Parse(value, numberStyles, CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/Common/QuantConnect.csproj b/Common/QuantConnect.csproj index 7676d16b4a67..abc5d47f8aee 100644 --- a/Common/QuantConnect.csproj +++ b/Common/QuantConnect.csproj @@ -123,6 +123,9 @@ ..\packages\QLNet.1.9.2\lib\net45\QLNet.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + @@ -696,6 +699,8 @@ + + diff --git a/Common/StringExtensions.cs b/Common/StringExtensions.cs new file mode 100644 index 000000000000..5bff73085304 --- /dev/null +++ b/Common/StringExtensions.cs @@ -0,0 +1,209 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System; +using System.Globalization; + +namespace QuantConnect +{ + /// + /// Provides extension methods for properly parsing and serializing values while properly using + /// an IFormatProvider/CultureInfo when applicable + /// + public static class StringExtensions + { + /// + /// Parses the specified string as using + /// + public static T ConvertInvariant(this object convertible) + { + return (T)convertible.ConvertInvariant(typeof(T)); + } + + /// + /// Parses the specified string as using + /// + public static object ConvertInvariant(this object convertible, Type conversionType) + { + return Convert.ChangeType(convertible, conversionType, CultureInfo.InvariantCulture); + } + + /// + /// Non-extension method alias for + /// This supports the using static QuantConnect.StringExtensions syntax + /// and is aimed at ensuring all formatting is piped through this class instead of + /// alternatively piping through directly to + /// + public static string Invariant(FormattableString formattable) + { + return FormattableString.Invariant(formattable); + } + + /// + /// Converts the provided value to a string using + /// + public static string ToStringInvariant(this IConvertible convertible) + { + if (convertible == null) + { + return string.Empty; + } + + return convertible.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Formats the provided value using the specified and + /// + /// + public static string ToStringInvariant(this IFormattable formattable, string format) + { + if (formattable == null) + { + return string.Empty; + } + + // if we have a colon, this implies there's a width parameter in the format it seems this isn't handled + // as one would expect. For example, specifying something like $"{value,10:0.00}" would force the string + // to be at least 10 characters wide with extra padding in the front, but passing the string '10:0.00' or + // ',10:0.00' doesn't work. If we are able to detect a colon in the format and the values preceding the colon, + // are numeric, then we know it starts with a width parameter and we can pipe it into a custom-formed + // string.format call to get the correct output + if (format != null) + { + var indexOfColon = format.IndexOfInvariant(":"); + if (indexOfColon != -1) + { + int padding; + var beforeColon = format.Substring(0, indexOfColon); + if (int.TryParse(beforeColon, out padding)) + { + return string.Format(CultureInfo.InvariantCulture, $"{{0,{format}}}", formattable); + } + } + } + + return formattable.ToString(format, CultureInfo.InvariantCulture); + } + + /// + /// Provides a convenience methods for converting a to an invariant ISO-8601 string + /// + public static string ToIso8601Invariant(this DateTime dateTime) + { + return dateTime.ToStringInvariant("O"); + } + + /// + /// Checks if the string starts with the provided using + /// while optionally ignoring case. + /// + public static bool StartsWithInvariant(this string value, string beginning, bool ignoreCase = false) + { + return value.StartsWith(beginning, ignoreCase, CultureInfo.InvariantCulture); + } + + /// + /// Checks if the string ends with the provided using + /// while optionally ignoring case. + /// + public static bool EndsWithInvariant(this string value, string ending, bool ignoreCase = false) + { + return value.EndsWith(ending, ignoreCase, CultureInfo.CurrentCulture); + } + + /// + /// Gets the index of the specified using + /// + public static int IndexOfInvariant(this string value, char character) + { + return value.IndexOf(character); + } + + /// + /// Gets the index of the specified using + /// + public static int IndexOfInvariant(this string value, string substring, bool ignoreCase = false) + { + return value.IndexOf(substring, ignoreCase + ? StringComparison.InvariantCultureIgnoreCase + : StringComparison.InvariantCulture + ); + } + + /// + /// Gets the index of the specified using + /// + public static int LastIndexOfInvariant(this string value, string substring, bool ignoreCase = false) + { + return value.LastIndexOf(substring, ignoreCase + ? StringComparison.InvariantCultureIgnoreCase + : StringComparison.InvariantCulture + ); + } + + /// + /// Provides a shorthand for avoiding the more verbose ternary equivalent. + /// Consider the following: + /// + /// string.IsNullOrEmpty(str) ? (decimal?)null : Convert.ToDecimal(str, CultureInfo.InvariantCulture) + /// + /// Can be expressed as: + /// + /// str.IfNotNullOrEmpty<decimal?>(s => Convert.ToDecimal(str, CultureInfo.InvariantCulture)) + /// + /// When combined with additional methods from this class, reducing further to a declarative: + /// + /// str.IfNotNullOrEmpty<decimal?>(s => s.ParseDecimalInvariant()) + /// str.IfNotNullOrEmpty<decimal?>(s => s.ConvertInvariant<decimal>()) + /// + /// + /// The string value to check for null or empty + /// The default value to use if null or empty + /// Function run on non-null string w/ length > 0 + public static T IfNotNullOrEmpty(this string value, T defaultValue, Func func) + { + if (string.IsNullOrEmpty(value)) + { + return defaultValue; + } + + return func(value); + } + + /// + /// Provides a shorthand for avoiding the more verbose ternary equivalent. + /// Consider the following: + /// + /// string.IsNullOrEmpty(str) ? (decimal?)null : Convert.ToDecimal(str, CultureInfo.InvariantCulture) + /// + /// Can be expressed as: + /// + /// str.IfNotNullOrEmpty<decimal?>(s => Convert.ToDecimal(str, CultureInfo.InvariantCulture)) + /// + /// When combined with additional methods from this class, reducing further to a declarative: + /// + /// str.IfNotNullOrEmpty<decimal?>(s => s.ParseDecimalInvariant()) + /// str.IfNotNullOrEmpty<decimal?>(s => s.ConvertInvariant<decimal>()) + /// + /// + /// The string value to check for null or empty + /// Function run on non-null string w/ length > 0 + public static T IfNotNullOrEmpty(this string value, Func func) + { + return value.IfNotNullOrEmpty(default(T), func); + } + } +} \ No newline at end of file diff --git a/Common/packages.config b/Common/packages.config index 9ba41e6099e5..2e5e712edfc4 100644 --- a/Common/packages.config +++ b/Common/packages.config @@ -14,4 +14,5 @@ + \ No newline at end of file diff --git a/Compression/QuantConnect.Compression.csproj b/Compression/QuantConnect.Compression.csproj index 2fc260a3cc0b..77e934407c46 100644 --- a/Compression/QuantConnect.Compression.csproj +++ b/Compression/QuantConnect.Compression.csproj @@ -57,6 +57,9 @@ ..\packages\ICSharpCode.SharpZipLib.dll.0.85.4.369\lib\net20\ICSharpCode.SharpZipLib.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Compression/packages.config b/Compression/packages.config index 1e7bc106cf37..4d026b60ce16 100644 --- a/Compression/packages.config +++ b/Compression/packages.config @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/Configuration/QuantConnect.Configuration.csproj b/Configuration/QuantConnect.Configuration.csproj index 12adca895442..09ae154df89d 100644 --- a/Configuration/QuantConnect.Configuration.csproj +++ b/Configuration/QuantConnect.Configuration.csproj @@ -58,6 +58,9 @@ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Configuration/packages.config b/Configuration/packages.config index a29efb1e334c..41ce96b1452f 100644 --- a/Configuration/packages.config +++ b/Configuration/packages.config @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/Engine/QuantConnect.Lean.Engine.csproj b/Engine/QuantConnect.Lean.Engine.csproj index 45a8a8cb4776..a1c34dba5508 100644 --- a/Engine/QuantConnect.Lean.Engine.csproj +++ b/Engine/QuantConnect.Lean.Engine.csproj @@ -144,6 +144,9 @@ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Engine/packages.config b/Engine/packages.config index cf146b1424ba..c23df19ede03 100644 --- a/Engine/packages.config +++ b/Engine/packages.config @@ -12,4 +12,5 @@ + \ No newline at end of file diff --git a/Indicators/QuantConnect.Indicators.csproj b/Indicators/QuantConnect.Indicators.csproj index 77494e9548f7..88c0ae6bc8dd 100644 --- a/Indicators/QuantConnect.Indicators.csproj +++ b/Indicators/QuantConnect.Indicators.csproj @@ -48,6 +48,9 @@ ..\packages\MathNet.Numerics.3.19.0\lib\net40\MathNet.Numerics.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Indicators/packages.config b/Indicators/packages.config index 36d5bfafa6e9..a7fd66e570e6 100644 --- a/Indicators/packages.config +++ b/Indicators/packages.config @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/Jupyter/QuantConnect.Jupyter.csproj b/Jupyter/QuantConnect.Jupyter.csproj index 4aa443d559ec..ad02c58746f7 100644 --- a/Jupyter/QuantConnect.Jupyter.csproj +++ b/Jupyter/QuantConnect.Jupyter.csproj @@ -48,6 +48,9 @@ ..\packages\NodaTime.1.3.4\lib\net35-Client\NodaTime.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Jupyter/packages.config b/Jupyter/packages.config index 80b3e7292fa4..d013d994e350 100644 --- a/Jupyter/packages.config +++ b/Jupyter/packages.config @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/Launcher/QuantConnect.Lean.Launcher.csproj b/Launcher/QuantConnect.Lean.Launcher.csproj index 05bb3d2283dd..7ebc7ea7c051 100644 --- a/Launcher/QuantConnect.Lean.Launcher.csproj +++ b/Launcher/QuantConnect.Lean.Launcher.csproj @@ -146,6 +146,9 @@ ..\packages\R.NET.Community.1.6.5\lib\net40\RDotNet.NativeLibrary.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Launcher/packages.config b/Launcher/packages.config index 2012b60b4f76..0da44c0aeece 100644 --- a/Launcher/packages.config +++ b/Launcher/packages.config @@ -9,4 +9,5 @@ + \ No newline at end of file diff --git a/Logging/QuantConnect.Logging.csproj b/Logging/QuantConnect.Logging.csproj index f205d1b6be92..db1336df043a 100644 --- a/Logging/QuantConnect.Logging.csproj +++ b/Logging/QuantConnect.Logging.csproj @@ -89,6 +89,9 @@ ..\packages\NLog.4.4.11\lib\net45\NLog.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Logging/packages.config b/Logging/packages.config index ff2bf01af109..0111dfe96cca 100644 --- a/Logging/packages.config +++ b/Logging/packages.config @@ -10,4 +10,5 @@ + \ No newline at end of file diff --git a/Messaging/QuantConnect.Messaging.csproj b/Messaging/QuantConnect.Messaging.csproj index b59a1d65bec8..c54bb8763dc8 100644 --- a/Messaging/QuantConnect.Messaging.csproj +++ b/Messaging/QuantConnect.Messaging.csproj @@ -60,6 +60,9 @@ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Messaging/packages.config b/Messaging/packages.config index 7918817e5da3..006d1b58e438 100644 --- a/Messaging/packages.config +++ b/Messaging/packages.config @@ -9,4 +9,5 @@ + \ No newline at end of file diff --git a/Queues/QuantConnect.Queues.csproj b/Queues/QuantConnect.Queues.csproj index f7ad00a66992..d0bfd59af309 100644 --- a/Queues/QuantConnect.Queues.csproj +++ b/Queues/QuantConnect.Queues.csproj @@ -51,6 +51,9 @@ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/Queues/packages.config b/Queues/packages.config index 09c71c7fbb9d..00024eee5919 100644 --- a/Queues/packages.config +++ b/Queues/packages.config @@ -6,4 +6,5 @@ + \ No newline at end of file diff --git a/Tests/Common/ParseTests.cs b/Tests/Common/ParseTests.cs new file mode 100644 index 000000000000..e297e387ecf4 --- /dev/null +++ b/Tests/Common/ParseTests.cs @@ -0,0 +1,104 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System; +using System.Globalization; +using NUnit.Framework; + +namespace QuantConnect.Tests.Common +{ + [TestFixture] + public class ParseTests + { + [Test] + public void DoubleIsTheSameAs_DoubleDotParse_WithInvariantCulture() + { + var str = "1,123.45608"; + var expected = double.Parse(str, CultureInfo.InvariantCulture); + var actual = Parse.Double(str); + Assert.IsInstanceOf(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void DecimalIsTheSameAs_DecimalDotParse_WithInvariantCulture() + { + var str = "1,123.45608"; + var expected = decimal.Parse(str, CultureInfo.InvariantCulture); + var actual = Parse.Decimal(str); + Assert.IsInstanceOf(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void DecimalSupports_NumberStyles() + { + var str = "1e-3"; + var expected = 0.001m; + var actual = Parse.Decimal(str, NumberStyles.AllowExponent); + Assert.IsInstanceOf(expected); + Assert.AreEqual(expected, actual); + } + + [Test] + public void LongIsTheSameAs_LongDotParse_WithInvariantCulture() + { + var str = "1123"; + var expected = long.Parse(str, CultureInfo.InvariantCulture); + var actual = Parse.Long(str); + Assert.IsInstanceOf(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void IntIsTheSameAs_IntDotParse_WithInvariantCulture() + { + var str = "1123"; + var expected = int.Parse(str, CultureInfo.InvariantCulture); + var actual = Parse.Int(str); + Assert.IsInstanceOf(actual); + Assert.AreEqual(expected, actual); + } + + [Test] + public void DateTimeIsTheSameAs_DateTimeDotParse_WithInvariantCulture() + { + var str = "7/29/2019"; + var expected = DateTime.Parse(str, CultureInfo.InvariantCulture); + var actual = Parse.DateTime(str); + Assert.AreEqual(expected, actual); + } + + [Test] + public void DateTimeWithFormat_IsTheSameAs_DateTimeDotParse_WithFormatAndInvariantCulture() + { + var str = "07+29+2019"; + var format = "MM+dd+2019"; + var expected = DateTime.ParseExact(str, format, CultureInfo.InvariantCulture); + var actual = Parse.DateTimeExact(str, format); + Assert.AreEqual(expected, actual); + } + + [Test] + public void DateTimeExact_IsTheSameAs_DateTimeDotParseExact_WithFormatAndInvariantCulture() + { + var str = "07/29-2019Q14.22"; + var format = "MM/dd-yyyyQHH.mm"; + var expected = DateTime.ParseExact(str, format, CultureInfo.InvariantCulture); + var actual = Parse.DateTimeExact(str, format); + Assert.AreEqual(expected, actual); + } + } +} \ No newline at end of file diff --git a/Tests/Common/StringExtensionsTests.cs b/Tests/Common/StringExtensionsTests.cs new file mode 100644 index 000000000000..18073c5b7529 --- /dev/null +++ b/Tests/Common/StringExtensionsTests.cs @@ -0,0 +1,185 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System; +using System.Globalization; +using NUnit.Framework; + +namespace QuantConnect.Tests.Common +{ + [TestFixture] + public class StringExtensionsTests + { + [Test] + [TestCase(typeof(string), "123", typeof(long), 123L)] + [TestCase(typeof(string), "123", typeof(int), 123)] + [TestCase(typeof(string), "123", typeof(decimal), 123)] + [TestCase(typeof(long), "123", typeof(decimal), 123)] + public void ConvertInvariant(Type sourceType, string sourceString, Type conversionType, object expected) + { + // we can't put a decimal in the attribute, so this ensure the runtime types are correct + expected = Convert.ChangeType(expected, conversionType, CultureInfo.InvariantCulture); + Assert.IsInstanceOf(conversionType, expected); + + var source = Convert.ChangeType(sourceString, sourceType, CultureInfo.InvariantCulture); + Assert.IsInstanceOf(sourceType, source); + + var converted = source.ConvertInvariant(conversionType); + Assert.AreEqual(expected, converted); + } + + [Test] + public void ConvertInvariantString() + { + var source = 123L; + const string expected = "123"; + var actual = source.ConvertInvariant(); + Assert.AreEqual(expected, actual); + } + + [Test] + public void Convertible_ToStringInvariant_Equals_ToStringInvariantCulture() + { + IConvertible convertible = 1; + var formatted = convertible.ToStringInvariant(); + Assert.AreEqual(convertible.ToString(CultureInfo.InvariantCulture), formatted); + } + + [Test] + public void Formattable_ToStringInvariant_DoesNotRequire_FormatParameter() + { + var format = (string) null; + IFormattable formattable = 1; + var formatted = formattable.ToStringInvariant(format); + Assert.AreEqual(formattable.ToString(format, CultureInfo.InvariantCulture), formatted); + } + + [Test] + [TestCase(TypeCode.DateTime, "07/28/2019", "yyyy-MM-dd")] + public void Formattable_ToStringInvariant_UsesProvided_FormatParameter( + TypeCode typeCode, + string value, + string format + ) + { + var formattable = (IFormattable) Convert.ChangeType(value, typeCode, null); + var formatted = formattable.ToStringInvariant(format); + var expected = formattable.ToString(format, CultureInfo.InvariantCulture); + Assert.AreEqual(expected, formatted, $"Failed on type code: {typeCode}"); + } + + [Test] + public void Formattable_ToStringInvariant_RespectsFieldWidth_InFormatParameter() + { + // BEHAVIOR CHANGE -- + var value = 5.678m; + var format = "-20:P2"; + var sameBehavior = $"{value,-20:P2}"; + var expected = string.Format(CultureInfo.InvariantCulture, "{0,-20:P2}", value); + + // the usage of the InvariantCulture add a space between the number of the percent sign + //Assert.AreEqual("567.80% ", sameBehavior); // this passes in windows but failed in travis + Assert.AreEqual("567.80 % ", expected); + + var actual = value.ToStringInvariant(format); + Assert.AreEqual(expected, actual); + } + + [Test] + public void StartsWithInvariant_IsTheSameAs_StartsWith_WithInvariantCulture_DefaultingToCaseSensitive() + { + var str = "aBcD"; + var beginning = "aB"; + Assert.IsTrue(str.StartsWithInvariant(beginning)); + + beginning = "AB"; + Assert.IsFalse(str.StartsWithInvariant(beginning)); + Assert.IsTrue(str.StartsWithInvariant(beginning, ignoreCase: true)); + } + + [Test] + public void EndsWithInvariant_IsTheSameAs_EndsWith_WithInvariantCulture_DefaultingToCaseSensitive() + { + var str = "aBcD"; + var ending = "cD"; + Assert.IsTrue(str.EndsWithInvariant(ending)); + + ending = "CD"; + Assert.IsFalse(str.EndsWithInvariant(ending)); + Assert.IsTrue(str.EndsWithInvariant(ending, ignoreCase: true)); + } + + [Test] + public void ToIso8601Invariant_IsTheSameAs_ToString_O_WithInvariantCulture() + { + var date = DateTime.UtcNow; + var expected = date.ToString("O", CultureInfo.InvariantCulture); + var actual = date.ToIso8601Invariant(); + Assert.AreEqual(expected, actual); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void IndexOfInvariant_IsTheSameAs_IndexOf_WithStringComparisonInvariantCulture(bool ignoreCase) + { + var comparison = ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture; + var str = "abcdefg"; + var substring1 = "de"; + var expected1 = str.IndexOf(substring1, comparison); + var actual1 = str.IndexOfInvariant(substring1, ignoreCase); + Assert.AreEqual(expected1, actual1); + + var substring2 = "dE"; + var expected2 = str.IndexOf(substring2, comparison); + var actual2 = str.IndexOfInvariant(substring2, ignoreCase); + Assert.AreEqual(expected2, actual2); + } + + [Test] + [TestCase("")] + [TestCase(null)] + public void IfNotNullOrEmpty_ReturnsSpecifiedDefaultValue_WhenNullOrEmpty(string str) + { + int defaultValue = 42; + var actual = str.IfNotNullOrEmpty(defaultValue, int.Parse); + Assert.AreEqual(defaultValue, actual); + } + + [Test] + [TestCase("")] + [TestCase(null)] + public void IfNotNullOrEmpty_ReturnsDefaultValue_WhenNullOrEmpty(string str) + { + var actual = str.IfNotNullOrEmpty(int.Parse); + Assert.AreEqual(default(int), actual); + } + + [Test] + public void IfNotNullOrEmpty_ReturnsResultOfFunc_WhenNotEmpty() + { + var actual = "42".IfNotNullOrEmpty(int.Parse); + Assert.AreEqual(42, actual); + } + + [Test] + public void IfNotNullOrEmpty_ReturnsResultOfFunc_WhenNotEmptyAndDefaultValueSpecified() + { + var defaultValue = -42; + var actual = "42".IfNotNullOrEmpty(defaultValue, int.Parse); + Assert.AreEqual(42, actual); + } + } +} \ No newline at end of file diff --git a/Tests/QuantConnect.Tests.csproj b/Tests/QuantConnect.Tests.csproj index 397b5d969ef3..3a0b75e05f96 100644 --- a/Tests/QuantConnect.Tests.csproj +++ b/Tests/QuantConnect.Tests.csproj @@ -176,6 +176,9 @@ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + @@ -321,6 +324,8 @@ + + diff --git a/Tests/packages.config b/Tests/packages.config index 942c77942837..baf8f43b7437 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -20,5 +20,6 @@ + \ No newline at end of file diff --git a/ToolBox/QuantConnect.ToolBox.csproj b/ToolBox/QuantConnect.ToolBox.csproj index 9ad32373301d..ffd307c390fc 100644 --- a/ToolBox/QuantConnect.ToolBox.csproj +++ b/ToolBox/QuantConnect.ToolBox.csproj @@ -163,6 +163,9 @@ ..\packages\SocketIoClientDotNet.1.0.7.1\lib\net45\SocketIoClientDotNet.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + ..\packages\SuperSocket.ClientEngine.Core.0.10.0\lib\net45\SuperSocket.ClientEngine.dll diff --git a/ToolBox/packages.config b/ToolBox/packages.config index 26f822d01d8c..b4ec176d6307 100644 --- a/ToolBox/packages.config +++ b/ToolBox/packages.config @@ -16,6 +16,7 @@ + diff --git a/UserInterface/QuantConnect.Views.csproj b/UserInterface/QuantConnect.Views.csproj index c7ef1ca938e2..37cbc3ff2731 100644 --- a/UserInterface/QuantConnect.Views.csproj +++ b/UserInterface/QuantConnect.Views.csproj @@ -99,6 +99,9 @@ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/UserInterface/packages.config b/UserInterface/packages.config new file mode 100644 index 000000000000..081c0e38a211 --- /dev/null +++ b/UserInterface/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VisualStudioPlugin/QuantConnect.VisualStudio17Plugin.csproj b/VisualStudioPlugin/QuantConnect.VisualStudio17Plugin.csproj index 206d25e76bb6..90738234c93c 100644 --- a/VisualStudioPlugin/QuantConnect.VisualStudio17Plugin.csproj +++ b/VisualStudioPlugin/QuantConnect.VisualStudio17Plugin.csproj @@ -275,6 +275,9 @@ False + + ..\packages\StringInterpolationBridgeStrong.0.9.1\lib\net40\StringInterpolationBridge.dll + diff --git a/VisualStudioPlugin/packages.QuantConnect.VisualStudio17Plugin.config b/VisualStudioPlugin/packages.QuantConnect.VisualStudio17Plugin.config index 5c19dde9c71c..8fe9478ff2d0 100644 --- a/VisualStudioPlugin/packages.QuantConnect.VisualStudio17Plugin.config +++ b/VisualStudioPlugin/packages.QuantConnect.VisualStudio17Plugin.config @@ -20,4 +20,5 @@ + \ No newline at end of file