Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / go / net / url / url_test.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package url
6
7 import (
8         "fmt"
9         "reflect"
10         "strings"
11         "testing"
12 )
13
14 type URLTest struct {
15         in        string
16         out       *URL
17         roundtrip string // expected result of reserializing the URL; empty means same as "in".
18 }
19
20 var urltests = []URLTest{
21         // no path
22         {
23                 "http://www.google.com",
24                 &URL{
25                         Scheme: "http",
26                         Host:   "www.google.com",
27                 },
28                 "",
29         },
30         // path
31         {
32                 "http://www.google.com/",
33                 &URL{
34                         Scheme: "http",
35                         Host:   "www.google.com",
36                         Path:   "/",
37                 },
38                 "",
39         },
40         // path with hex escaping
41         {
42                 "http://www.google.com/file%20one%26two",
43                 &URL{
44                         Scheme: "http",
45                         Host:   "www.google.com",
46                         Path:   "/file one&two",
47                 },
48                 "http://www.google.com/file%20one&two",
49         },
50         // user
51         {
52                 "ftp://webmaster@www.google.com/",
53                 &URL{
54                         Scheme: "ftp",
55                         User:   User("webmaster"),
56                         Host:   "www.google.com",
57                         Path:   "/",
58                 },
59                 "",
60         },
61         // escape sequence in username
62         {
63                 "ftp://john%20doe@www.google.com/",
64                 &URL{
65                         Scheme: "ftp",
66                         User:   User("john doe"),
67                         Host:   "www.google.com",
68                         Path:   "/",
69                 },
70                 "ftp://john%20doe@www.google.com/",
71         },
72         // query
73         {
74                 "http://www.google.com/?q=go+language",
75                 &URL{
76                         Scheme:   "http",
77                         Host:     "www.google.com",
78                         Path:     "/",
79                         RawQuery: "q=go+language",
80                 },
81                 "",
82         },
83         // query with hex escaping: NOT parsed
84         {
85                 "http://www.google.com/?q=go%20language",
86                 &URL{
87                         Scheme:   "http",
88                         Host:     "www.google.com",
89                         Path:     "/",
90                         RawQuery: "q=go%20language",
91                 },
92                 "",
93         },
94         // %20 outside query
95         {
96                 "http://www.google.com/a%20b?q=c+d",
97                 &URL{
98                         Scheme:   "http",
99                         Host:     "www.google.com",
100                         Path:     "/a b",
101                         RawQuery: "q=c+d",
102                 },
103                 "",
104         },
105         // path without leading /, so no parsing
106         {
107                 "http:www.google.com/?q=go+language",
108                 &URL{
109                         Scheme:   "http",
110                         Opaque:   "www.google.com/",
111                         RawQuery: "q=go+language",
112                 },
113                 "http:www.google.com/?q=go+language",
114         },
115         // path without leading /, so no parsing
116         {
117                 "http:%2f%2fwww.google.com/?q=go+language",
118                 &URL{
119                         Scheme:   "http",
120                         Opaque:   "%2f%2fwww.google.com/",
121                         RawQuery: "q=go+language",
122                 },
123                 "http:%2f%2fwww.google.com/?q=go+language",
124         },
125         // non-authority with path
126         {
127                 "mailto:/webmaster@golang.org",
128                 &URL{
129                         Scheme: "mailto",
130                         Path:   "/webmaster@golang.org",
131                 },
132                 "mailto:///webmaster@golang.org", // unfortunate compromise
133         },
134         // non-authority
135         {
136                 "mailto:webmaster@golang.org",
137                 &URL{
138                         Scheme: "mailto",
139                         Opaque: "webmaster@golang.org",
140                 },
141                 "",
142         },
143         // unescaped :// in query should not create a scheme
144         {
145                 "/foo?query=http://bad",
146                 &URL{
147                         Path:     "/foo",
148                         RawQuery: "query=http://bad",
149                 },
150                 "",
151         },
152         // leading // without scheme should create an authority
153         {
154                 "//foo",
155                 &URL{
156                         Host: "foo",
157                 },
158                 "",
159         },
160         // leading // without scheme, with userinfo, path, and query
161         {
162                 "//user@foo/path?a=b",
163                 &URL{
164                         User:     User("user"),
165                         Host:     "foo",
166                         Path:     "/path",
167                         RawQuery: "a=b",
168                 },
169                 "",
170         },
171         // Three leading slashes isn't an authority, but doesn't return an error.
172         // (We can't return an error, as this code is also used via
173         // ServeHTTP -> ReadRequest -> Parse, which is arguably a
174         // different URL parsing context, but currently shares the
175         // same codepath)
176         {
177                 "///threeslashes",
178                 &URL{
179                         Path: "///threeslashes",
180                 },
181                 "",
182         },
183         {
184                 "http://user:password@google.com",
185                 &URL{
186                         Scheme: "http",
187                         User:   UserPassword("user", "password"),
188                         Host:   "google.com",
189                 },
190                 "http://user:password@google.com",
191         },
192         // unescaped @ in username should not confuse host
193         {
194                 "http://j@ne:password@google.com",
195                 &URL{
196                         Scheme: "http",
197                         User:   UserPassword("j@ne", "password"),
198                         Host:   "google.com",
199                 },
200                 "http://j%40ne:password@google.com",
201         },
202         // unescaped @ in password should not confuse host
203         {
204                 "http://jane:p@ssword@google.com",
205                 &URL{
206                         Scheme: "http",
207                         User:   UserPassword("jane", "p@ssword"),
208                         Host:   "google.com",
209                 },
210                 "http://jane:p%40ssword@google.com",
211         },
212         {
213                 "http://j@ne:password@google.com/p@th?q=@go",
214                 &URL{
215                         Scheme:   "http",
216                         User:     UserPassword("j@ne", "password"),
217                         Host:     "google.com",
218                         Path:     "/p@th",
219                         RawQuery: "q=@go",
220                 },
221                 "http://j%40ne:password@google.com/p@th?q=@go",
222         },
223         {
224                 "http://www.google.com/?q=go+language#foo",
225                 &URL{
226                         Scheme:   "http",
227                         Host:     "www.google.com",
228                         Path:     "/",
229                         RawQuery: "q=go+language",
230                         Fragment: "foo",
231                 },
232                 "",
233         },
234         {
235                 "http://www.google.com/?q=go+language#foo%26bar",
236                 &URL{
237                         Scheme:   "http",
238                         Host:     "www.google.com",
239                         Path:     "/",
240                         RawQuery: "q=go+language",
241                         Fragment: "foo&bar",
242                 },
243                 "http://www.google.com/?q=go+language#foo&bar",
244         },
245         {
246                 "file:///home/adg/rabbits",
247                 &URL{
248                         Scheme: "file",
249                         Host:   "",
250                         Path:   "/home/adg/rabbits",
251                 },
252                 "file:///home/adg/rabbits",
253         },
254 }
255
256 // more useful string for debugging than fmt's struct printer
257 func ufmt(u *URL) string {
258         var user, pass interface{}
259         if u.User != nil {
260                 user = u.User.Username()
261                 if p, ok := u.User.Password(); ok {
262                         pass = p
263                 }
264         }
265         return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawq=%q, frag=%q",
266                 u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawQuery, u.Fragment)
267 }
268
269 func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
270         for _, tt := range tests {
271                 u, err := parse(tt.in)
272                 if err != nil {
273                         t.Errorf("%s(%q) returned error %s", name, tt.in, err)
274                         continue
275                 }
276                 if !reflect.DeepEqual(u, tt.out) {
277                         t.Errorf("%s(%q):\n\thave %v\n\twant %v\n",
278                                 name, tt.in, ufmt(u), ufmt(tt.out))
279                 }
280         }
281 }
282
283 func BenchmarkString(b *testing.B) {
284         b.StopTimer()
285         b.ReportAllocs()
286         for _, tt := range urltests {
287                 u, err := Parse(tt.in)
288                 if err != nil {
289                         b.Errorf("Parse(%q) returned error %s", tt.in, err)
290                         continue
291                 }
292                 if tt.roundtrip == "" {
293                         continue
294                 }
295                 b.StartTimer()
296                 var g string
297                 for i := 0; i < b.N; i++ {
298                         g = u.String()
299                 }
300                 b.StopTimer()
301                 if w := tt.roundtrip; g != w {
302                         b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
303                 }
304         }
305 }
306
307 func TestParse(t *testing.T) {
308         DoTest(t, Parse, "Parse", urltests)
309 }
310
311 const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
312
313 var parseRequestURLTests = []struct {
314         url           string
315         expectedValid bool
316 }{
317         {"http://foo.com", true},
318         {"http://foo.com/", true},
319         {"http://foo.com/path", true},
320         {"/", true},
321         {pathThatLooksSchemeRelative, true},
322         {"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
323         {"foo.html", false},
324         {"../dir/", false},
325         {"*", true},
326 }
327
328 func TestParseRequestURI(t *testing.T) {
329         for _, test := range parseRequestURLTests {
330                 _, err := ParseRequestURI(test.url)
331                 valid := err == nil
332                 if valid != test.expectedValid {
333                         t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
334                 }
335         }
336
337         url, err := ParseRequestURI(pathThatLooksSchemeRelative)
338         if err != nil {
339                 t.Fatalf("Unexpected error %v", err)
340         }
341         if url.Path != pathThatLooksSchemeRelative {
342                 t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path)
343         }
344 }
345
346 func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
347         for _, tt := range tests {
348                 u, err := parse(tt.in)
349                 if err != nil {
350                         t.Errorf("%s(%q) returned error %s", name, tt.in, err)
351                         continue
352                 }
353                 expected := tt.in
354                 if len(tt.roundtrip) > 0 {
355                         expected = tt.roundtrip
356                 }
357                 s := u.String()
358                 if s != expected {
359                         t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected)
360                 }
361         }
362 }
363
364 func TestURLString(t *testing.T) {
365         DoTestString(t, Parse, "Parse", urltests)
366 }
367
368 type EscapeTest struct {
369         in  string
370         out string
371         err error
372 }
373
374 var unescapeTests = []EscapeTest{
375         {
376                 "",
377                 "",
378                 nil,
379         },
380         {
381                 "abc",
382                 "abc",
383                 nil,
384         },
385         {
386                 "1%41",
387                 "1A",
388                 nil,
389         },
390         {
391                 "1%41%42%43",
392                 "1ABC",
393                 nil,
394         },
395         {
396                 "%4a",
397                 "J",
398                 nil,
399         },
400         {
401                 "%6F",
402                 "o",
403                 nil,
404         },
405         {
406                 "%", // not enough characters after %
407                 "",
408                 EscapeError("%"),
409         },
410         {
411                 "%a", // not enough characters after %
412                 "",
413                 EscapeError("%a"),
414         },
415         {
416                 "%1", // not enough characters after %
417                 "",
418                 EscapeError("%1"),
419         },
420         {
421                 "123%45%6", // not enough characters after %
422                 "",
423                 EscapeError("%6"),
424         },
425         {
426                 "%zzzzz", // invalid hex digits
427                 "",
428                 EscapeError("%zz"),
429         },
430 }
431
432 func TestUnescape(t *testing.T) {
433         for _, tt := range unescapeTests {
434                 actual, err := QueryUnescape(tt.in)
435                 if actual != tt.out || (err != nil) != (tt.err != nil) {
436                         t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
437                 }
438         }
439 }
440
441 var escapeTests = []EscapeTest{
442         {
443                 "",
444                 "",
445                 nil,
446         },
447         {
448                 "abc",
449                 "abc",
450                 nil,
451         },
452         {
453                 "one two",
454                 "one+two",
455                 nil,
456         },
457         {
458                 "10%",
459                 "10%25",
460                 nil,
461         },
462         {
463                 " ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
464                 "+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",
465                 nil,
466         },
467 }
468
469 func TestEscape(t *testing.T) {
470         for _, tt := range escapeTests {
471                 actual := QueryEscape(tt.in)
472                 if tt.out != actual {
473                         t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
474                 }
475
476                 // for bonus points, verify that escape:unescape is an identity.
477                 roundtrip, err := QueryUnescape(actual)
478                 if roundtrip != tt.in || err != nil {
479                         t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
480                 }
481         }
482 }
483
484 //var userinfoTests = []UserinfoTest{
485 //      {"user", "password", "user:password"},
486 //      {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
487 //              "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
488 //}
489
490 type EncodeQueryTest struct {
491         m        Values
492         expected string
493 }
494
495 var encodeQueryTests = []EncodeQueryTest{
496         {nil, ""},
497         {Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
498         {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
499         {Values{
500                 "a": {"a1", "a2", "a3"},
501                 "b": {"b1", "b2", "b3"},
502                 "c": {"c1", "c2", "c3"},
503         }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
504 }
505
506 func TestEncodeQuery(t *testing.T) {
507         for _, tt := range encodeQueryTests {
508                 if q := tt.m.Encode(); q != tt.expected {
509                         t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
510                 }
511         }
512 }
513
514 var resolvePathTests = []struct {
515         base, ref, expected string
516 }{
517         {"a/b", ".", "a/"},
518         {"a/b", "c", "a/c"},
519         {"a/b", "..", ""},
520         {"a/", "..", ""},
521         {"a/", "../..", ""},
522         {"a/b/c", "..", "a/"},
523         {"a/b/c", "../d", "a/d"},
524         {"a/b/c", ".././d", "a/d"},
525         {"a/b", "./..", ""},
526         {"a/./b", ".", "a/./"},
527         {"a/../", ".", "a/../"},
528         {"a/.././b", "c", "a/.././c"},
529 }
530
531 func TestResolvePath(t *testing.T) {
532         for _, test := range resolvePathTests {
533                 got := resolvePath(test.base, test.ref)
534                 if got != test.expected {
535                         t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected)
536                 }
537         }
538 }
539
540 var resolveReferenceTests = []struct {
541         base, rel, expected string
542 }{
543         // Absolute URL references
544         {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
545         {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
546         {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
547
548         // Path-absolute references
549         {"http://foo.com/bar", "/baz", "http://foo.com/baz"},
550         {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
551         {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
552
553         // Scheme-relative
554         {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
555
556         // Path-relative references:
557
558         // ... current directory
559         {"http://foo.com", ".", "http://foo.com/"},
560         {"http://foo.com/bar", ".", "http://foo.com/"},
561         {"http://foo.com/bar/", ".", "http://foo.com/bar/"},
562
563         // ... going down
564         {"http://foo.com", "bar", "http://foo.com/bar"},
565         {"http://foo.com/", "bar", "http://foo.com/bar"},
566         {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
567
568         // ... going up
569         {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
570         {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
571         {"http://foo.com/bar", "..", "http://foo.com/"},
572         {"http://foo.com/bar/baz", "./..", "http://foo.com/"},
573         // ".." in the middle (issue 3560)
574         {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
575         {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
576         {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
577         {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
578         {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
579         {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
580         {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
581         {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"},
582
583         // "." and ".." in the base aren't special
584         {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},
585
586         // Triple dot isn't special
587         {"http://foo.com/bar", "...", "http://foo.com/..."},
588
589         // Fragment
590         {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
591 }
592
593 func TestResolveReference(t *testing.T) {
594         mustParse := func(url string) *URL {
595                 u, err := Parse(url)
596                 if err != nil {
597                         t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
598                 }
599                 return u
600         }
601         for _, test := range resolveReferenceTests {
602                 base := mustParse(test.base)
603                 rel := mustParse(test.rel)
604                 url := base.ResolveReference(rel)
605                 urlStr := url.String()
606                 if urlStr != test.expected {
607                         t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr)
608                 }
609         }
610
611         // Test that new instances are returned.
612         base := mustParse("http://foo.com/")
613         abs := base.ResolveReference(mustParse("."))
614         if base == abs {
615                 t.Errorf("Expected no-op reference to return new URL instance.")
616         }
617         barRef := mustParse("http://bar.com/")
618         abs = base.ResolveReference(barRef)
619         if abs == barRef {
620                 t.Errorf("Expected resolution of absolute reference to return new URL instance.")
621         }
622
623         // Test the convenience wrapper too
624         base = mustParse("http://foo.com/path/one/")
625         abs, _ = base.Parse("../two")
626         expected := "http://foo.com/path/two"
627         if abs.String() != expected {
628                 t.Errorf("Parse wrapper got %q; expected %q", abs.String(), expected)
629         }
630         _, err := base.Parse("")
631         if err == nil {
632                 t.Errorf("Expected an error from Parse wrapper parsing an empty string.")
633         }
634
635         // Ensure Opaque resets the URL.
636         base = mustParse("scheme://user@foo.com/bar")
637         abs = base.ResolveReference(&URL{Opaque: "opaque"})
638         want := mustParse("scheme:opaque")
639         if *abs != *want {
640                 t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", abs, want)
641         }
642 }
643
644 func TestResolveReferenceOpaque(t *testing.T) {
645         mustParse := func(url string) *URL {
646                 u, err := Parse(url)
647                 if err != nil {
648                         t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
649                 }
650                 return u
651         }
652         for _, test := range resolveReferenceTests {
653                 base := mustParse(test.base)
654                 rel := mustParse(test.rel)
655                 url := base.ResolveReference(rel)
656                 urlStr := url.String()
657                 if urlStr != test.expected {
658                         t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr)
659                 }
660         }
661
662         // Test that new instances are returned.
663         base := mustParse("http://foo.com/")
664         abs := base.ResolveReference(mustParse("."))
665         if base == abs {
666                 t.Errorf("Expected no-op reference to return new URL instance.")
667         }
668         barRef := mustParse("http://bar.com/")
669         abs = base.ResolveReference(barRef)
670         if abs == barRef {
671                 t.Errorf("Expected resolution of absolute reference to return new URL instance.")
672         }
673
674         // Test the convenience wrapper too
675         base = mustParse("http://foo.com/path/one/")
676         abs, _ = base.Parse("../two")
677         expected := "http://foo.com/path/two"
678         if abs.String() != expected {
679                 t.Errorf("Parse wrapper got %q; expected %q", abs.String(), expected)
680         }
681         _, err := base.Parse("")
682         if err == nil {
683                 t.Errorf("Expected an error from Parse wrapper parsing an empty string.")
684         }
685
686 }
687
688 func TestQueryValues(t *testing.T) {
689         u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2")
690         v := u.Query()
691         if len(v) != 2 {
692                 t.Errorf("got %d keys in Query values, want 2", len(v))
693         }
694         if g, e := v.Get("foo"), "bar"; g != e {
695                 t.Errorf("Get(foo) = %q, want %q", g, e)
696         }
697         // Case sensitive:
698         if g, e := v.Get("Foo"), ""; g != e {
699                 t.Errorf("Get(Foo) = %q, want %q", g, e)
700         }
701         if g, e := v.Get("bar"), "1"; g != e {
702                 t.Errorf("Get(bar) = %q, want %q", g, e)
703         }
704         if g, e := v.Get("baz"), ""; g != e {
705                 t.Errorf("Get(baz) = %q, want %q", g, e)
706         }
707         v.Del("bar")
708         if g, e := v.Get("bar"), ""; g != e {
709                 t.Errorf("second Get(bar) = %q, want %q", g, e)
710         }
711 }
712
713 type parseTest struct {
714         query string
715         out   Values
716 }
717
718 var parseTests = []parseTest{
719         {
720                 query: "a=1&b=2",
721                 out:   Values{"a": []string{"1"}, "b": []string{"2"}},
722         },
723         {
724                 query: "a=1&a=2&a=banana",
725                 out:   Values{"a": []string{"1", "2", "banana"}},
726         },
727         {
728                 query: "ascii=%3Ckey%3A+0x90%3E",
729                 out:   Values{"ascii": []string{"<key: 0x90>"}},
730         },
731         {
732                 query: "a=1;b=2",
733                 out:   Values{"a": []string{"1"}, "b": []string{"2"}},
734         },
735         {
736                 query: "a=1&a=2;a=banana",
737                 out:   Values{"a": []string{"1", "2", "banana"}},
738         },
739 }
740
741 func TestParseQuery(t *testing.T) {
742         for i, test := range parseTests {
743                 form, err := ParseQuery(test.query)
744                 if err != nil {
745                         t.Errorf("test %d: Unexpected error: %v", i, err)
746                         continue
747                 }
748                 if len(form) != len(test.out) {
749                         t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
750                 }
751                 for k, evs := range test.out {
752                         vs, ok := form[k]
753                         if !ok {
754                                 t.Errorf("test %d: Missing key %q", i, k)
755                                 continue
756                         }
757                         if len(vs) != len(evs) {
758                                 t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
759                                 continue
760                         }
761                         for j, ev := range evs {
762                                 if v := vs[j]; v != ev {
763                                         t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
764                                 }
765                         }
766                 }
767         }
768 }
769
770 type RequestURITest struct {
771         url *URL
772         out string
773 }
774
775 var requritests = []RequestURITest{
776         {
777                 &URL{
778                         Scheme: "http",
779                         Host:   "example.com",
780                         Path:   "",
781                 },
782                 "/",
783         },
784         {
785                 &URL{
786                         Scheme: "http",
787                         Host:   "example.com",
788                         Path:   "/a b",
789                 },
790                 "/a%20b",
791         },
792         {
793                 &URL{
794                         Scheme:   "http",
795                         Host:     "example.com",
796                         Path:     "/a b",
797                         RawQuery: "q=go+language",
798                 },
799                 "/a%20b?q=go+language",
800         },
801         {
802                 &URL{
803                         Scheme: "myschema",
804                         Opaque: "opaque",
805                 },
806                 "opaque",
807         },
808         {
809                 &URL{
810                         Scheme:   "myschema",
811                         Opaque:   "opaque",
812                         RawQuery: "q=go+language",
813                 },
814                 "opaque?q=go+language",
815         },
816 }
817
818 func TestRequestURI(t *testing.T) {
819         for _, tt := range requritests {
820                 s := tt.url.RequestURI()
821                 if s != tt.out {
822                         t.Errorf("%#v.RequestURI() == %q (expected %q)", tt.url, s, tt.out)
823                 }
824         }
825 }
826
827 func TestParseFailure(t *testing.T) {
828         // Test that the first parse error is returned.
829         const url = "%gh&%ij"
830         _, err := ParseQuery(url)
831         errStr := fmt.Sprint(err)
832         if !strings.Contains(errStr, "%gh") {
833                 t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
834         }
835 }