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

Annotation of pkgsrc/pkgtools/pkglint/files/check_test.go, Revision 1.66

1.30      rillig      1: package pkglint
1.1       rillig      2:
                      3: import (
                      4:        "bytes"
1.6       rillig      5:        "fmt"
1.21      rillig      6:        "io"
1.3       rillig      7:        "io/ioutil"
1.57      rillig      8:        "netbsd.org/pkglint/intqa"
1.25      rillig      9:        "netbsd.org/pkglint/regex"
1.3       rillig     10:        "os"
1.40      rillig     11:        "regexp"
1.60      rillig     12:        "strconv"
1.6       rillig     13:        "strings"
1.1       rillig     14:        "testing"
                     15:
1.15      rillig     16:        "gopkg.in/check.v1"
1.1       rillig     17: )
                     18:
1.44      rillig     19: const CvsID = "$" + "NetBSD$"
                     20: const MkCvsID = "# $" + "NetBSD$"
                     21: const PlistCvsID = "@comment $" + "NetBSD$"
1.16      rillig     22:
1.1       rillig     23: type Suite struct {
1.16      rillig     24:        Tester *Tester
1.11      rillig     25: }
                     26:
1.25      rillig     27: // Init creates and returns a test helper that allows to:
1.17      rillig     28: //
1.28      rillig     29: // * create files for the test:
1.32      rillig     30: // CreateFileLines, SetUpPkgsrc, SetUpPackage
1.25      rillig     31: //
1.28      rillig     32: // * load these files into Line and MkLine objects (for tests spanning multiple files):
1.32      rillig     33: // SetUpFileLines, SetUpFileMkLines
1.25      rillig     34: //
1.28      rillig     35: // * create new in-memory Line and MkLine objects (for simple tests):
                     36: // NewLine, NewLines, NewMkLine, NewMkLines
1.25      rillig     37: //
1.28      rillig     38: // * check the files that have been changed by the --autofix feature:
                     39: // CheckFileLines
1.25      rillig     40: //
1.32      rillig     41: // * check the pkglint diagnostics: CheckOutputEmpty, CheckOutputLines
1.16      rillig     42: func (s *Suite) Init(c *check.C) *Tester {
1.25      rillig     43:
                     44:        // Note: the check.C object from SetUpTest cannot be used here,
                     45:        // and the parameter given here cannot be used in TearDownTest;
                     46:        // see https://github.com/go-check/check/issues/22.
                     47:
1.16      rillig     48:        t := s.Tester // Has been initialized by SetUpTest
1.28      rillig     49:        if t.c != nil {
1.11      rillig     50:                panic("Suite.Init must only be called once.")
                     51:        }
1.28      rillig     52:        t.c = c
1.16      rillig     53:        return t
1.11      rillig     54: }
                     55:
1.16      rillig     56: func (s *Suite) SetUpTest(c *check.C) {
1.38      rillig     57:        t := Tester{c: c, testName: c.TestName()}
1.29      rillig     58:        s.Tester = &t
1.16      rillig     59:
1.54      rillig     60:        G = NewPkglint(&t.stdout, &t.stderr)
1.26      rillig     61:        G.Testing = true
1.16      rillig     62:        trace.Out = &t.stdout
1.28      rillig     63:
1.23      rillig     64:        G.Pkgsrc = NewPkgsrc(t.File("."))
1.16      rillig     65:
1.28      rillig     66:        t.c = c
1.46      rillig     67:        t.SetUpCommandLine("-Wall")    // To catch duplicate warnings
1.52      rillig     68:        G.Todo.Pop()                   // The "." was inserted by default.
1.46      rillig     69:        t.seenSetUpCommandLine = false // This default call doesn't count.
1.1       rillig     70:
1.28      rillig     71:        // To improve code coverage and ensure that trace.Result works
                     72:        // in all cases. The latter cannot be ensured at compile time.
1.24      rillig     73:        t.EnableSilentTracing()
                     74:
                     75:        prevdir, err := os.Getwd()
1.44      rillig     76:        assertNil(err, "Cannot get current working directory: %s", err)
1.57      rillig     77:        t.prevdir = NewCurrPathString(prevdir)
1.44      rillig     78:
                     79:        // No longer usable; see https://github.com/go-check/check/issues/22
                     80:        t.c = nil
1.1       rillig     81: }
                     82:
1.16      rillig     83: func (s *Suite) TearDownTest(c *check.C) {
                     84:        t := s.Tester
1.28      rillig     85:        t.c = nil // No longer usable; see https://github.com/go-check/check/issues/22
1.1       rillig     86:
1.57      rillig     87:        err := os.Chdir(t.prevdir.String())
1.44      rillig     88:        assertNil(err, "Cannot chdir back to previous dir: %s", err)
1.38      rillig     89:
                     90:        if t.seenSetupPkgsrc > 0 && !t.seenFinish && !t.seenMain {
1.56      rillig     91:                t.InternalErrorf("After t.SetupPkgsrc(), either t.FinishSetUp() or t.Main() must be called.")
1.6       rillig     92:        }
1.35      rillig     93:
1.56      rillig     94:        t.ReportUncheckedOutput()
1.16      rillig     95:        t.tmpdir = ""
1.24      rillig     96:        t.DisableTracing()
1.35      rillig     97:
1.42      rillig     98:        G = unusablePkglint()
1.6       rillig     99: }
                    100:
1.57      rillig    101: // Ensures that all test names follow a common naming scheme:
                    102: //
                    103: //  Test_${Type}_${Method}__${description_using_underscores}
1.59      rillig    104: func Test__qa(t *testing.T) {
                    105:        ck := intqa.NewQAChecker(t.Errorf)
1.58      rillig    106:
                    107:        ck.Configure("buildlink3.go", "*", "*", -intqa.EMissingTest)     // TODO
                    108:        ck.Configure("distinfo.go", "*", "*", -intqa.EMissingTest)       // TODO
                    109:        ck.Configure("files.go", "*", "*", -intqa.EMissingTest)          // TODO
                    110:        ck.Configure("licenses.go", "*", "*", -intqa.EMissingTest)       // TODO
                    111:        ck.Configure("line.go", "*", "*", -intqa.EMissingTest)           // TODO
                    112:        ck.Configure("linechecker.go", "*", "*", -intqa.EMissingTest)    // TODO
                    113:        ck.Configure("lineslexer.go", "*", "*", -intqa.EMissingTest)     // TODO
                    114:        ck.Configure("lines.go", "*", "*", -intqa.EMissingTest)          // TODO
                    115:        ck.Configure("logging.go", "*", "*", -intqa.EMissingTest)        // TODO
                    116:        ck.Configure("mkline.go", "*", "*", -intqa.EMissingTest)         // TODO
                    117:        ck.Configure("mklineparser.go", "*", "*", -intqa.EMissingTest)   // TODO
                    118:        ck.Configure("mklinechecker.go", "*", "*", -intqa.EMissingTest)  // TODO
                    119:        ck.Configure("mklines.go", "*", "*", -intqa.EMissingTest)        // TODO
                    120:        ck.Configure("mkparser.go", "*", "*", -intqa.EMissingTest)       // TODO
                    121:        ck.Configure("mkshparser.go", "*", "*", -intqa.EMissingTest)     // TODO
                    122:        ck.Configure("mkshtypes.go", "*", "*", -intqa.EMissingTest)      // TODO
                    123:        ck.Configure("mkshwalker.go", "*", "*", -intqa.EMissingTest)     // TODO
                    124:        ck.Configure("mktokenslexer.go", "*", "*", -intqa.EMissingTest)  // TODO
                    125:        ck.Configure("mktypes.go", "*", "*", -intqa.EMissingTest)        // TODO
                    126:        ck.Configure("options.go", "*", "*", -intqa.EMissingTest)        // TODO
                    127:        ck.Configure("package.go", "*", "*", -intqa.EMissingTest)        // TODO
                    128:        ck.Configure("paragraph.go", "*", "*", -intqa.EMissingTest)      // TODO
                    129:        ck.Configure("patches.go", "*", "*", -intqa.EMissingTest)        // TODO
                    130:        ck.Configure("pkglint.go", "*", "*", -intqa.EMissingTest)        // TODO
                    131:        ck.Configure("pkgsrc.go", "*", "*", -intqa.EMissingTest)         // TODO
                    132:        ck.Configure("redundantscope.go", "*", "*", -intqa.EMissingTest) // TODO
                    133:        ck.Configure("shell.go", "*", "*", -intqa.EMissingTest)          // TODO
                    134:        ck.Configure("shtokenizer.go", "*", "*", -intqa.EMissingTest)    // TODO
                    135:        ck.Configure("shtypes.go", "*", "*", -intqa.EMissingTest)        // TODO
                    136:        ck.Configure("substcontext.go", "*", "*", -intqa.EMissingTest)   // TODO
                    137:        ck.Configure("tools.go", "*", "*", -intqa.EMissingTest)          // TODO
                    138:        ck.Configure("util.go", "*", "*", -intqa.EMissingTest)           // TODO
                    139:        ck.Configure("var.go", "*", "*", -intqa.EMissingTest)            // TODO
1.60      rillig    140:
                    141:        ck.Configure("varalignblock.go", "*", "*", -intqa.EMissingTest)            // TODO
                    142:        ck.Configure("varalignblock.go", "varalignLine", "*", +intqa.EMissingTest) // TODO: remove as redundant
                    143:
                    144:        ck.Configure("vardefs.go", "*", "*", -intqa.EMissingTest)   // TODO
                    145:        ck.Configure("vargroups.go", "*", "*", -intqa.EMissingTest) // TODO
                    146:        ck.Configure("vartype.go", "*", "*", -intqa.EMissingTest)   // TODO
1.58      rillig    147:
1.65      rillig    148:        // Don't require tests for helper methods.
                    149:        ck.Configure("*.go", "VartypeCheck", "[a-z]*", -intqa.EMissingTest)
                    150:
1.58      rillig    151:        // For now, don't require tests for all the test code.
                    152:        // Having good coverage for the main code is more important.
                    153:        ck.Configure("*_test.go", "*", "*", -intqa.EMissingTest)
                    154:
                    155:        // These helper methods are usually so simple that they don't need
                    156:        // separate tests.
                    157:        // They are tested indirectly by the tests of their corresponding checks.
                    158:        ck.Configure("*", "*", "warn[A-Z]*", -intqa.EMissingTest)
                    159:
                    160:        // Generated code doesn't need a unit test.
                    161:        // If any, every grammar production in the corresponding yacc file
                    162:        // should have a unit test.
1.57      rillig    163:        ck.Configure("*yacc.go", "*", "*", intqa.ENone)
1.58      rillig    164:
                    165:        // Type definitions don't need a unit test.
                    166:        // Only functions and methods do.
1.57      rillig    167:        ck.Configure("*", "*", "", -intqa.EMissingTest)
1.58      rillig    168:
                    169:        // The Suite type is used for testing all parts of pkglint.
                    170:        // Therefore its test methods may be everywhere.
1.57      rillig    171:        ck.Configure("*.go", "Suite", "*", -intqa.EMethodsSameFile)
1.60      rillig    172:        ck.Configure("*.go", "Tester", "*", -intqa.EMethodsSameFile)
1.58      rillig    173:
1.57      rillig    174:        ck.Check()
                    175: }
                    176:
1.16      rillig    177: var _ = check.Suite(new(Suite))
1.13      rillig    178:
1.16      rillig    179: func Test(t *testing.T) { check.TestingT(t) }
1.13      rillig    180:
1.16      rillig    181: // Tester provides utility methods for testing pkglint.
                    182: // It is separated from the Suite since the latter contains
                    183: // all the test methods, which makes it difficult to find
                    184: // a method by auto-completion.
                    185: type Tester struct {
1.38      rillig    186:        c        *check.C // Only usable during the test method itself
                    187:        testName string
1.49      rillig    188:        argv     []string // from the last invocation of Tester.SetUpCommandLine
1.38      rillig    189:
1.24      rillig    190:        stdout  bytes.Buffer
                    191:        stderr  bytes.Buffer
1.57      rillig    192:        tmpdir  CurrPath
                    193:        prevdir CurrPath // The current working directory before the test started
                    194:        cwd     RelPath  // relative to tmpdir; see Tester.Chdir
1.38      rillig    195:
1.46      rillig    196:        seenSetUpCommandLine bool
                    197:        seenSetupPkgsrc      int
                    198:        seenFinish           bool
                    199:        seenMain             bool
1.10      rillig    200: }
                    201:
1.32      rillig    202: // SetUpCommandLine simulates a command line for the remainder of the test.
1.16      rillig    203: // See Pkglint.ParseCommandLine.
1.28      rillig    204: //
1.32      rillig    205: // If SetUpCommandLine is not called explicitly in a test, the command line
1.28      rillig    206: // "-Wall" is used, to provide a high code coverage in the tests.
1.32      rillig    207: func (t *Tester) SetUpCommandLine(args ...string) {
1.24      rillig    208:
                    209:        // Prevent tracing from being disabled; see EnableSilentTracing.
                    210:        prevTracing := trace.Tracing
                    211:        defer func() { trace.Tracing = prevTracing }()
                    212:
1.49      rillig    213:        argv := append([]string{"pkglint"}, args...)
                    214:        t.argv = argv
                    215:        exitcode := G.ParseCommandLine(argv)
1.30      rillig    216:        if exitcode != -1 && exitcode != 0 {
1.16      rillig    217:                t.CheckOutputEmpty()
1.28      rillig    218:                t.c.Fatalf("Cannot parse command line: %#v", args)
1.1       rillig    219:        }
1.28      rillig    220:
                    221:        // Duplicate diagnostics often mean that the checking code is run
                    222:        // twice, which is unnecessary.
                    223:        //
                    224:        // It also reveals diagnostics that are logged multiple times per
                    225:        // line and thus can easily get annoying to the pkgsrc developers.
1.56      rillig    226:        //
                    227:        // To avoid running a check multiple times, see Line.once or MkLines.once.
                    228:        G.Logger.verbose = true
1.46      rillig    229:
                    230:        t.seenSetUpCommandLine = true
1.1       rillig    231: }
                    232:
1.32      rillig    233: // SetUpVartypes registers a few hundred variables like MASTER_SITES,
1.19      rillig    234: // WRKSRC, SUBST_SED.*, so that their data types are known to pkglint.
1.28      rillig    235: //
                    236: // Without calling this, there will be many warnings about undefined
                    237: // or unused variables, or unknown shell commands.
                    238: //
1.32      rillig    239: // See SetUpTool for registering tools like echo, awk, perl.
                    240: func (t *Tester) SetUpVartypes() {
1.37      rillig    241:        G.Pkgsrc.vartypes.Init(&G.Pkgsrc)
1.19      rillig    242: }
                    243:
1.32      rillig    244: func (t *Tester) SetUpMasterSite(varname string, urls ...string) {
1.54      rillig    245:        if !G.Pkgsrc.vartypes.IsDefinedExact(varname) {
1.41      rillig    246:                G.Pkgsrc.vartypes.DefineParse(varname, BtFetchURL,
                    247:                        List|SystemProvided,
                    248:                        "buildlink3.mk: none",
                    249:                        "*: use")
                    250:        }
                    251:
1.9       rillig    252:        for _, url := range urls {
1.28      rillig    253:                G.Pkgsrc.registerMasterSite(varname, url)
1.9       rillig    254:        }
                    255: }
                    256:
1.43      rillig    257: // SetUpOption pretends that the given package option is defined in mk/defaults/options.description.
1.53      rillig    258: //
                    259: // In tests, the description may be left empty.
1.32      rillig    260: func (t *Tester) SetUpOption(name, description string) {
1.20      rillig    261:        G.Pkgsrc.PkgOptions[name] = description
                    262: }
                    263:
1.32      rillig    264: func (t *Tester) SetUpTool(name, varname string, validity Validity) *Tool {
1.39      rillig    265:        return G.Pkgsrc.Tools.def(name, varname, false, validity, nil)
1.5       rillig    266: }
                    267:
1.59      rillig    268: // SetUpType defines a variable to have a certain type and access permissions,
                    269: // like in the type definitions in vardefs.go.
                    270: //
                    271: // Example:
                    272: //  SetUpType("PKGPATH", BtPkgpath, DefinedIfInScope|NonemptyIfDefined,
                    273: //      "Makefile, *.mk: default, set, append, use, use-loadtime")
                    274: func (t *Tester) SetUpType(varname string, basicType *BasicType,
                    275:        options vartypeOptions, aclEntries ...string) {
                    276:
                    277:        if len(aclEntries) == 0 {
                    278:                aclEntries = []string{"Makefile, *.mk: default, set, append, use, use-loadtime"}
                    279:        }
                    280:
                    281:        G.Pkgsrc.vartypes.acl(varname, basicType, options, aclEntries...)
                    282:
                    283:        // Make sure that registering the type succeeds.
                    284:        // This is necessary for BtUnknown and guessed types.
                    285:        vartype := G.Pkgsrc.VariableType(nil, varname)
                    286:        t.c.Assert(vartype.basicType, check.Equals, basicType)
                    287: }
                    288:
1.32      rillig    289: // SetUpFileLines creates a temporary file and writes the given lines to it.
1.28      rillig    290: // The file is then read in, without interpreting line continuations.
                    291: //
1.32      rillig    292: // See SetUpFileMkLines for loading a Makefile fragment.
1.57      rillig    293: func (t *Tester) SetUpFileLines(filename RelPath, lines ...string) *Lines {
                    294:        abs := t.CreateFileLines(filename, lines...)
                    295:        return Load(abs, MustSucceed)
1.16      rillig    296: }
1.6       rillig    297:
1.32      rillig    298: // SetUpFileLines creates a temporary file and writes the given lines to it.
1.16      rillig    299: // The file is then read in, handling line continuations for Makefiles.
1.28      rillig    300: //
1.32      rillig    301: // See SetUpFileLines for loading an ordinary file.
1.58      rillig    302: //
                    303: // If the filename is irrelevant for the particular test, take filename.mk.
1.57      rillig    304: func (t *Tester) SetUpFileMkLines(filename RelPath, lines ...string) *MkLines {
1.63      rillig    305:        return t.SetUpFileMkLinesPkg(filename, nil, lines...)
                    306: }
                    307:
                    308: func (t *Tester) SetUpFileMkLinesPkg(filename RelPath, pkg *Package, lines ...string) *MkLines {
1.57      rillig    309:        abs := t.CreateFileLines(filename, lines...)
1.63      rillig    310:        return LoadMk(abs, pkg, MustSucceed)
1.6       rillig    311: }
1.3       rillig    312:
1.35      rillig    313: // LoadMkInclude loads the given Makefile fragment and all the files it includes,
                    314: // merging all the lines into a single MkLines object.
                    315: //
                    316: // This is useful for testing code related to Package.readMakefile.
1.57      rillig    317: func (t *Tester) LoadMkInclude(filename RelPath) *MkLines {
1.44      rillig    318:        var lines []*Line
1.35      rillig    319:
                    320:        // TODO: Include files with multiple-inclusion guard only once.
                    321:        // TODO: Include files without multiple-inclusion guard as often as needed.
                    322:
1.57      rillig    323:        var load func(filename CurrPath)
                    324:        load = func(filename CurrPath) {
1.64      rillig    325:                mklines := NewMkLines(Load(filename, MustSucceed), nil, nil)
1.63      rillig    326:                for _, mkline := range mklines.mklines {
1.35      rillig    327:                        lines = append(lines, mkline.Line)
                    328:
                    329:                        if mkline.IsInclude() {
1.48      rillig    330:                                load(mkline.IncludedFileFull())
1.35      rillig    331:                        }
                    332:                }
                    333:        }
                    334:
1.57      rillig    335:        load(t.File(filename))
1.35      rillig    336:
                    337:        // This assumes that the test files do not contain parse errors.
                    338:        // Otherwise the diagnostics would appear twice.
1.64      rillig    339:        return NewMkLines(NewLines(t.File(filename), lines), nil, nil)
1.35      rillig    340: }
                    341:
1.32      rillig    342: // SetUpPkgsrc sets up a minimal but complete pkgsrc installation in the
1.23      rillig    343: // temporary folder, so that pkglint runs without any errors.
1.32      rillig    344: // Individual files may be overwritten by calling other SetUp* methods.
1.30      rillig    345: //
1.23      rillig    346: // This setup is especially interesting for testing Pkglint.Main.
1.32      rillig    347: func (t *Tester) SetUpPkgsrc() {
1.23      rillig    348:
                    349:        // This file is needed to locate the pkgsrc root directory.
                    350:        // See findPkgsrcTopdir.
                    351:        t.CreateFileLines("mk/bsd.pkg.mk",
1.44      rillig    352:                MkCvsID)
1.23      rillig    353:
                    354:        // See Pkgsrc.loadDocChanges.
                    355:        t.CreateFileLines("doc/CHANGES-2018",
1.44      rillig    356:                CvsID)
1.23      rillig    357:
                    358:        // See Pkgsrc.loadSuggestedUpdates.
                    359:        t.CreateFileLines("doc/TODO",
1.44      rillig    360:                CvsID)
1.23      rillig    361:
1.25      rillig    362:        // Some example licenses so that the tests for whole packages
                    363:        // don't need to define them on their own.
                    364:        t.CreateFileLines("licenses/2-clause-bsd",
                    365:                "Redistribution and use in source and binary forms ...")
                    366:        t.CreateFileLines("licenses/gnu-gpl-v2",
1.29      rillig    367:                "The licenses for most software are designed to take away ...")
1.25      rillig    368:
1.28      rillig    369:        // The various MASTER_SITE_* variables for use in the
                    370:        // MASTER_SITES are defined in this file.
                    371:        //
1.41      rillig    372:        // To define a MASTER_SITE for a pkglint test, call t.SetUpMasterSite.
                    373:        //
1.23      rillig    374:        // See Pkgsrc.loadMasterSites.
                    375:        t.CreateFileLines("mk/fetch/sites.mk",
1.44      rillig    376:                MkCvsID)
1.23      rillig    377:
1.28      rillig    378:        // The options for the PKG_OPTIONS framework are defined here.
                    379:        //
1.23      rillig    380:        // See Pkgsrc.loadPkgOptions.
1.28      rillig    381:        t.CreateFileLines("mk/defaults/options.description",
                    382:                "example-option   Description for the example option",
                    383:                "example-option-without-description")
1.23      rillig    384:
                    385:        // The user-defined variables are read in to check for missing
                    386:        // BUILD_DEFS declarations in the package Makefile.
                    387:        t.CreateFileLines("mk/defaults/mk.conf",
1.44      rillig    388:                MkCvsID)
1.23      rillig    389:
1.28      rillig    390:        // The tool definitions are defined in various files in mk/tools/.
                    391:        // The relevant files are listed in bsd.tools.mk.
                    392:        // The tools that are defined here can be used in USE_TOOLS.
1.23      rillig    393:        t.CreateFileLines("mk/tools/bsd.tools.mk",
                    394:                ".include \"defaults.mk\"")
                    395:        t.CreateFileLines("mk/tools/defaults.mk",
1.44      rillig    396:                MkCvsID)
1.28      rillig    397:
                    398:        // Those tools that are added to USE_TOOLS in bsd.prefs.mk may be
                    399:        // used at load time by packages.
                    400:        t.CreateFileLines("mk/bsd.prefs.mk",
1.44      rillig    401:                MkCvsID)
1.35      rillig    402:        t.CreateFileLines("mk/bsd.fast.prefs.mk",
1.44      rillig    403:                MkCvsID)
1.28      rillig    404:
1.47      rillig    405:        // This file is used for initializing the allowed values for
                    406:        // USE_LANGUAGES; see VarTypeRegistry.compilerLanguages.
                    407:        t.CreateFileLines("mk/compiler.mk",
                    408:                "_CXX_STD_VERSIONS=\tc++ c++14",
                    409:                ".if ${USE_LANGUAGES:Mada} || \\",
                    410:                "    ${USE_LANGUAGES:Mc} || \\",
                    411:                "    ${USE_LANGUAGES:Mc99} || \\",
                    412:                "    ${USE_LANGUAGES:Mobjc} || \\",
                    413:                "    ${USE_LANGUAGES:Mfortran} || \\",
                    414:                "    ${USE_LANGUAGES:Mfortran77}",
                    415:                ".endif")
                    416:
1.28      rillig    417:        // Category Makefiles require this file for the common definitions.
                    418:        t.CreateFileLines("mk/misc/category.mk")
1.38      rillig    419:
1.58      rillig    420:        // TODO
                    421:        // assert(!t.File("mk/bsd.options.mk").IsFile())
                    422:        // t.CreateFileLines("mk/bsd.options.mk")
                    423:
1.38      rillig    424:        t.seenSetupPkgsrc++
1.23      rillig    425: }
                    426:
1.32      rillig    427: // SetUpCategory makes the given category valid by creating a dummy Makefile.
1.28      rillig    428: // After that, it can be mentioned in the CATEGORIES variable of a package.
1.57      rillig    429: func (t *Tester) SetUpCategory(name RelPath) {
1.59      rillig    430:        assert(G.Pkgsrc.Rel(t.File(name)).Count() == 1)
1.28      rillig    431:
1.55      rillig    432:        makefile := name.JoinNoClean("Makefile")
                    433:        if !t.File(makefile).IsFile() {
                    434:                t.CreateFileLines(makefile,
1.44      rillig    435:                        MkCvsID)
1.26      rillig    436:        }
                    437: }
                    438:
1.32      rillig    439: // SetUpPackage sets up all files for a package (including the pkgsrc
1.28      rillig    440: // infrastructure) so that it does not produce any warnings.
1.26      rillig    441: //
                    442: // The given makefileLines start in line 20. Except if they are variable
                    443: // definitions for already existing variables, then they replace that line.
                    444: //
1.31      rillig    445: // Returns the path to the package, ready to be used with Pkglint.Check.
1.28      rillig    446: //
                    447: // After calling this method, individual files can be overwritten as necessary.
1.38      rillig    448: // At the end of the setup phase, t.FinishSetUp() must be called to load all
                    449: // the files.
1.64      rillig    450: //
                    451: // If the package path does not really matter for this test,
                    452: // just use "category/package".
1.57      rillig    453: func (t *Tester) SetUpPackage(pkgpath RelPath, makefileLines ...string) CurrPath {
                    454:        assertf(
                    455:                matches(pkgpath.String(), `^[^/]+/[^/]+$`),
                    456:                "pkgpath %q must have the form \"category/package\"", pkgpath)
1.33      rillig    457:
1.55      rillig    458:        distname := pkgpath.Base()
1.66    ! rillig    459:        category := pkgpath.Dir()
1.33      rillig    460:        if category == "wip" {
                    461:                // To avoid boilerplate CATEGORIES definitions for wip packages.
                    462:                category = "local"
                    463:        }
1.26      rillig    464:
1.32      rillig    465:        t.SetUpPkgsrc()
                    466:        t.SetUpCategory(category)
1.26      rillig    467:
1.55      rillig    468:        t.CreateFileLines(pkgpath.JoinNoClean("DESCR"),
1.26      rillig    469:                "Package description")
1.55      rillig    470:        t.CreateFileLines(pkgpath.JoinNoClean("PLIST"),
1.44      rillig    471:                PlistCvsID,
1.26      rillig    472:                "bin/program")
1.33      rillig    473:
1.34      rillig    474:        // Because the package Makefile includes this file, the check for the
                    475:        // correct ordering of variables is skipped. As of February 2019, the
                    476:        // SetupPackage function does not insert the custom variables in the
                    477:        // correct position. To prevent the tests from having to mention the
                    478:        // unrelated warnings about the variable order, that check is suppressed
                    479:        // here.
1.55      rillig    480:        t.CreateFileLines(pkgpath.JoinNoClean("suppress-varorder.mk"),
1.44      rillig    481:                MkCvsID)
1.34      rillig    482:
1.33      rillig    483:        // This distinfo file contains dummy hashes since pkglint cannot check the
                    484:        // distfiles hashes anyway. It can only check the hashes for the patches.
1.55      rillig    485:        t.CreateFileLines(pkgpath.JoinNoClean("distinfo"),
1.44      rillig    486:                CvsID,
1.26      rillig    487:                "",
1.33      rillig    488:                "SHA1 (distfile-1.0.tar.gz) = 12341234",
                    489:                "RMD160 (distfile-1.0.tar.gz) = 12341234",
                    490:                "SHA512 (distfile-1.0.tar.gz) = 12341234",
1.26      rillig    491:                "Size (distfile-1.0.tar.gz) = 12341234")
                    492:
1.28      rillig    493:        mlines := []string{
1.44      rillig    494:                MkCvsID,
1.26      rillig    495:                "",
1.44      rillig    496:                "DISTNAME=\t" + distname + "-1.0",
1.33      rillig    497:                "#PKGNAME=\tpackage-1.0",
1.55      rillig    498:                "CATEGORIES=\t" + category.String(),
1.26      rillig    499:                "MASTER_SITES=\t# none",
                    500:                "",
                    501:                "MAINTAINER=\tpkgsrc-users@NetBSD.org",
                    502:                "HOMEPAGE=\t# none",
                    503:                "COMMENT=\tDummy package",
                    504:                "LICENSE=\t2-clause-bsd",
1.34      rillig    505:                "",
                    506:                ".include \"suppress-varorder.mk\""}
1.51      rillig    507:        if len(mlines) < 19 {
                    508:                mlines = append(mlines, "")
                    509:        }
                    510:        for len(mlines) < 18 {
                    511:                mlines = append(mlines, "# filler")
                    512:        }
                    513:        if len(mlines) < 19 {
                    514:                mlines = append(mlines, "")
1.26      rillig    515:        }
                    516:
                    517: line:
                    518:        for _, line := range makefileLines {
1.44      rillig    519:                assert(!hasSuffix(line, "\\")) // Continuation lines are not yet supported.
                    520:
1.54      rillig    521:                if m, varname := match1(line, `^#?(\w+)[!+:?]?=`); m {
1.38      rillig    522:                        for i, existingLine := range mlines[:19] {
1.54      rillig    523:                                if hasPrefix(strings.TrimPrefix(existingLine, "#"), varname+"=") {
1.26      rillig    524:                                        mlines[i] = line
                    525:                                        continue line
                    526:                                }
                    527:                        }
                    528:                }
                    529:                mlines = append(mlines, line)
                    530:        }
                    531:
                    532:        mlines = append(mlines,
                    533:                "",
                    534:                ".include \"../../mk/bsd.pkg.mk\"")
                    535:
1.55      rillig    536:        t.CreateFileLines(pkgpath.JoinNoClean("Makefile"),
1.26      rillig    537:                mlines...)
                    538:
                    539:        return t.File(pkgpath)
                    540: }
                    541:
1.28      rillig    542: // CreateFileLines creates a file in the temporary directory and writes the
                    543: // given lines to it.
                    544: //
                    545: // It returns the full path to the created file.
1.57      rillig    546: func (t *Tester) CreateFileLines(filename RelPath, lines ...string) CurrPath {
1.55      rillig    547:        var content strings.Builder
1.16      rillig    548:        for _, line := range lines {
1.28      rillig    549:                content.WriteString(line)
                    550:                content.WriteString("\n")
1.7       rillig    551:        }
1.16      rillig    552:
1.57      rillig    553:        abs := t.File(filename)
1.66    ! rillig    554:        err := os.MkdirAll(abs.Dir().String(), 0777)
1.28      rillig    555:        t.c.Assert(err, check.IsNil)
1.16      rillig    556:
1.57      rillig    557:        err = abs.WriteString(content.String())
1.28      rillig    558:        t.c.Assert(err, check.IsNil)
1.16      rillig    559:
1.57      rillig    560:        G.fileCache.Evict(abs)
1.26      rillig    561:
1.57      rillig    562:        return abs
1.26      rillig    563: }
                    564:
                    565: // CreateFileDummyPatch creates a patch file with the given name in the
                    566: // temporary directory.
1.57      rillig    567: func (t *Tester) CreateFileDummyPatch(filename RelPath) {
                    568:        // Patch files only make sense in category/package/patches directories.
1.59      rillig    569:        assert(G.Pkgsrc.Rel(t.File(filename)).Count() == 4)
1.57      rillig    570:
                    571:        t.CreateFileLines(filename,
1.44      rillig    572:                CvsID,
1.26      rillig    573:                "",
                    574:                "Documentation",
                    575:                "",
                    576:                "--- oldfile",
                    577:                "+++ newfile",
                    578:                "@@ -1 +1 @@",
                    579:                "-old",
                    580:                "+new")
1.7       rillig    581: }
                    582:
1.58      rillig    583: func (t *Tester) CreateFileBuildlink3(filename RelPath, customLines ...string) {
1.57      rillig    584:        // Buildlink3.mk files only make sense in category/package directories.
1.59      rillig    585:        assert(G.Pkgsrc.Rel(t.File(filename)).Count() == 3)
1.57      rillig    586:
1.66    ! rillig    587:        dir := filename.Dir().Clean()
1.55      rillig    588:        lower := dir.Base()
1.44      rillig    589:        // see pkgtools/createbuildlink/files/createbuildlink, "package specific variables"
                    590:        upper := strings.Replace(strings.ToUpper(lower), "-", "_", -1)
1.29      rillig    591:
1.54      rillig    592:        width := tabWidthSlice("BUILDLINK_API_DEPENDS.", lower, "+=\t")
1.29      rillig    593:
                    594:        aligned := func(format string, args ...interface{}) string {
                    595:                msg := sprintf(format, args...)
                    596:                for tabWidth(msg) < width {
                    597:                        msg += "\t"
                    598:                }
                    599:                return msg
                    600:        }
                    601:
1.33      rillig    602:        var lines []string
                    603:        lines = append(lines,
1.44      rillig    604:                MkCvsID,
1.33      rillig    605:                "",
1.29      rillig    606:                sprintf("BUILDLINK_TREE+=\t%s", lower),
1.33      rillig    607:                "",
1.29      rillig    608:                sprintf(".if !defined(%s_BUILDLINK3_MK)", upper),
                    609:                sprintf("%s_BUILDLINK3_MK:=", upper),
1.33      rillig    610:                "",
1.29      rillig    611:                aligned("BUILDLINK_API_DEPENDS.%s+=", lower)+sprintf("%s>=0", lower),
                    612:                aligned("BUILDLINK_PKGSRCDIR.%s?=", lower)+sprintf("../../%s", dir),
                    613:                aligned("BUILDLINK_DEPMETHOD.%s?=", lower)+"build",
1.33      rillig    614:                "")
                    615:        lines = append(lines, customLines...)
                    616:        lines = append(lines,
                    617:                "",
1.29      rillig    618:                sprintf(".endif # %s_BUILDLINK3_MK", upper),
1.33      rillig    619:                "",
1.29      rillig    620:                sprintf("BUILDLINK_TREE+=\t-%s", lower))
1.33      rillig    621:
1.57      rillig    622:        t.CreateFileLines(filename, lines...)
1.29      rillig    623: }
                    624:
1.23      rillig    625: // File returns the absolute path to the given file in the
                    626: // temporary directory. It doesn't check whether that file exists.
1.29      rillig    627: // Calls to Tester.Chdir change the base directory for the relative filename.
1.57      rillig    628: func (t *Tester) File(filename RelPath) CurrPath {
                    629:        if t.tmpdir.IsEmpty() {
                    630:                t.tmpdir = NewCurrPathSlash(t.c.MkDir())
1.11      rillig    631:        }
1.57      rillig    632:        if t.cwd != "" {
                    633:                return NewCurrPath(filename.Clean().AsPath())
1.24      rillig    634:        }
1.58      rillig    635:        return t.tmpdir.JoinClean(filename)
1.16      rillig    636: }
                    637:
1.41      rillig    638: // Copy copies a file inside the temporary directory.
1.57      rillig    639: func (t *Tester) Copy(source, target RelPath) {
                    640:        absSource := t.File(source)
                    641:        absTarget := t.File(target)
1.44      rillig    642:
1.57      rillig    643:        data, err := absSource.ReadString()
1.43      rillig    644:        assertNil(err, "Copy.Read")
1.66    ! rillig    645:        err = os.MkdirAll(absTarget.Dir().Clean().String(), 0777)
1.44      rillig    646:        assertNil(err, "Copy.MkdirAll")
1.57      rillig    647:        err = absTarget.WriteString(data)
1.43      rillig    648:        assertNil(err, "Copy.Write")
1.41      rillig    649: }
                    650:
1.24      rillig    651: // Chdir changes the current working directory to the given subdirectory
                    652: // of the temporary directory, creating it if necessary.
                    653: //
                    654: // After this call, all files loaded from the temporary directory via
1.32      rillig    655: // SetUpFileLines or CreateFileLines or similar methods will use path names
1.24      rillig    656: // relative to this directory.
                    657: //
                    658: // After the test, the previous working directory is restored, so that
                    659: // the other tests are unaffected.
                    660: //
                    661: // As long as this method is not called in a test, the current working
                    662: // directory is indeterminate.
1.57      rillig    663: func (t *Tester) Chdir(dirname RelPath) {
                    664:        if t.cwd != "" {
1.24      rillig    665:                // When multiple calls of Chdir are mixed with calls to CreateFileLines,
1.29      rillig    666:                // the resulting Lines and MkLines variables will use relative filenames,
1.24      rillig    667:                // and these will point to different areas in the file system. This is
                    668:                // usually not indented and therefore prevented.
1.57      rillig    669:                t.c.Fatalf("Chdir must only be called once per test; already in %q.", t.cwd)
1.24      rillig    670:        }
                    671:
1.57      rillig    672:        absDirName := t.File(dirname)
1.55      rillig    673:        assertNil(os.MkdirAll(absDirName.String(), 0700), "MkDirAll")
                    674:        assertNil(os.Chdir(absDirName.String()), "Chdir")
1.57      rillig    675:        t.cwd = dirname
1.36      rillig    676:        G.cwd = absDirName
1.58      rillig    677:        G.Pkgsrc.topdir = NewCurrPath(absDirName.Rel(G.Pkgsrc.topdir).AsPath())
1.24      rillig    678: }
                    679:
1.44      rillig    680: // Remove removes the file or directory from the temporary directory.
                    681: // The file or directory must exist.
1.57      rillig    682: func (t *Tester) Remove(filename RelPath) {
                    683:        abs := t.File(filename)
                    684:        err := os.Remove(abs.String())
1.28      rillig    685:        t.c.Assert(err, check.IsNil)
1.57      rillig    686:        G.fileCache.Evict(abs)
1.26      rillig    687: }
                    688:
1.35      rillig    689: // SetUpHierarchy provides a function for creating hierarchies of MkLines
                    690: // that include each other.
                    691: // The hierarchy is created only in memory, nothing is written to disk.
                    692: //
                    693: //  include, get := t.SetUpHierarchy()
                    694: //
                    695: //  include("including.mk",
                    696: //      include("other.mk",
                    697: //          "VAR= other"),
1.37      rillig    698: //      include("subdir/module.mk",
1.35      rillig    699: //          "VAR= module",
1.37      rillig    700: //          include("subdir/version.mk",
1.35      rillig    701: //              "VAR= version"),
1.37      rillig    702: //          include("subdir/env.mk",
1.35      rillig    703: //              "VAR= env")))
                    704: //
                    705: //  mklines := get("including.mk")
1.41      rillig    706: //  module := get("subdir/module.mk")
1.37      rillig    707: //
                    708: // The filenames passed to the include function are all relative to the
1.57      rillig    709: // same location, which is typically the pkgsrc root or a package directory,
                    710: // but the code doesn't care.
                    711: //
                    712: // The generated .include lines take the relative paths into account.
                    713: // For example, when subdir/module.mk includes subdir/version.mk,
                    714: // the include line is just:
1.37      rillig    715: //  .include "version.mk"
1.35      rillig    716: func (t *Tester) SetUpHierarchy() (
1.57      rillig    717:        include func(filename RelPath, args ...interface{}) *MkLines,
                    718:        get func(path RelPath) *MkLines) {
1.35      rillig    719:
1.57      rillig    720:        files := map[RelPath]*MkLines{}
                    721:        basedir := NewPath(".")
1.35      rillig    722:
1.57      rillig    723:        // includePath returns the path to be used in an .include.
                    724:        //
                    725:        // This is the same mechanism that is used in Pkgsrc.Relpath.
1.58      rillig    726:        includePath := func(including, included RelPath) RelPath {
1.66    ! rillig    727:                fromDir := including.Dir().Clean()
1.58      rillig    728:                to := basedir.Rel(included.AsPath())
1.66    ! rillig    729:                if fromDir == to.Dir() {
1.58      rillig    730:                        return NewRelPathString(to.Base())
1.57      rillig    731:                } else {
                    732:                        return fromDir.Rel(basedir).JoinNoClean(to).CleanDot()
                    733:                }
                    734:        }
                    735:
                    736:        include = func(filename RelPath, args ...interface{}) *MkLines {
1.44      rillig    737:                var lines []*Line
1.35      rillig    738:                lineno := 1
1.57      rillig    739:                relFilename := basedir.Rel(filename.AsPath())
1.35      rillig    740:
                    741:                addLine := func(text string) {
1.58      rillig    742:                        lines = append(lines, t.NewLine(NewCurrPath(relFilename.AsPath()), lineno, text))
1.35      rillig    743:                        lineno++
                    744:                }
                    745:
                    746:                for _, arg := range args {
                    747:                        switch arg := arg.(type) {
                    748:                        case string:
                    749:                                addLine(arg)
1.44      rillig    750:                        case *MkLines:
1.58      rillig    751:                                rel := includePath(relFilename, NewRelPath(arg.lines.Filename.AsPath()))
1.57      rillig    752:                                addLine(sprintf(".include %q", rel))
1.35      rillig    753:                                lines = append(lines, arg.lines.Lines...)
                    754:                        default:
                    755:                                panic("invalid type")
                    756:                        }
                    757:                }
                    758:
1.64      rillig    759:                mklines := NewMkLines(NewLines(NewCurrPath(relFilename.AsPath()), lines), nil, nil)
1.43      rillig    760:                assertf(files[filename] == nil, "MkLines with name %q already exists.", filename)
1.35      rillig    761:                files[filename] = mklines
                    762:                return mklines
                    763:        }
                    764:
1.57      rillig    765:        get = func(filename RelPath) *MkLines {
1.43      rillig    766:                assertf(files[filename] != nil, "MkLines with name %q doesn't exist.", filename)
1.35      rillig    767:                return files[filename]
                    768:        }
                    769:
                    770:        return
                    771: }
                    772:
1.37      rillig    773: // Demonstrates that Tester.SetUpHierarchy uses relative paths for the
                    774: // .include directives.
                    775: func (s *Suite) Test_Tester_SetUpHierarchy(c *check.C) {
                    776:        t := s.Init(c)
                    777:
                    778:        include, get := t.SetUpHierarchy()
                    779:        include("including.mk",
                    780:                include("other.mk",
                    781:                        "VAR= other"),
                    782:                include("subdir/module.mk",
                    783:                        "VAR= module",
                    784:                        include("subdir/version.mk",
                    785:                                "VAR= version"),
                    786:                        include("subdir/env.mk",
                    787:                                "VAR= env")))
                    788:
                    789:        mklines := get("including.mk")
                    790:
1.44      rillig    791:        mklines.ForEach(func(mkline *MkLine) { mkline.Notef("Text is: %s", mkline.Text) })
1.37      rillig    792:
                    793:        t.CheckOutputLines(
                    794:                "NOTE: including.mk:1: Text is: .include \"other.mk\"",
                    795:                "NOTE: other.mk:1: Text is: VAR= other",
                    796:                "NOTE: including.mk:2: Text is: .include \"subdir/module.mk\"",
                    797:                "NOTE: subdir/module.mk:1: Text is: VAR= module",
                    798:                "NOTE: subdir/module.mk:2: Text is: .include \"version.mk\"",
                    799:                "NOTE: subdir/version.mk:1: Text is: VAR= version",
                    800:                "NOTE: subdir/module.mk:3: Text is: .include \"env.mk\"",
                    801:                "NOTE: subdir/env.mk:1: Text is: VAR= env")
                    802: }
                    803:
1.58      rillig    804: // FinishSetup loads the pkgsrc infrastructure.
                    805: // Later changes to the files in mk/ have no effect.
1.38      rillig    806: func (t *Tester) FinishSetUp() {
                    807:        if t.seenSetupPkgsrc == 0 {
1.56      rillig    808:                t.InternalErrorf("Unnecessary t.FinishSetUp() since t.SetUpPkgsrc() has not been called.")
1.38      rillig    809:        }
                    810:
                    811:        if !t.seenFinish {
                    812:                t.seenFinish = true
                    813:                G.Pkgsrc.LoadInfrastructure()
                    814:        } else {
1.56      rillig    815:                t.InternalErrorf("Redundant t.FinishSetup() since it was called multiple times.")
1.38      rillig    816:        }
                    817: }
                    818:
                    819: // Main runs the pkglint main program with the given command line arguments.
1.48      rillig    820: // Other than in the other tests, the -Wall option is not added implicitly.
1.39      rillig    821: //
                    822: // Arguments that name existing files or directories in the temporary test
                    823: // directory are transformed to their actual paths.
1.52      rillig    824: //
                    825: // Does not work in combination with SetUpOption.
1.38      rillig    826: func (t *Tester) Main(args ...string) int {
                    827:        if t.seenFinish && !t.seenMain {
1.56      rillig    828:                t.InternalErrorf("Calling t.FinishSetup() before t.Main() is redundant " +
1.38      rillig    829:                        "since t.Main() loads the pkgsrc infrastructure.")
                    830:        }
1.46      rillig    831:        if t.seenSetUpCommandLine {
1.56      rillig    832:                t.InternalErrorf("Calling t.SetupCommandLine() before t.Main() is redundant " +
1.46      rillig    833:                        "since t.Main() accepts the command line options directly.")
                    834:        }
1.38      rillig    835:
                    836:        t.seenMain = true
                    837:
                    838:        // Reset the logger, for tests where t.Main is called multiple times.
1.42      rillig    839:        G.Logger.errors = 0
                    840:        G.Logger.warnings = 0
                    841:        G.Logger.logged = Once{}
1.38      rillig    842:
1.39      rillig    843:        argv := []string{"pkglint"}
                    844:        for _, arg := range args {
1.58      rillig    845:                fileArg := NewCurrPathSlash(arg)
                    846:                if fileArg.IsAbs() {
                    847:                        argv = append(argv, arg)
                    848:                        continue
                    849:                }
                    850:
                    851:                file := t.File(NewRelPathString(arg))
                    852:                if file.Exists() {
                    853:                        argv = append(argv, file.String())
1.39      rillig    854:                } else {
                    855:                        argv = append(argv, arg)
                    856:                }
                    857:        }
                    858:
1.44      rillig    859:        return G.Main(&t.stdout, &t.stderr, argv)
1.38      rillig    860: }
                    861:
1.28      rillig    862: // Check delegates a check to the check.Check function.
                    863: // Thereby, there is no need to distinguish between c.Check and t.Check
                    864: // in the test code.
                    865: func (t *Tester) Check(obj interface{}, checker check.Checker, args ...interface{}) bool {
                    866:        return t.c.Check(obj, checker, args...)
                    867: }
                    868:
1.45      rillig    869: func (t *Tester) CheckEquals(actual interface{}, expected interface{}) bool {
                    870:        return t.c.Check(actual, check.Equals, expected)
                    871: }
                    872:
1.60      rillig    873: func (t *Tester) CheckEqualsf(actual interface{}, expected interface{}, format string, args ...interface{}) bool {
                    874:        return t.c.Check(actual, check.Equals, expected,
                    875:                check.Commentf(format, args...))
                    876: }
                    877:
1.45      rillig    878: func (t *Tester) CheckDeepEquals(actual interface{}, expected interface{}) bool {
                    879:        return t.c.Check(actual, check.DeepEquals, expected)
                    880: }
                    881:
1.60      rillig    882: func (t *Tester) CheckDeepEqualsf(actual interface{}, expected interface{}, format string, args ...interface{}) bool {
                    883:        return t.c.Check(actual, check.DeepEquals, expected,
                    884:                check.Commentf(format, args...))
                    885: }
                    886:
1.56      rillig    887: // InternalErrorf reports a consistency error in the tests.
                    888: func (t *Tester) InternalErrorf(format string, args ...interface{}) {
                    889:        // It is not possible to panic here since check.v1 would then
                    890:        // ignore all subsequent tests.
1.38      rillig    891:        _, _ = fmt.Fprintf(os.Stderr, "In %s: %s\n", t.testName, sprintf(format, args...))
                    892: }
                    893:
1.25      rillig    894: // ExpectFatal runs the given action and expects that this action calls
                    895: // Line.Fatalf or uses some other way to panic with a pkglintFatal.
1.24      rillig    896: //
                    897: // Usage:
1.25      rillig    898: //  t.ExpectFatal(
                    899: //      func() { /* do something that panics */ },
                    900: //      "FATAL: ~/Makefile:1: Must not be empty")
                    901: func (t *Tester) ExpectFatal(action func(), expectedLines ...string) {
                    902:        defer func() {
                    903:                r := recover()
                    904:                if r == nil {
1.28      rillig    905:                        panic("Expected a pkglint fatal error but didn't get one.")
1.25      rillig    906:                } else if _, ok := r.(pkglintFatal); ok {
                    907:                        t.CheckOutputLines(expectedLines...)
                    908:                } else {
                    909:                        panic(r)
                    910:                }
                    911:        }()
                    912:
                    913:        action()
                    914: }
                    915:
                    916: // ExpectFatalMatches runs the given action and expects that this action
                    917: // calls Line.Fatalf or uses some other way to panic with a pkglintFatal.
1.28      rillig    918: // It then matches the output against the given regular expression.
1.24      rillig    919: //
1.25      rillig    920: // Usage:
                    921: //  t.ExpectFatalMatches(
                    922: //      func() { /* do something that panics */ },
                    923: //      `FATAL: ~/Makefile:1: .*\n`)
                    924: func (t *Tester) ExpectFatalMatches(action func(), expected regex.Pattern) {
                    925:        defer func() {
                    926:                r := recover()
                    927:                if r == nil {
1.28      rillig    928:                        panic("Expected a pkglint fatal error but didn't get one.")
1.25      rillig    929:                } else if _, ok := r.(pkglintFatal); ok {
1.40      rillig    930:                        pattern := `^(?:` + string(expected) + `)$`
                    931:                        t.Check(t.Output(), check.Matches, pattern)
1.25      rillig    932:                } else {
                    933:                        panic(r)
                    934:                }
                    935:        }()
                    936:
                    937:        action()
1.1       rillig    938: }
                    939:
1.28      rillig    940: // ExpectPanic runs the given action and expects that this action calls
1.45      rillig    941: // assert or assertf, or uses some other way to panic.
1.28      rillig    942: //
                    943: // Usage:
                    944: //  t.ExpectPanic(
                    945: //      func() { /* do something that panics */ },
1.45      rillig    946: //      "runtime error: path not found")
1.28      rillig    947: func (t *Tester) ExpectPanic(action func(), expectedMessage string) {
                    948:        t.Check(action, check.Panics, expectedMessage)
                    949: }
                    950:
1.45      rillig    951: // ExpectPanicMatches runs the given action and expects that this action
                    952: // calls assert or assertf, or uses some other way to panic.
                    953: func (t *Tester) ExpectPanicMatches(action func(), expectedMessage string) {
                    954:        t.Check(action, check.PanicMatches, expectedMessage)
                    955: }
                    956:
1.44      rillig    957: // ExpectAssert runs the given action and expects that this action calls assert.
                    958: //
                    959: // Usage:
                    960: //  t.ExpectAssert(
                    961: //      func() { /* do something that panics */ })
                    962: func (t *Tester) ExpectAssert(action func()) {
                    963:        t.Check(action, check.Panics, "Pkglint internal error")
                    964: }
                    965:
1.56      rillig    966: // ExpectDiagnosticsAutofix first runs the given action with -Wall, and
                    967: // then another time with -Wall --autofix.
1.62      rillig    968: //
                    969: // Note that to be realistic, the action must work with completely freshly
                    970: // created objects, to prevent suppression of duplicate diagnostics and
                    971: // changes to the text due to autofixes.
1.59      rillig    972: func (t *Tester) ExpectDiagnosticsAutofix(action func(autofix bool), diagnostics ...string) {
1.56      rillig    973:        t.SetUpCommandLine("-Wall")
1.59      rillig    974:        action(false)
1.56      rillig    975:
                    976:        t.SetUpCommandLine("-Wall", "--autofix")
1.59      rillig    977:        action(true)
1.56      rillig    978:
                    979:        t.CheckOutput(diagnostics)
                    980: }
                    981:
1.28      rillig    982: // NewLine creates an in-memory line with the given text.
                    983: // This line does not correspond to any line in a file.
1.57      rillig    984: func (t *Tester) NewLine(filename CurrPath, lineno int, text string) *Line {
1.66    ! rillig    985:        return NewLine(filename, lineno, text, &RawLine{text + "\n"})
1.15      rillig    986: }
                    987:
1.28      rillig    988: // NewMkLine creates an in-memory line in the Makefile format with the given text.
1.57      rillig    989: func (t *Tester) NewMkLine(filename CurrPath, lineno int, text string) *MkLine {
1.55      rillig    990:        basename := filename.Base()
1.43      rillig    991:        assertf(
1.40      rillig    992:                hasSuffix(basename, ".mk") ||
                    993:                        basename == "Makefile" ||
                    994:                        hasPrefix(basename, "Makefile.") ||
                    995:                        basename == "mk.conf",
1.35      rillig    996:                "filename %q must be realistic, otherwise the variable permissions are wrong", filename)
                    997:
1.50      rillig    998:        return NewMkLineParser().Parse(t.NewLine(filename, lineno, text))
1.15      rillig    999: }
                   1000:
1.43      rillig   1001: func (t *Tester) NewShellLineChecker(text string) *ShellLineChecker {
                   1002:        mklines := t.NewMkLines("filename.mk", text)
                   1003:        return NewShellLineChecker(mklines, mklines.mklines[0])
1.15      rillig   1004: }
                   1005:
1.28      rillig   1006: // NewLines returns a list of simple lines that belong together.
                   1007: //
1.32      rillig   1008: // To work with line continuations like in Makefiles, use SetUpFileMkLines.
1.57      rillig   1009: func (t *Tester) NewLines(filename CurrPath, lines ...string) *Lines {
1.29      rillig   1010:        return t.NewLinesAt(filename, 1, lines...)
1.15      rillig   1011: }
                   1012:
1.28      rillig   1013: // NewLinesAt returns a list of simple lines that belong together.
                   1014: //
1.32      rillig   1015: // To work with line continuations like in Makefiles, use SetUpFileMkLines.
1.57      rillig   1016: func (t *Tester) NewLinesAt(filename CurrPath, firstLine int, texts ...string) *Lines {
1.44      rillig   1017:        lines := make([]*Line, len(texts))
1.15      rillig   1018:        for i, text := range texts {
1.29      rillig   1019:                lines[i] = t.NewLine(filename, i+firstLine, text)
1.15      rillig   1020:        }
1.29      rillig   1021:        return NewLines(filename, lines)
1.15      rillig   1022: }
1.16      rillig   1023:
1.28      rillig   1024: // NewMkLines returns a list of lines in Makefile format,
                   1025: // as if they were parsed from a Makefile fragment,
                   1026: // taking continuation lines into account.
                   1027: //
                   1028: // No actual file is created for the lines;
1.32      rillig   1029: // see SetUpFileMkLines for loading Makefile fragments with line continuations.
1.58      rillig   1030: //
                   1031: // After calling Tester.Chdir, NewMkLines creates the same object as
                   1032: // SetUpFileMkLines, just without anything being written to disk.
                   1033: // This can lead to strange error messages such as "Relative path %s does
                   1034: // not exist." because an intermediate directory in the path does not exist.
                   1035: //
1.64      rillig   1036: // If the filename is irrelevant for the particular test, just use filename.mk.
1.57      rillig   1037: func (t *Tester) NewMkLines(filename CurrPath, lines ...string) *MkLines {
1.63      rillig   1038:        return t.NewMkLinesPkg(filename, nil, lines...)
                   1039: }
                   1040:
                   1041: func (t *Tester) NewMkLinesPkg(filename CurrPath, pkg *Package, lines ...string) *MkLines {
1.55      rillig   1042:        basename := filename.Base()
1.43      rillig   1043:        assertf(
1.35      rillig   1044:                hasSuffix(basename, ".mk") || basename == "Makefile" || hasPrefix(basename, "Makefile."),
                   1045:                "filename %q must be realistic, otherwise the variable permissions are wrong", filename)
                   1046:
1.28      rillig   1047:        var rawText strings.Builder
1.20      rillig   1048:        for _, line := range lines {
1.28      rillig   1049:                rawText.WriteString(line)
                   1050:                rawText.WriteString("\n")
1.20      rillig   1051:        }
1.64      rillig   1052:        return NewMkLines(convertToLogicalLines(filename, rawText.String(), true), pkg, nil)
1.16      rillig   1053: }
                   1054:
                   1055: // Returns and consumes the output from both stdout and stderr.
1.28      rillig   1056: // In the output, the temporary directory is replaced with a tilde (~).
1.16      rillig   1057: func (t *Tester) Output() string {
                   1058:        stdout := t.stdout.String()
                   1059:        stderr := t.stderr.String()
                   1060:
                   1061:        t.stdout.Reset()
                   1062:        t.stderr.Reset()
1.54      rillig   1063:        if G.isUsable() {
1.42      rillig   1064:                G.Logger.logged = Once{}
                   1065:                if G.Logger.out != nil { // Necessary because Main resets the G variable.
                   1066:                        G.Logger.out.state = 0 // Prevent an empty line at the beginning of the next output.
                   1067:                        G.Logger.err.state = 0
                   1068:                }
1.35      rillig   1069:        }
1.16      rillig   1070:
1.43      rillig   1071:        assertf(t.tmpdir != "", "Tester must be initialized before checking the output.")
1.55      rillig   1072:        return strings.Replace(stdout+stderr, t.tmpdir.String(), "~", -1)
1.16      rillig   1073: }
                   1074:
1.28      rillig   1075: // CheckOutputEmpty ensures that the output up to now is empty.
                   1076: //
                   1077: // See CheckOutputLines.
1.16      rillig   1078: func (t *Tester) CheckOutputEmpty() {
1.36      rillig   1079:        t.CheckOutput(nil)
1.16      rillig   1080: }
                   1081:
                   1082: // CheckOutputLines checks that the output up to now equals the given lines.
1.42      rillig   1083: //
1.16      rillig   1084: // After the comparison, the output buffers are cleared so that later
                   1085: // calls only check against the newly added output.
1.28      rillig   1086: //
1.42      rillig   1087: // See CheckOutputEmpty, CheckOutputLinesIgnoreSpace.
1.16      rillig   1088: func (t *Tester) CheckOutputLines(expectedLines ...string) {
1.43      rillig   1089:        assertf(len(expectedLines) > 0, "To check empty lines, use CheckOutputEmpty instead.")
1.36      rillig   1090:        t.CheckOutput(expectedLines)
                   1091: }
1.28      rillig   1092:
1.44      rillig   1093: // CheckOutputLinesMatching checks that the lines from the output that match
                   1094: // the given pattern equal the given lines.
                   1095: //
                   1096: // After the comparison, the output buffers are cleared so that later
                   1097: // calls only check against the newly added output.
                   1098: //
                   1099: // See CheckOutputEmpty, CheckOutputLinesIgnoreSpace.
                   1100: func (t *Tester) CheckOutputLinesMatching(pattern regex.Pattern, expectedLines ...string) {
                   1101:        output := t.Output()
                   1102:        var actualLines []string
                   1103:        for _, line := range strings.Split(strings.TrimSuffix(output, "\n"), "\n") {
                   1104:                if matches(line, pattern) {
                   1105:                        actualLines = append(actualLines, line)
                   1106:                }
                   1107:        }
1.45      rillig   1108:        t.CheckDeepEquals(emptyToNil(actualLines), emptyToNil(expectedLines))
1.44      rillig   1109: }
                   1110:
1.42      rillig   1111: // CheckOutputLinesIgnoreSpace checks that the output up to now equals the given lines.
                   1112: // During comparison, each run of whitespace (space, tab, newline) is normalized so that
                   1113: // different line breaks are ignored. This is useful for testing line-wrapped explanations.
                   1114: //
                   1115: // After the comparison, the output buffers are cleared so that later
                   1116: // calls only check against the newly added output.
                   1117: //
                   1118: // See CheckOutputEmpty, CheckOutputLines.
                   1119: func (t *Tester) CheckOutputLinesIgnoreSpace(expectedLines ...string) {
1.43      rillig   1120:        assertf(len(expectedLines) > 0, "To check empty lines, use CheckOutputEmpty instead.")
1.57      rillig   1121:        assertf(!t.tmpdir.IsEmpty(), "Tester must be initialized before checking the output.")
1.42      rillig   1122:
                   1123:        rawOutput := t.stdout.String() + t.stderr.String()
                   1124:        _ = t.Output() // Just to consume the output
                   1125:
                   1126:        actual, expected := t.compareOutputIgnoreSpace(rawOutput, expectedLines, t.tmpdir)
1.45      rillig   1127:        t.CheckDeepEquals(actual, expected)
1.42      rillig   1128: }
                   1129:
1.57      rillig   1130: func (t *Tester) compareOutputIgnoreSpace(rawOutput string, expectedLines []string, tmpdir CurrPath) ([]string, []string) {
1.42      rillig   1131:        whitespace := regexp.MustCompile(`\s+`)
                   1132:
                   1133:        // Replace all occurrences of tmpdir in the raw output with a tilde,
                   1134:        // also covering cases where tmpdir is wrapped into multiple lines.
                   1135:        output := func() string {
                   1136:                var tmpdirPattern strings.Builder
1.55      rillig   1137:                for i, part := range whitespace.Split(tmpdir.String(), -1) {
1.42      rillig   1138:                        if i > 0 {
                   1139:                                tmpdirPattern.WriteString("\\s+")
                   1140:                        }
                   1141:                        tmpdirPattern.WriteString(regexp.QuoteMeta(part))
                   1142:                }
                   1143:
                   1144:                return regexp.MustCompile(tmpdirPattern.String()).ReplaceAllString(rawOutput, "~")
                   1145:        }()
                   1146:
                   1147:        normSpace := func(s string) string {
                   1148:                return whitespace.ReplaceAllString(s, " ")
                   1149:        }
                   1150:        if normSpace(output) == normSpace(strings.Join(expectedLines, "\n")) {
                   1151:                return nil, nil
                   1152:        }
                   1153:
                   1154:        actualLines := strings.Split(output, "\n")
                   1155:        actualLines = actualLines[:len(actualLines)-1]
                   1156:
                   1157:        return emptyToNil(actualLines), emptyToNil(expectedLines)
                   1158: }
                   1159:
                   1160: func (s *Suite) Test_Tester_compareOutputIgnoreSpace(c *check.C) {
                   1161:        t := s.Init(c)
                   1162:
                   1163:        lines := func(lines ...string) []string { return lines }
1.57      rillig   1164:        test := func(rawOutput string, expectedLines []string, tmpdir CurrPath, eq bool) {
1.42      rillig   1165:                actual, expected := t.compareOutputIgnoreSpace(rawOutput, expectedLines, tmpdir)
1.45      rillig   1166:                t.CheckEquals(actual == nil && expected == nil, eq)
1.42      rillig   1167:        }
                   1168:
                   1169:        test("", lines(), "/tmp", true)
                   1170:
                   1171:        // The expectedLines are missing a space at the end.
                   1172:        test(" \t\noutput\n\t ", lines("\toutput"), "/tmp", false)
                   1173:
                   1174:        test(" \t\noutput\n\t ", lines("\toutput\n"), "/tmp", true)
                   1175:
                   1176:        test("/tmp/\n\t \nspace", lines("~"), "/tmp/\t\t\t   \n\n\nspace", true)
                   1177:
                   1178:        // The rawOutput contains more spaces than the tmpdir.
                   1179:        test("/tmp/\n\t \nspace", lines("~"), "/tmp/space", false)
                   1180:
                   1181:        // The tmpdir contains more spaces than the rawOutput.
                   1182:        test("/tmp/space", lines("~"), "/tmp/ \t\nspace", false)
                   1183: }
                   1184:
1.40      rillig   1185: // CheckOutputMatches checks that the output up to now matches the given lines.
                   1186: // Each line may either be an exact string or a regular expression.
                   1187: // By convention, regular expressions are written in backticks.
                   1188: //
                   1189: // After the comparison, the output buffers are cleared so that later
                   1190: // calls only check against the newly added output.
                   1191: //
                   1192: // See CheckOutputEmpty.
                   1193: func (t *Tester) CheckOutputMatches(expectedLines ...regex.Pattern) {
                   1194:        output := t.Output()
                   1195:        actualLines := strings.Split(output, "\n")
                   1196:        actualLines = actualLines[:len(actualLines)-1]
                   1197:
                   1198:        ok := func(actualLine string, expectedLine regex.Pattern) bool {
                   1199:                if actualLine == string(expectedLine) {
                   1200:                        return true
                   1201:                }
                   1202:
                   1203:                pattern := `^(?:` + string(expectedLine) + `)$`
                   1204:                re, err := regexp.Compile(pattern)
1.44      rillig   1205:                return err == nil && re.MatchString(actualLine)
1.40      rillig   1206:        }
                   1207:
                   1208:        // If a line matches the corresponding pattern, make them equal in the
                   1209:        // comparison output, in order to concentrate on the lines that don't match.
                   1210:        var patterns []string
                   1211:        for i, expectedLine := range expectedLines {
                   1212:                if i < len(actualLines) && ok(actualLines[i], expectedLine) {
                   1213:                        patterns = append(patterns, actualLines[i])
                   1214:                } else {
                   1215:                        patterns = append(patterns, string(expectedLine))
                   1216:                }
                   1217:        }
                   1218:
1.45      rillig   1219:        t.CheckDeepEquals(emptyToNil(actualLines), emptyToNil(patterns))
1.40      rillig   1220: }
                   1221:
1.36      rillig   1222: // CheckOutput checks that the output up to now equals the given lines.
                   1223: // After the comparison, the output buffers are cleared so that later
                   1224: // calls only check against the newly added output.
                   1225: //
                   1226: // The expectedLines can be either empty or non-empty.
                   1227: //
                   1228: // When the output is always empty, use CheckOutputEmpty instead.
                   1229: // When the output always contain some lines, use CheckOutputLines instead.
                   1230: // This variant should only be used when the expectedLines are generated dynamically.
                   1231: func (t *Tester) CheckOutput(expectedLines []string) {
1.16      rillig   1232:        output := t.Output()
                   1233:        actualLines := strings.Split(output, "\n")
                   1234:        actualLines = actualLines[:len(actualLines)-1]
1.45      rillig   1235:        t.CheckDeepEquals(emptyToNil(actualLines), emptyToNil(expectedLines))
1.16      rillig   1236: }
                   1237:
1.28      rillig   1238: // EnableTracing logs the tracing output to os.Stdout instead of silently discarding it.
                   1239: // The normal diagnostics are written to the in-memory buffer as usual,
                   1240: // and additionally they are written to os.Stdout,
                   1241: // where they are shown together with the trace log.
                   1242: //
1.21      rillig   1243: // This is useful when stepping through the code, especially
1.32      rillig   1244: // in combination with SetUpCommandLine("--debug").
1.17      rillig   1245: func (t *Tester) EnableTracing() {
1.42      rillig   1246:        G.Logger.out = NewSeparatorWriter(io.MultiWriter(os.Stdout, &t.stdout))
1.16      rillig   1247:        trace.Out = os.Stdout
                   1248:        trace.Tracing = true
                   1249: }
                   1250:
1.24      rillig   1251: // EnableTracingToLog enables the tracing and writes the tracing output
                   1252: // to the test log that can be examined with Tester.Output.
                   1253: func (t *Tester) EnableTracingToLog() {
1.42      rillig   1254:        G.Logger.out = NewSeparatorWriter(&t.stdout)
1.24      rillig   1255:        trace.Out = &t.stdout
1.34      rillig   1256:        trace.Tracing = true
1.24      rillig   1257: }
                   1258:
1.28      rillig   1259: // EnableSilentTracing enables tracing mode but discards any tracing output.
                   1260: // This is the default mode when running the tests.
                   1261: // The diagnostics go to the in-memory buffer.
                   1262: //
                   1263: // It is used to check all calls to trace.Result, since the compiler
                   1264: // cannot check them.
1.24      rillig   1265: func (t *Tester) EnableSilentTracing() {
1.42      rillig   1266:        G.Logger.out = NewSeparatorWriter(&t.stdout)
1.24      rillig   1267:        trace.Out = ioutil.Discard
                   1268:        trace.Tracing = true
                   1269: }
                   1270:
1.28      rillig   1271: // DisableTracing skips all tracing code.
                   1272: // The diagnostics go to the in-memory buffer again,
                   1273: // ready to be checked with CheckOutputLines.
1.17      rillig   1274: func (t *Tester) DisableTracing() {
1.54      rillig   1275:        if G.isUsable() {
1.42      rillig   1276:                G.Logger.out = NewSeparatorWriter(&t.stdout)
                   1277:        }
1.16      rillig   1278:        trace.Tracing = false
1.24      rillig   1279:        trace.Out = nil
1.16      rillig   1280: }
                   1281:
                   1282: // CheckFileLines loads the lines from the temporary file and checks that
                   1283: // they equal the given lines.
1.57      rillig   1284: func (t *Tester) CheckFileLines(filename RelPath, lines ...string) {
                   1285:        content, err := t.File(filename).ReadString()
1.28      rillig   1286:        t.c.Assert(err, check.IsNil)
1.55      rillig   1287:        actualLines := strings.Split(content, "\n")
1.16      rillig   1288:        actualLines = actualLines[:len(actualLines)-1]
1.45      rillig   1289:        t.CheckDeepEquals(emptyToNil(actualLines), emptyToNil(lines))
1.16      rillig   1290: }
                   1291:
                   1292: // CheckFileLinesDetab loads the lines from the temporary file and checks
                   1293: // that they equal the given lines. The loaded file may use tabs or spaces
                   1294: // for indentation, while the lines in the code use spaces exclusively,
1.28      rillig   1295: // in order to make the depth of the indentation clearly visible in the test code.
1.57      rillig   1296: func (t *Tester) CheckFileLinesDetab(filename RelPath, lines ...string) {
                   1297:        actualLines := Load(t.File(filename), MustSucceed)
1.16      rillig   1298:
1.28      rillig   1299:        var detabbedLines []string
                   1300:        for _, line := range actualLines.Lines {
                   1301:                detabbedLines = append(detabbedLines, detab(line.Text))
1.16      rillig   1302:        }
                   1303:
1.45      rillig   1304:        t.CheckDeepEquals(detabbedLines, lines)
1.16      rillig   1305: }
1.38      rillig   1306:
1.60      rillig   1307: // CheckDotColumns verifies that each appearance of "..34" is indeed
                   1308: // right-aligned at column 34, taking tabs into account.
                   1309: // Columns are zero-based.
                   1310: func (t *Tester) CheckDotColumns(lines ...string) {
                   1311:        for index, line := range lines {
                   1312:                ms := regcomp(`\.\.(\d+)`).FindAllStringSubmatchIndex(line, -1)
                   1313:                for _, m := range ms {
                   1314:                        prefix := line[:m[1]]
                   1315:                        width := tabWidth(prefix)
                   1316:                        num, err := strconv.Atoi(line[m[2]:m[3]])
                   1317:                        assertNil(err, "")
1.61      rillig   1318:                        t.CheckEqualsf(num, width,
                   1319:                                "The dots in lines[%d] are wrong.", index)
1.60      rillig   1320:                }
                   1321:        }
                   1322: }
                   1323:
1.38      rillig   1324: // Use marks all passed functions as used for the Go compiler.
                   1325: //
                   1326: // This means that the test cases that follow do not have to use each of them,
                   1327: // and this in turn allows uninteresting test cases to be deleted during
                   1328: // development.
1.54      rillig   1329: func (t *Tester) Use(...interface{}) {}
1.49      rillig   1330:
1.58      rillig   1331: // Shquote renders the given paths into the message, adding shell quoting
                   1332: // around the paths if necessary.
                   1333: //
                   1334: // It is typically used to check the advertisement lines at the very end
                   1335: // of the pkglint output. ("Run \"pkglint -e %s\" to show explanations.")
                   1336: func (t *Tester) Shquote(format string, rels ...RelPath) string {
1.49      rillig   1337:        var subs []interface{}
                   1338:        for _, rel := range rels {
1.55      rillig   1339:                quoted := shquote(t.tmpdir.JoinClean(rel).String())
                   1340:                subs = append(subs, strings.Replace(quoted, t.tmpdir.String(), "~", -1))
1.49      rillig   1341:        }
                   1342:        return sprintf(format, subs...)
                   1343: }
1.56      rillig   1344:
                   1345: func (t *Tester) ReportUncheckedOutput() {
                   1346:        out := t.Output()
                   1347:        if out == "" {
                   1348:                return
                   1349:        }
                   1350:
                   1351:        var msg strings.Builder
                   1352:        msg.WriteString("\n")
                   1353:        _, _ = fmt.Fprintf(&msg, "Unchecked output in %s; check with:\n", t.testName)
                   1354:        msg.WriteString("\n")
                   1355:        msg.WriteString("t.CheckOutputLines(\n")
                   1356:        lines := strings.Split(strings.TrimSpace(out), "\n")
                   1357:        for i, line := range lines {
                   1358:                _, _ = fmt.Fprintf(&msg, "\t%q%s\n", line, condStr(i == len(lines)-1, ")", ","))
                   1359:        }
                   1360:        _, _ = fmt.Fprintf(&msg, "\n")
                   1361:        _, _ = os.Stderr.WriteString(msg.String())
                   1362: }
1.60      rillig   1363:
                   1364: // SplitStringsBool unpacks the given varargs into a string slice and a bool.
                   1365: func (t *Tester) SplitStringsBool(data []interface{}) ([]string, bool) {
                   1366:        var strs []string
                   1367:        for _, text := range data[:len(data)-1] {
                   1368:                strs = append(strs, text.(string))
                   1369:        }
                   1370:        return strs, data[len(data)-1].(bool)
                   1371: }

CVSweb <webmaster@jp.NetBSD.org>