Skip to content

Commit 4ee4fec

Browse files
committed
Add non-ASCII XML streaming tests for SqlDataReader.GetChars
Add tests that verify SqlDataReader.GetChars correctly handles non-ASCII characters when reading XML columns with SequentialAccess: - GetChars_NonAsciiContent: Latin accented characters (2-byte UTF-8) - GetChars_NonAsciiContent_BulkRead: Bulk read path with accented chars - GetChars_CjkContent: CJK characters (3-byte UTF-8) These tests establish a baseline for correct behavior on main before PR #3974 (issue #1877) refactors SqlStreamingXml internals.
1 parent 3303d80 commit 4ee4fec

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<Compile Include="SQL\DataReaderTest\DataReaderStreamsTest.cs" />
8686
<Compile Include="SQL\DataReaderTest\DataReaderTest.cs" />
8787
<Compile Include="SQL\DataStreamTest\DataStreamTest.cs" />
88+
<Compile Include="SQL\SqlStreamingXmlTest\SqlStreamingXmlTest.cs" />
8889
<Compile Include="SQL\DateTimeTest\DateTimeTest.cs" />
8990
<Compile Include="SQL\DNSCachingTest\DNSCachingTest.cs" />
9091
<Compile Include="SQL\ExceptionTest\ConnectionExceptionTest.cs" />
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Data;
6+
using Xunit;
7+
8+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
9+
{
10+
public static class SqlStreamingXmlTest
11+
{
12+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
13+
public static void GetChars_NonAsciiContent()
14+
{
15+
SqlConnection connection = new(DataTestUtility.TCPConnectionString);
16+
// XML containing non-ASCII characters:
17+
// - \u00E9 (e-acute) - 2 bytes in UTF-8
18+
// - \u00F1 (n-tilde) - 2 bytes in UTF-8
19+
// - \u00FC (u-umlaut) - 2 bytes in UTF-8
20+
string xml = "<r>caf\u00E9 se\u00F1or \u00FCber</r>";
21+
int expectedLength = xml.Length;
22+
string commandText = $"SELECT Convert(xml, N'{xml}')";
23+
24+
using (SqlCommand command = connection.CreateCommand())
25+
{
26+
connection.Open();
27+
command.CommandText = commandText;
28+
29+
SqlDataReader sqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
30+
Assert.True(sqlDataReader.Read(), "Expected to read a row");
31+
32+
(long length, string result) = ReadAllChars(sqlDataReader, expectedLength + 10);
33+
34+
Assert.Equal(expectedLength, length);
35+
Assert.Equal(xml, result.Substring(0, (int)length));
36+
connection.Close();
37+
}
38+
}
39+
40+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
41+
public static void GetChars_NonAsciiContent_BulkRead()
42+
{
43+
SqlConnection connection = new(DataTestUtility.TCPConnectionString);
44+
// Same non-ASCII XML but read in a single bulk GetChars call
45+
string xml = "<name>Jos\u00E9 Garc\u00EDa</name>";
46+
int expectedLength = xml.Length;
47+
string commandText = $"SELECT Convert(xml, N'{xml}')";
48+
49+
using (SqlCommand command = connection.CreateCommand())
50+
{
51+
connection.Open();
52+
command.CommandText = commandText;
53+
54+
SqlDataReader sqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
55+
Assert.True(sqlDataReader.Read(), "Expected to read a row");
56+
57+
char[] buffer = new char[expectedLength + 10];
58+
long charsRead = sqlDataReader.GetChars(0, 0, buffer, 0, buffer.Length);
59+
60+
Assert.Equal(expectedLength, charsRead);
61+
string result = new string(buffer, 0, (int)charsRead);
62+
Assert.Equal(xml, result);
63+
connection.Close();
64+
}
65+
}
66+
67+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
68+
public static void GetChars_CjkContent()
69+
{
70+
SqlConnection connection = new(DataTestUtility.TCPConnectionString);
71+
// CJK characters: 3 bytes each in UTF-8
72+
string xml = "<data>\u65E5\u672C\u8A9E\u30C6\u30B9\u30C8</data>";
73+
int expectedLength = xml.Length;
74+
string commandText = $"SELECT Convert(xml, N'{xml}')";
75+
76+
using (SqlCommand command = connection.CreateCommand())
77+
{
78+
connection.Open();
79+
command.CommandText = commandText;
80+
81+
SqlDataReader sqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess);
82+
Assert.True(sqlDataReader.Read(), "Expected to read a row");
83+
84+
(long length, string result) = ReadAllChars(sqlDataReader, expectedLength + 10);
85+
86+
Assert.Equal(expectedLength, length);
87+
Assert.Equal(xml, result.Substring(0, (int)length));
88+
connection.Close();
89+
}
90+
}
91+
92+
/// <summary>
93+
/// Read all chars one at a time using GetChars with SequentialAccess,
94+
/// replicating the pattern from issue #1877.
95+
/// </summary>
96+
private static (long, string) ReadAllChars(SqlDataReader sqlDataReader, int expectedSize)
97+
{
98+
char[] text = new char[expectedSize];
99+
char[] buffer = new char[1];
100+
101+
long position = 0;
102+
long numCharsRead;
103+
do
104+
{
105+
numCharsRead = sqlDataReader.GetChars(0, position, buffer, 0, 1);
106+
if (numCharsRead > 0)
107+
{
108+
text[position] = buffer[0];
109+
position += numCharsRead;
110+
}
111+
}
112+
while (numCharsRead > 0);
113+
114+
return (position, new string(text));
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)