Skip to content

Commit

Permalink
add tests to increase enum serialization code coverage (#40601)
Browse files Browse the repository at this point in the history
* add tests to increase enum serialization code coverage

* implement feedback and add test to cover naming policy scenario

* remove unnecessary bound check now that all primitive types are supported

* Revert "remove unnecessary bound check now that all primitive types are supported"

This reverts commit 0a6a345.

* implement review feedback

remove unnecessary serializer options in test
rename JsonNamingPolicy ToLower -> ToLowerNamingPolicy

* Remove commented-out code

Co-authored-by: David Cantú <[email protected]>
  • Loading branch information
Jacksondr5 and jozkee authored Sep 23, 2020
1 parent 0a69f02 commit 0446e4f
Showing 1 changed file with 230 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -33,7 +34,7 @@ public void ConvertDayOfWeek()

// Try a unique naming policy
options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter(new ToLower()));
options.Converters.Add(new JsonStringEnumConverter(new ToLowerNamingPolicy()));

json = JsonSerializer.Serialize(DayOfWeek.Friday, options);
Assert.Equal(@"""friday""", json);
Expand All @@ -48,7 +49,7 @@ public void ConvertDayOfWeek()
Assert.Throws<JsonException>(() => JsonSerializer.Serialize((DayOfWeek)(-1), options));
}

public class ToLower : JsonNamingPolicy
public class ToLowerNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) => name.ToLowerInvariant();
}
Expand Down Expand Up @@ -92,7 +93,7 @@ public void ConvertFileAttributes()

// Try a unique casing
options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter(new ToLower()));
options.Converters.Add(new JsonStringEnumConverter(new ToLowerNamingPolicy()));

json = JsonSerializer.Serialize(FileAttributes.NoScrubData, options);
Assert.Equal(@"""noscrubdata""", json);
Expand Down Expand Up @@ -144,7 +145,7 @@ private class LowerCaseEnumAttribute : JsonConverterAttribute
public LowerCaseEnumAttribute() { }

public override JsonConverter CreateConverter(Type typeToConvert)
=> new JsonStringEnumConverter(new ToLower());
=> new JsonStringEnumConverter(new ToLowerNamingPolicy());
}

[Fact]
Expand Down Expand Up @@ -185,7 +186,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer
private enum MyCustomEnum
{
First = 1,
Second =2
Second = 2
}

[Fact]
Expand Down Expand Up @@ -232,6 +233,23 @@ public static void MoreThan64EnumValuesToSerialize()
}
}

[Fact]
public static void MoreThan64EnumValuesToSerializeWithNamingPolicy()
{
var options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter(new ToLowerNamingPolicy()) }
};

for (int i = 0; i < 128; i++)
{
MyEnum value = (MyEnum)i;
string asStr = value.ToString().ToLowerInvariant();
string expected = char.IsLetter(asStr[0]) ? $@"""{asStr}""" : asStr;
Assert.Equal(expected, JsonSerializer.Serialize(value, options));
}
}

[Fact, OuterLoop]
public static void VeryLargeAmountOfEnumsToSerialize()
{
Expand Down Expand Up @@ -294,5 +312,212 @@ public enum MyEnum
U = 1 << 20,
V = 1 << 21,
}

[Fact, OuterLoop]
public static void VeryLargeAmountOfEnumDictionaryKeysToSerialize()
{
// Ensure we don't throw OutOfMemoryException.

const int MaxValue = (int)MyEnum.V;

// Every value between 0 and MaxValue maps to a valid enum
// identifier, and is a candidate to go into the name cache.

// Write the first 45 values.
Dictionary<MyEnum, int> dictionary;
for (int i = 1; i < 46; i++)
{
dictionary = new Dictionary<MyEnum, int> { { (MyEnum)i, i } };
JsonSerializer.Serialize(dictionary);
}

// At this point, there are 60 values in the name cache;
// 22 cached at warm-up, the rest in the above loop.

// Ensure the approximate size limit for the name cache (a concurrent dictionary) is honored.
// Use multiple threads to perhaps go over the soft limit of 64, but not by more than a couple.
Parallel.For(
0,
8,
i =>
{
dictionary = new Dictionary<MyEnum, int> { { (MyEnum)(46 + i), i } };
JsonSerializer.Serialize(dictionary);
}
);

// Write the remaining enum values. The cache is capped to avoid
// OutOfMemoryException due to having too many cached items.
for (int i = 54; i <= MaxValue; i++)
{
dictionary = new Dictionary<MyEnum, int> { { (MyEnum)i, i } };
JsonSerializer.Serialize(dictionary);
}
}

public abstract class NumericEnumKeyDictionaryBase<T>
{
public abstract Dictionary<T, int> BuildDictionary(int i);

[Fact]
public void SerilizeDictionaryWhenCacheIsFull()
{
Dictionary<T, int> dictionary;
for (int i = 1; i <= 64; i++)
{
dictionary = BuildDictionary(i);
JsonSerializer.Serialize(dictionary);
}

dictionary = BuildDictionary(0);
string json = JsonSerializer.Serialize(dictionary);
Assert.Equal($"{{\"0\":0}}", json);
}
}

public class Int32EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumInt32>
{
public override Dictionary<SampleEnumInt32, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumInt32, int> { { (SampleEnumInt32)i, i } };
}

public class UInt32EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumUInt32>
{
public override Dictionary<SampleEnumUInt32, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumUInt32, int> { { (SampleEnumUInt32)i, i } };
}

public class UInt64EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumUInt64>
{
public override Dictionary<SampleEnumUInt64, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumUInt64, int> { { (SampleEnumUInt64)i, i } };
}

public class Int64EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumInt64>
{
public override Dictionary<SampleEnumInt64, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumInt64, int> { { (SampleEnumInt64)i, i } };
}

public class Int16EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumInt16>
{
public override Dictionary<SampleEnumInt16, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumInt16, int> { { (SampleEnumInt16)i, i } };
}

public class UInt16EnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumUInt16>
{
public override Dictionary<SampleEnumUInt16, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumUInt16, int> { { (SampleEnumUInt16)i, i } };
}

public class ByteEnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumByte>
{
public override Dictionary<SampleEnumByte, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumByte, int> { { (SampleEnumByte)i, i } };
}

public class SByteEnumDictionary : NumericEnumKeyDictionaryBase<SampleEnumSByte>
{
public override Dictionary<SampleEnumSByte, int> BuildDictionary(int i) =>
new Dictionary<SampleEnumSByte, int> { { (SampleEnumSByte)i, i } };
}


[Flags]
public enum SampleEnumInt32
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumUInt32 : uint
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumUInt64 : ulong
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumInt64 : long
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumInt16 : short
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumUInt16 : ushort
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumByte : byte
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}

[Flags]
public enum SampleEnumSByte : sbyte
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
F = 1 << 5,
G = 1 << 6,
}
}
}

0 comments on commit 0446e4f

Please sign in to comment.