Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ error rather than a panic.
### Notest comments
Blocks or files with a `// notest` comment are excluded.

### Generated code
Generated code files, that contain the respective comment line that is
specified by the [Go Team](https://github.com/golang/go/issues/41196) in
[`go generate`](https://golang.org/s/generatedcode).

This exclude is disabled by default. Use flag `-g` to enable this behavior.

### Blocks returning a error tested to be non-nil
We only exclude blocks where the error being returned has been tested to be
non-nil, so:
Expand Down Expand Up @@ -114,6 +121,11 @@ courtney -t="-count=2" -t="-parallel=4"

All the output from the `go test -v` command is shown.

### Exclude generated code: -g
`Exclude generated code from coverage`

All generated code is excluded from coverage.

# Output
Courtney will fail if the tests fail. If the tests succeed, it will create or
overwrite a `coverage.out` file in the current directory.
Expand Down
20 changes: 11 additions & 9 deletions courtney.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@ func main() {
argsFlag := new(argsValue)
flag.Var(argsFlag, "t", "Argument to pass to the 'go test' command. Can be used more than once.")
loadFlag := flag.String("l", "", "Load coverage file(s) instead of running 'go test'")
excludeGeneratedCodeFlag := flag.Bool("g", false, "Exclude generated code from coverage")

flag.Parse()

setup := &shared.Setup{
Env: env,
Paths: patsy.NewCache(env),
Enforce: *enforceFlag,
Verbose: *verboseFlag,
Short: *shortFlag,
Timeout: *timeoutFlag,
Output: *outputFlag,
TestArgs: argsFlag.args,
Load: *loadFlag,
Env: env,
Paths: patsy.NewCache(env),
Enforce: *enforceFlag,
Verbose: *verboseFlag,
Short: *shortFlag,
Timeout: *timeoutFlag,
Output: *outputFlag,
TestArgs: argsFlag.args,
Load: *loadFlag,
ExcludeGeneratedCode: *excludeGeneratedCodeFlag,
}
if err := Run(setup); err != nil {
fmt.Printf("%+v", err)
Expand Down
36 changes: 36 additions & 0 deletions scanner/generated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2013 The Go Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd.

// Taken from https://github.com/golang/lint/blob/85993ffd0a6cd043291f3f63d45d656d97b165bd/lint.go

// Once https://github.com/golang/go/issues/28089 is implemented (proposal is accepted),
// this can be replaced with the official implementation.

package scanner

import (
"bufio"
"bytes"
)

var (
genHdr = []byte("// Code generated ")
genFtr = []byte(" DO NOT EDIT.")
)

// isGenerated reports whether the source file is generated code
// according the rules from https://golang.org/s/generatedcode.
//
func isGenerated(src []byte) bool {
sc := bufio.NewScanner(bytes.NewReader(src))
for sc.Scan() {
b := sc.Bytes()
if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
return true
}
}
return false
}
38 changes: 38 additions & 0 deletions scanner/generated_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2013 The Go Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd.

// Taken from https://github.com/golang/lint/blob/85993ffd0a6cd043291f3f63d45d656d97b165bd/lint_test.go

package scanner

import "testing"

func TestIsGenerated(t *testing.T) {
tests := []struct {
source string
generated bool
}{
{"// Code Generated by some tool. DO NOT EDIT.", false},
{"// Code generated by some tool. DO NOT EDIT.", true},
{"// Code generated by some tool. DO NOT EDIT", false},
{"// Code generated DO NOT EDIT.", true},
{"// Code generated DO NOT EDIT.", false},
{"\t\t// Code generated by some tool. DO NOT EDIT.\npackage foo\n", false},
{"// Code generated by some tool. DO NOT EDIT.\npackage foo\n", true},
{"package foo\n// Code generated by some tool. DO NOT EDIT.\ntype foo int\n", true},
{"package foo\n // Code generated by some tool. DO NOT EDIT.\ntype foo int\n", false},
{"package foo\n// Code generated by some tool. DO NOT EDIT. \ntype foo int\n", false},
{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.\n", true},
{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.", true},
}

for i, test := range tests {
got := isGenerated([]byte(test.source))
if got != test.generated {
t.Errorf("test %d, isGenerated() = %v, want %v", i, got, test.generated)
}
}
}
18 changes: 18 additions & 0 deletions scanner/scanner.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package scanner

import (
"bytes"
"fmt"
"go/ast"
"go/constant"
"go/token"
"go/types"
"os"

"github.com/dave/astrid"
"github.com/dave/brenda"
Expand Down Expand Up @@ -129,6 +131,22 @@ func (p *PackageMap) ScanPackage() error {
return errors.WithStack(err)
}
}

if p.setup.ExcludeGeneratedCode {
// Exclude complete files, if the code is generated.
for _, f := range p.pkg.GoFiles {
body, err := os.ReadFile(f)
if err != nil {
return errors.WithStack(err)
}
if !isGenerated(body) {
continue
}
for i := range bytes.Split(body, []byte("\n")) {
p.addExclude(f, i+1)
}
}
}
return nil
}

Expand Down
19 changes: 16 additions & 3 deletions scanner/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,17 @@ func TestComments(t *testing.T) {
test(t, tests)
}

func TestGenerated(t *testing.T) {
tests := map[string]string{
"generated": `// Code generated by courtney. DO NOT EDIT.
package foo // *
func Baz() int { // *
return 0 // *
} // *`,
}
test(t, tests)
}

func test(t *testing.T, tests map[string]string) {
for name, source := range tests {
env := vos.Mock()
Expand All @@ -704,8 +715,9 @@ func test(t *testing.T, tests map[string]string) {

paths := patsy.NewCache(env)
setup := &shared.Setup{
Env: env,
Paths: paths,
Env: env,
Paths: paths,
ExcludeGeneratedCode: true,
}
if err := setup.Parse([]string{ppath}); err != nil {
t.Fatalf("Error parsing args in %s: %+v", name, err)
Expand All @@ -726,7 +738,8 @@ func test(t *testing.T, tests map[string]string) {
for i, line := range strings.Split(source, "\n") {
expected := strings.HasSuffix(line, "// *") ||
strings.HasSuffix(line, "//notest") ||
strings.HasSuffix(line, "// notest")
strings.HasSuffix(line, "// notest") ||
strings.HasSuffix(line, "// Code generated by courtney. DO NOT EDIT.")
if result[i+1] != expected {
t.Fatalf("Unexpected state in %s, line %d: %s\n", name, i, strconv.Quote(strings.Trim(line, "\t")))
}
Expand Down
21 changes: 11 additions & 10 deletions shared/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import (
// Setup holds globals, environment and command line flags for the courtney
// command
type Setup struct {
Env vos.Env
Paths *patsy.Cache
Enforce bool
Verbose bool
Short bool
Timeout string
Load string
Output string
TestArgs []string
Packages []PackageSpec
Env vos.Env
Paths *patsy.Cache
Enforce bool
Verbose bool
Short bool
Timeout string
Load string
Output string
TestArgs []string
ExcludeGeneratedCode bool
Packages []PackageSpec
}

// PackageSpec identifies a package by dir and path
Expand Down