Skip to content

Commit 6233bec

Browse files
authored
[Test Only] Fix Idn tests (#115030)
* Fix Idn tests [Test Only] Port the Idna test fixes to support Unicode 15 * Minor fix * Fine tune the idn to Unicode versions * Add missing files
1 parent dbc3718 commit 6233bec

File tree

12 files changed

+13134
-7
lines changed

12 files changed

+13134
-7
lines changed

src/libraries/System.Globalization.Extensions/tests/IdnMapping/Data/ConformanceIdnaTestResult.cs

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,30 @@ public class ConformanceIdnaTestResult
2727
/// </summary>
2828
public string Value { get; private set; }
2929

30+
public string? Source { get; private set; }
31+
3032
public IdnaTestResultType ResultType { get; private set; }
3133

3234
public string StatusValue { get; private set; }
3335

3436
public ConformanceIdnaTestResult(string entry, string fallbackValue, IdnaTestResultType resultType = IdnaTestResultType.ToAscii)
35-
: this(entry, fallbackValue, null, null, useValueForStatus: true, resultType)
37+
: this(entry, fallbackValue, null, null, useValueForStatus: true, resultType, null)
3638
{
3739
}
3840

3941
public ConformanceIdnaTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, IdnaTestResultType resultType = IdnaTestResultType.ToAscii)
40-
: this(entry, fallbackValue, statusValue, statusFallbackValue, useValueForStatus: false, resultType)
42+
: this(entry, fallbackValue, statusValue, statusFallbackValue, useValueForStatus: false, resultType, null)
43+
{
44+
}
45+
46+
public ConformanceIdnaTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, string? source, IdnaTestResultType resultType = IdnaTestResultType.ToAscii)
47+
: this(entry, fallbackValue, statusValue, statusFallbackValue, useValueForStatus: false, resultType, source)
4148
{
4249
}
4350

44-
private ConformanceIdnaTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, bool useValueForStatus, IdnaTestResultType resultType)
51+
private ConformanceIdnaTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, bool useValueForStatus, IdnaTestResultType resultType, string? source)
4552
{
53+
Source = source;
4654
ResultType = resultType;
4755
SetValue(string.IsNullOrEmpty(entry.Trim()) ? fallbackValue : entry);
4856
SetSuccess(useValueForStatus ?
@@ -81,11 +89,120 @@ private void SetSuccess(string statusValue)
8189
}
8290
}
8391

92+
// Fullwidth Full Stop, Ideographic Full Stop, and Halfwidth Ideographic Full Stop
93+
private static char[] AllDots = ['.', '\uFF0E', '\u3002', '\uFF61'];
94+
95+
private const char SoftHyphen = '\u00AD';
96+
97+
private bool IsIgnorableA4_2Rule()
98+
{
99+
if (Source is null)
100+
{
101+
return false;
102+
}
103+
104+
// Check the label lengths for the ASCII
105+
int lastIndex = 0;
106+
int index = Value.IndexOfAny(AllDots);
107+
while (index >= 0)
108+
{
109+
if (index - lastIndex > 63) // 63 max label length
110+
{
111+
return false;
112+
}
113+
114+
lastIndex = index + 1;
115+
index = Value.IndexOfAny(AllDots, lastIndex);
116+
}
117+
118+
if (Value.Length - lastIndex > 63)
119+
{
120+
return false;
121+
}
122+
123+
// Remove Hyphen as it is ignored
124+
if (Source.IndexOf(SoftHyphen) >= 0)
125+
{
126+
Span<char> span = stackalloc char[Source.Length];
127+
int spanIndex = 0;
128+
129+
for (int i = 0; i < Source.Length; i++)
130+
{
131+
if (Source[i] != SoftHyphen)
132+
{
133+
span[spanIndex++] = Source[i];
134+
}
135+
}
136+
137+
Source = span.Slice(0, spanIndex).ToString();
138+
}
139+
140+
// Check the label lengths for the Source
141+
lastIndex = 0;
142+
index = Source.IndexOfAny(AllDots);
143+
while (index >= 0)
144+
{
145+
if (index - lastIndex > 63) // 63 max label length
146+
{
147+
return false;
148+
}
149+
150+
lastIndex = index + 1;
151+
index = Source.IndexOfAny(AllDots, lastIndex);
152+
}
153+
154+
if (Source.Length - lastIndex > 63)
155+
{
156+
return false;
157+
}
158+
159+
if (Source[0] is '.') // Leading dot
160+
{
161+
return false;
162+
}
163+
164+
for (int i = 0; i < Source.Length - 1; i++)
165+
{
166+
// Consequence dots
167+
if ((Source[i] is '.' or '\uFF0E' or '\u3002' or '\uFF61') && (Source[i + 1] is '.' or '\uFF0E' or '\u3002' or '\uFF61'))
168+
{
169+
return false;
170+
}
171+
172+
// Check Historical Ranges
173+
if (Source[i] >= 0x2C00 && Source[i] <= 0x2C5F) // Glagolitic (U+2C00–U+2C5F)
174+
return false;
175+
176+
switch (Source[i])
177+
{
178+
case '\uD800':
179+
if (Source[i + 1] >= 0xDFA0 && Source[i + 1] <= 0xDFDF) return false; // Old Persian (U+103A0–U+103DF)
180+
if (Source[i + 1] >= 0xDF30 && Source[i + 1] <= 0xDF4F) return false; // Gothic (U+10330–U+1034F)
181+
if (Source[i + 1] >= 0xDC00 && Source[i + 1] <= 0xDC7F) return false; // Linear B (U+10000–U+1007F)
182+
break;
183+
case '\uD802':
184+
if (Source[i + 1] >= 0xDD00 && Source[i + 1] <= 0xDD1F) return false; // Phoenician (U+10900–U+1091F)
185+
break;
186+
case '\uD803':
187+
if (Source[i + 1] >= 0xDEA0 && Source[i + 1] <= 0xDEAF) return false; // Elymaic (U+10EA0–U+10EAF)
188+
break;
189+
case '\uD808':
190+
if (Source[i + 1] >= 0xDC00 && Source[i + 1] <= 0xDFFF) return false; // Cuneiform (U+12000–U+123FF)
191+
break;
192+
case '\uD838':
193+
if (Source[i + 1] >= 0xDC00 && Source[i + 1] <= 0xDCDF) return false; // Indic Siyaq Numbers (U+1E800–U+1E8DF)
194+
break;
195+
}
196+
}
197+
198+
return true;
199+
}
200+
84201
private bool IsIgnoredError(string statusCode)
85202
{
86203
// We don't validate for BIDI rule so we can ignore BIDI codes
87204
// If we're validating ToAscii we ignore rule V2 (UIDNA_ERROR_HYPHEN_3_4) for compatibility with windows.
88-
return statusCode.StartsWith('B') || (ResultType == IdnaTestResultType.ToAscii && statusCode == "V2");
205+
return statusCode.StartsWith('B') || (ResultType == IdnaTestResultType.ToAscii && statusCode == "V2") || (statusCode.StartsWith("A4_2") && IsIgnorableA4_2Rule());
89206
}
90207
}
91208
}

src/libraries/System.Globalization.Extensions/tests/IdnMapping/Data/ConformanceIdnaUnicodeTestResult.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ public ConformanceIdnaUnicodeTestResult(string entry, string fallbackValue, bool
1313
ValidDomainName = validDomainName;
1414
}
1515

16+
public ConformanceIdnaUnicodeTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, string? source, bool validDomainName = true)
17+
: base(entry, fallbackValue, statusValue, statusFallbackValue, source, IdnaTestResultType.ToUnicode)
18+
{
19+
ValidDomainName = validDomainName;
20+
}
21+
1622
public ConformanceIdnaUnicodeTestResult(string entry, string fallbackValue, string statusValue, string statusFallbackValue, bool validDomainName = true)
1723
: base(entry, fallbackValue, statusValue, statusFallbackValue, IdnaTestResultType.ToUnicode)
1824
{

src/libraries/System.Globalization.Extensions/tests/IdnMapping/Data/Factory.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ private static string RemoveComment(string line)
2626
private static Stream GetIdnaTestTxt()
2727
{
2828
string fileName = null;
29-
if (PlatformDetection.ICUVersion >= new Version(74, 0))
29+
if (PlatformDetection.ICUVersion >= new Version(76, 0))
30+
fileName = "IdnaTest_16.txt";
31+
else if (PlatformDetection.ICUVersion >= new Version(72, 1, 0, 4))
3032
fileName = "IdnaTest_15_1.txt";
33+
else if (PlatformDetection.ICUVersion >= new Version(72, 0))
34+
fileName = "IdnaTest_15_0.txt";
3135
else if (PlatformDetection.ICUVersion >= new Version(66, 0))
3236
fileName = "IdnaTest_13.txt";
3337
else if (PlatformDetection.IsWindows7)
@@ -63,8 +67,12 @@ private static IEnumerable<IConformanceIdnaTest> ParseFile(Stream stream, Func<s
6367

6468
private static IConformanceIdnaTest GetConformanceIdnaTest(string line, int lineCount)
6569
{
66-
if (PlatformDetection.ICUVersion >= new Version(74, 0))
70+
if (PlatformDetection.ICUVersion >= new Version(76, 0))
71+
return new Unicode_16_0_IdnaTest(line, lineCount);
72+
else if (PlatformDetection.ICUVersion >= new Version(72, 1, 0, 4))
6773
return new Unicode_15_1_IdnaTest(line, lineCount);
74+
else if (PlatformDetection.ICUVersion >= new Version(72, 0))
75+
return new Unicode_15_0_IdnaTest(line, lineCount);
6876
else if (PlatformDetection.ICUVersion >= new Version(66, 0))
6977
return new Unicode_13_0_IdnaTest(line, lineCount);
7078
else if (PlatformDetection.IsWindows7)

0 commit comments

Comments
 (0)