[cli] Support gathering code coverage data.

Add support for gathering code coverage data from a special build of
the Pulumi CLI. This build takes advantage of `go test` and `TestMain`
to build a coverage-instrumented binary.
This commit is contained in:
Pat Gavlin 2021-10-19 10:41:06 -07:00
parent b14bc09b1c
commit 6ffea35ddc
3 changed files with 108 additions and 7 deletions

View file

@ -31,18 +31,33 @@ generate::
$(call STEP_MESSAGE)
echo "This command does not do anything anymore. It will be removed in a future version."
ifeq ($(PULUMI_TEST_COVERAGE_PATH),)
build::
cd pkg && go install -ldflags "-X github.com/pulumi/pulumi/pkg/v3/version.Version=${VERSION}" ${PROJECT}
install::
cd pkg && GOBIN=$(PULUMI_BIN) go install -ldflags "-X github.com/pulumi/pulumi/pkg/v3/version.Version=${VERSION}" ${PROJECT}
else
build:: build_cover
install:: install_cover
endif
build_debug::
cd pkg && go install -gcflags="all=-N -l" -ldflags "-X github.com/pulumi/pulumi/pkg/v3/version.Version=${VERSION}" ${PROJECT}
build_cover::
cd pkg && go test -coverpkg github.com/pulumi/pulumi/pkg/v3/...,github.com/pulumi/pulumi/sdk/v3/... -cover -c -o $(shell go env GOPATH)/bin/pulumi -ldflags "-X github.com/pulumi/pulumi/pkg/v3/version.Version=${VERSION}" ${PROJECT}
install_cover:: build_cover
cp $(shell go env GOPATH)/bin/pulumi $(PULUMI_BIN)
build_covmerge::
cd pkg && go install ./cmd/covmerge
developer_docs::
cd developer-docs && make html
install::
cd pkg && GOBIN=$(PULUMI_BIN) go install -ldflags "-X github.com/pulumi/pulumi/pkg/v3/version.Version=${VERSION}" ${PROJECT}
install_all:: install
dist:: build

View file

@ -0,0 +1,81 @@
package main
import (
"flag"
"fmt"
"io"
"os"
"path"
"testing"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/spf13/pflag"
)
type noTestDeps int
func (noTestDeps) ImportPath() string { return "" }
func (noTestDeps) MatchString(pat, str string) (bool, error) { return false, nil }
func (noTestDeps) SetPanicOnExit0(bool) {}
func (noTestDeps) StartCPUProfile(io.Writer) error { return nil }
func (noTestDeps) StopCPUProfile() {}
func (noTestDeps) StartTestLog(io.Writer) {}
func (noTestDeps) StopTestLog() error { return nil }
func (noTestDeps) WriteProfileTo(string, io.Writer, int) error { return nil }
// flushProfiles flushes test profiles to disk.
func flushProfiles() {
// Redirect Stdout/err temporarily so the testing code doesn't output the
// regular:
// PASS
// coverage: 21.4% of statements
oldstdout, oldstderr := os.Stdout, os.Stderr
defer func() {
os.Stdout, os.Stderr = oldstdout, oldstderr
}()
os.Stdout, _ = os.Open(os.DevNull)
os.Stderr, _ = os.Open(os.DevNull)
cmdLine := flag.CommandLine
defer func() { flag.CommandLine = cmdLine }()
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
err := flag.CommandLine.Parse(nil)
contract.IgnoreError(err)
m := testing.MainStart(noTestDeps(0), nil, nil, nil)
m.Run()
}
func addGoFlag(pf *pflag.FlagSet, f *flag.Flag) {
if pf.Lookup(f.Name) != nil || len(f.Name) == 1 && pf.ShorthandLookup(f.Name) != nil {
return
}
pf.AddGoFlag(f)
}
func TestMain(m *testing.M) {
// If the binary is invoked as `pulumi`, we are being asked to run the coverage-instrumented program. Otherwise,
// we are running tests as usual.
if path.Base(os.Args[0]) != "pulumi" {
flag.Parse()
os.Exit(m.Run())
}
defer panicHandler()
// Copy the test flags into the Pulumi command's flags.
cmd := NewPulumiCmd()
flag.CommandLine.VisitAll(func(f *flag.Flag) {
addGoFlag(cmd.PersistentFlags(), f)
})
// Now, execute the Pulumi command and dump coverage data if requested.
err := cmd.Execute()
flushProfiles()
if err != nil {
_, err = fmt.Fprintf(os.Stderr, "An error occurred: %v\n", err)
contract.IgnoreError(err)
os.Exit(1)
}
}

View file

@ -63,6 +63,13 @@ func Flush() {
glog.Flush()
}
func maybeSetFlag(name, value string) {
if f := flag.Lookup(name); f != nil {
err := f.Value.Set(value)
assertNoError(err)
}
}
// InitLogging ensures the logging library has been initialized with the given settings.
func InitLogging(logToStderr bool, verbose int, logFlow bool) {
// Remember the settings in case someone inquires.
@ -78,12 +85,10 @@ func InitLogging(logToStderr bool, verbose int, logFlow bool) {
assertNoError(err)
}
if logToStderr {
err := flag.Lookup("logtostderr").Value.Set("true")
assertNoError(err)
maybeSetFlag("logtostderr", "true")
}
if verbose > 0 {
err := flag.Lookup("v").Value.Set(strconv.Itoa(verbose))
assertNoError(err)
maybeSetFlag("v", strconv.Itoa(verbose))
}
}