[BACK]Return to path_test.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

File: [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files / path_test.go (download)

Revision 1.9, Sat Jan 18 21:56:09 2020 UTC (2 months, 2 weeks ago) by rillig
Branch: MAIN
Changes since 1.8: +59 -0 lines

pkgtools/pkglint: update to 19.4.4

Changes since 19.4.3:

Packages that still use http in their HOMEPAGE URL generate warnings that
the URL should use https instead.

package pkglint

import (
	"gopkg.in/check.v1"
	"io"
	"os"
	"runtime"
	"strings"
)

func (s *Suite) Test_NewPath(c *check.C) {
	t := s.Init(c)

	t.CheckEquals(NewPath("filename"), NewPath("filename"))
	t.CheckEquals(NewPath("\\"), NewPath("\\"))
	c.Check(NewPath("\\"), check.Not(check.Equals), NewPath("/"))
}

func (s *Suite) Test_Path_String(c *check.C) {
	t := s.Init(c)

	test := func(p Path) {
		t.CheckEquals(p.String(), string(p))
	}

	test("")
	test("filename")
	test("a/b")

	// No normalization takes place here.
	// That's what NewPathSlash is for.
	test("c\\d")
}

func (s *Suite) Test_Path_GoString(c *check.C) {
	t := s.Init(c)

	test := func(p Path, s string) {
		t.CheckEquals(p.GoString(), s)
	}

	test("", "\"\"")
	test("filename", "\"filename\"")
	test("a/b", "\"a/b\"")
	test("c\\d", "\"c\\\\d\"")
}

func (s *Suite) Test_Path_IsEmpty(c *check.C) {
	t := s.Init(c)

	test := func(p Path, isEmpty bool) {
		t.CheckEquals(p.IsEmpty(), isEmpty)
	}

	test("", true)
	test(".", false)
	test("/", false)
}

func (s *Suite) Test_Path_DirClean(c *check.C) {
	t := s.Init(c)

	test := func(p, dir Path) {
		t.CheckEquals(p.DirClean(), dir)
	}

	test("", ".")
	test("././././", ".")
	test("/root", "/")
	test("filename", ".")
	test("dir/filename", "dir")
	test("dir/filename\\with\\backslash", "dir")

	test("././././dir/filename", "dir")
}

func (s *Suite) Test_Path_DirNoClean(c *check.C) {
	t := s.Init(c)

	test := func(p, dir Path) {
		t.CheckEquals(p.DirNoClean(), dir)
	}

	test("", ".")
	test("././././", ".")
	test("/root", "/")
	test("filename", ".")
	test("dir/filename", "dir")
	test("dir/filename\\with\\backslash", "dir")
	test("dir/./file", "dir")
	test("./file", ".")
}

func (s *Suite) Test_Path_Base(c *check.C) {
	t := s.Init(c)

	test := func(p Path, base string) {
		t.CheckEquals(p.Base(), base)
	}

	test("", ".") // That's a bit surprising
	test("././././", ".")
	test("/root", "root")
	test("filename", "filename")
	test("dir/filename", "filename")
	test("dir/filename\\with\\backslash", "filename\\with\\backslash")
}

func (s *Suite) Test_Path_Split(c *check.C) {
	t := s.Init(c)

	test := func(p, dir, base string) {
		actualDir, actualBase := NewPath(p).Split()

		t.CheckDeepEquals(
			[]string{actualDir.String(), actualBase},
			[]string{dir, base})
	}

	test("", "", "")
	test("././././", "././././", "")
	test("/root", "/", "root")
	test("filename", "", "filename")
	test("dir/filename", "dir/", "filename")
	test("dir/filename\\with\\backslash", "dir/", "filename\\with\\backslash")
}

func (s *Suite) Test_Path_Parts(c *check.C) {
	t := s.Init(c)

	test := func(p string, parts ...string) {
		t.CheckDeepEquals(NewPath(p).Parts(), parts)
	}

	// Only the empty path returns an empty slice.
	test("", nil...)

	// The standard cases for relative paths.
	test("relative", "relative")
	test("relative/subdir", "relative", "subdir")
	test("relative////subdir", "relative", "subdir")
	test("relative/..", "relative", "..")
	test("relative/.", "relative")

	// Leading dots are removed when they are followed by something.
	test("./relative", "relative")

	// A path consisting of only dots produces a single dot.
	test("./././.", ".")

	// Slashes at the end are treated like a single dot.
	test("././././", ".")
	test(".///////", ".")

	// Absolute paths have an empty first component.
	test("/", "")
	test("/.", "")
	test("/root", "", "root")

	// The backslash is not a path separator.
	test("dir/filename\\with\\backslash", "dir", "filename\\with\\backslash")
}

func (s *Suite) Test_Path_Count(c *check.C) {
	t := s.Init(c)

	test := func(p string, count int) {
		t.CheckEquals(NewPath(p).Count(), count)
	}

	test("././././", 1)
	test("/root", 2)
	test("filename", 1)
	test("dir/filename", 2)
	test("dir/filename\\with\\backslash", 2)

	// Only the empty path returns an empty slice.
	test("", 0)

	// The standard cases for canonical relative paths.
	test("relative", 1)
	test("relative/subdir", 2)
	test("relative////subdir", 2)
	test("relative/..", 2)
	test("relative/.", 1)

	// A path consisting of only dots produces a single dot.
	test("./././.", 1)

	// Slashes at the end are treated like a single dot.
	test("././././", 1)
	test(".///////", 1)

	// Absolute paths have an empty first component.
	test("/", 1)
	test("/.", 1)
	test("/root", 2)

	// The backslash is not a path separator.
	test("dir/filename\\with\\backslash", 2)
}

func (s *Suite) Test_Path_HasPrefixText(c *check.C) {
	t := s.Init(c)

	test := func(p, prefix string, hasPrefix bool) {
		t.CheckEquals(NewPath(p).HasPrefixText(prefix), hasPrefix)
	}

	test("", "", true)
	test("filename", "", true)
	test("", "x", false)
	test("/root", "/r", true)
	test("/root", "/root", true)
	test("/root", "/root/", false)
	test("/root", "root/", false)
}

func (s *Suite) Test_Path_HasPrefixPath(c *check.C) {
	t := s.Init(c)

	test := func(p, prefix Path, hasPrefix bool) {
		t.CheckEquals(p.HasPrefixPath(prefix), hasPrefix)
	}

	test("", "", true)
	test("filename", "", false)
	test("", "x", false)
	test("/root", "/r", false)
	test("/root", "/root", true)

	// Even though the textual representation of the prefix is longer than
	// the path. The trailing slash marks the path as a directory, and
	// there are only a few cases where the difference matters, such as
	// in rsync and mkdir.
	test("/root", "/root/", true)

	test("/root/", "/root", true)
	test("/root/", "root", false)
	test("/root/subdir", "/root", true)
	test("filename", ".", true)
	test("filename", "./filename", true)
	test("filename", "./file", false)
	test("filename", "./filename/sub", false)
	test("/anything", ".", false)
}

func (s *Suite) Test_Path_ContainsText(c *check.C) {
	t := s.Init(c)

	test := func(p Path, text string, contains bool) {
		t.CheckEquals(p.ContainsText(text), contains)
	}

	test("", "", true)
	test("filename", "", true)
	test("filename", ".", false)
	test("a.b", ".", true)
	test("..", ".", true)
	test("", "x", false)
	test("/root", "/r", true)
	test("/root", "/root", true)
	test("/root", "/root/", false)
	test("/root", "root/", false)
	test("/root", "ro", true)
	test("/root", "ot", true)
}

func (s *Suite) Test_Path_ContainsPath(c *check.C) {
	t := s.Init(c)

	test := func(p, sub Path, contains bool) {
		t.CheckEquals(p.ContainsPath(sub), contains)
	}

	test("", "", true)          // It doesn't make sense to search for empty paths.
	test(".", "", false)        // It doesn't make sense to search for empty paths.
	test("filename", ".", true) // Every relative path contains "." implicitly at the beginning
	test("a.b", ".", true)
	test("..", ".", true)
	test("filename", "", false)
	test("filename", "filename", true)
	test("a/b/c", "a", true)
	test("a/b/c", "b", true)
	test("a/b/c", "c", true)
	test("a/b/c", "a/b", true)
	test("a/b/c", "b/c", true)
	test("a/b/c", "a/b/c", true)
	test("aa/b/c", "a", false)
	test("a/bb/c", "b", false)
	test("a/bb/c", "b/c", false)
	test("mk/fetch/fetch.mk", "mk", true)
	test("category/package/../../wip/mk/../..", "mk", true)

	test("a", "a", true)
	test("a", "b", false)
	test("a", "A", false)
	test("a/b/c", "a", true)
	test("a/b/c", "b", true)
	test("a/b/c", "c", true)
	test("a/b/c", "a/b", true)
	test("a/b/c", "b/c", true)
	test("a/b/c", "a/b/c", true)
	test("aa/bb/cc", "a/b", false)
	test("aa/bb/cc", "a/bb", false)
	test("aa/bb/cc", "aa/b", false)
	test("aa/bb/cc", "aa/bb", true)
	test("aa/bb/cc", "a", false)
	test("aa/bb/cc", "b", false)
	test("aa/bb/cc", "c", false)
}

func (s *Suite) Test_Path_HasSuffixText(c *check.C) {
	t := s.Init(c)

	test := func(p Path, suffix string, has bool) {
		t.CheckEquals(p.HasSuffixText(suffix), has)
	}

	test("", "", true)
	test("a/bb/c", "", true)
	test("a/bb/c", "c", true)
	test("a/bb/c", "/c", true)
	test("a/bb/c", "b/c", true)
	test("aa/b/c", "bb", false)
}

func (s *Suite) Test_Path_HasSuffixPath(c *check.C) {
	t := s.Init(c)

	test := func(p, suffix Path, has bool) {
		t.CheckEquals(p.HasSuffixPath(suffix), has)
	}

	test("", "", true)
	test("a/bb/c", "", false)
	test("a/bb/c", "c", true)
	test("a/bb/c", "/c", false)
	test("a/bb/c", "b/c", false)
	test("aa/b/c", "bb", false)
}

func (s *Suite) Test_Path_HasBase(c *check.C) {
	t := s.Init(c)

	test := func(p Path, suffix string, hasBase bool) {
		t.CheckEquals(p.HasBase(suffix), hasBase)
	}

	test("dir/file", "e", false)
	test("dir/file", "file", true)
	test("dir/file", "file.ext", false)
	test("dir/file", "/file", false)
	test("dir/file", "dir/file", false)
}

func (s *Suite) Test_Path_TrimSuffix(c *check.C) {
	t := s.Init(c)

	test := func(p Path, suffix string, result Path) {
		t.CheckEquals(p.TrimSuffix(suffix), result)
	}

	test("dir/file", "e", "dir/fil")
	test("dir/file", "file", "dir/")
	test("dir/file", "/file", "dir")
	test("dir/file", "dir/file", "")
	test("dir/file", "subdir/file", "dir/file")
}

func (s *Suite) Test_Path_Replace(c *check.C) {
	t := s.Init(c)

	test := func(p Path, from, to string, result Path) {
		t.CheckEquals(p.Replace(from, to), result)
	}

	test("dir/file", "dir", "other", "other/file")
	test("dir/file", "r", "sk", "disk/file")
	test("aaa/file", "a", "sub/", "sub/sub/sub//file")
}

func (s *Suite) Test_Path_JoinClean(c *check.C) {
	t := s.Init(c)

	test := func(p Path, rel RelPath, result Path) {
		t.CheckEquals(p.JoinClean(rel), result)
	}

	test("dir", "file", "dir/file")
	test("dir", "///file", "dir/file")
	test("dir/./../dir/", "///file", "dir/file")
	test("dir", "..", ".")
}

func (s *Suite) Test_Path_JoinNoClean(c *check.C) {
	t := s.Init(c)

	test := func(p Path, rel RelPath, result Path) {
		t.CheckEquals(p.JoinNoClean(rel), result)
	}

	test("dir", "file", "dir/file")
	test("dir", "///file", "dir////file")
	test("dir/./../dir/", "///file", "dir/./../dir/////file")
	test("dir", "..", "dir/..")
	test(".", "sub", "./sub")
}

func (s *Suite) Test_Path_Clean(c *check.C) {
	t := s.Init(c)

	test := func(p, result Path) {
		t.CheckEquals(p.Clean(), result)
	}

	test("", ".")
	test(".", ".")
	test("./././", ".")
	test("a/bb///../c", "a/c")
}

func (s *Suite) Test_Path_CleanDot(c *check.C) {
	t := s.Init(c)

	test := func(p, result Path) {
		t.CheckEquals(p.CleanDot(), result)
	}

	test("", "")
	test(".", ".")
	test("./././", ".")
	test("dir/", "dir/") // TODO: Or maybe "dir/."?
	test("a/bb///../c", "a/bb/../c")
	test("./filename", "filename")
	test("/absolute", "/absolute")
	test("/usr/pkgsrc/wip/package", "/usr/pkgsrc/wip/package")
	test("/usr/pkgsrc/wip/package/../mk/git-package.mk", "/usr/pkgsrc/wip/package/../mk/git-package.mk")
	test("a//b", "a/b")
}

func (s *Suite) Test_Path_CleanPath(c *check.C) {
	t := s.Init(c)

	test := func(from, to Path) {
		t.CheckEquals(from.CleanPath(), to)
	}

	test("simple/path", "simple/path")
	test("/absolute/path", "/absolute/path")

	// Single dot components are removed, unless it's the only component of the path.
	test("./././.", ".")
	test("./././", ".")
	test("dir/multi/././/file", "dir/multi/file")
	test("dir/", "dir")

	test("dir/", "dir")

	// Components like aa/bb/../.. are removed, but not in the initial part of the path,
	// and only if they are not followed by another "..".
	test("dir/../dir/../dir/../dir/subdir/../../Makefile", "dir/../dir/../dir/../Makefile")
	test("111/222/../../333/444/../../555/666/../../777/888/9", "111/222/../../777/888/9")
	test("1/2/3/../../4/5/6/../../7/8/9/../../../../10", "1/2/3/../../4/7/8/9/../../../../10")
	test("cat/pkg.v1/../../cat/pkg.v2/Makefile", "cat/pkg.v1/../../cat/pkg.v2/Makefile")
	test("aa/../../../../../a/b/c/d", "aa/../../../../../a/b/c/d")
	test("aa/bb/../../../../a/b/c/d", "aa/bb/../../../../a/b/c/d")
	test("aa/bb/cc/../../../a/b/c/d", "aa/bb/cc/../../../a/b/c/d")
	test("aa/bb/cc/dd/../../a/b/c/d", "aa/bb/a/b/c/d")
	test("aa/bb/cc/dd/ee/../a/b/c/d", "aa/bb/cc/dd/ee/../a/b/c/d")
	test("../../../../../a/b/c/d", "../../../../../a/b/c/d")
	test("aa/../../../../a/b/c/d", "aa/../../../../a/b/c/d")
	test("aa/bb/../../../a/b/c/d", "aa/bb/../../../a/b/c/d")
	test("aa/bb/cc/../../a/b/c/d", "aa/bb/cc/../../a/b/c/d")
	test("aa/bb/cc/dd/../a/b/c/d", "aa/bb/cc/dd/../a/b/c/d")
	test("aa/../cc/../../a/b/c/d", "aa/../cc/../../a/b/c/d")

	// The initial 2 components of the path are typically category/package, when
	// pkglint is called from the pkgsrc top-level directory.
	// This path serves as the context and therefore is always kept.
	test("aa/bb/../../cc/dd/../../ee/ff", "aa/bb/../../ee/ff")
	test("aa/bb/../../cc/dd/../..", "aa/bb/../..")
	test("aa/bb/cc/dd/../..", "aa/bb")
	test("aa/bb/../../cc/dd/../../ee/ff/buildlink3.mk", "aa/bb/../../ee/ff/buildlink3.mk")
	test("./aa/bb/../../cc/dd/../../ee/ff/buildlink3.mk", "aa/bb/../../ee/ff/buildlink3.mk")

	test("../.", "..")
	test("../././././././.", "..")
	test(".././././././././", "..")

	test(
		"x11/kde-runtime4/../../misc/kdepimlibs4/../../databases/openldap-client/buildlink3.mk",
		"x11/kde-runtime4/../../databases/openldap-client/buildlink3.mk")
}

func (s *Suite) Test_Path_IsAbs(c *check.C) {
	t := s.Init(c)

	test := func(p Path, abs bool) {
		t.CheckEquals(p.IsAbs(), abs)
	}

	test("", false)
	test(".", false)
	test("a/b", false)
	test("/a", true)
	test("C:/", runtime.GOOS == "windows")
	test("c:/", runtime.GOOS == "windows")
}

func (s *Suite) Test_Path_Rel(c *check.C) {
	t := s.Init(c)

	test := func(base Path, other Path, result RelPath) {
		t.CheckEquals(base.Rel(other), result)
	}

	test("a/b/c", "d/e/f/file", "../../../d/e/f/file")
	test(".", ".", ".")

	// The trailing dot marks the difference between a file and a directory.
	// This is the same behavior as with filepath.Rel.
	test("a/b/c", ".", "../../../.")

	// Intermediate dotdot components are removed.
	test("a/../b", "c/../d", "../d")

	test(".", "dir/file", "dir/file")
	// XXX: maybe the /. is missing at the end
	test(".", "dir/subdir/", "dir/subdir")
	// XXX: maybe the /. is missing at the end
	test(".", "dir/subdir/.", "dir/subdir")
}

func (s *Suite) Test_NewCurrPath(c *check.C) {
	t := s.Init(c)

	curr := NewCurrPath("dir/.///file")

	t.CheckEquals(curr.String(), "dir/.///file")
}

func (s *Suite) Test_NewCurrPathString(c *check.C) {
	t := s.Init(c)

	curr := NewCurrPathString("dir/.///file")

	t.CheckEquals(curr.String(), "dir/.///file")
}

func (s *Suite) Test_NewCurrPathSlash(c *check.C) {
	t := s.Init(c)

	test := func(path, curr string) {
		t.CheckEquals(NewCurrPathSlash(path).String(), curr)
	}

	test("filename", "filename")
	test("dir/.///file", "dir/.///file")
}

func (s *Suite) Test_NewCurrPathSlash__windows(c *check.C) {
	t := s.Init(c)

	test := func(path, currWindows, currOther string) {
		t.CheckEquals(
			NewCurrPathSlash(path).String(),
			condStr(runtime.GOOS == "windows", currWindows, currOther))
	}

	test("\\", "/", "\\")
	test("dir\\.\\\\\\file", "dir/.///file", "dir\\.\\\\\\file")
}

func (s *Suite) Test_CurrPath_GoString(c *check.C) {
	t := s.Init(c)

	test := func(p CurrPath, str string) {
		t.CheckEquals(p.GoString(), str)
	}

	// Tabs in filenames are rare, probably typos.
	test("dir/file\t", "\"dir/file\\t\"")
}

func (s *Suite) Test_CurrPath_String(c *check.C) {
	t := s.Init(c)

	test := func(p CurrPath, str string) {
		t.CheckEquals(p.String(), str)
	}

	// Tabs in filenames are rare, probably typos.
	test("dir/file\t", "dir/file\t")
}

func (s *Suite) Test_CurrPath_AsPath(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, asPath Path) {
		t.CheckEquals(curr.AsPath(), asPath)
	}

	// Tabs in filenames are rare, probably typos.
	test("dir/file\t", "dir/file\t")
}

func (s *Suite) Test_CurrPath_IsEmpty(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, isEmpty bool) {
		t.CheckEquals(curr.IsEmpty(), isEmpty)
	}

	test("", true)
	test(".", false)
	test("/", false)
}

func (s *Suite) Test_CurrPath_DirClean(c *check.C) {
	t := s.Init(c)

	test := func(curr, dir CurrPath) {
		t.CheckEquals(curr.DirClean(), dir)
	}

	test("./dir/../dir///./file", "dir")
}

func (s *Suite) Test_CurrPath_DirNoClean(c *check.C) {
	t := s.Init(c)

	test := func(curr, dir CurrPath) {
		t.CheckEquals(curr.DirNoClean(), dir)
	}

	test("./dir/../dir///./file", "./dir/../dir")
}

func (s *Suite) Test_CurrPath_Base(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, base string) {
		t.CheckEquals(curr.Base(), base)
	}

	test("dir/file", "file")
}

func (s *Suite) Test_CurrPath_Split(c *check.C) {
	t := s.Init(c)

	test := func(curr, dir CurrPath, base string) {
		actualDir, actualBase := curr.Split()
		t.CheckEquals(actualDir, dir)
		t.CheckEquals(actualBase, base)
	}

	test("dir/file", "dir/", "file")
}

func (s *Suite) Test_CurrPath_Parts(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, parts ...string) {
		t.CheckDeepEquals(curr.Parts(), parts)
	}

	test("dir/file", "dir", "file")
}

func (s *Suite) Test_CurrPath_IsAbs(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, isAbs bool) {
		t.CheckDeepEquals(curr.IsAbs(), isAbs)
	}

	test("/", true)
	test("./", false)
	test("C:/", runtime.GOOS == "windows")
}

func (s *Suite) Test_CurrPath_HasPrefixPath(c *check.C) {
	t := s.Init(c)

	test := func(curr, prefix CurrPath, hasPrefix bool) {
		t.CheckEquals(curr.HasPrefixPath(prefix), hasPrefix)
	}

	test("dir/file", "dir", true)
	test("dir/file", "file", false)
	test("dir", ".", true)
}

func (s *Suite) Test_CurrPath_ContainsPath(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, sub Path, hasPrefix bool) {
		t.CheckEquals(curr.ContainsPath(sub), hasPrefix)
	}

	test("dir/file", "dir", true)
	test("dir/file", "file", true)
	test("dir/file", "fi", false)
	test("dir", ".", true)
}

func (s *Suite) Test_CurrPath_ContainsText(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, sub string, hasPrefix bool) {
		t.CheckEquals(curr.ContainsText(sub), hasPrefix)
	}

	test("dir/file", "dir", true)
	test("dir/file", "r/f", true)
}

func (s *Suite) Test_CurrPath_HasSuffixPath(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, suffix Path, hasPrefix bool) {
		t.CheckEquals(curr.HasSuffixPath(suffix), hasPrefix)
	}

	test("dir/file", "dir", false)
	test("dir/file", "file", true)
	test("dir/file", "le", false)

	// In contrast to HasPrefixPath, it doesn't really make sense to
	// ask whether a path ends with the current directory.
	test("dir", ".", false)
}

func (s *Suite) Test_CurrPath_HasSuffixText(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, suffix string, hasPrefix bool) {
		t.CheckEquals(curr.HasSuffixText(suffix), hasPrefix)
	}

	test("dir/file", "dir", false)
	test("dir/file", "file", true)
	test("dir/file", "le", true)
}

func (s *Suite) Test_CurrPath_HasBase(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, base string, hasPrefix bool) {
		t.CheckEquals(curr.HasBase(base), hasPrefix)
	}

	test("dir/file", "dir", false)
	test("dir/file", "file", true)
	test("dir/file", "le", false)
}

func (s *Suite) Test_CurrPath_TrimSuffix(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, suffix string, trimmed CurrPath) {
		t.CheckEquals(curr.TrimSuffix(suffix), trimmed)
	}

	test("dir/file", "dir", "dir/file")
	test("dir/file", "file", "dir/")
	test("dir/file", "le", "dir/fi")
}

func (s *Suite) Test_CurrPath_ReplaceSuffix(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, from, to string, replaced CurrPath) {
		t.CheckEquals(curr.ReplaceSuffix(from, to), replaced)
	}

	test("dir/file", "file", "subdir", "dir/subdir")

	// The path must actually end with the suffix, otherwise there is
	// the risk of creating unintended paths.
	t.ExpectAssert(
		func() { test("dir/file", "no-match", "anything", "dir/file") })
}

func (s *Suite) Test_CurrPath_Clean(c *check.C) {
	t := s.Init(c)

	test := func(curr, cleaned CurrPath) {
		t.CheckEquals(curr.Clean(), cleaned)
	}

	test("dir/file", "dir/file")
	test("dir/.////../file", "file")
}

func (s *Suite) Test_CurrPath_CleanDot(c *check.C) {
	t := s.Init(c)

	test := func(curr, cleaned CurrPath) {
		t.CheckEquals(curr.CleanDot(), cleaned)
	}

	test("dir/file", "dir/file")
	test("dir/.////../file", "dir/../file")
}

func (s *Suite) Test_CurrPath_CleanPath(c *check.C) {
	t := s.Init(c)

	test := func(curr, cleaned CurrPath) {
		t.CheckEquals(curr.CleanPath(), cleaned)
	}

	test("a/b/../../c/d/../../e/../f", "a/b/../../e/../f")
}

func (s *Suite) Test_CurrPath_JoinNoClean(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, rel RelPath, joined CurrPath) {
		t.CheckEquals(curr.JoinNoClean(rel), joined)
	}

	test("", "", "/")
	test(".", "file", "./file")
	test("dir", "subdir/file", "dir/subdir/file")
}

func (s *Suite) Test_CurrPath_JoinClean(c *check.C) {
	t := s.Init(c)

	test := func(curr CurrPath, rel RelPath, joined CurrPath) {
		t.CheckEquals(curr.JoinClean(rel), joined)
	}

	test("", "", "")
	test(".", "./////file", "file")
	test("dir/./.", "../subdir/file", "subdir/file")
}

func (s *Suite) Test_CurrPath_Rel(c *check.C) {
	t := s.Init(c)

	test := func(curr, rel CurrPath, result RelPath) {
		t.CheckEquals(curr.Rel(rel), result)
	}

	test("dir/subdir", "dir", "..")
	test("dir/subdir", "file", "../../file")
}

func (s *Suite) Test_CurrPath_Rename(c *check.C) {
	t := s.Init(c)

	f := t.CreateFileLines("filename.old",
		"line 1")
	t.CheckEquals(f.IsFile(), true)
	dst := f.ReplaceSuffix(".old", ".new")

	err := f.Rename(dst)

	assertNil(err, "Rename")
	t.CheckEquals(f.IsFile(), false)
	t.CheckFileLines("filename.new",
		"line 1")
}

func (s *Suite) Test_CurrPath_Lstat(c *check.C) {
	t := s.Init(c)

	test := func(f CurrPath, isDir bool) {
		st, err := f.Lstat()
		assertNil(err, "Lstat")
		t.CheckEquals(st.Mode()&os.ModeDir != 0, isDir)
	}

	t.CreateFileLines("subdir/file")
	t.CreateFileLines("file")

	test(t.File("subdir"), true)
	test(t.File("file"), false)
}

func (s *Suite) Test_CurrPath_Stat(c *check.C) {
	t := s.Init(c)

	test := func(f CurrPath, isDir bool) {
		st, err := f.Stat()
		assertNil(err, "Stat")
		t.CheckEquals(st.Mode()&os.ModeDir != 0, isDir)
	}

	t.CreateFileLines("subdir/file")
	t.CreateFileLines("file")

	test(t.File("subdir"), true)
	test(t.File("file"), false)
}

func (s *Suite) Test_CurrPath_Exists(c *check.C) {
	t := s.Init(c)

	test := func(f CurrPath, exists bool) {
		t.CheckEquals(f.Exists(), exists)
	}

	t.CreateFileLines("subdir/file")
	t.CreateFileLines("file")

	test(t.File("subdir"), true)
	test(t.File("file"), true)
	test(t.File("enoent"), false)
}

func (s *Suite) Test_CurrPath_IsFile(c *check.C) {
	t := s.Init(c)

	t.CreateFileLines("dir/file")
	t.Chdir(".")

	test := func(curr CurrPath, isFile bool) {
		t.CheckEquals(curr.IsFile(), isFile)
	}

	test("nonexistent", false)
	test("dir", false)
	test("dir/nonexistent", false)
	test("dir/file", true)
}

func (s *Suite) Test_CurrPath_IsDir(c *check.C) {
	t := s.Init(c)

	t.CreateFileLines("dir/file")
	t.Chdir(".")

	test := func(curr CurrPath, isFile bool) {
		t.CheckEquals(curr.IsDir(), isFile)
	}

	test("nonexistent", false)
	test("dir", true)
	test("dir/nonexistent", false)
	test("dir/file", false)
}

func (s *Suite) Test_CurrPath_Chmod(c *check.C) {
	t := s.Init(c)

	testWritable := func(f CurrPath, writable bool) {
		lstat, err := f.Lstat()
		assertNil(err, "Lstat")
		t.CheckEquals(lstat.Mode().Perm()&0200 != 0, writable)
	}

	f := t.CreateFileLines("file")
	testWritable(f, true)

	err := f.Chmod(0444)
	assertNil(err, "Chmod")

	testWritable(f, false)
}

func (s *Suite) Test_CurrPath_ReadDir(c *check.C) {
	t := s.Init(c)

	t.CreateFileLines("subdir/file")
	t.CreateFileLines("file")
	t.CreateFileLines("CVS/Entries")
	t.CreateFileLines(".git/info/exclude")
	t.Chdir(".")

	test := func(curr CurrPath, entries ...string) {
		infos, err := curr.ReadDir()
		assertNil(err, "ReadDir")

		var names []string
		for _, info := range infos {
			names = append(names, info.Name())
		}

		t.CheckDeepEquals(names, entries)
	}

	test(".",
		".git", "CVS", "file", "subdir")
	test("subdir",
		"file")
	test("CVS",
		"Entries")
}

func (s *Suite) Test_CurrPath_ReadPaths(c *check.C) {
	t := s.Init(c)

	t.CreateFileLines("dir/subdir/file")
	t.CreateFileLines("dir/CVS/Entries")
	t.CreateFileLines("dir/file")
	t.Chdir(".")

	test := func(dir CurrPath, entries ...CurrPath) {
		t.CheckDeepEquals(dir.ReadPaths(), entries)
	}

	test(".",
		"dir")

	test("dir",
		"dir/file", "dir/subdir")
}

func (s *Suite) Test_CurrPath_Open(c *check.C) {
	t := s.Init(c)

	t.CreateFileLines("filename",
		"line 1",
		"line 2")
	t.Chdir(".")

	test := func(curr CurrPath, content string) {
		f, err := curr.Open()
		assertNil(err, "Open")
		defer func() { assertNil(f.Close(), "Close") }()

		var sb strings.Builder
		n, err := io.Copy(&sb, f)
		assertNil(err, "Copy")

		t.CheckEquals(n, int64(len(content)))
		t.CheckEquals(sb.String(), content)
	}

	test("filename", "line 1\nline 2\n")
}

func (s *Suite) Test_CurrPath_ReadString(c *check.C) {
	t := s.Init(c)

	t.Chdir(".")
	t.CreateFileLines("empty")
	t.CreateFileLines("filename",
		"line 1",
		"line 2")

	test := func(curr CurrPath, content string) {
		text, err := curr.ReadString()

		assertNil(err, "ReadString")
		t.CheckEquals(text, content)
	}

	test("empty", "")
	test("filename", "line 1\nline 2\n")
}

func (s *Suite) Test_CurrPath_WriteString(c *check.C) {
	t := s.Init(c)

	t.Chdir(".")

	test := func(curr CurrPath, content string, lines ...string) {
		err := curr.WriteString(content)
		assertNil(err, "WriteString")

		t.CheckFileLines(NewRelPath(curr.AsPath()),
			lines...)
	}

	test("empty", "",
		nil...)

	test("filename", "line 1\nline 2\n",
		"line 1",
		"line 2")
}

func (s *Suite) Test_NewPkgsrcPath(c *check.C) {
	t := s.Init(c)

	p := NewPkgsrcPath("category/package")

	t.CheckEquals(p.AsPath(), NewPath("category/package"))
}

func (s *Suite) Test_PkgsrcPath_String(c *check.C) {
	t := s.Init(c)

	p := NewPkgsrcPath("any string..././")

	str := p.String()

	// No normalization takes place because it is typically not needed.
	t.CheckEquals(str, "any string..././")
}

func (s *Suite) Test_PkgsrcPath_AsPath(c *check.C) {
	t := s.Init(c)

	pp := NewPkgsrcPath("./category/package/Makefile")

	p := pp.AsPath()

	t.CheckEquals(p.String(), "./category/package/Makefile")
}

func (s *Suite) Test_PkgsrcPath_AsRelPath(c *check.C) {
	t := s.Init(c)

	pp := NewPkgsrcPath("./category/package/Makefile")

	rel := pp.AsRelPath()

	t.CheckEquals(rel.String(), "./category/package/Makefile")
}

func (s *Suite) Test_PkgsrcPath_DirClean(c *check.C) {
	t := s.Init(c)

	test := func(pp, cleaned PkgsrcPath) {
		t.CheckEquals(pp.DirClean(), cleaned)
	}

	test("./dir/../dir/base///.", "dir/base")
}

func (s *Suite) Test_PkgsrcPath_DirNoClean(c *check.C) {
	t := s.Init(c)

	test := func(pp, cleaned PkgsrcPath) {
		t.CheckEquals(pp.DirNoClean(), cleaned)
	}

	test("./dir/../dir/base///.", "./dir/../dir/base")
}

func (s *Suite) Test_PkgsrcPath_Base(c *check.C) {
	t := s.Init(c)

	test := func(pp PkgsrcPath, base string) {
		t.CheckEquals(pp.Base(), base)
	}

	test("./dir/../dir/base///.", ".")
}

func (s *Suite) Test_PkgsrcPath_Count(c *check.C) {
	t := s.Init(c)

	test := func(pp PkgsrcPath, count int) {
		t.CheckEquals(pp.Count(), count)
	}

	test("./...////dir", 2)
}

func (s *Suite) Test_PkgsrcPath_HasPrefixPath(c *check.C) {
	t := s.Init(c)

	test := func(pp PkgsrcPath, prefix Path, hasPrefixPath bool) {
		t.CheckEquals(pp.HasPrefixPath(prefix), hasPrefixPath)
	}

	test("./././///prefix/suffix", "prefix", true)
}

func (s *Suite) Test_PkgsrcPath_JoinNoClean(c *check.C) {
	t := s.Init(c)

	test := func(pp PkgsrcPath, rel RelPath, joined PkgsrcPath) {
		t.CheckEquals(pp.JoinNoClean(rel), joined)
	}

	test("base///.", "./../rel", "base///././../rel")
}

func (s *Suite) Test_NewPackagePath(c *check.C) {
	t := s.Init(c)

	p := NewPackagePath("../../category/package")

	t.CheckEquals(p.AsPath(), NewPath("../../category/package"))
}

func (s *Suite) Test_NewPackagePathString(c *check.C) {
	t := s.Init(c)

	p := NewPackagePathString("../../category/package")

	t.CheckEquals(p.AsPath(), NewPath("../../category/package"))
}

func (s *Suite) Test_PackagePath_AsPath(c *check.C) {
	t := s.Init(c)

	pp := NewPackagePath("../../category/package/Makefile")

	p := pp.AsPath()

	t.CheckEquals(p.String(), "../../category/package/Makefile")
}

func (s *Suite) Test_PackagePath_AsRelPath(c *check.C) {
	t := s.Init(c)

	pp := NewPackagePath("./category/package/Makefile")

	rel := pp.AsRelPath()

	t.CheckEquals(rel.String(), "./category/package/Makefile")
}

func (s *Suite) Test_PackagePath_String(c *check.C) {
	t := s.Init(c)

	pp := NewPackagePath("../../category/package/Makefile")

	str := pp.String()

	t.CheckEquals(str, "../../category/package/Makefile")
}

func (s *Suite) Test_PackagePath_JoinNoClean(c *check.C) {
	t := s.Init(c)

	test := func(pp PackagePath, other RelPath, joined PackagePath) {
		t.CheckEquals(pp.JoinNoClean(other), joined)

	}

	test("../../category/package/patches", "patch-aa",
		"../../category/package/patches/patch-aa")
}

func (s *Suite) Test_PackagePath_CleanPath(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, cleaned PackagePath) {
		t.CheckEquals(p.CleanPath(), cleaned)
	}

	test("a/b/../../c/d/../.././e/../f", "a/b/../../e/../f")
}

func (s *Suite) Test_PackagePath_IsEmpty(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, isEmpty bool) {
		t.CheckEquals(p.IsEmpty(), isEmpty)
	}

	test("", true)
	test(".", false)
}

func (s *Suite) Test_PackagePath_HasPrefixPath(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, sub Path, hasPrefix bool) {
		t.CheckEquals(p.HasPrefixPath(sub), hasPrefix)
	}

	test("/root/subdir", "subdir", false)
	test("/root/subdir", "/root", true)
	test("/root/subdir", "/r", false)
}

func (s *Suite) Test_PackagePath_ContainsPath(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, sub Path, hasPrefix bool) {
		t.CheckEquals(p.ContainsPath(sub), hasPrefix)
	}

	test("/root/subdir", "subdir", true)
	test("/root/subdir", "/root", true)
	test("/root/subdir", "/r", false)
}

func (s *Suite) Test_PackagePath_ContainsText(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, sub string, hasPrefix bool) {
		t.CheckEquals(p.ContainsText(sub), hasPrefix)
	}

	test("/root/subdir", "subdir", true)
	test("/root/subdir", "/root", true)
	test("/root/subdir", "/r", true)
	test("/root/subdir", "t//sub", false)
}

func (s *Suite) Test_PackagePath_Replace(c *check.C) {
	t := s.Init(c)

	test := func(p PackagePath, from, to string, result PackagePath) {
		t.CheckEquals(p.Replace(from, to), result)
	}

	test("dir/file", "dir", "other", "other/file")
	test("dir/file", "r", "sk", "disk/file")
	test("aaa/file", "a", "sub/", "sub/sub/sub//file")
}

func (s *Suite) Test_NewRelPath(c *check.C) {
	t := s.Init(c)

	rel := NewRelPath("dir/file")

	t.CheckEquals(rel.String(), "dir/file")
}

func (s *Suite) Test_NewRelPathString(c *check.C) {
	t := s.Init(c)

	rel := NewRelPathString("dir/file")

	t.CheckEquals(rel.String(), "dir/file")
}

func (s *Suite) Test_RelPath_AsPath(c *check.C) {
	t := s.Init(c)

	rel := NewRelPath("relative")

	path := rel.AsPath()

	t.CheckEquals(path.String(), "relative")
}

func (s *Suite) Test_RelPath_String(c *check.C) {
	t := s.Init(c)

	rel := NewRelPath(".///rel")

	str := rel.String()

	t.CheckEquals(str, ".///rel")
}

func (s *Suite) Test_RelPath_IsEmpty(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, isEmpty bool) {
		t.CheckEquals(rel.IsEmpty(), isEmpty)
	}

	test("", true)
	test(".", false)
	test("/", false)
}

func (s *Suite) Test_RelPath_Split(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, dir RelPath, base string) {
		actualDir, actualBase := rel.Split()
		t.CheckEquals(actualDir, dir)
		t.CheckEquals(actualBase, base)
	}

	test("dir/file", "dir/", "file")
	test("././///file", "././///", "file")

	t.ExpectAssert(
		func() { test("/", "/", "") })

}

func (s *Suite) Test_RelPath_DirClean(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, dir RelPath) {
		t.CheckEquals(rel.DirClean(), dir)
	}

	test("./dir/../dir///./file", "dir")
}

func (s *Suite) Test_RelPath_DirNoClean(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, dir RelPath) {
		t.CheckEquals(rel.DirNoClean(), dir)
	}

	test("./dir/../dir///./file", "./dir/../dir")
}

func (s *Suite) Test_RelPath_Base(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, base string) {
		t.CheckEquals(rel.Base(), base)
	}

	test("./dir/../dir///./file", "file")
}

func (s *Suite) Test_RelPath_HasBase(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, base string, hasBase bool) {
		t.CheckEquals(rel.HasBase(base), hasBase)
	}

	test("./dir/Makefile", "Makefile", true)
	test("./dir/Makefile", "Make", false)
	test("./dir/Makefile", "file", false)
	test("./dir/Makefile", "dir/Makefile", false)
}

func (s *Suite) Test_RelPath_Parts(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, parts ...string) {
		t.CheckDeepEquals(rel.Parts(), parts)
	}

	test("./dir/.///base", "dir", "base")
}

func (s *Suite) Test_RelPath_Count(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, count int) {
		t.CheckEquals(rel.Count(), count)
	}

	test("./dir/.///base", 2)
}

func (s *Suite) Test_RelPath_Clean(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, cleaned RelPath) {
		t.CheckDeepEquals(rel.Clean(), cleaned)
	}

	test("a/b/../../c/d/../.././e/../f", "f")
}

func (s *Suite) Test_RelPath_CleanDot(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, cleaned RelPath) {
		t.CheckEquals(rel.CleanDot(), cleaned)
	}

	test("a/b/../../c/d/../.././e/../f", "a/b/../../c/d/../../e/../f")
}

func (s *Suite) Test_RelPath_CleanPath(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, cleaned RelPath) {
		t.CheckEquals(rel.CleanPath(), cleaned)
	}

	test("a/b/../../c/d/../.././e/../f", "a/b/../../e/../f")
}

func (s *Suite) Test_RelPath_JoinNoClean(c *check.C) {
	t := s.Init(c)

	test := func(rel, other, joined RelPath) {
		t.CheckEquals(rel.JoinNoClean(other), joined)
	}

	test("basedir/.//", "./other", "basedir/.///./other")
}

func (s *Suite) Test_RelPath_Replace(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, from, to string, result RelPath) {
		t.CheckEquals(rel.Replace(from, to), result)
	}

	test("dir/subdir/file", "/", ":", "dir:subdir:file")
}

func (s *Suite) Test_RelPath_HasPrefixPath(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix Path, hasPrefixPath bool) {
		t.CheckEquals(rel.HasPrefixPath(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", true)
	test("dir/subdir/file", "dir/sub", false)
	test("dir/subdir/file", "subdir", false)
}

func (s *Suite) Test_RelPath_HasPrefixText(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix string, hasPrefixPath bool) {
		t.CheckEquals(rel.HasPrefixText(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", true)
	test("dir/subdir/file", "dir/sub", true)
	test("dir/subdir/file", "subdir", false)
	test("dir/subdir/file", "super", false)
}

func (s *Suite) Test_RelPath_ContainsPath(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix Path, hasPrefixPath bool) {
		t.CheckEquals(rel.ContainsPath(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", true)
	test("dir/subdir/file", "dir/sub", false)
	test("dir/subdir/file", "subdir", true)
}

func (s *Suite) Test_RelPath_ContainsText(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix string, hasPrefixPath bool) {
		t.CheckEquals(rel.ContainsText(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", true)
	test("dir/subdir/file", "dir/sub", true)
	test("dir/subdir/file", "subdir", true)
	test("dir/subdir/file", "super", false)
}

func (s *Suite) Test_RelPath_HasSuffixPath(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix Path, hasPrefixPath bool) {
		t.CheckEquals(rel.HasSuffixPath(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", false)
	test("dir/subdir/file", "file", true)
	test("dir/subdir/file", "le", false)
	test("dir/subdir/file", "subdir/file", true)
	test("dir/subdir/file", "subdir", false)
}

func (s *Suite) Test_RelPath_HasSuffixText(c *check.C) {
	t := s.Init(c)

	test := func(rel RelPath, prefix string, hasPrefixPath bool) {
		t.CheckEquals(rel.HasSuffixText(prefix), hasPrefixPath)
	}

	test("dir/subdir/file", "dir", false)
	test("dir/subdir/file", "file", true)
	test("dir/subdir/file", "le", true)
	test("dir/subdir/file", "subdir/file", true)
	test("dir/subdir/file", "subdir", false)
}

func (s *Suite) Test_RelPath_Rel(c *check.C) {
	t := s.Init(c)

	test := func(base RelPath, other Path, result RelPath) {
		t.CheckEquals(base.Rel(other), result)
	}

	test("a/b/c", "d/e/f/file", "../../../d/e/f/file")
	test(".", ".", ".")

	// The trailing dot marks the difference between a file and a directory.
	// This is the same behavior as with filepath.Rel.
	test("a/b/c", ".", "../../../.")
}