[3.0 Bug fix]JsonSerializer.Parse throws for UInt64 backed enum with value -1 (dotnet...
authorMarco Rossignoli <marco.rossignoli@gmail.com>
Thu, 13 Jun 2019 17:10:01 +0000 (19:10 +0200)
committerJeremy Kuhne <jkuhne@microsoft.com>
Thu, 13 Jun 2019 17:10:01 +0000 (10:10 -0700)
* fix ulong enum converter

* save work

* address PR feedback

* add more tests

* remove -1 = Max support for unsigned

* address PR fedback

* update tests

* address PR feedback

* address PR feedback

* address PR feedback

Commit migrated from https://github.com/dotnet/corefx/commit/42e11c64d878d5875dbc6f5d228cb034ee4b2e59

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterEnum.cs
src/libraries/System.Text.Json/tests/Serialization/EnumTests.cs
src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs
src/libraries/System.Text.Json/tests/Serialization/TestClasses.cs

index 653ff7b..455209f 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Runtime.CompilerServices;
 using System.Text.Json.Serialization.Policies;
 
 namespace System.Text.Json.Serialization.Converters
@@ -9,7 +10,7 @@ namespace System.Text.Json.Serialization.Converters
     internal sealed class JsonValueConverterEnum<TValue> : JsonValueConverter<TValue>
         where TValue : struct, Enum
     {
-        private static readonly bool s_isUint64 = Enum.GetUnderlyingType(typeof(TValue)) == typeof(ulong);
+        private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TValue)));
 
         public bool TreatAsString { get; private set; }
 
@@ -39,18 +40,87 @@ namespace System.Text.Json.Serialization.Converters
                 return false;
             }
 
-            if (s_isUint64)
-            {
-                if (reader.TryGetUInt64(out ulong ulongValue))
-                {
-                    value = (TValue)Enum.ToObject(valueType, ulongValue);
-                    return true;
-                }
-            }
-            else if (reader.TryGetInt64(out long longValue))
+            // When utf8reader/writer will support all primitive types we should remove custom bound checks
+            // https://github.com/dotnet/corefx/issues/36125
+
+            switch (s_enumTypeCode)
             {
-                value = (TValue)Enum.ToObject(valueType, longValue);
-                return true;
+                case TypeCode.SByte:
+                    {
+                        if (reader.TryGetInt32(out int byte8) && JsonHelpers.IsInRangeInclusive(byte8, sbyte.MinValue, sbyte.MaxValue))
+                        {
+                            sbyte byte8Value = (sbyte)byte8;
+                            value = Unsafe.As<sbyte, TValue>(ref byte8Value);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.Byte:
+                    {
+                        if (reader.TryGetUInt32(out uint ubyte8) && JsonHelpers.IsInRangeInclusive(ubyte8, byte.MinValue, byte.MaxValue))
+                        {
+                            byte ubyte8Value = (byte)ubyte8;
+                            value = Unsafe.As<byte, TValue>(ref ubyte8Value);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.Int16:
+                    {
+                        if (reader.TryGetInt32(out int int16) && JsonHelpers.IsInRangeInclusive(int16, short.MinValue, short.MaxValue))
+                        {
+                            short shortValue = (short)int16;
+                            value = Unsafe.As<short, TValue>(ref shortValue);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.UInt16:
+                    {
+                        if (reader.TryGetUInt32(out uint uint16) && JsonHelpers.IsInRangeInclusive(uint16, ushort.MinValue, ushort.MaxValue))
+                        {
+                            ushort ushortValue = (ushort)uint16;
+                            value = Unsafe.As<ushort, TValue>(ref ushortValue);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.Int32:
+                    {
+                        if (reader.TryGetInt32(out int int32))
+                        {
+                            value = Unsafe.As<int, TValue>(ref int32);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.UInt32:
+                    {
+                        if (reader.TryGetUInt32(out uint uint32))
+                        {
+                            value = Unsafe.As<uint, TValue>(ref uint32);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.Int64:
+                    {
+                        if (reader.TryGetInt64(out long int64))
+                        {
+                            value = Unsafe.As<long, TValue>(ref int64);
+                            return true;
+                        }
+                        break;
+                    }
+                case TypeCode.UInt64:
+                    {
+                        if (reader.TryGetUInt64(out ulong uint64))
+                        {
+                            value = Unsafe.As<ulong, TValue>(ref uint64);
+                            return true;
+                        }
+                        break;
+                    }
             }
 
             value = default;
@@ -63,7 +133,7 @@ namespace System.Text.Json.Serialization.Converters
             {
                 writer.WriteStringValue(value.ToString());
             }
-            else if (s_isUint64)
+            else if (s_enumTypeCode == TypeCode.UInt64)
             {
                 // Use the ulong converter to prevent conversion into a signed\long value.
                 ulong ulongValue = Convert.ToUInt64(value);
@@ -83,7 +153,7 @@ namespace System.Text.Json.Serialization.Converters
             {
                 writer.WriteString(propertyName, value.ToString());
             }
-            else if (s_isUint64)
+            else if (s_enumTypeCode == TypeCode.UInt64)
             {
                 // Use the ulong converter to prevent conversion into a signed\long value.
                 ulong ulongValue = Convert.ToUInt64(value);
index 70e6a1a..2df4405 100644 (file)
@@ -18,6 +18,11 @@ namespace System.Text.Json.Serialization.Tests
                 @"""MyEnum"" : 2" +
                 @"}";
 
+        private static readonly string s_jsonInt16Enum =
+                @"{" +
+                @"""MyInt16Enum"" : 2" +
+                @"}";
+
         private static readonly string s_jsonInt64EnumMin =
                 @"{" +
                 @"""MyInt64Enum"" : " + long.MinValue +
@@ -33,25 +38,236 @@ namespace System.Text.Json.Serialization.Tests
                 @"""MyUInt64Enum"" : " + ulong.MaxValue +
                 @"}";
 
+        private static readonly string s_jsonUInt16EnumMax =
+                @"{" +
+                @"""MyUInt16Enum"" : " + ushort.MaxValue +
+                @"}";
+
         private static readonly string s_jsonByteEnum =
                 @"{" +
                 @"""MyByteEnum"" : " + byte.MaxValue +
                 @"}";
 
-        private const string UlongMaxPlus1 = "18446744073709551616"; // ulong.MaxValue + 1;
+        private static readonly string s_jsonSByteEnum =
+        @"{" +
+        @"""MySByteEnum"" : " + sbyte.MaxValue +
+        @"}";
+
+        private const string UInt64MaxPlus1 = "18446744073709551616"; // ulong.MaxValue + 1;
+        private const string MinusUInt64MaxMinus1 = "-18446744073709551616"; // -ulong.MaxValue - 1
+
+        private const string Int64MaxPlus1 = "9223372036854775808"; // long.MaxValue + 1;
+        private const string Int64MinMinus1 = "-9223372036854775809"; // long.MinValue - 1
+
+        private const string UInt32MaxPlus1 = "4294967296"; // uint.MaxValue + 1;
+        private const string MinusUInt32MaxMinus1 = "-4294967296"; // -uint.MaxValue - 1
+
+        private const string Int32MaxPlus1 = "2147483648"; // int.MaxValue + 1;
+        private const string Int32MinMinus1 = "-2147483649"; // int.MinValue - 1
+
+        private const string UInt16MaxPlus1 = "65536"; // ushort.MaxValue + 1;
+        private const string MinusUInt16MaxMinus1 = "-65536"; // -ushort.MaxValue - 1
+
+        private const string Int16MaxPlus1 = "32768"; // int.MaxValue + 1;
+        private const string Int16MinMinus1 = "-32769"; // int.MinValue - 1
+
+        private const string ByteMaxPlus1 = "256"; // byte.MaxValue + 1;
+        private const string MinusByteMaxMinus1 = "-257"; // -byte.MaxValue - 1
+
+        [Fact]
+        public static void Parse_MinusByteMaxMinus1_Throws()
+        {
+            Assert.Equal(int.Parse(MinusByteMaxMinus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {MinusByteMaxMinus1} }}").MyEnum);
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {MinusByteMaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {MinusByteMaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {MinusByteMaxMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{MinusByteMaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{MinusByteMaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{MinusByteMaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{MinusByteMaxMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_ByteMaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {ByteMaxPlus1} }}"));
+            Assert.Equal(int.Parse(ByteMaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {ByteMaxPlus1} }}").MyEnum);
+            Assert.Equal(int.Parse(ByteMaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {ByteMaxPlus1} }}").MyUInt32Enum);
+            Assert.Equal(int.Parse(ByteMaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {ByteMaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{ByteMaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{ByteMaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{ByteMaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{ByteMaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int16MinMinus1_Throws()
+        {
+            Assert.Equal(int.Parse(Int16MinMinus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int16MinMinus1} }}").MyEnum);
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int16MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int16MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int16MinMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int16MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int16MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int16MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int16MinMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int16MaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int16MaxPlus1} }}"));
+            Assert.Equal(int.Parse(Int16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int16MaxPlus1} }}").MyEnum);
+            Assert.Equal(int.Parse(Int16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int16MaxPlus1} }}").MyUInt32Enum);
+            Assert.Equal(int.Parse(Int16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int16MaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int16MaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_MinusUInt16MaxMinus1_Throws()
+        {
+            Assert.Equal(int.Parse(MinusUInt16MaxMinus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {MinusUInt16MaxMinus1} }}").MyEnum);
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {MinusUInt16MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {MinusUInt16MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {MinusUInt16MaxMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{MinusUInt16MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{MinusUInt16MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{MinusUInt16MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{MinusUInt16MaxMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_UInt16MaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {UInt16MaxPlus1} }}"));
+            Assert.Equal(int.Parse(UInt16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {UInt16MaxPlus1} }}").MyEnum);
+            Assert.Equal(int.Parse(UInt16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {UInt16MaxPlus1} }}").MyUInt32Enum);
+            Assert.Equal(int.Parse(UInt16MaxPlus1), (int)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {UInt16MaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{UInt16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{UInt16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{UInt16MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{UInt16MaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int32MinMinus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int32MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int32MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int32MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int32MinMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int32MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int32MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int32MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int32MinMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int32MaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int32MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int32MaxPlus1} }}"));
+            Assert.Equal(ulong.Parse(Int32MaxPlus1), (ulong)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int32MaxPlus1} }}").MyUInt32Enum);
+            Assert.Equal(ulong.Parse(Int32MaxPlus1), (ulong)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int32MaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int32MaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_MinusUInt32MaxMinus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {MinusUInt32MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {MinusUInt32MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {MinusUInt32MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {MinusUInt32MaxMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int64MinMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_UInt32MaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {UInt32MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {UInt32MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {UInt32MaxPlus1} }}"));
+            Assert.Equal(ulong.Parse(UInt32MaxPlus1), (ulong)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {UInt32MaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{UInt32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{UInt32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{UInt32MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{UInt32MaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int64MinMinus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int64MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int64MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int64MinMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int64MinMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int64MinMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int64MinMinus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_Int64MaxPlus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {Int64MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {Int64MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {Int64MaxPlus1} }}"));
+            Assert.Equal(ulong.Parse(Int64MaxPlus1), (ulong)JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {Int64MaxPlus1} }}").MyUInt64Enum);
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{Int64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{Int64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{Int64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{Int64MaxPlus1}\" }}"));
+        }
+
+        [Fact]
+        public static void Parse_MinusUInt64MaxMinus1_Throws()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {MinusUInt64MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {MinusUInt64MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {MinusUInt64MaxMinus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {MinusUInt64MaxMinus1} }}"));
+
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{MinusUInt64MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{MinusUInt64MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{MinusUInt64MaxMinus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{MinusUInt64MaxMinus1}\" }}"));
+        }
 
         [Fact]
-        public static void Parse_UlongMaxPlus1_Throws()
+        public static void Parse_UInt64MaxPlus1_Throws()
         {
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {UlongMaxPlus1} }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {UlongMaxPlus1} }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {UlongMaxPlus1} }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {UlongMaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : {UInt64MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {UInt64MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : {UInt64MaxPlus1} }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : {UInt64MaxPlus1} }}"));
 
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{UlongMaxPlus1}\" }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{UlongMaxPlus1}\" }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{UlongMaxPlus1}\" }}"));
-            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{UlongMaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{UInt64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{UInt64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt32Enum\" : \"{UInt64MaxPlus1}\" }}"));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyUInt64Enum\" : \"{UInt64MaxPlus1}\" }}"));
         }
 
         [Fact]
@@ -73,12 +289,9 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
-        public static void Parse_MaxValuePlus1_QuotedVsNotQuoted_QuotedThrows()
+        public static void Parse_MaxValuePlus1_QuotedVsNotQuoted_Throws()
         {
-            SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(
-                $"{{ \"MyByteEnum\" : {(ulong)byte.MaxValue + 1}, \"MyUInt32Enum\" : {(ulong)UInt32.MaxValue + 1} }}");
-            Assert.Equal((SampleByteEnum)0, result.MyByteEnum);
-            Assert.Equal((SampleUInt32Enum)0, result.MyUInt32Enum);
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : {(ulong)byte.MaxValue + 1}, \"MyUInt32Enum\" : {(ulong)UInt32.MaxValue + 1} }}"));
 
             // Quoted throws
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{(ulong)byte.MaxValue + 1}\" }}"));
@@ -89,10 +302,9 @@ namespace System.Text.Json.Serialization.Tests
         public static void Parse_Negative1_QuotedVsUnquoted_QuotedThrows()
         {
             string json = "{ \"MyByteEnum\" : -1, \"MyUInt32Enum\" : -1 }";
-            SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(json);
-            Assert.Equal((SampleByteEnum)byte.MaxValue, result.MyByteEnum);
-            Assert.Equal((SampleUInt32Enum)UInt32.MaxValue, result.MyUInt32Enum);
 
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>(json));
+            
             // Quoted throws
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("{ \"MyByteEnum\" : \"-1\" }"));
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("{ \"MyUInt32Enum\" : \"-1\" }"));
@@ -103,11 +315,9 @@ namespace System.Text.Json.Serialization.Tests
         [InlineData("{ \"MyUInt64Enum\" : -1 }")]
         [InlineData("{ \"MyUInt64Enum\" : -1, \"MyUInt32Enum\" : -1 }")]
         [InlineData("{ \"MyUInt64Enum\" : -1, \"MyUInt32Enum\" : -1, \"MyByteEnum\" : -1 }")]
-        [ActiveIssue(38363)]
-        public static void Parse_Negative1ForUInt64Enum_ShouldNotThrow(string json)
+        public static void Parse_Negative1ForUInt64Enum_Throw(string json)
         {
-            SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(json);
-            Assert.Equal((SampleUInt64Enum)UInt64.MaxValue, result.MyUInt64Enum);
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>(json));
         }
 
         [Theory]
@@ -115,13 +325,10 @@ namespace System.Text.Json.Serialization.Tests
         [InlineData((ulong)byte.MaxValue + 2, (ulong)UInt32.MaxValue + 2, (SampleByteEnum)1, (SampleUInt32Enum)1)]
         [InlineData((ulong)byte.MaxValue + 13, (ulong)UInt32.MaxValue + 13, (SampleByteEnum)12, (SampleUInt32Enum)12)]
         [InlineData((ulong)byte.MaxValue * 2, (ulong)UInt32.MaxValue * 2, (SampleByteEnum)byte.MaxValue - 1, (SampleUInt32Enum)UInt32.MaxValue - 1)]
-        public static void Parse_Ulong_JsonWithAcceptableInvalidNumber_Success(ulong i, ulong j, SampleByteEnum e1, SampleUInt32Enum e2)
+        public static void Parse_Ulong_InvalidNumber_Throw(ulong i, ulong j, SampleByteEnum e1, SampleUInt32Enum e2)
         {
             string json = $"{{ \"MyByteEnum\" : {i}, \"MyUInt32Enum\" : {j} }}";
-            SimpleTestClass result = JsonSerializer.Parse<SimpleTestClass>(json);
-            Assert.Equal(e1, result.MyByteEnum);
-            Assert.Equal(e2, result.MyUInt32Enum);
-
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>(json));
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyEnum\" : \"{i}\" }}"));
             Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>($"{{ \"MyByteEnum\" : \"{j}\" }}"));
         }
@@ -140,6 +347,13 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
+        public static void EnumAsInt16()
+        {
+            SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonInt16Enum);
+            Assert.Equal(SampleEnumInt16.Two, obj.MyInt16Enum);
+        }
+
+        [Fact]
         public static void EnumAsInt64Min()
         {
             SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonInt64EnumMin);
@@ -161,10 +375,24 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
+        public static void EnumAsUInt16Max()
+        {
+            SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonUInt16EnumMax);
+            Assert.Equal(SampleEnumUInt16.Max, obj.MyUInt16Enum);
+        }
+
+        [Fact]
         public static void EnumAsByteMax()
         {
             SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonByteEnum);
             Assert.Equal(SampleByteEnum.Max, obj.MyByteEnum);
         }
+
+        [Fact]
+        public static void EnumAsSByteMax()
+        {
+            SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonSByteEnum);
+            Assert.Equal(SampleSByteEnum.Max, obj.MySByteEnum);
+        }
     }
 }
index c788fd2..8049d12 100644 (file)
@@ -28,10 +28,13 @@ namespace System.Text.Json.Serialization.Tests
         public double MyDouble { get; set; }
         public DateTime MyDateTime { get; set; }
         public DateTimeOffset MyDateTimeOffset { get; set; }
+        public SampleSByteEnum MySByteEnum { get; set; }
         public SampleByteEnum MyByteEnum { get; set; }
         public SampleEnum MyEnum { get; set; }
+        public SampleEnumInt16 MyInt16Enum { get; set; }
         public SampleInt64Enum MyInt64Enum { get; set; }
         public SampleUInt32Enum MyUInt32Enum { get; set; }
+        public SampleEnumUInt16 MyUInt16Enum { get; set; }
         public SampleUInt64Enum MyUInt64Enum { get; set; }
         public short[] MyInt16Array { get; set; }
         public int[] MyInt32Array { get; set; }
index ef9dcc8..db237f8 100644 (file)
@@ -22,12 +22,33 @@ namespace System.Text.Json.Serialization.Tests
         Max = byte.MaxValue
     }
 
+    public enum SampleSByteEnum : sbyte
+    {
+        One = 0,
+        Two = 1,
+        Max = sbyte.MaxValue
+    }
+
     public enum SampleEnum
     {
         One = 1,
         Two = 2
     }
 
+    public enum SampleEnumInt16 : short
+    {
+        One = 1,
+        Two = 2,
+        Max = short.MaxValue
+    }
+
+    public enum SampleEnumUInt16 : ushort
+    {
+        One = 1,
+        Two = 2,
+        Max = ushort.MaxValue
+    }
+
     public enum SampleInt64Enum : long
     {
         Min = long.MinValue,