mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-05-14 05:06:59 +00:00
copy config.yaml to docker image enable external usage of dashboard package gofmt fix comment use packr for assets add gobuffalo/packr dependency add dependencies fix pointer issues add output-file option
214 lines
6.1 KiB
Go
214 lines
6.1 KiB
Go
// Copyright 2018 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.
|
|
|
|
package testscript
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
var profileId int32 = 0
|
|
|
|
// TestingM is implemented by *testing.M. It's defined as an interface
|
|
// to allow testscript to co-exist with other testing frameworks
|
|
// that might also wish to call M.Run.
|
|
type TestingM interface {
|
|
Run() int
|
|
}
|
|
|
|
var ignoreMissedCoverage = false
|
|
|
|
// IgnoreMissedCoverage causes any missed coverage information
|
|
// (for example when a function passed to RunMain
|
|
// calls os.Exit, for example) to be ignored.
|
|
// This function should be called before calling RunMain.
|
|
func IgnoreMissedCoverage() {
|
|
ignoreMissedCoverage = true
|
|
}
|
|
|
|
// RunMain should be called within a TestMain function to allow
|
|
// subcommands to be run in the testscript context.
|
|
//
|
|
// The commands map holds the set of command names, each
|
|
// with an associated run function which should return the
|
|
// code to pass to os.Exit. It's OK for a command function to
|
|
// exit itself, but this may result in loss of coverage information.
|
|
//
|
|
// When Run is called, these commands will be available as
|
|
// testscript commands; note that these commands behave like
|
|
// commands run with the "exec" command: they set stdout
|
|
// and stderr, and can be run in the background by passing "&"
|
|
// as a final argument.
|
|
//
|
|
// This function returns an exit code to pass to os.Exit, after calling m.Run.
|
|
func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
|
|
goCoverProfileMerge()
|
|
cmdName := os.Getenv("TESTSCRIPT_COMMAND")
|
|
if cmdName == "" {
|
|
defer func() {
|
|
if err := finalizeCoverProfile(); err != nil {
|
|
log.Printf("cannot merge cover profiles: %v", err)
|
|
exitCode = 2
|
|
}
|
|
}()
|
|
// We're not in a subcommand.
|
|
for name := range commands {
|
|
name := name
|
|
scriptCmds[name] = func(ts *TestScript, neg bool, args []string) {
|
|
path, err := os.Executable()
|
|
if err != nil {
|
|
ts.Fatalf("cannot determine path to test binary: %v", err)
|
|
}
|
|
id := atomic.AddInt32(&profileId, 1) - 1
|
|
oldEnvLen := len(ts.env)
|
|
cprof := coverFilename(id)
|
|
ts.env = append(ts.env,
|
|
"TESTSCRIPT_COMMAND="+name,
|
|
"TESTSCRIPT_COVERPROFILE="+cprof,
|
|
)
|
|
ts.cmdExec(neg, append([]string{path}, args...))
|
|
ts.env = ts.env[0:oldEnvLen]
|
|
if cprof == "" {
|
|
return
|
|
}
|
|
f, err := os.Open(cprof)
|
|
if err != nil {
|
|
if ignoreMissedCoverage {
|
|
return
|
|
}
|
|
ts.Fatalf("command %s (args %q) failed to generate coverage information", name, args)
|
|
return
|
|
}
|
|
coverChan <- f
|
|
}
|
|
}
|
|
return m.Run()
|
|
}
|
|
mainf := commands[cmdName]
|
|
if mainf == nil {
|
|
log.Printf("unknown command name %q", cmdName)
|
|
return 2
|
|
}
|
|
// The command being registered is being invoked, so run it, then exit.
|
|
os.Args[0] = cmdName
|
|
cprof := os.Getenv("TESTSCRIPT_COVERPROFILE")
|
|
if cprof == "" {
|
|
// No coverage, act as normal.
|
|
return mainf()
|
|
}
|
|
return runCoverSubcommand(cprof, mainf)
|
|
}
|
|
|
|
// runCoverSubcommand runs the given function, then writes any generated
|
|
// coverage information to the cprof file.
|
|
// This is called inside a separately run executable.
|
|
func runCoverSubcommand(cprof string, mainf func() int) (exitCode int) {
|
|
// Change the error handling mode to PanicOnError
|
|
// so that in the common case of calling flag.Parse in main we'll
|
|
// be able to catch the panic instead of just exiting.
|
|
flag.CommandLine.Init(flag.CommandLine.Name(), flag.PanicOnError)
|
|
defer func() {
|
|
panicErr := recover()
|
|
if _, ok := panicErr.(error); ok {
|
|
// The flag package will already have printed this error, assuming,
|
|
// that is, that the error was created in the flag package.
|
|
// TODO check the stack to be sure it was actually raised by the flag package.
|
|
exitCode = 2
|
|
panicErr = nil
|
|
}
|
|
// Set os.Args so that flag.Parse will tell testing the correct
|
|
// coverprofile setting. Unfortunately this isn't sufficient because
|
|
// the testing oackage explicitly avoids calling flag.Parse again
|
|
// if flag.Parsed returns true, so we the coverprofile value directly
|
|
// too.
|
|
os.Args = []string{os.Args[0], "-test.coverprofile=" + cprof}
|
|
setCoverProfile(cprof)
|
|
|
|
// Suppress the chatty coverage and test report.
|
|
devNull, err := os.Open(os.DevNull)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
os.Stdout = devNull
|
|
os.Stderr = devNull
|
|
|
|
// Run MainStart (recursively, but it we should be ok) with no tests
|
|
// so that it writes the coverage profile.
|
|
m := testing.MainStart(nopTestDeps{}, nil, nil, nil)
|
|
if code := m.Run(); code != 0 && exitCode == 0 {
|
|
exitCode = code
|
|
}
|
|
if _, err := os.Stat(cprof); err != nil {
|
|
log.Printf("failed to write coverage profile %q", cprof)
|
|
}
|
|
if panicErr != nil {
|
|
// The error didn't originate from the flag package (we know that
|
|
// flag.PanicOnError causes an error value that implements error),
|
|
// so carry on panicking.
|
|
panic(panicErr)
|
|
}
|
|
}()
|
|
return mainf()
|
|
}
|
|
|
|
func coverFilename(id int32) string {
|
|
if cprof := coverProfile(); cprof != "" {
|
|
return fmt.Sprintf("%s_%d", cprof, id)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func coverProfileFlag() flag.Getter {
|
|
f := flag.CommandLine.Lookup("test.coverprofile")
|
|
if f == nil {
|
|
// We've imported testing so it definitely should be there.
|
|
panic("cannot find test.coverprofile flag")
|
|
}
|
|
return f.Value.(flag.Getter)
|
|
}
|
|
|
|
func coverProfile() string {
|
|
return coverProfileFlag().Get().(string)
|
|
}
|
|
|
|
func setCoverProfile(cprof string) {
|
|
coverProfileFlag().Set(cprof)
|
|
}
|
|
|
|
type nopTestDeps struct{}
|
|
|
|
func (nopTestDeps) MatchString(pat, str string) (result bool, err error) {
|
|
return false, nil
|
|
}
|
|
|
|
func (nopTestDeps) StartCPUProfile(w io.Writer) error {
|
|
return nil
|
|
}
|
|
|
|
func (nopTestDeps) StopCPUProfile() {}
|
|
|
|
func (nopTestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
|
|
return nil
|
|
}
|
|
func (nopTestDeps) ImportPath() string {
|
|
return ""
|
|
}
|
|
func (nopTestDeps) StartTestLog(w io.Writer) {}
|
|
|
|
func (nopTestDeps) StopTestLog() error {
|
|
return nil
|
|
}
|
|
|
|
// Note: WriteHeapProfile is needed for Go 1.10 but not Go 1.11.
|
|
func (nopTestDeps) WriteHeapProfile(io.Writer) error {
|
|
// Not needed for Go 1.10.
|
|
return nil
|
|
}
|