Changes since 5.6.2: * Add check for version patterns 1.5*, which should rather be 1.5.* * Re-enable check for "set -e" and commands that may silently fail because of missing error checking * Lots of internal clean-up and tests
402 lines
12 KiB
Go
402 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"gopkg.in/check.v1"
|
|
"os"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func (s *Suite) Test_YesNoUnknown_String(c *check.C) {
|
|
c.Check(yes.String(), equals, "yes")
|
|
c.Check(no.String(), equals, "no")
|
|
c.Check(unknown.String(), equals, "unknown")
|
|
}
|
|
|
|
func (s *Suite) Test_mkopSubst__middle(c *check.C) {
|
|
c.Check(mkopSubst("pkgname", false, "kgna", false, "ri", ""), equals, "prime")
|
|
c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", ""), equals, "replacement")
|
|
c.Check(mkopSubst("aaaaaaa", false, "a", false, "b", ""), equals, "baaaaaa")
|
|
}
|
|
|
|
func (s *Suite) Test_mkopSubst__left(c *check.C) {
|
|
c.Check(mkopSubst("pkgname", true, "kgna", false, "ri", ""), equals, "pkgname")
|
|
c.Check(mkopSubst("pkgname", true, "pkgname", false, "replacement", ""), equals, "replacement")
|
|
}
|
|
|
|
func (s *Suite) Test_mkopSubst__right(c *check.C) {
|
|
c.Check(mkopSubst("pkgname", false, "kgna", true, "ri", ""), equals, "pkgname")
|
|
c.Check(mkopSubst("pkgname", false, "pkgname", true, "replacement", ""), equals, "replacement")
|
|
}
|
|
|
|
func (s *Suite) Test_mkopSubst__left_and_right(c *check.C) {
|
|
c.Check(mkopSubst("pkgname", true, "kgna", true, "ri", ""), equals, "pkgname")
|
|
c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", ""), equals, "replacement")
|
|
}
|
|
|
|
func (s *Suite) Test_mkopSubst__gflag(c *check.C) {
|
|
c.Check(mkopSubst("aaaaa", false, "a", false, "b", "g"), equals, "bbbbb")
|
|
c.Check(mkopSubst("aaaaa", true, "a", false, "b", "g"), equals, "baaaa")
|
|
c.Check(mkopSubst("aaaaa", false, "a", true, "b", "g"), equals, "aaaab")
|
|
c.Check(mkopSubst("aaaaa", true, "a", true, "b", "g"), equals, "aaaaa")
|
|
}
|
|
|
|
func (s *Suite) Test__regex_ReplaceFirst(c *check.C) {
|
|
m, rest := G.res.ReplaceFirst("a+b+c+d", `(\w)(.)(\w)`, "X")
|
|
|
|
c.Assert(m, check.NotNil)
|
|
c.Check(m, check.DeepEquals, []string{"a+b", "a", "+", "b"})
|
|
c.Check(rest, equals, "X+c+d")
|
|
}
|
|
|
|
func (s *Suite) Test_mustMatch(c *check.C) {
|
|
c.Check(
|
|
func() { mustMatch("aaa", `b`) },
|
|
check.Panics,
|
|
"mustMatch \"aaa\" \"b\"")
|
|
}
|
|
|
|
func (s *Suite) Test_shorten(c *check.C) {
|
|
c.Check(shorten("aaaaa", 3), equals, "aaa...")
|
|
c.Check(shorten("aaaaa", 5), equals, "aaaaa")
|
|
c.Check(shorten("aaa", 5), equals, "aaa")
|
|
}
|
|
|
|
func (s *Suite) Test_tabWidth(c *check.C) {
|
|
c.Check(tabWidth("12345"), equals, 5)
|
|
c.Check(tabWidth("\t"), equals, 8)
|
|
c.Check(tabWidth("123\t"), equals, 8)
|
|
c.Check(tabWidth("1234567\t"), equals, 8)
|
|
c.Check(tabWidth("12345678\t"), equals, 16)
|
|
}
|
|
|
|
func (s *Suite) Test_cleanpath(c *check.C) {
|
|
c.Check(cleanpath("simple/path"), equals, "simple/path")
|
|
c.Check(cleanpath("/absolute/path"), equals, "/absolute/path")
|
|
c.Check(cleanpath("./././."), equals, ".")
|
|
c.Check(cleanpath("./././"), equals, ".")
|
|
c.Check(cleanpath("dir/../dir/../dir/../dir/subdir/../../Makefile"), equals, "dir/../dir/../dir/../Makefile")
|
|
c.Check(cleanpath("dir/multi/././/file"), equals, "dir/multi/file")
|
|
c.Check(cleanpath("111/222/../../333/444/../../555/666/../../777/888/9"), equals, "111/222/../../777/888/9")
|
|
c.Check(cleanpath("1/2/3/../../4/5/6/../../7/8/9/../../../../10"), equals, "1/10")
|
|
c.Check(cleanpath("cat/pkg.v1/../../cat/pkg.v2/Makefile"), equals, "cat/pkg.v1/../../cat/pkg.v2/Makefile")
|
|
c.Check(cleanpath("dir/"), equals, "dir")
|
|
}
|
|
|
|
func (s *Suite) Test_relpath(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
if runtime.GOOS == "windows" {
|
|
t.ExpectFatal(
|
|
func() { relpath("c:/", "d:/") },
|
|
"FATAL: Pkglint internal error: relpath \"c:/\" \"d:/\".")
|
|
}
|
|
}
|
|
|
|
func (s *Suite) Test_abspath(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
if runtime.GOOS == "windows" {
|
|
t.ExpectFatal(
|
|
func() { abspath("file\u0000name") },
|
|
"FATAL: Pkglint internal error: abspath \"file\\x00name\".")
|
|
}
|
|
}
|
|
|
|
func (s *Suite) Test_isEmptyDir__and_getSubdirs(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
t.CreateFileLines("CVS/Entries",
|
|
"dummy")
|
|
|
|
if dir := t.File("."); true {
|
|
c.Check(isEmptyDir(dir), equals, true)
|
|
c.Check(getSubdirs(dir), check.DeepEquals, []string(nil))
|
|
|
|
t.CreateFileLines("somedir/file")
|
|
|
|
c.Check(isEmptyDir(dir), equals, false)
|
|
c.Check(getSubdirs(dir), check.DeepEquals, []string{"somedir"})
|
|
}
|
|
|
|
if absent := t.File("nonexistent"); true {
|
|
c.Check(isEmptyDir(absent), equals, true) // Counts as empty.
|
|
|
|
// The last group from the error message is localized, therefore the matching.
|
|
t.ExpectFatalMatches(
|
|
func() { getSubdirs(absent) },
|
|
`FATAL: ~/nonexistent: Cannot be read: open ~/nonexistent: (.+)\n`)
|
|
}
|
|
}
|
|
|
|
func (s *Suite) Test_isEmptyDir__empty_subdir(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
t.CreateFileLines("CVS/Entries",
|
|
"dummy")
|
|
t.CreateFileLines("subdir/CVS/Entries",
|
|
"dummy")
|
|
|
|
c.Check(isEmptyDir(t.File(".")), equals, true)
|
|
}
|
|
|
|
func (s *Suite) Test__PrefixReplacer_Since(c *check.C) {
|
|
repl := G.NewPrefixReplacer("hello, world")
|
|
mark := repl.Mark()
|
|
repl.AdvanceRegexp(`^\w+`)
|
|
c.Check(repl.Since(mark), equals, "hello")
|
|
}
|
|
|
|
func (s *Suite) Test_detab(c *check.C) {
|
|
c.Check(detab(""), equals, "")
|
|
c.Check(detab("\t"), equals, " ")
|
|
c.Check(detab("1234\t9"), equals, "1234 9")
|
|
c.Check(detab("1234567\t"), equals, "1234567 ")
|
|
c.Check(detab("12345678\t"), equals, "12345678 ")
|
|
}
|
|
|
|
const reMkIncludeBenchmark = `^\.(\s*)(s?include)\s+\"([^\"]+)\"\s*(?:#.*)?$`
|
|
const reMkIncludeBenchmarkPositive = `^\.(\s*)(s?include)\s+\"(.+)\"\s*(?:#.*)?$`
|
|
|
|
func Benchmark_match3_buildlink3(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmark)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_bsd_pkg_mk(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmark)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_same_dir(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"options.mk\"", reMkIncludeBenchmark)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_bsd_pkg_mk_comment(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../mk/bsd.pkg.mk\" # infrastructure ", reMkIncludeBenchmark)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_buildlink3_positive(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmarkPositive)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_bsd_pkg_mk_positive(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmarkPositive)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_same_dir_positive(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"options.mk\"", reMkIncludeBenchmarkPositive)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_bsd_pkg_mk_comment_positive(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
match3(".include \"../../mk/bsd.pkg.mk\" # infrastructure ", reMkIncludeBenchmarkPositive)
|
|
}
|
|
}
|
|
|
|
func Benchmark_match3_explicit(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
MatchMkInclude(".include \"../../mk/bsd.pkg.mk\" # infrastructure ")
|
|
}
|
|
}
|
|
|
|
func emptyToNil(slice []string) []string {
|
|
if len(slice) == 0 {
|
|
return nil
|
|
}
|
|
return slice
|
|
}
|
|
|
|
func (s *Suite) Test_splitOnSpace(c *check.C) {
|
|
c.Check(splitOnSpace("aaaaa"), deepEquals, []string{"aaaaa"})
|
|
c.Check(splitOnSpace(" a b\tc\n"), deepEquals, []string{"a", "b", "c"})
|
|
c.Check(splitOnSpace(" "), check.IsNil)
|
|
c.Check(splitOnSpace(""), check.IsNil)
|
|
}
|
|
|
|
func (s *Suite) Test_isLocallyModified(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
unmodified := t.CreateFileLines("unmodified")
|
|
modTime := time.Unix(1136239445, 0)
|
|
|
|
err := os.Chtimes(unmodified, modTime, modTime)
|
|
c.Check(err, check.IsNil)
|
|
|
|
st, err := os.Lstat(unmodified)
|
|
c.Check(err, check.IsNil)
|
|
|
|
// Make sure that the file system has second precision and accuracy.
|
|
c.Check(st.ModTime(), check.DeepEquals, modTime)
|
|
|
|
modified := t.CreateFileLines("modified")
|
|
|
|
t.CreateFileLines("CVS/Entries",
|
|
"/unmodified//"+modTime.Format(time.ANSIC)+"//",
|
|
"/modified//"+modTime.Format(time.ANSIC)+"//",
|
|
"/enoent//"+modTime.Format(time.ANSIC)+"//")
|
|
|
|
c.Check(isLocallyModified(unmodified), equals, false)
|
|
c.Check(isLocallyModified(modified), equals, true)
|
|
c.Check(isLocallyModified(t.File("enoent")), equals, true)
|
|
c.Check(isLocallyModified(t.File("not_mentioned")), equals, false)
|
|
}
|
|
|
|
func (s *Suite) Test_Scope_Defined(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
scope := NewScope()
|
|
scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value"))
|
|
|
|
c.Check(scope.Defined("VAR.param"), equals, true)
|
|
c.Check(scope.Defined("VAR.other"), equals, false)
|
|
c.Check(scope.Defined("VARIABLE.*"), equals, false)
|
|
|
|
c.Check(scope.DefinedSimilar("VAR.param"), equals, true)
|
|
c.Check(scope.DefinedSimilar("VAR.other"), equals, true)
|
|
c.Check(scope.DefinedSimilar("VARIABLE.*"), equals, false)
|
|
}
|
|
|
|
func (s *Suite) Test_Scope_Used(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
scope := NewScope()
|
|
scope.Use("VAR.param", t.NewMkLine("file.mk", 1, "\techo ${VAR.param}"))
|
|
|
|
c.Check(scope.Used("VAR.param"), equals, true)
|
|
c.Check(scope.Used("VAR.other"), equals, false)
|
|
c.Check(scope.Used("VARIABLE.*"), equals, false)
|
|
|
|
c.Check(scope.UsedSimilar("VAR.param"), equals, true)
|
|
c.Check(scope.UsedSimilar("VAR.other"), equals, true)
|
|
c.Check(scope.UsedSimilar("VARIABLE.*"), equals, false)
|
|
}
|
|
|
|
func (s *Suite) Test_Scope_DefineAll(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
src := NewScope()
|
|
|
|
dst := NewScope()
|
|
dst.DefineAll(src)
|
|
|
|
c.Check(dst.defined, check.HasLen, 0)
|
|
c.Check(dst.used, check.HasLen, 0)
|
|
|
|
src.Define("VAR", t.NewMkLine("file.mk", 1, "VAR=value"))
|
|
dst.DefineAll(src)
|
|
|
|
c.Check(dst.Defined("VAR"), equals, true)
|
|
}
|
|
|
|
func (s *Suite) Test_naturalLess(c *check.C) {
|
|
c.Check(naturalLess("0", "a"), equals, true)
|
|
c.Check(naturalLess("a", "0"), equals, false)
|
|
c.Check(naturalLess("000", "0000"), equals, true)
|
|
c.Check(naturalLess("0000", "000"), equals, false)
|
|
c.Check(naturalLess("000", "000"), equals, false)
|
|
c.Check(naturalLess("00011", "000111"), equals, true)
|
|
c.Check(naturalLess("00011", "00012"), equals, true)
|
|
}
|
|
|
|
func (s *Suite) Test_varnameBase(c *check.C) {
|
|
c.Check(varnameBase("VAR"), equals, "VAR")
|
|
c.Check(varnameBase("VAR.param"), equals, "VAR")
|
|
c.Check(varnameBase(".CURDIR"), equals, ".CURDIR")
|
|
}
|
|
|
|
func (s *Suite) Test_varnameParam(c *check.C) {
|
|
c.Check(varnameParam("VAR"), equals, "")
|
|
c.Check(varnameParam("VAR.param"), equals, "param")
|
|
c.Check(varnameParam(".CURDIR"), equals, "")
|
|
}
|
|
|
|
func (s *Suite) Test_varnameCanon(c *check.C) {
|
|
c.Check(varnameCanon("VAR"), equals, "VAR")
|
|
c.Check(varnameCanon("VAR.param"), equals, "VAR.*")
|
|
c.Check(varnameCanon(".CURDIR"), equals, ".CURDIR")
|
|
}
|
|
|
|
func (s *Suite) Test_isalnum(c *check.C) {
|
|
c.Check(isalnum(""), equals, true)
|
|
c.Check(isalnum("/"), equals, false)
|
|
c.Check(isalnum("0"), equals, true)
|
|
c.Check(isalnum("9"), equals, true)
|
|
c.Check(isalnum(":"), equals, false)
|
|
c.Check(isalnum("@"), equals, false)
|
|
c.Check(isalnum("A"), equals, true)
|
|
c.Check(isalnum("Z"), equals, true)
|
|
c.Check(isalnum("["), equals, false)
|
|
c.Check(isalnum("_"), equals, true)
|
|
c.Check(isalnum("`"), equals, false)
|
|
c.Check(isalnum("a"), equals, true)
|
|
c.Check(isalnum("z"), equals, true)
|
|
c.Check(isalnum("{"), equals, false)
|
|
c.Check(isalnum("Hello_world005"), equals, true)
|
|
c.Check(isalnum("Hello,world005"), equals, false)
|
|
}
|
|
|
|
func (s *Suite) Test_FileCache(c *check.C) {
|
|
t := s.Init(c)
|
|
|
|
cache := NewFileCache(3)
|
|
|
|
lines := t.NewLines("Makefile",
|
|
MkRcsID,
|
|
"# line 2")
|
|
|
|
c.Check(cache.Get("Makefile", 0), check.IsNil)
|
|
c.Check(cache.hits, equals, 0)
|
|
c.Check(cache.misses, equals, 1)
|
|
|
|
cache.Put("Makefile", 0, lines)
|
|
c.Check(cache.Get("Makefile", MustSucceed|LogErrors), check.IsNil) // Wrong LoadOptions.
|
|
|
|
linesFromCache := cache.Get("Makefile", 0)
|
|
c.Check(linesFromCache, check.HasLen, 2)
|
|
c.Check(linesFromCache[1].Filename, equals, "Makefile")
|
|
|
|
// Cache keys are normalized using path.Clean.
|
|
linesFromCache2 := cache.Get("./Makefile", 0)
|
|
c.Check(linesFromCache2, check.HasLen, 2)
|
|
c.Check(linesFromCache2[1].Filename, equals, "./Makefile")
|
|
|
|
cache.Put("file1.mk", 0, lines)
|
|
cache.Put("file2.mk", 0, lines)
|
|
|
|
// Now the cache is full. All three entries can be retrieved.
|
|
c.Check(cache.Get("Makefile", 0), check.NotNil)
|
|
c.Check(cache.Get("file1.mk", 0), check.NotNil)
|
|
c.Check(cache.Get("file2.mk", 0), check.NotNil)
|
|
|
|
// Adding another entry removes all entries with minimum count,
|
|
// which currently are file1.mk and file2.mk.
|
|
// Makefile is still in the cache because it was accessed once.
|
|
cache.Put("file3.mk", 0, lines)
|
|
|
|
c.Check(cache.Get("Makefile", 0), check.NotNil)
|
|
c.Check(cache.Get("file1.mk", 0), check.IsNil)
|
|
c.Check(cache.Get("file2.mk", 0), check.IsNil)
|
|
c.Check(cache.Get("file3.mk", 0), check.NotNil)
|
|
|
|
cache.Evict("Makefile")
|
|
|
|
c.Check(cache.Get("Makefile", 0), check.IsNil)
|
|
c.Check(cache.table, check.HasLen, 1)
|
|
c.Check(cache.mapping, check.HasLen, 1)
|
|
c.Check(cache.hits, equals, 7)
|
|
c.Check(cache.misses, equals, 5)
|
|
}
|