OSDN Git Service

libgo: Update to weekly.2012-03-13.
[pf3gnuchains/gcc-fork.git] / libgo / go / path / filepath / path_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 filepath_test
6
7 import (
8         "io/ioutil"
9         "os"
10         "path/filepath"
11         "reflect"
12         "runtime"
13         "strings"
14         "testing"
15 )
16
17 type PathTest struct {
18         path, result string
19 }
20
21 var cleantests = []PathTest{
22         // Already clean
23         {"", "."},
24         {"abc", "abc"},
25         {"abc/def", "abc/def"},
26         {"a/b/c", "a/b/c"},
27         {".", "."},
28         {"..", ".."},
29         {"../..", "../.."},
30         {"../../abc", "../../abc"},
31         {"/abc", "/abc"},
32         {"/", "/"},
33
34         // Remove trailing slash
35         {"abc/", "abc"},
36         {"abc/def/", "abc/def"},
37         {"a/b/c/", "a/b/c"},
38         {"./", "."},
39         {"../", ".."},
40         {"../../", "../.."},
41         {"/abc/", "/abc"},
42
43         // Remove doubled slash
44         {"abc//def//ghi", "abc/def/ghi"},
45         {"//abc", "/abc"},
46         {"///abc", "/abc"},
47         {"//abc//", "/abc"},
48         {"abc//", "abc"},
49
50         // Remove . elements
51         {"abc/./def", "abc/def"},
52         {"/./abc/def", "/abc/def"},
53         {"abc/.", "abc"},
54
55         // Remove .. elements
56         {"abc/def/ghi/../jkl", "abc/def/jkl"},
57         {"abc/def/../ghi/../jkl", "abc/jkl"},
58         {"abc/def/..", "abc"},
59         {"abc/def/../..", "."},
60         {"/abc/def/../..", "/"},
61         {"abc/def/../../..", ".."},
62         {"/abc/def/../../..", "/"},
63         {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
64
65         // Combinations
66         {"abc/./../def", "def"},
67         {"abc//./../def", "def"},
68         {"abc/../../././../def", "../../def"},
69 }
70
71 var wincleantests = []PathTest{
72         {`c:`, `c:.`},
73         {`c:\`, `c:\`},
74         {`c:\abc`, `c:\abc`},
75         {`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
76         {`c:\abc\def\..\..`, `c:\`},
77         {`c:\..\abc`, `c:\abc`},
78         {`c:..\abc`, `c:..\abc`},
79         {`\`, `\`},
80         {`/`, `\`},
81         {`\\i\..\c$`, `\c$`},
82         {`\\i\..\i\c$`, `\i\c$`},
83         {`\\i\..\I\c$`, `\I\c$`},
84         {`\\host\share\foo\..\bar`, `\\host\share\bar`},
85         {`//host/share/foo/../baz`, `\\host\share\baz`},
86         {`\\a\b\..\c`, `\\a\b\c`},
87         {`\\a\b`, `\\a\b`},
88 }
89
90 func TestClean(t *testing.T) {
91         tests := cleantests
92         if runtime.GOOS == "windows" {
93                 for i := range tests {
94                         tests[i].result = filepath.FromSlash(tests[i].result)
95                 }
96                 tests = append(tests, wincleantests...)
97         }
98         for _, test := range tests {
99                 if s := filepath.Clean(test.path); s != test.result {
100                         t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
101                 }
102         }
103 }
104
105 const sep = filepath.Separator
106
107 var slashtests = []PathTest{
108         {"", ""},
109         {"/", string(sep)},
110         {"/a/b", string([]byte{sep, 'a', sep, 'b'})},
111         {"a//b", string([]byte{'a', sep, sep, 'b'})},
112 }
113
114 func TestFromAndToSlash(t *testing.T) {
115         for _, test := range slashtests {
116                 if s := filepath.FromSlash(test.path); s != test.result {
117                         t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result)
118                 }
119                 if s := filepath.ToSlash(test.result); s != test.path {
120                         t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path)
121                 }
122         }
123 }
124
125 type SplitListTest struct {
126         list   string
127         result []string
128 }
129
130 const lsep = filepath.ListSeparator
131
132 var splitlisttests = []SplitListTest{
133         {"", []string{}},
134         {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}},
135         {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
136 }
137
138 func TestSplitList(t *testing.T) {
139         for _, test := range splitlisttests {
140                 if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) {
141                         t.Errorf("SplitList(%q) = %s, want %s", test.list, l, test.result)
142                 }
143         }
144 }
145
146 type SplitTest struct {
147         path, dir, file string
148 }
149
150 var unixsplittests = []SplitTest{
151         {"a/b", "a/", "b"},
152         {"a/b/", "a/b/", ""},
153         {"a/", "a/", ""},
154         {"a", "", "a"},
155         {"/", "/", ""},
156 }
157
158 var winsplittests = []SplitTest{
159         {`c:`, `c:`, ``},
160         {`c:/`, `c:/`, ``},
161         {`c:/foo`, `c:/`, `foo`},
162         {`c:/foo/bar`, `c:/foo/`, `bar`},
163         {`//host/share`, `//host/share`, ``},
164         {`//host/share/`, `//host/share/`, ``},
165         {`//host/share/foo`, `//host/share/`, `foo`},
166         {`\\host\share`, `\\host\share`, ``},
167         {`\\host\share\`, `\\host\share\`, ``},
168         {`\\host\share\foo`, `\\host\share\`, `foo`},
169 }
170
171 func TestSplit(t *testing.T) {
172         var splittests []SplitTest
173         splittests = unixsplittests
174         if runtime.GOOS == "windows" {
175                 splittests = append(splittests, winsplittests...)
176         }
177         for _, test := range splittests {
178                 if d, f := filepath.Split(test.path); d != test.dir || f != test.file {
179                         t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
180                 }
181         }
182 }
183
184 type JoinTest struct {
185         elem []string
186         path string
187 }
188
189 var jointests = []JoinTest{
190         // zero parameters
191         {[]string{}, ""},
192
193         // one parameter
194         {[]string{""}, ""},
195         {[]string{"a"}, "a"},
196
197         // two parameters
198         {[]string{"a", "b"}, "a/b"},
199         {[]string{"a", ""}, "a"},
200         {[]string{"", "b"}, "b"},
201         {[]string{"/", "a"}, "/a"},
202         {[]string{"/", ""}, "/"},
203         {[]string{"a/", "b"}, "a/b"},
204         {[]string{"a/", ""}, "a"},
205         {[]string{"", ""}, ""},
206 }
207
208 var winjointests = []JoinTest{
209         {[]string{`directory`, `file`}, `directory\file`},
210         {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
211         {[]string{`C:\Windows\`, ``}, `C:\Windows`},
212         {[]string{`C:\`, `Windows`}, `C:\Windows`},
213         {[]string{`C:`, `Windows`}, `C:\Windows`},
214         {[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
215         {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
216 }
217
218 // join takes a []string and passes it to Join.
219 func join(elem []string, args ...string) string {
220         args = elem
221         return filepath.Join(args...)
222 }
223
224 func TestJoin(t *testing.T) {
225         if runtime.GOOS == "windows" {
226                 jointests = append(jointests, winjointests...)
227         }
228         for _, test := range jointests {
229                 if p := join(test.elem); p != filepath.FromSlash(test.path) {
230                         t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path)
231                 }
232         }
233 }
234
235 type ExtTest struct {
236         path, ext string
237 }
238
239 var exttests = []ExtTest{
240         {"path.go", ".go"},
241         {"path.pb.go", ".go"},
242         {"a.dir/b", ""},
243         {"a.dir/b.go", ".go"},
244         {"a.dir/", ""},
245 }
246
247 func TestExt(t *testing.T) {
248         for _, test := range exttests {
249                 if x := filepath.Ext(test.path); x != test.ext {
250                         t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext)
251                 }
252         }
253 }
254
255 type Node struct {
256         name    string
257         entries []*Node // nil if the entry is a file
258         mark    int
259 }
260
261 var tree = &Node{
262         "testdata",
263         []*Node{
264                 {"a", nil, 0},
265                 {"b", []*Node{}, 0},
266                 {"c", nil, 0},
267                 {
268                         "d",
269                         []*Node{
270                                 {"x", nil, 0},
271                                 {"y", []*Node{}, 0},
272                                 {
273                                         "z",
274                                         []*Node{
275                                                 {"u", nil, 0},
276                                                 {"v", nil, 0},
277                                         },
278                                         0,
279                                 },
280                         },
281                         0,
282                 },
283         },
284         0,
285 }
286
287 func walkTree(n *Node, path string, f func(path string, n *Node)) {
288         f(path, n)
289         for _, e := range n.entries {
290                 walkTree(e, filepath.Join(path, e.name), f)
291         }
292 }
293
294 func makeTree(t *testing.T) {
295         walkTree(tree, tree.name, func(path string, n *Node) {
296                 if n.entries == nil {
297                         fd, err := os.Create(path)
298                         if err != nil {
299                                 t.Errorf("makeTree: %v", err)
300                                 return
301                         }
302                         fd.Close()
303                 } else {
304                         os.Mkdir(path, 0770)
305                 }
306         })
307 }
308
309 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
310
311 func checkMarks(t *testing.T, report bool) {
312         walkTree(tree, tree.name, func(path string, n *Node) {
313                 if n.mark != 1 && report {
314                         t.Errorf("node %s mark = %d; expected 1", path, n.mark)
315                 }
316                 n.mark = 0
317         })
318 }
319
320 // Assumes that each node name is unique. Good enough for a test.
321 // If clear is true, any incoming error is cleared before return. The errors
322 // are always accumulated, though.
323 func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
324         if err != nil {
325                 *errors = append(*errors, err)
326                 if clear {
327                         return nil
328                 }
329                 return err
330         }
331         name := info.Name()
332         walkTree(tree, tree.name, func(path string, n *Node) {
333                 if n.name == name {
334                         n.mark++
335                 }
336         })
337         return nil
338 }
339
340 func TestWalk(t *testing.T) {
341         makeTree(t)
342         errors := make([]error, 0, 10)
343         clear := true
344         markFn := func(path string, info os.FileInfo, err error) error {
345                 return mark(path, info, err, &errors, clear)
346         }
347         // Expect no errors.
348         err := filepath.Walk(tree.name, markFn)
349         if err != nil {
350                 t.Fatalf("no error expected, found: %s", err)
351         }
352         if len(errors) != 0 {
353                 t.Fatalf("unexpected errors: %s", errors)
354         }
355         checkMarks(t, true)
356         errors = errors[0:0]
357
358         // Test permission errors.  Only possible if we're not root
359         // and only on some file systems (AFS, FAT).  To avoid errors during
360         // all.bash on those file systems, skip during go test -short.
361         if os.Getuid() > 0 && !testing.Short() {
362                 // introduce 2 errors: chmod top-level directories to 0
363                 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
364                 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
365
366                 // 3) capture errors, expect two.
367                 // mark respective subtrees manually
368                 markTree(tree.entries[1])
369                 markTree(tree.entries[3])
370                 // correct double-marking of directory itself
371                 tree.entries[1].mark--
372                 tree.entries[3].mark--
373                 err := filepath.Walk(tree.name, markFn)
374                 if err != nil {
375                         t.Fatalf("expected no error return from Walk, got %s", err)
376                 }
377                 if len(errors) != 2 {
378                         t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
379                 }
380                 // the inaccessible subtrees were marked manually
381                 checkMarks(t, true)
382                 errors = errors[0:0]
383
384                 // 4) capture errors, stop after first error.
385                 // mark respective subtrees manually
386                 markTree(tree.entries[1])
387                 markTree(tree.entries[3])
388                 // correct double-marking of directory itself
389                 tree.entries[1].mark--
390                 tree.entries[3].mark--
391                 clear = false // error will stop processing
392                 err = filepath.Walk(tree.name, markFn)
393                 if err == nil {
394                         t.Fatalf("expected error return from Walk")
395                 }
396                 if len(errors) != 1 {
397                         t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
398                 }
399                 // the inaccessible subtrees were marked manually
400                 checkMarks(t, false)
401                 errors = errors[0:0]
402
403                 // restore permissions
404                 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
405                 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
406         }
407
408         // cleanup
409         if err := os.RemoveAll(tree.name); err != nil {
410                 t.Errorf("removeTree: %v", err)
411         }
412 }
413
414 var basetests = []PathTest{
415         {"", "."},
416         {".", "."},
417         {"/.", "."},
418         {"/", "/"},
419         {"////", "/"},
420         {"x/", "x"},
421         {"abc", "abc"},
422         {"abc/def", "def"},
423         {"a/b/.x", ".x"},
424         {"a/b/c.", "c."},
425         {"a/b/c.x", "c.x"},
426 }
427
428 var winbasetests = []PathTest{
429         {`c:\`, `\`},
430         {`c:.`, `.`},
431         {`c:\a\b`, `b`},
432         {`c:a\b`, `b`},
433         {`c:a\b\c`, `c`},
434         {`\\host\share\`, `\`},
435         {`\\host\share\a`, `a`},
436         {`\\host\share\a\b`, `b`},
437 }
438
439 func TestBase(t *testing.T) {
440         tests := basetests
441         if runtime.GOOS == "windows" {
442                 // make unix tests work on windows
443                 for i := range tests {
444                         tests[i].result = filepath.Clean(tests[i].result)
445                 }
446                 // add windows specific tests
447                 tests = append(tests, winbasetests...)
448         }
449         for _, test := range tests {
450                 if s := filepath.Base(test.path); s != test.result {
451                         t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result)
452                 }
453         }
454 }
455
456 var dirtests = []PathTest{
457         {"", "."},
458         {".", "."},
459         {"/.", "/"},
460         {"/", "/"},
461         {"////", "/"},
462         {"/foo", "/"},
463         {"x/", "x"},
464         {"abc", "."},
465         {"abc/def", "abc"},
466         {"a/b/.x", "a/b"},
467         {"a/b/c.", "a/b"},
468         {"a/b/c.x", "a/b"},
469 }
470
471 var windirtests = []PathTest{
472         {`c:\`, `c:\`},
473         {`c:.`, `c:.`},
474         {`c:\a\b`, `c:\a`},
475         {`c:a\b`, `c:a`},
476         {`c:a\b\c`, `c:a\b`},
477         {`\\host\share\`, `\\host\share\`},
478         {`\\host\share\a`, `\\host\share\`},
479         {`\\host\share\a\b`, `\\host\share\a`},
480 }
481
482 func TestDir(t *testing.T) {
483         tests := dirtests
484         if runtime.GOOS == "windows" {
485                 // make unix tests work on windows
486                 for i := range tests {
487                         tests[i].result = filepath.Clean(tests[i].result)
488                 }
489                 // add windows specific tests
490                 tests = append(tests, windirtests...)
491         }
492         for _, test := range tests {
493                 if s := filepath.Dir(test.path); s != test.result {
494                         t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result)
495                 }
496         }
497 }
498
499 type IsAbsTest struct {
500         path  string
501         isAbs bool
502 }
503
504 var isabstests = []IsAbsTest{
505         {"", false},
506         {"/", true},
507         {"/usr/bin/gcc", true},
508         {"..", false},
509         {"/a/../bb", true},
510         {".", false},
511         {"./", false},
512         {"lala", false},
513 }
514
515 var winisabstests = []IsAbsTest{
516         {`C:\`, true},
517         {`c\`, false},
518         {`c::`, false},
519         {`c:`, false},
520         {`/`, false},
521         {`\`, false},
522         {`\Windows`, false},
523         {`c:a\b`, false},
524         {`\\host\share\foo`, true},
525         {`//host/share/foo/bar`, true},
526 }
527
528 func TestIsAbs(t *testing.T) {
529         var tests []IsAbsTest
530         if runtime.GOOS == "windows" {
531                 tests = append(tests, winisabstests...)
532                 // All non-windows tests should fail, because they have no volume letter.
533                 for _, test := range isabstests {
534                         tests = append(tests, IsAbsTest{test.path, false})
535                 }
536                 // All non-windows test should work as intended if prefixed with volume letter.
537                 for _, test := range isabstests {
538                         tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
539                 }
540         } else {
541                 tests = isabstests
542         }
543
544         for _, test := range tests {
545                 if r := filepath.IsAbs(test.path); r != test.isAbs {
546                         t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
547                 }
548         }
549 }
550
551 type EvalSymlinksTest struct {
552         // If dest is empty, the path is created; otherwise the dest is symlinked to the path.
553         path, dest string
554 }
555
556 var EvalSymlinksTestDirs = []EvalSymlinksTest{
557         {"test", ""},
558         {"test/dir", ""},
559         {"test/dir/link3", "../../"},
560         {"test/link1", "../test"},
561         {"test/link2", "dir"},
562         {"test/linkabs", "/"},
563 }
564
565 var EvalSymlinksTests = []EvalSymlinksTest{
566         {"test", "test"},
567         {"test/dir", "test/dir"},
568         {"test/dir/../..", "."},
569         {"test/link1", "test"},
570         {"test/link2", "test/dir"},
571         {"test/link1/dir", "test/dir"},
572         {"test/link2/..", "test"},
573         {"test/dir/link3", "."},
574         {"test/link2/link3/test", "test"},
575         {"test/linkabs", "/"},
576 }
577
578 var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
579         {`c:\`, `c:\`},
580 }
581
582 // simpleJoin builds a file name from the directory and path.
583 // It does not use Join because we don't want ".." to be evaluated.
584 func simpleJoin(dir, path string) string {
585         return dir + string(filepath.Separator) + path
586 }
587
588 func TestEvalSymlinks(t *testing.T) {
589         tmpDir, err := ioutil.TempDir("", "evalsymlink")
590         if err != nil {
591                 t.Fatal("creating temp dir:", err)
592         }
593         defer os.RemoveAll(tmpDir)
594
595         // /tmp may itself be a symlink! Avoid the confusion, although
596         // it means trusting the thing we're testing.
597         tmpDir, err = filepath.EvalSymlinks(tmpDir)
598         if err != nil {
599                 t.Fatal("eval symlink for tmp dir:", err)
600         }
601
602         // Create the symlink farm using relative paths.
603         for _, d := range EvalSymlinksTestDirs {
604                 var err error
605                 path := simpleJoin(tmpDir, d.path)
606                 if d.dest == "" {
607                         err = os.Mkdir(path, 0755)
608                 } else {
609                         if runtime.GOOS != "windows" {
610                                 err = os.Symlink(d.dest, path)
611                         }
612                 }
613                 if err != nil {
614                         t.Fatal(err)
615                 }
616         }
617
618         var tests []EvalSymlinksTest
619         if runtime.GOOS == "windows" {
620                 for _, d := range EvalSymlinksTests {
621                         if d.path == d.dest {
622                                 // will test only real files and directories
623                                 tests = append(tests, d)
624                                 // test "canonical" names
625                                 d2 := EvalSymlinksTest{
626                                         path: strings.ToUpper(d.path),
627                                         dest: d.dest,
628                                 }
629                                 tests = append(tests, d2)
630                         }
631                 }
632         } else {
633                 tests = EvalSymlinksTests
634         }
635
636         // Evaluate the symlink farm.
637         for _, d := range tests {
638                 path := simpleJoin(tmpDir, d.path)
639                 dest := simpleJoin(tmpDir, d.dest)
640                 if filepath.IsAbs(d.dest) {
641                         dest = d.dest
642                 }
643                 if p, err := filepath.EvalSymlinks(path); err != nil {
644                         t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
645                 } else if filepath.Clean(p) != filepath.Clean(dest) {
646                         t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
647                 }
648         }
649 }
650
651 // Test directories relative to temporary directory.
652 // The tests are run in absTestDirs[0].
653 var absTestDirs = []string{
654         "a",
655         "a/b",
656         "a/b/c",
657 }
658
659 // Test paths relative to temporary directory. $ expands to the directory.
660 // The tests are run in absTestDirs[0].
661 // We create absTestDirs first.
662 var absTests = []string{
663         ".",
664         "b",
665         "../a",
666         "../a/b",
667         "../a/b/./c/../../.././a",
668         "$",
669         "$/.",
670         "$/a/../a/b",
671         "$/a/b/c/../../.././a",
672 }
673
674 func TestAbs(t *testing.T) {
675         oldwd, err := os.Getwd()
676         if err != nil {
677                 t.Fatal("Getwd failed: ", err)
678         }
679         defer os.Chdir(oldwd)
680
681         root, err := ioutil.TempDir("", "TestAbs")
682         if err != nil {
683                 t.Fatal("TempDir failed: ", err)
684         }
685         defer os.RemoveAll(root)
686
687         err = os.Chdir(root)
688         if err != nil {
689                 t.Fatal("chdir failed: ", err)
690         }
691
692         for _, dir := range absTestDirs {
693                 err = os.Mkdir(dir, 0777)
694                 if err != nil {
695                         t.Fatal("Mkdir failed: ", err)
696                 }
697         }
698
699         err = os.Chdir(absTestDirs[0])
700         if err != nil {
701                 t.Fatal("chdir failed: ", err)
702         }
703
704         for _, path := range absTests {
705                 path = strings.Replace(path, "$", root, -1)
706                 info, err := os.Stat(path)
707                 if err != nil {
708                         t.Errorf("%s: %s", path, err)
709                         continue
710                 }
711
712                 abspath, err := filepath.Abs(path)
713                 if err != nil {
714                         t.Errorf("Abs(%q) error: %v", path, err)
715                         continue
716                 }
717                 absinfo, err := os.Stat(abspath)
718                 if err != nil || !os.SameFile(absinfo, info) {
719                         t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
720                 }
721                 if !filepath.IsAbs(abspath) {
722                         t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
723                 }
724                 if filepath.IsAbs(path) && abspath != filepath.Clean(path) {
725                         t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
726                 }
727         }
728 }
729
730 type RelTests struct {
731         root, path, want string
732 }
733
734 var reltests = []RelTests{
735         {"a/b", "a/b", "."},
736         {"a/b/.", "a/b", "."},
737         {"a/b", "a/b/.", "."},
738         {"./a/b", "a/b", "."},
739         {"a/b", "./a/b", "."},
740         {"ab/cd", "ab/cde", "../cde"},
741         {"ab/cd", "ab/c", "../c"},
742         {"a/b", "a/b/c/d", "c/d"},
743         {"a/b", "a/b/../c", "../c"},
744         {"a/b/../c", "a/b", "../b"},
745         {"a/b/c", "a/c/d", "../../c/d"},
746         {"a/b", "c/d", "../../c/d"},
747         {"a/b/c/d", "a/b", "../.."},
748         {"a/b/c/d", "a/b/", "../.."},
749         {"a/b/c/d/", "a/b", "../.."},
750         {"a/b/c/d/", "a/b/", "../.."},
751         {"../../a/b", "../../a/b/c/d", "c/d"},
752         {"/a/b", "/a/b", "."},
753         {"/a/b/.", "/a/b", "."},
754         {"/a/b", "/a/b/.", "."},
755         {"/ab/cd", "/ab/cde", "../cde"},
756         {"/ab/cd", "/ab/c", "../c"},
757         {"/a/b", "/a/b/c/d", "c/d"},
758         {"/a/b", "/a/b/../c", "../c"},
759         {"/a/b/../c", "/a/b", "../b"},
760         {"/a/b/c", "/a/c/d", "../../c/d"},
761         {"/a/b", "/c/d", "../../c/d"},
762         {"/a/b/c/d", "/a/b", "../.."},
763         {"/a/b/c/d", "/a/b/", "../.."},
764         {"/a/b/c/d/", "/a/b", "../.."},
765         {"/a/b/c/d/", "/a/b/", "../.."},
766         {"/../../a/b", "/../../a/b/c/d", "c/d"},
767         {".", "a/b", "a/b"},
768         {".", "..", ".."},
769
770         // can't do purely lexically
771         {"..", ".", "err"},
772         {"..", "a", "err"},
773         {"../..", "..", "err"},
774         {"a", "/a", "err"},
775         {"/a", "a", "err"},
776 }
777
778 var winreltests = []RelTests{
779         {`C:a\b\c`, `C:a/b/d`, `..\d`},
780         {`C:\`, `D:\`, `err`},
781         {`C:`, `D:`, `err`},
782 }
783
784 func TestRel(t *testing.T) {
785         tests := append([]RelTests{}, reltests...)
786         if runtime.GOOS == "windows" {
787                 for i := range tests {
788                         tests[i].want = filepath.FromSlash(tests[i].want)
789                 }
790                 tests = append(tests, winreltests...)
791         }
792         for _, test := range tests {
793                 got, err := filepath.Rel(test.root, test.path)
794                 if test.want == "err" {
795                         if err == nil {
796                                 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
797                         }
798                         continue
799                 }
800                 if err != nil {
801                         t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
802                 }
803                 if got != test.want {
804                         t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
805                 }
806         }
807 }
808
809 type VolumeNameTest struct {
810         path string
811         vol  string
812 }
813
814 var volumenametests = []VolumeNameTest{
815         {`c:/foo/bar`, `c:`},
816         {`c:`, `c:`},
817         {``, ``},
818         {`\\\host`, ``},
819         {`\\\host\`, ``},
820         {`\\\host\share`, ``},
821         {`\\\host\\share`, ``},
822         {`\\host`, ``},
823         {`//host`, ``},
824         {`\\host\`, ``},
825         {`//host/`, ``},
826         {`\\host\share`, `\\host\share`},
827         {`//host/share`, `//host/share`},
828         {`\\host\share\`, `\\host\share`},
829         {`//host/share/`, `//host/share`},
830         {`\\host\share\foo`, `\\host\share`},
831         {`//host/share/foo`, `//host/share`},
832         {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
833         {`//host/share//foo///bar////baz`, `//host/share`},
834         {`\\host\share\foo\..\bar`, `\\host\share`},
835         {`//host/share/foo/../bar`, `//host/share`},
836 }
837
838 func TestVolumeName(t *testing.T) {
839         if runtime.GOOS != "windows" {
840                 return
841         }
842         for _, v := range volumenametests {
843                 if vol := filepath.VolumeName(v.path); vol != v.vol {
844                         t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol)
845                 }
846         }
847 }