// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using Xunit;

namespace System.Json
{
    /// <summary>
    /// JsonPrimitive unit tests
    /// </summary>
    public class JsonPrimitiveTests
    {
        const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffK";

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Int16"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromInt16()
        {
            short[] values = new short[] { Int16.MinValue, Int16.MaxValue, 1 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<short>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Int32"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromInt32()
        {
            int[] values = new int[] { Int32.MinValue, Int32.MaxValue, 12345678 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<int>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Int64"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromInt64()
        {
            long[] values = new long[] { Int64.MinValue, Int64.MaxValue, 12345678901232L };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<long>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="UInt64"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromUInt64()
        {
            ulong[] values = new ulong[] { UInt64.MinValue, UInt64.MaxValue, 12345678901232L };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<ulong>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="UInt32"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromUInt32()
        {
            uint[] values = new uint[] { UInt32.MinValue, UInt32.MaxValue, 3234567890 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<uint>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="UInt16"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromUInt16()
        {
            ushort[] values = new ushort[] { UInt16.MinValue, UInt16.MaxValue, 33333 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<ushort>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Byte"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromByte()
        {
            byte[] values = new byte[] { Byte.MinValue, Byte.MaxValue, 0x83 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<byte>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="SByte"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromSByte()
        {
            sbyte[] values = new sbyte[] { SByte.MinValue, SByte.MaxValue, -0x33 };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<sbyte>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Single"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromFloat()
        {
            float[] values = new float[] { float.MinValue, float.MaxValue, 1.234f, float.PositiveInfinity, float.NegativeInfinity, float.NaN };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<float>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Double"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromDouble()
        {
            double[] values = new double[] { double.MinValue, double.MaxValue, 1.234, double.PositiveInfinity, double.NegativeInfinity, double.NaN };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<double>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Decimal"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromDecimal()
        {
            decimal[] values = new decimal[] { decimal.MinValue, decimal.MaxValue, 123456789.123456789m };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Number);
                this.TestReadAsRoundtrip<decimal>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Boolean"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromBoolean()
        {
            bool[] values = new bool[] { true, false };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.Boolean);
                this.TestReadAsRoundtrip<bool>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Char"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromChar()
        {
            char[] values = new char[] { 'H', '\0', '\uffff' };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.String);
                this.TestReadAsRoundtrip<char>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="String"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromString()
        {
            string[] values = new string[] { "Hello", "abcdef", "\r\t123\n32" };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.String);
                this.TestReadAsRoundtrip<string>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="DateTime"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromDateTime()
        {
            DateTime[] values = new DateTime[]
            {
                new DateTime(2000, 10, 16, 8, 0, 0, DateTimeKind.Utc),
                new DateTime(2000, 10, 16, 8, 0, 0, DateTimeKind.Local),
            };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.String);
                this.TestReadAsRoundtrip<DateTime>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Uri"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromUri()
        {
            Uri[] values = new Uri[] { new Uri("http://tempuri.org"), new Uri("foo/bar", UriKind.Relative) };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.String);
                this.TestReadAsRoundtrip<Uri>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from <see cref="Guid"/> values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromGuid()
        {
            Guid[] values = new Guid[] { Guid.NewGuid(), Guid.Empty, Guid.NewGuid() };
            for (int i = 0; i < values.Length; i++)
            {
                this.ValidateJson(new JsonPrimitive(values[i]), GetExpectedRepresentation(values[i]), JsonType.String);
                this.TestReadAsRoundtrip<Guid>(new JsonPrimitive(values[i]), values[i]);
            }
        }

        /// <summary>
        /// Validates round-trip of <see cref="JsonPrimitive"/> values created from different types of values.
        /// </summary>
        [Fact]
        public void JsonPrimitiveFromObject()
        {
            List<KeyValuePair<object, JsonType>> values = new List<KeyValuePair<object, JsonType>>
            {
                new KeyValuePair<object, JsonType>(true, JsonType.Boolean),
                new KeyValuePair<object, JsonType>((short)1, JsonType.Number),
                new KeyValuePair<object, JsonType>(234, JsonType.Number),
                new KeyValuePair<object, JsonType>(3435434233443L, JsonType.Number),
                new KeyValuePair<object, JsonType>(UInt64.MaxValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(UInt32.MaxValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(UInt16.MaxValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(Byte.MaxValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(SByte.MinValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(double.MaxValue, JsonType.Number),
                new KeyValuePair<object, JsonType>(float.Epsilon, JsonType.Number),
                new KeyValuePair<object, JsonType>(decimal.MinusOne, JsonType.Number),
                new KeyValuePair<object, JsonType>("hello", JsonType.String),
                new KeyValuePair<object, JsonType>(Guid.NewGuid(), JsonType.String),
                new KeyValuePair<object, JsonType>(DateTime.UtcNow, JsonType.String),
                new KeyValuePair<object, JsonType>(new Uri("http://www.microsoft.com"), JsonType.String),
            };

            foreach (var value in values)
            {
                string json = GetExpectedRepresentation(value.Key);
                JsonValue jsonValue = JsonValue.Parse(json);
                Assert.IsType(typeof(JsonPrimitive), jsonValue);
                this.ValidateJson((JsonPrimitive)jsonValue, json, value.Value);
            }
        }

        /// <summary>
        /// Negative tests for <see cref="JsonPrimitive"/> constructors with null values.
        /// </summary>
        [Fact]
        public void NullChecks()
        {
            ExpectException<ArgumentNullException>(() => new JsonPrimitive((string)null));
            ExpectException<ArgumentNullException>(() => new JsonPrimitive((Uri)null));
        }

        /// <summary>
        /// Tests for casting string values into non-string values.
        /// </summary>
        [Fact]
        public void CastingFromStringTests()
        {
            int seed = MethodBase.GetCurrentMethod().Name.GetHashCode();
            Random rndGen = new Random(seed);

            Assert.Equal(false, (bool)(new JsonPrimitive("false")));
            Assert.Equal(false, (bool)(new JsonPrimitive("False")));
            Assert.Equal(true, (bool)(new JsonPrimitive("true")));
            Assert.Equal(true, (bool)(new JsonPrimitive("True")));

            byte b = PrimitiveCreator.CreateInstanceOfByte(rndGen);
            Assert.Equal(b, (byte)(new JsonPrimitive(b.ToString(CultureInfo.InvariantCulture))));

            decimal dec = PrimitiveCreator.CreateInstanceOfDecimal(rndGen);
            Assert.Equal(dec, (decimal)(new JsonPrimitive(dec.ToString(CultureInfo.InvariantCulture))));

            double dbl = rndGen.NextDouble() * rndGen.Next();
            Assert.Equal(dbl, (double)(new JsonPrimitive(dbl.ToString("R", CultureInfo.InvariantCulture))));

            Assert.Equal(Double.PositiveInfinity, (double)(new JsonPrimitive("Infinity")));
            Assert.Equal(Double.NegativeInfinity, (double)(new JsonPrimitive("-Infinity")));
            Assert.Equal(Double.NaN, (double)(new JsonPrimitive("NaN")));

            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("INF")); });
            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("-INF")); });
            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("infinity")); });
            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("INFINITY")); });
            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("nan")); });
            ExpectException<InvalidCastException>(delegate { var d = (double)(new JsonPrimitive("Nan")); });

            float flt = (float)(rndGen.NextDouble() * rndGen.Next());
            Assert.Equal(flt, (float)(new JsonPrimitive(flt.ToString("R", CultureInfo.InvariantCulture))));

            Assert.Equal(Single.PositiveInfinity, (float)(new JsonPrimitive("Infinity")));
            Assert.Equal(Single.NegativeInfinity, (float)(new JsonPrimitive("-Infinity")));
            Assert.Equal(Single.NaN, (float)(new JsonPrimitive("NaN")));

            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("INF")); });
            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("-INF")); });
            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("infinity")); });
            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("INFINITY")); });
            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("nan")); });
            ExpectException<InvalidCastException>(delegate { var f = (float)(new JsonPrimitive("Nan")); });

            int i = PrimitiveCreator.CreateInstanceOfInt32(rndGen);
            Assert.Equal(i, (int)(new JsonPrimitive(i.ToString(CultureInfo.InvariantCulture))));

            long l = PrimitiveCreator.CreateInstanceOfInt64(rndGen);
            Assert.Equal(l, (long)(new JsonPrimitive(l.ToString(CultureInfo.InvariantCulture))));

            sbyte sb = PrimitiveCreator.CreateInstanceOfSByte(rndGen);
            Assert.Equal(sb, (sbyte)(new JsonPrimitive(sb.ToString(CultureInfo.InvariantCulture))));

            short s = PrimitiveCreator.CreateInstanceOfInt16(rndGen);
            Assert.Equal(s, (short)(new JsonPrimitive(s.ToString(CultureInfo.InvariantCulture))));

            ushort ui16 = PrimitiveCreator.CreateInstanceOfUInt16(rndGen);
            Assert.Equal(ui16, (ushort)(new JsonPrimitive(ui16.ToString(CultureInfo.InvariantCulture))));

            uint ui32 = PrimitiveCreator.CreateInstanceOfUInt32(rndGen);
            Assert.Equal(ui32, (uint)(new JsonPrimitive(ui32.ToString(CultureInfo.InvariantCulture))));

            ulong ui64 = PrimitiveCreator.CreateInstanceOfUInt64(rndGen);
            Assert.Equal(ui64, (ulong)(new JsonPrimitive(ui64.ToString(CultureInfo.InvariantCulture))));
        }

        /// <summary>
        /// Tests for casting <see cref="JsonPrimitive"/> created from special floating point values (infinity, NaN).
        /// </summary>
        [Fact]
        public void CastingNumbersTest()
        {
            Assert.Equal(float.PositiveInfinity, (float)(new JsonPrimitive(double.PositiveInfinity)));
            Assert.Equal(float.NegativeInfinity, (float)(new JsonPrimitive(double.NegativeInfinity)));
            Assert.Equal(float.NaN, (float)(new JsonPrimitive(double.NaN)));

            Assert.Equal(double.PositiveInfinity, (double)(new JsonPrimitive(float.PositiveInfinity)));
            Assert.Equal(double.NegativeInfinity, (double)(new JsonPrimitive(float.NegativeInfinity)));
            Assert.Equal(double.NaN, (double)(new JsonPrimitive(float.NaN)));
        }

        /// <summary>
        /// Tests for the many formats which can be cast to a <see cref="DateTime"/>.
        /// </summary>
        [Fact]
        public void CastingDateTimeTest()
        {
            int seed = MethodBase.GetCurrentMethod().Name.GetHashCode();
            Random rndGen = new Random(seed);
            DateTime dt = new DateTime(
                rndGen.Next(1000, 3000), // year
                rndGen.Next(1, 13), // month
                rndGen.Next(1, 28), // day
                rndGen.Next(0, 24), // hour
                rndGen.Next(0, 60), // minute
                rndGen.Next(0, 60), // second
                DateTimeKind.Utc);
            Log.Info("dt = {0}", dt);

            const string JsonDateFormat = "yyyy-MM-ddTHH:mm:ssZ";
            string dateString = dt.ToString(JsonDateFormat, CultureInfo.InvariantCulture);
            JsonValue jv = dateString;
            DateTime dt2 = (DateTime)jv;
            Assert.Equal(dt.ToUniversalTime(), dt2.ToUniversalTime());

            const string DateTimeLocalFormat = "yyyy-MM-ddTHH:mm:ss";
            const string DateLocalFormat = "yyyy-MM-dd";
            const string TimeLocalFormat = "HH:mm:ss";

            for (int i = 0; i < 100; i++)
            {
                DateTime dateLocal = PrimitiveCreator.CreateInstanceOfDateTime(rndGen).ToLocalTime();
                dateLocal = new DateTime(dateLocal.Year, dateLocal.Month, dateLocal.Day, dateLocal.Hour, dateLocal.Minute, dateLocal.Second, DateTimeKind.Local);
                string localDateTime = dateLocal.ToString(DateTimeLocalFormat, CultureInfo.InvariantCulture);
                string localDate = dateLocal.ToString(DateLocalFormat, CultureInfo.InvariantCulture);
                string localTime = dateLocal.ToString(TimeLocalFormat, CultureInfo.InvariantCulture);

                Assert.Equal(dateLocal, new JsonPrimitive(localDateTime).ReadAs<DateTime>());
                Assert.Equal(dateLocal.Date, new JsonPrimitive(localDate).ReadAs<DateTime>());
                DateTime timeOnly = new JsonPrimitive(localTime).ReadAs<DateTime>();
                Assert.Equal(dateLocal.Hour, timeOnly.Hour);
                Assert.Equal(dateLocal.Minute, timeOnly.Minute);
                Assert.Equal(dateLocal.Second, timeOnly.Second);

                DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(DateTime));
                using (MemoryStream ms = new MemoryStream())
                {
                    dcjs.WriteObject(ms, dateLocal);
                    ms.Position = 0;
                    JsonValue jvFromString = JsonValue.Load(ms);
                    Assert.Equal(dateLocal, jvFromString.ReadAs<DateTime>());
                }

                using (MemoryStream ms = new MemoryStream())
                {
                    DateTime dateUtc = dateLocal.ToUniversalTime();
                    dcjs.WriteObject(ms, dateUtc);
                    ms.Position = 0;
                    JsonValue jvFromString = JsonValue.Load(ms);
                    Assert.Equal(dateUtc, jvFromString.ReadAs<DateTime>());
                }
            }
        }

        /// <summary>
        /// Tests for date parsing form the RFC2822 format.
        /// </summary>
        [Fact]
        public void Rfc2822DateTimeFormatTest()
        {
            string[] localFormats = new string[]
            {
                "ddd, d MMM yyyy HH:mm:ss zzz",
                "d MMM yyyy HH:mm:ss zzz",
                "ddd, dd MMM yyyy HH:mm:ss zzz",
                "ddd, dd MMM yyyy HH:mm zzz",
            };

            string[] utcFormats = new string[]
            {
                @"ddd, d MMM yyyy HH:mm:ss \U\T\C",
                "d MMM yyyy HH:mm:ssZ",
                @"ddd, dd MMM yyyy HH:mm:ss \U\T\C",
                "ddd, dd MMM yyyy HH:mmZ",
            };

            DateTime today = DateTime.Today;
            int seed = today.Year * 10000 + today.Month * 100 + today.Day;
            Log.Info("Seed: {0}", seed);
            Random rndGen = new Random(seed);
            const int DatesToTry = 100;
            const string DateTraceFormat = "ddd yyyy/MM/dd HH:mm:ss.fffZ";

            for (int i = 0; i < DatesToTry; i++)
            {
                DateTime dt = PrimitiveCreator.CreateInstanceOfDateTime(rndGen);
                dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind);
                Log.Info("Test with date: {0} ({1})", dt.ToString(DateTraceFormat, CultureInfo.InvariantCulture), dt.Kind);
                string[] formatsToTest = dt.Kind == DateTimeKind.Utc ? utcFormats : localFormats;
                foreach (string format in formatsToTest)
                {
                    string strDate = dt.ToString(format, CultureInfo.InvariantCulture);
                    Log.Info("As string: {0} (format = {1})", strDate, format);
                    JsonPrimitive jp = new JsonPrimitive(strDate);
                    DateTime parsedDate = jp.ReadAs<DateTime>();
                    Log.Info("Parsed date: {0} ({1})", parsedDate.ToString(DateTraceFormat, CultureInfo.InvariantCulture), parsedDate.Kind);

                    DateTime dtExpected = dt;
                    DateTime dtActual = parsedDate;

                    if (dt.Kind != parsedDate.Kind)
                    {
                        dtExpected = dtExpected.ToUniversalTime();
                        dtActual = dtActual.ToUniversalTime();
                    }

                    Assert.Equal(dtExpected.Year, dtActual.Year);
                    Assert.Equal(dtExpected.Month, dtActual.Month);
                    Assert.Equal(dtExpected.Day, dtActual.Day);
                    Assert.Equal(dtExpected.Hour, dtActual.Hour);
                    Assert.Equal(dtExpected.Minute, dtActual.Minute);
                    if (format.Contains(":ss"))
                    {
                        Assert.Equal(dtExpected.Second, dtActual.Second);
                    }
                    else
                    {
                        Assert.Equal(0, parsedDate.Second);
                    }
                }

                Log.Info("");
            }
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()"/> function from string values.
        /// </summary>
        [Fact]
        public void ReadAsFromStringTests()
        {
            int seed = MethodBase.GetCurrentMethod().Name.GetHashCode();
            Random rndGen = new Random(seed);

            TestReadAsFromStringRoundtrip<bool>(false, "false");
            TestReadAsFromStringRoundtrip<bool>(false, "False");
            TestReadAsFromStringRoundtrip<bool>(true, "true");
            TestReadAsFromStringRoundtrip<bool>(true, "True");
            TestReadAsFromStringRoundtrip<byte>(PrimitiveCreator.CreateInstanceOfByte(rndGen));
            TestReadAsFromStringRoundtrip<char>(PrimitiveCreator.CreateInstanceOfChar(rndGen));
            TestReadAsFromStringRoundtrip<decimal>(PrimitiveCreator.CreateInstanceOfDecimal(rndGen));
            TestReadAsFromStringRoundtrip<int>(PrimitiveCreator.CreateInstanceOfInt32(rndGen));
            TestReadAsFromStringRoundtrip<long>(PrimitiveCreator.CreateInstanceOfInt64(rndGen));
            TestReadAsFromStringRoundtrip<sbyte>(PrimitiveCreator.CreateInstanceOfSByte(rndGen));
            TestReadAsFromStringRoundtrip<short>(PrimitiveCreator.CreateInstanceOfInt16(rndGen));
            TestReadAsFromStringRoundtrip<ushort>(PrimitiveCreator.CreateInstanceOfUInt16(rndGen));
            TestReadAsFromStringRoundtrip<uint>(PrimitiveCreator.CreateInstanceOfUInt32(rndGen));
            TestReadAsFromStringRoundtrip<ulong>(PrimitiveCreator.CreateInstanceOfUInt64(rndGen));
            double dbl = rndGen.NextDouble() * rndGen.Next();
            TestReadAsFromStringRoundtrip<double>(dbl, dbl.ToString("R", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<double>(double.PositiveInfinity, "Infinity");
            TestReadAsFromStringRoundtrip<double>(double.NegativeInfinity, "-Infinity");
            TestReadAsFromStringRoundtrip<double>(double.NaN, "NaN");
            float flt = (float)(rndGen.NextDouble() * rndGen.Next());
            TestReadAsFromStringRoundtrip<float>(flt, flt.ToString("R", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<float>(float.PositiveInfinity, "Infinity");
            TestReadAsFromStringRoundtrip<float>(float.NegativeInfinity, "-Infinity");
            TestReadAsFromStringRoundtrip<float>(float.NaN, "NaN");
            Guid guid = PrimitiveCreator.CreateInstanceOfGuid(rndGen);
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("N", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("D", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("B", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("P", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture).Replace("0x", "0X"));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture).Replace("{", " { "));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture).Replace("}", " } "));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture).Replace(",", "  ,    "));
            TestReadAsFromStringRoundtrip<Guid>(guid, guid.ToString("X", CultureInfo.InvariantCulture).Replace("0x", "0x0000"));
            Uri uri = null;
            do
            {
                try
                {
                    uri = PrimitiveCreator.CreateInstanceOfUri(rndGen);
                }
                catch (UriFormatException)
                {
                }
            } while (uri == null);

            TestReadAsFromStringRoundtrip<Uri>(uri);
            TestReadAsFromStringRoundtrip<string>(PrimitiveCreator.CreateInstanceOfString(rndGen));

            // Roundtrip reference DateTime to remove some of the precision in the ticks. Otherwise, value is too precise.
            DateTimeOffset dateTimeOffset = PrimitiveCreator.CreateInstanceOfDateTimeOffset(rndGen);
            const string ISO8601Format = "yyyy-MM-ddTHH:mm:sszzz";
            dateTimeOffset = DateTimeOffset.ParseExact(dateTimeOffset.ToString(ISO8601Format, CultureInfo.InvariantCulture), ISO8601Format, CultureInfo.InvariantCulture);
            DateTime dateTime = dateTimeOffset.UtcDateTime;
            TestReadAsFromStringRoundtrip<DateTime>(dateTime, dateTimeOffset.ToUniversalTime().ToString(@"ddd, d MMM yyyy HH:mm:ss \U\T\C", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTime>(dateTime, dateTimeOffset.ToUniversalTime().ToString(@"ddd, d MMM yyyy HH:mm:ss \G\M\T", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTime>(dateTime.ToLocalTime(), dateTimeOffset.ToString(@"ddd, d MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTime>(dateTime, dateTime.ToString("yyyy-MM-ddTHH:mm:ssK"));
            TestReadAsFromStringRoundtrip<DateTime>(dateTime.ToLocalTime(), dateTimeOffset.ToString(@"ddd, d MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTime>(dateTime.ToLocalTime(), dateTimeOffset.ToString("yyyy-MM-ddTHH:mm:sszzz"));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset.UtcDateTime, dateTimeOffset.ToUniversalTime().ToString(@"ddd, d MMM yyyy HH:mm:ss \U\T\C", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset.UtcDateTime, dateTimeOffset.ToUniversalTime().ToString(@"ddd, d MMM yyyy HH:mm:ss \G\M\T", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, dateTimeOffset.ToUniversalTime().ToString(@"ddd, d MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, dateTime.ToString("yyyy-MM-ddTHH:mm:ssK"));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, dateTimeOffset.ToString("yyyy-MM-ddTHH:mm:sszzz"));
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, dateTimeOffset.ToString(@"ddd, d MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture));

            // Create ASPNetFormat DateTime
            long unixEpochMilliseconds = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000;
            long millisecondsFromUnixEpoch = dateTime.Ticks / 10000 - unixEpochMilliseconds;
            string AspNetFormattedDateTime = String.Format("/Date({0})/", millisecondsFromUnixEpoch);
            string AspNetFormattedDateTimeWithValidTZ = String.Format("/Date({0}+0700)/", millisecondsFromUnixEpoch);
            string AspNetFormattedDateTimeInvalid1 = String.Format("/Date({0}+99999)/", millisecondsFromUnixEpoch);
            string AspNetFormattedDateTimeInvalid2 = String.Format("/Date({0}+07z0)/", millisecondsFromUnixEpoch);
            TestReadAsFromStringRoundtrip<DateTime>(dateTime, AspNetFormattedDateTime);
            TestReadAsFromStringRoundtrip<DateTime>(dateTime.ToLocalTime(), AspNetFormattedDateTimeWithValidTZ);
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, AspNetFormattedDateTime);
            TestReadAsFromStringRoundtrip<DateTimeOffset>(dateTimeOffset, AspNetFormattedDateTimeWithValidTZ);

            ExpectException<FormatException>(delegate { new JsonPrimitive(AspNetFormattedDateTimeInvalid1).ReadAs<DateTime>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive(AspNetFormattedDateTimeInvalid2).ReadAs<DateTime>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive(AspNetFormattedDateTimeInvalid1).ReadAs<DateTimeOffset>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive(AspNetFormattedDateTimeInvalid2).ReadAs<DateTimeOffset>(); });

            ExpectException<FormatException>(delegate { new JsonPrimitive("INF").ReadAs<float>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("-INF").ReadAs<float>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("infinity").ReadAs<float>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("INFINITY").ReadAs<float>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("nan").ReadAs<float>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("Nan").ReadAs<float>(); });

            ExpectException<FormatException>(delegate { new JsonPrimitive("INF").ReadAs<double>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("-INF").ReadAs<double>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("infinity").ReadAs<double>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("INFINITY").ReadAs<double>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("nan").ReadAs<double>(); });
            ExpectException<FormatException>(delegate { new JsonPrimitive("Nan").ReadAs<double>(); });
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()">JsonValue.ReadAs&lt;string&gt;</see> method from number values.
        /// </summary>
        [Fact]
        public void TestReadAsStringFromNumbers()
        {
            int seed = MethodBase.GetCurrentMethod().Name.GetHashCode();
            Random rndGen = new Random(seed);

            int intValue = PrimitiveCreator.CreateInstanceOfInt32(rndGen);
            JsonValue jv = intValue;
            Assert.Equal(intValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(intValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            uint uintValue = PrimitiveCreator.CreateInstanceOfUInt32(rndGen);
            jv = uintValue;
            Assert.Equal(uintValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(uintValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            long longValue = PrimitiveCreator.CreateInstanceOfInt64(rndGen);
            jv = longValue;
            Assert.Equal(longValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(longValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            ulong ulongValue = PrimitiveCreator.CreateInstanceOfUInt64(rndGen);
            jv = ulongValue;
            Assert.Equal(ulongValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(ulongValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            short shortValue = PrimitiveCreator.CreateInstanceOfInt16(rndGen);
            jv = shortValue;
            Assert.Equal(shortValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(shortValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            ushort ushortValue = PrimitiveCreator.CreateInstanceOfUInt16(rndGen);
            jv = ushortValue;
            Assert.Equal(ushortValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(ushortValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            byte byteValue = PrimitiveCreator.CreateInstanceOfByte(rndGen);
            jv = byteValue;
            Assert.Equal(byteValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(byteValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            sbyte sbyteValue = PrimitiveCreator.CreateInstanceOfSByte(rndGen);
            jv = sbyteValue;
            Assert.Equal(sbyteValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(sbyteValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            decimal decValue = PrimitiveCreator.CreateInstanceOfDecimal(rndGen);
            jv = decValue;
            Assert.Equal(decValue.ToString(CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(decValue.ToString(CultureInfo.InvariantCulture), jv.ReadAs<string>());

            float fltValue = PrimitiveCreator.CreateInstanceOfSingle(rndGen);
            jv = fltValue;
            Assert.Equal(fltValue.ToString("R", CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(fltValue.ToString("R", CultureInfo.InvariantCulture), jv.ReadAs<string>());

            double dblValue = PrimitiveCreator.CreateInstanceOfDouble(rndGen);
            jv = dblValue;
            Assert.Equal(dblValue.ToString("R", CultureInfo.InvariantCulture), jv.ToString());
            Assert.Equal(dblValue.ToString("R", CultureInfo.InvariantCulture), jv.ReadAs<string>());
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()">JsonValue.ReadAs&lt;string&gt;</see> method from date values.
        /// </summary>
        [Fact]
        public void TestReadAsStringFromDates()
        {
            int seed = MethodBase.GetCurrentMethod().Name.GetHashCode();
            Random rndGen = new Random(seed);

            DateTime dateTimeValue = PrimitiveCreator.CreateInstanceOfDateTime(rndGen);
            JsonValue jv = dateTimeValue;
            Assert.Equal("\"" + dateTimeValue.ToString(DateTimeFormat, CultureInfo.InvariantCulture) + "\"", jv.ToString());
            Assert.Equal(dateTimeValue.ToString(DateTimeFormat, CultureInfo.InvariantCulture), jv.ReadAs<string>());
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()">JsonValue.ReadAs&lt;string&gt;</see> method from char values.
        /// </summary>
        [Fact]
        public void TestReadAsStringFromChar()
        {
            char[] chars = "abc\u0000\b\f\r\n\t\ufedc".ToCharArray();

            foreach (char c in chars)
            {
                string expected = new string(c, 1);
                JsonValue jv = c;
                string actual1 = jv.ReadAs<string>();
                string actual2 = (string)jv;

                Assert.Equal(expected, actual1);
                Assert.Equal(expected, actual2);
            }
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()"/> method where T is a number type and the value is created from a string.
        /// </summary>
        [Fact]
        public void TestReadAsNumberFromStrings()
        {
            Dictionary<object, List<Type>> valuesToNonOverflowingTypesMapping = new Dictionary<object, List<Type>>
            {
                { double.NaN.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(float), typeof(double) } },
                { double.NegativeInfinity.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(float), typeof(double) } },
                { double.PositiveInfinity.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(float), typeof(double) } },
                { double.MaxValue.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(double), typeof(float) } },
                { double.MinValue.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(double), typeof(float) } },
                { float.MaxValue.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(double), typeof(float) } },
                { float.MinValue.ToString("R", CultureInfo.InvariantCulture), new List<Type> { typeof(double), typeof(float) } },
                { Int64.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(ulong) } },
                { Int64.MinValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long) } },
                { Int32.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(ulong), typeof(uint) } },
                { Int32.MinValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int) } },
                { Int16.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(ulong), typeof(uint), typeof(ushort) } },
                { Int16.MinValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short) } },
                { SByte.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { SByte.MinValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { UInt64.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(ulong) } },
                { UInt32.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(ulong), typeof(uint) } },
                { UInt16.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(ulong), typeof(uint), typeof(ushort) } },
                { Byte.MaxValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { Byte.MinValue.ToString(), new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "1", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "+01", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "01.1e+01", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "1e1", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "1.0", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "01.0", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "-1", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { "-1.0", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { "-01.0", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { "-01.0e+01", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { "-01.0e-01", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "-.1", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { "-0100.0e-1", new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
            };

            foreach (KeyValuePair<object, List<Type>> mapping in valuesToNonOverflowingTypesMapping)
            {
                ConvertValueToNumber<double>(mapping);
                ConvertValueToNumber<float>(mapping);
                ConvertValueToNumber<decimal>(mapping);
                ConvertValueToNumber<long>(mapping);
                ConvertValueToNumber<int>(mapping);
                ConvertValueToNumber<short>(mapping);
                ConvertValueToNumber<sbyte>(mapping);
                ConvertValueToNumber<ulong>(mapping);
                ConvertValueToNumber<uint>(mapping);
                ConvertValueToNumber<ushort>(mapping);
                ConvertValueToNumber<byte>(mapping);
            }

            Dictionary<object, List<Type>> valuesThatAreInvalidNumber = new Dictionary<object, List<Type>>
            {
                { "1L", new List<Type> { } },
                { "0x1", new List<Type> { } },
                { "1e309", new List<Type> { } },
                { "", new List<Type> { } },
                { "-", new List<Type> { } },
                { "e10", new List<Type> { } },
            };

            foreach (KeyValuePair<object, List<Type>> mapping in valuesThatAreInvalidNumber)
            {
                ConvertValueToNumber<double, FormatException>(mapping);
                ConvertValueToNumber<float, FormatException>(mapping);
                ConvertValueToNumber<decimal, FormatException>(mapping);
                ConvertValueToNumber<long, FormatException>(mapping);
                ConvertValueToNumber<int, FormatException>(mapping);
                ConvertValueToNumber<short, FormatException>(mapping);
                ConvertValueToNumber<sbyte, FormatException>(mapping);
                ConvertValueToNumber<ulong, FormatException>(mapping);
                ConvertValueToNumber<uint, FormatException>(mapping);
                ConvertValueToNumber<ushort, FormatException>(mapping);
                ConvertValueToNumber<byte, FormatException>(mapping);
            }
        }

        /// <summary>
        /// Tests for the <see cref="System.Json.JsonValue.ReadAs{T}()"/> method where T is a number type and the value is created from a number.
        /// This is essentially a number conversion test.
        /// </summary>
        [Fact]
        public void TestReadAsNumberFromNumber()
        {
            Dictionary<object, List<Type>> valuesToNonOverflowingTypesMapping = new Dictionary<object, List<Type>>
            { 
                { double.NaN, new List<Type> { typeof(float), typeof(double) } },
                { double.NegativeInfinity, new List<Type> { typeof(float), typeof(double) } },
                { double.PositiveInfinity, new List<Type> { typeof(float), typeof(double) } },
                { float.NaN, new List<Type> { typeof(float), typeof(double) } },
                { float.NegativeInfinity, new List<Type> { typeof(float), typeof(double) } },
                { float.PositiveInfinity, new List<Type> { typeof(float), typeof(double) } },
                { double.MaxValue, new List<Type> { typeof(double), typeof(float) } },
                { double.MinValue, new List<Type> { typeof(double), typeof(float) } },
                { float.MaxValue, new List<Type> { typeof(double), typeof(float) } },
                { float.MinValue, new List<Type> { typeof(double), typeof(float) } },
                { Int64.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(ulong) } },
                { Int64.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long) } },
                { Int32.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(ulong), typeof(uint) } },
                { Int32.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int) } },
                { Int16.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(ulong), typeof(uint), typeof(ushort) } },
                { Int16.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short) } },
                { SByte.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { SByte.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte) } },
                { UInt64.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(ulong) } },
                { UInt64.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { UInt32.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(ulong), typeof(uint) } },
                { UInt32.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { UInt16.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(ulong), typeof(uint), typeof(ushort) } },
                { UInt16.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { Byte.MaxValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { Byte.MinValue, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (double)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (float)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (decimal)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (long)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (int)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (short)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (sbyte)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (ulong)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (uint)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (ushort)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
                { (byte)1, new List<Type> { typeof(double), typeof(float), typeof(decimal), typeof(long), typeof(int), typeof(short), typeof(sbyte), typeof(ulong), typeof(uint), typeof(ushort), typeof(byte) } },
            };

            foreach (KeyValuePair<object, List<Type>> mapping in valuesToNonOverflowingTypesMapping)
            {
                ConvertValueToNumber<double>(mapping);
                ConvertValueToNumber<float>(mapping);
                ConvertValueToNumber<decimal>(mapping);
                ConvertValueToNumber<long>(mapping);
                ConvertValueToNumber<int>(mapping);
                ConvertValueToNumber<short>(mapping);
                ConvertValueToNumber<sbyte>(mapping);
                ConvertValueToNumber<ulong>(mapping);
                ConvertValueToNumber<uint>(mapping);
                ConvertValueToNumber<ushort>(mapping);
                ConvertValueToNumber<byte>(mapping);
            }
        }

        static void ConvertValueToNumber<T>(KeyValuePair<object, List<Type>> mapping)
        {
            ConvertValueToNumber<T, OverflowException>(mapping);
        }

        static void ConvertValueToNumber<T, TException>(KeyValuePair<object, List<Type>> mapping)
            where TException : Exception
        {
            JsonValue jsonValue = CastToJsonValue(mapping.Key);

            Log.Info("Converting value {0} of type {1} to type {2}.", mapping.Key, mapping.Key.GetType().Name, typeof(T).Name);

            if (mapping.Value.Contains(typeof(T)))
            {
                Console.Write("Conversion should work... ");
                T valueOfT;
                Assert.True(jsonValue.TryReadAs<T>(out valueOfT));
                if (mapping.Key.GetType() != typeof(string))
                {
                    Console.Write("and original value casted to {0} should be the same as the retrieved value... ", typeof(T).Name);
                    T castValue = (T)Convert.ChangeType(mapping.Key, typeof(T), CultureInfo.InvariantCulture);
                    Assert.Equal<T>(castValue, valueOfT);
                }
            }
            else
            {
                Console.Write("Conversion should fail... ");
                T valueOfT;
                Assert.False(jsonValue.TryReadAs<T>(out valueOfT), String.Format("It was possible to read the value as {0}", valueOfT));
                ExpectException<TException>(delegate
                {
                    jsonValue.ReadAs<T>();
                });
            }

            Log.Info("Success!");
        }

        static JsonValue CastToJsonValue(object o)
        {
            switch (Type.GetTypeCode(o.GetType()))
            {
                case TypeCode.Boolean:
                    return (JsonValue)(bool)o;
                case TypeCode.Byte:
                    return (JsonValue)(byte)o;
                case TypeCode.Char:
                    return (JsonValue)(char)o;
                case TypeCode.DateTime:
                    return (JsonValue)(DateTime)o;
                case TypeCode.Decimal:
                    return (JsonValue)(decimal)o;
                case TypeCode.Double:
                    return (JsonValue)(double)o;
                case TypeCode.Int16:
                    return (JsonValue)(short)o;
                case TypeCode.Int32:
                    return (JsonValue)(int)o;
                case TypeCode.Int64:
                    return (JsonValue)(long)o;
                case TypeCode.SByte:
                    return (JsonValue)(sbyte)o;
                case TypeCode.Single:
                    return (JsonValue)(float)o;
                case TypeCode.String:
                    return (JsonValue)(string)o;
                case TypeCode.UInt16:
                    return (JsonValue)(ushort)o;
                case TypeCode.UInt32:
                    return (JsonValue)(uint)o;
                case TypeCode.UInt64:
                    return (JsonValue)(ulong)o;
                default:
                    if (o.GetType() == typeof(DateTimeOffset))
                    {
                        return (JsonValue)(DateTimeOffset)o;
                    }

                    if (o.GetType() == typeof(Guid))
                    {
                        return (JsonValue)(Guid)o;
                    }

                    if (o.GetType() == typeof(Uri))
                    {
                        return (JsonValue)(Uri)o;
                    }

                    break;
            }

            return (JsonObject)o;
        }

        static void ExpectException<T>(Action action) where T : Exception
        {
            JsonValueTests.ExpectException<T>(action);
        }

        static string GetExpectedRepresentation(object obj)
        {
            if (obj is double)
            {
                double dbl = (double)obj;
                if (Double.IsPositiveInfinity(dbl))
                {
                    return "Infinity";
                }
                else if (Double.IsNegativeInfinity(dbl))
                {
                    return "-Infinity";
                }
            }
            else if (obj is float)
            {
                float flt = (float)obj;
                if (Single.IsPositiveInfinity(flt))
                {
                    return "Infinity";
                }
                else if (Single.IsNegativeInfinity(flt))
                {
                    return "-Infinity";
                }
            }
            else if (obj is DateTime)
            {
                DateTime dt = (DateTime)obj;
                return "\"" + dt.ToString(DateTimeFormat, CultureInfo.InvariantCulture) + "\"";
            }

            using (MemoryStream ms = new MemoryStream())
            {
                DataContractJsonSerializer dcjs = new DataContractJsonSerializer(obj.GetType());
                dcjs.WriteObject(ms, obj);
                return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Position);
            }
        }

        void ValidateJson(JsonPrimitive jsonPrim, string expectedJson, JsonType expectedJsonType)
        {
            Assert.Equal(expectedJson, jsonPrim.ToString());
            Assert.Equal(expectedJsonType, jsonPrim.JsonType);
        }

        void TestReadAsRoundtrip<T>(JsonPrimitive jsonPrim, T myOriginalObjectOfT)
        {
            T myReadObjectOfT = jsonPrim.ReadAs<T>();
            T myTryReadObjectOfT;
            Assert.True(jsonPrim.TryReadAs<T>(out myTryReadObjectOfT));
            Assert.Equal(myOriginalObjectOfT, myReadObjectOfT);
            Assert.Equal(myOriginalObjectOfT, myTryReadObjectOfT);

            string stringValue;
            Assert.True(jsonPrim.TryReadAs<string>(out stringValue));
            if (typeof(T) == typeof(bool))
            {
                // bool returns a lowercase version. make sure we get something usable by doing another roundtrip of the value in .NET
                Assert.Equal(String.Format(CultureInfo.InvariantCulture, "{0}", myOriginalObjectOfT), bool.Parse(stringValue).ToString(CultureInfo.InvariantCulture));
            }
            else if (typeof(T) == typeof(float) || typeof(T) == typeof(double))
            {
                Assert.Equal(String.Format(CultureInfo.InvariantCulture, "{0:R}", myOriginalObjectOfT), stringValue);
            }
            else if (typeof(T) == typeof(DateTime))
            {
                Assert.Equal(String.Format(CultureInfo.InvariantCulture, "{0:" + DateTimeFormat + "}", myOriginalObjectOfT), stringValue);
            }
            else
            {
                Assert.Equal(String.Format(CultureInfo.InvariantCulture, "{0}", myOriginalObjectOfT), stringValue);
            }
        }

        void TestReadAsFromStringRoundtrip<T>(T value)
        {
            TestReadAsFromStringRoundtrip<T>(value, String.Format(CultureInfo.InvariantCulture, "{0}", value));
        }

        void TestReadAsFromStringRoundtrip<T>(T value, string valueString)
        {
            T tempOfT;
            JsonPrimitive jsonPrim = new JsonPrimitive(valueString);
            Assert.True(jsonPrim.TryReadAs<T>(out tempOfT));
            Assert.Equal<T>(value, tempOfT);
        }
    }
}
