[System] UriKind.RelativeOrAbsolute workaround.
[mono.git] / mcs / class / System / Test / System / UriPermutationsTest.cs
1 using System;
2 using System.Text;
3 using NUnit.Framework;
4
5 namespace MonoTests.System {
6         [TestFixture]
7         public class UriPermutationsTest {
8
9                 // Set this to true to generate the expected values
10                 // The tests should run first on .NET with CreateMode = true
11                 // The generated files should then be used when running the tests in Mono with CreateMode = false
12                 private const bool createMode = false;
13
14                 // The final location depends on NET_2_0, NET_4_0, NET_4_5.
15                 private const string location = "./Test/System/UriPermutationsTest/";
16
17                 private const string nonAsciiTestedChars = "☕";
18
19                 // Chars that can change the current component.
20                 // Those characters are tested alone.
21                 private const string specialTestedChars = "@:?#";
22
23                 // Scheme news: and custom: are not tested because there is a strange behavior on .NET 4.5
24                 // new Uri("news:a/a%30").ToString() == "news:a/a%30a/a0"
25                 private static readonly string [] schemes = {
26                         "http://", "https://", "file://", "ftp://", "gopher://", "ldap://", "mailto:",
27                         "net.pipe://", "net.tcp://",  "nntp://", "telnet://", "custom://",
28                         //"news:", "custom:"
29                 };
30
31                 private static readonly string [] componentLocations = {
32                         "a/a{0}?", "b/a{0}#", "c/a?", "d/a#",
33                         "a/a{0}?%30#", "a/a?{0}#%30", "a/a%30?#",   // see why on TestChars comment
34                 };
35
36                 private static readonly string [] specialCases = {
37                         "a/a#%#", "a/a#%25#", // '%' cause '#' to escape in some cases
38                         "a/%80%81%B8%B9", // invalid utf8 encoding
39                 };
40
41                 private static readonly string [] reduceLocations = {
42                         "a/b/{0}", "a/b/{0}a", "a/b/c{0}",
43                         "a/b/{0}/a", "a/b/{0}a/a", "a/b/c{0}/a",
44                         // Test '\\'
45                         "a/b\\{0}", "a/b\\{0}a", "a/b\\c{0}",
46                         "a/b\\{0}\\a", "a/b\\{0}a\\a", "a/b\\c{0}\\a",
47                         // Test '/' %2F
48                         "a/b%2F{0}", "a/b%2F{0}a", "a/b%2Fc{0}",
49                         "a/b/{0}%2Fa", "a/b/{0}a%2Fa", "a/b/c{0}%2Fa",
50                         "a/b%2F{0}/a", "a/b%2F{0}a/a", "a/b%2Fc{0}/a",
51                         // Test '\\' %5C
52                         "a/b%5C{0}", "a/b%5C{0}a", "a/b%5Cc{0}",
53                         "a/b/{0}%5Ca", "a/b/{0}a%5Ca", "a/b/c{0}%5Ca",
54                         "a/b%5C{0}/a", "a/b%5C{0}a/a", "a/b%5Cc{0}/a",
55                 };
56
57                 private static readonly string [] reduceElements = {
58                         "", ".", "..", "...", "%2E", "%2E%2E", "%2E%2E%2E"
59                 };
60
61                 [SetUp]
62                 public void Setup()
63                 {
64                         StringTester.CreateMode = createMode;
65 #if NET_4_5
66                         StringTester.Location = location + "NET_4_5";
67 #elif NET_4_0
68                         StringTester.Location = location + "NET_4_0";
69 #else
70                         StringTester.Location = location + "NET_2_0";
71 #endif
72                 }
73
74                 [TearDown]
75                 public void Teardown()
76                 {
77                         StringTester.Save();
78                 }
79
80                 // With IriParsing: http://a/a%21 does not unescape to http://a/a!
81                 // but http://a/a%21%30 does unescape to http://a/a!0
82                 // This happens with alpha numeric characters, non ASCII,'-','.','_' and '~'.
83                 // So we tests characters with and without those characters.
84                 private void TestChars (Action<string> action)
85                 {
86                         var sb1 = new StringBuilder ();
87                         var sb2 = new StringBuilder ();
88                         for (char c = '\0'; c <= 0x7f; c++) {
89                                 if (specialTestedChars.Contains ("" + c))
90                                         continue;
91
92                                 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
93                                          c == '-' || c == '.' || c == '_' || c == '~') {
94                                          sb2.Append (c);
95                                          continue;
96                                 }
97
98                                 sb1.Append (c);
99                                 sb2.Append (c);
100                         }
101
102                         foreach (char c in nonAsciiTestedChars)
103                                 sb2.Append (c);
104
105                         action (sb1.ToString ());
106                         action (sb2.ToString ());
107
108                         foreach (char c in specialTestedChars)
109                                 action ("" + c);
110                 }
111
112                 internal static string HexEscapeMultiByte (char character, bool upper)
113                 {
114                         const string hex_upper_chars = "0123456789ABCDEF";
115                         const string hex_lower_chars = "0123456789abcdef";
116
117                         string hex_chars = (upper)? hex_upper_chars : hex_lower_chars;
118                         string ret = "";
119                         byte [] bytes = Encoding.UTF8.GetBytes (new [] {character});
120                         foreach (byte b in bytes)
121                                 ret += "%" + hex_chars [((b & 0xf0) >> 4)] + hex_chars [((b & 0x0f))];
122
123                         return ret;
124                 }
125
126                 private void TestScheme(Action<string> action)
127                 {
128                         foreach (string scheme in schemes)
129                                 action(scheme);
130                 }
131
132                 private delegate string UriToStringDelegate (Uri uri);
133
134                 private void TestLocation (string id, string str, UriToStringDelegate toString, bool testRelative = true)
135                 {
136                         TestScheme (scheme => {
137                                 string uri = scheme + str;
138                                 string actual = toString (new Uri (scheme + str, UriKind.Absolute));
139                                 StringTester.Assert (scheme + id, actual);
140                         });
141
142                         if (!testRelative)
143                                 return;
144
145                         string relActual = toString (new Uri ("./" + str, UriKind.Relative));
146                         StringTester.Assert ("./" + id, relActual);
147                 }
148
149                 private void TestLocations (string [] locations, string id, string str, UriToStringDelegate toString,
150                         bool testRelative = true)
151                 {
152                         foreach (string location in locations) {
153                                 if (location.Contains ("{0}"))
154                                         TestLocation (string.Format (location, id), string.Format (location, str), toString, testRelative);
155                                 else
156                                         TestLocation (location + id, location + str, toString, testRelative);
157                         }
158                 }
159
160                 private void TestPercentageEncoding (UriToStringDelegate toString, bool testRelative = false, string id = "")
161                 {
162                         TestChars (unescapedStr => {
163                                 var sbUpper = new StringBuilder ();
164                                 var sbLower = new StringBuilder ();
165                                 foreach (char c in unescapedStr) {
166                                         sbUpper.Append (HexEscapeMultiByte (c, true));
167                                         sbLower.Append (HexEscapeMultiByte (c, false));
168                                 }
169                                 string escapedUpperStr = sbUpper.ToString ();
170                                 string escapedLowerStr = sbLower.ToString ();
171
172                                 TestLocations (componentLocations, unescapedStr+id, unescapedStr, toString, testRelative);
173                                 TestLocations (componentLocations, escapedUpperStr+id+"[Upper]", escapedUpperStr, toString, testRelative);
174                                 TestLocations (componentLocations, escapedLowerStr+id+"[Lower]", escapedLowerStr, toString, testRelative);
175                         });
176
177                         TestLocations (specialCases, id, "", toString, testRelative);
178                 }
179
180                 private void TestReduce (UriToStringDelegate toString, bool testRelative = true, string id = "")
181                 {
182                         foreach(var el in reduceElements)
183                                 TestLocations (reduceLocations, el + id, el, toString, testRelative);
184                 }
185
186                 private void TestComponent (UriComponents component)
187                 {
188                         TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.SafeUnescaped), id: "[SafeUnescaped]");
189                         TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.Unescaped), id: "[Unescaped]");
190                         TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.UriEscaped), id: "[UriEscaped]");
191                 }
192
193                 private delegate void TestStringDelegate (UriToStringDelegate toString, bool testRelative = true, string id = "");
194
195                 [Test]
196                 public void PercentageEncoding_AbsoluteUri ()
197                 {
198                         TestPercentageEncoding (uri => uri.AbsoluteUri);
199                 }
200
201                 [Test]
202                 public void PercentageEncoding_AbsolutePath ()
203                 {
204                         TestPercentageEncoding (uri => uri.AbsolutePath);
205                 }
206
207                 [Test]
208                 public void PercentageEncoding_Fragment ()
209                 {
210                         TestPercentageEncoding (uri => uri.Fragment);
211                 }
212
213                 [Test]
214                 public void PercentageEncoding_GetComponents_AbsoluteUri ()
215                 {
216                         TestComponent (UriComponents.AbsoluteUri);
217                 }
218
219                 [Test]
220                 public void PercentageEncoding_GetComponents_Fragment ()
221                 {
222                         TestComponent (UriComponents.Fragment);
223                 }
224
225                 [Test]
226                 public void PercentageEncoding_GetComponents_Host ()
227                 {
228                         TestComponent (UriComponents.Host);
229                 }
230
231                 [Test]
232                 public void PercentageEncoding_GetComponents_Path ()
233                 {
234                         TestComponent (UriComponents.Path);
235                 }
236
237                 [Test]
238                 public void PercentageEncoding_GetComponents_PathAndQuery ()
239                 {
240                         TestComponent (UriComponents.PathAndQuery);
241                 }
242
243                 [Test]
244                 public void PercentageEncoding_GetComponents_Query ()
245                 {
246                         TestComponent (UriComponents.Query);
247                 }
248
249                 [Test]
250                 public void PercentageEncoding_Query ()
251                 {
252                         TestPercentageEncoding (uri => uri.Query);
253                 }
254
255                 [Test]
256                 public void PercentageEncoding_ToString ()
257                 {
258                         TestPercentageEncoding (uri => uri.ToString (), true);
259                 }
260
261                 class UriEx : Uri
262                 {
263                         public UriEx (string s) : base (s)
264                         {
265                         }
266
267                         public string UnescapeString (string s)
268                         {
269                                 return Unescape (s);
270                         }
271
272                         public static string UnescapeString (string uri, string target)
273                         {
274                                 return new UriEx (uri).UnescapeString (target);
275                         }
276                 }
277
278                 [Test]
279                 public void PercentageEncoding_Unescape ()
280                 {
281                         TestChars (str => {
282                                 var sbUpper = new StringBuilder ();
283                                 var sbLower = new StringBuilder ();
284                                 foreach (char c in str) {
285                                         sbUpper.Append (HexEscapeMultiByte (c, true));
286                                         sbLower.Append (HexEscapeMultiByte (c, false));
287                                 }
288                                 string escapedUpperStr = sbUpper.ToString ();
289                                 string escapedLowerStr = sbLower.ToString ();
290
291                                 StringTester.Assert (str + "[Unescaped]", UriEx.UnescapeString ("file://a/", str));
292                                 StringTester.Assert (escapedUpperStr + "[EscapedUpper]", UriEx.UnescapeString ("file://a/", escapedUpperStr));
293                                 StringTester.Assert (escapedLowerStr + "[EscapedLower]", UriEx.UnescapeString ("file://a/", escapedLowerStr));
294                         });
295
296                         foreach (var str in specialCases)
297                                 StringTester.Assert (str, UriEx.UnescapeString("file://a/", str));
298                 }
299
300                 [Test]
301                 public void Reduce_AbsoluteUri ()
302                 {
303                         TestReduce (uri => uri.AbsoluteUri, false);
304                 }
305
306                 [Test]
307                 public void Reduce_ToString ()
308                 {
309                         TestReduce (uri => uri.ToString (), true);
310                 }
311         }
312 }