Received: by mail.netbsd.org (Postfix, from userid 605) id CA0EE84D9D; Tue, 19 Nov 2019 06:51:42 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by mail.netbsd.org (Postfix) with ESMTP id 51A0E84D51 for ; Tue, 19 Nov 2019 06:51:42 +0000 (UTC) X-Virus-Scanned: amavisd-new at netbsd.org Received: from mail.netbsd.org ([127.0.0.1]) by localhost (mail.netbsd.org [127.0.0.1]) (amavisd-new, port 10025) with ESMTP id Ty0wFWDRFOFV for ; Tue, 19 Nov 2019 06:51:39 +0000 (UTC) Received: from cvs.NetBSD.org (ivanova.NetBSD.org [IPv6:2001:470:a085:999:28c:faff:fe03:5984]) by mail.netbsd.org (Postfix) with ESMTP id 86B5684CE3 for ; Tue, 19 Nov 2019 06:51:39 +0000 (UTC) Received: by cvs.NetBSD.org (Postfix, from userid 500) id 7A479FA97; Tue, 19 Nov 2019 06:51:39 +0000 (UTC) Content-Transfer-Encoding: 7bit Content-Type: multipart/mixed; boundary="_----------=_1574146299191670" MIME-Version: 1.0 Date: Tue, 19 Nov 2019 06:51:39 +0000 From: "Roland Illig" Subject: CVS commit: pkgsrc/pkgtools/pkglint To: pkgsrc-changes@NetBSD.org Reply-To: rillig@netbsd.org X-Mailer: log_accum Message-Id: <20191119065139.7A479FA97@cvs.NetBSD.org> Sender: pkgsrc-changes-owner@NetBSD.org List-Id: pkgsrc-changes.NetBSD.org Precedence: bulk List-Unsubscribe: This is a multi-part message in MIME format. --_----------=_1574146299191670 Content-Disposition: inline Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" Module Name: pkgsrc Committed By: rillig Date: Tue Nov 19 06:51:39 UTC 2019 Modified Files: pkgsrc/pkgtools/pkglint: Makefile pkgsrc/pkgtools/pkglint/files: alternatives.go alternatives_test.go mktypes.go pkgsrc.go pkgsrc_test.go testnames_test.go pkgsrc/pkgtools/pkglint/files/getopt: getopt_test.go pkgsrc/pkgtools/pkglint/files/histogram: histogram_test.go pkgsrc/pkgtools/pkglint/files/intqa: testnames.go testnames_test.go pkgsrc/pkgtools/pkglint/files/licenses: licenses_test.go pkgsrc/pkgtools/pkglint/files/pkgver: vercmp_test.go pkgsrc/pkgtools/pkglint/files/textproc: lexer_test.go pkgsrc/pkgtools/pkglint/files/trace: tracing_test.go Log Message: pkgtools/pkglint: update to 19.3.9 Changes since 19.3.8: Match man pages in ALTERNATIVES with their counterparts in PLIST. In PLIST files, ${PKGMANDIR} may be abbreviated as a simple "man", but not in ALTERNATIVES. To generate a diff of this commit: cvs rdiff -u -r1.608 -r1.609 pkgsrc/pkgtools/pkglint/Makefile cvs rdiff -u -r1.15 -r1.16 pkgsrc/pkgtools/pkglint/files/alternatives.go cvs rdiff -u -r1.16 -r1.17 pkgsrc/pkgtools/pkglint/files/alternatives_test.go cvs rdiff -u -r1.19 -r1.20 pkgsrc/pkgtools/pkglint/files/mktypes.go cvs rdiff -u -r1.40 -r1.41 pkgsrc/pkgtools/pkglint/files/pkgsrc.go cvs rdiff -u -r1.34 -r1.35 pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go cvs rdiff -u -r1.7 -r1.8 pkgsrc/pkgtools/pkglint/files/testnames_test.go cvs rdiff -u -r1.11 -r1.12 \ pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go cvs rdiff -u -r1.2 -r1.3 \ pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go cvs rdiff -u -r1.5 -r1.6 pkgsrc/pkgtools/pkglint/files/intqa/testnames.go cvs rdiff -u -r1.2 -r1.3 \ pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go cvs rdiff -u -r1.6 -r1.7 \ pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go cvs rdiff -u -r1.6 -r1.7 pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go cvs rdiff -u -r1.7 -r1.8 pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go cvs rdiff -u -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. --_----------=_1574146299191670 Content-Disposition: inline Content-Length: 43323 Content-Transfer-Encoding: binary Content-Type: text/x-diff; charset=us-ascii Modified files: Index: pkgsrc/pkgtools/pkglint/Makefile diff -u pkgsrc/pkgtools/pkglint/Makefile:1.608 pkgsrc/pkgtools/pkglint/Makefile:1.609 --- pkgsrc/pkgtools/pkglint/Makefile:1.608 Sun Nov 17 02:06:01 2019 +++ pkgsrc/pkgtools/pkglint/Makefile Tue Nov 19 06:51:38 2019 @@ -1,6 +1,6 @@ -# $NetBSD: Makefile,v 1.608 2019/11/17 02:06:01 rillig Exp $ +# $NetBSD: Makefile,v 1.609 2019/11/19 06:51:38 rillig Exp $ -PKGNAME= pkglint-19.3.8 +PKGNAME= pkglint-19.3.9 CATEGORIES= pkgtools DISTNAME= tools MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/} Index: pkgsrc/pkgtools/pkglint/files/alternatives.go diff -u pkgsrc/pkgtools/pkglint/files/alternatives.go:1.15 pkgsrc/pkgtools/pkglint/files/alternatives.go:1.16 --- pkgsrc/pkgtools/pkglint/files/alternatives.go:1.15 Sun Nov 17 01:26:25 2019 +++ pkgsrc/pkgtools/pkglint/files/alternatives.go Tue Nov 19 06:51:38 2019 @@ -28,6 +28,9 @@ func CheckFileAlternatives(filename stri if plist.Files[plistName] != nil || G.Pkg.vars.IsDefined("ALTERNATIVES_SRC") { return } + if plist.Files[strings.Replace(plistName, "${PKGMANDIR}", "man", 1)] != nil { + return + } switch { Index: pkgsrc/pkgtools/pkglint/files/alternatives_test.go diff -u pkgsrc/pkgtools/pkglint/files/alternatives_test.go:1.16 pkgsrc/pkgtools/pkglint/files/alternatives_test.go:1.17 --- pkgsrc/pkgtools/pkglint/files/alternatives_test.go:1.16 Sun Jul 14 21:25:47 2019 +++ pkgsrc/pkgtools/pkglint/files/alternatives_test.go Tue Nov 19 06:51:38 2019 @@ -106,3 +106,25 @@ func (s *Suite) Test_CheckFileAlternativ t.CheckOutputEmpty() } + +// When a man page is mentioned in the ALTERNATIVES file, it must use the +// PKGMANDIR variable. In the PLIST files though, there is some magic +// in the pkgsrc infrastructure that maps man/ to ${PKGMANDIR}, which +// leads to a bit less typing. +// +// Seen in graphics/py-blockdiag. +func (s *Suite) Test_CheckFileAlternatives__PLIST_man(c *check.C) { + t := s.Init(c) + + t.SetUpPackage("category/package") + t.CreateFileLines("category/package/ALTERNATIVES", + "@PKGMANDIR@/man1/blockdiag @PREFIX@/@PKGMANDIR@/man1/blockdiag-@PYVERSSUFFIX@.1") + t.CreateFileLines("category/package/PLIST", + PlistCvsID, + "man/man1/blockdiag-${PYVERSSUFFIX}.1") + t.FinishSetUp() + + G.Check(t.File("category/package")) + + t.CheckOutputEmpty() +} Index: pkgsrc/pkgtools/pkglint/files/mktypes.go diff -u pkgsrc/pkgtools/pkglint/files/mktypes.go:1.19 pkgsrc/pkgtools/pkglint/files/mktypes.go:1.20 --- pkgsrc/pkgtools/pkglint/files/mktypes.go:1.19 Fri Nov 1 19:56:53 2019 +++ pkgsrc/pkgtools/pkglint/files/mktypes.go Tue Nov 19 06:51:38 2019 @@ -110,7 +110,7 @@ func (m MkVarUseModifier) IsToLower() bo func (m MkVarUseModifier) ChangesWords() bool { text := m.Text - // See MkParser.VarUseModifiers for the meaning of these modifiers. + // See MkParser.varUseModifier for the meaning of these modifiers. switch text[0] { case 'E', 'H', 'M', 'N', 'O', 'R', 'T': Index: pkgsrc/pkgtools/pkglint/files/pkgsrc.go diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.40 pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.41 --- pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.40 Sun Nov 17 02:55:56 2019 +++ pkgsrc/pkgtools/pkglint/files/pkgsrc.go Tue Nov 19 06:51:38 2019 @@ -219,7 +219,7 @@ func (src *Pkgsrc) loadDocChangesFromFil continue } - if year != "" && len(change.Date) >= 4 && change.Date[0:4] != year { + if year != "" && change.Date[0:4] != year { line.Warnf("Year %q for %s does not match the filename %s.", change.Date[0:4], change.Pkgpath, filename) } Index: pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.34 pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.35 --- pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go:1.34 Sun Nov 17 02:55:56 2019 +++ pkgsrc/pkgtools/pkglint/files/pkgsrc_test.go Tue Nov 19 06:51:38 2019 @@ -368,6 +368,10 @@ func (s *Suite) Test_Pkgsrc_parseDocChan test("\t Too large indentation", "WARN: doc/CHANGES-2019:123: Package changes should be indented using a single tab, not \"\\t \".") + test("\t", + "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: \t") + test("\t1", + "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: \t1") test("\t1 2 3 4", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: \t1 2 3 4") test("\t1 2 3 4 5", @@ -387,25 +391,25 @@ func (s *Suite) Test_Pkgsrc_parseDocChan nil...) // "to" is wrong - test("\tAdded pkgpath to 1.0 [author date]", + test("\tAdded pkgpath to 1.0 [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tAdded pkgpath to 1.0 [author date]") + "\tAdded pkgpath to 1.0 [author 2019-01-01]") test("\tUpdated pkgpath to 1.0 [author 2019-01-01]", nil...) // "from" is wrong - test("\tUpdated pkgpath from 1.0 [author date]", + test("\tUpdated pkgpath from 1.0 [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tUpdated pkgpath from 1.0 [author date]") + "\tUpdated pkgpath from 1.0 [author 2019-01-01]") test("\tDowngraded pkgpath to 1.0 [author 2019-01-01]", nil...) // "from" is wrong - test("\tDowngraded pkgpath from 1.0 [author date]", + test("\tDowngraded pkgpath from 1.0 [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tDowngraded pkgpath from 1.0 [author date]") + "\tDowngraded pkgpath from 1.0 [author 2019-01-01]") test("\tRemoved pkgpath [author 2019-01-01]", nil...) @@ -414,30 +418,30 @@ func (s *Suite) Test_Pkgsrc_parseDocChan nil...) // "and" is wrong - test("\tRemoved pkgpath and pkgpath [author date]", + test("\tRemoved pkgpath and pkgpath [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tRemoved pkgpath and pkgpath [author date]") + "\tRemoved pkgpath and pkgpath [author 2019-01-01]") test("\tRenamed pkgpath to other [author 2019-01-01]", nil...) // "from" is wrong - test("\tRenamed pkgpath from previous [author date]", + test("\tRenamed pkgpath from previous [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tRenamed pkgpath from previous [author date]") + "\tRenamed pkgpath from previous [author 2019-01-01]") test("\tMoved pkgpath to other [author 2019-01-01]", nil...) // "from" is wrong - test("\tMoved pkgpath from previous [author date]", + test("\tMoved pkgpath from previous [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tMoved pkgpath from previous [author date]") + "\tMoved pkgpath from previous [author 2019-01-01]") // "Split" is wrong - test("\tSplit pkgpath into a and b [author date]", + test("\tSplit pkgpath into a and b [author 2019-01-01]", "WARN: doc/CHANGES-2019:123: Invalid doc/CHANGES line: "+ - "\tSplit pkgpath into a and b [author date]") + "\tSplit pkgpath into a and b [author 2019-01-01]") // Entries ending in a colon are used for infrastructure changes. test("\tmk: remove support for USE_CROSSBASE [author 2016-06-19]", Index: pkgsrc/pkgtools/pkglint/files/testnames_test.go diff -u pkgsrc/pkgtools/pkglint/files/testnames_test.go:1.7 pkgsrc/pkgtools/pkglint/files/testnames_test.go:1.8 --- pkgsrc/pkgtools/pkglint/files/testnames_test.go:1.7 Sun Nov 17 01:26:25 2019 +++ pkgsrc/pkgtools/pkglint/files/testnames_test.go Tue Nov 19 06:51:38 2019 @@ -10,7 +10,7 @@ import ( // Test_${Type}_${Method}__${description_using_underscores} func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.IgnoreFiles("*yacc.go") - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) + ck.Configure("*yacc.go", "*", "*", intqa.ENone) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go diff -u pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go:1.11 pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go:1.12 --- pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go:1.11 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/getopt/getopt_test.go Tue Nov 19 06:51:38 2019 @@ -411,6 +411,6 @@ func (s *Suite) Test_Options_Help__with_ func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go diff -u pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go:1.2 pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go:1.3 --- pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go:1.2 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/histogram/histogram_test.go Tue Nov 19 06:51:38 2019 @@ -30,6 +30,6 @@ func (s *Suite) Test_Histogram(c *check. func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/intqa/testnames.go diff -u pkgsrc/pkgtools/pkglint/files/intqa/testnames.go:1.5 pkgsrc/pkgtools/pkglint/files/intqa/testnames.go:1.6 --- pkgsrc/pkgtools/pkglint/files/intqa/testnames.go:1.5 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/intqa/testnames.go Tue Nov 19 06:51:38 2019 @@ -8,7 +8,8 @@ import ( "go/token" "io" "os" - "path/filepath" + "path" + "reflect" "sort" "strings" "unicode" @@ -17,7 +18,7 @@ import ( type Error int const ( - ENone Error = iota + ENone Error = iota + 1 EAll // A function or method does not have a corresponding test. @@ -45,43 +46,47 @@ const ( type TestNameChecker struct { errorf func(format string, args ...interface{}) - ignoredFiles []string - order int + filters []filter + order int testees []*testee tests []*test - errorsMask uint64 - errors []string - out io.Writer + errors []string + out io.Writer } // NewTestNameChecker creates a new checker. -// By default, all errors are disabled; call Enable to enable them. +// By default, all errors are enabled; +// call Configure to disable them selectively. func NewTestNameChecker(errorf func(format string, args ...interface{})) *TestNameChecker { - return &TestNameChecker{errorf: errorf, out: os.Stderr} -} + ck := TestNameChecker{errorf: errorf, out: os.Stderr} -func (ck *TestNameChecker) IgnoreFiles(fileGlob string) { - ck.ignoredFiles = append(ck.ignoredFiles, fileGlob) -} - -func (ck *TestNameChecker) Enable(errors ...Error) { - for _, err := range errors { - if err == ENone { - ck.errorsMask = 0 - } else if err == EAll { - ck.errorsMask = ^uint64(0) - } else if err < 0 { - ck.errorsMask &= ^(uint64(1) << -uint(err)) - } else { - ck.errorsMask |= uint64(1) << uint(err) - } - } + // For test fixtures from https://gopkg.in/check/v1. + ck.Configure("*_test.go", "*", "SetUpTest", -EMissingTest) + ck.Configure("*_test.go", "*", "TearDownTest", -EMissingTest) + + // See https://github.com/rillig/gobco. + ck.Configure("gobco_*.go", "gobco*", "*", -EMissingTest) + ck.Configure("gobco_*.go", "", "gobco*", -EMissingTest) + + return &ck +} + +// Configure sets the errors that are activated for the given code, +// specified by shell patterns like in path.Match. +// +// All rules are applied in order. Later rules overwrite earlier rules. +// +// Individual errors can be enabled by giving their constant and disabled +// by negating them, such as -EMissingTestee. To reset everything, use +// either EAll or ENone. +func (ck *TestNameChecker) Configure(filenames, typeNames, funcNames string, errors ...Error) { + ck.filters = append(ck.filters, filter{filenames, typeNames, funcNames, errors}) } func (ck *TestNameChecker) Check() { - ck.load() + ck.load(".") ck.checkTestees() ck.checkTests() ck.checkOrder() @@ -89,29 +94,18 @@ func (ck *TestNameChecker) Check() { } // load loads all type, function and method names from the current package. -func (ck *TestNameChecker) load() { +func (ck *TestNameChecker) load(dir string) { + fileSet := token.NewFileSet() - pkgs, err := parser.ParseDir(fileSet, ".", nil, 0) + pkgs, err := parser.ParseDir(fileSet, dir, nil, 0) if err != nil { panic(err) } - var pkgnames []string - for pkgname := range pkgs { - pkgnames = append(pkgnames, pkgname) - } - sort.Strings(pkgnames) - - for _, pkgname := range pkgnames { + for _, pkgname := range sortedKeys(pkgs) { files := pkgs[pkgname].Files - var filenames []string - for filename := range files { - filenames = append(filenames, filename) - } - sort.Strings(filenames) - - for _, filename := range filenames { + for _, filename := range sortedKeys(files) { file := files[filename] for _, decl := range file.Decls { ck.loadDecl(decl, filename) @@ -131,7 +125,7 @@ func (ck *TestNameChecker) loadDecl(decl switch spec := spec.(type) { case *ast.TypeSpec: typeName := spec.Name.Name - ck.addCode(code{filename, typeName, "", ck.nextOrder()}) + ck.addCode(code{filename, typeName, "", 0}) } } @@ -145,16 +139,28 @@ func (ck *TestNameChecker) loadDecl(decl typeName = typeExpr.(*ast.Ident).Name } } - ck.addCode(code{filename, typeName, decl.Name.Name, ck.nextOrder()}) + funcName := decl.Name.Name + ck.addCode(code{filename, typeName, funcName, 0}) } } func (ck *TestNameChecker) addCode(code code) { - isTest := strings.HasSuffix(code.file, "_test.go") && - code.Type != "" && - strings.HasPrefix(code.Func, "Test") + if code.isTestScope() && code.isFunc() && code.Func == "TestMain" { + // This is not a test for Main, but a wrapper function of the test. + // Therefore it is completely ignored. + // See https://golang.org/pkg/testing/#hdr-Main. + // + // Among others, this function is created by + // https://github.com/rillig/gobco when measuring the branch + // coverage of a package. + return + } - if isTest { + if !ck.isRelevant(code.file, code.Type, code.Func, EAll) { + return + } + + if code.isTest() { ck.addTest(code) } else { ck.addTestee(code) @@ -162,15 +168,19 @@ func (ck *TestNameChecker) addCode(code } func (ck *TestNameChecker) addTestee(code code) { + code.order = ck.nextOrder() ck.testees = append(ck.testees, &testee{code}) } func (ck *TestNameChecker) addTest(code code) { - if !strings.HasPrefix(code.Func, "Test_") { + if !strings.HasPrefix(code.Func, "Test_") && + code.Func != "Test" && ck.addError( EName, + code, "Test %q must start with %q.", - code.fullName(), "Test_") + code.fullName(), "Test_") { + return } @@ -178,16 +188,18 @@ func (ck *TestNameChecker) addTest(code testeeName := strings.TrimPrefix(strings.TrimPrefix(parts[0], "Test"), "_") descr := "" if len(parts) > 1 { - if parts[1] == "" { + if parts[1] == "" && ck.addError( EName, - "Test %q must not have a nonempty description.", - code.fullName()) + code, + "Test %q must have a nonempty description.", + code.fullName()) { return } descr = parts[1] } + code.order = ck.nextOrder() ck.tests = append(ck.tests, &test{code, testeeName, descr, nil}) } @@ -213,8 +225,8 @@ func (ck *TestNameChecker) relate() { func (ck *TestNameChecker) checkTests() { for _, test := range ck.tests { ck.checkTestFile(test) - ck.checkTestName(test) ck.checkTestTestee(test) + ck.checkTestDescr(test) } } @@ -225,12 +237,15 @@ func (ck *TestNameChecker) checkTestFile } correctTestFile := strings.TrimSuffix(testee.file, ".go") + "_test.go" - if correctTestFile != test.file { - ck.addError( - EFile, - "Test %q for %q must be in %s instead of %s.", - test.fullName(), testee.fullName(), correctTestFile, test.file) + if correctTestFile == test.file { + return } + + ck.addError( + EFile, + test.code, + "Test %q for %q must be in %s instead of %s.", + test.fullName(), testee.fullName(), correctTestFile, test.file) } func (ck *TestNameChecker) checkTestTestee(test *test) { @@ -242,27 +257,23 @@ func (ck *TestNameChecker) checkTestTest testeeName := strings.Replace(test.testeeName, "_", ".", -1) ck.addError( EMissingTestee, + test.code, "Missing testee %q for test %q.", testeeName, test.fullName()) } -// checkTestName ensures that the method name does not accidentally -// end up in the description of the test. This could happen if there is a -// double underscore instead of a single underscore. -func (ck *TestNameChecker) checkTestName(test *test) { +// checkTestDescr ensures that the type or function name of the testee +// does not accidentally end up in the description of the test. This could +// happen if there is a double underscore instead of a single underscore. +func (ck *TestNameChecker) checkTestDescr(test *test) { testee := test.testee - if testee == nil { - return - } - if testee.Type != "" && testee.Func != "" { - return - } - if !isCamelCase(test.descr) { + if testee == nil || testee.isMethod() || !isCamelCase(test.descr) { return } ck.addError( EName, + testee.code, "%s: Test description %q must not use CamelCase in the first word.", test.fullName(), test.descr) } @@ -274,29 +285,49 @@ func (ck *TestNameChecker) checkTestees( } for _, testee := range ck.testees { - if tested[testee] || testee.Func == "" { - continue - } + ck.checkTesteeTest(testee, tested) + } +} - testName := "Test_" + join(testee.Type, "_", testee.Func) - ck.addError( - EMissingTest, - "Missing unit test %q for %q.", - testName, testee.fullName()) +func (ck *TestNameChecker) checkTesteeTest(testee *testee, tested map[*testee]bool) { + if tested[testee] || testee.isType() { + return } + + testName := "Test_" + join(testee.Type, "_", testee.Func) + ck.addError( + EMissingTest, + testee.code, + "Missing unit test %q for %q.", + testName, testee.fullName()) } -func (ck *TestNameChecker) isIgnored(filename string) bool { - for _, mask := range ck.ignoredFiles { - ok, err := filepath.Match(mask, filename) - if err != nil { - panic(err) +// isRelevant checks whether the given error is enabled. +func (ck *TestNameChecker) isRelevant(filename, typeName, funcName string, e Error) bool { + mask := ^uint64(0) + for _, filter := range ck.filters { + if matches(filename, filter.filenames) && + matches(typeName, filter.typeNames) && + matches(funcName, filter.funcNames) { + mask = ck.errorsMask(mask, filter.errors...) } - if ok { - return true + } + return mask&ck.errorsMask(0, e) != 0 +} + +func (ck *TestNameChecker) errorsMask(mask uint64, errors ...Error) uint64 { + for _, err := range errors { + if err == ENone { + mask = 0 + } else if err == EAll { + mask = ^uint64(0) + } else if err < 0 { + mask &= ^(uint64(1) << -uint(err)) + } else { + mask |= uint64(1) << uint(err) } } - return false + return mask } // checkOrder ensures that the tests appear in the same order as their @@ -323,18 +354,22 @@ func (ck *TestNameChecker) checkOrder() break } } + ck.addError( EOrder, - "Test %q should be ordered before %q.", + test.code, + "Test %q must be ordered before %q.", test.fullName(), insertBefore.fullName()) } } } -func (ck *TestNameChecker) addError(e Error, format string, args ...interface{}) { - if ck.errorsMask&(uint64(1)< 0 { - ck.errorf("%s.", errors) + n := len(ck.errors) + if n > 1 { + ck.errorf("%d errors.", n) + } else if n == 1 { + ck.errorf("%d error.", n) } } -type code struct { - file string // The file containing the code - Type string // The type, e.g. MkLine - Func string // The function or method name, e.g. Warnf - order int // The relative order in the file +type filter struct { + filenames string + typeNames string + funcNames string + errors []Error } -func (c *code) fullName() string { return join(c.Type, ".", c.Func) } - // testee is an element of the source code that can be tested. -// It is either a type, a function or a method. type testee struct { code } @@ -371,15 +405,24 @@ type test struct { testee *testee } -func plural(n int, sg, pl string) string { - if n == 0 { - return "" - } - form := pl - if n == 1 { - form = sg - } - return fmt.Sprintf("%d %s", n, form) +// code is either a type, a function or a method. +type code struct { + file string // the file containing the code + Type string // the type, e.g. MkLine + Func string // the function or method name, e.g. Warnf + order int // the relative order in the file +} + +func (c *code) fullName() string { return join(c.Type, ".", c.Func) } +func (c *code) isFunc() bool { return c.Type == "" } +func (c *code) isType() bool { return c.Func == "" } +func (c *code) isMethod() bool { return c.Type != "" && c.Func != "" } + +func (c *code) isTest() bool { + return c.isTestScope() && strings.HasPrefix(c.Func, "Test") +} +func (c *code) isTestScope() bool { + return strings.HasSuffix(c.file, "_test.go") } func isCamelCase(str string) bool { @@ -400,3 +443,21 @@ func join(a, sep, b string) string { } return a + sep + b } + +func matches(subj string, pattern string) bool { + ok, err := path.Match(pattern, subj) + if err != nil { + panic(err) + } + return ok +} + +// sortedKeys returns the sorted keys from an arbitrary map. +func sortedKeys(m interface{}) []string { + var keys []string + for _, key := range reflect.ValueOf(m).MapKeys() { + keys = append(keys, key.Interface().(string)) + } + sort.Strings(keys) + return keys +} Index: pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go diff -u pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go:1.2 pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go:1.3 --- pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go:1.2 Sun Nov 17 02:06:01 2019 +++ pkgsrc/pkgtools/pkglint/files/intqa/testnames_test.go Tue Nov 19 06:51:38 2019 @@ -3,8 +3,10 @@ package intqa import ( "bytes" "fmt" + "go/ast" "gopkg.in/check.v1" "io/ioutil" + "path" "testing" ) @@ -26,7 +28,6 @@ func (s *Suite) Init(c *check.C) *TestNa s.c = c s.ck = NewTestNameChecker(errorf) - s.ck.Enable(EAll) s.ck.out = ioutil.Discard return s.ck } @@ -37,6 +38,25 @@ func (s *Suite) TearDownTest(c *check.C) s.CheckSummary("") } +func (s *Suite) CheckTestees(testees ...*testee) { + s.c.Check(s.ck.testees, check.DeepEquals, testees) + s.ck.testees = nil +} + +func (*Suite) newTestee(filename, typeName, funcName string, order int) *testee { + return &testee{code{filename, typeName, funcName, order}} +} + +func (s *Suite) CheckTests(tests ...*test) { + s.c.Check(s.ck.tests, check.DeepEquals, tests) + s.ck.tests = nil +} + +func (*Suite) newTest(filename, typeName, funcName string, order int, testeeName, descr string, testee *testee) *test { + c := code{filename, typeName, funcName, order} + return &test{c, testeeName, descr, testee} +} + func (s *Suite) CheckErrors(errors ...string) { s.c.Check(s.ck.errors, check.DeepEquals, errors) s.ck.errors = nil @@ -47,51 +67,169 @@ func (s *Suite) CheckSummary(summary str s.summary = "" } -func (s *Suite) Test_TestNameChecker_Enable(c *check.C) { +func (s *Suite) Test_NewTestNameChecker(c *check.C) { + ck := s.Init(c) + + c.Check(ck.isRelevant("*_test.go", "Suite", "SetUpTest", EAll), check.Equals, true) + c.Check(ck.isRelevant("*_test.go", "Suite", "SetUpTest", EMissingTest), check.Equals, false) + + c.Check(ck.isRelevant("*_test.go", "Suite", "TearDownTest", EAll), check.Equals, true) + c.Check(ck.isRelevant("*_test.go", "Suite", "TearDownTest", EMissingTest), check.Equals, false) +} + +func (s *Suite) Test_TestNameChecker_Configure(c *check.C) { ck := s.Init(c) - ck.Enable(ENone) // overwrite initialization from Suite.Init + ck.Configure("*", "*", "*", ENone) // overwrite initialization from Suite.Init + + c.Check(ck.isRelevant("", "", "", EAll), check.Equals, false) + c.Check(ck.isRelevant("", "", "", EMissingTestee), check.Equals, false) + c.Check(ck.isRelevant("", "", "", EMissingTest), check.Equals, false) + + ck.Configure("*", "*", "*", EAll) - c.Check(ck.errorsMask, check.Equals, uint64(0)) + c.Check(ck.isRelevant("", "", "", EAll), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTestee), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTest), check.Equals, true) - ck.Enable(EAll) + ck.Configure("*", "*", "*", -EMissingTestee) - c.Check(ck.errorsMask, check.Equals, ^uint64(0)) + c.Check(ck.isRelevant("", "", "", EAll), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTestee), check.Equals, false) + c.Check(ck.isRelevant("", "", "", EMissingTest), check.Equals, true) + + ck.Configure("*", "*", "*", ENone, EMissingTest) + + c.Check(ck.isRelevant("", "", "", EAll), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTestee), check.Equals, false) + c.Check(ck.isRelevant("", "", "", EMissingTest), check.Equals, true) + + ck.Configure("*", "*", "*", EAll, -EMissingTest) + + c.Check(ck.isRelevant("", "", "", EAll), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTestee), check.Equals, true) + c.Check(ck.isRelevant("", "", "", EMissingTest), check.Equals, false) +} - ck.Enable(ENone, EMissingTest) +func (s *Suite) Test_TestNameChecker_Configure__ignore_single_function(c *check.C) { + ck := s.Init(c) - c.Check(ck.errorsMask, check.Equals, uint64(4)) + ck.Configure("*", "*", "*", EAll) - ck.Enable(EAll, -EMissingTest) + // The intention of this rule is that this particular function is ignored. + // Everything else from that file is still processed. + ck.Configure("*_test.go", "", "TestMain", ENone) - c.Check(ck.errorsMask, check.Equals, ^uint64(0)^4) + c.Check(ck.isRelevant("file_test.go", "", "", EAll), check.Equals, true) + c.Check(ck.isRelevant("file_test.go", "*", "*", EAll), check.Equals, true) + c.Check(ck.isRelevant("file_test.go", "*", "Other", EAll), check.Equals, true) + c.Check(ck.isRelevant("file_test.go", "", "TestMain", EAll), check.Equals, false) + c.Check(ck.isRelevant("file_test.go", "*", "TestMain", EAll), check.Equals, true) } func (s *Suite) Test_TestNameChecker_Check(c *check.C) { ck := s.Init(c) + ck.Configure("*", "Suite", "*", -EMissingTest) + ck.Check() s.CheckErrors( - "Missing unit test \"Test_NewTestNameChecker\" for \"NewTestNameChecker\".", - "Missing unit test \"Test_TestNameChecker_IgnoreFiles\" for \"TestNameChecker.IgnoreFiles\".", - "Missing unit test \"Test_TestNameChecker_load\" for \"TestNameChecker.load\".", - "Missing unit test \"Test_TestNameChecker_loadDecl\" for \"TestNameChecker.loadDecl\".", "Missing unit test \"Test_TestNameChecker_addCode\" for \"TestNameChecker.addCode\".", - "Missing unit test \"Test_TestNameChecker_addTestee\" for \"TestNameChecker.addTestee\".", - "Missing unit test \"Test_TestNameChecker_nextOrder\" for \"TestNameChecker.nextOrder\".", "Missing unit test \"Test_TestNameChecker_relate\" for \"TestNameChecker.relate\".", - "Missing unit test \"Test_TestNameChecker_checkTests\" for \"TestNameChecker.checkTests\".", - "Missing unit test \"Test_TestNameChecker_checkTestees\" for \"TestNameChecker.checkTestees\".", - "Missing unit test \"Test_TestNameChecker_isIgnored\" for \"TestNameChecker.isIgnored\".", - "Missing unit test \"Test_TestNameChecker_addError\" for \"TestNameChecker.addError\".", - "Missing unit test \"Test_Test\" for \"Test\".", - "Missing unit test \"Test_Suite_Init\" for \"Suite.Init\".", - "Missing unit test \"Test_Suite_TearDownTest\" for \"Suite.TearDownTest\".", - "Missing unit test \"Test_Suite_CheckErrors\" for \"Suite.CheckErrors\".", - "Missing unit test \"Test_Suite_CheckSummary\" for \"Suite.CheckSummary\".", - "Missing unit test \"Test_Value_Method\" for \"Value.Method\".") - s.CheckSummary("18 errors.") + "Missing unit test \"Test_TestNameChecker_isRelevant\" for \"TestNameChecker.isRelevant\".") + s.CheckSummary("3 errors.") +} + +func (s *Suite) Test_TestNameChecker_load__filtered_nothing(c *check.C) { + ck := s.Init(c) + + ck.Configure("*", "*", "*", ENone) + + ck.load(".") + + c.Check(ck.testees, check.IsNil) + c.Check(ck.tests, check.IsNil) +} + +func (s *Suite) Test_TestNameChecker_load__filtered_only_Value(c *check.C) { + ck := s.Init(c) + + ck.Configure("*", "*", "*", ENone) + ck.Configure("*", "Value", "*", EAll) + + ck.load(".") + + c.Check(ck.testees, check.DeepEquals, []*testee{ + {code{"testnames_test.go", "Value", "", 0}}, + {code{"testnames_test.go", "Value", "Method", 1}}}) + c.Check(ck.tests, check.IsNil) +} + +func (s *Suite) Test_TestNameChecker_load__panic(c *check.C) { + ck := s.Init(c) + + c.Check( + func() { ck.load("does-not-exist") }, + check.PanicMatches, + `^open does-not-exist\b.*`) +} + +func (s *Suite) Test_TestNameChecker_loadDecl(c *check.C) { + ck := s.Init(c) + + typeDecl := func(name string) *ast.GenDecl { + return &ast.GenDecl{Specs: []ast.Spec{&ast.TypeSpec{Name: &ast.Ident{Name: name}}}} + } + funcDecl := func(name string) *ast.FuncDecl { + return &ast.FuncDecl{Name: &ast.Ident{Name: name}} + } + methodDecl := func(typeName, methodName string) *ast.FuncDecl { + return &ast.FuncDecl{ + Name: &ast.Ident{Name: methodName}, + Recv: &ast.FieldList{List: []*ast.Field{{Type: &ast.Ident{Name: typeName}}}}} + } + + ck.loadDecl(typeDecl("TypeName"), "file_test.go") + + s.CheckTestees( + s.newTestee("file_test.go", "TypeName", "", 0)) + + // The freestanding TestMain function is ignored by a hardcoded rule, + // independently of the configuration. + ck.loadDecl(funcDecl("TestMain"), "file_test.go") + + // The TestMain method on a type is relevant, but violates the naming rule. + // Therefore it is ignored. + ck.loadDecl(methodDecl("Suite", "TestMain"), "file_test.go") + + s.CheckTests( + nil...) + s.CheckErrors( + "Test \"Suite.TestMain\" must start with \"Test_\".") + + // The above error can be disabled, and then the method is handled + // like any other test method. + ck.Configure("*", "Suite", "*", -EName) + ck.loadDecl(methodDecl("Suite", "TestMain"), "file_test.go") + + s.CheckTests( + s.newTest("file_test.go", "Suite", "TestMain", 1, "Main", "", nil)) + + // There is no special handling for TestMain method with a description. + ck.loadDecl(methodDecl("Suite", "TestMain__descr"), "file_test.go") + + s.CheckTests( + s.newTest("file_test.go", "Suite", "TestMain__descr", 2, "Main", "descr", nil)) +} + +func (s *Suite) Test_TestNameChecker_addTestee(c *check.C) { + ck := s.Init(c) + + code := code{"filename.go", "Type", "Method", 0} + ck.addTestee(code) + + c.Check(ck.testees, check.DeepEquals, []*testee{{code}}) } func (s *Suite) Test_TestNameChecker_addTest(c *check.C) { @@ -99,6 +237,7 @@ func (s *Suite) Test_TestNameChecker_add ck.addTest(code{"filename.go", "Type", "Method", 0}) + c.Check(ck.tests, check.IsNil) s.CheckErrors( "Test \"Type.Method\" must start with \"Test_\".") } @@ -106,10 +245,56 @@ func (s *Suite) Test_TestNameChecker_add func (s *Suite) Test_TestNameChecker_addTest__empty_description(c *check.C) { ck := s.Init(c) - ck.addTest(code{"filename.go", "Suite", "Test_Method__", 0}) + ck.addTest(code{"f_test.go", "Suite", "Test_Method__", 0}) + + s.CheckErrors( + "Test \"Suite.Test_Method__\" must have a nonempty description.") + + // The test is not registered and thus cannot complain about its missing + // testee. + ck.checkTests() + + s.CheckErrors( + nil...) +} + +func (s *Suite) Test_TestNameChecker_addTest__suppressed_empty_description(c *check.C) { + ck := s.Init(c) + + ck.Configure("*", "*", "*", -EName) + ck.addTest(code{"f_test.go", "Suite", "Test_Method__", 0}) + + s.CheckErrors( + nil...) + + // Since there was no error above, the test is added normally + // and can complain about its missing testee. + ck.checkTests() + + s.CheckErrors( + "Missing testee \"Method\" for test \"Suite.Test_Method__\".") +} + +func (s *Suite) Test_TestNameChecker_nextOrder(c *check.C) { + ck := s.Init(c) + + c.Check(ck.nextOrder(), check.Equals, 0) + c.Check(ck.nextOrder(), check.Equals, 1) + c.Check(ck.nextOrder(), check.Equals, 2) +} + +func (s *Suite) Test_TestNameChecker_checkTests(c *check.C) { + ck := s.Init(c) + + ck.tests = append(ck.tests, + s.newTest("wrong_test.go", "", "Test_Func", 0, "Func", "", + s.newTestee("source.go", "", "Func", 1))) + + ck.checkTests() s.CheckErrors( - "Test \"Suite.Test_Method__\" must not have a nonempty description.") + "Test \"Test_Func\" for \"Func\" " + + "must be in source_test.go instead of wrong_test.go.") } func (s *Suite) Test_TestNameChecker_checkTestFile__global(c *check.C) { @@ -165,10 +350,10 @@ func (s *Suite) Test_TestNameChecker_che nil...) } -func (s *Suite) Test_TestNameChecker_checkTestName__camel_case(c *check.C) { +func (s *Suite) Test_TestNameChecker_checkTestDescr__camel_case(c *check.C) { ck := s.Init(c) - ck.checkTestName(&test{ + ck.checkTestDescr(&test{ code{"demo_test.go", "Suite", "Test_Missing__CamelCase", 0}, "Missing", "CamelCase", @@ -179,6 +364,45 @@ func (s *Suite) Test_TestNameChecker_che "must not use CamelCase in the first word.") } +func (s *Suite) Test_TestNameChecker_checkTestees(c *check.C) { + ck := s.Init(c) + + ck.testees = []*testee{s.newTestee("s.go", "", "Func", 0)} + ck.tests = nil // force an error + + ck.checkTestees() + + s.CheckErrors( + "Missing unit test \"Test_Func\" for \"Func\".") +} + +func (s *Suite) Test_TestNameChecker_checkTesteeTest(c *check.C) { + ck := s.Init(c) + + ck.checkTesteeTest( + &testee{code{"demo.go", "Type", "", 0}}, + nil) + ck.checkTesteeTest( + &testee{code{"demo.go", "", "Func", 0}}, + nil) + ck.checkTesteeTest( + &testee{code{"demo.go", "Type", "Method", 0}}, + nil) + + s.CheckErrors( + "Missing unit test \"Test_Func\" for \"Func\".", + "Missing unit test \"Test_Type_Method\" for \"Type.Method\".") +} + +func (s *Suite) Test_TestNameChecker_errorsMask(c *check.C) { + ck := s.Init(c) + + c.Check(ck.errorsMask(0, EAll), check.Equals, ^uint64(0)) + c.Check(ck.errorsMask(12345, ENone), check.Equals, uint64(0)) + c.Check(ck.errorsMask(12345, ENone, EMissingTest), check.Equals, uint64(8)) + c.Check(ck.errorsMask(2, EMissingTest), check.Equals, uint64(10)) +} + func (s *Suite) Test_TestNameChecker_checkOrder(c *check.C) { ck := s.Init(c) @@ -186,6 +410,8 @@ func (s *Suite) Test_TestNameChecker_che ck.addTestee(code{"f.go", "T", "M1", 11}) ck.addTestee(code{"f.go", "T", "M2", 12}) ck.addTestee(code{"f.go", "T", "M3", 13}) + ck.addTest(code{"a_test.go", "S", "Test_A", 98}) // different file, is skipped + ck.addTest(code{"f_test.go", "S", "Test_Missing", 99}) // missing testee, is skipped ck.addTest(code{"f_test.go", "S", "Test_T_M1", 100}) // maxTestee = 11 ck.addTest(code{"f_test.go", "S", "Test_T_M2", 101}) // maxTestee = 12 ck.addTest(code{"f_test.go", "S", "Test_T", 102}) // testee 10 < maxTestee 12: insert before first [.testee > testee 10] == T_M1 @@ -198,10 +424,23 @@ func (s *Suite) Test_TestNameChecker_che ck.checkOrder() s.CheckErrors( - "Test \"S.Test_T\" should be ordered before \"S.Test_T_M1\".", - "Test \"S.Test_T__1\" should be ordered before \"S.Test_T_M1\".", - "Test \"S.Test_T__2\" should be ordered before \"S.Test_T_M1\".", - "Test \"S.Test_T_M2__1\" should be ordered before \"S.Test_T_M3\".") + "Test \"S.Test_T\" must be ordered before \"S.Test_T_M1\".", + "Test \"S.Test_T__1\" must be ordered before \"S.Test_T_M1\".", + "Test \"S.Test_T__2\" must be ordered before \"S.Test_T_M1\".", + "Test \"S.Test_T_M2__1\" must be ordered before \"S.Test_T_M3\".") +} + +func (s *Suite) Test_TestNameChecker_addError(c *check.C) { + ck := s.Init(c) + + ck.Configure("ignored*", "*", "*", -EName) + ok1 := ck.addError(EName, code{"ignored.go", "", "Func", 0}, "E1") + ok2 := ck.addError(EName, code{"reported.go", "", "Func", 0}, "E2") + + c.Check(ok1, check.Equals, false) + c.Check(ok2, check.Equals, true) + s.CheckErrors( + "E2") } func (s *Suite) Test_TestNameChecker_print__empty(c *check.C) { @@ -214,12 +453,12 @@ func (s *Suite) Test_TestNameChecker_pri c.Check(out.String(), check.Equals, "") } -func (s *Suite) Test_TestNameChecker_print__errors(c *check.C) { +func (s *Suite) Test_TestNameChecker_print__1_error(c *check.C) { var out bytes.Buffer ck := s.Init(c) ck.out = &out + ck.addError(EName, code{}, "1") - ck.addError(EName, "1") ck.print() c.Check(out.String(), check.Equals, "1\n") @@ -227,6 +466,20 @@ func (s *Suite) Test_TestNameChecker_pri s.CheckSummary("1 error.") } +func (s *Suite) Test_TestNameChecker_print__2_errors(c *check.C) { + var out bytes.Buffer + ck := s.Init(c) + ck.out = &out + ck.addError(EName, code{}, "1") + ck.addError(EName, code{}, "2") + + ck.print() + + c.Check(out.String(), check.Equals, "1\n2\n") + s.CheckErrors("1", "2") + s.CheckSummary("2 errors.") +} + func (s *Suite) Test_code_fullName(c *check.C) { _ = s.Init(c) @@ -240,13 +493,77 @@ func (s *Suite) Test_code_fullName(c *ch test("Type", "Method", "Type.Method") } -func (s *Suite) Test_plural(c *check.C) { +func (s *Suite) Test_code_isFunc(c *check.C) { _ = s.Init(c) - c.Check(plural(0, "singular", "plural"), check.Equals, "") - c.Check(plural(1, "singular", "plural"), check.Equals, "1 singular") - c.Check(plural(2, "singular", "plural"), check.Equals, "2 plural") - c.Check(plural(1000, "singular", "plural"), check.Equals, "1000 plural") + test := func(typeName, funcName string, isFunc bool) { + code := code{"filename", typeName, funcName, 0} + c.Check(code.isFunc(), check.Equals, isFunc) + } + + test("Type", "", false) + test("", "Func", true) + test("Type", "Method", false) +} + +func (s *Suite) Test_code_isType(c *check.C) { + _ = s.Init(c) + + test := func(typeName, funcName string, isType bool) { + code := code{"filename", typeName, funcName, 0} + c.Check(code.isType(), check.Equals, isType) + } + + test("Type", "", true) + test("", "Func", false) + test("Type", "Method", false) +} + +func (s *Suite) Test_code_isMethod(c *check.C) { + _ = s.Init(c) + + test := func(typeName, funcName string, isMethod bool) { + code := code{"filename", typeName, funcName, 0} + c.Check(code.isMethod(), check.Equals, isMethod) + } + + test("Type", "", false) + test("", "Func", false) + test("Type", "Method", true) +} + +func (s *Suite) Test_code_isTest(c *check.C) { + _ = s.Init(c) + + test := func(filename, typeName, funcName string, isTest bool) { + code := code{filename, typeName, funcName, 0} + c.Check(code.isTest(), check.Equals, isTest) + } + + test("f.go", "Type", "", false) + test("f.go", "", "Func", false) + test("f.go", "Type", "Method", false) + test("f.go", "Type", "Test", false) + test("f.go", "Type", "Test_Type_Method", false) + test("f.go", "", "Test_Type_Method", false) + test("f_test.go", "Type", "Test", true) + test("f_test.go", "Type", "Test_Type_Method", true) + test("f_test.go", "", "Test_Type_Method", true) +} + +func (s *Suite) Test_code_isTestScope(c *check.C) { + _ = s.Init(c) + + test := func(filename string, isTestScope bool) { + code := code{filename, "", "", 0} + c.Check(code.isTestScope(), check.Equals, isTestScope) + } + + test("f.go", false) + test("test.go", false) + test("_test.go", true) + test("file_test.go", true) + test("file_linux_test.go", true) } func (s *Suite) Test_isCamelCase(c *check.C) { @@ -273,6 +590,37 @@ func (s *Suite) Test_join(c *check.C) { c.Check(join("one", " and ", "two"), check.Equals, "one and two") } +func (s *Suite) Test_matches(c *check.C) { + _ = s.Init(c) + + c.Check(matches("*", "*"), check.Equals, true) + c.Check(matches("anything", "*"), check.Equals, true) + c.Check(matches("*", "anything"), check.Equals, false) + c.Check(func() { matches("any", "[") }, check.Panics, path.ErrBadPattern) +} + +func (s *Suite) Test_sortedKeys(c *check.C) { + _ = s.Init(c) + + m := make(map[string]uint8) + m["first"] = 1 + m["second"] = 2 + m["third"] = 3 + m["fourth"] = 4 + + c.Check( + sortedKeys(m), + check.DeepEquals, + []string{"first", "fourth", "second", "third"}) +} + +func (s *Suite) Test_Value_Method(c *check.C) { + _ = s.Init(c) + + // Just for code coverage of checkTestFile, to have a piece of code + // that lives in the same file as its test. +} + type Value struct{} // Method has no star on the receiver, Index: pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go diff -u pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go:1.6 pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go:1.7 --- pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go:1.6 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/licenses/licenses_test.go Tue Nov 19 06:51:38 2019 @@ -133,6 +133,6 @@ func Test(t *testing.T) { func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go diff -u pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go:1.6 pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go:1.7 --- pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go:1.6 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/pkgver/vercmp_test.go Tue Nov 19 06:51:38 2019 @@ -95,6 +95,6 @@ func (s *Suite) Test_newVersion(c *check func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go diff -u pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go:1.7 pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go:1.8 --- pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go:1.7 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/textproc/lexer_test.go Tue Nov 19 06:51:39 2019 @@ -415,6 +415,6 @@ func (s *Suite) Test__Alpha(c *check.C) func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } Index: pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go diff -u pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go:1.4 pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go:1.5 --- pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go:1.4 Sun Nov 17 01:26:26 2019 +++ pkgsrc/pkgtools/pkglint/files/trace/tracing_test.go Tue Nov 19 06:51:39 2019 @@ -145,6 +145,6 @@ func (str) String() string { func (s *Suite) Test__test_names(c *check.C) { ck := intqa.NewTestNameChecker(c.Errorf) - ck.Enable(intqa.EAll, -intqa.EMissingTest) + ck.Configure("*", "*", "*", -intqa.EMissingTest) ck.Check() } --_----------=_1574146299191670--