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>