blob: eae822bb7b8dd6587bd9a4be2c5916869070ab16 [file] [log] [blame]
// Copyright 2016 The Upspin 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 path
import (
"testing"
"upspin.io/upspin"
)
type parseTest struct {
path upspin.PathName
cleanPath upspin.PathName
isRoot bool
}
var goodParseTests = []parseTest{
{"u@google.com", "u@google.com/", true},
{"u@google.com/", "u@google.com/", true},
{"u@google.com///", "u@google.com/", true},
{"u@google.com/a", "u@google.com/a", false},
{"u@google.com/a////", "u@google.com/a", false},
{"u@google.com/a///b/c/d/", "u@google.com/a/b/c/d", false},
// Dot.
{"u@google.com/.", "u@google.com/", true},
{"u@google.com/a/../b", "u@google.com/b", false},
{"u@google.com/./a///b/./c/d/./.", "u@google.com/a/b/c/d", false},
// Dot-Dot.
{"u@google.com/..", "u@google.com/", true},
{"u@google.com/a/../b", "u@google.com/b", false},
{"u@google.com/../a///b/../c/d/..", "u@google.com/a/c", false},
}
func TestParse(t *testing.T) {
for _, test := range goodParseTests {
p, err := Parse(test.path)
if err != nil {
t.Errorf("%q: unexpected error %v", test.path, err)
continue
}
if p.path != test.cleanPath {
t.Errorf("%q: expected %v got %v", test.path, test.cleanPath, p.path)
continue
}
if test.isRoot != p.IsRoot() {
t.Errorf("%q: expected IsRoot %v, got %v", test.path, test.isRoot, p.IsRoot())
}
}
}
func TestCountMallocs(t *testing.T) {
if testing.Short() {
t.Skip("skipping malloc count in short mode")
}
parse := func() {
Parse("u@google.com/a/b/c/d/e/f/g")
}
mallocs := testing.AllocsPerRun(100, parse)
if mallocs != 0 {
t.Errorf("got %d allocs, want 0", int64(mallocs))
}
}
var badParseTests = []upspin.PathName{
"u@x/a/b", // User name too short.
"user/a/b", // Invalid user name.
"user@domain.com*", // Spurious character in user name.
"user@domain.com*/a/b", // Spurious character in user name.
}
func TestBadParse(t *testing.T) {
for _, test := range badParseTests {
_, err := Parse(test)
if err == nil {
t.Errorf("%q: got no error, expected one", test)
continue
}
}
}
var userTests = []upspin.PathName{
"a@b.co/",
"a@b.co/a",
"a@b.co/a/b/c",
"a@b.co/a/b/c/d",
}
func TestUser(t *testing.T) {
const want = "a@b.co"
for _, test := range userTests {
p, err := Parse(test)
if err != nil {
t.Errorf("error parsing %q: %v\n", test, err)
}
user := p.User()
if user != want {
t.Errorf("User(%q)=%q; expected %q", test, user, want)
}
}
}
type nelemTest struct {
path upspin.PathName
nelem int
}
var nelemTests = []nelemTest{
{"a@b.co", 0},
{"a@b.co/", 0},
{"a@b.co/a", 1},
{"a@b.co/a/b", 2},
}
func TestNelem(t *testing.T) {
for _, test := range nelemTests {
p, err := Parse(test.path)
if err != nil {
t.Errorf("error parsing %q: %v\n", test, err)
}
nelem := p.NElem()
if nelem != test.nelem {
t.Errorf("NElem(%q)=%d; expected %d", test, nelem, test.nelem)
}
}
}
type pathTestWithCount struct {
path upspin.PathName
count int
expect upspin.PathName
}
var elemTests = []pathTestWithCount{
{"a@b.co/a/b/c", 0, "a"},
{"a@b.co/a/b/c", 1, "b"},
{"a@b.co/a/b/c", 2, "c"},
{"a@b.co/a", 0, "a"},
}
func TestElem(t *testing.T) {
for _, test := range elemTests {
p, err := Parse(test.path)
if err != nil {
t.Errorf("error parsing %q: %v\n", test, err)
}
elem := p.Elem(test.count)
if elem != string(test.expect) {
t.Errorf("Elem(%q, %d)=%q; expected %q", test.path, test.count, elem, test.expect)
}
}
}
type prefixTest struct {
root upspin.PathName
path upspin.PathName
hasPrefix bool
}
var prefixTests = []prefixTest{
{"u@google.com/", "u@google.com/", true},
{"u@google.com/a", "u@google.com/a", true},
{"u@google.com/a", "u@google.com/a/b", true},
{"user@domain.com/", "user@domain.com/dir/fileB.txt", true},
{"u@google.com/a/b/c", "u@google.com/a/b/c/d", true},
{"u@google.com/", "x@google.com/", false},
{"u@google.com/a", "u@google.com/b", false},
{"u@google.com/a/b", "u@google.com/a", false},
}
func TestHasPrefix(t *testing.T) {
for _, test := range prefixTests {
p, err := Parse(test.path)
if err != nil {
t.Errorf("%q: unexpected error %v", test.path, err)
continue
}
r, err := Parse(test.root)
if err != nil {
t.Errorf("%q: unexpected error %v", test.root, err)
continue
}
// Validate the input - they must be canonical.
if p.Path() != test.path {
t.Errorf("path %q not canonical", test.path)
continue
}
if r.Path() != test.root {
t.Errorf("root %q not canonical", test.root)
continue
}
if hasPrefix := p.HasPrefix(r); hasPrefix != test.hasPrefix {
t.Errorf("HasPrefix(%q, %q)=%t; expected %t", test.path, test.root, hasPrefix, test.hasPrefix)
}
}
}
// The join and clean tests are based on those in Go's path/path_test.go.
type JoinTest struct {
elem []string
path upspin.PathName
}
var jointests = []JoinTest{
// zero parameters
{[]string{}, ""},
// one parameter
{[]string{""}, ""},
{[]string{"a"}, "a"},
// two parameters
{[]string{"a", "b"}, "a/b"},
{[]string{"a", ""}, "a"},
{[]string{"", "b"}, "b"},
{[]string{"/", "a"}, "/a"},
{[]string{"/", ""}, "/"},
{[]string{"a/", "b"}, "a/b"},
{[]string{"a/", ""}, "a"},
{[]string{"", ""}, ""},
}
// join takes a []string and passes it to Join.
func join(args ...string) upspin.PathName {
if len(args) == 0 {
return Join("")
}
return Join(upspin.PathName(args[0]), args[1:]...)
}
func TestJoin(t *testing.T) {
for _, test := range jointests {
if p := join(test.elem...); p != test.path {
t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path)
}
}
}
type pathTest struct {
path, result upspin.PathName
}
var cleantests = []pathTest{
// Already clean
{"", "."},
{"abc", "abc"},
{"abc/def", "abc/def"},
{"a/b/c", "a/b/c"},
{".", "."},
{"..", ".."},
{"../..", "../.."},
{"../../abc", "../../abc"},
{"/abc", "/abc"},
{"/", "/"},
// 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"},
// Combinations
{"abc/./../def", "def"},
{"abc//./../def", "def"},
{"abc/../../././../def", "../../def"},
// Now some real Upspin paths.
{"joe@blow.com", "joe@blow.com/"}, // User root always has a trailing slash.
{"joe@blow.com/", "joe@blow.com/"},
{"joe@blow.com/..", "joe@blow.com/"},
{"joe@blow.com/../", "joe@blow.com/"},
{"joe@blow.com/a/b/../b/c", "joe@blow.com/a/b/c"},
}
func TestClean(t *testing.T) {
for _, test := range cleantests {
if s := Clean(test.path); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
}
if s := Clean(test.result); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
}
}
}
type compareTest struct {
path1, path2 upspin.PathName
expect int
}
var compareTests = []compareTest{
// Some the same
{"joe@bar.com", "joe@bar.com", 0},
{"joe@bar.com/", "joe@bar.com", 0},
{"joe@bar.com/", "joe@bar.com/", 0},
{"joe@bar.com/a/b/c", "joe@bar.com/a/b/c", 0},
// Same domain sorts by user.
{"joe@bar.com", "adam@bar.com", 1},
{"joe@bar.com/a/b/c", "adam@bar.com/a/b/c", 1},
{"adam@bar.com", "joe@bar.com", -1},
{"adam@bar.com/a/b/c", "joe@bar.com/a/b/c", -1},
// Different paths.
{"joe@bar.com/a/b/c", "joe@bar.com/a/b/d", -1},
{"joe@bar.com/a/b/d", "joe@bar.com/a/b/c", 1},
// Different length paths.
{"joe@bar.com/a/b/c", "joe@bar.com/a/b/c/d", -1},
{"joe@bar.com/a/b/c/d", "joe@bar.com/a/b/c", 1},
}
func TestCompare(t *testing.T) {
for _, test := range compareTests {
p1, err := Parse(test.path1)
if err != nil {
t.Fatalf("%s: %s\n", test.path1, err)
}
p2, err := Parse(test.path2)
if err != nil {
t.Fatalf("%s: %s\n", test.path2, err)
}
if got := p1.Compare(p2); got != test.expect {
t.Errorf("Compare(%q, %q) = %d; expected %d", test.path1, test.path2, got, test.expect)
}
// Verify for compare too.
if (p1.Compare(p2) == 0) != p1.Equal(p2) {
t.Errorf("Equal(%q, %q) = %t; expected otherwise", test.path1, test.path2, p1.Equal(p2))
}
}
}
var dropPathTests = []pathTestWithCount{
{"a@b.co/a/b", 1, "a@b.co/a"},
{"a@b.co/a/b", 2, "a@b.co/"},
// Won't go past the root.
{"a@b.co/a/b", 3, "a@b.co/"},
// Multiple slashes are OK.
{"a@b.co/a/b///", 1, "a@b.co/a"},
{"a@b.co///a//////b///c/////", 2, "a@b.co/a"},
// No slash, but return with one.
{"a@b.co", 1, "a@b.co/"},
{"a@b.co", 100, "a@b.co/"},
}
func TestDropPath(t *testing.T) {
for _, test := range dropPathTests {
got := DropPath(test.path, test.count)
if got != test.expect {
t.Errorf("DropPath(%q, %d) = %q; expected %q", test.path, test.count, got, test.expect)
}
}
}
var firstPathTests = []pathTestWithCount{
{"a@b.co/a/b/c/d", 0, "a@b.co/"},
{"a@b.co/a/b/c/d", 1, "a@b.co/a"},
{"a@b.co/a/b/c/d", 2, "a@b.co/a/b"},
{"a@b.co/a/b/c/d/", 3, "a@b.co/a/b/c"},
{"a@b.co/a/b/c/d/", 4, "a@b.co/a/b/c/d"},
{"a@b.co/a/b/c/d", 10, "a@b.co/a/b/c/d"},
{"a@b.co/a/b/c/d/", 10, "a@b.co/a/b/c/d"},
// Multiple slashes are OK.
{"a@b.co/a/b///", 1, "a@b.co/a"},
{"a@b.co///a//////b///c/////", 2, "a@b.co/a/b"},
// No slash, but return with one.
{"a@b.co", 1, "a@b.co/"},
{"a@b.co", 100, "a@b.co/"},
}
func TestFirstPath(t *testing.T) {
for _, test := range firstPathTests {
got := FirstPath(test.path, test.count)
if got != test.expect {
t.Errorf("FirstPath(%q, %d) = %q; expected %q", test.path, test.count, got, test.expect)
}
}
}