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

Annotation of pkgsrc/pkgtools/pkglint/files/util_test.go, Revision 1.50

1.19      rillig      1: package pkglint
1.1       rillig      2:
                      3: import (
1.30      rillig      4:        "errors"
1.40      rillig      5:        "fmt"
1.9       rillig      6:        "gopkg.in/check.v1"
1.13      rillig      7:        "os"
1.40      rillig      8:        "reflect"
                      9:        "sort"
1.6       rillig     10:        "testing"
1.13      rillig     11:        "time"
1.1       rillig     12: )
                     13:
1.35      rillig     14: func (s *Suite) Test_YesNoUnknown_String(c *check.C) {
                     15:        t := s.Init(c)
                     16:
                     17:        t.CheckEquals(yes.String(), "yes")
                     18:        t.CheckEquals(no.String(), "no")
                     19:        t.CheckEquals(unknown.String(), "unknown")
                     20: }
                     21:
                     22: func (s *Suite) Test_trimHspace(c *check.C) {
                     23:        t := s.Init(c)
                     24:
                     25:        t.CheckEquals(trimHspace("a b"), "a b")
                     26:        t.CheckEquals(trimHspace(" a b "), "a b")
                     27:        t.CheckEquals(trimHspace("\ta b\t"), "a b")
                     28:        t.CheckEquals(trimHspace(" \t a b\t \t"), "a b")
                     29: }
                     30:
                     31: func (s *Suite) Test_trimCommon(c *check.C) {
                     32:        t := s.Init(c)
                     33:
                     34:        test := func(a, b, trimmedA, trimmedB string) {
                     35:                ta, tb := trimCommon(a, b)
                     36:                t.CheckEquals(ta, trimmedA)
                     37:                t.CheckEquals(tb, trimmedB)
                     38:        }
                     39:
                     40:        test("", "",
                     41:                "", "")
                     42:
                     43:        test("equal", "equal",
                     44:                "", "")
                     45:
                     46:        test("prefixA", "prefixB",
                     47:                "A", "B")
                     48:
                     49:        test("ASuffix", "BSuffix",
                     50:                "A", "B")
                     51:
                     52:        test("PreMiddlePost", "PreCenterPost",
                     53:                "Middle", "Center")
                     54:
                     55:        test("", "b",
                     56:                "", "b")
                     57:
                     58:        test("a", "",
                     59:                "a", "")
                     60: }
                     61:
1.45      rillig     62: func (s *Suite) Test_replaceOnce(c *check.C) {
                     63:        t := s.Init(c)
                     64:
                     65:        test := func(s, from, to, result string) {
                     66:                ok, actualResult := replaceOnce(s, from, to)
                     67:
                     68:                t.CheckEquals(actualResult, result)
                     69:                t.CheckEquals(ok, result != s)
                     70:        }
                     71:
                     72:        // The text does not occur at all.
                     73:        test("something else", "from", "to", "something else")
                     74:
                     75:        // The text occurs exactly once.
                     76:        test("from", "from", "to", "to")
                     77:
                     78:        // The text occurs at two places, non-overlapping.
                     79:        test("from from", "from", "to", "from from")
                     80:
                     81:        // The text occurs at three places, non-overlapping.
                     82:        test("aaa", "a", "b", "aaa")
                     83:
                     84:        // The text occurs at two places, the occurrences overlap.
                     85:        test("aaa", "aa", "b", "aaa")
                     86: }
                     87:
1.47      rillig     88: func (s *Suite) Test_condStr(c *check.C) {
                     89:        t := s.Init(c)
                     90:
                     91:        t.CheckEquals(condStr(true, "T", "F"), "T")
                     92:        t.CheckEquals(condStr(false, "T", "F"), "F")
                     93: }
                     94:
                     95: func (s *Suite) Test_condInt(c *check.C) {
                     96:        t := s.Init(c)
                     97:
                     98:        t.CheckEquals(condInt(true, 123, 456), 123)
                     99:        t.CheckEquals(condInt(false, 123, 456), 456)
                    100: }
                    101:
                    102: func (s *Suite) Test_imax(c *check.C) {
                    103:        t := s.Init(c)
                    104:
                    105:        t.CheckEquals(imax(2, 5), 5)
                    106:        t.CheckEquals(imax(5, 2), 5)
                    107: }
                    108:
                    109: func (s *Suite) Test_imin(c *check.C) {
                    110:        t := s.Init(c)
                    111:
                    112:        t.CheckEquals(imin(2, 5), 2)
                    113:        t.CheckEquals(imin(5, 2), 2)
                    114: }
                    115:
1.30      rillig    116: func (s *Suite) Test_assertNil(c *check.C) {
                    117:        t := s.Init(c)
                    118:
                    119:        assertNil(nil, "this is not an error")
                    120:
                    121:        t.ExpectPanic(
                    122:                func() { assertNil(errors.New("unexpected error"), "Oops") },
                    123:                "Pkglint internal error: Oops: unexpected error")
                    124: }
                    125:
1.31      rillig    126: func (s *Suite) Test_assertNotNil(c *check.C) {
                    127:        t := s.Init(c)
                    128:
                    129:        assertNotNil("this string is not nil")
                    130:
                    131:        t.ExpectPanic(
                    132:                func() { assertNotNil(nil) },
                    133:                "Pkglint internal error: unexpected nil pointer")
                    134:        t.ExpectPanic(
                    135:                func() { var ptr *string; assertNotNil(ptr) },
                    136:                "Pkglint internal error: unexpected nil pointer")
                    137: }
                    138:
1.47      rillig    139: func (s *Suite) Test_assert(c *check.C) {
                    140:        t := s.Init(c)
                    141:
                    142:        assert(true)
                    143:        t.ExpectAssert(func() { assert(false) })
                    144: }
                    145:
1.35      rillig    146: func (s *Suite) Test_isEmptyDir(c *check.C) {
                    147:        t := s.Init(c)
                    148:
                    149:        t.CreateFileLines("CVS/Entries",
                    150:                "dummy")
                    151:        t.CreateFileLines("subdir/CVS/Entries",
                    152:                "dummy")
                    153:
                    154:        t.CheckEquals(isEmptyDir(t.File(".")), true)
                    155:        t.CheckEquals(isEmptyDir(t.File("CVS")), true)
1.37      rillig    156:
                    157:        t.Chdir(".")
                    158:
                    159:        t.CheckEquals(isEmptyDir("."), true)
                    160:        t.CheckEquals(isEmptyDir("CVS"), true)
1.35      rillig    161: }
                    162:
                    163: func (s *Suite) Test_isEmptyDir__and_getSubdirs(c *check.C) {
                    164:        t := s.Init(c)
                    165:
                    166:        t.CreateFileLines("CVS/Entries",
                    167:                "dummy")
                    168:
                    169:        if dir := t.File("."); true {
                    170:                t.CheckEquals(isEmptyDir(dir), true)
1.40      rillig    171:                t.CheckDeepEquals(getSubdirs(dir), []RelPath(nil))
1.35      rillig    172:
                    173:                t.CreateFileLines("somedir/file")
                    174:
                    175:                t.CheckEquals(isEmptyDir(dir), false)
1.40      rillig    176:                t.CheckDeepEquals(getSubdirs(dir), []RelPath{"somedir"})
1.35      rillig    177:        }
                    178:
                    179:        if absent := t.File("nonexistent"); true {
                    180:                t.CheckEquals(isEmptyDir(absent), true) // Counts as empty.
                    181:
                    182:                // The last group from the error message is localized, therefore the matching.
                    183:                t.ExpectFatalMatches(
                    184:                        func() { getSubdirs(absent) },
                    185:                        `FATAL: ~/nonexistent: Cannot be read: open ~/nonexistent: (.+)\n`)
                    186:        }
                    187: }
                    188:
                    189: func (s *Suite) Test_getSubdirs(c *check.C) {
                    190:        t := s.Init(c)
                    191:
                    192:        t.CreateFileLines("subdir/file")
                    193:        t.CreateFileLines("empty/file")
1.36      rillig    194:        c.Check(os.Remove(t.File("empty/file").String()), check.IsNil)
1.35      rillig    195:
1.40      rillig    196:        t.CheckDeepEquals(getSubdirs(t.File(".")), []RelPath{"subdir"})
                    197: }
                    198:
                    199: func (s *Suite) Test_isIgnoredFilename(c *check.C) {
                    200:        t := s.Init(c)
                    201:
                    202:        test := func(filename string, isIgnored bool) {
                    203:                t.CheckEquals(isIgnoredFilename(filename), isIgnored)
                    204:        }
                    205:
                    206:        test("filename.mk", false)
                    207:        test(".gitignore", false)
                    208:        test(".git", true)
                    209:        test(".gitattributes", false)
                    210:        test("CVS", true)
                    211:        test(".svn", true)
                    212:        test(".hg", true)
                    213:
                    214:        // There is actually an IDEA plugin for pkgsrc.
                    215:        // See https://github.com/rillig/intellij-pkgsrc.
                    216:        test(".idea", true)
1.35      rillig    217: }
                    218:
                    219: func (s *Suite) Test_isLocallyModified(c *check.C) {
1.32      rillig    220:        t := s.Init(c)
                    221:
1.35      rillig    222:        unmodified := t.CreateFileLines("unmodified")
                    223:        modTime := time.Unix(1136239445, 0).UTC()
                    224:
1.36      rillig    225:        err := os.Chtimes(unmodified.String(), modTime, modTime)
1.35      rillig    226:        c.Check(err, check.IsNil)
                    227:
1.36      rillig    228:        st, err := os.Lstat(unmodified.String())
1.35      rillig    229:        c.Check(err, check.IsNil)
                    230:
                    231:        // Make sure that the file system has second precision and accuracy.
                    232:        t.CheckDeepEquals(st.ModTime().UTC(), modTime)
                    233:
                    234:        modified := t.CreateFileLines("modified")
                    235:
                    236:        t.CreateFileLines("CVS/Entries",
                    237:                "/unmodified//"+modTime.Format(time.ANSIC)+"//",
                    238:                "/modified//"+modTime.Format(time.ANSIC)+"//",
                    239:                "/enoent//"+modTime.Format(time.ANSIC)+"//")
                    240:
                    241:        t.CheckEquals(isLocallyModified(unmodified), false)
                    242:        t.CheckEquals(isLocallyModified(modified), true)
                    243:        t.CheckEquals(isLocallyModified(t.File("enoent")), true)
                    244:        t.CheckEquals(isLocallyModified(t.File("not_mentioned")), false)
                    245:        t.CheckEquals(isLocallyModified(t.File("subdir/file")), false)
                    246:
                    247:        t.DisableTracing()
                    248:
                    249:        t.CheckEquals(isLocallyModified(t.File("unmodified")), false)
1.11      rillig    250: }
                    251:
1.35      rillig    252: func (s *Suite) Test_tabWidth(c *check.C) {
1.32      rillig    253:        t := s.Init(c)
                    254:
1.35      rillig    255:        t.CheckEquals(tabWidth("12345"), 5)
                    256:        t.CheckEquals(tabWidth("\t"), 8)
                    257:        t.CheckEquals(tabWidth("123\t"), 8)
                    258:        t.CheckEquals(tabWidth("1234567\t"), 8)
                    259:        t.CheckEquals(tabWidth("12345678\t"), 16)
1.1       rillig    260: }
                    261:
1.35      rillig    262: // Since tabWidthAppend is used with logical lines (Line.Text) as well as with
                    263: // raw lines (RawLine.textnl or RawLine.orignl), and since the width only
                    264: // makes sense for a single line, better panic.
                    265: func (s *Suite) Test_tabWidthAppend__panic(c *check.C) {
1.32      rillig    266:        t := s.Init(c)
                    267:
1.35      rillig    268:        t.ExpectAssert(func() { tabWidthAppend(0, "\n") })
1.1       rillig    269: }
                    270:
1.35      rillig    271: func (s *Suite) Test_detab(c *check.C) {
1.32      rillig    272:        t := s.Init(c)
                    273:
1.35      rillig    274:        t.CheckEquals(detab(""), "")
                    275:        t.CheckEquals(detab("\t"), "        ")
                    276:        t.CheckEquals(detab("1234\t9"), "1234    9")
                    277:        t.CheckEquals(detab("1234567\t"), "1234567 ")
                    278:        t.CheckEquals(detab("12345678\t"), "12345678        ")
1.1       rillig    279: }
                    280:
1.35      rillig    281: func (s *Suite) Test_alignWith(c *check.C) {
1.32      rillig    282:        t := s.Init(c)
                    283:
1.35      rillig    284:        test := func(str, other, expected string) {
1.43      rillig    285:                aligned := alignWith(str, other)
                    286:                t.CheckEquals(aligned, expected)
                    287:                t.CheckEquals(hasPrefix(aligned, str), true)
                    288:
                    289:                // It would be unusual to call this function with a string
                    290:                // that itself ends with space.
                    291:                t.CheckEquals(rtrimHspace(aligned), str)
1.35      rillig    292:        }
                    293:
1.43      rillig    294:        // The needed alignment may be empty.
                    295:        // In some contexts like the value of a variable assignment, this
                    296:        // should not happen. In other contexts it's ok.
                    297:        test("", "", "")
1.35      rillig    298:
1.42      rillig    299:        test("VAR=", "1234567", "VAR=   ")
1.35      rillig    300:        test("VAR=", "12345678", "VAR=\t")
1.42      rillig    301:        test("VAR=", "123456789", "VAR=\t ")
1.35      rillig    302:
1.43      rillig    303:        // If the other string is shorter, no extra tab is added.
                    304:        test("1234567890=", "V=", "1234567890=")
1.1       rillig    305: }
                    306:
1.50    ! rillig    307: func (s *Suite) Test_alignmentToWidths(c *check.C) {
        !           308:        t := s.Init(c)
        !           309:
        !           310:        t.CheckEquals(alignmentToWidths(8, 72), "\t\t\t\t\t\t\t\t")
        !           311: }
        !           312:
1.35      rillig    313: func (s *Suite) Test_indent(c *check.C) {
1.32      rillig    314:        t := s.Init(c)
                    315:
1.35      rillig    316:        test := func(width int, ind string) {
                    317:                actual := indent(width)
                    318:
                    319:                t.CheckEquals(actual, ind)
                    320:        }
                    321:
                    322:        test(0, "")
                    323:        test(1, " ")
                    324:        test(7, "       ")
                    325:        test(8, "\t")
                    326:        test(15, "\t       ")
                    327:        test(16, "\t\t")
                    328:        test(72, "\t\t\t\t\t\t\t\t\t")
1.43      rillig    329:        test(79, "\t\t\t\t\t\t\t\t\t       ")
                    330:        test(80, "\t\t\t\t\t\t\t\t\t\t")
                    331:        test(87, "\t\t\t\t\t\t\t\t\t\t       ")
1.1       rillig    332: }
                    333:
1.35      rillig    334: func (s *Suite) Test_alignmentAfter(c *check.C) {
1.32      rillig    335:        t := s.Init(c)
                    336:
1.35      rillig    337:        test := func(prefix string, width int, ind string) {
                    338:                actual := alignmentAfter(prefix, width)
                    339:
                    340:                t.CheckEquals(actual, ind)
                    341:        }
                    342:
                    343:        test("", 0, "")
                    344:        test("", 15, "\t       ")
                    345:
                    346:        test("  ", 5, "   ")
                    347:        test("      ", 10, "\t  ")
                    348:
                    349:        test("\t", 15, "       ")
                    350:        test(" \t", 15, "       ")
                    351:        test("       \t", 15, "       ")
                    352:        test("\t    ", 15, "   ")
                    353:
                    354:        test("    ", 16, "\t\t")
1.1       rillig    355:
1.35      rillig    356:        // The desired width must be at least the width of the prefix.
                    357:        t.ExpectAssert(func() { test("\t", 7, "") })
1.1       rillig    358: }
                    359:
1.13      rillig    360: func (s *Suite) Test_shorten(c *check.C) {
1.32      rillig    361:        t := s.Init(c)
                    362:
                    363:        t.CheckEquals(shorten("aaaaa", 3), "aaa...")
                    364:        t.CheckEquals(shorten("aaaaa", 5), "aaaaa")
                    365:        t.CheckEquals(shorten("aaa", 5), "aaa")
1.13      rillig    366: }
                    367:
1.35      rillig    368: func (s *Suite) Test_varnameBase(c *check.C) {
                    369:        t := s.Init(c)
                    370:
                    371:        t.CheckEquals(varnameBase("VAR"), "VAR")
                    372:        t.CheckEquals(varnameBase("VAR.param"), "VAR")
                    373:        t.CheckEquals(varnameBase(".CURDIR"), ".CURDIR")
                    374: }
                    375:
                    376: func (s *Suite) Test_varnameCanon(c *check.C) {
                    377:        t := s.Init(c)
                    378:
                    379:        t.CheckEquals(varnameCanon("VAR"), "VAR")
                    380:        t.CheckEquals(varnameCanon("VAR.param"), "VAR.*")
                    381:        t.CheckEquals(varnameCanon(".CURDIR"), ".CURDIR")
                    382: }
                    383:
                    384: func (s *Suite) Test_varnameParam(c *check.C) {
                    385:        t := s.Init(c)
                    386:
                    387:        t.CheckEquals(varnameParam("VAR"), "")
                    388:        t.CheckEquals(varnameParam("VAR.param"), "param")
                    389:        t.CheckEquals(varnameParam(".CURDIR"), "")
                    390: }
                    391:
                    392: func (s *Suite) Test__regex_ReplaceFirst(c *check.C) {
                    393:        t := s.Init(c)
1.21      rillig    394:
1.35      rillig    395:        m, rest := G.res.ReplaceFirst("a+b+c+d", `(\w)(.)(\w)`, "X")
1.24      rillig    396:
1.35      rillig    397:        c.Assert(m, check.NotNil)
                    398:        t.CheckDeepEquals(m, []string{"a+b", "a", "+", "b"})
                    399:        t.CheckEquals(rest, "X+c+d")
1.24      rillig    400: }
                    401:
1.17      rillig    402: const reMkIncludeBenchmark = `^\.([\t ]*)(s?include)[\t ]+\"([^\"]+)\"[\t ]*(?:#.*)?$`
                    403: const reMkIncludeBenchmarkPositive = `^\.([\t ]*)(s?include)[\t ]+\"(.+)\"[\t ]*(?:#.*)?$`
1.6       rillig    404:
                    405: func Benchmark_match3_buildlink3(b *testing.B) {
                    406:        for i := 0; i < b.N; i++ {
                    407:                match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmark)
                    408:        }
                    409: }
                    410:
                    411: func Benchmark_match3_bsd_pkg_mk(b *testing.B) {
                    412:        for i := 0; i < b.N; i++ {
                    413:                match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmark)
                    414:        }
                    415: }
                    416:
1.14      rillig    417: func Benchmark_match3_same_dir(b *testing.B) {
1.6       rillig    418:        for i := 0; i < b.N; i++ {
                    419:                match3(".include \"options.mk\"", reMkIncludeBenchmark)
                    420:        }
                    421: }
                    422:
                    423: func Benchmark_match3_bsd_pkg_mk_comment(b *testing.B) {
                    424:        for i := 0; i < b.N; i++ {
                    425:                match3(".include \"../../mk/bsd.pkg.mk\"          # infrastructure     ", reMkIncludeBenchmark)
                    426:        }
                    427: }
                    428:
                    429: func Benchmark_match3_buildlink3_positive(b *testing.B) {
                    430:        for i := 0; i < b.N; i++ {
                    431:                match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmarkPositive)
                    432:        }
                    433: }
                    434:
                    435: func Benchmark_match3_bsd_pkg_mk_positive(b *testing.B) {
                    436:        for i := 0; i < b.N; i++ {
                    437:                match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmarkPositive)
                    438:        }
                    439: }
                    440:
1.14      rillig    441: func Benchmark_match3_same_dir_positive(b *testing.B) {
1.6       rillig    442:        for i := 0; i < b.N; i++ {
                    443:                match3(".include \"options.mk\"", reMkIncludeBenchmarkPositive)
                    444:        }
                    445: }
                    446:
                    447: func Benchmark_match3_bsd_pkg_mk_comment_positive(b *testing.B) {
                    448:        for i := 0; i < b.N; i++ {
                    449:                match3(".include \"../../mk/bsd.pkg.mk\"          # infrastructure     ", reMkIncludeBenchmarkPositive)
                    450:        }
                    451: }
                    452:
                    453: func Benchmark_match3_explicit(b *testing.B) {
                    454:        for i := 0; i < b.N; i++ {
                    455:                MatchMkInclude(".include \"../../mk/bsd.pkg.mk\"          # infrastructure     ")
                    456:        }
                    457: }
1.9       rillig    458:
                    459: func emptyToNil(slice []string) []string {
                    460:        if len(slice) == 0 {
                    461:                return nil
                    462:        }
                    463:        return slice
                    464: }
1.10      rillig    465:
1.46      rillig    466: func (s *Suite) Test_containsVarUse(c *check.C) {
1.40      rillig    467:        t := s.Init(c)
                    468:
                    469:        test := func(str string, containsVar bool) {
1.46      rillig    470:                t.CheckEquals(containsVarUse(str), containsVar)
1.40      rillig    471:        }
                    472:
                    473:        test("", false)
                    474:        test("$", false) // A syntax error.
                    475:
                    476:        // See the bmake manual page.
1.41      rillig    477:        test("$>", true) // .ALLSRC
                    478:        test("$!", true) // .ARCHIVE
                    479:        test("$<", true) // .IMPSRC
                    480:        test("$%", true) // .MEMBER
                    481:        test("$?", true) // .OODATE
                    482:        test("$*", true) // .PREFIX
                    483:        test("$@", true) // .TARGET
1.40      rillig    484:
1.41      rillig    485:        test("$V", true)
                    486:        test("$v", true)
1.40      rillig    487:        test("${Var}", true)
                    488:        test("${VAR.${param}}", true)
                    489:        test("$(VAR)", true)
                    490:
1.41      rillig    491:        test("$$", false)      // An escaped dollar character.
                    492:        test("$$(VAR)", false) // An escaped dollar character; probably a subshell.
                    493:        test("$${VAR}", false) // An escaped dollar character; probably a shell variable.
                    494:        test("$$VAR", false)   // An escaped dollar character.
1.40      rillig    495: }
                    496:
1.35      rillig    497: func (s *Suite) Test_Once(c *check.C) {
1.32      rillig    498:        t := s.Init(c)
                    499:
1.35      rillig    500:        var once Once
1.32      rillig    501:
1.35      rillig    502:        t.CheckEquals(once.FirstTime("str"), true)
                    503:        t.CheckEquals(once.FirstTime("str"), false)
                    504:        t.CheckEquals(once.FirstTimeSlice("str"), false)
                    505:        t.CheckEquals(once.FirstTimeSlice("str", "str2"), true)
                    506:        t.CheckEquals(once.FirstTimeSlice("str", "str2"), false)
                    507: }
1.32      rillig    508:
1.35      rillig    509: func (s *Suite) Test_Once__trace(c *check.C) {
                    510:        t := s.Init(c)
1.32      rillig    511:
1.35      rillig    512:        var once Once
                    513:        once.Trace = true
1.32      rillig    514:
1.35      rillig    515:        t.CheckEquals(once.FirstTime("str"), true)
                    516:        t.CheckEquals(once.FirstTime("str"), false)
                    517:        t.CheckEquals(once.FirstTimeSlice("str"), false)
                    518:        t.CheckEquals(once.FirstTimeSlice("str", "str2"), true)
                    519:        t.CheckEquals(once.FirstTimeSlice("str", "str2"), false)
1.32      rillig    520:
1.35      rillig    521:        t.CheckOutputLines(
                    522:                "FirstTime: str",
                    523:                "FirstTime: str, str2")
1.32      rillig    524: }
                    525:
1.35      rillig    526: func (s *Suite) Test_Scope__no_tracing(c *check.C) {
1.13      rillig    527:        t := s.Init(c)
                    528:
1.35      rillig    529:        scope := NewScope()
                    530:        scope.Define("VAR.param", t.NewMkLine("fname.mk", 3, "VAR.param=\tvalue"))
                    531:        t.DisableTracing()
1.13      rillig    532:
1.35      rillig    533:        t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true)
                    534:        t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true)
                    535:        t.CheckEquals(scope.IsDefinedSimilar("OTHER"), false)
                    536: }
1.13      rillig    537:
1.35      rillig    538: func (s *Suite) Test_Scope__commented_varassign(c *check.C) {
                    539:        t := s.Init(c)
1.13      rillig    540:
1.35      rillig    541:        mkline := t.NewMkLine("mk/defaults/mk.conf", 3, "#VAR=default")
                    542:        scope := NewScope()
                    543:        scope.Define("VAR", mkline)
1.13      rillig    544:
1.35      rillig    545:        t.CheckEquals(scope.IsDefined("VAR"), false)
                    546:        t.Check(scope.FirstDefinition("VAR"), check.IsNil)
                    547:        t.Check(scope.LastDefinition("VAR"), check.IsNil)
1.13      rillig    548:
1.35      rillig    549:        t.CheckEquals(scope.Mentioned("VAR"), mkline)
                    550:        t.CheckEquals(scope.Commented("VAR"), mkline)
1.13      rillig    551:
1.49      rillig    552:        value, found, indeterminate := scope.LastValueFound("VAR")
1.35      rillig    553:        t.CheckEquals(value, "")
                    554:        t.CheckEquals(found, false)
1.49      rillig    555:        t.CheckEquals(indeterminate, false)
1.13      rillig    556: }
                    557:
1.23      rillig    558: func (s *Suite) Test_Scope_Define(c *check.C) {
                    559:        t := s.Init(c)
                    560:
                    561:        scope := NewScope()
                    562:
1.49      rillig    563:        test := func(line string, found, indeterminate bool, value string) {
1.48      rillig    564:                mkline := t.NewMkLine("file.mk", 123, line)
                    565:                scope.Define("BUILD_DIRS", mkline)
1.43      rillig    566:
1.49      rillig    567:                actualValue, actualFound, actualIndeterminate := scope.LastValueFound("BUILD_DIRS")
1.43      rillig    568:
1.49      rillig    569:                t.CheckDeepEquals(
                    570:                        []interface{}{actualFound, actualIndeterminate, actualValue},
                    571:                        []interface{}{found, indeterminate, value})
                    572:                t.CheckEquals(scope.vs["BUILD_DIRS"].value, value)
1.43      rillig    573:        }
                    574:
                    575:        test("BUILD_DIRS?=\tdefault",
1.49      rillig    576:                true, false, "default")
1.23      rillig    577:
1.43      rillig    578:        test(
                    579:                "BUILD_DIRS=\tone two three",
1.49      rillig    580:                true, false, "one two three")
1.23      rillig    581:
1.43      rillig    582:        test(
                    583:                "BUILD_DIRS+=\tfour",
1.49      rillig    584:                true, false, "one two three four")
1.23      rillig    585:
                    586:        // Later default assignments do not have an effect.
1.43      rillig    587:        test("BUILD_DIRS?=\tdefault",
1.49      rillig    588:                true, false, "one two three four")
1.23      rillig    589:
1.43      rillig    590:        test("BUILD_DIRS!=\techo dynamic",
1.49      rillig    591:                true, true, "")
1.48      rillig    592:
1.49      rillig    593:        // The shell assignment above sets the variable to an indeterminate
                    594:        // value, after which all further default assignments are ignored.
1.48      rillig    595:        test("BUILD_DIRS?=\tdefault after shell assignment",
1.49      rillig    596:                true, true, "")
1.23      rillig    597: }
                    598:
1.35      rillig    599: func (s *Suite) Test_Scope_Mentioned(c *check.C) {
1.13      rillig    600:        t := s.Init(c)
                    601:
1.35      rillig    602:        assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
                    603:        commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
                    604:        documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
                    605:
1.13      rillig    606:        scope := NewScope()
1.35      rillig    607:        scope.Define("VAR", assigned)
                    608:        scope.Define("COMMENTED", commented)
                    609:        scope.Define("DOCUMENTED", documented)
1.13      rillig    610:
1.35      rillig    611:        t.CheckEquals(scope.Mentioned("VAR"), assigned)
                    612:        t.CheckEquals(scope.Mentioned("COMMENTED"), commented)
                    613:        t.CheckEquals(scope.Mentioned("DOCUMENTED"), documented)
                    614:        t.Check(scope.Mentioned("UNKNOWN"), check.IsNil)
1.13      rillig    615: }
                    616:
1.35      rillig    617: func (s *Suite) Test_Scope_IsDefined(c *check.C) {
1.13      rillig    618:        t := s.Init(c)
                    619:
                    620:        scope := NewScope()
1.35      rillig    621:        scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value"))
                    622:
                    623:        t.CheckEquals(scope.IsDefined("VAR.param"), true)
                    624:        t.CheckEquals(scope.IsDefined("VAR.other"), false)
                    625:        t.CheckEquals(scope.IsDefined("VARIABLE.*"), false)
1.13      rillig    626:
1.35      rillig    627:        t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true)
                    628:        t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true)
                    629:        t.CheckEquals(scope.IsDefinedSimilar("VARIABLE.*"), false)
1.13      rillig    630: }
                    631:
1.35      rillig    632: func (s *Suite) Test_Scope_IsUsed(c *check.C) {
1.13      rillig    633:        t := s.Init(c)
                    634:
1.35      rillig    635:        scope := NewScope()
                    636:        mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}")
                    637:        scope.Use("VAR.param", mkline, VucRunTime)
1.13      rillig    638:
1.35      rillig    639:        t.CheckEquals(scope.IsUsed("VAR.param"), true)
                    640:        t.CheckEquals(scope.IsUsed("VAR.other"), false)
                    641:        t.CheckEquals(scope.IsUsed("VARIABLE.*"), false)
                    642:
                    643:        t.CheckEquals(scope.IsUsedSimilar("VAR.param"), true)
                    644:        t.CheckEquals(scope.IsUsedSimilar("VAR.other"), true)
                    645:        t.CheckEquals(scope.IsUsedSimilar("VARIABLE.*"), false)
1.13      rillig    646: }
                    647:
1.21      rillig    648: func (s *Suite) Test_Scope_FirstDefinition(c *check.C) {
                    649:        t := s.Init(c)
                    650:
                    651:        mkline1 := t.NewMkLine("fname.mk", 3, "VAR=\tvalue")
1.23      rillig    652:        mkline2 := t.NewMkLine("fname.mk", 3, ".if ${SNEAKY::=value}")
1.21      rillig    653:
                    654:        scope := NewScope()
                    655:        scope.Define("VAR", mkline1)
                    656:        scope.Define("SNEAKY", mkline2)
                    657:
1.32      rillig    658:        t.CheckEquals(scope.FirstDefinition("VAR"), mkline1)
1.21      rillig    659:
                    660:        // This call returns nil because it's not a variable assignment
                    661:        // and the calling code typically assumes a variable definition.
                    662:        // These sneaky variables with implicit definition are an edge
                    663:        // case that only few people actually know. It's better that way.
                    664:        t.Check(scope.FirstDefinition("SNEAKY"), check.IsNil)
1.40      rillig    665:
                    666:        t.CheckOutputLines(
                    667:                "ERROR: fname.mk:3: Assignment modifiers like \":=\" " +
                    668:                        "must not be used at all.")
1.21      rillig    669: }
                    670:
1.35      rillig    671: func (s *Suite) Test_Scope_Commented(c *check.C) {
                    672:        t := s.Init(c)
                    673:
                    674:        assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
                    675:        commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
                    676:        documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
                    677:
                    678:        scope := NewScope()
                    679:        scope.Define("VAR", assigned)
                    680:        scope.Define("COMMENTED", commented)
                    681:        scope.Define("DOCUMENTED", documented)
                    682:
                    683:        t.Check(scope.Commented("VAR"), check.IsNil)
                    684:        t.CheckEquals(scope.Commented("COMMENTED"), commented)
                    685:        t.Check(scope.Commented("DOCUMENTED"), check.IsNil)
                    686:        t.Check(scope.Commented("UNKNOWN"), check.IsNil)
                    687: }
                    688:
1.23      rillig    689: func (s *Suite) Test_Scope_LastValue(c *check.C) {
                    690:        t := s.Init(c)
                    691:
                    692:        mklines := t.NewMkLines("file.mk",
1.31      rillig    693:                MkCvsID,
1.23      rillig    694:                "VAR=\tfirst",
                    695:                "VAR=\tsecond",
                    696:                ".if 1",
                    697:                "VAR=\tthird (conditional)",
                    698:                ".endif")
                    699:
                    700:        mklines.Check()
                    701:
1.41      rillig    702:        // TODO: At load time, use loadVars instead of allVars.
                    703:        t.CheckEquals(mklines.allVars.LastValue("VAR"), "third (conditional)")
1.23      rillig    704:
                    705:        t.CheckOutputLines(
                    706:                "WARN: file.mk:2: VAR is defined but not used.")
                    707: }
                    708:
1.47      rillig    709: // Up to 2020-01-06, pkglint wrongly returned "one" as the variable value,
                    710: // even though Makefile.common is included before appending "two".
                    711: func (s *Suite) Test_Scope_LastValue__append_in_multiple_files(c *check.C) {
                    712:        t := s.Init(c)
                    713:
                    714:        t.SetUpPackage("category/package",
                    715:                ".include \"Makefile.common\"",
                    716:                "PLIST_VARS+=\ttwo",
                    717:                "PLIST.two=\tyes")
                    718:        t.CreateFileLines("category/package/Makefile.common",
                    719:                MkCvsID,
                    720:                "PLIST_VARS=\tone",
                    721:                "PLIST.one=\tyes")
                    722:        pkg := NewPackage(t.File("category/package"))
                    723:        t.FinishSetUp()
                    724:
                    725:        pkg.Check()
                    726:
                    727:        t.CheckEquals(pkg.vars.LastValue("PLIST_VARS"), "one two")
                    728: }
                    729:
1.35      rillig    730: func (s *Suite) Test_Scope_DefineAll(c *check.C) {
1.21      rillig    731:        t := s.Init(c)
                    732:
1.35      rillig    733:        src := NewScope()
1.21      rillig    734:
1.35      rillig    735:        dst := NewScope()
                    736:        dst.DefineAll(src)
1.21      rillig    737:
1.49      rillig    738:        c.Check(dst.vs, check.HasLen, 0)
1.28      rillig    739:
1.35      rillig    740:        src.Define("VAR", t.NewMkLine("file.mk", 1, "VAR=value"))
                    741:        dst.DefineAll(src)
1.28      rillig    742:
1.35      rillig    743:        t.CheckEquals(dst.IsDefined("VAR"), true)
1.28      rillig    744: }
                    745:
1.13      rillig    746: func (s *Suite) Test_naturalLess(c *check.C) {
1.32      rillig    747:        t := s.Init(c)
1.21      rillig    748:
1.32      rillig    749:        var elements = []string{
                    750:                "",
                    751:                // Numbers are always considered smaller than other characters.
                    752:                "0", "000", "0000", "5", "7", "00011", "12", "00012", "000111",
                    753:                "!", "a", "a0", "a ", "aa", "ab", "b"}
1.21      rillig    754:
1.32      rillig    755:        test := func(i int, ie string, j int, je string) {
                    756:                actual := naturalLess(ie, je)
                    757:                expected := i < j
                    758:                if actual != expected {
                    759:                        t.CheckDeepEquals(
                    760:                                []interface{}{i, ie, j, je, actual},
                    761:                                []interface{}{i, ie, j, je, expected})
                    762:                }
                    763:        }
1.21      rillig    764:
1.32      rillig    765:        for i, ie := range elements {
                    766:                for j, je := range elements {
                    767:                        test(i, ie, j, je)
                    768:                }
                    769:        }
1.14      rillig    770: }
                    771:
                    772: func (s *Suite) Test_FileCache(c *check.C) {
                    773:        t := s.Init(c)
                    774:
1.18      rillig    775:        t.EnableTracingToLog()
                    776:
1.14      rillig    777:        cache := NewFileCache(3)
                    778:
                    779:        lines := t.NewLines("Makefile",
1.31      rillig    780:                MkCvsID,
1.14      rillig    781:                "# line 2")
                    782:
                    783:        c.Check(cache.Get("Makefile", 0), check.IsNil)
1.32      rillig    784:        t.CheckEquals(cache.hits, 0)
                    785:        t.CheckEquals(cache.misses, 1)
1.14      rillig    786:
                    787:        cache.Put("Makefile", 0, lines)
                    788:        c.Check(cache.Get("Makefile", MustSucceed|LogErrors), check.IsNil) // Wrong LoadOptions.
                    789:
                    790:        linesFromCache := cache.Get("Makefile", 0)
1.39      rillig    791:        t.CheckEquals(linesFromCache.Filename, NewCurrPath("Makefile"))
1.17      rillig    792:        c.Check(linesFromCache.Lines, check.HasLen, 2)
1.48      rillig    793:        t.CheckEquals(linesFromCache.Lines[0].Filename(), NewCurrPath("Makefile"))
1.14      rillig    794:
                    795:        // Cache keys are normalized using path.Clean.
                    796:        linesFromCache2 := cache.Get("./Makefile", 0)
1.39      rillig    797:        t.CheckEquals(linesFromCache2.Filename, NewCurrPath("./Makefile"))
1.17      rillig    798:        c.Check(linesFromCache2.Lines, check.HasLen, 2)
1.48      rillig    799:        t.CheckEquals(linesFromCache2.Lines[0].Filename(), NewCurrPath("./Makefile"))
1.14      rillig    800:
                    801:        cache.Put("file1.mk", 0, lines)
                    802:        cache.Put("file2.mk", 0, lines)
                    803:
                    804:        // Now the cache is full. All three entries can be retrieved.
                    805:        c.Check(cache.Get("Makefile", 0), check.NotNil)
                    806:        c.Check(cache.Get("file1.mk", 0), check.NotNil)
                    807:        c.Check(cache.Get("file2.mk", 0), check.NotNil)
                    808:
                    809:        // Adding another entry removes all entries with minimum count,
                    810:        // which currently are file1.mk and file2.mk.
                    811:        // Makefile is still in the cache because it was accessed once.
                    812:        cache.Put("file3.mk", 0, lines)
                    813:
                    814:        c.Check(cache.Get("Makefile", 0), check.NotNil)
                    815:        c.Check(cache.Get("file1.mk", 0), check.IsNil)
                    816:        c.Check(cache.Get("file2.mk", 0), check.IsNil)
                    817:        c.Check(cache.Get("file3.mk", 0), check.NotNil)
                    818:
                    819:        cache.Evict("Makefile")
                    820:
                    821:        c.Check(cache.Get("Makefile", 0), check.IsNil)
                    822:        c.Check(cache.table, check.HasLen, 1)
                    823:        c.Check(cache.mapping, check.HasLen, 1)
1.32      rillig    824:        t.CheckEquals(cache.hits, 7)
                    825:        t.CheckEquals(cache.misses, 5)
1.17      rillig    826:
                    827:        t.CheckOutputLines(
1.18      rillig    828:                "TRACE:   FileCache \"Makefile\" with count 4.",
                    829:                "TRACE:   FileCache \"file1.mk\" with count 2.",
                    830:                "TRACE:   FileCache \"file2.mk\" with count 2.",
                    831:                "TRACE:   FileCache.Evict \"file2.mk\" with count 2.",
                    832:                "TRACE:   FileCache.Evict \"file1.mk\" with count 2.",
                    833:                "TRACE:   FileCache.Halve \"Makefile\" with count 4.")
1.17      rillig    834: }
                    835:
1.29      rillig    836: func (s *Suite) Test_FileCache_removeOldEntries__branch_coverage(c *check.C) {
                    837:        t := s.Init(c)
                    838:
                    839:        t.EnableTracingToLog()
                    840:        G.Testing = false
                    841:
                    842:        lines := t.NewLines("filename.mk",
1.31      rillig    843:                MkCvsID)
1.29      rillig    844:        cache := NewFileCache(3)
                    845:        cache.Put("filename1.mk", 0, lines)
                    846:        cache.Put("filename2.mk", 0, lines)
                    847:        cache.Get("filename2.mk", 0)
                    848:        cache.Get("filename2.mk", 0)
                    849:        cache.Put("filename3.mk", 0, lines)
                    850:        cache.Put("filename4.mk", 0, lines)
                    851:
                    852:        t.CheckOutputLines(
                    853:                "TRACE:   FileCache.Evict \"filename3.mk\" with count 1.",
                    854:                "TRACE:   FileCache.Evict \"filename1.mk\" with count 1.",
                    855:                "TRACE:   FileCache.Halve \"filename2.mk\" with count 3.")
                    856: }
                    857:
1.30      rillig    858: func (s *Suite) Test_FileCache_removeOldEntries__no_tracing(c *check.C) {
                    859:        t := s.Init(c)
                    860:
                    861:        t.DisableTracing()
                    862:
                    863:        lines := t.NewLines("filename.mk",
1.31      rillig    864:                MkCvsID)
1.30      rillig    865:        cache := NewFileCache(3)
                    866:        cache.Put("filename1.mk", 0, lines)
                    867:        cache.Put("filename2.mk", 0, lines)
                    868:        cache.Get("filename2.mk", 0)
                    869:        cache.Get("filename2.mk", 0)
                    870:        cache.Put("filename3.mk", 0, lines)
                    871:        cache.Put("filename4.mk", 0, lines)
                    872:
                    873:        t.CheckOutputEmpty()
                    874: }
                    875:
                    876: // Covers the newLen > 0 condition.
                    877: func (s *Suite) Test_FileCache_removeOldEntries__zero_capacity(c *check.C) {
                    878:        t := s.Init(c)
                    879:
                    880:        lines := t.NewLines("filename.mk",
1.31      rillig    881:                MkCvsID)
1.30      rillig    882:        cache := NewFileCache(1)
                    883:        cache.Put("filename1.mk", 0, lines)
                    884:
                    885:        // This call removes all existing entries from the cache,
                    886:        // as the cache's capacity is only 1.
                    887:        cache.Put("filename2.mk", 0, lines)
                    888: }
                    889:
                    890: func (s *Suite) Test_FileCache_Evict__sort(c *check.C) {
                    891:        t := s.Init(c)
                    892:
                    893:        lines := t.NewLines("filename.mk",
1.31      rillig    894:                MkCvsID)
1.30      rillig    895:        cache := NewFileCache(10)
                    896:        cache.Put("filename0.mk", 0, lines)
                    897:        cache.Put("filename1.mk", 0, lines)
                    898:        cache.Put("filename2.mk", 0, lines)
                    899:        cache.Put("filename3.mk", 0, lines)
                    900:        cache.Put("filename4.mk", 0, lines)
                    901:        cache.Put("filename5.mk", 0, lines)
                    902:        cache.Put("filename6.mk", 0, lines)
                    903:        cache.Put("filename7.mk", 0, lines)
                    904:        cache.Put("filename8.mk", 0, lines)
                    905:        cache.Put("filename9.mk", 0, lines)
                    906:
                    907:        cache.Evict("filename5.mk")
                    908:
                    909:        t.Check(cache.table, check.HasLen, 9)
                    910:        t.Check(cache.Get("filename5.mk", 0), check.IsNil)
                    911:        t.Check(cache.Get("filename6.mk", 0), check.NotNil)
                    912: }
                    913:
1.34      rillig    914: func (s *Suite) Test_bmakeHelp(c *check.C) {
1.32      rillig    915:        t := s.Init(c)
                    916:
1.34      rillig    917:        t.CheckEquals(bmakeHelp("subst"), confMake+" help topic=subst")
1.13      rillig    918: }
1.18      rillig    919:
                    920: func (s *Suite) Test_wrap(c *check.C) {
1.32      rillig    921:        t := s.Init(c)
1.18      rillig    922:
                    923:        wrapped := wrap(20,
                    924:                "See the pkgsrc guide, section \"Package components, Makefile\":",
                    925:                "https://www.NetBSD.org/doc/pkgsrc/pkgsrc.html#components.Makefile.",
                    926:                "",
                    927:                "For more information, ask on the tech-pkg@NetBSD.org mailing list.",
                    928:                "",
                    929:                "\tpreformatted line 1",
                    930:                "\tpreformatted line 2",
                    931:                "",
                    932:                "    intentionally indented",
                    933:                "*   itemization",
                    934:                "",
                    935:                "Normal",
                    936:                "text",
                    937:                "continues",
                    938:                "here",
                    939:                "with",
                    940:                "linebreaks.",
                    941:                "",
1.22      rillig    942:                "Sentence one.  Sentence two.",
                    943:                "",
                    944:                "A\tB\tC\tD",
                    945:                "E\tveryVeryVeryVeryVeryVeryVeryVeryLong")
1.18      rillig    946:
                    947:        expected := []string{
                    948:                "See the pkgsrc",
                    949:                "guide, section",
                    950:                "\"Package components,",
                    951:                "Makefile\":",
                    952:                "https://www.NetBSD.org/doc/pkgsrc/pkgsrc.html#components.Makefile.",
                    953:                "",
                    954:                "For more",
                    955:                "information, ask on",
                    956:                "the",
                    957:                "tech-pkg@NetBSD.org",
                    958:                "mailing list.",
                    959:                "",
                    960:                "\tpreformatted line 1",
                    961:                "\tpreformatted line 2",
                    962:                "",
                    963:                "    intentionally indented",
                    964:                "*   itemization",
                    965:                "",
                    966:                "Normal text",
                    967:                "continues here with",
                    968:                "linebreaks.",
                    969:                "",
                    970:                "Sentence one.",
1.22      rillig    971:                "Sentence two.",
                    972:                "",
                    973:                "A\tB\tC\tD E",
                    974:                "veryVeryVeryVeryVeryVeryVeryVeryLong"}
1.18      rillig    975:
1.32      rillig    976:        t.CheckDeepEquals(wrapped, expected)
1.18      rillig    977: }
                    978:
                    979: func (s *Suite) Test_escapePrintable(c *check.C) {
1.32      rillig    980:        t := s.Init(c)
                    981:
                    982:        t.CheckEquals(escapePrintable(""), "")
                    983:        t.CheckEquals(escapePrintable("ASCII only~\n\t"), "ASCII only~\n\t")
                    984:        t.CheckEquals(escapePrintable("Beep \u0007 control \u001F"), "Beep <U+0007> control <U+001F>")
                    985:        t.CheckEquals(escapePrintable("Bad \xFF character"), "Bad <0xFF> character")
                    986:        t.CheckEquals(escapePrintable("Unicode \uFFFD replacement"), "Unicode <U+FFFD> replacement")
1.18      rillig    987: }
1.22      rillig    988:
                    989: func (s *Suite) Test_stringSliceLess(c *check.C) {
1.32      rillig    990:        t := s.Init(c)
                    991:
1.22      rillig    992:        var elements = [][][]string{
                    993:                {nil, {}},
                    994:                {{"a"}},
                    995:                {{"a", "a"}},
                    996:                {{"a", "b"}},
                    997:                {{"b"}},
                    998:                {{"b", "a"}}}
                    999:
                   1000:        test := func(i int, iElement []string, j int, jElement []string) {
                   1001:                actual := stringSliceLess(iElement, jElement)
                   1002:                expected := i < j
                   1003:                if actual != expected {
1.32      rillig   1004:                        t.CheckDeepEquals(
1.22      rillig   1005:                                []interface{}{i, iElement, j, jElement, actual},
                   1006:                                []interface{}{i, iElement, j, jElement, expected})
                   1007:                }
                   1008:        }
                   1009:
                   1010:        for i, iElements := range elements {
                   1011:                for j, jElements := range elements {
                   1012:                        for _, iElement := range iElements {
                   1013:                                for _, jElement := range jElements {
                   1014:                                        test(i, iElement, j, jElement)
                   1015:                                }
                   1016:                        }
                   1017:                }
                   1018:        }
                   1019: }
                   1020:
                   1021: func (s *Suite) Test_joinSkipEmpty(c *check.C) {
                   1022:        t := s.Init(c)
                   1023:
1.32      rillig   1024:        t.CheckDeepEquals(
1.22      rillig   1025:                joinSkipEmpty(", ", "", "one", "", "", "two", "", "three"),
                   1026:                "one, two, three")
                   1027: }
                   1028:
1.49      rillig   1029: func (s *Suite) Test_joinCambridge(c *check.C) {
1.22      rillig   1030:        t := s.Init(c)
                   1031:
1.32      rillig   1032:        t.CheckDeepEquals(
1.49      rillig   1033:                joinCambridge("and", "", "one", "", "", "two", "", "three"),
1.22      rillig   1034:                "one, two and three")
                   1035:
1.32      rillig   1036:        t.CheckDeepEquals(
1.49      rillig   1037:                joinCambridge("and", "", "one", "", ""),
1.22      rillig   1038:                "one")
                   1039: }
                   1040:
1.49      rillig   1041: func (s *Suite) Test_joinOxford(c *check.C) {
1.22      rillig   1042:        t := s.Init(c)
                   1043:
1.32      rillig   1044:        t.CheckDeepEquals(
1.49      rillig   1045:                joinOxford("and", "", "one", "", "", "two", "", "three"),
1.22      rillig   1046:                "one, two, and three")
                   1047: }
                   1048:
1.31      rillig   1049: func (s *Suite) Test_newPathMatcher(c *check.C) {
                   1050:        t := s.Init(c)
                   1051:
                   1052:        test := func(pattern string, matchType pathMatchType, matchPattern string) {
1.32      rillig   1053:                t.CheckEquals(*newPathMatcher(pattern), pathMatcher{matchType, matchPattern, pattern})
1.31      rillig   1054:        }
                   1055:
                   1056:        testPanic := func(pattern string) {
                   1057:                t.ExpectPanic(
                   1058:                        func() { _ = newPathMatcher(pattern) },
                   1059:                        "Pkglint internal error")
                   1060:        }
                   1061:
                   1062:        testPanic("*.[0123456]")
                   1063:        testPanic("file.???")
                   1064:        testPanic("*.???")
                   1065:        test("", pmExact, "")
                   1066:        test("exact", pmExact, "exact")
                   1067:        test("*.mk", pmSuffix, ".mk")
                   1068:        test("Makefile.*", pmPrefix, "Makefile.")
                   1069:        testPanic("*.*")
                   1070:        testPanic("**")
                   1071:        testPanic("a*b")
                   1072:        testPanic("[")
                   1073:        testPanic("malformed[")
                   1074: }
                   1075:
                   1076: func (s *Suite) Test_pathMatcher_matches(c *check.C) {
1.32      rillig   1077:        t := s.Init(c)
1.31      rillig   1078:
                   1079:        test := func(pattern string, subject string, expected bool) {
                   1080:                matcher := newPathMatcher(pattern)
1.32      rillig   1081:                t.CheckEquals(matcher.matches(subject), expected)
1.31      rillig   1082:        }
                   1083:
                   1084:        test("", "", true)
                   1085:        test("", "any", false)
                   1086:        test("exact", "exact", true)
                   1087:        test("exact", "different", false)
                   1088:
                   1089:        test("*.mk", "filename.mk", true)
                   1090:        test("*.mk", "filename.txt", false)
                   1091:        test("*.mk", "filename.mkx", false)
                   1092:        test("*.mk", ".mk", true)
                   1093:
                   1094:        test("Makefile.*", "Makefile", false)
                   1095:        test("Makefile.*", "Makefile.", true)
                   1096:        test("Makefile.*", "Makefile.txt", true)
                   1097:        test("Makefile.*", "makefile.txt", false)
                   1098: }
                   1099:
1.22      rillig   1100: func (s *Suite) Test_StringInterner(c *check.C) {
                   1101:        t := s.Init(c)
                   1102:
                   1103:        si := NewStringInterner()
                   1104:
1.32      rillig   1105:        t.CheckEquals(si.Intern(""), "")
                   1106:        t.CheckEquals(si.Intern("Hello"), "Hello")
                   1107:        t.CheckEquals(si.Intern("Hello, world"), "Hello, world")
                   1108:        t.CheckEquals(si.Intern("Hello, world"[0:5]), "Hello")
1.22      rillig   1109: }
1.34      rillig   1110:
                   1111: func (s *Suite) Test_shquote(c *check.C) {
                   1112:        t := s.Init(c)
                   1113:
                   1114:        test := func(in, out string) {
                   1115:                t.CheckEquals(shquote(in), out)
                   1116:        }
                   1117:
                   1118:        test("", "''")
                   1119:        test("'", "''\\'''")
                   1120:        test("simple", "simple")
                   1121:        test("~", "'~'")
                   1122: }
1.38      rillig   1123:
                   1124: func (s *Suite) Test_LazyStringBuilder_WriteByte__exact_match(c *check.C) {
                   1125:        t := s.Init(c)
                   1126:
                   1127:        sb := NewLazyStringBuilder("word")
                   1128:
                   1129:        sb.WriteByte('w')
                   1130:        sb.WriteByte('o')
                   1131:        sb.WriteByte('r')
                   1132:        sb.WriteByte('d')
                   1133:
                   1134:        t.CheckEquals(sb.String(), "word")
                   1135:        c.Check(sb.buf, check.IsNil)
                   1136: }
                   1137:
                   1138: func (s *Suite) Test_LazyStringBuilder_WriteByte__longer_than_expected(c *check.C) {
                   1139:        t := s.Init(c)
                   1140:
                   1141:        sb := NewLazyStringBuilder("word")
                   1142:        sb.WriteByte('w')
                   1143:        sb.WriteByte('o')
                   1144:        sb.WriteByte('r')
                   1145:        sb.WriteByte('d')
                   1146:        sb.WriteByte('s')
                   1147:
                   1148:        t.CheckEquals(sb.String(), "words")
                   1149:        t.CheckDeepEquals(sb.buf, []byte{'w', 'o', 'r', 'd', 's'})
                   1150: }
                   1151:
                   1152: func (s *Suite) Test_LazyStringBuilder_WriteByte__shorter_than_expected(c *check.C) {
                   1153:        t := s.Init(c)
                   1154:
                   1155:        sb := NewLazyStringBuilder("word")
                   1156:        sb.WriteByte('w')
                   1157:        sb.WriteByte('o')
                   1158:
                   1159:        t.CheckEquals(sb.String(), "wo")
                   1160:        c.Check(sb.buf, check.IsNil)
                   1161:
                   1162:        sb.WriteByte('r')
                   1163:        sb.WriteByte('d')
                   1164:
                   1165:        t.CheckEquals(sb.String(), "word")
                   1166:        c.Check(sb.buf, check.IsNil)
                   1167: }
                   1168:
                   1169: func (s *Suite) Test_LazyStringBuilder_WriteByte__other_than_expected(c *check.C) {
                   1170:        t := s.Init(c)
                   1171:
                   1172:        sb := NewLazyStringBuilder("word")
                   1173:        sb.WriteByte('w')
                   1174:        sb.WriteByte('o')
                   1175:        sb.WriteByte('l')
                   1176:        sb.WriteByte('f')
                   1177:
                   1178:        t.CheckEquals(sb.String(), "wolf")
                   1179:        t.CheckDeepEquals(sb.buf, []byte{'w', 'o', 'l', 'f'})
                   1180: }
                   1181:
1.39      rillig   1182: func (s *Suite) Test_LazyStringBuilder_writeToBuf__assertion(c *check.C) {
                   1183:        t := s.Init(c)
                   1184:
                   1185:        sb := NewLazyStringBuilder("0123456789abcdef0123456789abcdef")
                   1186:        sb.WriteString("0123456789abcdef0123456789abcdeX")
                   1187:
                   1188:        t.CheckEquals(cap(sb.buf), 32)
                   1189:
                   1190:        sb.Reset("0123456789abcdef")
                   1191:        sb.WriteString("01234567")
                   1192:
                   1193:        // Intentionally violate the invariant of the LazyStringBuilder that
                   1194:        // as long as sb.usingBuf is false, sb.len is at most len(sb.expected).
                   1195:        sb.expected = ""
                   1196:        t.ExpectAssert(func() { sb.writeToBuf('x') })
                   1197: }
                   1198:
1.38      rillig   1199: func (s *Suite) Test_LazyStringBuilder_Reset(c *check.C) {
                   1200:        t := s.Init(c)
                   1201:
                   1202:        sb := NewLazyStringBuilder("word")
                   1203:        sb.WriteByte('w')
                   1204:
                   1205:        sb.Reset("other")
                   1206:
                   1207:        t.CheckEquals(sb.String(), "")
                   1208:
                   1209:        sb.WriteString("word")
                   1210:
                   1211:        t.CheckEquals(sb.String(), "word")
                   1212:        t.CheckEquals(sb.usingBuf, true)
                   1213:        t.CheckDeepEquals(sb.buf, []byte("word"))
                   1214:
                   1215:        sb.Reset("")
                   1216:
                   1217:        t.CheckEquals(sb.String(), "")
                   1218:        t.CheckEquals(sb.usingBuf, false)
                   1219:        t.CheckDeepEquals(sb.buf, []byte("word"))
                   1220:
                   1221:        sb.WriteByte('x')
                   1222:
                   1223:        // Ensure that the buffer is reset properly.
                   1224:        t.CheckEquals(sb.String(), "x")
                   1225:        t.CheckEquals(sb.usingBuf, true)
                   1226:        t.CheckDeepEquals(sb.buf, []byte("x"))
                   1227: }
1.40      rillig   1228:
                   1229: // sortedKeys takes the keys from an arbitrary map,
                   1230: // converts them to strings if necessary,
                   1231: // and then returns them sorted.
                   1232: //
                   1233: // It is only available during tests since it uses reflection.
                   1234: func keys(m interface{}) []string {
                   1235:        var keys []string
                   1236:        for _, key := range reflect.ValueOf(m).MapKeys() {
                   1237:                switch key := key.Interface().(type) {
                   1238:                case fmt.Stringer:
                   1239:                        keys = append(keys, key.String())
                   1240:                default:
                   1241:                        keys = append(keys, key.(string))
                   1242:                }
                   1243:        }
                   1244:        sort.Strings(keys)
                   1245:        return keys
                   1246: }
1.44      rillig   1247:
                   1248: func (s *Suite) Test_interval(c *check.C) {
                   1249:        t := s.Init(c)
                   1250:
                   1251:        i := newInterval()
                   1252:
                   1253:        t.CheckEquals(i.min > i.max, true)
                   1254:
                   1255:        i.add(3)
                   1256:
                   1257:        t.CheckEquals(i.min, 3)
                   1258:        t.CheckEquals(i.max, 3)
                   1259:
                   1260:        i.add(7)
                   1261:
                   1262:        t.CheckEquals(i.min, 3)
                   1263:        t.CheckEquals(i.max, 7)
                   1264:
                   1265:        i.add(-5)
                   1266:
                   1267:        t.CheckEquals(i.min, -5)
                   1268:        t.CheckEquals(i.max, 7)
                   1269: }

CVSweb <webmaster@jp.NetBSD.org>