summaryrefslogtreecommitdiff
path: root/vendor/github.com/onsi/ginkgo
diff options
context:
space:
mode:
authorValentin Rothberg <rothberg@redhat.com>2021-01-20 10:31:18 +0100
committerValentin Rothberg <rothberg@redhat.com>2021-01-20 10:31:24 +0100
commit175fc3867a70dd5a0d5c0becdb828751ccd88e9c (patch)
tree4c63109005f9c1a83bdf6259cdb2b189b2cd99d8 /vendor/github.com/onsi/ginkgo
parent5e7262ddf595f9187d01e12f5dcee2fe1c713798 (diff)
downloadpodman-175fc3867a70dd5a0d5c0becdb828751ccd88e9c.tar.gz
podman-175fc3867a70dd5a0d5c0becdb828751ccd88e9c.tar.bz2
podman-175fc3867a70dd5a0d5c0becdb828751ccd88e9c.zip
Revert "ginkgo: install on demand via `go get -u`"
This reverts commit de05e5816869073600ae8e851093b4b9a9d7fab0. Running `go get -u` will change the local Go module causing CI to fail as the local git tree is being changed. Reverting the change for now until we have a better idea. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'vendor/github.com/onsi/ginkgo')
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go199
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/build_command.go66
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go123
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go90
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go128
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go56
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go162
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go130
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go45
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go256
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/help_command.go31
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go52
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go14
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go7
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/main.go302
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go196
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go77
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/notifications.go141
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/run_command.go291
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go169
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go173
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args.go7
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args_old.go7
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go52
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go27
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go554
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go115
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go16
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go15
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go180
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/version_command.go24
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go22
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go75
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go92
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go104
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go85
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go87
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go175
38 files changed, 4345 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
new file mode 100644
index 000000000..93150d1a4
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
@@ -0,0 +1,199 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "text/template"
+
+ "go/build"
+
+ "github.com/onsi/ginkgo/ginkgo/nodot"
+)
+
+func BuildBootstrapCommand() *Command {
+ var (
+ agouti, noDot, internal bool
+ customBootstrapFile string
+ )
+ flagSet := flag.NewFlagSet("bootstrap", flag.ExitOnError)
+ flagSet.BoolVar(&agouti, "agouti", false, "If set, bootstrap will generate a bootstrap file for writing Agouti tests")
+ flagSet.BoolVar(&noDot, "nodot", false, "If set, bootstrap will generate a bootstrap file that does not . import ginkgo and gomega")
+ flagSet.BoolVar(&internal, "internal", false, "If set, generate will generate a test file that uses the regular package name")
+ flagSet.StringVar(&customBootstrapFile, "template", "", "If specified, generate will use the contents of the file passed as the bootstrap template")
+
+ return &Command{
+ Name: "bootstrap",
+ FlagSet: flagSet,
+ UsageCommand: "ginkgo bootstrap <FLAGS>",
+ Usage: []string{
+ "Bootstrap a test suite for the current package",
+ "Accepts the following flags:",
+ },
+ Command: func(args []string, additionalArgs []string) {
+ generateBootstrap(agouti, noDot, internal, customBootstrapFile)
+ },
+ }
+}
+
+var bootstrapText = `package {{.Package}}
+
+import (
+ "testing"
+
+ {{.GinkgoImport}}
+ {{.GomegaImport}}
+)
+
+func Test{{.FormattedName}}(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "{{.FormattedName}} Suite")
+}
+`
+
+var agoutiBootstrapText = `package {{.Package}}
+
+import (
+ "testing"
+
+ {{.GinkgoImport}}
+ {{.GomegaImport}}
+ "github.com/sclevine/agouti"
+)
+
+func Test{{.FormattedName}}(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "{{.FormattedName}} Suite")
+}
+
+var agoutiDriver *agouti.WebDriver
+
+var _ = BeforeSuite(func() {
+ // Choose a WebDriver:
+
+ agoutiDriver = agouti.PhantomJS()
+ // agoutiDriver = agouti.Selenium()
+ // agoutiDriver = agouti.ChromeDriver()
+
+ Expect(agoutiDriver.Start()).To(Succeed())
+})
+
+var _ = AfterSuite(func() {
+ Expect(agoutiDriver.Stop()).To(Succeed())
+})
+`
+
+type bootstrapData struct {
+ Package string
+ FormattedName string
+ GinkgoImport string
+ GomegaImport string
+}
+
+func getPackageAndFormattedName() (string, string, string) {
+ path, err := os.Getwd()
+ if err != nil {
+ complainAndQuit("Could not get current working directory: \n" + err.Error())
+ }
+
+ dirName := strings.Replace(filepath.Base(path), "-", "_", -1)
+ dirName = strings.Replace(dirName, " ", "_", -1)
+
+ pkg, err := build.ImportDir(path, 0)
+ packageName := pkg.Name
+ if err != nil {
+ packageName = dirName
+ }
+
+ formattedName := prettifyPackageName(filepath.Base(path))
+ return packageName, dirName, formattedName
+}
+
+func prettifyPackageName(name string) string {
+ name = strings.Replace(name, "-", " ", -1)
+ name = strings.Replace(name, "_", " ", -1)
+ name = strings.Title(name)
+ name = strings.Replace(name, " ", "", -1)
+ return name
+}
+
+func determinePackageName(name string, internal bool) string {
+ if internal {
+ return name
+ }
+
+ return name + "_test"
+}
+
+func fileExists(path string) bool {
+ _, err := os.Stat(path)
+ return err == nil
+}
+
+func generateBootstrap(agouti, noDot, internal bool, customBootstrapFile string) {
+ packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName()
+ data := bootstrapData{
+ Package: determinePackageName(packageName, internal),
+ FormattedName: formattedName,
+ GinkgoImport: `. "github.com/onsi/ginkgo"`,
+ GomegaImport: `. "github.com/onsi/gomega"`,
+ }
+
+ if noDot {
+ data.GinkgoImport = `"github.com/onsi/ginkgo"`
+ data.GomegaImport = `"github.com/onsi/gomega"`
+ }
+
+ targetFile := fmt.Sprintf("%s_suite_test.go", bootstrapFilePrefix)
+ if fileExists(targetFile) {
+ fmt.Printf("%s already exists.\n\n", targetFile)
+ os.Exit(1)
+ } else {
+ fmt.Printf("Generating ginkgo test suite bootstrap for %s in:\n\t%s\n", packageName, targetFile)
+ }
+
+ f, err := os.Create(targetFile)
+ if err != nil {
+ complainAndQuit("Could not create file: " + err.Error())
+ panic(err.Error())
+ }
+ defer f.Close()
+
+ var templateText string
+ if customBootstrapFile != "" {
+ tpl, err := ioutil.ReadFile(customBootstrapFile)
+ if err != nil {
+ panic(err.Error())
+ }
+ templateText = string(tpl)
+ } else if agouti {
+ templateText = agoutiBootstrapText
+ } else {
+ templateText = bootstrapText
+ }
+
+ bootstrapTemplate, err := template.New("bootstrap").Parse(templateText)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ buf := &bytes.Buffer{}
+ bootstrapTemplate.Execute(buf, data)
+
+ if noDot {
+ contents, err := nodot.ApplyNoDot(buf.Bytes())
+ if err != nil {
+ complainAndQuit("Failed to import nodot declarations: " + err.Error())
+ }
+ fmt.Println("To update the nodot declarations in the future, switch to this directory and run:\n\tginkgo nodot")
+ buf = bytes.NewBuffer(contents)
+ }
+
+ buf.WriteTo(f)
+
+ goFmt(targetFile)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/build_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
new file mode 100644
index 000000000..2fddef0f7
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/onsi/ginkgo/ginkgo/interrupthandler"
+ "github.com/onsi/ginkgo/ginkgo/testrunner"
+)
+
+func BuildBuildCommand() *Command {
+ commandFlags := NewBuildCommandFlags(flag.NewFlagSet("build", flag.ExitOnError))
+ interruptHandler := interrupthandler.NewInterruptHandler()
+ builder := &SpecBuilder{
+ commandFlags: commandFlags,
+ interruptHandler: interruptHandler,
+ }
+
+ return &Command{
+ Name: "build",
+ FlagSet: commandFlags.FlagSet,
+ UsageCommand: "ginkgo build <FLAGS> <PACKAGES>",
+ Usage: []string{
+ "Build the passed in <PACKAGES> (or the package in the current directory if left blank).",
+ "Accepts the following flags:",
+ },
+ Command: builder.BuildSpecs,
+ }
+}
+
+type SpecBuilder struct {
+ commandFlags *RunWatchAndBuildCommandFlags
+ interruptHandler *interrupthandler.InterruptHandler
+}
+
+func (r *SpecBuilder) BuildSpecs(args []string, additionalArgs []string) {
+ r.commandFlags.computeNodes()
+
+ suites, _ := findSuites(args, r.commandFlags.Recurse, r.commandFlags.SkipPackage, false)
+
+ if len(suites) == 0 {
+ complainAndQuit("Found no test suites")
+ }
+
+ passed := true
+ for _, suite := range suites {
+ runner := testrunner.New(suite, 1, false, 0, r.commandFlags.GoOpts, nil)
+ fmt.Printf("Compiling %s...\n", suite.PackageName)
+
+ path, _ := filepath.Abs(filepath.Join(suite.Path, fmt.Sprintf("%s.test", suite.PackageName)))
+ err := runner.CompileTo(path)
+ if err != nil {
+ fmt.Println(err.Error())
+ passed = false
+ } else {
+ fmt.Printf(" compiled %s.test\n", suite.PackageName)
+ }
+ }
+
+ if passed {
+ os.Exit(0)
+ }
+ os.Exit(1)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
new file mode 100644
index 000000000..02e2b3b32
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
@@ -0,0 +1,123 @@
+package convert
+
+import (
+ "fmt"
+ "go/ast"
+ "strings"
+ "unicode"
+)
+
+/*
+ * Creates a func init() node
+ */
+func createVarUnderscoreBlock() *ast.ValueSpec {
+ valueSpec := &ast.ValueSpec{}
+ object := &ast.Object{Kind: 4, Name: "_", Decl: valueSpec, Data: 0}
+ ident := &ast.Ident{Name: "_", Obj: object}
+ valueSpec.Names = append(valueSpec.Names, ident)
+ return valueSpec
+}
+
+/*
+ * Creates a Describe("Testing with ginkgo", func() { }) node
+ */
+func createDescribeBlock() *ast.CallExpr {
+ blockStatement := &ast.BlockStmt{List: []ast.Stmt{}}
+
+ fieldList := &ast.FieldList{}
+ funcType := &ast.FuncType{Params: fieldList}
+ funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement}
+ basicLit := &ast.BasicLit{Kind: 9, Value: "\"Testing with Ginkgo\""}
+ describeIdent := &ast.Ident{Name: "Describe"}
+ return &ast.CallExpr{Fun: describeIdent, Args: []ast.Expr{basicLit, funcLit}}
+}
+
+/*
+ * Convenience function to return the name of the *testing.T param
+ * for a Test function that will be rewritten. This is useful because
+ * we will want to replace the usage of this named *testing.T inside the
+ * body of the function with a GinktoT.
+ */
+func namedTestingTArg(node *ast.FuncDecl) string {
+ return node.Type.Params.List[0].Names[0].Name // *exhale*
+}
+
+/*
+ * Convenience function to return the block statement node for a Describe statement
+ */
+func blockStatementFromDescribe(desc *ast.CallExpr) *ast.BlockStmt {
+ var funcLit *ast.FuncLit
+ var found = false
+
+ for _, node := range desc.Args {
+ switch node := node.(type) {
+ case *ast.FuncLit:
+ found = true
+ funcLit = node
+ break
+ }
+ }
+
+ if !found {
+ panic("Error finding ast.FuncLit inside describe statement. Somebody done goofed.")
+ }
+
+ return funcLit.Body
+}
+
+/* convenience function for creating an It("TestNameHere")
+ * with all the body of the test function inside the anonymous
+ * func passed to It()
+ */
+func createItStatementForTestFunc(testFunc *ast.FuncDecl) *ast.ExprStmt {
+ blockStatement := &ast.BlockStmt{List: testFunc.Body.List}
+ fieldList := &ast.FieldList{}
+ funcType := &ast.FuncType{Params: fieldList}
+ funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement}
+
+ testName := rewriteTestName(testFunc.Name.Name)
+ basicLit := &ast.BasicLit{Kind: 9, Value: fmt.Sprintf("\"%s\"", testName)}
+ itBlockIdent := &ast.Ident{Name: "It"}
+ callExpr := &ast.CallExpr{Fun: itBlockIdent, Args: []ast.Expr{basicLit, funcLit}}
+ return &ast.ExprStmt{X: callExpr}
+}
+
+/*
+* rewrite test names to be human readable
+* eg: rewrites "TestSomethingAmazing" as "something amazing"
+ */
+func rewriteTestName(testName string) string {
+ nameComponents := []string{}
+ currentString := ""
+ indexOfTest := strings.Index(testName, "Test")
+ if indexOfTest != 0 {
+ return testName
+ }
+
+ testName = strings.Replace(testName, "Test", "", 1)
+ first, rest := testName[0], testName[1:]
+ testName = string(unicode.ToLower(rune(first))) + rest
+
+ for _, rune := range testName {
+ if unicode.IsUpper(rune) {
+ nameComponents = append(nameComponents, currentString)
+ currentString = string(unicode.ToLower(rune))
+ } else {
+ currentString += string(rune)
+ }
+ }
+
+ return strings.Join(append(nameComponents, currentString), " ")
+}
+
+func newGinkgoTFromIdent(ident *ast.Ident) *ast.CallExpr {
+ return &ast.CallExpr{
+ Lparen: ident.NamePos + 1,
+ Rparen: ident.NamePos + 2,
+ Fun: &ast.Ident{Name: "GinkgoT"},
+ }
+}
+
+func newGinkgoTInterface() *ast.Ident {
+ return &ast.Ident{Name: "GinkgoTInterface"}
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
new file mode 100644
index 000000000..06c6ec94c
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
@@ -0,0 +1,90 @@
+package convert
+
+import (
+ "fmt"
+ "go/ast"
+)
+
+/*
+ * Given the root node of an AST, returns the node containing the
+ * import statements for the file.
+ */
+func importsForRootNode(rootNode *ast.File) (imports *ast.GenDecl, err error) {
+ for _, declaration := range rootNode.Decls {
+ decl, ok := declaration.(*ast.GenDecl)
+ if !ok || len(decl.Specs) == 0 {
+ continue
+ }
+
+ _, ok = decl.Specs[0].(*ast.ImportSpec)
+ if ok {
+ imports = decl
+ return
+ }
+ }
+
+ err = fmt.Errorf("Could not find imports for root node:\n\t%#v\n", rootNode)
+ return
+}
+
+/*
+ * Removes "testing" import, if present
+ */
+func removeTestingImport(rootNode *ast.File) {
+ importDecl, err := importsForRootNode(rootNode)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ var index int
+ for i, importSpec := range importDecl.Specs {
+ importSpec := importSpec.(*ast.ImportSpec)
+ if importSpec.Path.Value == "\"testing\"" {
+ index = i
+ break
+ }
+ }
+
+ importDecl.Specs = append(importDecl.Specs[:index], importDecl.Specs[index+1:]...)
+}
+
+/*
+ * Adds import statements for onsi/ginkgo, if missing
+ */
+func addGinkgoImports(rootNode *ast.File) {
+ importDecl, err := importsForRootNode(rootNode)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ if len(importDecl.Specs) == 0 {
+ // TODO: might need to create a import decl here
+ panic("unimplemented : expected to find an imports block")
+ }
+
+ needsGinkgo := true
+ for _, importSpec := range importDecl.Specs {
+ importSpec, ok := importSpec.(*ast.ImportSpec)
+ if !ok {
+ continue
+ }
+
+ if importSpec.Path.Value == "\"github.com/onsi/ginkgo\"" {
+ needsGinkgo = false
+ }
+ }
+
+ if needsGinkgo {
+ importDecl.Specs = append(importDecl.Specs, createImport(".", "\"github.com/onsi/ginkgo\""))
+ }
+}
+
+/*
+ * convenience function to create an import statement
+ */
+func createImport(name, path string) *ast.ImportSpec {
+ return &ast.ImportSpec{
+ Name: &ast.Ident{Name: name},
+ Path: &ast.BasicLit{Kind: 9, Value: path},
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
new file mode 100644
index 000000000..363e52fe2
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
@@ -0,0 +1,128 @@
+package convert
+
+import (
+ "fmt"
+ "go/build"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+)
+
+/*
+ * RewritePackage takes a name (eg: my-package/tools), finds its test files using
+ * Go's build package, and then rewrites them. A ginkgo test suite file will
+ * also be added for this package, and all of its child packages.
+ */
+func RewritePackage(packageName string) {
+ pkg, err := packageWithName(packageName)
+ if err != nil {
+ panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error()))
+ }
+
+ for _, filename := range findTestsInPackage(pkg) {
+ rewriteTestsInFile(filename)
+ }
+}
+
+/*
+ * Given a package, findTestsInPackage reads the test files in the directory,
+ * and then recurses on each child package, returning a slice of all test files
+ * found in this process.
+ */
+func findTestsInPackage(pkg *build.Package) (testfiles []string) {
+ for _, file := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) {
+ testfile, _ := filepath.Abs(filepath.Join(pkg.Dir, file))
+ testfiles = append(testfiles, testfile)
+ }
+
+ dirFiles, err := ioutil.ReadDir(pkg.Dir)
+ if err != nil {
+ panic(fmt.Sprintf("unexpected error reading dir: '%s'\n%s\n", pkg.Dir, err.Error()))
+ }
+
+ re := regexp.MustCompile(`^[._]`)
+
+ for _, file := range dirFiles {
+ if !file.IsDir() {
+ continue
+ }
+
+ if re.Match([]byte(file.Name())) {
+ continue
+ }
+
+ packageName := filepath.Join(pkg.ImportPath, file.Name())
+ subPackage, err := packageWithName(packageName)
+ if err != nil {
+ panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error()))
+ }
+
+ testfiles = append(testfiles, findTestsInPackage(subPackage)...)
+ }
+
+ addGinkgoSuiteForPackage(pkg)
+ goFmtPackage(pkg)
+ return
+}
+
+/*
+ * Shells out to `ginkgo bootstrap` to create a test suite file
+ */
+func addGinkgoSuiteForPackage(pkg *build.Package) {
+ originalDir, err := os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+
+ suite_test_file := filepath.Join(pkg.Dir, pkg.Name+"_suite_test.go")
+
+ _, err = os.Stat(suite_test_file)
+ if err == nil {
+ return // test file already exists, this should be a no-op
+ }
+
+ err = os.Chdir(pkg.Dir)
+ if err != nil {
+ panic(err)
+ }
+
+ output, err := exec.Command("ginkgo", "bootstrap").Output()
+
+ if err != nil {
+ panic(fmt.Sprintf("error running 'ginkgo bootstrap'.\nstdout: %s\n%s\n", output, err.Error()))
+ }
+
+ err = os.Chdir(originalDir)
+ if err != nil {
+ panic(err)
+ }
+}
+
+/*
+ * Shells out to `go fmt` to format the package
+ */
+func goFmtPackage(pkg *build.Package) {
+ path, _ := filepath.Abs(pkg.ImportPath)
+ output, err := exec.Command("go", "fmt", path).CombinedOutput()
+
+ if err != nil {
+ fmt.Printf("Warning: Error running 'go fmt %s'.\nstdout: %s\n%s\n", path, output, err.Error())
+ }
+}
+
+/*
+ * Attempts to return a package with its test files already read.
+ * The ImportMode arg to build.Import lets you specify if you want go to read the
+ * buildable go files inside the package, but it fails if the package has no go files
+ */
+func packageWithName(name string) (pkg *build.Package, err error) {
+ pkg, err = build.Default.Import(name, ".", build.ImportMode(0))
+ if err == nil {
+ return
+ }
+
+ pkg, err = build.Default.Import(name, ".", build.ImportMode(1))
+ return
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
new file mode 100644
index 000000000..b33595c9a
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
@@ -0,0 +1,56 @@
+package convert
+
+import (
+ "go/ast"
+ "regexp"
+)
+
+/*
+ * Given a root node, walks its top level statements and returns
+ * points to function nodes to rewrite as It statements.
+ * These functions, according to Go testing convention, must be named
+ * TestWithCamelCasedName and receive a single *testing.T argument.
+ */
+func findTestFuncs(rootNode *ast.File) (testsToRewrite []*ast.FuncDecl) {
+ testNameRegexp := regexp.MustCompile("^Test[0-9A-Z].+")
+
+ ast.Inspect(rootNode, func(node ast.Node) bool {
+ if node == nil {
+ return false
+ }
+
+ switch node := node.(type) {
+ case *ast.FuncDecl:
+ matches := testNameRegexp.MatchString(node.Name.Name)
+
+ if matches && receivesTestingT(node) {
+ testsToRewrite = append(testsToRewrite, node)
+ }
+ }
+
+ return true
+ })
+
+ return
+}
+
+/*
+ * convenience function that looks at args to a function and determines if its
+ * params include an argument of type *testing.T
+ */
+func receivesTestingT(node *ast.FuncDecl) bool {
+ if len(node.Type.Params.List) != 1 {
+ return false
+ }
+
+ base, ok := node.Type.Params.List[0].Type.(*ast.StarExpr)
+ if !ok {
+ return false
+ }
+
+ intermediate := base.X.(*ast.SelectorExpr)
+ isTestingPackage := intermediate.X.(*ast.Ident).Name == "testing"
+ isTestingT := intermediate.Sel.Name == "T"
+
+ return isTestingPackage && isTestingT
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
new file mode 100644
index 000000000..60c73504a
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
@@ -0,0 +1,162 @@
+package convert
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "os"
+)
+
+/*
+ * Given a file path, rewrites any tests in the Ginkgo format.
+ * First, we parse the AST, and update the imports declaration.
+ * Then, we walk the first child elements in the file, returning tests to rewrite.
+ * A top level init func is declared, with a single Describe func inside.
+ * Then the test functions to rewrite are inserted as It statements inside the Describe.
+ * Finally we walk the rest of the file, replacing other usages of *testing.T
+ * Once that is complete, we write the AST back out again to its file.
+ */
+func rewriteTestsInFile(pathToFile string) {
+ fileSet := token.NewFileSet()
+ rootNode, err := parser.ParseFile(fileSet, pathToFile, nil, parser.ParseComments)
+ if err != nil {
+ panic(fmt.Sprintf("Error parsing test file '%s':\n%s\n", pathToFile, err.Error()))
+ }
+
+ addGinkgoImports(rootNode)
+ removeTestingImport(rootNode)
+
+ varUnderscoreBlock := createVarUnderscoreBlock()
+ describeBlock := createDescribeBlock()
+ varUnderscoreBlock.Values = []ast.Expr{describeBlock}
+
+ for _, testFunc := range findTestFuncs(rootNode) {
+ rewriteTestFuncAsItStatement(testFunc, rootNode, describeBlock)
+ }
+
+ underscoreDecl := &ast.GenDecl{
+ Tok: 85, // gah, magick numbers are needed to make this work
+ TokPos: 14, // this tricks Go into writing "var _ = Describe"
+ Specs: []ast.Spec{varUnderscoreBlock},
+ }
+
+ imports := rootNode.Decls[0]
+ tail := rootNode.Decls[1:]
+ rootNode.Decls = append(append([]ast.Decl{imports}, underscoreDecl), tail...)
+ rewriteOtherFuncsToUseGinkgoT(rootNode.Decls)
+ walkNodesInRootNodeReplacingTestingT(rootNode)
+
+ var buffer bytes.Buffer
+ if err = format.Node(&buffer, fileSet, rootNode); err != nil {
+ panic(fmt.Sprintf("Error formatting ast node after rewriting tests.\n%s\n", err.Error()))
+ }
+
+ fileInfo, err := os.Stat(pathToFile)
+
+ if err != nil {
+ panic(fmt.Sprintf("Error stat'ing file: %s\n", pathToFile))
+ }
+
+ err = ioutil.WriteFile(pathToFile, buffer.Bytes(), fileInfo.Mode())
+}
+
+/*
+ * Given a test func named TestDoesSomethingNeat, rewrites it as
+ * It("does something neat", func() { __test_body_here__ }) and adds it
+ * to the Describe's list of statements
+ */
+func rewriteTestFuncAsItStatement(testFunc *ast.FuncDecl, rootNode *ast.File, describe *ast.CallExpr) {
+ var funcIndex int = -1
+ for index, child := range rootNode.Decls {
+ if child == testFunc {
+ funcIndex = index
+ break
+ }
+ }
+
+ if funcIndex < 0 {
+ panic(fmt.Sprintf("Assert failed: Error finding index for test node %s\n", testFunc.Name.Name))
+ }
+
+ var block *ast.BlockStmt = blockStatementFromDescribe(describe)
+ block.List = append(block.List, createItStatementForTestFunc(testFunc))
+ replaceTestingTsWithGinkgoT(block, namedTestingTArg(testFunc))
+
+ // remove the old test func from the root node's declarations
+ rootNode.Decls = append(rootNode.Decls[:funcIndex], rootNode.Decls[funcIndex+1:]...)
+}
+
+/*
+ * walks nodes inside of a test func's statements and replaces the usage of
+ * it's named *testing.T param with GinkgoT's
+ */
+func replaceTestingTsWithGinkgoT(statementsBlock *ast.BlockStmt, testingT string) {
+ ast.Inspect(statementsBlock, func(node ast.Node) bool {
+ if node == nil {
+ return false
+ }
+
+ keyValueExpr, ok := node.(*ast.KeyValueExpr)
+ if ok {
+ replaceNamedTestingTsInKeyValueExpression(keyValueExpr, testingT)
+ return true
+ }
+
+ funcLiteral, ok := node.(*ast.FuncLit)
+ if ok {
+ replaceTypeDeclTestingTsInFuncLiteral(funcLiteral)
+ return true
+ }
+
+ callExpr, ok := node.(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+ replaceTestingTsInArgsLists(callExpr, testingT)
+
+ funCall, ok := callExpr.Fun.(*ast.SelectorExpr)
+ if ok {
+ replaceTestingTsMethodCalls(funCall, testingT)
+ }
+
+ return true
+ })
+}
+
+/*
+ * rewrite t.Fail() or any other *testing.T method by replacing with T().Fail()
+ * This function receives a selector expression (eg: t.Fail()) and
+ * the name of the *testing.T param from the function declaration. Rewrites the
+ * selector expression in place if the target was a *testing.T
+ */
+func replaceTestingTsMethodCalls(selectorExpr *ast.SelectorExpr, testingT string) {
+ ident, ok := selectorExpr.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+
+ if ident.Name == testingT {
+ selectorExpr.X = newGinkgoTFromIdent(ident)
+ }
+}
+
+/*
+ * replaces usages of a named *testing.T param inside of a call expression
+ * with a new GinkgoT object
+ */
+func replaceTestingTsInArgsLists(callExpr *ast.CallExpr, testingT string) {
+ for index, arg := range callExpr.Args {
+ ident, ok := arg.(*ast.Ident)
+ if !ok {
+ continue
+ }
+
+ if ident.Name == testingT {
+ callExpr.Args[index] = newGinkgoTFromIdent(ident)
+ }
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
new file mode 100644
index 000000000..418cdc4e5
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
@@ -0,0 +1,130 @@
+package convert
+
+import (
+ "go/ast"
+)
+
+/*
+ * Rewrites any other top level funcs that receive a *testing.T param
+ */
+func rewriteOtherFuncsToUseGinkgoT(declarations []ast.Decl) {
+ for _, decl := range declarations {
+ decl, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+
+ for _, param := range decl.Type.Params.List {
+ starExpr, ok := param.Type.(*ast.StarExpr)
+ if !ok {
+ continue
+ }
+
+ selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+
+ xIdent, ok := selectorExpr.X.(*ast.Ident)
+ if !ok || xIdent.Name != "testing" {
+ continue
+ }
+
+ if selectorExpr.Sel.Name != "T" {
+ continue
+ }
+
+ param.Type = newGinkgoTInterface()
+ }
+ }
+}
+
+/*
+ * Walks all of the nodes in the file, replacing *testing.T in struct
+ * and func literal nodes. eg:
+ * type foo struct { *testing.T }
+ * var bar = func(t *testing.T) { }
+ */
+func walkNodesInRootNodeReplacingTestingT(rootNode *ast.File) {
+ ast.Inspect(rootNode, func(node ast.Node) bool {
+ if node == nil {
+ return false
+ }
+
+ switch node := node.(type) {
+ case *ast.StructType:
+ replaceTestingTsInStructType(node)
+ case *ast.FuncLit:
+ replaceTypeDeclTestingTsInFuncLiteral(node)
+ }
+
+ return true
+ })
+}
+
+/*
+ * replaces named *testing.T inside a composite literal
+ */
+func replaceNamedTestingTsInKeyValueExpression(kve *ast.KeyValueExpr, testingT string) {
+ ident, ok := kve.Value.(*ast.Ident)
+ if !ok {
+ return
+ }
+
+ if ident.Name == testingT {
+ kve.Value = newGinkgoTFromIdent(ident)
+ }
+}
+
+/*
+ * replaces *testing.T params in a func literal with GinkgoT
+ */
+func replaceTypeDeclTestingTsInFuncLiteral(functionLiteral *ast.FuncLit) {
+ for _, arg := range functionLiteral.Type.Params.List {
+ starExpr, ok := arg.Type.(*ast.StarExpr)
+ if !ok {
+ continue
+ }
+
+ selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+
+ target, ok := selectorExpr.X.(*ast.Ident)
+ if !ok {
+ continue
+ }
+
+ if target.Name == "testing" && selectorExpr.Sel.Name == "T" {
+ arg.Type = newGinkgoTInterface()
+ }
+ }
+}
+
+/*
+ * Replaces *testing.T types inside of a struct declaration with a GinkgoT
+ * eg: type foo struct { *testing.T }
+ */
+func replaceTestingTsInStructType(structType *ast.StructType) {
+ for _, field := range structType.Fields.List {
+ starExpr, ok := field.Type.(*ast.StarExpr)
+ if !ok {
+ continue
+ }
+
+ selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+
+ xIdent, ok := selectorExpr.X.(*ast.Ident)
+ if !ok {
+ continue
+ }
+
+ if xIdent.Name == "testing" && selectorExpr.Sel.Name == "T" {
+ field.Type = newGinkgoTInterface()
+ }
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
new file mode 100644
index 000000000..5944ed85c
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/onsi/ginkgo/ginkgo/convert"
+)
+
+func BuildConvertCommand() *Command {
+ return &Command{
+ Name: "convert",
+ FlagSet: flag.NewFlagSet("convert", flag.ExitOnError),
+ UsageCommand: "ginkgo convert /path/to/package",
+ Usage: []string{
+ "Convert the package at the passed in path from an XUnit-style test to a Ginkgo-style test",
+ },
+ Command: convertPackage,
+ }
+}
+
+func convertPackage(args []string, additionalArgs []string) {
+ if len(args) != 1 {
+ println(fmt.Sprintf("usage: ginkgo convert /path/to/your/package"))
+ os.Exit(1)
+ }
+
+ defer func() {
+ err := recover()
+ if err != nil {
+ switch err := err.(type) {
+ case error:
+ println(err.Error())
+ case string:
+ println(err)
+ default:
+ println(fmt.Sprintf("unexpected error: %#v", err))
+ }
+ os.Exit(1)
+ }
+ }()
+
+ convert.RewritePackage(args[0])
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
new file mode 100644
index 000000000..99557048a
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
@@ -0,0 +1,256 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "text/template"
+)
+
+func BuildGenerateCommand() *Command {
+ var agouti, noDot, internal bool
+ flagSet := flag.NewFlagSet("generate", flag.ExitOnError)
+ flagSet.BoolVar(&agouti, "agouti", false, "If set, generate will generate a test file for writing Agouti tests")
+ flagSet.BoolVar(&noDot, "nodot", false, "If set, generate will generate a test file that does not . import ginkgo and gomega")
+ flagSet.BoolVar(&internal, "internal", false, "If set, generate will generate a test file that uses the regular package name")
+
+ return &Command{
+ Name: "generate",
+ FlagSet: flagSet,
+ UsageCommand: "ginkgo generate <filename(s)>",
+ Usage: []string{
+ "Generate a test file named filename_test.go",
+ "If the optional <filenames> argument is omitted, a file named after the package in the current directory will be created.",
+ "Accepts the following flags:",
+ },
+ Command: func(args []string, additionalArgs []string) {
+ generateSpec(args, agouti, noDot, internal)
+ },
+ }
+}
+
+var specText = `package {{.Package}}
+
+import (
+ {{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
+ {{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
+
+ {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}}
+)
+
+var _ = Describe("{{.Subject}}", func() {
+
+})
+`
+
+var agoutiSpecText = `package {{.Package}}
+
+import (
+ {{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
+ {{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
+ "github.com/sclevine/agouti"
+ . "github.com/sclevine/agouti/matchers"
+
+ {{if .ImportPackage}}"{{.PackageImportPath}}"{{end}}
+)
+
+var _ = Describe("{{.Subject}}", func() {
+ var page *agouti.Page
+
+ BeforeEach(func() {
+ var err error
+ page, err = agoutiDriver.NewPage()
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ AfterEach(func() {
+ Expect(page.Destroy()).To(Succeed())
+ })
+})
+`
+
+type specData struct {
+ Package string
+ Subject string
+ PackageImportPath string
+ IncludeImports bool
+ ImportPackage bool
+}
+
+func generateSpec(args []string, agouti, noDot, internal bool) {
+ if len(args) == 0 {
+ err := generateSpecForSubject("", agouti, noDot, internal)
+ if err != nil {
+ fmt.Println(err.Error())
+ fmt.Println("")
+ os.Exit(1)
+ }
+ fmt.Println("")
+ return
+ }
+
+ var failed bool
+ for _, arg := range args {
+ err := generateSpecForSubject(arg, agouti, noDot, internal)
+ if err != nil {
+ failed = true
+ fmt.Println(err.Error())
+ }
+ }
+ fmt.Println("")
+ if failed {
+ os.Exit(1)
+ }
+}
+
+func generateSpecForSubject(subject string, agouti, noDot, internal bool) error {
+ packageName, specFilePrefix, formattedName := getPackageAndFormattedName()
+ if subject != "" {
+ specFilePrefix = formatSubject(subject)
+ formattedName = prettifyPackageName(specFilePrefix)
+ }
+
+ data := specData{
+ Package: determinePackageName(packageName, internal),
+ Subject: formattedName,
+ PackageImportPath: getPackageImportPath(),
+ IncludeImports: !noDot,
+ ImportPackage: !internal,
+ }
+
+ targetFile := fmt.Sprintf("%s_test.go", specFilePrefix)
+ if fileExists(targetFile) {
+ return fmt.Errorf("%s already exists.", targetFile)
+ } else {
+ fmt.Printf("Generating ginkgo test for %s in:\n %s\n", data.Subject, targetFile)
+ }
+
+ f, err := os.Create(targetFile)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ var templateText string
+ if agouti {
+ templateText = agoutiSpecText
+ } else {
+ templateText = specText
+ }
+
+ specTemplate, err := template.New("spec").Parse(templateText)
+ if err != nil {
+ return err
+ }
+
+ specTemplate.Execute(f, data)
+ goFmt(targetFile)
+ return nil
+}
+
+func formatSubject(name string) string {
+ name = strings.Replace(name, "-", "_", -1)
+ name = strings.Replace(name, " ", "_", -1)
+ name = strings.Split(name, ".go")[0]
+ name = strings.Split(name, "_test")[0]
+ return name
+}
+
+// moduleName returns module name from go.mod from given module root directory
+func moduleName(modRoot string) string {
+ modFile, err := os.Open(filepath.Join(modRoot, "go.mod"))
+ if err != nil {
+ return ""
+ }
+
+ mod := make([]byte, 128)
+ _, err = modFile.Read(mod)
+ if err != nil {
+ return ""
+ }
+
+ slashSlash := []byte("//")
+ moduleStr := []byte("module")
+
+ for len(mod) > 0 {
+ line := mod
+ mod = nil
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, mod = line[:i], line[i+1:]
+ }
+ if i := bytes.Index(line, slashSlash); i >= 0 {
+ line = line[:i]
+ }
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, moduleStr) {
+ continue
+ }
+ line = line[len(moduleStr):]
+ n := len(line)
+ line = bytes.TrimSpace(line)
+ if len(line) == n || len(line) == 0 {
+ continue
+ }
+
+ if line[0] == '"' || line[0] == '`' {
+ p, err := strconv.Unquote(string(line))
+ if err != nil {
+ return "" // malformed quoted string or multiline module path
+ }
+ return p
+ }
+
+ return string(line)
+ }
+
+ return "" // missing module path
+}
+
+func findModuleRoot(dir string) (root string) {
+ dir = filepath.Clean(dir)
+
+ // Look for enclosing go.mod.
+ for {
+ if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
+ return dir
+ }
+ d := filepath.Dir(dir)
+ if d == dir {
+ break
+ }
+ dir = d
+ }
+ return ""
+}
+
+func getPackageImportPath() string {
+ workingDir, err := os.Getwd()
+ if err != nil {
+ panic(err.Error())
+ }
+
+ sep := string(filepath.Separator)
+
+ // Try go.mod file first
+ modRoot := findModuleRoot(workingDir)
+ if modRoot != "" {
+ modName := moduleName(modRoot)
+ if modName != "" {
+ cd := strings.Replace(workingDir, modRoot, "", -1)
+ cd = strings.ReplaceAll(cd, sep, "/")
+ return modName + cd
+ }
+ }
+
+ // Fallback to GOPATH structure
+ paths := strings.Split(workingDir, sep+"src"+sep)
+ if len(paths) == 1 {
+ fmt.Printf("\nCouldn't identify package import path.\n\n\tginkgo generate\n\nMust be run within a package directory under $GOPATH/src/...\nYou're going to have to change UNKNOWN_PACKAGE_PATH in the generated file...\n\n")
+ return "UNKNOWN_PACKAGE_PATH"
+ }
+ return filepath.ToSlash(paths[len(paths)-1])
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
new file mode 100644
index 000000000..23b1d2f11
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+)
+
+func BuildHelpCommand() *Command {
+ return &Command{
+ Name: "help",
+ FlagSet: flag.NewFlagSet("help", flag.ExitOnError),
+ UsageCommand: "ginkgo help <COMMAND>",
+ Usage: []string{
+ "Print usage information. If a command is passed in, print usage information just for that command.",
+ },
+ Command: printHelp,
+ }
+}
+
+func printHelp(args []string, additionalArgs []string) {
+ if len(args) == 0 {
+ usage()
+ } else {
+ command, found := commandMatching(args[0])
+ if !found {
+ complainAndQuit(fmt.Sprintf("Unknown command: %s", args[0]))
+ }
+
+ usageForCommand(command, true)
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
new file mode 100644
index 000000000..ec456bf29
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
@@ -0,0 +1,52 @@
+package interrupthandler
+
+import (
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+)
+
+type InterruptHandler struct {
+ interruptCount int
+ lock *sync.Mutex
+ C chan bool
+}
+
+func NewInterruptHandler() *InterruptHandler {
+ h := &InterruptHandler{
+ lock: &sync.Mutex{},
+ C: make(chan bool),
+ }
+
+ go h.handleInterrupt()
+ SwallowSigQuit()
+
+ return h
+}
+
+func (h *InterruptHandler) WasInterrupted() bool {
+ h.lock.Lock()
+ defer h.lock.Unlock()
+
+ return h.interruptCount > 0
+}
+
+func (h *InterruptHandler) handleInterrupt() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+
+ <-c
+ signal.Stop(c)
+
+ h.lock.Lock()
+ h.interruptCount++
+ if h.interruptCount == 1 {
+ close(h.C)
+ } else if h.interruptCount > 5 {
+ os.Exit(1)
+ }
+ h.lock.Unlock()
+
+ go h.handleInterrupt()
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
new file mode 100644
index 000000000..43c18544a
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
@@ -0,0 +1,14 @@
+// +build freebsd openbsd netbsd dragonfly darwin linux solaris
+
+package interrupthandler
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+func SwallowSigQuit() {
+ c := make(chan os.Signal, 1024)
+ signal.Notify(c, syscall.SIGQUIT)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
new file mode 100644
index 000000000..7f4a50e19
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
@@ -0,0 +1,7 @@
+// +build windows
+
+package interrupthandler
+
+func SwallowSigQuit() {
+ //noop
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/main.go b/vendor/github.com/onsi/ginkgo/ginkgo/main.go
new file mode 100644
index 000000000..f60c48a72
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/main.go
@@ -0,0 +1,302 @@
+/*
+The Ginkgo CLI
+
+The Ginkgo CLI is fully documented [here](http://onsi.github.io/ginkgo/#the_ginkgo_cli)
+
+You can also learn more by running:
+
+ ginkgo help
+
+Here are some of the more commonly used commands:
+
+To install:
+
+ go install github.com/onsi/ginkgo/ginkgo
+
+To run tests:
+
+ ginkgo
+
+To run tests in all subdirectories:
+
+ ginkgo -r
+
+To run tests in particular packages:
+
+ ginkgo <flags> /path/to/package /path/to/another/package
+
+To pass arguments/flags to your tests:
+
+ ginkgo <flags> <packages> -- <pass-throughs>
+
+To run tests in parallel
+
+ ginkgo -p
+
+this will automatically detect the optimal number of nodes to use. Alternatively, you can specify the number of nodes with:
+
+ ginkgo -nodes=N
+
+(note that you don't need to provide -p in this case).
+
+By default the Ginkgo CLI will spin up a server that the individual test processes send test output to. The CLI aggregates this output and then presents coherent test output, one test at a time, as each test completes.
+An alternative is to have the parallel nodes run and stream interleaved output back. This useful for debugging, particularly in contexts where tests hang/fail to start. To get this interleaved output:
+
+ ginkgo -nodes=N -stream=true
+
+On windows, the default value for stream is true.
+
+By default, when running multiple tests (with -r or a list of packages) Ginkgo will abort when a test fails. To have Ginkgo run subsequent test suites instead you can:
+
+ ginkgo -keepGoing
+
+To fail if there are ginkgo tests in a directory but no test suite (missing `RunSpecs`)
+
+ ginkgo -requireSuite
+
+To monitor packages and rerun tests when changes occur:
+
+ ginkgo watch <-r> </path/to/package>
+
+passing `ginkgo watch` the `-r` flag will recursively detect all test suites under the current directory and monitor them.
+`watch` does not detect *new* packages. Moreover, changes in package X only rerun the tests for package X, tests for packages
+that depend on X are not rerun.
+
+[OSX & Linux only] To receive (desktop) notifications when a test run completes:
+
+ ginkgo -notify
+
+this is particularly useful with `ginkgo watch`. Notifications are currently only supported on OS X and require that you `brew install terminal-notifier`
+
+Sometimes (to suss out race conditions/flakey tests, for example) you want to keep running a test suite until it fails. You can do this with:
+
+ ginkgo -untilItFails
+
+To bootstrap a test suite:
+
+ ginkgo bootstrap
+
+To generate a test file:
+
+ ginkgo generate <test_file_name>
+
+To bootstrap/generate test files without using "." imports:
+
+ ginkgo bootstrap --nodot
+ ginkgo generate --nodot
+
+this will explicitly export all the identifiers in Ginkgo and Gomega allowing you to rename them to avoid collisions. When you pull to the latest Ginkgo/Gomega you'll want to run
+
+ ginkgo nodot
+
+to refresh this list and pull in any new identifiers. In particular, this will pull in any new Gomega matchers that get added.
+
+To convert an existing XUnit style test suite to a Ginkgo-style test suite:
+
+ ginkgo convert .
+
+To unfocus tests:
+
+ ginkgo unfocus
+
+or
+
+ ginkgo blur
+
+To compile a test suite:
+
+ ginkgo build <path-to-package>
+
+will output an executable file named `package.test`. This can be run directly or by invoking
+
+ ginkgo <path-to-package.test>
+
+To print out Ginkgo's version:
+
+ ginkgo version
+
+To get more help:
+
+ ginkgo help
+*/
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+)
+
+const greenColor = "\x1b[32m"
+const redColor = "\x1b[91m"
+const defaultStyle = "\x1b[0m"
+const lightGrayColor = "\x1b[37m"
+
+type Command struct {
+ Name string
+ AltName string
+ FlagSet *flag.FlagSet
+ Usage []string
+ UsageCommand string
+ Command func(args []string, additionalArgs []string)
+ SuppressFlagDocumentation bool
+ FlagDocSubstitute []string
+}
+
+func (c *Command) Matches(name string) bool {
+ return c.Name == name || (c.AltName != "" && c.AltName == name)
+}
+
+func (c *Command) Run(args []string, additionalArgs []string) {
+ c.FlagSet.Usage = usage
+ c.FlagSet.Parse(args)
+ c.Command(c.FlagSet.Args(), additionalArgs)
+}
+
+var DefaultCommand *Command
+var Commands []*Command
+
+func init() {
+ DefaultCommand = BuildRunCommand()
+ Commands = append(Commands, BuildWatchCommand())
+ Commands = append(Commands, BuildBuildCommand())
+ Commands = append(Commands, BuildBootstrapCommand())
+ Commands = append(Commands, BuildGenerateCommand())
+ Commands = append(Commands, BuildNodotCommand())
+ Commands = append(Commands, BuildConvertCommand())
+ Commands = append(Commands, BuildUnfocusCommand())
+ Commands = append(Commands, BuildVersionCommand())
+ Commands = append(Commands, BuildHelpCommand())
+}
+
+func main() {
+ args := []string{}
+ additionalArgs := []string{}
+
+ foundDelimiter := false
+
+ for _, arg := range os.Args[1:] {
+ if !foundDelimiter {
+ if arg == "--" {
+ foundDelimiter = true
+ continue
+ }
+ }
+
+ if foundDelimiter {
+ additionalArgs = append(additionalArgs, arg)
+ } else {
+ args = append(args, arg)
+ }
+ }
+
+ if len(args) > 0 {
+ commandToRun, found := commandMatching(args[0])
+ if found {
+ commandToRun.Run(args[1:], additionalArgs)
+ return
+ }
+ }
+
+ DefaultCommand.Run(args, additionalArgs)
+}
+
+func commandMatching(name string) (*Command, bool) {
+ for _, command := range Commands {
+ if command.Matches(name) {
+ return command, true
+ }
+ }
+ return nil, false
+}
+
+func usage() {
+ fmt.Printf("Ginkgo Version %s\n\n", config.VERSION)
+ usageForCommand(DefaultCommand, false)
+ for _, command := range Commands {
+ fmt.Printf("\n")
+ usageForCommand(command, false)
+ }
+}
+
+func usageForCommand(command *Command, longForm bool) {
+ fmt.Printf("%s\n%s\n", command.UsageCommand, strings.Repeat("-", len(command.UsageCommand)))
+ fmt.Printf("%s\n", strings.Join(command.Usage, "\n"))
+ if command.SuppressFlagDocumentation && !longForm {
+ fmt.Printf("%s\n", strings.Join(command.FlagDocSubstitute, "\n "))
+ } else {
+ command.FlagSet.SetOutput(os.Stdout)
+ command.FlagSet.PrintDefaults()
+ }
+}
+
+func complainAndQuit(complaint string) {
+ fmt.Fprintf(os.Stderr, "%s\nFor usage instructions:\n\tginkgo help\n", complaint)
+ os.Exit(1)
+}
+
+func findSuites(args []string, recurseForAll bool, skipPackage string, allowPrecompiled bool) ([]testsuite.TestSuite, []string) {
+ suites := []testsuite.TestSuite{}
+
+ if len(args) > 0 {
+ for _, arg := range args {
+ if allowPrecompiled {
+ suite, err := testsuite.PrecompiledTestSuite(arg)
+ if err == nil {
+ suites = append(suites, suite)
+ continue
+ }
+ }
+ recurseForSuite := recurseForAll
+ if strings.HasSuffix(arg, "/...") && arg != "/..." {
+ arg = arg[:len(arg)-4]
+ recurseForSuite = true
+ }
+ suites = append(suites, testsuite.SuitesInDir(arg, recurseForSuite)...)
+ }
+ } else {
+ suites = testsuite.SuitesInDir(".", recurseForAll)
+ }
+
+ skippedPackages := []string{}
+ if skipPackage != "" {
+ skipFilters := strings.Split(skipPackage, ",")
+ filteredSuites := []testsuite.TestSuite{}
+ for _, suite := range suites {
+ skip := false
+ for _, skipFilter := range skipFilters {
+ if strings.Contains(suite.Path, skipFilter) {
+ skip = true
+ break
+ }
+ }
+ if skip {
+ skippedPackages = append(skippedPackages, suite.Path)
+ } else {
+ filteredSuites = append(filteredSuites, suite)
+ }
+ }
+ suites = filteredSuites
+ }
+
+ return suites, skippedPackages
+}
+
+func goFmt(path string) {
+ out, err := exec.Command("go", "fmt", path).CombinedOutput()
+ if err != nil {
+ complainAndQuit("Could not fmt: " + err.Error() + "\n" + string(out))
+ }
+}
+
+func pluralizedWord(singular, plural string, count int) string {
+ if count == 1 {
+ return singular
+ }
+ return plural
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go b/vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
new file mode 100644
index 000000000..c87b72165
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
@@ -0,0 +1,196 @@
+package nodot
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "path/filepath"
+ "strings"
+)
+
+func ApplyNoDot(data []byte) ([]byte, error) {
+ sections, err := generateNodotSections()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, section := range sections {
+ data = section.createOrUpdateIn(data)
+ }
+
+ return data, nil
+}
+
+type nodotSection struct {
+ name string
+ pkg string
+ declarations []string
+ types []string
+}
+
+func (s nodotSection) createOrUpdateIn(data []byte) []byte {
+ renames := map[string]string{}
+
+ contents := string(data)
+
+ lines := strings.Split(contents, "\n")
+
+ comment := "// Declarations for " + s.name
+
+ newLines := []string{}
+ for _, line := range lines {
+ if line == comment {
+ continue
+ }
+
+ words := strings.Split(line, " ")
+ lastWord := words[len(words)-1]
+
+ if s.containsDeclarationOrType(lastWord) {
+ renames[lastWord] = words[1]
+ continue
+ }
+
+ newLines = append(newLines, line)
+ }
+
+ if len(newLines[len(newLines)-1]) > 0 {
+ newLines = append(newLines, "")
+ }
+
+ newLines = append(newLines, comment)
+
+ for _, typ := range s.types {
+ name, ok := renames[s.prefix(typ)]
+ if !ok {
+ name = typ
+ }
+ newLines = append(newLines, fmt.Sprintf("type %s %s", name, s.prefix(typ)))
+ }
+
+ for _, decl := range s.declarations {
+ name, ok := renames[s.prefix(decl)]
+ if !ok {
+ name = decl
+ }
+ newLines = append(newLines, fmt.Sprintf("var %s = %s", name, s.prefix(decl)))
+ }
+
+ newLines = append(newLines, "")
+
+ newContents := strings.Join(newLines, "\n")
+
+ return []byte(newContents)
+}
+
+func (s nodotSection) prefix(declOrType string) string {
+ return s.pkg + "." + declOrType
+}
+
+func (s nodotSection) containsDeclarationOrType(word string) bool {
+ for _, declaration := range s.declarations {
+ if s.prefix(declaration) == word {
+ return true
+ }
+ }
+
+ for _, typ := range s.types {
+ if s.prefix(typ) == word {
+ return true
+ }
+ }
+
+ return false
+}
+
+func generateNodotSections() ([]nodotSection, error) {
+ sections := []nodotSection{}
+
+ declarations, err := getExportedDeclerationsForPackage("github.com/onsi/ginkgo", "ginkgo_dsl.go", "GINKGO_VERSION", "GINKGO_PANIC")
+ if err != nil {
+ return nil, err
+ }
+ sections = append(sections, nodotSection{
+ name: "Ginkgo DSL",
+ pkg: "ginkgo",
+ declarations: declarations,
+ types: []string{"Done", "Benchmarker"},
+ })
+
+ declarations, err = getExportedDeclerationsForPackage("github.com/onsi/gomega", "gomega_dsl.go", "GOMEGA_VERSION")
+ if err != nil {
+ return nil, err
+ }
+ sections = append(sections, nodotSection{
+ name: "Gomega DSL",
+ pkg: "gomega",
+ declarations: declarations,
+ })
+
+ declarations, err = getExportedDeclerationsForPackage("github.com/onsi/gomega", "matchers.go")
+ if err != nil {
+ return nil, err
+ }
+ sections = append(sections, nodotSection{
+ name: "Gomega Matchers",
+ pkg: "gomega",
+ declarations: declarations,
+ })
+
+ return sections, nil
+}
+
+func getExportedDeclerationsForPackage(pkgPath string, filename string, blacklist ...string) ([]string, error) {
+ pkg, err := build.Import(pkgPath, ".", 0)
+ if err != nil {
+ return []string{}, err
+ }
+
+ declarations, err := getExportedDeclarationsForFile(filepath.Join(pkg.Dir, filename))
+ if err != nil {
+ return []string{}, err
+ }
+
+ blacklistLookup := map[string]bool{}
+ for _, declaration := range blacklist {
+ blacklistLookup[declaration] = true
+ }
+
+ filteredDeclarations := []string{}
+ for _, declaration := range declarations {
+ if blacklistLookup[declaration] {
+ continue
+ }
+ filteredDeclarations = append(filteredDeclarations, declaration)
+ }
+
+ return filteredDeclarations, nil
+}
+
+func getExportedDeclarationsForFile(path string) ([]string, error) {
+ fset := token.NewFileSet()
+ tree, err := parser.ParseFile(fset, path, nil, 0)
+ if err != nil {
+ return []string{}, err
+ }
+
+ declarations := []string{}
+ ast.FileExports(tree)
+ for _, decl := range tree.Decls {
+ switch x := decl.(type) {
+ case *ast.GenDecl:
+ switch s := x.Specs[0].(type) {
+ case *ast.ValueSpec:
+ declarations = append(declarations, s.Names[0].Name)
+ }
+ case *ast.FuncDecl:
+ if x.Recv == nil {
+ declarations = append(declarations, x.Name.Name)
+ }
+ }
+ }
+
+ return declarations, nil
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
new file mode 100644
index 000000000..39b88b5d1
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+ "bufio"
+ "flag"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+
+ "github.com/onsi/ginkgo/ginkgo/nodot"
+)
+
+func BuildNodotCommand() *Command {
+ return &Command{
+ Name: "nodot",
+ FlagSet: flag.NewFlagSet("bootstrap", flag.ExitOnError),
+ UsageCommand: "ginkgo nodot",
+ Usage: []string{
+ "Update the nodot declarations in your test suite",
+ "Any missing declarations (from, say, a recently added matcher) will be added to your bootstrap file.",
+ "If you've renamed a declaration, that name will be honored and not overwritten.",
+ },
+ Command: updateNodot,
+ }
+}
+
+func updateNodot(args []string, additionalArgs []string) {
+ suiteFile, perm := findSuiteFile()
+
+ data, err := ioutil.ReadFile(suiteFile)
+ if err != nil {
+ complainAndQuit("Failed to update nodot declarations: " + err.Error())
+ }
+
+ content, err := nodot.ApplyNoDot(data)
+ if err != nil {
+ complainAndQuit("Failed to update nodot declarations: " + err.Error())
+ }
+ ioutil.WriteFile(suiteFile, content, perm)
+
+ goFmt(suiteFile)
+}
+
+func findSuiteFile() (string, os.FileMode) {
+ workingDir, err := os.Getwd()
+ if err != nil {
+ complainAndQuit("Could not find suite file for nodot: " + err.Error())
+ }
+
+ files, err := ioutil.ReadDir(workingDir)
+ if err != nil {
+ complainAndQuit("Could not find suite file for nodot: " + err.Error())
+ }
+
+ re := regexp.MustCompile(`RunSpecs\(|RunSpecsWithDefaultAndCustomReporters\(|RunSpecsWithCustomReporters\(`)
+
+ for _, file := range files {
+ if file.IsDir() {
+ continue
+ }
+ path := filepath.Join(workingDir, file.Name())
+ f, err := os.Open(path)
+ if err != nil {
+ complainAndQuit("Could not find suite file for nodot: " + err.Error())
+ }
+ defer f.Close()
+
+ if re.MatchReader(bufio.NewReader(f)) {
+ return path, file.Mode()
+ }
+ }
+
+ complainAndQuit("Could not find a suite file for nodot: you need a bootstrap file that call's Ginkgo's RunSpecs() command.\nTry running ginkgo bootstrap first.")
+
+ return "", 0
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/notifications.go b/vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
new file mode 100644
index 000000000..368d61fb3
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
@@ -0,0 +1,141 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "regexp"
+ "runtime"
+ "strings"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+)
+
+type Notifier struct {
+ commandFlags *RunWatchAndBuildCommandFlags
+}
+
+func NewNotifier(commandFlags *RunWatchAndBuildCommandFlags) *Notifier {
+ return &Notifier{
+ commandFlags: commandFlags,
+ }
+}
+
+func (n *Notifier) VerifyNotificationsAreAvailable() {
+ if n.commandFlags.Notify {
+ onLinux := (runtime.GOOS == "linux")
+ onOSX := (runtime.GOOS == "darwin")
+ if onOSX {
+
+ _, err := exec.LookPath("terminal-notifier")
+ if err != nil {
+ fmt.Printf(`--notify requires terminal-notifier, which you don't seem to have installed.
+
+OSX:
+
+To remedy this:
+
+ brew install terminal-notifier
+
+To learn more about terminal-notifier:
+
+ https://github.com/alloy/terminal-notifier
+`)
+ os.Exit(1)
+ }
+
+ } else if onLinux {
+
+ _, err := exec.LookPath("notify-send")
+ if err != nil {
+ fmt.Printf(`--notify requires terminal-notifier or notify-send, which you don't seem to have installed.
+
+Linux:
+
+Download and install notify-send for your distribution
+`)
+ os.Exit(1)
+ }
+
+ }
+ }
+}
+
+func (n *Notifier) SendSuiteCompletionNotification(suite testsuite.TestSuite, suitePassed bool) {
+ if suitePassed {
+ n.SendNotification("Ginkgo [PASS]", fmt.Sprintf(`Test suite for "%s" passed.`, suite.PackageName))
+ } else {
+ n.SendNotification("Ginkgo [FAIL]", fmt.Sprintf(`Test suite for "%s" failed.`, suite.PackageName))
+ }
+}
+
+func (n *Notifier) SendNotification(title string, subtitle string) {
+
+ if n.commandFlags.Notify {
+ onLinux := (runtime.GOOS == "linux")
+ onOSX := (runtime.GOOS == "darwin")
+
+ if onOSX {
+
+ _, err := exec.LookPath("terminal-notifier")
+ if err == nil {
+ args := []string{"-title", title, "-subtitle", subtitle, "-group", "com.onsi.ginkgo"}
+ terminal := os.Getenv("TERM_PROGRAM")
+ if terminal == "iTerm.app" {
+ args = append(args, "-activate", "com.googlecode.iterm2")
+ } else if terminal == "Apple_Terminal" {
+ args = append(args, "-activate", "com.apple.Terminal")
+ }
+
+ exec.Command("terminal-notifier", args...).Run()
+ }
+
+ } else if onLinux {
+
+ _, err := exec.LookPath("notify-send")
+ if err == nil {
+ args := []string{"-a", "ginkgo", title, subtitle}
+ exec.Command("notify-send", args...).Run()
+ }
+
+ }
+ }
+}
+
+func (n *Notifier) RunCommand(suite testsuite.TestSuite, suitePassed bool) {
+
+ command := n.commandFlags.AfterSuiteHook
+ if command != "" {
+
+ // Allow for string replacement to pass input to the command
+ passed := "[FAIL]"
+ if suitePassed {
+ passed = "[PASS]"
+ }
+ command = strings.Replace(command, "(ginkgo-suite-passed)", passed, -1)
+ command = strings.Replace(command, "(ginkgo-suite-name)", suite.PackageName, -1)
+
+ // Must break command into parts
+ splitArgs := regexp.MustCompile(`'.+'|".+"|\S+`)
+ parts := splitArgs.FindAllString(command, -1)
+
+ output, err := exec.Command(parts[0], parts[1:]...).CombinedOutput()
+ if err != nil {
+ fmt.Println("Post-suite command failed:")
+ if config.DefaultReporterConfig.NoColor {
+ fmt.Printf("\t%s\n", output)
+ } else {
+ fmt.Printf("\t%s%s%s\n", redColor, string(output), defaultStyle)
+ }
+ n.SendNotification("Ginkgo [ERROR]", fmt.Sprintf(`After suite command "%s" failed`, n.commandFlags.AfterSuiteHook))
+ } else {
+ fmt.Println("Post-suite command succeeded:")
+ if config.DefaultReporterConfig.NoColor {
+ fmt.Printf("\t%s\n", output)
+ } else {
+ fmt.Printf("\t%s%s%s\n", greenColor, string(output), defaultStyle)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
new file mode 100644
index 000000000..f225d272f
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
@@ -0,0 +1,291 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "math/rand"
+ "os"
+ "regexp"
+ "strings"
+ "time"
+
+ "io/ioutil"
+ "path/filepath"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/interrupthandler"
+ "github.com/onsi/ginkgo/ginkgo/testrunner"
+ "github.com/onsi/ginkgo/types"
+)
+
+func BuildRunCommand() *Command {
+ commandFlags := NewRunCommandFlags(flag.NewFlagSet("ginkgo", flag.ExitOnError))
+ notifier := NewNotifier(commandFlags)
+ interruptHandler := interrupthandler.NewInterruptHandler()
+ runner := &SpecRunner{
+ commandFlags: commandFlags,
+ notifier: notifier,
+ interruptHandler: interruptHandler,
+ suiteRunner: NewSuiteRunner(notifier, interruptHandler),
+ }
+
+ return &Command{
+ Name: "",
+ FlagSet: commandFlags.FlagSet,
+ UsageCommand: "ginkgo <FLAGS> <PACKAGES> -- <PASS-THROUGHS>",
+ Usage: []string{
+ "Run the tests in the passed in <PACKAGES> (or the package in the current directory if left blank).",
+ "Any arguments after -- will be passed to the test.",
+ "Accepts the following flags:",
+ },
+ Command: runner.RunSpecs,
+ }
+}
+
+type SpecRunner struct {
+ commandFlags *RunWatchAndBuildCommandFlags
+ notifier *Notifier
+ interruptHandler *interrupthandler.InterruptHandler
+ suiteRunner *SuiteRunner
+}
+
+func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
+ r.commandFlags.computeNodes()
+ r.notifier.VerifyNotificationsAreAvailable()
+
+ suites, skippedPackages := findSuites(args, r.commandFlags.Recurse, r.commandFlags.SkipPackage, true)
+ if len(skippedPackages) > 0 {
+ fmt.Println("Will skip:")
+ for _, skippedPackage := range skippedPackages {
+ fmt.Println(" " + skippedPackage)
+ }
+ }
+
+ if len(skippedPackages) > 0 && len(suites) == 0 {
+ fmt.Println("All tests skipped! Exiting...")
+ os.Exit(0)
+ }
+
+ if len(suites) == 0 {
+ complainAndQuit("Found no test suites")
+ }
+
+ r.ComputeSuccinctMode(len(suites))
+
+ t := time.Now()
+
+ runners := []*testrunner.TestRunner{}
+ for _, suite := range suites {
+ runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.Timeout, r.commandFlags.GoOpts, additionalArgs))
+ }
+
+ numSuites := 0
+ runResult := testrunner.PassingRunResult()
+ if r.commandFlags.UntilItFails {
+ iteration := 0
+ for {
+ r.UpdateSeed()
+ randomizedRunners := r.randomizeOrder(runners)
+ runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
+ iteration++
+
+ if r.interruptHandler.WasInterrupted() {
+ break
+ }
+
+ if runResult.Passed {
+ fmt.Printf("\nAll tests passed...\nWill keep running them until they fail.\nThis was attempt #%d\n%s\n", iteration, orcMessage(iteration))
+ } else {
+ fmt.Printf("\nTests failed on attempt #%d\n\n", iteration)
+ break
+ }
+ }
+ } else {
+ randomizedRunners := r.randomizeOrder(runners)
+ runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
+ }
+
+ for _, runner := range runners {
+ runner.CleanUp()
+ }
+
+ if r.isInCoverageMode() {
+ if r.getOutputDir() != "" {
+ // If coverprofile is set, combine coverages
+ if r.getCoverprofile() != "" {
+ if err := r.combineCoverprofiles(runners); err != nil {
+ fmt.Println(err.Error())
+ os.Exit(1)
+ }
+ } else {
+ // Just move them
+ r.moveCoverprofiles(runners)
+ }
+ }
+ }
+
+ fmt.Printf("\nGinkgo ran %d %s in %s\n", numSuites, pluralizedWord("suite", "suites", numSuites), time.Since(t))
+
+ if runResult.Passed {
+ if runResult.HasProgrammaticFocus && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
+ fmt.Printf("Test Suite Passed\n")
+ fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE)
+ os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
+ } else {
+ fmt.Printf("Test Suite Passed\n")
+ os.Exit(0)
+ }
+ } else {
+ fmt.Printf("Test Suite Failed\n")
+ os.Exit(1)
+ }
+}
+
+// Moves all generated profiles to specified directory
+func (r *SpecRunner) moveCoverprofiles(runners []*testrunner.TestRunner) {
+ for _, runner := range runners {
+ _, filename := filepath.Split(runner.CoverageFile)
+ err := os.Rename(runner.CoverageFile, filepath.Join(r.getOutputDir(), filename))
+
+ if err != nil {
+ fmt.Printf("Unable to move coverprofile %s, %v\n", runner.CoverageFile, err)
+ return
+ }
+ }
+}
+
+// Combines all generated profiles in the specified directory
+func (r *SpecRunner) combineCoverprofiles(runners []*testrunner.TestRunner) error {
+
+ path, _ := filepath.Abs(r.getOutputDir())
+ if !fileExists(path) {
+ return fmt.Errorf("Unable to create combined profile, outputdir does not exist: %s", r.getOutputDir())
+ }
+
+ fmt.Println("path is " + path)
+
+ combined, err := os.OpenFile(
+ filepath.Join(path, r.getCoverprofile()),
+ os.O_WRONLY|os.O_CREATE,
+ 0666,
+ )
+
+ if err != nil {
+ fmt.Printf("Unable to create combined profile, %v\n", err)
+ return nil // non-fatal error
+ }
+
+ modeRegex := regexp.MustCompile(`^mode: .*\n`)
+ for index, runner := range runners {
+ contents, err := ioutil.ReadFile(runner.CoverageFile)
+
+ if err != nil {
+ fmt.Printf("Unable to read coverage file %s to combine, %v\n", runner.CoverageFile, err)
+ return nil // non-fatal error
+ }
+
+ // remove the cover mode line from every file
+ // except the first one
+ if index > 0 {
+ contents = modeRegex.ReplaceAll(contents, []byte{})
+ }
+
+ _, err = combined.Write(contents)
+
+ // Add a newline to the end of every file if missing.
+ if err == nil && len(contents) > 0 && contents[len(contents)-1] != '\n' {
+ _, err = combined.Write([]byte("\n"))
+ }
+
+ if err != nil {
+ fmt.Printf("Unable to append to coverprofile, %v\n", err)
+ return nil // non-fatal error
+ }
+ }
+
+ fmt.Println("All profiles combined")
+ return nil
+}
+
+func (r *SpecRunner) isInCoverageMode() bool {
+ opts := r.commandFlags.GoOpts
+ return *opts["cover"].(*bool) || *opts["coverpkg"].(*string) != "" || *opts["covermode"].(*string) != ""
+}
+
+func (r *SpecRunner) getCoverprofile() string {
+ return *r.commandFlags.GoOpts["coverprofile"].(*string)
+}
+
+func (r *SpecRunner) getOutputDir() string {
+ return *r.commandFlags.GoOpts["outputdir"].(*string)
+}
+
+func (r *SpecRunner) ComputeSuccinctMode(numSuites int) {
+ if config.DefaultReporterConfig.Verbose {
+ config.DefaultReporterConfig.Succinct = false
+ return
+ }
+
+ if numSuites == 1 {
+ return
+ }
+
+ if numSuites > 1 && !r.commandFlags.wasSet("succinct") {
+ config.DefaultReporterConfig.Succinct = true
+ }
+}
+
+func (r *SpecRunner) UpdateSeed() {
+ if !r.commandFlags.wasSet("seed") {
+ config.GinkgoConfig.RandomSeed = time.Now().Unix()
+ }
+}
+
+func (r *SpecRunner) randomizeOrder(runners []*testrunner.TestRunner) []*testrunner.TestRunner {
+ if !r.commandFlags.RandomizeSuites {
+ return runners
+ }
+
+ if len(runners) <= 1 {
+ return runners
+ }
+
+ randomizedRunners := make([]*testrunner.TestRunner, len(runners))
+ randomizer := rand.New(rand.NewSource(config.GinkgoConfig.RandomSeed))
+ permutation := randomizer.Perm(len(runners))
+ for i, j := range permutation {
+ randomizedRunners[i] = runners[j]
+ }
+ return randomizedRunners
+}
+
+func orcMessage(iteration int) string {
+ if iteration < 10 {
+ return ""
+ } else if iteration < 30 {
+ return []string{
+ "If at first you succeed...",
+ "...try, try again.",
+ "Looking good!",
+ "Still good...",
+ "I think your tests are fine....",
+ "Yep, still passing",
+ "Oh boy, here I go testin' again!",
+ "Even the gophers are getting bored",
+ "Did you try -race?",
+ "Maybe you should stop now?",
+ "I'm getting tired...",
+ "What if I just made you a sandwich?",
+ "Hit ^C, hit ^C, please hit ^C",
+ "Make it stop. Please!",
+ "Come on! Enough is enough!",
+ "Dave, this conversation can serve no purpose anymore. Goodbye.",
+ "Just what do you think you're doing, Dave? ",
+ "I, Sisyphus",
+ "Insanity: doing the same thing over and over again and expecting different results. -Einstein",
+ "I guess Einstein never tried to churn butter",
+ }[iteration-10] + "\n"
+ } else {
+ return "No, seriously... you can probably stop now.\n"
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go b/vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
new file mode 100644
index 000000000..e0994fc3c
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
@@ -0,0 +1,169 @@
+package main
+
+import (
+ "flag"
+ "runtime"
+
+ "time"
+
+ "github.com/onsi/ginkgo/config"
+)
+
+type RunWatchAndBuildCommandFlags struct {
+ Recurse bool
+ SkipPackage string
+ GoOpts map[string]interface{}
+
+ //for run and watch commands
+ NumCPU int
+ NumCompilers int
+ ParallelStream bool
+ Notify bool
+ AfterSuiteHook string
+ AutoNodes bool
+ Timeout time.Duration
+
+ //only for run command
+ KeepGoing bool
+ UntilItFails bool
+ RandomizeSuites bool
+
+ //only for watch command
+ Depth int
+ WatchRegExp string
+
+ FlagSet *flag.FlagSet
+}
+
+const runMode = 1
+const watchMode = 2
+const buildMode = 3
+
+func NewRunCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
+ c := &RunWatchAndBuildCommandFlags{
+ FlagSet: flagSet,
+ }
+ c.flags(runMode)
+ return c
+}
+
+func NewWatchCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
+ c := &RunWatchAndBuildCommandFlags{
+ FlagSet: flagSet,
+ }
+ c.flags(watchMode)
+ return c
+}
+
+func NewBuildCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
+ c := &RunWatchAndBuildCommandFlags{
+ FlagSet: flagSet,
+ }
+ c.flags(buildMode)
+ return c
+}
+
+func (c *RunWatchAndBuildCommandFlags) wasSet(flagName string) bool {
+ wasSet := false
+ c.FlagSet.Visit(func(f *flag.Flag) {
+ if f.Name == flagName {
+ wasSet = true
+ }
+ })
+
+ return wasSet
+}
+
+func (c *RunWatchAndBuildCommandFlags) computeNodes() {
+ if c.wasSet("nodes") {
+ return
+ }
+ if c.AutoNodes {
+ switch n := runtime.NumCPU(); {
+ case n <= 4:
+ c.NumCPU = n
+ default:
+ c.NumCPU = n - 1
+ }
+ }
+}
+
+func (c *RunWatchAndBuildCommandFlags) stringSlot(slot string) *string {
+ var opt string
+ c.GoOpts[slot] = &opt
+ return &opt
+}
+
+func (c *RunWatchAndBuildCommandFlags) boolSlot(slot string) *bool {
+ var opt bool
+ c.GoOpts[slot] = &opt
+ return &opt
+}
+
+func (c *RunWatchAndBuildCommandFlags) intSlot(slot string) *int {
+ var opt int
+ c.GoOpts[slot] = &opt
+ return &opt
+}
+
+func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
+ c.GoOpts = make(map[string]interface{})
+
+ onWindows := (runtime.GOOS == "windows")
+
+ c.FlagSet.BoolVar(&(c.Recurse), "r", false, "Find and run test suites under the current directory recursively.")
+ c.FlagSet.BoolVar(c.boolSlot("race"), "race", false, "Run tests with race detection enabled.")
+ c.FlagSet.BoolVar(c.boolSlot("cover"), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory.")
+ c.FlagSet.StringVar(c.stringSlot("coverpkg"), "coverpkg", "", "Run tests with coverage on the given external modules.")
+ c.FlagSet.StringVar(&(c.SkipPackage), "skipPackage", "", "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored.")
+ c.FlagSet.StringVar(c.stringSlot("tags"), "tags", "", "A list of build tags to consider satisfied during the build.")
+ c.FlagSet.StringVar(c.stringSlot("gcflags"), "gcflags", "", "Arguments to pass on each go tool compile invocation.")
+ c.FlagSet.StringVar(c.stringSlot("covermode"), "covermode", "", "Set the mode for coverage analysis.")
+ c.FlagSet.BoolVar(c.boolSlot("a"), "a", false, "Force rebuilding of packages that are already up-to-date.")
+ c.FlagSet.BoolVar(c.boolSlot("n"), "n", false, "Have `go test` print the commands but do not run them.")
+ c.FlagSet.BoolVar(c.boolSlot("msan"), "msan", false, "Enable interoperation with memory sanitizer.")
+ c.FlagSet.BoolVar(c.boolSlot("x"), "x", false, "Have `go test` print the commands.")
+ c.FlagSet.BoolVar(c.boolSlot("work"), "work", false, "Print the name of the temporary work directory and do not delete it when exiting.")
+ c.FlagSet.StringVar(c.stringSlot("asmflags"), "asmflags", "", "Arguments to pass on each go tool asm invocation.")
+ c.FlagSet.StringVar(c.stringSlot("buildmode"), "buildmode", "", "Build mode to use. See 'go help buildmode' for more.")
+ c.FlagSet.StringVar(c.stringSlot("mod"), "mod", "", "Go module control. See 'go help modules' for more.")
+ c.FlagSet.StringVar(c.stringSlot("compiler"), "compiler", "", "Name of compiler to use, as in runtime.Compiler (gccgo or gc).")
+ c.FlagSet.StringVar(c.stringSlot("gccgoflags"), "gccgoflags", "", "Arguments to pass on each gccgo compiler/linker invocation.")
+ c.FlagSet.StringVar(c.stringSlot("installsuffix"), "installsuffix", "", "A suffix to use in the name of the package installation directory.")
+ c.FlagSet.StringVar(c.stringSlot("ldflags"), "ldflags", "", "Arguments to pass on each go tool link invocation.")
+ c.FlagSet.BoolVar(c.boolSlot("linkshared"), "linkshared", false, "Link against shared libraries previously created with -buildmode=shared.")
+ c.FlagSet.StringVar(c.stringSlot("pkgdir"), "pkgdir", "", "install and load all packages from the given dir instead of the usual locations.")
+ c.FlagSet.StringVar(c.stringSlot("toolexec"), "toolexec", "", "a program to use to invoke toolchain programs like vet and asm.")
+ c.FlagSet.IntVar(c.intSlot("blockprofilerate"), "blockprofilerate", 1, "Control the detail provided in goroutine blocking profiles by calling runtime.SetBlockProfileRate with the given value.")
+ c.FlagSet.StringVar(c.stringSlot("coverprofile"), "coverprofile", "", "Write a coverage profile to the specified file after all tests have passed.")
+ c.FlagSet.StringVar(c.stringSlot("cpuprofile"), "cpuprofile", "", "Write a CPU profile to the specified file before exiting.")
+ c.FlagSet.StringVar(c.stringSlot("memprofile"), "memprofile", "", "Write a memory profile to the specified file after all tests have passed.")
+ c.FlagSet.IntVar(c.intSlot("memprofilerate"), "memprofilerate", 0, "Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate.")
+ c.FlagSet.StringVar(c.stringSlot("outputdir"), "outputdir", "", "Place output files from profiling in the specified directory.")
+ c.FlagSet.BoolVar(c.boolSlot("requireSuite"), "requireSuite", false, "Fail if there are ginkgo tests in a directory but no test suite (missing RunSpecs)")
+ c.FlagSet.StringVar(c.stringSlot("vet"), "vet", "", "Configure the invocation of 'go vet' to use the comma-separated list of vet checks. If list is 'off', 'go test' does not run 'go vet' at all.")
+
+ if mode == runMode || mode == watchMode {
+ config.Flags(c.FlagSet, "", false)
+ c.FlagSet.IntVar(&(c.NumCPU), "nodes", 1, "The number of parallel test nodes to run")
+ c.FlagSet.IntVar(&(c.NumCompilers), "compilers", 0, "The number of concurrent compilations to run (0 will autodetect)")
+ c.FlagSet.BoolVar(&(c.AutoNodes), "p", false, "Run in parallel with auto-detected number of nodes")
+ c.FlagSet.BoolVar(&(c.ParallelStream), "stream", onWindows, "stream parallel test output in real time: less coherent, but useful for debugging")
+ if !onWindows {
+ c.FlagSet.BoolVar(&(c.Notify), "notify", false, "Send desktop notifications when a test run completes")
+ }
+ c.FlagSet.StringVar(&(c.AfterSuiteHook), "afterSuiteHook", "", "Run a command when a suite test run completes")
+ c.FlagSet.DurationVar(&(c.Timeout), "timeout", 24*time.Hour, "Suite fails if it does not complete within the specified timeout")
+ }
+
+ if mode == runMode {
+ c.FlagSet.BoolVar(&(c.KeepGoing), "keepGoing", false, "When true, failures from earlier test suites do not prevent later test suites from running")
+ c.FlagSet.BoolVar(&(c.UntilItFails), "untilItFails", false, "When true, Ginkgo will keep rerunning tests until a failure occurs")
+ c.FlagSet.BoolVar(&(c.RandomizeSuites), "randomizeSuites", false, "When true, Ginkgo will randomize the order in which test suites run")
+ }
+
+ if mode == watchMode {
+ c.FlagSet.IntVar(&(c.Depth), "depth", 1, "Ginkgo will watch dependencies down to this depth in the dependency tree")
+ c.FlagSet.StringVar(&(c.WatchRegExp), "watchRegExp", `\.go$`, "Files matching this regular expression will be watched for changes")
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go b/vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
new file mode 100644
index 000000000..ab746d7e9
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
@@ -0,0 +1,173 @@
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/interrupthandler"
+ "github.com/onsi/ginkgo/ginkgo/testrunner"
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+ colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
+)
+
+type compilationInput struct {
+ runner *testrunner.TestRunner
+ result chan compilationOutput
+}
+
+type compilationOutput struct {
+ runner *testrunner.TestRunner
+ err error
+}
+
+type SuiteRunner struct {
+ notifier *Notifier
+ interruptHandler *interrupthandler.InterruptHandler
+}
+
+func NewSuiteRunner(notifier *Notifier, interruptHandler *interrupthandler.InterruptHandler) *SuiteRunner {
+ return &SuiteRunner{
+ notifier: notifier,
+ interruptHandler: interruptHandler,
+ }
+}
+
+func (r *SuiteRunner) compileInParallel(runners []*testrunner.TestRunner, numCompilers int, willCompile func(suite testsuite.TestSuite)) chan compilationOutput {
+ //we return this to the consumer, it will return each runner in order as it compiles
+ compilationOutputs := make(chan compilationOutput, len(runners))
+
+ //an array of channels - the nth runner's compilation output is sent to the nth channel in this array
+ //we read from these channels in order to ensure we run the suites in order
+ orderedCompilationOutputs := []chan compilationOutput{}
+ for range runners {
+ orderedCompilationOutputs = append(orderedCompilationOutputs, make(chan compilationOutput, 1))
+ }
+
+ //we're going to spin up numCompilers compilers - they're going to run concurrently and will consume this channel
+ //we prefill the channel then close it, this ensures we compile things in the correct order
+ workPool := make(chan compilationInput, len(runners))
+ for i, runner := range runners {
+ workPool <- compilationInput{runner, orderedCompilationOutputs[i]}
+ }
+ close(workPool)
+
+ //pick a reasonable numCompilers
+ if numCompilers == 0 {
+ numCompilers = runtime.NumCPU()
+ }
+
+ //a WaitGroup to help us wait for all compilers to shut down
+ wg := &sync.WaitGroup{}
+ wg.Add(numCompilers)
+
+ //spin up the concurrent compilers
+ for i := 0; i < numCompilers; i++ {
+ go func() {
+ defer wg.Done()
+ for input := range workPool {
+ if r.interruptHandler.WasInterrupted() {
+ return
+ }
+
+ if willCompile != nil {
+ willCompile(input.runner.Suite)
+ }
+
+ //We retry because Go sometimes steps on itself when multiple compiles happen in parallel. This is ugly, but should help resolve flakiness...
+ var err error
+ retries := 0
+ for retries <= 5 {
+ if r.interruptHandler.WasInterrupted() {
+ return
+ }
+ if err = input.runner.Compile(); err == nil {
+ break
+ }
+ retries++
+ }
+
+ input.result <- compilationOutput{input.runner, err}
+ }
+ }()
+ }
+
+ //read from the compilation output channels *in order* and send them to the caller
+ //close the compilationOutputs channel to tell the caller we're done
+ go func() {
+ defer close(compilationOutputs)
+ for _, orderedCompilationOutput := range orderedCompilationOutputs {
+ select {
+ case compilationOutput := <-orderedCompilationOutput:
+ compilationOutputs <- compilationOutput
+ case <-r.interruptHandler.C:
+ //interrupt detected, wait for the compilers to shut down then bail
+ //this ensure we clean up after ourselves as we don't leave any compilation processes running
+ wg.Wait()
+ return
+ }
+ }
+ }()
+
+ return compilationOutputs
+}
+
+func (r *SuiteRunner) RunSuites(runners []*testrunner.TestRunner, numCompilers int, keepGoing bool, willCompile func(suite testsuite.TestSuite)) (testrunner.RunResult, int) {
+ runResult := testrunner.PassingRunResult()
+
+ compilationOutputs := r.compileInParallel(runners, numCompilers, willCompile)
+
+ numSuitesThatRan := 0
+ suitesThatFailed := []testsuite.TestSuite{}
+ for compilationOutput := range compilationOutputs {
+ if compilationOutput.err != nil {
+ fmt.Print(compilationOutput.err.Error())
+ }
+ numSuitesThatRan++
+ suiteRunResult := testrunner.FailingRunResult()
+ if compilationOutput.err == nil {
+ suiteRunResult = compilationOutput.runner.Run()
+ }
+ r.notifier.SendSuiteCompletionNotification(compilationOutput.runner.Suite, suiteRunResult.Passed)
+ r.notifier.RunCommand(compilationOutput.runner.Suite, suiteRunResult.Passed)
+ runResult = runResult.Merge(suiteRunResult)
+ if !suiteRunResult.Passed {
+ suitesThatFailed = append(suitesThatFailed, compilationOutput.runner.Suite)
+ if !keepGoing {
+ break
+ }
+ }
+ if numSuitesThatRan < len(runners) && !config.DefaultReporterConfig.Succinct {
+ fmt.Println("")
+ }
+ }
+
+ if keepGoing && !runResult.Passed {
+ r.listFailedSuites(suitesThatFailed)
+ }
+
+ return runResult, numSuitesThatRan
+}
+
+func (r *SuiteRunner) listFailedSuites(suitesThatFailed []testsuite.TestSuite) {
+ fmt.Println("")
+ fmt.Println("There were failures detected in the following suites:")
+
+ maxPackageNameLength := 0
+ for _, suite := range suitesThatFailed {
+ if len(suite.PackageName) > maxPackageNameLength {
+ maxPackageNameLength = len(suite.PackageName)
+ }
+ }
+
+ packageNameFormatter := fmt.Sprintf("%%%ds", maxPackageNameLength)
+
+ for _, suite := range suitesThatFailed {
+ if config.DefaultReporterConfig.NoColor {
+ fmt.Printf("\t"+packageNameFormatter+" %s\n", suite.PackageName, suite.Path)
+ } else {
+ fmt.Fprintf(colorable.NewColorableStdout(), "\t%s"+packageNameFormatter+"%s %s%s%s\n", redColor, suite.PackageName, defaultStyle, lightGrayColor, suite.Path, defaultStyle)
+ }
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args.go
new file mode 100644
index 000000000..3b1a238c2
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args.go
@@ -0,0 +1,7 @@
+// +build go1.10
+
+package testrunner
+
+var (
+ buildArgs = []string{"test", "-c"}
+)
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args_old.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args_old.go
new file mode 100644
index 000000000..14d70dbcc
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/build_args_old.go
@@ -0,0 +1,7 @@
+// +build !go1.10
+
+package testrunner
+
+var (
+ buildArgs = []string{"test", "-c", "-i"}
+)
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
new file mode 100644
index 000000000..a73a6e379
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
@@ -0,0 +1,52 @@
+package testrunner
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+ "sync"
+)
+
+type logWriter struct {
+ buffer *bytes.Buffer
+ lock *sync.Mutex
+ log *log.Logger
+}
+
+func newLogWriter(target io.Writer, node int) *logWriter {
+ return &logWriter{
+ buffer: &bytes.Buffer{},
+ lock: &sync.Mutex{},
+ log: log.New(target, fmt.Sprintf("[%d] ", node), 0),
+ }
+}
+
+func (w *logWriter) Write(data []byte) (n int, err error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.buffer.Write(data)
+ contents := w.buffer.String()
+
+ lines := strings.Split(contents, "\n")
+ for _, line := range lines[0 : len(lines)-1] {
+ w.log.Println(line)
+ }
+
+ w.buffer.Reset()
+ w.buffer.Write([]byte(lines[len(lines)-1]))
+ return len(data), nil
+}
+
+func (w *logWriter) Close() error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ if w.buffer.Len() > 0 {
+ w.log.Println(w.buffer.String())
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
new file mode 100644
index 000000000..5d472acb8
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
@@ -0,0 +1,27 @@
+package testrunner
+
+type RunResult struct {
+ Passed bool
+ HasProgrammaticFocus bool
+}
+
+func PassingRunResult() RunResult {
+ return RunResult{
+ Passed: true,
+ HasProgrammaticFocus: false,
+ }
+}
+
+func FailingRunResult() RunResult {
+ return RunResult{
+ Passed: false,
+ HasProgrammaticFocus: false,
+ }
+}
+
+func (r RunResult) Merge(o RunResult) RunResult {
+ return RunResult{
+ Passed: r.Passed && o.Passed,
+ HasProgrammaticFocus: r.HasProgrammaticFocus || o.HasProgrammaticFocus,
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
new file mode 100644
index 000000000..66c0f06f6
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
@@ -0,0 +1,554 @@
+package testrunner
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+ "github.com/onsi/ginkgo/internal/remote"
+ "github.com/onsi/ginkgo/reporters/stenographer"
+ colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
+ "github.com/onsi/ginkgo/types"
+)
+
+type TestRunner struct {
+ Suite testsuite.TestSuite
+
+ compiled bool
+ compilationTargetPath string
+
+ numCPU int
+ parallelStream bool
+ timeout time.Duration
+ goOpts map[string]interface{}
+ additionalArgs []string
+ stderr *bytes.Buffer
+
+ CoverageFile string
+}
+
+func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, timeout time.Duration, goOpts map[string]interface{}, additionalArgs []string) *TestRunner {
+ runner := &TestRunner{
+ Suite: suite,
+ numCPU: numCPU,
+ parallelStream: parallelStream,
+ goOpts: goOpts,
+ additionalArgs: additionalArgs,
+ timeout: timeout,
+ stderr: new(bytes.Buffer),
+ }
+
+ if !suite.Precompiled {
+ runner.compilationTargetPath, _ = filepath.Abs(filepath.Join(suite.Path, suite.PackageName+".test"))
+ }
+
+ return runner
+}
+
+func (t *TestRunner) Compile() error {
+ return t.CompileTo(t.compilationTargetPath)
+}
+
+func (t *TestRunner) BuildArgs(path string) []string {
+ args := make([]string, len(buildArgs), len(buildArgs)+3)
+ copy(args, buildArgs)
+ args = append(args, "-o", path, t.Suite.Path)
+
+ if t.getCoverMode() != "" {
+ args = append(args, "-cover", fmt.Sprintf("-covermode=%s", t.getCoverMode()))
+ } else {
+ if t.shouldCover() || t.getCoverPackage() != "" {
+ args = append(args, "-cover", "-covermode=atomic")
+ }
+ }
+
+ boolOpts := []string{
+ "a",
+ "n",
+ "msan",
+ "race",
+ "x",
+ "work",
+ "linkshared",
+ }
+
+ for _, opt := range boolOpts {
+ if s, found := t.goOpts[opt].(*bool); found && *s {
+ args = append(args, fmt.Sprintf("-%s", opt))
+ }
+ }
+
+ intOpts := []string{
+ "memprofilerate",
+ "blockprofilerate",
+ }
+
+ for _, opt := range intOpts {
+ if s, found := t.goOpts[opt].(*int); found {
+ args = append(args, fmt.Sprintf("-%s=%d", opt, *s))
+ }
+ }
+
+ stringOpts := []string{
+ "asmflags",
+ "buildmode",
+ "compiler",
+ "gccgoflags",
+ "installsuffix",
+ "ldflags",
+ "pkgdir",
+ "toolexec",
+ "coverprofile",
+ "cpuprofile",
+ "memprofile",
+ "outputdir",
+ "coverpkg",
+ "tags",
+ "gcflags",
+ "vet",
+ "mod",
+ }
+
+ for _, opt := range stringOpts {
+ if s, found := t.goOpts[opt].(*string); found && *s != "" {
+ args = append(args, fmt.Sprintf("-%s=%s", opt, *s))
+ }
+ }
+ return args
+}
+
+func (t *TestRunner) CompileTo(path string) error {
+ if t.compiled {
+ return nil
+ }
+
+ if t.Suite.Precompiled {
+ return nil
+ }
+
+ args := t.BuildArgs(path)
+ cmd := exec.Command("go", args...)
+
+ output, err := cmd.CombinedOutput()
+
+ if err != nil {
+ if len(output) > 0 {
+ return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, output)
+ }
+ return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
+ }
+
+ if len(output) > 0 {
+ fmt.Println(string(output))
+ }
+
+ if !fileExists(path) {
+ compiledFile := t.Suite.PackageName + ".test"
+ if fileExists(compiledFile) {
+ // seems like we are on an old go version that does not support the -o flag on go test
+ // move the compiled test file to the desired location by hand
+ err = os.Rename(compiledFile, path)
+ if err != nil {
+ // We cannot move the file, perhaps because the source and destination
+ // are on different partitions. We can copy the file, however.
+ err = copyFile(compiledFile, path)
+ if err != nil {
+ return fmt.Errorf("Failed to copy compiled file: %s", err)
+ }
+ }
+ } else {
+ return fmt.Errorf("Failed to compile %s: output file %q could not be found", t.Suite.PackageName, path)
+ }
+ }
+
+ t.compiled = true
+
+ return nil
+}
+
+func fileExists(path string) bool {
+ _, err := os.Stat(path)
+ return err == nil || !os.IsNotExist(err)
+}
+
+// copyFile copies the contents of the file named src to the file named
+// by dst. The file will be created if it does not already exist. If the
+// destination file exists, all it's contents will be replaced by the contents
+// of the source file.
+func copyFile(src, dst string) error {
+ srcInfo, err := os.Stat(src)
+ if err != nil {
+ return err
+ }
+ mode := srcInfo.Mode()
+
+ in, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+
+ defer in.Close()
+
+ out, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+
+ defer func() {
+ closeErr := out.Close()
+ if err == nil {
+ err = closeErr
+ }
+ }()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return err
+ }
+
+ err = out.Sync()
+ if err != nil {
+ return err
+ }
+
+ return out.Chmod(mode)
+}
+
+func (t *TestRunner) Run() RunResult {
+ if t.Suite.IsGinkgo {
+ if t.numCPU > 1 {
+ if t.parallelStream {
+ return t.runAndStreamParallelGinkgoSuite()
+ } else {
+ return t.runParallelGinkgoSuite()
+ }
+ } else {
+ return t.runSerialGinkgoSuite()
+ }
+ } else {
+ return t.runGoTestSuite()
+ }
+}
+
+func (t *TestRunner) CleanUp() {
+ if t.Suite.Precompiled {
+ return
+ }
+ os.Remove(t.compilationTargetPath)
+}
+
+func (t *TestRunner) runSerialGinkgoSuite() RunResult {
+ ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
+ return t.run(t.cmd(ginkgoArgs, os.Stdout, 1), nil)
+}
+
+func (t *TestRunner) runGoTestSuite() RunResult {
+ return t.run(t.cmd([]string{"-test.v"}, os.Stdout, 1), nil)
+}
+
+func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
+ completions := make(chan RunResult)
+ writers := make([]*logWriter, t.numCPU)
+
+ server, err := remote.NewServer(t.numCPU)
+ if err != nil {
+ panic("Failed to start parallel spec server")
+ }
+
+ server.Start()
+ defer server.Close()
+
+ for cpu := 0; cpu < t.numCPU; cpu++ {
+ config.GinkgoConfig.ParallelNode = cpu + 1
+ config.GinkgoConfig.ParallelTotal = t.numCPU
+ config.GinkgoConfig.SyncHost = server.Address()
+
+ ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
+
+ writers[cpu] = newLogWriter(os.Stdout, cpu+1)
+
+ cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)
+
+ server.RegisterAlive(cpu+1, func() bool {
+ if cmd.ProcessState == nil {
+ return true
+ }
+ return !cmd.ProcessState.Exited()
+ })
+
+ go t.run(cmd, completions)
+ }
+
+ res := PassingRunResult()
+
+ for cpu := 0; cpu < t.numCPU; cpu++ {
+ res = res.Merge(<-completions)
+ }
+
+ for _, writer := range writers {
+ writer.Close()
+ }
+
+ os.Stdout.Sync()
+
+ if t.shouldCombineCoverprofiles() {
+ t.combineCoverprofiles()
+ }
+
+ return res
+}
+
+func (t *TestRunner) runParallelGinkgoSuite() RunResult {
+ result := make(chan bool)
+ completions := make(chan RunResult)
+ writers := make([]*logWriter, t.numCPU)
+ reports := make([]*bytes.Buffer, t.numCPU)
+
+ stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1, colorable.NewColorableStdout())
+ aggregator := remote.NewAggregator(t.numCPU, result, config.DefaultReporterConfig, stenographer)
+
+ server, err := remote.NewServer(t.numCPU)
+ if err != nil {
+ panic("Failed to start parallel spec server")
+ }
+ server.RegisterReporters(aggregator)
+ server.Start()
+ defer server.Close()
+
+ for cpu := 0; cpu < t.numCPU; cpu++ {
+ config.GinkgoConfig.ParallelNode = cpu + 1
+ config.GinkgoConfig.ParallelTotal = t.numCPU
+ config.GinkgoConfig.SyncHost = server.Address()
+ config.GinkgoConfig.StreamHost = server.Address()
+
+ ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
+
+ reports[cpu] = &bytes.Buffer{}
+ writers[cpu] = newLogWriter(reports[cpu], cpu+1)
+
+ cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)
+
+ server.RegisterAlive(cpu+1, func() bool {
+ if cmd.ProcessState == nil {
+ return true
+ }
+ return !cmd.ProcessState.Exited()
+ })
+
+ go t.run(cmd, completions)
+ }
+
+ res := PassingRunResult()
+
+ for cpu := 0; cpu < t.numCPU; cpu++ {
+ res = res.Merge(<-completions)
+ }
+
+ //all test processes are done, at this point
+ //we should be able to wait for the aggregator to tell us that it's done
+
+ select {
+ case <-result:
+ fmt.Println("")
+ case <-time.After(time.Second):
+ //the aggregator never got back to us! something must have gone wrong
+ fmt.Println(`
+ -------------------------------------------------------------------
+ | |
+ | Ginkgo timed out waiting for all parallel nodes to report back! |
+ | |
+ -------------------------------------------------------------------`)
+ fmt.Println("\n", t.Suite.PackageName, "timed out. path:", t.Suite.Path)
+ os.Stdout.Sync()
+
+ for _, writer := range writers {
+ writer.Close()
+ }
+
+ for _, report := range reports {
+ fmt.Print(report.String())
+ }
+
+ os.Stdout.Sync()
+ }
+
+ if t.shouldCombineCoverprofiles() {
+ t.combineCoverprofiles()
+ }
+
+ return res
+}
+
+const CoverProfileSuffix = ".coverprofile"
+
+func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
+ args := []string{"--test.timeout=" + t.timeout.String()}
+
+ coverProfile := t.getCoverProfile()
+
+ if t.shouldCombineCoverprofiles() {
+
+ testCoverProfile := "--test.coverprofile="
+
+ coverageFile := ""
+ // Set default name for coverage results
+ if coverProfile == "" {
+ coverageFile = t.Suite.PackageName + CoverProfileSuffix
+ } else {
+ coverageFile = coverProfile
+ }
+
+ testCoverProfile += coverageFile
+
+ t.CoverageFile = filepath.Join(t.Suite.Path, coverageFile)
+
+ if t.numCPU > 1 {
+ testCoverProfile = fmt.Sprintf("%s.%d", testCoverProfile, node)
+ }
+ args = append(args, testCoverProfile)
+ }
+
+ args = append(args, ginkgoArgs...)
+ args = append(args, t.additionalArgs...)
+
+ path := t.compilationTargetPath
+ if t.Suite.Precompiled {
+ path, _ = filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
+ }
+
+ cmd := exec.Command(path, args...)
+
+ cmd.Dir = t.Suite.Path
+ cmd.Stderr = io.MultiWriter(stream, t.stderr)
+ cmd.Stdout = stream
+
+ return cmd
+}
+
+func (t *TestRunner) shouldCover() bool {
+ return *t.goOpts["cover"].(*bool)
+}
+
+func (t *TestRunner) shouldRequireSuite() bool {
+ return *t.goOpts["requireSuite"].(*bool)
+}
+
+func (t *TestRunner) getCoverProfile() string {
+ return *t.goOpts["coverprofile"].(*string)
+}
+
+func (t *TestRunner) getCoverPackage() string {
+ return *t.goOpts["coverpkg"].(*string)
+}
+
+func (t *TestRunner) getCoverMode() string {
+ return *t.goOpts["covermode"].(*string)
+}
+
+func (t *TestRunner) shouldCombineCoverprofiles() bool {
+ return t.shouldCover() || t.getCoverPackage() != "" || t.getCoverMode() != ""
+}
+
+func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
+ var res RunResult
+
+ defer func() {
+ if completions != nil {
+ completions <- res
+ }
+ }()
+
+ err := cmd.Start()
+ if err != nil {
+ fmt.Printf("Failed to run test suite!\n\t%s", err.Error())
+ return res
+ }
+
+ cmd.Wait()
+
+ exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
+ res.Passed = (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
+ res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
+
+ if strings.Contains(t.stderr.String(), "warning: no tests to run") {
+ if t.shouldRequireSuite() {
+ res.Passed = false
+ }
+ fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`)
+ }
+
+ return res
+}
+
+func (t *TestRunner) combineCoverprofiles() {
+ profiles := []string{}
+
+ coverProfile := t.getCoverProfile()
+
+ for cpu := 1; cpu <= t.numCPU; cpu++ {
+ var coverFile string
+ if coverProfile == "" {
+ coverFile = fmt.Sprintf("%s%s.%d", t.Suite.PackageName, CoverProfileSuffix, cpu)
+ } else {
+ coverFile = fmt.Sprintf("%s.%d", coverProfile, cpu)
+ }
+
+ coverFile = filepath.Join(t.Suite.Path, coverFile)
+ coverProfile, err := ioutil.ReadFile(coverFile)
+ os.Remove(coverFile)
+
+ if err == nil {
+ profiles = append(profiles, string(coverProfile))
+ }
+ }
+
+ if len(profiles) != t.numCPU {
+ return
+ }
+
+ lines := map[string]int{}
+ lineOrder := []string{}
+ for i, coverProfile := range profiles {
+ for _, line := range strings.Split(coverProfile, "\n")[1:] {
+ if len(line) == 0 {
+ continue
+ }
+ components := strings.Split(line, " ")
+ count, _ := strconv.Atoi(components[len(components)-1])
+ prefix := strings.Join(components[0:len(components)-1], " ")
+ lines[prefix] += count
+ if i == 0 {
+ lineOrder = append(lineOrder, prefix)
+ }
+ }
+ }
+
+ output := []string{"mode: atomic"}
+ for _, line := range lineOrder {
+ output = append(output, fmt.Sprintf("%s %d", line, lines[line]))
+ }
+ finalOutput := strings.Join(output, "\n")
+
+ finalFilename := ""
+
+ if coverProfile != "" {
+ finalFilename = coverProfile
+ } else {
+ finalFilename = fmt.Sprintf("%s%s", t.Suite.PackageName, CoverProfileSuffix)
+ }
+
+ coverageFilepath := filepath.Join(t.Suite.Path, finalFilename)
+ ioutil.WriteFile(coverageFilepath, []byte(finalOutput), 0666)
+
+ t.CoverageFile = coverageFilepath
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
new file mode 100644
index 000000000..9de8c2bb4
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
@@ -0,0 +1,115 @@
+package testsuite
+
+import (
+ "errors"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+type TestSuite struct {
+ Path string
+ PackageName string
+ IsGinkgo bool
+ Precompiled bool
+}
+
+func PrecompiledTestSuite(path string) (TestSuite, error) {
+ info, err := os.Stat(path)
+ if err != nil {
+ return TestSuite{}, err
+ }
+
+ if info.IsDir() {
+ return TestSuite{}, errors.New("this is a directory, not a file")
+ }
+
+ if filepath.Ext(path) != ".test" {
+ return TestSuite{}, errors.New("this is not a .test binary")
+ }
+
+ if info.Mode()&0111 == 0 {
+ return TestSuite{}, errors.New("this is not executable")
+ }
+
+ dir := relPath(filepath.Dir(path))
+ packageName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
+
+ return TestSuite{
+ Path: dir,
+ PackageName: packageName,
+ IsGinkgo: true,
+ Precompiled: true,
+ }, nil
+}
+
+func SuitesInDir(dir string, recurse bool) []TestSuite {
+ suites := []TestSuite{}
+
+ if vendorExperimentCheck(dir) {
+ return suites
+ }
+
+ files, _ := ioutil.ReadDir(dir)
+ re := regexp.MustCompile(`^[^._].*_test\.go$`)
+ for _, file := range files {
+ if !file.IsDir() && re.Match([]byte(file.Name())) {
+ suites = append(suites, New(dir, files))
+ break
+ }
+ }
+
+ if recurse {
+ re = regexp.MustCompile(`^[._]`)
+ for _, file := range files {
+ if file.IsDir() && !re.Match([]byte(file.Name())) {
+ suites = append(suites, SuitesInDir(dir+"/"+file.Name(), recurse)...)
+ }
+ }
+ }
+
+ return suites
+}
+
+func relPath(dir string) string {
+ dir, _ = filepath.Abs(dir)
+ cwd, _ := os.Getwd()
+ dir, _ = filepath.Rel(cwd, filepath.Clean(dir))
+
+ if string(dir[0]) != "." {
+ dir = "." + string(filepath.Separator) + dir
+ }
+
+ return dir
+}
+
+func New(dir string, files []os.FileInfo) TestSuite {
+ return TestSuite{
+ Path: relPath(dir),
+ PackageName: packageNameForSuite(dir),
+ IsGinkgo: filesHaveGinkgoSuite(dir, files),
+ }
+}
+
+func packageNameForSuite(dir string) string {
+ path, _ := filepath.Abs(dir)
+ return filepath.Base(path)
+}
+
+func filesHaveGinkgoSuite(dir string, files []os.FileInfo) bool {
+ reTestFile := regexp.MustCompile(`_test\.go$`)
+ reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"`)
+
+ for _, file := range files {
+ if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
+ contents, _ := ioutil.ReadFile(dir + "/" + file.Name())
+ if reGinkgo.Match(contents) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go
new file mode 100644
index 000000000..75f827a12
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go15.go
@@ -0,0 +1,16 @@
+// +build !go1.6
+
+package testsuite
+
+import (
+ "os"
+ "path"
+)
+
+// "This change will only be enabled if the go command is run with
+// GO15VENDOREXPERIMENT=1 in its environment."
+// c.f. the vendor-experiment proposal https://goo.gl/2ucMeC
+func vendorExperimentCheck(dir string) bool {
+ vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT")
+ return vendorExperiment == "1" && path.Base(dir) == "vendor"
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go
new file mode 100644
index 000000000..596e5e5c1
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/testsuite/vendor_check_go16.go
@@ -0,0 +1,15 @@
+// +build go1.6
+
+package testsuite
+
+import (
+ "os"
+ "path"
+)
+
+// in 1.6 the vendor directory became the default go behaviour, so now
+// check if its disabled.
+func vendorExperimentCheck(dir string) bool {
+ vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT")
+ return vendorExperiment != "0" && path.Base(dir) == "vendor"
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
new file mode 100644
index 000000000..d9dfb6e44
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
@@ -0,0 +1,180 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+)
+
+func BuildUnfocusCommand() *Command {
+ return &Command{
+ Name: "unfocus",
+ AltName: "blur",
+ FlagSet: flag.NewFlagSet("unfocus", flag.ExitOnError),
+ UsageCommand: "ginkgo unfocus (or ginkgo blur)",
+ Usage: []string{
+ "Recursively unfocuses any focused tests under the current directory",
+ },
+ Command: unfocusSpecs,
+ }
+}
+
+func unfocusSpecs([]string, []string) {
+ fmt.Println("Scanning for focus...")
+
+ goFiles := make(chan string)
+ go func() {
+ unfocusDir(goFiles, ".")
+ close(goFiles)
+ }()
+
+ const workers = 10
+ wg := sync.WaitGroup{}
+ wg.Add(workers)
+
+ for i := 0; i < workers; i++ {
+ go func() {
+ for path := range goFiles {
+ unfocusFile(path)
+ }
+ wg.Done()
+ }()
+ }
+
+ wg.Wait()
+}
+
+func unfocusDir(goFiles chan string, path string) {
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+
+ for _, f := range files {
+ switch {
+ case f.IsDir() && shouldProcessDir(f.Name()):
+ unfocusDir(goFiles, filepath.Join(path, f.Name()))
+ case !f.IsDir() && shouldProcessFile(f.Name()):
+ goFiles <- filepath.Join(path, f.Name())
+ }
+ }
+}
+
+func shouldProcessDir(basename string) bool {
+ return basename != "vendor" && !strings.HasPrefix(basename, ".")
+}
+
+func shouldProcessFile(basename string) bool {
+ return strings.HasSuffix(basename, ".go")
+}
+
+func unfocusFile(path string) {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ fmt.Printf("error reading file '%s': %s\n", path, err.Error())
+ return
+ }
+
+ ast, err := parser.ParseFile(token.NewFileSet(), path, bytes.NewReader(data), 0)
+ if err != nil {
+ fmt.Printf("error parsing file '%s': %s\n", path, err.Error())
+ return
+ }
+
+ eliminations := scanForFocus(ast)
+ if len(eliminations) == 0 {
+ return
+ }
+
+ fmt.Printf("...updating %s\n", path)
+ backup, err := writeBackup(path, data)
+ if err != nil {
+ fmt.Printf("error creating backup file: %s\n", err.Error())
+ return
+ }
+
+ if err := updateFile(path, data, eliminations); err != nil {
+ fmt.Printf("error writing file '%s': %s\n", path, err.Error())
+ return
+ }
+
+ os.Remove(backup)
+}
+
+func writeBackup(path string, data []byte) (string, error) {
+ t, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path))
+
+ if err != nil {
+ return "", fmt.Errorf("error creating temporary file: %w", err)
+ }
+ defer t.Close()
+
+ if _, err := io.Copy(t, bytes.NewReader(data)); err != nil {
+ return "", fmt.Errorf("error writing to temporary file: %w", err)
+ }
+
+ return t.Name(), nil
+}
+
+func updateFile(path string, data []byte, eliminations []int64) error {
+ to, err := os.Create(path)
+ if err != nil {
+ return fmt.Errorf("error opening file for writing '%s': %w\n", path, err)
+ }
+ defer to.Close()
+
+ from := bytes.NewReader(data)
+ var cursor int64
+ for _, byteToEliminate := range eliminations {
+ if _, err := io.CopyN(to, from, byteToEliminate-cursor); err != nil {
+ return fmt.Errorf("error copying data: %w", err)
+ }
+
+ cursor = byteToEliminate + 1
+
+ if _, err := from.Seek(1, io.SeekCurrent); err != nil {
+ return fmt.Errorf("error seeking to position in buffer: %w", err)
+ }
+ }
+
+ if _, err := io.Copy(to, from); err != nil {
+ return fmt.Errorf("error copying end data: %w", err)
+ }
+
+ return nil
+}
+
+func scanForFocus(file *ast.File) (eliminations []int64) {
+ ast.Inspect(file, func(n ast.Node) bool {
+ if c, ok := n.(*ast.CallExpr); ok {
+ if i, ok := c.Fun.(*ast.Ident); ok {
+ if isFocus(i.Name) {
+ eliminations = append(eliminations, int64(i.Pos()-file.Pos()))
+ }
+ }
+ }
+
+ return true
+ })
+
+ return eliminations
+}
+
+func isFocus(name string) bool {
+ switch name {
+ case "FDescribe", "FContext", "FIt", "FMeasure", "FDescribeTable", "FEntry", "FSpecify", "FWhen":
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
new file mode 100644
index 000000000..f586908e8
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/onsi/ginkgo/config"
+)
+
+func BuildVersionCommand() *Command {
+ return &Command{
+ Name: "version",
+ FlagSet: flag.NewFlagSet("version", flag.ExitOnError),
+ UsageCommand: "ginkgo version",
+ Usage: []string{
+ "Print Ginkgo's version",
+ },
+ Command: printVersion,
+ }
+}
+
+func printVersion([]string, []string) {
+ fmt.Printf("Ginkgo Version %s\n", config.VERSION)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
new file mode 100644
index 000000000..6c485c5b1
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
@@ -0,0 +1,22 @@
+package watch
+
+import "sort"
+
+type Delta struct {
+ ModifiedPackages []string
+
+ NewSuites []*Suite
+ RemovedSuites []*Suite
+ modifiedSuites []*Suite
+}
+
+type DescendingByDelta []*Suite
+
+func (a DescendingByDelta) Len() int { return len(a) }
+func (a DescendingByDelta) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a DescendingByDelta) Less(i, j int) bool { return a[i].Delta() > a[j].Delta() }
+
+func (d Delta) ModifiedSuites() []*Suite {
+ sort.Sort(DescendingByDelta(d.modifiedSuites))
+ return d.modifiedSuites
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
new file mode 100644
index 000000000..a628303d7
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
@@ -0,0 +1,75 @@
+package watch
+
+import (
+ "fmt"
+
+ "regexp"
+
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+)
+
+type SuiteErrors map[testsuite.TestSuite]error
+
+type DeltaTracker struct {
+ maxDepth int
+ watchRegExp *regexp.Regexp
+ suites map[string]*Suite
+ packageHashes *PackageHashes
+}
+
+func NewDeltaTracker(maxDepth int, watchRegExp *regexp.Regexp) *DeltaTracker {
+ return &DeltaTracker{
+ maxDepth: maxDepth,
+ watchRegExp: watchRegExp,
+ packageHashes: NewPackageHashes(watchRegExp),
+ suites: map[string]*Suite{},
+ }
+}
+
+func (d *DeltaTracker) Delta(suites []testsuite.TestSuite) (delta Delta, errors SuiteErrors) {
+ errors = SuiteErrors{}
+ delta.ModifiedPackages = d.packageHashes.CheckForChanges()
+
+ providedSuitePaths := map[string]bool{}
+ for _, suite := range suites {
+ providedSuitePaths[suite.Path] = true
+ }
+
+ d.packageHashes.StartTrackingUsage()
+
+ for _, suite := range d.suites {
+ if providedSuitePaths[suite.Suite.Path] {
+ if suite.Delta() > 0 {
+ delta.modifiedSuites = append(delta.modifiedSuites, suite)
+ }
+ } else {
+ delta.RemovedSuites = append(delta.RemovedSuites, suite)
+ }
+ }
+
+ d.packageHashes.StopTrackingUsageAndPrune()
+
+ for _, suite := range suites {
+ _, ok := d.suites[suite.Path]
+ if !ok {
+ s, err := NewSuite(suite, d.maxDepth, d.packageHashes)
+ if err != nil {
+ errors[suite] = err
+ continue
+ }
+ d.suites[suite.Path] = s
+ delta.NewSuites = append(delta.NewSuites, s)
+ }
+ }
+
+ return delta, errors
+}
+
+func (d *DeltaTracker) WillRun(suite testsuite.TestSuite) error {
+ s, ok := d.suites[suite.Path]
+ if !ok {
+ return fmt.Errorf("unknown suite %s", suite.Path)
+ }
+
+ return s.MarkAsRunAndRecomputedDependencies(d.maxDepth)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go
new file mode 100644
index 000000000..f5ddff30f
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go
@@ -0,0 +1,92 @@
+package watch
+
+import (
+ "go/build"
+ "regexp"
+)
+
+var ginkgoAndGomegaFilter = regexp.MustCompile(`github\.com/onsi/ginkgo|github\.com/onsi/gomega`)
+var ginkgoIntegrationTestFilter = regexp.MustCompile(`github\.com/onsi/ginkgo/integration`) //allow us to integration test this thing
+
+type Dependencies struct {
+ deps map[string]int
+}
+
+func NewDependencies(path string, maxDepth int) (Dependencies, error) {
+ d := Dependencies{
+ deps: map[string]int{},
+ }
+
+ if maxDepth == 0 {
+ return d, nil
+ }
+
+ err := d.seedWithDepsForPackageAtPath(path)
+ if err != nil {
+ return d, err
+ }
+
+ for depth := 1; depth < maxDepth; depth++ {
+ n := len(d.deps)
+ d.addDepsForDepth(depth)
+ if n == len(d.deps) {
+ break
+ }
+ }
+
+ return d, nil
+}
+
+func (d Dependencies) Dependencies() map[string]int {
+ return d.deps
+}
+
+func (d Dependencies) seedWithDepsForPackageAtPath(path string) error {
+ pkg, err := build.ImportDir(path, 0)
+ if err != nil {
+ return err
+ }
+
+ d.resolveAndAdd(pkg.Imports, 1)
+ d.resolveAndAdd(pkg.TestImports, 1)
+ d.resolveAndAdd(pkg.XTestImports, 1)
+
+ delete(d.deps, pkg.Dir)
+ return nil
+}
+
+func (d Dependencies) addDepsForDepth(depth int) {
+ for dep, depDepth := range d.deps {
+ if depDepth == depth {
+ d.addDepsForDep(dep, depth+1)
+ }
+ }
+}
+
+func (d Dependencies) addDepsForDep(dep string, depth int) {
+ pkg, err := build.ImportDir(dep, 0)
+ if err != nil {
+ println(err.Error())
+ return
+ }
+ d.resolveAndAdd(pkg.Imports, depth)
+}
+
+func (d Dependencies) resolveAndAdd(deps []string, depth int) {
+ for _, dep := range deps {
+ pkg, err := build.Import(dep, ".", 0)
+ if err != nil {
+ continue
+ }
+ if !pkg.Goroot && (!ginkgoAndGomegaFilter.Match([]byte(pkg.Dir)) || ginkgoIntegrationTestFilter.Match([]byte(pkg.Dir))) {
+ d.addDepIfNotPresent(pkg.Dir, depth)
+ }
+ }
+}
+
+func (d Dependencies) addDepIfNotPresent(dep string, depth int) {
+ _, ok := d.deps[dep]
+ if !ok {
+ d.deps[dep] = depth
+ }
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go
new file mode 100644
index 000000000..67e2c1c32
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go
@@ -0,0 +1,104 @@
+package watch
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "time"
+)
+
+var goTestRegExp = regexp.MustCompile(`_test\.go$`)
+
+type PackageHash struct {
+ CodeModifiedTime time.Time
+ TestModifiedTime time.Time
+ Deleted bool
+
+ path string
+ codeHash string
+ testHash string
+ watchRegExp *regexp.Regexp
+}
+
+func NewPackageHash(path string, watchRegExp *regexp.Regexp) *PackageHash {
+ p := &PackageHash{
+ path: path,
+ watchRegExp: watchRegExp,
+ }
+
+ p.codeHash, _, p.testHash, _, p.Deleted = p.computeHashes()
+
+ return p
+}
+
+func (p *PackageHash) CheckForChanges() bool {
+ codeHash, codeModifiedTime, testHash, testModifiedTime, deleted := p.computeHashes()
+
+ if deleted {
+ if !p.Deleted {
+ t := time.Now()
+ p.CodeModifiedTime = t
+ p.TestModifiedTime = t
+ }
+ p.Deleted = true
+ return true
+ }
+
+ modified := false
+ p.Deleted = false
+
+ if p.codeHash != codeHash {
+ p.CodeModifiedTime = codeModifiedTime
+ modified = true
+ }
+ if p.testHash != testHash {
+ p.TestModifiedTime = testModifiedTime
+ modified = true
+ }
+
+ p.codeHash = codeHash
+ p.testHash = testHash
+ return modified
+}
+
+func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Time, testHash string, testModifiedTime time.Time, deleted bool) {
+ infos, err := ioutil.ReadDir(p.path)
+
+ if err != nil {
+ deleted = true
+ return
+ }
+
+ for _, info := range infos {
+ if info.IsDir() {
+ continue
+ }
+
+ if goTestRegExp.Match([]byte(info.Name())) {
+ testHash += p.hashForFileInfo(info)
+ if info.ModTime().After(testModifiedTime) {
+ testModifiedTime = info.ModTime()
+ }
+ continue
+ }
+
+ if p.watchRegExp.Match([]byte(info.Name())) {
+ codeHash += p.hashForFileInfo(info)
+ if info.ModTime().After(codeModifiedTime) {
+ codeModifiedTime = info.ModTime()
+ }
+ }
+ }
+
+ testHash += codeHash
+ if codeModifiedTime.After(testModifiedTime) {
+ testModifiedTime = codeModifiedTime
+ }
+
+ return
+}
+
+func (p *PackageHash) hashForFileInfo(info os.FileInfo) string {
+ return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano())
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go
new file mode 100644
index 000000000..b4892bebf
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go
@@ -0,0 +1,85 @@
+package watch
+
+import (
+ "path/filepath"
+ "regexp"
+ "sync"
+)
+
+type PackageHashes struct {
+ PackageHashes map[string]*PackageHash
+ usedPaths map[string]bool
+ watchRegExp *regexp.Regexp
+ lock *sync.Mutex
+}
+
+func NewPackageHashes(watchRegExp *regexp.Regexp) *PackageHashes {
+ return &PackageHashes{
+ PackageHashes: map[string]*PackageHash{},
+ usedPaths: nil,
+ watchRegExp: watchRegExp,
+ lock: &sync.Mutex{},
+ }
+}
+
+func (p *PackageHashes) CheckForChanges() []string {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ modified := []string{}
+
+ for _, packageHash := range p.PackageHashes {
+ if packageHash.CheckForChanges() {
+ modified = append(modified, packageHash.path)
+ }
+ }
+
+ return modified
+}
+
+func (p *PackageHashes) Add(path string) *PackageHash {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ path, _ = filepath.Abs(path)
+ _, ok := p.PackageHashes[path]
+ if !ok {
+ p.PackageHashes[path] = NewPackageHash(path, p.watchRegExp)
+ }
+
+ if p.usedPaths != nil {
+ p.usedPaths[path] = true
+ }
+ return p.PackageHashes[path]
+}
+
+func (p *PackageHashes) Get(path string) *PackageHash {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ path, _ = filepath.Abs(path)
+ if p.usedPaths != nil {
+ p.usedPaths[path] = true
+ }
+ return p.PackageHashes[path]
+}
+
+func (p *PackageHashes) StartTrackingUsage() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ p.usedPaths = map[string]bool{}
+}
+
+func (p *PackageHashes) StopTrackingUsageAndPrune() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ for path := range p.PackageHashes {
+ if !p.usedPaths[path] {
+ delete(p.PackageHashes, path)
+ }
+ }
+
+ p.usedPaths = nil
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go
new file mode 100644
index 000000000..5deaba7cb
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go
@@ -0,0 +1,87 @@
+package watch
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+)
+
+type Suite struct {
+ Suite testsuite.TestSuite
+ RunTime time.Time
+ Dependencies Dependencies
+
+ sharedPackageHashes *PackageHashes
+}
+
+func NewSuite(suite testsuite.TestSuite, maxDepth int, sharedPackageHashes *PackageHashes) (*Suite, error) {
+ deps, err := NewDependencies(suite.Path, maxDepth)
+ if err != nil {
+ return nil, err
+ }
+
+ sharedPackageHashes.Add(suite.Path)
+ for dep := range deps.Dependencies() {
+ sharedPackageHashes.Add(dep)
+ }
+
+ return &Suite{
+ Suite: suite,
+ Dependencies: deps,
+
+ sharedPackageHashes: sharedPackageHashes,
+ }, nil
+}
+
+func (s *Suite) Delta() float64 {
+ delta := s.delta(s.Suite.Path, true, 0) * 1000
+ for dep, depth := range s.Dependencies.Dependencies() {
+ delta += s.delta(dep, false, depth)
+ }
+ return delta
+}
+
+func (s *Suite) MarkAsRunAndRecomputedDependencies(maxDepth int) error {
+ s.RunTime = time.Now()
+
+ deps, err := NewDependencies(s.Suite.Path, maxDepth)
+ if err != nil {
+ return err
+ }
+
+ s.sharedPackageHashes.Add(s.Suite.Path)
+ for dep := range deps.Dependencies() {
+ s.sharedPackageHashes.Add(dep)
+ }
+
+ s.Dependencies = deps
+
+ return nil
+}
+
+func (s *Suite) Description() string {
+ numDeps := len(s.Dependencies.Dependencies())
+ pluralizer := "ies"
+ if numDeps == 1 {
+ pluralizer = "y"
+ }
+ return fmt.Sprintf("%s [%d dependenc%s]", s.Suite.Path, numDeps, pluralizer)
+}
+
+func (s *Suite) delta(packagePath string, includeTests bool, depth int) float64 {
+ return math.Max(float64(s.dt(packagePath, includeTests)), 0) / float64(depth+1)
+}
+
+func (s *Suite) dt(packagePath string, includeTests bool) time.Duration {
+ packageHash := s.sharedPackageHashes.Get(packagePath)
+ var modifiedTime time.Time
+ if includeTests {
+ modifiedTime = packageHash.TestModifiedTime
+ } else {
+ modifiedTime = packageHash.CodeModifiedTime
+ }
+
+ return modifiedTime.Sub(s.RunTime)
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go
new file mode 100644
index 000000000..a6ef053c8
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go
@@ -0,0 +1,175 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "regexp"
+ "time"
+
+ "github.com/onsi/ginkgo/config"
+ "github.com/onsi/ginkgo/ginkgo/interrupthandler"
+ "github.com/onsi/ginkgo/ginkgo/testrunner"
+ "github.com/onsi/ginkgo/ginkgo/testsuite"
+ "github.com/onsi/ginkgo/ginkgo/watch"
+ colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
+)
+
+func BuildWatchCommand() *Command {
+ commandFlags := NewWatchCommandFlags(flag.NewFlagSet("watch", flag.ExitOnError))
+ interruptHandler := interrupthandler.NewInterruptHandler()
+ notifier := NewNotifier(commandFlags)
+ watcher := &SpecWatcher{
+ commandFlags: commandFlags,
+ notifier: notifier,
+ interruptHandler: interruptHandler,
+ suiteRunner: NewSuiteRunner(notifier, interruptHandler),
+ }
+
+ return &Command{
+ Name: "watch",
+ FlagSet: commandFlags.FlagSet,
+ UsageCommand: "ginkgo watch <FLAGS> <PACKAGES> -- <PASS-THROUGHS>",
+ Usage: []string{
+ "Watches the tests in the passed in <PACKAGES> and runs them when changes occur.",
+ "Any arguments after -- will be passed to the test.",
+ },
+ Command: watcher.WatchSpecs,
+ SuppressFlagDocumentation: true,
+ FlagDocSubstitute: []string{
+ "Accepts all the flags that the ginkgo command accepts except for --keepGoing and --untilItFails",
+ },
+ }
+}
+
+type SpecWatcher struct {
+ commandFlags *RunWatchAndBuildCommandFlags
+ notifier *Notifier
+ interruptHandler *interrupthandler.InterruptHandler
+ suiteRunner *SuiteRunner
+}
+
+func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) {
+ w.commandFlags.computeNodes()
+ w.notifier.VerifyNotificationsAreAvailable()
+
+ w.WatchSuites(args, additionalArgs)
+}
+
+func (w *SpecWatcher) runnersForSuites(suites []testsuite.TestSuite, additionalArgs []string) []*testrunner.TestRunner {
+ runners := []*testrunner.TestRunner{}
+
+ for _, suite := range suites {
+ runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.Timeout, w.commandFlags.GoOpts, additionalArgs))
+ }
+
+ return runners
+}
+
+func (w *SpecWatcher) WatchSuites(args []string, additionalArgs []string) {
+ suites, _ := findSuites(args, w.commandFlags.Recurse, w.commandFlags.SkipPackage, false)
+
+ if len(suites) == 0 {
+ complainAndQuit("Found no test suites")
+ }
+
+ fmt.Printf("Identified %d test %s. Locating dependencies to a depth of %d (this may take a while)...\n", len(suites), pluralizedWord("suite", "suites", len(suites)), w.commandFlags.Depth)
+ deltaTracker := watch.NewDeltaTracker(w.commandFlags.Depth, regexp.MustCompile(w.commandFlags.WatchRegExp))
+ delta, errors := deltaTracker.Delta(suites)
+
+ fmt.Printf("Watching %d %s:\n", len(delta.NewSuites), pluralizedWord("suite", "suites", len(delta.NewSuites)))
+ for _, suite := range delta.NewSuites {
+ fmt.Println(" " + suite.Description())
+ }
+
+ for suite, err := range errors {
+ fmt.Printf("Failed to watch %s: %s\n", suite.PackageName, err)
+ }
+
+ if len(suites) == 1 {
+ runners := w.runnersForSuites(suites, additionalArgs)
+ w.suiteRunner.RunSuites(runners, w.commandFlags.NumCompilers, true, nil)
+ runners[0].CleanUp()
+ }
+
+ ticker := time.NewTicker(time.Second)
+
+ for {
+ select {
+ case <-ticker.C:
+ suites, _ := findSuites(args, w.commandFlags.Recurse, w.commandFlags.SkipPackage, false)
+ delta, _ := deltaTracker.Delta(suites)
+ coloredStream := colorable.NewColorableStdout()
+
+ suitesToRun := []testsuite.TestSuite{}
+
+ if len(delta.NewSuites) > 0 {
+ fmt.Fprintf(coloredStream, greenColor+"Detected %d new %s:\n"+defaultStyle, len(delta.NewSuites), pluralizedWord("suite", "suites", len(delta.NewSuites)))
+ for _, suite := range delta.NewSuites {
+ suitesToRun = append(suitesToRun, suite.Suite)
+ fmt.Fprintln(coloredStream, " "+suite.Description())
+ }
+ }
+
+ modifiedSuites := delta.ModifiedSuites()
+ if len(modifiedSuites) > 0 {
+ fmt.Fprintln(coloredStream, greenColor+"\nDetected changes in:"+defaultStyle)
+ for _, pkg := range delta.ModifiedPackages {
+ fmt.Fprintln(coloredStream, " "+pkg)
+ }
+ fmt.Fprintf(coloredStream, greenColor+"Will run %d %s:\n"+defaultStyle, len(modifiedSuites), pluralizedWord("suite", "suites", len(modifiedSuites)))
+ for _, suite := range modifiedSuites {
+ suitesToRun = append(suitesToRun, suite.Suite)
+ fmt.Fprintln(coloredStream, " "+suite.Description())
+ }
+ fmt.Fprintln(coloredStream, "")
+ }
+
+ if len(suitesToRun) > 0 {
+ w.UpdateSeed()
+ w.ComputeSuccinctMode(len(suitesToRun))
+ runners := w.runnersForSuites(suitesToRun, additionalArgs)
+ result, _ := w.suiteRunner.RunSuites(runners, w.commandFlags.NumCompilers, true, func(suite testsuite.TestSuite) {
+ deltaTracker.WillRun(suite)
+ })
+ for _, runner := range runners {
+ runner.CleanUp()
+ }
+ if !w.interruptHandler.WasInterrupted() {
+ color := redColor
+ if result.Passed {
+ color = greenColor
+ }
+ fmt.Fprintln(coloredStream, color+"\nDone. Resuming watch..."+defaultStyle)
+ }
+ }
+
+ case <-w.interruptHandler.C:
+ return
+ }
+ }
+}
+
+func (w *SpecWatcher) ComputeSuccinctMode(numSuites int) {
+ if config.DefaultReporterConfig.Verbose {
+ config.DefaultReporterConfig.Succinct = false
+ return
+ }
+
+ if w.commandFlags.wasSet("succinct") {
+ return
+ }
+
+ if numSuites == 1 {
+ config.DefaultReporterConfig.Succinct = false
+ }
+
+ if numSuites > 1 {
+ config.DefaultReporterConfig.Succinct = true
+ }
+}
+
+func (w *SpecWatcher) UpdateSeed() {
+ if !w.commandFlags.wasSet("seed") {
+ config.GinkgoConfig.RandomSeed = time.Now().Unix()
+ }
+}