5 namespace MonoTests.System {
7 public class UriPermutationsTest {
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;
14 // The final location depends on NET_2_0, NET_4_0, NET_4_5.
15 private const string location = "./Test/System/UriPermutationsTest/";
17 private const string nonAsciiTestedChars = "☕";
19 // Chars that can change the current component.
20 // Those characters are tested alone.
21 private const string specialTestedChars = "@:?#";
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://",
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
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
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",
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",
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",
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",
57 private static readonly string [] reduceElements = {
58 "", ".", "..", "...", "%2E", "%2E%2E", "%2E%2E%2E"
64 StringTester.CreateMode = createMode;
66 StringTester.Location = location + "NET_4_5";
68 StringTester.Location = location + "NET_4_0";
70 StringTester.Location = location + "NET_2_0";
75 public void Teardown()
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)
86 var sb1 = new StringBuilder ();
87 var sb2 = new StringBuilder ();
88 for (char c = '\0'; c <= 0x7f; c++) {
89 if (specialTestedChars.Contains ("" + c))
92 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
93 c == '-' || c == '.' || c == '_' || c == '~') {
102 foreach (char c in nonAsciiTestedChars)
105 action (sb1.ToString ());
106 action (sb2.ToString ());
108 foreach (char c in specialTestedChars)
112 internal static string HexEscapeMultiByte (char character, bool upper)
114 const string hex_upper_chars = "0123456789ABCDEF";
115 const string hex_lower_chars = "0123456789abcdef";
117 string hex_chars = (upper)? hex_upper_chars : hex_lower_chars;
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))];
126 private void TestScheme(Action<string> action)
128 foreach (string scheme in schemes)
132 private delegate string UriToStringDelegate (Uri uri);
134 private void TestLocation (string id, string str, UriToStringDelegate toString, bool testRelative = true)
136 TestScheme (scheme => {
137 string uri = scheme + str;
138 string actual = toString (new Uri (scheme + str, UriKind.Absolute));
139 StringTester.Assert (scheme + id, actual);
145 string relActual = toString (new Uri ("./" + str, UriKind.Relative));
146 StringTester.Assert ("./" + id, relActual);
149 private void TestLocations (string [] locations, string id, string str, UriToStringDelegate toString,
150 bool testRelative = true)
152 foreach (string location in locations) {
153 if (location.Contains ("{0}"))
154 TestLocation (string.Format (location, id), string.Format (location, str), toString, testRelative);
156 TestLocation (location + id, location + str, toString, testRelative);
160 private void TestPercentageEncoding (UriToStringDelegate toString, bool testRelative = false, string id = "")
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));
169 string escapedUpperStr = sbUpper.ToString ();
170 string escapedLowerStr = sbLower.ToString ();
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);
177 TestLocations (specialCases, id, "", toString, testRelative);
180 private void TestReduce (UriToStringDelegate toString, bool testRelative = true, string id = "")
182 foreach(var el in reduceElements)
183 TestLocations (reduceLocations, el + id, el, toString, testRelative);
186 private void TestComponent (UriComponents component)
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]");
193 private delegate void TestStringDelegate (UriToStringDelegate toString, bool testRelative = true, string id = "");
196 public void PercentageEncoding_AbsoluteUri ()
198 TestPercentageEncoding (uri => uri.AbsoluteUri);
202 public void PercentageEncoding_AbsolutePath ()
204 TestPercentageEncoding (uri => uri.AbsolutePath);
208 public void PercentageEncoding_Fragment ()
210 TestPercentageEncoding (uri => uri.Fragment);
214 public void PercentageEncoding_GetComponents_AbsoluteUri ()
216 TestComponent (UriComponents.AbsoluteUri);
220 public void PercentageEncoding_GetComponents_Fragment ()
222 TestComponent (UriComponents.Fragment);
226 public void PercentageEncoding_GetComponents_Host ()
228 TestComponent (UriComponents.Host);
232 public void PercentageEncoding_GetComponents_Path ()
234 TestComponent (UriComponents.Path);
238 public void PercentageEncoding_GetComponents_PathAndQuery ()
240 TestComponent (UriComponents.PathAndQuery);
244 public void PercentageEncoding_GetComponents_Query ()
246 TestComponent (UriComponents.Query);
250 public void PercentageEncoding_Query ()
252 TestPercentageEncoding (uri => uri.Query);
256 public void PercentageEncoding_ToString ()
258 TestPercentageEncoding (uri => uri.ToString (), true);
263 public UriEx (string s) : base (s)
267 public string UnescapeString (string s)
272 public static string UnescapeString (string uri, string target)
274 return new UriEx (uri).UnescapeString (target);
279 public void PercentageEncoding_Unescape ()
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));
288 string escapedUpperStr = sbUpper.ToString ();
289 string escapedLowerStr = sbLower.ToString ();
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));
296 foreach (var str in specialCases)
297 StringTester.Assert (str, UriEx.UnescapeString("file://a/", str));
301 public void Reduce_AbsoluteUri ()
303 TestReduce (uri => uri.AbsoluteUri, false);
307 public void Reduce_ToString ()
309 TestReduce (uri => uri.ToString (), true);