// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration.Memory;
using Xunit;

namespace Microsoft.Extensions.Configuration.Test
{
    public abstract class ConfigurationProviderTestBase
    {
        [Fact]
        public virtual void Load_from_single_provider()
        {
            var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));

            AssertConfig(configRoot);
        }

        [Fact]
        public virtual void Has_debug_view()
        {
            var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));
            var providerTag = configRoot.Providers.Single().ToString();

            var expected =
                $@"Key1=Value1 ({providerTag})
Section1:
  Key2=Value12 ({providerTag})
  Section2:
    Key3=Value123 ({providerTag})
    Key3a:
      0=ArrayValue0 ({providerTag})
      1=ArrayValue1 ({providerTag})
      2=ArrayValue2 ({providerTag})
Section3:
  Section4:
    Key4=Value344 ({providerTag})
";

            AssertDebugView(configRoot, expected);
        }

        [Fact]
        public virtual void Null_values_are_included_in_the_config()
        {
            AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.NullsTestConfig)), expectNulls: true, nullValue: "");
        }

        [Fact]
        public virtual void Combine_after_other_provider()
        {
            AssertConfig(
                BuildConfigRoot(
                    LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig),
                    LoadThroughProvider(TestSection.MissingSection4Config)));

            AssertConfig(
                BuildConfigRoot(
                    LoadUsingMemoryProvider(TestSection.MissingSection4Config),
                    LoadThroughProvider(TestSection.MissingSection2ValuesConfig)));
        }

        [Fact]
        public virtual void Combine_before_other_provider()
        {
            AssertConfig(
                BuildConfigRoot(
                    LoadThroughProvider(TestSection.MissingSection2ValuesConfig),
                    LoadUsingMemoryProvider(TestSection.MissingSection4Config)));

            AssertConfig(
                BuildConfigRoot(
                    LoadThroughProvider(TestSection.MissingSection4Config),
                    LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig)));
        }

        [Fact]
        public virtual void Second_provider_overrides_values_from_first()
        {
            AssertConfig(
                BuildConfigRoot(
                    LoadUsingMemoryProvider(TestSection.NoValuesTestConfig),
                    LoadThroughProvider(TestSection.TestConfig)));
        }

        [Fact]
        public virtual void Combining_from_multiple_providers_is_case_insensitive()
        {
            AssertConfig(
                BuildConfigRoot(
                    LoadUsingMemoryProvider(TestSection.DifferentCasedTestConfig),
                    LoadThroughProvider(TestSection.TestConfig)));
        }

        [Fact]
        public virtual void Load_from_single_provider_with_duplicates_throws()
        {
            AssertFormatOrArgumentException(
                () => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesTestConfig)));
        }

        [Fact]
        public virtual void Load_from_single_provider_with_differing_case_duplicates_throws()
        {
            AssertFormatOrArgumentException(
                () => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesDifferentCaseTestConfig)));
        }

        private void AssertFormatOrArgumentException(Action test)
        {
            Exception caught = null;
            try
            {
                test();
            }
            catch (Exception e)
            {
                caught = e;
            }

            Assert.True(caught is ArgumentException
                        || caught is FormatException);
        }

        [Fact]
        public virtual void Bind_to_object()
        {
            var configuration = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));

            var options = configuration.Get<AsOptions>();

            Assert.Equal("Value1", options.Key1);
            Assert.Equal("Value12", options.Section1.Key2);
            Assert.Equal("Value123", options.Section1.Section2.Key3);
            Assert.Equal("Value344", options.Section3.Section4.Key4);
            Assert.Equal(new[] { "ArrayValue0", "ArrayValue1", "ArrayValue2" }, options.Section1.Section2.Key3a);
        }

        public class AsOptions
        {
            public string Key1 { get; set; }

            public Section1AsOptions Section1 { get; set; }
            public Section3AsOptions Section3 { get; set; }
        }

        public class Section1AsOptions
        {
            public string Key2 { get; set; }

            public Section2AsOptions Section2 { get; set; }
        }

        public class Section2AsOptions
        {
            public string Key3 { get; set; }
            public string[] Key3a { get; set; }
        }

        public class Section3AsOptions
        {
            public Section4AsOptions Section4 { get; set; }
        }

        public class Section4AsOptions
        {
            public string Key4 { get; set; }
        }

        protected virtual void AssertDebugView(
            IConfigurationRoot config,
            string expected)
        {
            string RemoveLineEnds(string source) => source.Replace("\n", "").Replace("\r", "");

            var actual = config.GetDebugView();

            Assert.Equal(
                RemoveLineEnds(expected),
                RemoveLineEnds(actual));
        }

        protected virtual void AssertConfig(
            IConfigurationRoot config,
            bool expectNulls = false,
            string nullValue = null)
        {
            var value1 = expectNulls ? nullValue : "Value1";
            var value12 = expectNulls ? nullValue : "Value12";
            var value123 = expectNulls ? nullValue : "Value123";
            var arrayvalue0 = expectNulls ? nullValue : "ArrayValue0";
            var arrayvalue1 = expectNulls ? nullValue : "ArrayValue1";
            var arrayvalue2 = expectNulls ? nullValue : "ArrayValue2";
            var value344 = expectNulls ? nullValue : "Value344";

            Assert.Equal(value1, config["Key1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value12, config["Section1:Key2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value123, config["Section1:Section2:Key3"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue0, config["Section1:Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, config["Section1:Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, config["Section1:Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value344, config["Section3:Section4:Key4"], StringComparer.InvariantCultureIgnoreCase);

            var section1 = config.GetSection("Section1");
            Assert.Equal(value12, section1["Key2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value123, section1["Section2:Key3"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue0, section1["Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, section1["Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, section1["Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1", section1.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section1.Value);

            var section2 = config.GetSection("Section1:Section2");
            Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section2.Value);

            section2 = section1.GetSection("Section2");
            Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section2.Value);

            var section3a = section2.GetSection("Key3a");
            Assert.Equal(arrayvalue0, section3a["0"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, section3a["1"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, section3a["2"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3a", section3a.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section3a.Value);

            var section3 = config.GetSection("Section3");
            Assert.Equal("Section3", section3.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section3.Value);

            var section4 = config.GetSection("Section3:Section4");
            Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section4.Value);

            section4 = config.GetSection("Section3").GetSection("Section4");
            Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(section4.Value);

            var sections = config.GetChildren().ToList();

            Assert.Equal(3, sections.Count);

            Assert.Equal("Key1", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Key1", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value1, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);

            Assert.Equal("Section1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(sections[1].Value);

            Assert.Equal("Section3", sections[2].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section3", sections[2].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(sections[2].Value);

            sections = section1.GetChildren().ToList();

            Assert.Equal(2, sections.Count);

            Assert.Equal("Key2", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Key2", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value12, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);

            Assert.Equal("Section2", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(sections[1].Value);

            sections = section2.GetChildren().ToList();

            Assert.Equal(2, sections.Count);

            Assert.Equal("Key3", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value123, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);

            Assert.Equal("Key3a", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3a", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(sections[1].Value);

            sections = section3a.GetChildren().ToList();

            Assert.Equal(3, sections.Count);

            Assert.Equal("0", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3a:0", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue0, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);

            Assert.Equal("1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3a:1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue1, sections[1].Value, StringComparer.InvariantCultureIgnoreCase);

            Assert.Equal("2", sections[2].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section1:Section2:Key3a:2", sections[2].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(arrayvalue2, sections[2].Value, StringComparer.InvariantCultureIgnoreCase);

            sections = section3.GetChildren().ToList();

            Assert.Single(sections);

            Assert.Equal("Section4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section3:Section4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Null(sections[0].Value);

            sections = section4.GetChildren().ToList();

            Assert.Single(sections);

            Assert.Equal("Key4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal("Section3:Section4:Key4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
            Assert.Equal(value344, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
        }

        protected abstract (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(TestSection testConfig);

        protected virtual IConfigurationRoot BuildConfigRoot(
            params (IConfigurationProvider Provider, Action Initializer)[] providers)
        {
            var root = new ConfigurationRoot(providers.Select(e => e.Provider).ToList());

            foreach (var initializer in providers.Select(e => e.Initializer))
            {
                initializer();
            }

            return root;
        }

        protected static (IConfigurationProvider Provider, Action Initializer) LoadUsingMemoryProvider(TestSection testConfig)
        {
            var values = new List<KeyValuePair<string, string>>();
            SectionToValues(testConfig, "", values);

            return (new MemoryConfigurationProvider(
                    new MemoryConfigurationSource
                    {
                        InitialData = values
                    }),
                () => { });
        }

        protected static void SectionToValues(
            TestSection section,
            string sectionName,
            IList<KeyValuePair<string, string>> values)
        {
            foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key)))
            {
                values.Add(new KeyValuePair<string, string>(sectionName + tuple.Key, tuple.Value));
            }

            foreach (var tuple in section.Sections)
            {
                SectionToValues(
                    tuple.Section,
                    sectionName + tuple.Key + ":",
                    values);
            }
        }

        protected class TestKeyValue
        {
            public object Value { get; }

            public TestKeyValue(string value)
            {
                Value = value;
            }

            public TestKeyValue(string[] values)
            {
                Value = values;
            }

            public static implicit operator TestKeyValue(string value) => new TestKeyValue(value);
            public static implicit operator TestKeyValue(string[] values) => new TestKeyValue(values);

            public string[] AsArray => Value as string[];

            public string AsString => Value as string;

            public IEnumerable<(string Key, string Value)> Expand(string key)
            {
                if (AsArray == null)
                {
                    yield return (key, AsString);
                }
                else
                {
                    for (var i = 0; i < AsArray.Length; i++)
                    {
                        yield return ($"{key}:{i}", AsArray[i]);
                    }
                }
            }
        }

        protected class TestSection
        {
            public IEnumerable<(string Key, TestKeyValue Value)> Values { get; set; }
                = Enumerable.Empty<(string, TestKeyValue)>();

            public IEnumerable<(string Key, TestSection Section)> Sections { get; set; }
                = Enumerable.Empty<(string, TestSection)>();

            public static TestSection TestConfig { get; }
                = new TestSection
                {
                    Values = new[] { ("Key1", (TestKeyValue)"Value1") },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"Value344")}
                                })
                            }
                        })
                    }
                };

            public static TestSection NoValuesTestConfig { get; }
                = new TestSection
                {
                    Values = new[] { ("Key1", (TestKeyValue)"------") },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"-------")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"-----"),
                                        ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"--------")}
                                })
                            }
                        })
                    }
                };

            public static TestSection MissingSection2ValuesConfig { get; }
                = new TestSection
                {
                    Values = new[] { ("Key1", (TestKeyValue)"Value1") },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0"})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"Value344")}
                                })
                            }
                        })
                    }
                };


            public static TestSection MissingSection4Config { get; }
                = new TestSection
                {
                    Values = new[] { ("Key1", (TestKeyValue)"Value1") },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection())
                    }
                };

            public static TestSection DifferentCasedTestConfig { get; }
                = new TestSection
                {
                    Values = new[] { ("KeY1", (TestKeyValue)"Value1") },
                    Sections = new[]
                    {
                        ("SectioN1", new TestSection
                        {
                            Values = new[] {("KeY2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("SectioN2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("KeY3", (TestKeyValue)"Value123"),
                                        ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                })
                            }
                        }),
                        ("SectioN3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("SectioN4", new TestSection
                                {
                                    Values = new[] {("KeY4", (TestKeyValue)"Value344")}
                                })
                            }
                        })
                    }
                };

            public static TestSection DuplicatesTestConfig { get; }
                = new TestSection
                {
                    Values = new[]
                    {
                        ("Key1", (TestKeyValue)"Value1"),
                        ("Key1", (TestKeyValue)"Value1")
                    },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                }),
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                })

                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"Value344")}
                                })
                            }
                        })
                    }
                };

            public static TestSection DuplicatesDifferentCaseTestConfig { get; }
                = new TestSection
                {
                    Values = new[]
                    {
                        ("Key1", (TestKeyValue)"Value1"),
                        ("KeY1", (TestKeyValue)"Value1")
                    },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", (TestKeyValue)"Value12")},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                }),
                                ("SectioN2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("KeY3", (TestKeyValue)"Value123"),
                                        ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
                                    },
                                })

                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"Value344")}
                                })
                            }
                        })
                    }
                };

            public static TestSection NullsTestConfig { get; }
                = new TestSection
                {
                    Values = new[] { ("Key1", new TestKeyValue((string)null)) },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[] {("Key2", new TestKeyValue((string)null))},
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", new TestKeyValue((string)null)),
                                        ("Key3a", (TestKeyValue)new string[] {null, null, null})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", new TestKeyValue((string)null))}
                                })
                            }
                        })
                    }
                };

            public static TestSection ExtraValuesTestConfig { get; }
                = new TestSection
                {
                    Values = new[]
                    {
                        ("Key1", (TestKeyValue)"Value1"),
                        ("Key1r", (TestKeyValue)"Value1r")
                    },
                    Sections = new[]
                    {
                        ("Section1", new TestSection
                        {
                            Values = new[]
                            {
                                ("Key2", (TestKeyValue)"Value12"),
                                ("Key2r", (TestKeyValue)"Value12r")
                            },
                            Sections = new[]
                            {
                                ("Section2", new TestSection
                                {
                                    Values = new[]
                                    {
                                        ("Key3", (TestKeyValue)"Value123"),
                                        ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2", "ArrayValue2r"}),
                                        ("Key3ar", (TestKeyValue)new[] {"ArrayValue0r"})
                                    },
                                })
                            }
                        }),
                        ("Section3", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section4", new TestSection
                                {
                                    Values = new[] {("Key4", (TestKeyValue)"Value344")}
                                })
                            }
                        }),
                        ("Section5r", new TestSection
                        {
                            Sections = new[]
                            {
                                ("Section6r", new TestSection
                                {
                                    Values = new[] {("Key5r", (TestKeyValue)"Value565r")}
                                })
                            }
                        })
                    }
                };
        }
    }
}