diff --git a/src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs b/src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs index 77eb1d8af32c3..80dfdfc84b3b4 100644 --- a/src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs +++ b/src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Diagnostics; using System.Globalization; +using System.Runtime.CompilerServices; namespace System.Text.Json { @@ -11,8 +13,13 @@ internal abstract class JsonSeparatorNamingPolicy : JsonNamingPolicy private readonly bool _lowercase; private readonly char _separator; - internal JsonSeparatorNamingPolicy(bool lowercase, char separator) => - (_lowercase, _separator) = (lowercase, separator); + internal JsonSeparatorNamingPolicy(bool lowercase, char separator) + { + Debug.Assert(char.IsPunctuation(separator)); + + _lowercase = lowercase; + _separator = separator; + } public sealed override string ConvertName(string name) { @@ -21,149 +28,155 @@ public sealed override string ConvertName(string name) ThrowHelper.ThrowArgumentNullException(nameof(name)); } - // Rented buffer 20% longer that the input. - int rentedBufferLength = (12 * name.Length) / 10; - char[]? rentedBuffer = rentedBufferLength > JsonConstants.StackallocCharThreshold - ? ArrayPool.Shared.Rent(rentedBufferLength) - : null; + return ConvertNameCore(_separator, _lowercase, name.AsSpan()); + } + + private static string ConvertNameCore(char separator, bool lowercase, ReadOnlySpan chars) + { + char[]? rentedBuffer = null; - int resultUsedLength = 0; - Span result = rentedBuffer is null + // While we can't predict the expansion factor of the resultant string, + // start with a buffer that is at least 20% larger than the input. + int initialBufferLength = (int)(1.2 * chars.Length); + Span destination = initialBufferLength <= JsonConstants.StackallocCharThreshold ? stackalloc char[JsonConstants.StackallocCharThreshold] - : rentedBuffer; + : (rentedBuffer = ArrayPool.Shared.Rent(initialBufferLength)); - void ExpandBuffer(ref Span result) + SeparatorState state = SeparatorState.NotStarted; + int charsWritten = 0; + + for (int i = 0; i < chars.Length; i++) { - char[] newBuffer = ArrayPool.Shared.Rent(result.Length * 2); + // NB this implementation does not handle surrogate pair letters + // cf. https://github.com/dotnet/runtime/issues/90352 - result.CopyTo(newBuffer); + char current = chars[i]; + UnicodeCategory category = char.GetUnicodeCategory(current); - if (rentedBuffer is not null) + switch (category) { - result.Slice(0, resultUsedLength).Clear(); - ArrayPool.Shared.Return(rentedBuffer); - } + case UnicodeCategory.UppercaseLetter: - rentedBuffer = newBuffer; - result = rentedBuffer; - } + switch (state) + { + case SeparatorState.NotStarted: + break; + + case SeparatorState.LowercaseLetterOrDigit: + case SeparatorState.SpaceSeparator: + // An uppercase letter following a sequence of lowercase letters or spaces + // denotes the start of a new grouping: emit a separator character. + WriteChar(separator, ref destination); + break; + + case SeparatorState.UppercaseLetter: + // We are reading through a sequence of two or more uppercase letters. + // Uppercase letters are grouped together with the exception of the + // final letter, assuming it is followed by lowercase letters. + // For example, the value 'XMLReader' should render as 'xml_reader', + // however 'SHA512Hash' should render as 'sha512-hash'. + if (i + 1 < chars.Length && char.IsLower(chars[i + 1])) + { + WriteChar(separator, ref destination); + } + break; + + default: + Debug.Fail($"Unexpected state {state}"); + break; + } - void WriteWord(ReadOnlySpan word, ref Span result) - { - if (word.IsEmpty) - { - return; - } + if (lowercase) + { + current = char.ToLowerInvariant(current); + } - int written; - while (true) - { - var destinationOffset = resultUsedLength != 0 - ? resultUsedLength + 1 - : resultUsedLength; + WriteChar(current, ref destination); + state = SeparatorState.UppercaseLetter; + break; - if (destinationOffset < result.Length) - { - Span destination = result.Slice(destinationOffset); + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.DecimalDigitNumber: - written = _lowercase - ? word.ToLowerInvariant(destination) - : word.ToUpperInvariant(destination); + if (state is SeparatorState.SpaceSeparator) + { + // Normalize preceding spaces to one separator. + WriteChar(separator, ref destination); + } - if (written > 0) + if (!lowercase && category is UnicodeCategory.LowercaseLetter) { - break; + current = char.ToUpperInvariant(current); } - } - ExpandBuffer(ref result); - } + WriteChar(current, ref destination); + state = SeparatorState.LowercaseLetterOrDigit; + break; - if (resultUsedLength != 0) - { - result[resultUsedLength] = _separator; - resultUsedLength += 1; - } + case UnicodeCategory.SpaceSeparator: + // Space characters are trimmed from the start and end of the input string + // but are normalized to separator characters if between letters. + if (state != SeparatorState.NotStarted) + { + state = SeparatorState.SpaceSeparator; + } + break; - resultUsedLength += written; + default: + // Non-alphanumeric characters (including the separator character and surrogates) + // are written as-is to the output and reset the separator state. + // E.g. 'ABC???def' maps to 'abc???def' in snake_case. + + WriteChar(current, ref destination); + state = SeparatorState.NotStarted; + break; + } } - int first = 0; - ReadOnlySpan chars = name.AsSpan(); - CharCategory previousCategory = CharCategory.Boundary; + string result = destination.Slice(0, charsWritten).ToString(); - for (int index = 0; index < chars.Length; index++) + if (rentedBuffer is not null) { - char current = chars[index]; - UnicodeCategory currentCategoryUnicode = char.GetUnicodeCategory(current); - - if (currentCategoryUnicode == UnicodeCategory.SpaceSeparator || - currentCategoryUnicode >= UnicodeCategory.ConnectorPunctuation && - currentCategoryUnicode <= UnicodeCategory.OtherPunctuation) - { - WriteWord(chars.Slice(first, index - first), ref result); - - previousCategory = CharCategory.Boundary; - first = index + 1; + destination.Slice(0, charsWritten).Clear(); + ArrayPool.Shared.Return(rentedBuffer); + } - continue; - } + return result; - if (index + 1 < chars.Length) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void WriteChar(char value, ref Span destination) + { + if (charsWritten == destination.Length) { - char next = chars[index + 1]; - CharCategory currentCategory = currentCategoryUnicode switch - { - UnicodeCategory.LowercaseLetter => CharCategory.Lowercase, - UnicodeCategory.UppercaseLetter => CharCategory.Uppercase, - _ => previousCategory - }; - - if (currentCategory == CharCategory.Lowercase && char.IsUpper(next) || - next == '_') - { - WriteWord(chars.Slice(first, index - first + 1), ref result); - - previousCategory = CharCategory.Boundary; - first = index + 1; - - continue; - } - - if (previousCategory == CharCategory.Uppercase && - currentCategoryUnicode == UnicodeCategory.UppercaseLetter && - char.IsLower(next)) - { - WriteWord(chars.Slice(first, index - first), ref result); - - previousCategory = CharCategory.Boundary; - first = index; - - continue; - } - - previousCategory = currentCategory; + ExpandBuffer(ref destination); } + + destination[charsWritten++] = value; } - WriteWord(chars.Slice(first), ref result); + void ExpandBuffer(ref Span destination) + { + int newSize = checked(destination.Length * 2); + char[] newBuffer = ArrayPool.Shared.Rent(newSize); + destination.CopyTo(newBuffer); - name = result.Slice(0, resultUsedLength).ToString(); + if (rentedBuffer is not null) + { + destination.Slice(0, charsWritten).Clear(); + ArrayPool.Shared.Return(rentedBuffer); + } - if (rentedBuffer is not null) - { - result.Slice(0, resultUsedLength).Clear(); - ArrayPool.Shared.Return(rentedBuffer); + rentedBuffer = newBuffer; + destination = rentedBuffer; } - - return name; } - private enum CharCategory + private enum SeparatorState { - Boundary, - Lowercase, - Uppercase, + NotStarted, + UppercaseLetter, + LowercaseLetterOrDigit, + SpaceSeparator, } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs index 6a62c2acdd1f9..3f468a7c614f6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NamingPolicyUnitTests.cs @@ -1,235 +1,369 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Linq; +using System.Reflection; +using Newtonsoft.Json.Serialization; using Xunit; namespace System.Text.Json.Serialization.Tests { public static class NamingPolicyUnitTests { - [Fact] - public static void ToCamelCaseTest() + private readonly static CamelCaseNamingStrategy s_newtonsoftCamelCaseNamingStrategy = new(); + + [Theory] + // These test cases were copied from Json.NET. + [InlineData("urlValue", "URLValue")] + [InlineData("url", "URL")] + [InlineData("id", "ID")] + [InlineData("i", "I")] + [InlineData("", "")] + [InlineData("😀葛🀄", "😀葛🀄")] // Surrogate pairs + [InlineData("άλφαΒήταΓάμμα", "ΆλφαΒήταΓάμμα")] // Non-ascii letters + [InlineData("𐐀𐐨𐐨𐐀𐐨𐐨", "𐐀𐐨𐐨𐐀𐐨𐐨")] // Surrogate pair letters don't normalize + [InlineData("\ude00\ud83d", "\ude00\ud83d")] // Unpaired surrogates + [InlineData("person", "Person")] + [InlineData("iPhone", "iPhone")] + [InlineData("iPhone", "IPhone")] + [InlineData("i Phone", "I Phone")] + [InlineData("i Phone", "I Phone")] + [InlineData(" IPhone", " IPhone")] + [InlineData(" IPhone ", " IPhone ")] + [InlineData("isCIA", "IsCIA")] + [InlineData("vmQ", "VmQ")] + [InlineData("xml2Json", "Xml2Json")] + [InlineData("snAkEcAsE", "SnAkEcAsE")] + [InlineData("snA__kEcAsE", "SnA__kEcAsE")] + [InlineData("snA__ kEcAsE", "SnA__ kEcAsE")] + [InlineData("already_snake_case_ ", "already_snake_case_ ")] + [InlineData("isJSONProperty", "IsJSONProperty")] + [InlineData("shoutinG_CASE", "SHOUTING_CASE")] + [InlineData("9999-12-31T23:59:59.9999999Z", "9999-12-31T23:59:59.9999999Z")] + [InlineData("hi!! This is text. Time to test.", "Hi!! This is text. Time to test.")] + [InlineData("building", "BUILDING")] + [InlineData("building Property", "BUILDING Property")] + [InlineData("building Property", "Building Property")] + [InlineData("building PROPERTY", "BUILDING PROPERTY")] + public static void ToCamelCaseTest(string expectedResult, string name) { - // These test cases were copied from Json.NET. - Assert.Equal("urlValue", Convert("URLValue")); - Assert.Equal("url", Convert("URL")); - Assert.Equal("id", Convert("ID")); - Assert.Equal("i", Convert("I")); - Assert.Equal("", Convert("")); - Assert.Null(Convert(null)); - Assert.Equal("person", Convert("Person")); - Assert.Equal("iPhone", Convert("iPhone")); - Assert.Equal("iPhone", Convert("IPhone")); - Assert.Equal("i Phone", Convert("I Phone")); - Assert.Equal("i Phone", Convert("I Phone")); - Assert.Equal(" IPhone", Convert(" IPhone")); - Assert.Equal(" IPhone ", Convert(" IPhone ")); - Assert.Equal("isCIA", Convert("IsCIA")); - Assert.Equal("vmQ", Convert("VmQ")); - Assert.Equal("xml2Json", Convert("Xml2Json")); - Assert.Equal("snAkEcAsE", Convert("SnAkEcAsE")); - Assert.Equal("snA__kEcAsE", Convert("SnA__kEcAsE")); - Assert.Equal("snA__ kEcAsE", Convert("SnA__ kEcAsE")); - Assert.Equal("already_snake_case_ ", Convert("already_snake_case_ ")); - Assert.Equal("isJSONProperty", Convert("IsJSONProperty")); - Assert.Equal("shoutinG_CASE", Convert("SHOUTING_CASE")); - Assert.Equal("9999-12-31T23:59:59.9999999Z", Convert("9999-12-31T23:59:59.9999999Z")); - Assert.Equal("hi!! This is text. Time to test.", Convert("Hi!! This is text. Time to test.")); - Assert.Equal("building", Convert("BUILDING")); - Assert.Equal("building Property", Convert("BUILDING Property")); - Assert.Equal("building Property", Convert("Building Property")); - Assert.Equal("building PROPERTY", Convert("BUILDING PROPERTY")); - - static string Convert(string name) - { - JsonNamingPolicy policy = JsonNamingPolicy.CamelCase; - string value = policy.ConvertName(name); - return value; - } + JsonNamingPolicy policy = JsonNamingPolicy.CamelCase; + string newtonsoftResult = s_newtonsoftCamelCaseNamingStrategy.GetPropertyName(name, false); + + string value = policy.ConvertName(name); + + Assert.Equal(expectedResult, value); + Assert.Equal(newtonsoftResult, value); } [Fact] - public static void ToSnakeLowerCase() + public static void CamelCaseNullNameReturnsNull() { - Assert.Equal("xml_http_request", Convert("XMLHttpRequest")); - Assert.Equal("camel_case", Convert("camelCase")); - Assert.Equal("camel_case", Convert("CamelCase")); - Assert.Equal("snake_case", Convert("snake_case")); - Assert.Equal("snake_case", Convert("SNAKE_CASE")); - Assert.Equal("kebab_case", Convert("kebab-case")); - Assert.Equal("kebab_case", Convert("KEBAB-CASE")); - Assert.Equal("double_space", Convert("double space")); - Assert.Equal("double_underscore", Convert("double__underscore")); - Assert.Equal("abc", Convert("abc")); - Assert.Equal("ab_c", Convert("abC")); - Assert.Equal("a_bc", Convert("aBc")); - Assert.Equal("a_bc", Convert("aBC")); - Assert.Equal("a_bc", Convert("ABc")); - Assert.Equal("abc", Convert("ABC")); - Assert.Equal("abc123def456", Convert("abc123def456")); - Assert.Equal("abc123_def456", Convert("abc123Def456")); - Assert.Equal("abc123_def456", Convert("abc123DEF456")); - Assert.Equal("abc123def456", Convert("ABC123DEF456")); - Assert.Equal("abc123def456", Convert("ABC123def456")); - Assert.Equal("abc123def456", Convert("Abc123def456")); - Assert.Equal("abc", Convert(" abc")); - Assert.Equal("abc", Convert("abc ")); - Assert.Equal("abc", Convert(" abc ")); - Assert.Equal("abc_def", Convert(" abc def ")); - Assert.Equal( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Convert("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "a_haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Convert("aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "a_towel_it_says_is_about_the_most_massively_useful_thing_an_interstellar_hitchhiker_can_have_partly_it_has_great_practical_value_you_can_wrap_it_around_you_for_warmth_as_you_bound_across_the_cold_moons_of_jaglan_beta_you_can_lie_on_it_on_the_brilliant_marble_sanded_beaches_of_santraginus_v_inhaling_the_heady_sea_vapors_you_can_sleep_under_it_beneath_the_stars_which_shine_so_redly_on_the_desert_world_of_kakrafoon_use_it_to_sail_a_miniraft_down_the_slow_heavy_river_moth_wet_it_for_use_in_hand_to_hand_combat_wrap_it_round_your_head_to_ward_off_noxious_fumes_or_avoid_the_gaze_of_the_ravenous_bugblatter_beast_of_traal_a_mind_bogglingly_stupid_animal_it_assumes_that_if_you_cant_see_it_it_cant_see_you_daft_as_a_brush_but_very_very_ravenous_you_can_wave_your_towel_in_emergencies_as_a_distress_signal_and_of_course_dry_yourself_of_with_it_if_it_still_seems_to_be_clean_enough", - Convert("ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")); - - static string Convert(string name) - { - JsonNamingPolicy policy = JsonNamingPolicy.SnakeCaseLower; - string value = policy.ConvertName(name); - return value; - } + JsonNamingPolicy policy = JsonNamingPolicy.CamelCase; + Assert.Null(policy.ConvertName(null)); } - [Fact] - public static void ToSnakeUpperCase() + [Theory, OuterLoop] + [MemberData(nameof(GetValidMemberNames))] + public static void CamelCaseNamingPolicyMatchesNewtonsoftNamingStrategy(string name) { - Assert.Equal("XML_HTTP_REQUEST", Convert("XMLHttpRequest")); - Assert.Equal("CAMEL_CASE", Convert("camelCase")); - Assert.Equal("CAMEL_CASE", Convert("CamelCase")); - Assert.Equal("SNAKE_CASE", Convert("snake_case")); - Assert.Equal("SNAKE_CASE", Convert("SNAKE_CASE")); - Assert.Equal("KEBAB_CASE", Convert("kebab-case")); - Assert.Equal("KEBAB_CASE", Convert("KEBAB-CASE")); - Assert.Equal("DOUBLE_SPACE", Convert("double space")); - Assert.Equal("DOUBLE_UNDERSCORE", Convert("double__underscore")); - Assert.Equal("ABC", Convert("abc")); - Assert.Equal("AB_C", Convert("abC")); - Assert.Equal("A_BC", Convert("aBc")); - Assert.Equal("A_BC", Convert("aBC")); - Assert.Equal("A_BC", Convert("ABc")); - Assert.Equal("ABC", Convert("ABC")); - Assert.Equal("ABC123DEF456", Convert("abc123def456")); - Assert.Equal("ABC123_DEF456", Convert("abc123Def456")); - Assert.Equal("ABC123_DEF456", Convert("abc123DEF456")); - Assert.Equal("ABC123DEF456", Convert("ABC123DEF456")); - Assert.Equal("ABC123DEF456", Convert("ABC123def456")); - Assert.Equal("ABC123DEF456", Convert("Abc123def456")); - Assert.Equal("ABC", Convert(" ABC")); - Assert.Equal("ABC", Convert("ABC ")); - Assert.Equal("ABC", Convert(" ABC ")); - Assert.Equal("ABC_DEF", Convert(" ABC def ")); - Assert.Equal( - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - Convert("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "A_HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - Convert("aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "A_TOWEL_IT_SAYS_IS_ABOUT_THE_MOST_MASSIVELY_USEFUL_THING_AN_INTERSTELLAR_HITCHHIKER_CAN_HAVE_PARTLY_IT_HAS_GREAT_PRACTICAL_VALUE_YOU_CAN_WRAP_IT_AROUND_YOU_FOR_WARMTH_AS_YOU_BOUND_ACROSS_THE_COLD_MOONS_OF_JAGLAN_BETA_YOU_CAN_LIE_ON_IT_ON_THE_BRILLIANT_MARBLE_SANDED_BEACHES_OF_SANTRAGINUS_V_INHALING_THE_HEADY_SEA_VAPORS_YOU_CAN_SLEEP_UNDER_IT_BENEATH_THE_STARS_WHICH_SHINE_SO_REDLY_ON_THE_DESERT_WORLD_OF_KAKRAFOON_USE_IT_TO_SAIL_A_MINIRAFT_DOWN_THE_SLOW_HEAVY_RIVER_MOTH_WET_IT_FOR_USE_IN_HAND_TO_HAND_COMBAT_WRAP_IT_ROUND_YOUR_HEAD_TO_WARD_OFF_NOXIOUS_FUMES_OR_AVOID_THE_GAZE_OF_THE_RAVENOUS_BUGBLATTER_BEAST_OF_TRAAL_A_MIND_BOGGLINGLY_STUPID_ANIMAL_IT_ASSUMES_THAT_IF_YOU_CANT_SEE_IT_IT_CANT_SEE_YOU_DAFT_AS_A_BRUSH_BUT_VERY_VERY_RAVENOUS_YOU_CAN_WAVE_YOUR_TOWEL_IN_EMERGENCIES_AS_A_DISTRESS_SIGNAL_AND_OF_COURSE_DRY_YOURSELF_OF_WITH_IT_IF_IT_STILL_SEEMS_TO_BE_CLEAN_ENOUGH", - Convert("ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")); - - static string Convert(string name) - { - JsonNamingPolicy policy = JsonNamingPolicy.SnakeCaseUpper; - string value = policy.ConvertName(name); - return value; - } + string newtonsoftResult = s_newtonsoftCamelCaseNamingStrategy.GetPropertyName(name, hasSpecifiedName: false); + string stjResult = JsonNamingPolicy.CamelCase.ConvertName(name); + Assert.Equal(newtonsoftResult, stjResult); } - [Fact] - public static void ToKebabLowerCase() + [Theory] + [InlineData("xml_http_request", "XMLHttpRequest")] + [InlineData("sha512_hash_algorithm", "SHA512HashAlgorithm")] + [InlineData("i18n", "i18n")] + [InlineData("i18n_policy", "I18nPolicy")] + [InlineData("7samurai", "7samurai")] + [InlineData("camel_case", "camelCase")] + [InlineData("camel_case", "CamelCase")] + [InlineData("snake_case", "snake_case")] + [InlineData("snake_case", "SNAKE_CASE")] + [InlineData("kebab-case", "kebab-case")] + [InlineData("kebab-case", "KEBAB-CASE")] + [InlineData("double_space", "double space")] + [InlineData("double__underscore", "double__underscore")] + [InlineData("double--dash", "double--dash")] + [InlineData("abc", "abc")] + [InlineData("ab_c", "abC")] + [InlineData("a_bc", "aBc")] + [InlineData("a_bc", "aBC")] + [InlineData("a_bc", "ABc")] + [InlineData("abc", "ABC")] + [InlineData("abc123def456", "abc123def456")] + [InlineData("abc123_def456", "abc123Def456")] + [InlineData("abc123_def456", "abc123DEF456")] + [InlineData("abc123_def456", "ABC123DEF456")] + [InlineData("abc123def456", "ABC123def456")] + [InlineData("abc123def456", "Abc123def456")] + [InlineData("abc", " abc")] + [InlineData("abc", "abc ")] + [InlineData("abc", " abc ")] + [InlineData("abc", " Abc ")] + [InlineData("7ab7", " 7ab7 ")] + [InlineData("abc_def", " abc def ")] + [InlineData("abc_def", " abc def ")] + [InlineData("abc_def", " abc def ")] + [InlineData("abc_7ef", " abc 7ef ")] + [InlineData("ab7_def", " ab7 def ")] + [InlineData("_abc", "_abc")] + [InlineData("", "")] + [InlineData("😀葛🀄", "😀葛🀄")] // Surrogate pairs + [InlineData("άλφα_βήτα_γάμμα", "ΆλφαΒήταΓάμμα")] // Non-ascii letters + [InlineData("𐐀𐐨𐐨𐐀𐐨𐐨", "𐐀𐐨𐐨𐐀𐐨𐐨")] // Surrogate pair letters don't normalize + [InlineData("𐐀abc_def𐐨abc😀def𐐀", "𐐀AbcDef𐐨Abc😀Def𐐀")] + [InlineData("\ude00\ud83d", "\ude00\ud83d")] // Unpaired surrogates + [InlineData("a%", "a%")] + [InlineData("_?#-", "_?#-")] + [InlineData("?!?", "? ! ?")] + [InlineData("$type", "$type")] + [InlineData("abc%def", "abc%def")] + [InlineData("__abc__def__", "__abc__def__")] + [InlineData("_abc_abc_abc", "_abcAbc_abc")] + [InlineData("abc???def", "ABC???def")] + [InlineData("ab_cd-_-de_f", "ABCd - _ - DE f")] + [InlineData( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "a_haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "a_towel_it_says_is_about_the_most_massively_useful_thing_an_interstellar_hitchhiker_can_have_partly_it_has_great_practical_value_you_can_wrap_it_around_you_for_warmth_as_you_bound_across_the_cold_moons_of_jaglan_beta_you_can_lie_on_it_on_the_brilliant_marble_sanded_beaches_of_santraginus_v_inhaling_the_heady_sea_vapors_you_can_sleep_under_it_beneath_the_stars_which_shine_so_redly_on_the_desert_world_of_kakrafoon_use_it_to_sail_a_miniraft_down_the_slow_heavy_river_moth_wet_it_for_use_in_hand_to_hand_combat_wrap_it_round_your_head_to_ward_off_noxious_fumes_or_avoid_the_gaze_of_the_ravenous_bugblatter_beast_of_traal_a_mind_bogglingly_stupid_animal_it_assumes_that_if_you_cant_see_it_it_cant_see_you_daft_as_a_brush_but_very_very_ravenous_you_can_wave_your_towel_in_emergencies_as_a_distress_signal_and_of_course_dry_yourself_of_with_it_if_it_still_seems_to_be_clean_enough", + "ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")] + public static void ToSnakeLowerCase(string expectedResult, string name) { - Assert.Equal("xml-http-request", Convert("XMLHttpRequest")); - Assert.Equal("camel-case", Convert("camelCase")); - Assert.Equal("camel-case", Convert("CamelCase")); - Assert.Equal("snake-case", Convert("snake_case")); - Assert.Equal("snake-case", Convert("SNAKE_CASE")); - Assert.Equal("kebab-case", Convert("kebab-case")); - Assert.Equal("kebab-case", Convert("KEBAB-CASE")); - Assert.Equal("double-space", Convert("double space")); - Assert.Equal("double-underscore", Convert("double__underscore")); - Assert.Equal("abc", Convert("abc")); - Assert.Equal("ab-c", Convert("abC")); - Assert.Equal("a-bc", Convert("aBc")); - Assert.Equal("a-bc", Convert("aBC")); - Assert.Equal("a-bc", Convert("ABc")); - Assert.Equal("abc", Convert("ABC")); - Assert.Equal("abc123def456", Convert("abc123def456")); - Assert.Equal("abc123-def456", Convert("abc123Def456")); - Assert.Equal("abc123-def456", Convert("abc123DEF456")); - Assert.Equal("abc123def456", Convert("ABC123DEF456")); - Assert.Equal("abc123def456", Convert("ABC123def456")); - Assert.Equal("abc123def456", Convert("Abc123def456")); - Assert.Equal("abc", Convert(" abc")); - Assert.Equal("abc", Convert("abc ")); - Assert.Equal("abc", Convert(" abc ")); - Assert.Equal("abc-def", Convert(" abc def ")); - Assert.Equal( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Convert("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "a-haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Convert("aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "a-towel-it-says-is-about-the-most-massively-useful-thing-an-interstellar-hitchhiker-can-have-partly-it-has-great-practical-value-you-can-wrap-it-around-you-for-warmth-as-you-bound-across-the-cold-moons-of-jaglan-beta-you-can-lie-on-it-on-the-brilliant-marble-sanded-beaches-of-santraginus-v-inhaling-the-heady-sea-vapors-you-can-sleep-under-it-beneath-the-stars-which-shine-so-redly-on-the-desert-world-of-kakrafoon-use-it-to-sail-a-miniraft-down-the-slow-heavy-river-moth-wet-it-for-use-in-hand-to-hand-combat-wrap-it-round-your-head-to-ward-off-noxious-fumes-or-avoid-the-gaze-of-the-ravenous-bugblatter-beast-of-traal-a-mind-bogglingly-stupid-animal-it-assumes-that-if-you-cant-see-it-it-cant-see-you-daft-as-a-brush-but-very-very-ravenous-you-can-wave-your-towel-in-emergencies-as-a-distress-signal-and-of-course-dry-yourself-of-with-it-if-it-still-seems-to-be-clean-enough", - Convert("ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")); - - static string Convert(string name) - { - JsonNamingPolicy policy = JsonNamingPolicy.KebabCaseLower; - string value = policy.ConvertName(name); - return value; - } + JsonNamingPolicy policy = JsonNamingPolicy.SnakeCaseLower; + + string result = policy.ConvertName(name); + + Assert.Equal(expectedResult, result); } - [Fact] - public static void ToKebabUpperCase() + [Theory] + [InlineData("XML_HTTP_REQUEST", "XMLHttpRequest")] + [InlineData("SHA512_HASH_ALGORITHM", "SHA512HashAlgorithm")] + [InlineData("I18N", "i18n")] + [InlineData("I18N_POLICY", "I18nPolicy")] + [InlineData("7SAMURAI", "7samurai")] + [InlineData("CAMEL_CASE", "camelCase")] + [InlineData("CAMEL_CASE", "CamelCase")] + [InlineData("SNAKE_CASE", "snake_case")] + [InlineData("SNAKE_CASE", "SNAKE_CASE")] + [InlineData("KEBAB-CASE", "kebab-case")] + [InlineData("KEBAB-CASE", "KEBAB-CASE")] + [InlineData("DOUBLE_SPACE", "double space")] + [InlineData("DOUBLE__UNDERSCORE", "double__underscore")] + [InlineData("DOUBLE--DASH", "double--dash")] + [InlineData("ABC", "abc")] + [InlineData("AB_C", "abC")] + [InlineData("A_BC", "aBc")] + [InlineData("A_BC", "aBC")] + [InlineData("A_BC", "ABc")] + [InlineData("ABC", "ABC")] + [InlineData("ABC123DEF456", "abc123def456")] + [InlineData("ABC123_DEF456", "abc123Def456")] + [InlineData("ABC123_DEF456", "abc123DEF456")] + [InlineData("ABC123_DEF456", "ABC123DEF456")] + [InlineData("ABC123DEF456", "ABC123def456")] + [InlineData("ABC123DEF456", "Abc123def456")] + [InlineData("ABC", " ABC")] + [InlineData("ABC", "ABC ")] + [InlineData("ABC", " ABC ")] + [InlineData("ABC", " Abc ")] + [InlineData("7AB7", " 7ab7 ")] + [InlineData("ABC_DEF", " ABC def ")] + [InlineData("ABC_DEF", " abc def ")] + [InlineData("ABC_DEF", " abc def ")] + [InlineData("ABC_7EF", " abc 7ef ")] + [InlineData("AB7_DEF", " ab7 def ")] + [InlineData("_ABC", "_abc")] + [InlineData("", "")] + [InlineData("😀葛🀄", "😀葛🀄")] // Surrogate pairs + [InlineData("ΆΛΦΑ_ΒΉΤΑ_ΓΆΜΜΑ", "ΆλφαΒήταΓάμμα")] // Non-ascii letters + [InlineData("𐐀𐐨𐐨𐐀𐐨𐐨", "𐐀𐐨𐐨𐐀𐐨𐐨")] // Surrogate pair letters don't normalize + [InlineData("𐐀ABC_DEF𐐨ABC😀DEF𐐀", "𐐀AbcDef𐐨Abc😀Def𐐀")] + [InlineData("\ude00\ud83d", "\ude00\ud83d")] // Unpaired surrogates + [InlineData("A%", "a%")] + [InlineData("_?#-", "_?#-")] + [InlineData("?!?", "? ! ?")] + [InlineData("$TYPE", "$type")] + [InlineData("ABC%DEF", "abc%def")] + [InlineData("__ABC__DEF__", "__abc__def__")] + [InlineData("_ABC_ABC_ABC", "_abcAbc_abc")] + [InlineData("ABC???DEF", "ABC???def")] + [InlineData("AB_CD-_-DE_F", "ABCd - _ - DE f")] + [InlineData( + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "A_HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "A_TOWEL_IT_SAYS_IS_ABOUT_THE_MOST_MASSIVELY_USEFUL_THING_AN_INTERSTELLAR_HITCHHIKER_CAN_HAVE_PARTLY_IT_HAS_GREAT_PRACTICAL_VALUE_YOU_CAN_WRAP_IT_AROUND_YOU_FOR_WARMTH_AS_YOU_BOUND_ACROSS_THE_COLD_MOONS_OF_JAGLAN_BETA_YOU_CAN_LIE_ON_IT_ON_THE_BRILLIANT_MARBLE_SANDED_BEACHES_OF_SANTRAGINUS_V_INHALING_THE_HEADY_SEA_VAPORS_YOU_CAN_SLEEP_UNDER_IT_BENEATH_THE_STARS_WHICH_SHINE_SO_REDLY_ON_THE_DESERT_WORLD_OF_KAKRAFOON_USE_IT_TO_SAIL_A_MINIRAFT_DOWN_THE_SLOW_HEAVY_RIVER_MOTH_WET_IT_FOR_USE_IN_HAND_TO_HAND_COMBAT_WRAP_IT_ROUND_YOUR_HEAD_TO_WARD_OFF_NOXIOUS_FUMES_OR_AVOID_THE_GAZE_OF_THE_RAVENOUS_BUGBLATTER_BEAST_OF_TRAAL_A_MIND_BOGGLINGLY_STUPID_ANIMAL_IT_ASSUMES_THAT_IF_YOU_CANT_SEE_IT_IT_CANT_SEE_YOU_DAFT_AS_A_BRUSH_BUT_VERY_VERY_RAVENOUS_YOU_CAN_WAVE_YOUR_TOWEL_IN_EMERGENCIES_AS_A_DISTRESS_SIGNAL_AND_OF_COURSE_DRY_YOURSELF_OF_WITH_IT_IF_IT_STILL_SEEMS_TO_BE_CLEAN_ENOUGH", + "ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")] + public static void ToSnakeUpperCase(string expectedResult, string name) + { + JsonNamingPolicy policy = JsonNamingPolicy.SnakeCaseUpper; + + string value = policy.ConvertName(name); + + Assert.Equal(expectedResult, value); + } + + [Theory] + [InlineData("xml-http-request", "XMLHttpRequest")] + [InlineData("sha512-hash-algorithm", "SHA512HashAlgorithm")] + [InlineData("i18n", "i18n")] + [InlineData("i18n-policy", "I18nPolicy")] + [InlineData("7samurai", "7samurai")] + [InlineData("camel-case", "camelCase")] + [InlineData("camel-case", "CamelCase")] + [InlineData("snake_case", "snake_case")] + [InlineData("snake_case", "SNAKE_CASE")] + [InlineData("kebab-case", "kebab-case")] + [InlineData("kebab-case", "KEBAB-CASE")] + [InlineData("double-space", "double space")] + [InlineData("double__underscore", "double__underscore")] + [InlineData("double--dash", "double--dash")] + [InlineData("abc", "abc")] + [InlineData("ab-c", "abC")] + [InlineData("a-bc", "aBc")] + [InlineData("a-bc", "aBC")] + [InlineData("a-bc", "ABc")] + [InlineData("abc", "ABC")] + [InlineData("abc123def456", "abc123def456")] + [InlineData("abc123-def456", "abc123Def456")] + [InlineData("abc123-def456", "abc123DEF456")] + [InlineData("abc123-def456", "ABC123DEF456")] + [InlineData("abc123def456", "ABC123def456")] + [InlineData("abc123def456", "Abc123def456")] + [InlineData("abc", " abc")] + [InlineData("abc", "abc ")] + [InlineData("abc", " abc ")] + [InlineData("abc", " Abc ")] + [InlineData("7ab7", " 7ab7 ")] + [InlineData("abc-def", " abc def ")] + [InlineData("abc-def", " abc def ")] + [InlineData("abc-def", " abc def ")] + [InlineData("abc-7ef", " abc 7ef ")] + [InlineData("ab7-def", " ab7 def ")] + [InlineData("-abc", "-abc")] + [InlineData("", "")] + [InlineData("😀葛🀄", "😀葛🀄")] // Surrogate pairs + [InlineData("άλφα-βήτα-γάμμα", "ΆλφαΒήταΓάμμα")] // Non-ascii letters + [InlineData("𐐀𐐨𐐨𐐀𐐨𐐨", "𐐀𐐨𐐨𐐀𐐨𐐨")] // Surrogate pair letters don't normalize + [InlineData("𐐀abc-def𐐨abc😀def𐐀", "𐐀AbcDef𐐨Abc😀Def𐐀")] + [InlineData("\ude00\ud83d", "\ude00\ud83d")] // Unpaired surrogates + [InlineData("a%", "a%")] + [InlineData("-?#_", "-?#_")] + [InlineData("?!?", "? ! ?")] + [InlineData("$type", "$type")] + [InlineData("abc%def", "abc%def")] + [InlineData("--abc--def--", "--abc--def--")] + [InlineData("-abc-abc-abc", "-abcAbc-abc")] + [InlineData("abc???def", "ABC???def")] + [InlineData("ab-cd-_-de-f", "ABCd - _ - DE f")] + [InlineData( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "a-haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "a-towel-it-says-is-about-the-most-massively-useful-thing-an-interstellar-hitchhiker-can-have_partly-it-has-great-practical-value_you-can-wrap-it-around-you-for-warmth-as-you-bound-across-the-cold-moons-of-jaglan-beta_you-can-lie-on-it-on-the-brilliant-marble-sanded-beaches-of-santraginus-v-inhaling-the-heady-sea-vapors_you-can-sleep-under-it-beneath-the-stars-which-shine-so-redly-on-the-desert-world-of-kakrafoon_use-it-to-sail-a-miniraft-down-the-slow-heavy-river-moth_wet-it-for-use-in-hand-to-hand-combat_wrap-it-round-your-head-to-ward-off-noxious-fumes-or-avoid-the-gaze-of-the-ravenous-bugblatter-beast-of-traal-a-mind-bogglingly-stupid-animal_it-assumes-that-if-you-cant-see-it-it-cant-see-you-daft-as-a-brush-but-very-very-ravenous_you-can-wave-your-towel-in-emergencies-as-a-distress-signal-and-of-course-dry-yourself-of-with-it-if-it-still-seems-to-be-clean-enough", + "ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")] + public static void ToKebabLowerCase(string expectedResult, string name) { - Assert.Equal("XML-HTTP-REQUEST", Convert("XMLHttpRequest")); - Assert.Equal("CAMEL-CASE", Convert("camelCase")); - Assert.Equal("CAMEL-CASE", Convert("CamelCase")); - Assert.Equal("SNAKE-CASE", Convert("snake_case")); - Assert.Equal("SNAKE-CASE", Convert("SNAKE_CASE")); - Assert.Equal("KEBAB-CASE", Convert("kebab-case")); - Assert.Equal("KEBAB-CASE", Convert("KEBAB-CASE")); - Assert.Equal("DOUBLE-SPACE", Convert("double space")); - Assert.Equal("DOUBLE-UNDERSCORE", Convert("double__underscore")); - Assert.Equal("ABC", Convert("abc")); - Assert.Equal("AB-C", Convert("abC")); - Assert.Equal("A-BC", Convert("aBc")); - Assert.Equal("A-BC", Convert("aBC")); - Assert.Equal("A-BC", Convert("ABc")); - Assert.Equal("ABC", Convert("ABC")); - Assert.Equal("ABC123DEF456", Convert("abc123def456")); - Assert.Equal("ABC123-DEF456", Convert("abc123Def456")); - Assert.Equal("ABC123-DEF456", Convert("abc123DEF456")); - Assert.Equal("ABC123DEF456", Convert("ABC123DEF456")); - Assert.Equal("ABC123DEF456", Convert("ABC123def456")); - Assert.Equal("ABC123DEF456", Convert("Abc123def456")); - Assert.Equal("ABC", Convert(" ABC")); - Assert.Equal("ABC", Convert("ABC ")); - Assert.Equal("ABC", Convert(" ABC ")); - Assert.Equal("ABC-DEF", Convert(" ABC def ")); - Assert.Equal( - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - Convert("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "A-HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - Convert("aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - Assert.Equal( - "A-TOWEL-IT-SAYS-IS-ABOUT-THE-MOST-MASSIVELY-USEFUL-THING-AN-INTERSTELLAR-HITCHHIKER-CAN-HAVE-PARTLY-IT-HAS-GREAT-PRACTICAL-VALUE-YOU-CAN-WRAP-IT-AROUND-YOU-FOR-WARMTH-AS-YOU-BOUND-ACROSS-THE-COLD-MOONS-OF-JAGLAN-BETA-YOU-CAN-LIE-ON-IT-ON-THE-BRILLIANT-MARBLE-SANDED-BEACHES-OF-SANTRAGINUS-V-INHALING-THE-HEADY-SEA-VAPORS-YOU-CAN-SLEEP-UNDER-IT-BENEATH-THE-STARS-WHICH-SHINE-SO-REDLY-ON-THE-DESERT-WORLD-OF-KAKRAFOON-USE-IT-TO-SAIL-A-MINIRAFT-DOWN-THE-SLOW-HEAVY-RIVER-MOTH-WET-IT-FOR-USE-IN-HAND-TO-HAND-COMBAT-WRAP-IT-ROUND-YOUR-HEAD-TO-WARD-OFF-NOXIOUS-FUMES-OR-AVOID-THE-GAZE-OF-THE-RAVENOUS-BUGBLATTER-BEAST-OF-TRAAL-A-MIND-BOGGLINGLY-STUPID-ANIMAL-IT-ASSUMES-THAT-IF-YOU-CANT-SEE-IT-IT-CANT-SEE-YOU-DAFT-AS-A-BRUSH-BUT-VERY-VERY-RAVENOUS-YOU-CAN-WAVE-YOUR-TOWEL-IN-EMERGENCIES-AS-A-DISTRESS-SIGNAL-AND-OF-COURSE-DRY-YOURSELF-OF-WITH-IT-IF-IT-STILL-SEEMS-TO-BE-CLEAN-ENOUGH", - Convert("ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")); - - static string Convert(string name) - { - JsonNamingPolicy policy = JsonNamingPolicy.KebabCaseUpper; - string value = policy.ConvertName(name); - return value; - } + JsonNamingPolicy policy = JsonNamingPolicy.KebabCaseLower; + + string value = policy.ConvertName(name); + + Assert.Equal(expectedResult, value); } + + [Theory] + [InlineData("XML-HTTP-REQUEST", "XMLHttpRequest")] + [InlineData("SHA512-HASH-ALGORITHM", "SHA512HashAlgorithm")] + [InlineData("I18N", "i18n")] + [InlineData("I18N-POLICY", "I18nPolicy")] + [InlineData("7SAMURAI", "7samurai")] + [InlineData("CAMEL-CASE", "camelCase")] + [InlineData("CAMEL-CASE", "CamelCase")] + [InlineData("SNAKE_CASE", "snake_case")] + [InlineData("SNAKE_CASE", "SNAKE_CASE")] + [InlineData("KEBAB-CASE", "kebab-case")] + [InlineData("KEBAB-CASE", "KEBAB-CASE")] + [InlineData("DOUBLE-SPACE", "double space")] + [InlineData("DOUBLE__UNDERSCORE", "double__underscore")] + [InlineData("DOUBLE--DASH", "double--dash")] + [InlineData("ABC", "abc")] + [InlineData("AB-C", "abC")] + [InlineData("A-BC", "aBc")] + [InlineData("A-BC", "aBC")] + [InlineData("A-BC", "ABc")] + [InlineData("ABC", "ABC")] + [InlineData("ABC123DEF456", "abc123def456")] + [InlineData("ABC123-DEF456", "abc123Def456")] + [InlineData("ABC123-DEF456", "abc123DEF456")] + [InlineData("ABC123-DEF456", "ABC123DEF456")] + [InlineData("ABC123DEF456", "ABC123def456")] + [InlineData("ABC123DEF456", "Abc123def456")] + [InlineData("ABC", " ABC")] + [InlineData("ABC", "ABC ")] + [InlineData("ABC", " ABC ")] + [InlineData("ABC", " Abc ")] + [InlineData("7AB7", " 7ab7 ")] + [InlineData("ABC-DEF", " ABC def ")] + [InlineData("ABC-DEF", " abc def ")] + [InlineData("ABC-DEF", " abc def ")] + [InlineData("ABC-7EF", " abc 7ef ")] + [InlineData("AB7-DEF", " ab7 def ")] + [InlineData("-ABC", "-abc")] + [InlineData("", "")] + [InlineData("😀葛🀄", "😀葛🀄")] // Surrogate pairs + [InlineData("ΆΛΦΑ-ΒΉΤΑ-ΓΆΜΜΑ", "ΆλφαΒήταΓάμμα")] // Non-ascii letters + [InlineData("𐐀𐐨𐐨𐐀𐐨𐐨", "𐐀𐐨𐐨𐐀𐐨𐐨")] // Surrogate pair letters don't normalize + [InlineData("𐐀ABC-DEF𐐨ABC😀DEF𐐀", "𐐀AbcDef𐐨Abc😀Def𐐀")] + [InlineData("\ude00\ud83d", "\ude00\ud83d")] // Unpaired surrogates + [InlineData("A%", "a%")] + [InlineData("-?#_", "-?#_")] + [InlineData("?!?", "? ! ?")] + [InlineData("$TYPE", "$type")] + [InlineData("ABC%DEF", "abc%def")] + [InlineData("--ABC--DEF--", "--abc--def--")] + [InlineData("-ABC-ABC-ABC", "-abcAbc-abc")] + [InlineData("ABC???DEF", "ABC???def")] + [InlineData("AB-CD-_-DE-F", "ABCd - _ - DE f")] + [InlineData( + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "A-HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [InlineData( + "A-TOWEL-IT-SAYS-IS-ABOUT-THE-MOST-MASSIVELY-USEFUL-THING-AN-INTERSTELLAR-HITCHHIKER-CAN-HAVE_PARTLY-IT-HAS-GREAT-PRACTICAL-VALUE_YOU-CAN-WRAP-IT-AROUND-YOU-FOR-WARMTH-AS-YOU-BOUND-ACROSS-THE-COLD-MOONS-OF-JAGLAN-BETA_YOU-CAN-LIE-ON-IT-ON-THE-BRILLIANT-MARBLE-SANDED-BEACHES-OF-SANTRAGINUS-V-INHALING-THE-HEADY-SEA-VAPORS_YOU-CAN-SLEEP-UNDER-IT-BENEATH-THE-STARS-WHICH-SHINE-SO-REDLY-ON-THE-DESERT-WORLD-OF-KAKRAFOON_USE-IT-TO-SAIL-A-MINIRAFT-DOWN-THE-SLOW-HEAVY-RIVER-MOTH_WET-IT-FOR-USE-IN-HAND-TO-HAND-COMBAT_WRAP-IT-ROUND-YOUR-HEAD-TO-WARD-OFF-NOXIOUS-FUMES-OR-AVOID-THE-GAZE-OF-THE-RAVENOUS-BUGBLATTER-BEAST-OF-TRAAL-A-MIND-BOGGLINGLY-STUPID-ANIMAL_IT-ASSUMES-THAT-IF-YOU-CANT-SEE-IT-IT-CANT-SEE-YOU-DAFT-AS-A-BRUSH-BUT-VERY-VERY-RAVENOUS_YOU-CAN-WAVE-YOUR-TOWEL-IN-EMERGENCIES-AS-A-DISTRESS-SIGNAL-AND-OF-COURSE-DRY-YOURSELF-OF-WITH-IT-IF-IT-STILL-SEEMS-TO-BE-CLEAN-ENOUGH", + "ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough")] + public static void ToKebabUpperCase(string expectedResult, string name) + { + JsonNamingPolicy policy = JsonNamingPolicy.KebabCaseUpper; + + string value = policy.ConvertName(name); + + Assert.Equal(expectedResult, value); + } + + public static IEnumerable GetValidMemberNames() + => typeof(PropertyNameTestsDynamic).Assembly.GetTypes() + .SelectMany(t => t.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + .Where(m => m.MemberType is MemberTypes.Property or MemberTypes.Field) + .Select(m => m.Name) + .Distinct() + .Select(name => new object[] { name }) + .ToArray(); } }