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";
73 public void Teardown()
78 // With IriParsing: http://a/a%21 does not unescape to http://a/a!
79 // but http://a/a%21%30 does unescape to http://a/a!0
80 // This happens with alpha numeric characters, non ASCII,'-','.','_' and '~'.
81 // So we tests characters with and without those characters.
82 private void TestChars (Action<string> action)
84 var sb1 = new StringBuilder ();
85 var sb2 = new StringBuilder ();
86 for (char c = '\0'; c <= 0x7f; c++) {
87 if (specialTestedChars.Contains ("" + c))
90 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
91 c == '-' || c == '.' || c == '_' || c == '~') {
100 foreach (char c in nonAsciiTestedChars)
103 action (sb1.ToString ());
104 action (sb2.ToString ());
106 foreach (char c in specialTestedChars)
110 internal static string HexEscapeMultiByte (char character, bool upper)
112 const string hex_upper_chars = "0123456789ABCDEF";
113 const string hex_lower_chars = "0123456789abcdef";
115 string hex_chars = (upper)? hex_upper_chars : hex_lower_chars;
117 byte [] bytes = Encoding.UTF8.GetBytes (new [] {character});
118 foreach (byte b in bytes)
119 ret += "%" + hex_chars [((b & 0xf0) >> 4)] + hex_chars [((b & 0x0f))];
124 private void TestScheme(Action<string> action)
126 foreach (string scheme in schemes)
130 private delegate string UriToStringDelegate (Uri uri);
132 private void TestLocation (string id, string str, UriToStringDelegate toString, bool testRelative = true)
134 TestScheme (scheme => {
135 string uri = scheme + str;
136 string actual = toString (new Uri (scheme + str, UriKind.Absolute));
137 StringTester.Assert (scheme + id, actual);
143 string relActual = toString (new Uri ("./" + str, UriKind.Relative));
144 StringTester.Assert ("./" + id, relActual);
147 private void TestLocations (string [] locations, string id, string str, UriToStringDelegate toString,
148 bool testRelative = true)
150 foreach (string location in locations) {
151 if (location.Contains ("{0}"))
152 TestLocation (string.Format (location, id), string.Format (location, str), toString, testRelative);
154 TestLocation (location + id, location + str, toString, testRelative);
158 private void TestPercentageEncoding (UriToStringDelegate toString, bool testRelative = false, string id = "")
160 TestChars (unescapedStr => {
161 var sbUpper = new StringBuilder ();
162 var sbLower = new StringBuilder ();
163 foreach (char c in unescapedStr) {
164 sbUpper.Append (HexEscapeMultiByte (c, true));
165 sbLower.Append (HexEscapeMultiByte (c, false));
167 string escapedUpperStr = sbUpper.ToString ();
168 string escapedLowerStr = sbLower.ToString ();
170 TestLocations (componentLocations, unescapedStr+id, unescapedStr, toString, testRelative);
171 TestLocations (componentLocations, escapedUpperStr+id+"[Upper]", escapedUpperStr, toString, testRelative);
172 TestLocations (componentLocations, escapedLowerStr+id+"[Lower]", escapedLowerStr, toString, testRelative);
175 TestLocations (specialCases, id, "", toString, testRelative);
178 private void TestReduce (UriToStringDelegate toString, bool testRelative = true, string id = "")
180 foreach(var el in reduceElements)
181 TestLocations (reduceLocations, el + id, el, toString, testRelative);
184 private void TestComponent (UriComponents component)
186 TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.SafeUnescaped), id: "[SafeUnescaped]");
187 TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.Unescaped), id: "[Unescaped]");
188 TestPercentageEncoding (uri => uri.GetComponents (component, UriFormat.UriEscaped), id: "[UriEscaped]");
191 private delegate void TestStringDelegate (UriToStringDelegate toString, bool testRelative = true, string id = "");
194 public void PercentageEncoding_AbsoluteUri ()
196 TestPercentageEncoding (uri => uri.AbsoluteUri);
200 public void PercentageEncoding_AbsolutePath ()
202 TestPercentageEncoding (uri => uri.AbsolutePath);
206 public void PercentageEncoding_Fragment ()
208 TestPercentageEncoding (uri => uri.Fragment);
212 public void PercentageEncoding_GetComponents_AbsoluteUri ()
214 TestComponent (UriComponents.AbsoluteUri);
218 public void PercentageEncoding_GetComponents_Fragment ()
220 TestComponent (UriComponents.Fragment);
224 public void PercentageEncoding_GetComponents_Host ()
226 TestComponent (UriComponents.Host);
230 public void PercentageEncoding_GetComponents_Path ()
232 TestComponent (UriComponents.Path);
236 public void PercentageEncoding_GetComponents_PathAndQuery ()
238 TestComponent (UriComponents.PathAndQuery);
242 public void PercentageEncoding_GetComponents_Query ()
244 TestComponent (UriComponents.Query);
248 public void PercentageEncoding_Query ()
250 TestPercentageEncoding (uri => uri.Query);
254 public void PercentageEncoding_ToString ()
256 TestPercentageEncoding (uri => uri.ToString (), true);
261 public UriEx (string s) : base (s)
265 public string UnescapeString (string s)
270 public static string UnescapeString (string uri, string target)
272 return new UriEx (uri).UnescapeString (target);
277 public void PercentageEncoding_Unescape ()
280 var sbUpper = new StringBuilder ();
281 var sbLower = new StringBuilder ();
282 foreach (char c in str) {
283 sbUpper.Append (HexEscapeMultiByte (c, true));
284 sbLower.Append (HexEscapeMultiByte (c, false));
286 string escapedUpperStr = sbUpper.ToString ();
287 string escapedLowerStr = sbLower.ToString ();
289 StringTester.Assert (str + "[Unescaped]", UriEx.UnescapeString ("file://a/", str));
290 StringTester.Assert (escapedUpperStr + "[EscapedUpper]", UriEx.UnescapeString ("file://a/", escapedUpperStr));
291 StringTester.Assert (escapedLowerStr + "[EscapedLower]", UriEx.UnescapeString ("file://a/", escapedLowerStr));
294 foreach (var str in specialCases)
295 StringTester.Assert (str, UriEx.UnescapeString("file://a/", str));
299 public void Reduce_AbsoluteUri ()
301 TestReduce (uri => uri.AbsoluteUri, false);
305 public void Reduce_ToString ()
307 TestReduce (uri => uri.ToString (), true);