diff --git a/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationFileParser.cs b/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationFileParser.cs new file mode 100644 index 00000000..b4aae7b4 --- /dev/null +++ b/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationFileParser.cs @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Framework.ConfigurationModel.Json +{ + internal class JsonConfigurationFileParser + { + private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Stack _context = new Stack(); + private string _currentPath; + + private JsonTextReader _reader; + + public IDictionary Parse(Stream input) + { + _data.Clear(); + _reader = new JsonTextReader(new StreamReader(input)); + _reader.DateParseHandling = DateParseHandling.None; + + var jsonConfig = JObject.Load(_reader); + + VisitJObject(jsonConfig); + + return _data; + } + + private void VisitJObject(JObject jObject) + { + foreach (var property in jObject.Properties()) + { + EnterContext(property.Name); + VisitProperty(property); + ExitContext(); + } + } + + private void VisitProperty(JProperty property) + { + VisitToken(property.Value); + } + + private void VisitToken(JToken token) + { + switch (token.Type) + { + case JTokenType.Object: + VisitJObject(token.Value()); + break; + + case JTokenType.Array: + VisitArray(token.Value()); + break; + + case JTokenType.Integer: + case JTokenType.Float: + case JTokenType.String: + case JTokenType.Boolean: + case JTokenType.Bytes: + case JTokenType.Raw: + case JTokenType.Null: + VisitPrimitive(token); + break; + + default: + throw new FormatException(Resources.FormatError_UnsupportedJSONToken( + _reader.TokenType, + _reader.Path, + _reader.LineNumber, + _reader.LinePosition)); + } + } + + private void VisitArray(JArray array) + { + for (int index = 0; index < array.Count; index++) + { + EnterContext(index.ToString()); + VisitToken(array[index]); + ExitContext(); + } + } + + private void VisitPrimitive(JToken data) + { + var key = _currentPath; + + if (_data.ContainsKey(key)) + { + throw new FormatException(Resources.FormatError_KeyIsDuplicated(key)); + } + _data[key] = data.ToString(); + } + + private void EnterContext(string context) + { + _context.Push(context); + _currentPath = string.Join(":", _context.Reverse()); + } + + private void ExitContext() + { + _context.Pop(); + _currentPath = string.Join(":", _context.Reverse()); + } + } +} diff --git a/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationSource.cs b/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationSource.cs index 81a54371..014d85ee 100644 --- a/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationSource.cs +++ b/src/Microsoft.Framework.ConfigurationModel.Json/JsonConfigurationSource.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using Microsoft.Framework.ConfigurationModel.Json; -using Newtonsoft.Json; namespace Microsoft.Framework.ConfigurationModel { @@ -78,129 +77,8 @@ public override void Load() internal void Load(Stream stream) { - var data = new Dictionary(StringComparer.OrdinalIgnoreCase); - - using (var reader = new JsonTextReader(new StreamReader(stream))) - { - var startObjectCount = 0; - - // Dates are parsed as strings - reader.DateParseHandling = DateParseHandling.None; - - // Move to the first token - reader.Read(); - - SkipComments(reader); - - if (reader.TokenType != JsonToken.StartObject) - { - throw new FormatException(Resources.FormatError_RootMustBeAnObject(reader.Path, - reader.LineNumber, reader.LinePosition)); - } - - do - { - SkipComments(reader); - - switch (reader.TokenType) - { - case JsonToken.StartObject: - startObjectCount++; - break; - - case JsonToken.EndObject: - startObjectCount--; - break; - - // Keys in key-value pairs - case JsonToken.PropertyName: - break; - - // Values in key-value pairs - case JsonToken.Integer: - case JsonToken.Float: - case JsonToken.String: - case JsonToken.Boolean: - case JsonToken.Bytes: - case JsonToken.Raw: - case JsonToken.Null: - var key = GetKey(reader.Path); - - if (data.ContainsKey(key)) - { - throw new FormatException(Resources.FormatError_KeyIsDuplicated(key)); - } - data[key] = reader.Value.ToString(); - break; - - // End of file - case JsonToken.None: - { - throw new FormatException(Resources.FormatError_UnexpectedEnd(reader.Path, - reader.LineNumber, reader.LinePosition)); - } - - default: - { - // Unsupported elements: Array, Constructor, Undefined - throw new FormatException(Resources.FormatError_UnsupportedJSONToken( - reader.TokenType, reader.Path, reader.LineNumber, reader.LinePosition)); - } - } - - reader.Read(); - - } while (startObjectCount > 0); - } - - Data = data; - } - - private string GetKey(string jsonPath) - { - var pathSegments = new List(); - var index = 0; - - while (index < jsonPath.Length) - { - // If the JSON element contains '.' in its name, JSON.net escapes that element as ['element'] - // while getting its Path. So before replacing '.' => ':' to represent JSON hierarchy, here - // we skip a '.' => ':' conversion if the element is not enclosed with in ['..']. - var start = jsonPath.IndexOf("['", index); - - if (start < 0) - { - // No more ['. Skip till end of string. - pathSegments.Add(jsonPath. - Substring(index). - Replace('.', ':')); - break; - } - else - { - if (start > index) - { - pathSegments.Add( - jsonPath - .Substring(index, start - index) // Anything between the previous [' and ']. - .Replace('.', ':')); - } - - var endIndex = jsonPath.IndexOf("']", start); - pathSegments.Add(jsonPath.Substring(start + 2, endIndex - start - 2)); - index = endIndex + 2; - } - } - - return string.Join(string.Empty, pathSegments); - } - - private void SkipComments(JsonReader reader) - { - while (reader.TokenType == JsonToken.Comment) - { - reader.Read(); - } + JsonConfigurationFileParser parser = new JsonConfigurationFileParser(); + Data = parser.Parse(stream); } } } diff --git a/src/Microsoft.Framework.ConfigurationModel.Json/Properties/Resources.Designer.cs b/src/Microsoft.Framework.ConfigurationModel.Json/Properties/Resources.Designer.cs index 6fedcbd2..d5bf2966 100644 --- a/src/Microsoft.Framework.ConfigurationModel.Json/Properties/Resources.Designer.cs +++ b/src/Microsoft.Framework.ConfigurationModel.Json/Properties/Resources.Designer.cs @@ -90,38 +90,6 @@ internal static string FormatError_KeyIsDuplicated(object p0) return string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0); } - /// - /// Only an object can be the root. Path '{0}', line {1} position {2}. - /// - internal static string Error_RootMustBeAnObject - { - get { return GetString("Error_RootMustBeAnObject"); } - } - - /// - /// Only an object can be the root. Path '{0}', line {1} position {2}. - /// - internal static string FormatError_RootMustBeAnObject(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Error_RootMustBeAnObject"), p0, p1, p2); - } - - /// - /// Unexpected end when parsing JSON. Path '{0}', line {1} position {2}. - /// - internal static string Error_UnexpectedEnd - { - get { return GetString("Error_UnexpectedEnd"); } - } - - /// - /// Unexpected end when parsing JSON. Path '{0}', line {1} position {2}. - /// - internal static string FormatError_UnexpectedEnd(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Error_UnexpectedEnd"), p0, p1, p2); - } - /// /// Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}. /// diff --git a/src/Microsoft.Framework.ConfigurationModel.Json/Resources.resx b/src/Microsoft.Framework.ConfigurationModel.Json/Resources.resx index 879590b5..60aef7d8 100644 --- a/src/Microsoft.Framework.ConfigurationModel.Json/Resources.resx +++ b/src/Microsoft.Framework.ConfigurationModel.Json/Resources.resx @@ -132,12 +132,6 @@ A duplicate key '{0}' was found. - - Only an object can be the root. Path '{0}', line {1} position {2}. - - - Unexpected end when parsing JSON. Path '{0}', line {1} position {2}. - Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}. diff --git a/src/Microsoft.Framework.ConfigurationModel.Json/project.json b/src/Microsoft.Framework.ConfigurationModel.Json/project.json index 7f52fb8a..9ee56a85 100644 --- a/src/Microsoft.Framework.ConfigurationModel.Json/project.json +++ b/src/Microsoft.Framework.ConfigurationModel.Json/project.json @@ -9,6 +9,10 @@ "frameworks": { "net45": { }, "dnx451": { }, - "dnxcore50": { } + "dnxcore50": { + "dependencies": { + "System.Dynamic.Runtime": "4.0.10-*" + } + } } } diff --git a/src/Microsoft.Framework.ConfigurationModel/ConfigurationKeyComparer.cs b/src/Microsoft.Framework.ConfigurationModel/ConfigurationKeyComparer.cs new file mode 100644 index 00000000..85114c2a --- /dev/null +++ b/src/Microsoft.Framework.ConfigurationModel/ConfigurationKeyComparer.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Framework.ConfigurationModel +{ + public class ConfigurationKeyComparer : IComparer + { + private const char Separator = ':'; + + public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer(); + + public int Compare(string x, string y) + { + var xParts = x?.Split(Separator) ?? new string[0]; + var yParts = y?.Split(Separator) ?? new string[0]; + + // Compare each part until we get two parts that are not equal + for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++) + { + x = xParts[i]; + y = yParts[i]; + + var value1 = 0; + var value2 = 0; + + var xIsInt = x != null && int.TryParse(x, out value1); + var yIsInt = y != null && int.TryParse(y, out value2); + + int result = 0; + + if (!xIsInt && !yIsInt) + { + // Both are strings + result = string.Compare(x, y, StringComparison.OrdinalIgnoreCase); + } + else if (xIsInt && yIsInt) + { + // Both are int + result = value1 - value2; + } + else + { + // Only one of them is int + result = xIsInt ? -1 : 1; + } + + if (result != 0) + { + // One of them is different + return result; + } + } + + // If we get here, the common parts are equal. + // If they are of the same length, then they are totally identical + return xParts.Length - yParts.Length; + } + } +} diff --git a/src/Microsoft.Framework.ConfigurationModel/Properties/AssemblyInfo.cs b/src/Microsoft.Framework.ConfigurationModel/Properties/AssemblyInfo.cs index 8a785635..3903ba73 100644 --- a/src/Microsoft.Framework.ConfigurationModel/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Framework.ConfigurationModel/Properties/AssemblyInfo.cs @@ -39,5 +39,6 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("Microsoft.Framework.ConfigurationModel.Test")] +[assembly: InternalsVisibleTo("Microsoft.Framework.ConfigurationModel.Json.Test")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyMetadata("Serviceable", "True")] diff --git a/src/Microsoft.Framework.ConfigurationModel/Sources/ConfigurationSource.cs b/src/Microsoft.Framework.ConfigurationModel/Sources/ConfigurationSource.cs index 18a9b6aa..886725ad 100644 --- a/src/Microsoft.Framework.ConfigurationModel/Sources/ConfigurationSource.cs +++ b/src/Microsoft.Framework.ConfigurationModel/Sources/ConfigurationSource.cs @@ -35,7 +35,8 @@ public virtual IEnumerable ProduceSubKeys(IEnumerable earlierKey return Data .Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) .Select(kv => Segment(kv.Key, prefix, delimiter)) - .Concat(earlierKeys); + .Concat(earlierKeys) + .OrderBy(k => k, ConfigurationKeyComparer.Instance); } private static string Segment(string key, string prefix, string delimiter) diff --git a/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ArrayTests.cs b/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ArrayTests.cs new file mode 100644 index 00000000..c9993f0b --- /dev/null +++ b/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ArrayTests.cs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using Xunit; + +namespace Microsoft.Framework.ConfigurationModel.FunctionalTests +{ + public class ArrayTests : IDisposable + { + private string _iniConfigFilePath; + private string _xmlConfigFilePath; + private string _json1ConfigFilePath; + private string _json2ConfigFilePath; + + private static readonly string _iniConfigFileContent = @" +[address] +2=ini_2.2.2.2 +i=ini_i.i.i.i +"; + private static readonly string _xmlConfigFileContent = @" + +
xml_4.4.4.4
+
xml_1.1.1.1
+
xml_x.x.x.x
+
+"; + private static readonly string _json1ConfigFileContent = @" +{ + 'address': [ + 'json_0.0.0.0', + 'json_1.1.1.1', + 'json_2.2.2.2' + ] +} +"; + + private static readonly string _json2ConfigFileContent = @" +{ + 'address': { + 'j': 'json_j.j.j.j', + '3': 'json_3.3.3.3' + } +} +"; + + [Fact] + public void DifferentConfigSources_Merged_KeysAreSorted() + { + var config = new Configuration(); + + config.AddJsonFile(_json1ConfigFilePath); + config.AddIniFile(_iniConfigFilePath); + config.AddJsonFile(_json2ConfigFilePath); + config.AddXmlFile(_xmlConfigFilePath); + + var subkey = config.GetSubKey("address"); + var indexSubkeys = subkey.GetSubKeys().ToArray(); + + Assert.Equal(8, indexSubkeys.Length); + Assert.Equal("0", indexSubkeys[0].Key); + Assert.Equal("1", indexSubkeys[1].Key); + Assert.Equal("2", indexSubkeys[2].Key); + Assert.Equal("3", indexSubkeys[3].Key); + Assert.Equal("4", indexSubkeys[4].Key); + Assert.Equal("i", indexSubkeys[5].Key); + Assert.Equal("j", indexSubkeys[6].Key); + Assert.Equal("x", indexSubkeys[7].Key); + } + + [Fact] + public void DifferentConfigSources_Merged_WithOverwrites() + { + var config = new Configuration(); + + config.AddJsonFile(_json1ConfigFilePath); + config.AddIniFile(_iniConfigFilePath); + config.AddJsonFile(_json2ConfigFilePath); + config.AddXmlFile(_xmlConfigFilePath); + + Assert.Equal("json_0.0.0.0", config.Get("address:0")); + Assert.Equal("xml_1.1.1.1", config.Get("address:1")); + Assert.Equal("ini_2.2.2.2", config.Get("address:2")); + Assert.Equal("json_3.3.3.3", config.Get("address:3")); + Assert.Equal("xml_4.4.4.4", config.Get("address:4")); + Assert.Equal("ini_i.i.i.i", config.Get("address:i")); + Assert.Equal("json_j.j.j.j", config.Get("address:j")); + Assert.Equal("xml_x.x.x.x", config.Get("address:x")); + } + + public ArrayTests() + { + _iniConfigFilePath = Path.GetTempFileName(); + _xmlConfigFilePath = Path.GetTempFileName(); + _json1ConfigFilePath = Path.GetTempFileName(); + _json2ConfigFilePath = Path.GetTempFileName(); + + File.WriteAllText(_iniConfigFilePath, _iniConfigFileContent); + File.WriteAllText(_xmlConfigFilePath, _xmlConfigFileContent); + File.WriteAllText(_json1ConfigFilePath, _json1ConfigFileContent); + File.WriteAllText(_json2ConfigFilePath, _json2ConfigFileContent); + } + + public void Dispose() + { + File.Delete(_iniConfigFilePath); + File.Delete(_xmlConfigFilePath); + File.Delete(_json1ConfigFilePath); + File.Delete(_json2ConfigFilePath); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ConfigurationTests.cs b/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ConfigurationTests.cs index 024bfdfa..4c5a03cb 100644 --- a/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ConfigurationTests.cs +++ b/test/Microsoft.Framework.ConfigurationModel.FunctionalTests/ConfigurationTests.cs @@ -7,7 +7,7 @@ using System.Linq; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class ConfigurationTests : IDisposable { diff --git a/test/Microsoft.Framework.ConfigurationModel.Json.Test/ArrayTest.cs b/test/Microsoft.Framework.ConfigurationModel.Json.Test/ArrayTest.cs new file mode 100644 index 00000000..3c227272 --- /dev/null +++ b/test/Microsoft.Framework.ConfigurationModel.Json.Test/ArrayTest.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.Framework.ConfigurationModel.Test; +using Xunit; + +namespace Microsoft.Framework.ConfigurationModel.Json.Test +{ + public class ArrayTest + { + [Fact] + public void ArraysAreConvertedToKeyValuePairs() + { + var json = @"{ + 'ip': [ + '1.2.3.4', + '7.8.9.10', + '11.12.13.14' + ] + }"; + + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); + + Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0")); + Assert.Equal("7.8.9.10", jsonConfigSource.Get("ip:1")); + Assert.Equal("11.12.13.14", jsonConfigSource.Get("ip:2")); + } + + [Fact] + public void ArrayOfObjects() + { + var json = @"{ + 'ip': [ + { + 'address': '1.2.3.4', + 'hidden': false + }, + { + 'address': '5.6.7.8', + 'hidden': true + } + ] + }"; + + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); + + Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0:address")); + Assert.Equal("False", jsonConfigSource.Get("ip:0:hidden")); + Assert.Equal("5.6.7.8", jsonConfigSource.Get("ip:1:address")); + Assert.Equal("True", jsonConfigSource.Get("ip:1:hidden")); + } + + [Fact] + public void NestedArrays() + { + var json = @"{ + 'ip': [ + [ + '1.2.3.4', + '5.6.7.8' + ], + [ + '9.10.11.12', + '13.14.15.16' + ], + ] + }"; + + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); + + Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0:0")); + Assert.Equal("5.6.7.8", jsonConfigSource.Get("ip:0:1")); + Assert.Equal("9.10.11.12", jsonConfigSource.Get("ip:1:0")); + Assert.Equal("13.14.15.16", jsonConfigSource.Get("ip:1:1")); + } + + [Fact] + public void ImplicitArrayItemReplacement() + { + var json1 = @"{ + 'ip': [ + '1.2.3.4', + '7.8.9.10', + '11.12.13.14' + ] + }"; + + var json2 = @"{ + 'ip': [ + '15.16.17.18' + ] + }"; + + var jsonConfigSource1 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource1.Load(TestStreamHelpers.StringToStream(json1)); + + var jsonConfigSource2 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource2.Load(TestStreamHelpers.StringToStream(json2)); + + var config = new Configuration(); + config.AddLoadedSource(jsonConfigSource1); + config.AddLoadedSource(jsonConfigSource2); + + Assert.Equal(3, config.GetSubKeys("ip").Count()); + Assert.Equal("15.16.17.18", config.Get("ip:0")); + Assert.Equal("7.8.9.10", config.Get("ip:1")); + Assert.Equal("11.12.13.14", config.Get("ip:2")); + } + + [Fact] + public void ExplicitArrayReplacement() + { + var json1 = @"{ + 'ip': [ + '1.2.3.4', + '7.8.9.10', + '11.12.13.14' + ] + }"; + + var json2 = @"{ + 'ip': { + '1': '15.16.17.18' + } + }"; + + var jsonConfigSource1 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource1.Load(TestStreamHelpers.StringToStream(json1)); + + var jsonConfigSource2 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource2.Load(TestStreamHelpers.StringToStream(json2)); + + var config = new Configuration(); + config.AddLoadedSource(jsonConfigSource1); + config.AddLoadedSource(jsonConfigSource2); + + Assert.Equal(3, config.GetSubKeys("ip").Count()); + Assert.Equal("1.2.3.4", config.Get("ip:0")); + Assert.Equal("15.16.17.18", config.Get("ip:1")); + Assert.Equal("11.12.13.14", config.Get("ip:2")); + } + + [Fact] + public void ArrayMerge() + { + var json1 = @"{ + 'ip': [ + '1.2.3.4', + '7.8.9.10', + '11.12.13.14' + ] + }"; + + var json2 = @"{ + 'ip': { + '3': '15.16.17.18' + } + }"; + + var jsonConfigSource1 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource1.Load(TestStreamHelpers.StringToStream(json1)); + + var jsonConfigSource2 = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource2.Load(TestStreamHelpers.StringToStream(json2)); + + var config = new Configuration(); + config.AddLoadedSource(jsonConfigSource1); + config.AddLoadedSource(jsonConfigSource2); + + Assert.Equal(4, config.GetSubKeys("ip").Count()); + Assert.Equal("1.2.3.4", config.Get("ip:0")); + Assert.Equal("7.8.9.10", config.Get("ip:1")); + Assert.Equal("11.12.13.14", config.Get("ip:2")); + Assert.Equal("15.16.17.18", config.Get("ip:3")); + } + + [Fact] + public void ArraysAreKeptInFileOrder() + { + var json = @"{ + 'setting': [ + 'b', + 'a', + '2' + ] + }"; + + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); + + var config = new Configuration(); + config.AddLoadedSource(jsonConfigSource); + + var subkey = config.GetSubKey("setting"); + var indexSubkeys = subkey.GetSubKeys().ToArray(); + + Assert.Equal(3, indexSubkeys.Count()); + Assert.Equal("b", indexSubkeys[0].Value.Get(null)); + Assert.Equal("a", indexSubkeys[1].Value.Get(null)); + Assert.Equal("2", indexSubkeys[2].Value.Get(null)); + } + + [Fact] + public void PropertiesAreSortedByNumberOnlyFirst() + { + var json = @"{ + 'setting': { + 'hello': 'a', + 'bob': 'b', + '42': 'c', + '4':'d', + '10': 'e', + '1text': 'f', + } + }"; + + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); + + var config = new Configuration(); + config.AddLoadedSource(jsonConfigSource); + + var subkey = config.GetSubKey("setting"); + var indexSubkeys = subkey.GetSubKeys().ToArray(); + + Assert.Equal(6, indexSubkeys.Count()); + Assert.Equal("4", indexSubkeys[0].Key); + Assert.Equal("10", indexSubkeys[1].Key); + Assert.Equal("42", indexSubkeys[2].Key); + Assert.Equal("1text", indexSubkeys[3].Key); + Assert.Equal("bob", indexSubkeys[4].Key); + Assert.Equal("hello", indexSubkeys[5].Key); + } + } +} diff --git a/test/Microsoft.Framework.ConfigurationModel.Json.Test/JsonConfigurationSourceTest.cs b/test/Microsoft.Framework.ConfigurationModel.Json.Test/JsonConfigurationSourceTest.cs index 8f61c6e4..918c7737 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Json.Test/JsonConfigurationSourceTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Json.Test/JsonConfigurationSourceTest.cs @@ -3,15 +3,14 @@ using System; using System.IO; -using Microsoft.Framework.ConfigurationModel.Json; +using Microsoft.Framework.ConfigurationModel.Test; +using Newtonsoft.Json; using Xunit; namespace Microsoft.Framework.ConfigurationModel { public class JsonConfigurationSourceTest { - private static readonly string ArbitraryFilePath = "Unit tests do not touch file system"; - [Fact] public void LoadKeyValuePairsFromValidJson() { @@ -24,9 +23,9 @@ public void LoadKeyValuePairsFromValidJson() 'zipcode': '12345' } }"; - var jsonConfigSrc = new JsonConfigurationSource(ArbitraryFilePath); + var jsonConfigSrc = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - jsonConfigSrc.Load(StringToStream(json)); + jsonConfigSrc.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal("test", jsonConfigSrc.Get("firstname")); Assert.Equal("last.name", jsonConfigSrc.Get("test.last.name")); @@ -41,9 +40,9 @@ public void LoadMethodCanHandleEmptyValue() { 'name': '' }"; - var jsonConfigSrc = new JsonConfigurationSource(ArbitraryFilePath); + var jsonConfigSrc = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - jsonConfigSrc.Load(StringToStream(json)); + jsonConfigSrc.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal(string.Empty, jsonConfigSrc.Get("name")); } @@ -52,12 +51,9 @@ public void LoadMethodCanHandleEmptyValue() public void NonObjectRootIsInvalid() { var json = @"'test'"; - var jsonConfigSource = new JsonConfigurationSource(ArbitraryFilePath); - var expectedMsg = Resources.FormatError_RootMustBeAnObject(string.Empty, 1, 6); - - var exception = Assert.Throws(() => jsonConfigSource.Load(StringToStream(json))); - - Assert.Equal(expectedMsg, exception.Message); + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + + var exception = Assert.Throws(() => jsonConfigSource.Load(TestStreamHelpers.StringToStream(json))); } [Fact] @@ -71,30 +67,15 @@ public void SupportAndIgnoreComments() ""zipcode"": ""12345"" } }"; - var jsonConfigSrc = new JsonConfigurationSource(ArbitraryFilePath); + var jsonConfigSrc = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - jsonConfigSrc.Load(StringToStream(json)); + jsonConfigSrc.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal("test", jsonConfigSrc.Get("name")); Assert.Equal("Something street", jsonConfigSrc.Get("address:street")); Assert.Equal("12345", jsonConfigSrc.Get("address:zipcode")); } - [Fact] - public void ArraysAreNotSupported() - { - var json = @"{ - 'name': 'test', - 'address': ['Something street', '12345'] - }"; - var jsonConfigSource = new JsonConfigurationSource(ArbitraryFilePath); - var expectedMsg = Resources.FormatError_UnsupportedJSONToken("StartArray", "address", 3, 29); - - var exception = Assert.Throws(() => jsonConfigSource.Load(StringToStream(json))); - - Assert.Equal(expectedMsg, exception.Message); - } - [Fact] public void ThrowExceptionWhenUnexpectedEndFoundBeforeFinishParsing() { @@ -105,12 +86,9 @@ public void ThrowExceptionWhenUnexpectedEndFoundBeforeFinishParsing() 'zipcode': '12345' } /* Missing a right brace here*/"; - var jsonConfigSource = new JsonConfigurationSource(ArbitraryFilePath); - var expectedMsg = Resources.FormatError_UnexpectedEnd("address", 7, 44); - - var exception = Assert.Throws(() => jsonConfigSource.Load(StringToStream(json))); - - Assert.Equal(expectedMsg, exception.Message); + var jsonConfigSource = new JsonConfigurationSource(TestStreamHelpers.ArbitraryFilePath); + + var exception = Assert.Throws(() => jsonConfigSource.Load(TestStreamHelpers.StringToStream(json))); } [Fact] @@ -150,42 +128,5 @@ public void JsonConfiguration_Does_Not_Throw_On_Optional_Configuration() configSource.Load(); Assert.Throws(() => configSource.Get("key")); } - - [Fact] - public void ThrowExceptionWhenKeyIsDuplicated() - { - var json = @"{ - 'name': 'test', - 'address': { - 'street': 'Something street', - 'zipcode': '12345' - }, - 'name': 'new name' - }"; - var jsonConfigSrc = new JsonConfigurationSource(ArbitraryFilePath); - - var exception = Assert.Throws(() => jsonConfigSrc.Load(StringToStream(json))); - - Assert.Equal(Resources.FormatError_KeyIsDuplicated("name"), exception.Message); - } - - private static Stream StringToStream(string str) - { - var memStream = new MemoryStream(); - var textWriter = new StreamWriter(memStream); - textWriter.Write(str); - textWriter.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - - return memStream; - } - - private static string StreamToString(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); - StreamReader reader = new StreamReader(stream); - - return reader.ReadToEnd(); - } } } \ No newline at end of file diff --git a/test/Microsoft.Framework.ConfigurationModel.Test.Common/ConfigurationSourceExtensions.cs b/test/Microsoft.Framework.ConfigurationModel.Test.Common/ConfigurationSourceExtensions.cs index 71e82bac..b0e7e0ee 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test.Common/ConfigurationSourceExtensions.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test.Common/ConfigurationSourceExtensions.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public static class ConfigurationSourceExtensions { diff --git a/test/Microsoft.Framework.ConfigurationModel.Test.Common/TestStreamHelpers.cs b/test/Microsoft.Framework.ConfigurationModel.Test.Common/TestStreamHelpers.cs new file mode 100644 index 00000000..889a8d27 --- /dev/null +++ b/test/Microsoft.Framework.ConfigurationModel.Test.Common/TestStreamHelpers.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; + +namespace Microsoft.Framework.ConfigurationModel.Test +{ + public static class TestStreamHelpers + { + public static readonly string ArbitraryFilePath = "Unit tests do not touch file system"; + + public static Stream StringToStream(string str) + { + var memStream = new MemoryStream(); + var textWriter = new StreamWriter(memStream); + textWriter.Write(str); + textWriter.Flush(); + memStream.Seek(0, SeekOrigin.Begin); + + return memStream; + } + + public static string StreamToString(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + var reader = new StreamReader(stream); + + return reader.ReadToEnd(); + } + } +} diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/CommandLineConfigurationSourceTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/CommandLineConfigurationSourceTest.cs index e4511859..d7423502 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test/CommandLineConfigurationSourceTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test/CommandLineConfigurationSourceTest.cs @@ -3,10 +3,9 @@ using System; using System.Collections.Generic; -using System.Linq; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class CommandLineConfigurationSourceTest { diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationExtensionsTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationExtensionsTest.cs index 691052b4..d7acc267 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationExtensionsTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationExtensionsTest.cs @@ -5,7 +5,7 @@ using System.IO; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class ConfigurationExtensionsTest { diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationPathComparerTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationPathComparerTest.cs new file mode 100644 index 00000000..38553dd6 --- /dev/null +++ b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationPathComparerTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Framework.ConfigurationModel.Test +{ + public class ConfigurationPathComparerTest + { + [Fact] + public void CompareWithNull() + { + ComparerTest(null, null, 0); + ComparerTest(null, "a", -1); + ComparerTest("b", null, 1); + } + + [Fact] + public void CompareWithSameLength() + { + ComparerTest("a", "a", 0); + ComparerTest("a", "A", 0); + + ComparerTest("aB", "Ab", 0); + } + + [Fact] + public void CompareWithDifferentLengths() + { + ComparerTest("a", "aa", -1); + ComparerTest("aa", "a", 1); + } + + [Fact] + public void CompareWithLetters() + { + ComparerTest("a", "b", -1); + ComparerTest("b", "a", 1); + } + + [Fact] + public void CompareWithNumbers() + { + ComparerTest("000", "0", 0); + ComparerTest("001", "1", 0); + + ComparerTest("1", "1", 0); + + ComparerTest("1", "10", -1); + ComparerTest("10", "1", 1); + + ComparerTest("2", "10", -1); + ComparerTest("10", "2", 1); + } + + [Fact] + public void CompareWithNumbersAndLetters() + { + ComparerTest("1", "a", -1); + ComparerTest("a", "1", 1); + + ComparerTest("100", "a", -1); + ComparerTest("a", "100", 1); + } + + [Fact] + public void CompareWithNonNumbers() + { + ComparerTest("1a", "100", 1); + ComparerTest("100", "1a", -1); + + ComparerTest("100a", "100", 1); + ComparerTest("100", "100a", -1); + + ComparerTest("a100", "100", 1); + ComparerTest("100", "a100", -1); + + ComparerTest("1a", "a", -1); + ComparerTest("a", "1a", 1); + } + + [Fact] + public void CompareIdenticalPaths() + { + ComparerTest("abc:DEF:0:a100", "ABC:DEF:0:a100", 0); + } + + [Fact] + public void CompareDifferentPaths() + { + ComparerTest("abc:def", "ghi:2", -1); + ComparerTest("ghi:2", "abc:def", 1); + } + + [Fact] + public void ComparePathsWithCommonPart() + { + ComparerTest("abc:def:XYQ", "abc:def:XYZ", -1); + ComparerTest("abc:def:XYZ", "abc:def:XYQ", 1); + } + + [Fact] + public void ComparePathsWithCommonPartButShorter() + { + ComparerTest("abc:def", "abc:def:ghi", -1); + ComparerTest("abc:def:ghi", "abc:def", 1); + } + + [Fact] + public void ComparePathsWithIndicesAtTheEnd() + { + ComparerTest("abc:def:2", "abc:def:10", -1); + ComparerTest("abc:def:10", "abc:def:2", 1); + + ComparerTest("abc:def:10", "abc:def:22", -1); + ComparerTest("abc:def:22", "abc:def:10", 1); + } + + [Fact] + public void ComparePathsWithIndicesInside() + { + ComparerTest("abc:def:1000:jkl", "abc:def:ghi:jkl", -1); + ComparerTest("abc:def:ghi:jkl", "abc:def:1000:jkl", 1); + + ComparerTest("abc:def:10:jkl", "abc:def:22:jkl", -1); + ComparerTest("abc:def:22:jkl", "abc:def:10:jkl", 1); + } + + private static void ComparerTest(string a, string b, int expectedSign) + { + var result = ConfigurationKeyComparer.Instance.Compare(a, b); + Assert.Equal(expectedSign, Math.Sign(result)); + } + } +} diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationTest.cs index c1e11d1a..f14d0fee 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test/ConfigurationTest.cs @@ -6,12 +6,10 @@ using System.Linq; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class ConfigurationTest { - private static readonly string ArbitraryFilePath = "Unit tests do not touch file system"; - [Fact] public void SetBasePathThroughConstructor() { diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/EnvironmentVariablesConfigurationSourceTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/EnvironmentVariablesConfigurationSourceTest.cs index 3309237b..055542e2 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test/EnvironmentVariablesConfigurationSourceTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test/EnvironmentVariablesConfigurationSourceTest.cs @@ -5,7 +5,7 @@ using System.Collections; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class EnvironmentVariablesConfigurationSourceTest { diff --git a/test/Microsoft.Framework.ConfigurationModel.Test/IniFileConfigurationSourceTest.cs b/test/Microsoft.Framework.ConfigurationModel.Test/IniFileConfigurationSourceTest.cs index 9b56f000..2c03f58e 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Test/IniFileConfigurationSourceTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Test/IniFileConfigurationSourceTest.cs @@ -5,12 +5,10 @@ using System.IO; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Test { public class IniFileConfigurationSourceTest { - private static readonly string ArbitraryFilePath = "Unit tests do not touch file system"; - [Fact] public void LoadKeyValuePairsFromValidIniFile() { @@ -20,9 +18,9 @@ public void LoadKeyValuePairsFromValidIniFile() [Data:Inventory] ConnectionString=AnotherTestConnectionString SubHeader:Provider=MySql"; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("defaultconnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DEFAULTCONNECTION:PROVIDER")); @@ -34,9 +32,9 @@ public void LoadKeyValuePairsFromValidIniFile() public void LoadMethodCanHandleEmptyValue() { var ini = @"DefaultKey="; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal(string.Empty, iniConfigSrc.Get("DefaultKey")); } @@ -50,9 +48,9 @@ public void LoadKeyValuePairsFromValidIniFileWithQuotedValues() "[Data:Inventory]\n" + "ConnectionString=\"AnotherTestConnectionString\"\n" + "Provider=\"MySql\""; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); @@ -66,9 +64,9 @@ public void DoubleQuoteIsPartOfValueIfNotPaired() var ini = "[ConnectionString]\n" + "DefaultConnection=\"TestConnectionString\n" + "Provider=SqlClient\""; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("\"TestConnectionString", iniConfigSrc.Get("ConnectionString:DefaultConnection")); Assert.Equal("SqlClient\"", iniConfigSrc.Get("ConnectionString:Provider")); @@ -80,9 +78,9 @@ public void DoubleQuoteIsPartOfValueIfAppearInTheMiddleOfValue() var ini = "[ConnectionString]\n" + "DefaultConnection=Test\"Connection\"String\n" + "Provider=Sql\"Client"; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("Test\"Connection\"String", iniConfigSrc.Get("ConnectionString:DefaultConnection")); Assert.Equal("Sql\"Client", iniConfigSrc.Get("ConnectionString:Provider")); @@ -97,9 +95,9 @@ public void LoadKeyValuePairsFromValidIniFileWithoutSectionHeader() Data:Inventory:ConnectionString=AnotherTestConnectionString Data:Inventory:Provider=MySql "; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); @@ -121,9 +119,9 @@ public void SupportAndIgnoreComments() ConnectionString=AnotherTestConnectionString Provider=MySql "; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); - iniConfigSrc.Load(StringToStream(ini)); + iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); @@ -137,10 +135,10 @@ public void ThrowExceptionWhenFoundInvalidLine() var ini = @" ConnectionString "; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); var expectedMsg = Resources.FormatError_UnrecognizedLineFormat("ConnectionString"); - var exception = Assert.Throws(() => iniConfigSrc.Load(StringToStream(ini))); + var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } @@ -152,10 +150,10 @@ public void ThrowExceptionWhenFoundBrokenSectionHeader() [ConnectionString DefaultConnection=TestConnectionString "; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); var expectedMsg = Resources.FormatError_UnrecognizedLineFormat("[ConnectionString"); - var exception = Assert.Throws(() => iniConfigSrc.Load(StringToStream(ini))); + var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } @@ -191,10 +189,10 @@ public void ThrowExceptionWhenKeyIsDuplicated() DefaultConnection:ConnectionString=AnotherTestConnectionString Provider=MySql "; - var iniConfigSrc = new IniFileConfigurationSource(ArbitraryFilePath); + var iniConfigSrc = new IniFileConfigurationSource(TestStreamHelpers.ArbitraryFilePath); var expectedMsg = Resources.FormatError_KeyIsDuplicated("Data:DefaultConnection:ConnectionString"); - var exception = Assert.Throws(() => iniConfigSrc.Load(StringToStream(ini))); + var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } @@ -224,25 +222,5 @@ public void IniConfiguration_Does_Not_Throw_On_Optional_Configuration() configSource.Load(); Assert.Throws(() => configSource.Get("key")); } - - private static Stream StringToStream(string str) - { - var memStream = new MemoryStream(); - var textWriter = new StreamWriter(memStream); - textWriter.Write(str); - textWriter.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - - return memStream; - } - - private static string StreamToString(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); - StreamReader reader = new StreamReader(stream); - - return reader.ReadToEnd(); - } - } } \ No newline at end of file diff --git a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationExtensionTest.cs b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationExtensionTest.cs index 9f9e2022..1aa9c904 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationExtensionTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationExtensionTest.cs @@ -5,7 +5,7 @@ using System.IO; using Xunit; -namespace Microsoft.Framework.ConfigurationModel.Xml +namespace Microsoft.Framework.ConfigurationModel.Xml.Test { public class XmlConfigurationExtensionTest { diff --git a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNet50.cs b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNet50.cs index 2a8a45d1..daf588e1 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNet50.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNet50.cs @@ -9,9 +9,10 @@ using System.Security.Cryptography.Xml; using System.Xml; using Microsoft.AspNet.Testing.xunit; +using Microsoft.Framework.ConfigurationModel.Test; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Xml.Test { public partial class XmlConfigurationSourceTest { @@ -58,7 +59,7 @@ public void LoadKeyValuePairsFromValidEncryptedXml() })); // Act - xmlConfigSrc.Load(StringToStream(xmlDocument.OuterXml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xmlDocument.OuterXml)); // Assert Assert.Equal("Test.Connection.String", xmlConfigSrc.Get("DATA.SETTING:DEFAULTCONNECTION:CONNECTION.STRING")); diff --git a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNetCore50.cs b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNetCore50.cs index 0af53b42..ef698240 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNetCore50.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.AspNetCore50.cs @@ -5,10 +5,10 @@ // These tests only run on Core CLR. using System; -using Microsoft.Framework.ConfigurationModel.Xml; +using Microsoft.Framework.ConfigurationModel.Test; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Xml.Test { public partial class XmlConfigurationSourceTest { @@ -49,7 +49,7 @@ public void LoadKeyValuePairsFromValidEncryptedXml_ThrowsPlatformNotSupported() var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); // Act & assert - var ex = Assert.Throws(() => xmlConfigSrc.Load(StringToStream(xml))); + var ex = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(Resources.Error_EncryptedXmlNotSupported, ex.Message); } } diff --git a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.cs b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.cs index 9f040171..260bb820 100644 --- a/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.cs +++ b/test/Microsoft.Framework.ConfigurationModel.Xml.Test/XmlConfigurationSourceTest.cs @@ -3,10 +3,10 @@ using System; using System.IO; -using Microsoft.Framework.ConfigurationModel.Xml; +using Microsoft.Framework.ConfigurationModel.Test; using Xunit; -namespace Microsoft.Framework.ConfigurationModel +namespace Microsoft.Framework.ConfigurationModel.Xml.Test { public partial class XmlConfigurationSourceTest { @@ -30,7 +30,7 @@ public void LoadKeyValuePairsFromValidXml() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("Test.Connection.String", xmlConfigSrc.Get("DATA.SETTING:DEFAULTCONNECTION:CONNECTION.STRING")); Assert.Equal("SqlClient", xmlConfigSrc.Get("DATA.SETTING:DefaultConnection:Provider")); @@ -50,7 +50,7 @@ public void LoadMethodCanHandleEmptyValue() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal(string.Empty, xmlConfigSrc.Get("Key1")); Assert.Equal(string.Empty, xmlConfigSrc.Get("Key2:Key3")); @@ -72,7 +72,7 @@ public void CommonAttributesContributeToKeyValuePairs() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("8008", xmlConfigSrc.Get("Port")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); @@ -97,7 +97,7 @@ public void SupportMixingChildElementsAndAttributes() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("8008", xmlConfigSrc.Get("Port")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); @@ -122,7 +122,7 @@ public void NameAttributeContributesToPrefix() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -146,7 +146,7 @@ public void NameAttributeInRootElementContributesToPrefix() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -168,7 +168,7 @@ public void SupportMixingNameAttributesAndCommonAttributes() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -189,7 +189,7 @@ public void SupportCDATAAsTextNode() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("SpecialStringWith<>", xmlConfigSrc.Get("Data:Inventory:Provider")); } @@ -212,7 +212,7 @@ public void SupportAndIgnoreComments() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -239,7 +239,7 @@ public void SupportAndIgnoreXMLDeclaration() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -268,7 +268,7 @@ public void SupportAndIgnoreProcessingInstructions() "; var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); - xmlConfigSrc.Load(StringToStream(xml)); + xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); @@ -303,7 +303,7 @@ public void ThrowExceptionWhenFindDTD() + "To enable DTD processing set the DtdProcessing property on XmlReaderSettings " + "to Parse and pass the settings into XmlReader.Create method."; - var exception = Assert.Throws(() => xmlConfigSrc.Load(StringToStream(xml))); + var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } @@ -327,7 +327,7 @@ public void ThrowExceptionWhenFindNamespace() var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath); var expectedMsg = Resources.FormatError_NamespaceIsNotSupported(Resources.FormatMsg_LineInfo(1, 11)); - var exception = Assert.Throws(() => xmlConfigSrc.Load(StringToStream(xml))); + var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } @@ -371,7 +371,7 @@ public void ThrowExceptionWhenKeyIsDuplicated() var expectedMsg = Resources.FormatError_KeyIsDuplicated("Data:DefaultConnection:ConnectionString", Resources.FormatMsg_LineInfo(8, 52)); - var exception = Assert.Throws(() => xmlConfigSrc.Load(StringToStream(xml))); + var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } @@ -401,24 +401,5 @@ public void XmlConfiguration_Does_Not_Throw_On_Optional_Configuration() configSource.Load(); Assert.Throws(() => configSource.Get("key")); } - - private static Stream StringToStream(string str) - { - var memStream = new MemoryStream(); - var textWriter = new StreamWriter(memStream); - textWriter.Write(str); - textWriter.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - - return memStream; - } - - private static string StreamToString(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); - StreamReader reader = new StreamReader(stream); - - return reader.ReadToEnd(); - } } -} \ No newline at end of file +}