| // Copyright 2017 The Goma Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package winpath |
| |
| import ( |
| "reflect" |
| "testing" |
| ) |
| |
| func TestIsAbs(t *testing.T) { |
| for _, tc := range []struct { |
| path string |
| isAbs bool |
| }{ |
| {`C:\`, true}, |
| {`c\`, false}, |
| {`c::`, false}, |
| {`c:`, false}, |
| {`/`, false}, |
| {`\`, false}, |
| {`\Windows`, false}, |
| {`c:a\b`, false}, |
| {`c:\a\b`, true}, |
| {`c:/a/b`, true}, |
| // TODO: support UNC? |
| } { |
| got := IsAbs(tc.path) |
| if got != tc.isAbs { |
| t.Errorf("IsAbs(%q)=%t; want=%t", tc.path, got, tc.isAbs) |
| } |
| } |
| } |
| |
| func TestBase(t *testing.T) { |
| for _, tc := range []struct { |
| path, result string |
| }{ |
| {"", "."}, |
| {".", "."}, |
| {"/.", "."}, |
| {"/", `\`}, |
| {"////", `\`}, |
| {"x/", "."}, // different from filepath.Base |
| {"abc", "abc"}, |
| {"abc/def", "def"}, |
| {"a/b/.x", ".x"}, |
| {"a/b/c.", "c."}, |
| {"a/b/c.x", "c.x"}, |
| |
| {`c:\`, `\`}, |
| {`c:.`, `.`}, |
| {`c:\a\b`, `b`}, |
| {`c:a\b`, `b`}, |
| {`c:a\b\c`, `c`}, |
| // TODO: support UNC? |
| } { |
| got := Base(tc.path) |
| if got != tc.result { |
| t.Errorf("Base(%q)=%q; want=%q", tc.path, got, tc.result) |
| } |
| } |
| } |
| |
| func TestDir(t *testing.T) { |
| for _, tc := range []struct { |
| path, result string |
| }{ |
| {"", "."}, |
| {".", "."}, |
| {"/.", `\`}, |
| {"/", `\`}, |
| {"////", `\`}, |
| {"/foo", `\`}, |
| {"x/", "x"}, |
| {"abc", "."}, |
| {"abc/def", "abc"}, |
| {"a/b/.x", `a\b`}, |
| {"a/b/c.", `a\b`}, |
| {"a/b/c.x", `a\b`}, |
| |
| {`c:\`, `c:\`}, |
| {`c:.`, `c:.`}, |
| {`c:\a\b`, `c:\a`}, |
| {`c:a\b`, `c:a`}, |
| {`c:a\b\c`, `c:a\b`}, |
| // TODO: support UNC |
| } { |
| got := Dir(tc.path) |
| if got != tc.result { |
| t.Errorf("Dir(%q)=%q; want=%q", tc.path, got, tc.result) |
| } |
| } |
| } |
| |
| func TestJoin(t *testing.T) { |
| for _, tc := range []struct { |
| elem []string |
| path string |
| }{ |
| // zero parameters. |
| {nil, ""}, |
| |
| // one parameter |
| {[]string{""}, ``}, |
| {[]string{"/"}, `\`}, |
| {[]string{"a"}, "a"}, |
| |
| // two parameters |
| {[]string{"a", "b"}, `a\b`}, |
| {[]string{"a", ""}, "a"}, |
| {[]string{"", "b"}, "b"}, |
| {[]string{"/", "a"}, `\a`}, |
| {[]string{"/", "a/b"}, `\a\b`}, |
| {[]string{"/", ""}, `\`}, |
| {[]string{"//", "a"}, `\a`}, |
| {[]string{"/a", "b"}, `\a\b`}, |
| {[]string{"a/", "b"}, `a\b`}, |
| {[]string{"a/", ""}, "a"}, |
| {[]string{"", ""}, ""}, |
| // different from filepath.Join, it won't clean \..\. |
| {[]string{"a", "../b"}, `a\..\b`}, |
| |
| // tree parameters |
| {[]string{"/", "a", "b"}, `\a\b`}, |
| |
| {[]string{`directory`, `file`}, `directory\file`}, |
| {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, |
| {[]string{`C:\Windows\`, ``}, `C:\Windows`}, |
| {[]string{`C:\`, `Windows`}, `C:\Windows`}, |
| {[]string{`C:`, `a`}, `C:a`}, |
| {[]string{`C:`, `a\b`}, `C:a\b`}, |
| {[]string{`C:`, `a`, `b`}, `C:a\b`}, |
| {[]string{`C:.`, `a`}, `C:a`}, |
| {[]string{`C:a`, `b`}, `C:a\b`}, |
| {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, |
| |
| // TODO: UNC path. |
| |
| {[]string{`\`}, `\`}, |
| {[]string{`\`, ``}, `\`}, |
| {[]string{`\`, `a`}, `\a`}, |
| {[]string{`\\`, `a`}, `\a`}, |
| {[]string{`\`, `a`, `b`}, `\a\b`}, |
| {[]string{`\\`, `a`, `b`}, `\a\b`}, |
| {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, |
| {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, |
| {[]string{`\\a\`, `b`, `c`}, `\a\b\c`}, |
| // different from filepath.Join, it won't clean \..\. |
| {[]string{`a\..`, `b`}, `a\..\b`}, |
| } { |
| got := Join(tc.elem...) |
| if got != tc.path { |
| t.Errorf("Join(%q)=%q; want=%q", tc.elem, got, tc.path) |
| } |
| } |
| } |
| |
| func TestRel(t *testing.T) { |
| for _, tc := range []struct { |
| root, path, want string |
| }{ |
| {"a/b", "a/b", "."}, |
| {"a/b/.", "a/b", "."}, |
| {"a/b", "a/b/.", "."}, |
| {"./a/b", "a/b", "."}, |
| {"a/b", "./a/b", "."}, |
| {"ab/cd", "ab/cde", `..\cde`}, |
| {"ab/cd", "ab/c", `..\c`}, |
| {"a/b", "a/b/c/d", `c\d`}, |
| {"a/b", "a/b/../c", `..\c`}, |
| {"a/b/../c", "a/b", `..\b`}, |
| {"a/b/c", "a/c/d", `..\..\c\d`}, |
| {"a/b", "c/d", `..\..\c\d`}, |
| {"a/b/c/d", "a/b", `..\..`}, |
| {"a/b/c/d", "a/b/", `..\..`}, |
| {"a/b/c/d/", "a/b", `..\..`}, |
| {"a/b/c/d/", "a/b/", `..\..`}, |
| {"../../a/b", "../../a/b/c/d", `c\d`}, |
| {"/a/b", "/a/b", "."}, |
| {"/a/b/.", "/a/b", "."}, |
| {"/a/b", "/a/b/.", "."}, |
| {"/ab/cd", "/ab/cde", `..\cde`}, |
| {"/ab/cd", "/ab/c", `..\c`}, |
| {"/a/b", "/a/b/c/d", `c\d`}, |
| {"/a/b", "/a/b/../c", `..\c`}, |
| {"/a/b/../c", "/a/b", `..\b`}, |
| {"/a/b/c", "/a/c/d", `..\..\c\d`}, |
| {"/a/b", "/c/d", `..\..\c\d`}, |
| {"/a/b/c/d", "/a/b", `..\..`}, |
| {"/a/b/c/d", "/a/b/", `..\..`}, |
| {"/a/b/c/d/", "/a/b", `..\..`}, |
| {"/a/b/c/d/", "/a/b/", `..\..`}, |
| {"/../../a/b", "/../../a/b/c/d", `c\d`}, |
| {".", "a/b", `a\b`}, |
| {".", "..", ".."}, |
| |
| {`C:a\b\c`, `C:a/b/d`, `..\d`}, |
| {`C:\Projects`, `C:\Projects\src`, `src`}, |
| {`C:\Projects`, `C:\Projects`, `.`}, |
| } { |
| got, err := Rel(tc.root, tc.path) |
| if err != nil || got != tc.want { |
| t.Errorf("Rel(%q, %q)=%q, %v; want %q, nil", tc.root, tc.path, got, err, tc.want) |
| } |
| } |
| |
| // can't do purely lexically |
| for _, tc := range []struct { |
| root, path string |
| }{ |
| {"..", "."}, |
| {"..", "a"}, |
| {"../..", ".."}, |
| {"a", "/a"}, |
| {"/a", "a"}, |
| |
| {`C:\`, `D:\`}, |
| {`C:`, `D:`}, |
| {`C:\Projects`, `c:\projects\src`}, // case mismatch |
| {`C:\Projects`, `c:\projects`}, // case mismatch |
| {`C:\Projects\a\..`, `c:\projects`}, // different from filepath.Rel |
| } { |
| got, err := Rel(tc.root, tc.path) |
| if err == nil { |
| t.Errorf("Rel(%q, %q)=%q, nil; want error", tc.root, tc.path, got) |
| } |
| } |
| } |
| |
| func TestClean(t *testing.T) { |
| for _, tc := range []struct { |
| path, result string |
| }{ |
| // https://golang.org/src/path/filepath/path_test.go |
| |
| // Already clean |
| {"abc", "abc"}, |
| {"abc/def", `abc\def`}, |
| {"a/b/c", `a\b\c`}, |
| {".", "."}, |
| {"..", ".."}, |
| {"../..", `..\..`}, |
| {"../../abc", `..\..\abc`}, |
| {"/abc", `\abc`}, |
| {"/", `\`}, |
| |
| // Empty is current dir |
| {"", "."}, |
| |
| // Remove trailing slash |
| {"abc/", "abc"}, |
| {"abc/def/", `abc\def`}, |
| {"a/b/c/", `a\b\c`}, |
| {"./", "."}, |
| {"../", ".."}, |
| {"../../", `..\..`}, |
| {"/abc/", `\abc`}, |
| |
| // Remove doubled slash |
| {"abc//def//ghi", `abc\def\ghi`}, |
| {"//abc", `\abc`}, |
| {"///abc", `\abc`}, |
| {"//abc//", `\abc`}, |
| {"abc//", "abc"}, |
| |
| // Remove . elements |
| {"abc/./def", `abc\def`}, |
| {"/./abc/def", `\abc\def`}, |
| {"abc/.", "abc"}, |
| |
| // Remove .. elements |
| {"abc/def/ghi/../jkl", `abc\def\jkl`}, |
| {"abc/def/../ghi/../jkl", `abc\jkl`}, |
| {"abc/def/..", "abc"}, |
| {"abc/def/../..", "."}, |
| {"/abc/def/../..", `\`}, |
| {"abc/def/../../..", ".."}, |
| {"/abc/def/../../..", `\`}, |
| {"abc/def/../../../ghi/jkl/../../../mno", `..\..\mno`}, |
| {"/../abc", `\abc`}, |
| |
| // Combinations |
| {"abc/./../def", "def"}, |
| {"abc//./../def", "def"}, |
| {"abc/../../././../def", `..\..\def`}, |
| |
| // win path case |
| {`c:`, `c:.`}, |
| {`c:\`, `c:\`}, |
| {`c:\abc`, `c:\abc`}, |
| {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, |
| {`c:\abc\def\..\..`, `c:\`}, |
| {`c:\..\abc`, `c:\abc`}, |
| {`\`, `\`}, |
| |
| // TODO: UNC path. |
| } { |
| got := Clean(tc.path) |
| if got != tc.result { |
| t.Errorf("Clean(%q)=%q; want=%q", tc.path, got, tc.result) |
| } |
| } |
| |
| } |
| |
| func TestSplitElem(t *testing.T) { |
| for _, tc := range []struct { |
| path string |
| elem []string |
| }{ |
| {"", nil}, |
| |
| {"/", []string{`\`}}, |
| {`\`, []string{`\`}}, |
| {"a", []string{"a"}}, |
| {".", []string{"."}}, |
| |
| {"/.", []string{`\`, "."}}, |
| {`\.`, []string{`\`, "."}}, |
| {"////", []string{`\`}}, |
| {`\\\\`, []string{`\`}}, |
| {"////.", []string{`\`, "."}}, |
| {`\\\\.`, []string{`\`, "."}}, |
| {".////", []string{"."}}, |
| {`.\\\\`, []string{"."}}, |
| |
| {"a/b", []string{"a", "b"}}, |
| {`a\b`, []string{"a", "b"}}, |
| {"/a/b", []string{`\`, "a", "b"}}, |
| {`\a\b`, []string{`\`, "a", "b"}}, |
| {"a/b/.", []string{"a", "b", "."}}, |
| {`a\b\.`, []string{"a", "b", "."}}, |
| {"a/b/", []string{"a", "b", "."}}, |
| {`a\b\`, []string{"a", "b", "."}}, |
| |
| {"a//b", []string{"a", "b"}}, |
| {`a\\/b`, []string{"a", "b"}}, |
| {"//a", []string{`\`, "a"}}, |
| {`\\a`, []string{`\`, "a"}}, |
| {"a//", []string{"a", "."}}, |
| {`a\\`, []string{"a", "."}}, |
| {"a//.", []string{"a", "."}}, |
| {`a\\/.`, []string{"a", "."}}, |
| {"a/./b", []string{"a", "b"}}, |
| {`a\.\b`, []string{"a", "b"}}, |
| {"a/././b", []string{"a", "b"}}, |
| {`a\.\.\b`, []string{"a", "b"}}, |
| |
| {"a/../b", []string{"a", "..", "b"}}, |
| {`a\..\b`, []string{"a", "..", "b"}}, |
| |
| {"c:/a/b", []string{`c:\`, "a", "b"}}, |
| {`c:\a/b`, []string{`c:\`, "a", "b"}}, |
| {"c:a/b", []string{"c:", "a", "b"}}, |
| {`c:\\\\`, []string{`c:\`}}, |
| } { |
| got := SplitElem(tc.path) |
| if !reflect.DeepEqual(got, tc.elem) { |
| t.Errorf("SplitElem(%q)=%q; want=%q", tc.path, got, tc.elem) |
| } |
| } |
| |
| } |
| |
| func TestToPosix(t *testing.T) { |
| for _, tc := range []struct { |
| path string |
| want string |
| }{ |
| {"a", "a"}, |
| {`a\b`, "a/b"}, |
| {"a/b", "a/b"}, |
| {`a\b\c`, "a/b/c"}, |
| {`a/b\c`, "a/b/c"}, |
| {`\a\b\c`, "/a/b/c"}, |
| {`c:\a`, "/a"}, |
| {`c:\a\b\c`, "/a/b/c"}, |
| {`c:a\b\c`, "a/b/c"}, |
| } { |
| got := ToPosix(tc.path) |
| if got != tc.want { |
| t.Errorf("ToPosix(%q)=%q; want=%q", tc.path, got, tc.want) |
| } |
| } |
| } |
| |
| func BenchmarkCleanAlreadyClean(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| Clean("../abc/def/ghi") |
| Clean(`..\abc\def\ghi`) |
| } |
| } |
| |
| func BenchmarkCleanAlreadyCleanAbs(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| Clean("/abc/def/ghi") |
| Clean(`\abc\def\ghi`) |
| Clean("c:/abc/def/ghi") |
| Clean(`c:\abc\def\ghi`) |
| } |
| } |
| |
| func BenchmarkCleanParentDir(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| Clean("abc/def/ghi/../jkl") |
| Clean(`abc\def\ghi\..\jkl`) |
| } |
| } |
| |
| func BenchmarkCleanParentDirAbs(b *testing.B) { |
| for i := 0; i < b.N; i++ { |
| Clean("/abc/def/ghi/../jkl") |
| Clean(`\abc\def\ghi\..\jkl`) |
| Clean("c:/abc/def/ghi/../jkl") |
| Clean(`c:\abc\def\ghi\..\jkl`) |
| } |
| } |