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.Collections.Generic;
6 using System.Runtime.InteropServices;
7 using Xunit;
8 
9 namespace System.Globalization.Tests
10 {
11     public class IdnMappingGetUnicodeTests
12     {
GetUnicode_TestData()13         public static IEnumerable<object[]> GetUnicode_TestData()
14         {
15             yield return new object[] { "xn--yda", 0, 7, "\u0101" };
16             yield return new object[] { "axn--ydab", 1, 7, "\u0101" };
17 
18             yield return new object[] { "xn--aa-cla", 0, 10, "\u0101\u0061a" };
19             yield return new object[] { "xn--ab-dla", 0, 10, "\u0061\u0101\u0062" };
20             yield return new object[] { "xn--ab-ela", 0, 10, "\u0061\u0062\u0101"  };
21 
22             yield return new object[] { "xn--097ccd", 0, 10, "\uD800\uDF00\uD800\uDF01\uD800\uDF02" }; // Surrogate pairs
23             yield return new object[] { "xn--ab-ic6nfag", 0, 14, "\uD800\uDF00\u0061\uD800\uDF01b\uD800\uDF02" }; // Surrogate pairs separated by ASCII
24             yield return new object[] { "xn--yda263v6b6kfag", 0, 18, "\uD800\uDF00\u0101\uD800\uDF01\u305D\uD800\uDF02" }; // Surrogate pairs separated by non-ASCII
25             yield return new object[] { "xn--a-nha4529qfag", 0, 17, "\uD800\uDF00\u0101\uD800\uDF01\u0061\uD800\uDF02" }; // Surrogate pairs separated by ASCII and non-ASCII
26             yield return new object[] { "\u0061\u0062\u0063", 0, 3, "\u0061\u0062\u0063" }; // ASCII only code points
27             yield return new object[] { "xn--d9juau41awczczp", 0, 19, "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067" }; // Non-ASCII only code points
28             yield return new object[] { "xn--de-jg4avhby1noc0d", 0, 21, "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" }; // ASCII and non-ASCII code points
29             yield return new object[] { "abc.xn--d9juau41awczczp.xn--de-jg4avhby1noc0d", 0, 45, "\u0061\u0062\u0063.\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067.\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" }; // Fully qualified domain name
30 
31             // Embedded domain name conversion (NLS + only)(Priority 1)
32             // Per the spec [7], "The index and count parameters (when provided) allow the
33             // conversion to be done on a larger string where the domain name is embedded
34             // (such as a URI or IRI). The output string is only the converted FQDN or
35             // label, not the whole input string (if the input string contains more
36             // character than the substring to convert)."
37             // Fully Qualified Domain Name (Label1.Label2.Label3)
38             yield return new object[] { "abc.xn--d9juau41awczczp.xn--de-jg4avhby1noc0d", 0, 45, "\u0061\u0062\u0063.\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067.\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" };
39             yield return new object[] { "abc.xn--d9juau41awczczp", 0, 23, "\u0061\u0062\u0063.\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067" };
40             yield return new object[] { "abc.xn--d9juau41awczczp.", 0, 24, "\u0061\u0062\u0063.\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067." };
41             yield return new object[] { "xn--d9juau41awczczp.xn--de-jg4avhby1noc0d", 0, 41, "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067.\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" };
42             yield return new object[] { "xn--d9juau41awczczp", 0, 19, "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067" };
43             yield return new object[] { "xn--d9juau41awczczp.", 0, 20, "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067." };
44             yield return new object[] { "xn--de-jg4avhby1noc0d", 0, 21, "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" };
45         }
46 
47         [Theory]
48         [MemberData(nameof(GetUnicode_TestData))]
GetUnicode(string ascii, int index, int count, string expected)49         public void GetUnicode(string ascii, int index, int count, string expected)
50         {
51             if (index + count == ascii.Length)
52             {
53                 if (index == 0)
54                 {
55                     Assert.Equal(expected, new IdnMapping().GetUnicode(ascii));
56                 }
57                 Assert.Equal(expected, new IdnMapping().GetUnicode(ascii, index));
58             }
59             Assert.Equal(expected, new IdnMapping().GetUnicode(ascii, index, count));
60         }
61 
GetUnicode_Invalid_TestData()62         public static IEnumerable<object[]> GetUnicode_Invalid_TestData()
63         {
64             // Ascii is null
65             yield return new object[] { null, 0, 0, typeof(ArgumentNullException) };
66             yield return new object[] { null, -5, -10, typeof(ArgumentNullException) };
67 
68             // Index or count are invalid
69             yield return new object[] { "abc", -1, 0, typeof(ArgumentOutOfRangeException) };
70             yield return new object[] { "abc", 0, -1, typeof(ArgumentOutOfRangeException) };
71             yield return new object[] { "abc", -5, -10, typeof(ArgumentOutOfRangeException) };
72             yield return new object[] { "abc", 2, 2, typeof(ArgumentOutOfRangeException) };
73             yield return new object[] { "abc", 4, 99, typeof(ArgumentOutOfRangeException) };
74             yield return new object[] { "abc", 3, 0, typeof(ArgumentException) };
75 
76             // Null containing strings
77             yield return new object[] { "abc\u0000", 0, 4, typeof(ArgumentException) };
78             yield return new object[] { "ab\u0000c", 0, 4, typeof(ArgumentException) };
79 
80             // Invalid unicode strings
81             for (int i = 0; i <= 0x1F; i++)
82             {
83                 yield return new object[] { "abc" + (char)i + "def", 0, 7, typeof(ArgumentException) };
84             }
85 
86             yield return new object[] { "abc" + (char)0x7F + "def", 0, 7, typeof(ArgumentException) };
87 
88             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/corefx/issues/8242
89             {
90                 yield return new object[] { "xn--\u1234", 0, 5, typeof(ArgumentException) };
91                 yield return new object[] { "xn--\u1234pck", 0, 8, typeof(ArgumentException) };
92             }
93         }
94 
95         [Theory]
96         [MemberData(nameof(GetUnicode_Invalid_TestData))]
GetUnicode_Invalid(string ascii, int index, int count, Type exceptionType)97         public void GetUnicode_Invalid(string ascii, int index, int count, Type exceptionType)
98         {
99             GetUnicode_Invalid(new IdnMapping() { UseStd3AsciiRules = false }, ascii, index, count, exceptionType);
100             GetUnicode_Invalid(new IdnMapping() { UseStd3AsciiRules = true }, ascii, index, count, exceptionType);
101         }
102 
GetUnicode_Invalid(IdnMapping idnMapping, string ascii, int index, int count, Type exceptionType)103         public static void GetUnicode_Invalid(IdnMapping idnMapping, string ascii, int index, int count, Type exceptionType)
104         {
105             if (ascii == null || index + count == ascii.Length)
106             {
107                 if (ascii == null || index == 0)
108                 {
109                     Assert.Throws(exceptionType, () => idnMapping.GetUnicode(ascii));
110                 }
111                 Assert.Throws(exceptionType, () => idnMapping.GetUnicode(ascii, index));
112             }
113             Assert.Throws(exceptionType, () => idnMapping.GetUnicode(ascii, index, count));
114         }
115     }
116 }
117