Skip to content

Commit 06c96c2

Browse files
nohwndYoussef1313
andauthored
Fix serializing special characters in Jsonite (#5124)
Co-authored-by: Youssef Victor <youssefvictor00@gmail.com>
1 parent e305db5 commit 06c96c2

File tree

2 files changed

+59
-4
lines changed
  • src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Jsonite
  • test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode

2 files changed

+59
-4
lines changed

src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Jsonite/Jsonite.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,9 +973,10 @@ private void WriteString(string text)
973973
writer.Write('t');
974974
break;
975975
default:
976-
ArgumentGuard.Ensure(c >= ' ', nameof(text), $"Invalid control character '{EscapeChar(c)}' found in string");
976+
// Intentionally diverging from the upstream code from xoofx/jsonite. See https://github.com/microsoft/testfx/issues/5120
977977

978-
if (IsHighSurrogate(c) || IsLowSurrogate(c))
978+
// Also, https://datatracker.ietf.org/doc/html/rfc4627#section-2.5
979+
if (c < ' ' || IsHighSurrogate(c) || IsLowSurrogate(c))
979980
{
980981
writer.Write('\\');
981982
writer.Write('u');
Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
#if !NETCOREAPP
5+
46
namespace Microsoft.Testing.Platform.UnitTests;
57

68
[TestClass]
@@ -9,11 +11,63 @@ public sealed class JsoniteTests
911
[TestMethod]
1012
public void Serialize_DateTimeOffset()
1113
{
12-
#if !NETCOREAPP
1314
string actual = Jsonite.Json.Serialize(new DateTimeOffset(2023, 01, 01, 01, 01, 01, 01, TimeSpan.Zero));
1415

1516
// Assert
1617
Assert.AreEqual("2023-01-01T01:01:01.0010000+00:00", actual.Trim('"'));
17-
#endif
18+
}
19+
20+
[TestMethod]
21+
public void Serialize_SpecialCharacters()
22+
{
23+
// This test is testing if we can serialize the range 0x0000 - 0x001FF correctly, this range contains special characters like NUL.
24+
// This is a fix for Jsonite, which throws when such characters are found in a string (but does not fail when we provide them as character).
25+
List<Exception> errors = new();
26+
27+
// This could be converted to Data source, but this way we have more control about where in the result message the
28+
// special characters will be (hopefully nowhere) so in case of failure, we can still serialize the message to IDE
29+
// even if the serializer does not support special characters.
30+
foreach (char character in Enumerable.Range(0x0000, 0x001F).Select(v => (char)v))
31+
{
32+
// Convert the char to string, otherwise there is no failure.
33+
string text = $"{character}";
34+
35+
// Serialize text via Jsonite, this is where the error used to happen.
36+
string jsoniteText = Jsonite.Json.Serialize(text);
37+
38+
// Serialize text via System.Text.Json to get our control examples, preserving special characters in text is
39+
// hard, and so we better test against a known, hopefully good state.
40+
string stjText = System.Text.Json.JsonSerializer.Serialize(text);
41+
42+
// This is our expected result, to do it the way System.Text.Json does it.
43+
string? deserializeStjViaStj = System.Text.Json.JsonSerializer.Deserialize<string>(stjText);
44+
45+
// Make sure we can deserialize the messages we send from server to VS.
46+
string? deserializeJsoniteViaStj = System.Text.Json.JsonSerializer.Deserialize<string>(jsoniteText);
47+
48+
// Make sure we can deserialie messages that VS sends to our server.
49+
string? deserializeStjViaJsonite = (string)Jsonite.Json.Deserialize(stjText);
50+
51+
// Make sure we can deserialize messages we produced, to know the Jsonite code is handling all the cases.
52+
string? deserializeJsoniteViaJsonite = (string)Jsonite.Json.Deserialize(jsoniteText);
53+
54+
try
55+
{
56+
Assert.AreEqual(deserializeStjViaStj, deserializeJsoniteViaStj);
57+
Assert.AreEqual(deserializeStjViaStj, deserializeStjViaJsonite);
58+
Assert.AreEqual(deserializeStjViaStj, deserializeJsoniteViaJsonite);
59+
}
60+
catch (Exception ex)
61+
{
62+
errors.Add(ex);
63+
}
64+
}
65+
66+
if (errors.Count > 0)
67+
{
68+
throw new Exception(string.Join(Environment.NewLine, errors));
69+
}
1870
}
1971
}
72+
73+
#endif

0 commit comments

Comments
 (0)