Godeps: bump ginkgo for table extensions
authorStefan Junker <mail@stefanjunker.de>
Thu, 17 Mar 2016 17:32:56 +0000 (18:32 +0100)
committerStefan Junker <mail@stefanjunker.de>
Mon, 21 Mar 2016 19:36:05 +0000 (20:36 +0100)
38 files changed:
Godeps/Godeps.json
vendor/github.com/onsi/ginkgo/extensions/table/table.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/build_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/help_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/main.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/notifications.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/run_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/version_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go [new file with mode: 0644]
vendor/github.com/onsi/ginkgo/integration/integration.go [new file with mode: 0644]

index 411edba..aad45d9 100644 (file)
@@ -1,7 +1,6 @@
 {
        "ImportPath": "github.com/appc/cni",
        "GoVersion": "go1.6",
-       "GodepVersion": "v58",
        "Packages": [
                "./..."
        ],
diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table.go b/vendor/github.com/onsi/ginkgo/extensions/table/table.go
new file mode 100644 (file)
index 0000000..ae8ab7d
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+
+Table provides a simple DSL for Ginkgo-native Table-Driven Tests
+
+The godoc documentation describes Table's API.  More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo#table-driven-tests
+
+*/
+
+package table
+
+import (
+       "fmt"
+       "reflect"
+
+       "github.com/onsi/ginkgo"
+)
+
+/*
+DescribeTable describes a table-driven test.
+
+For example:
+
+    DescribeTable("a simple table",
+        func(x int, y int, expected bool) {
+            Ω(x > y).Should(Equal(expected))
+        },
+        Entry("x > y", 1, 0, true),
+        Entry("x == y", 0, 0, false),
+        Entry("x < y", 0, 1, false),
+    )
+
+The first argument to `DescribeTable` is a string description.
+The second argument is a function that will be run for each table entry.  Your assertions go here - the function is equivalent to a Ginkgo It.
+The subsequent arguments must be of type `TableEntry`.  We recommend using the `Entry` convenience constructors.
+
+The `Entry` constructor takes a string description followed by an arbitrary set of parameters.  These parameters are passed into your function.
+
+Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`.  Each `Entry` is turned into an `It` within the `Describe`.
+
+It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run).
+
+Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry).  In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable.
+*/
+func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
+       describeTable(description, itBody, entries, false, false)
+       return true
+}
+
+/*
+You can focus a table with `FDescribeTable`.  This is equivalent to `FDescribe`.
+*/
+func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
+       describeTable(description, itBody, entries, false, true)
+       return true
+}
+
+/*
+You can mark a table as pending with `PDescribeTable`.  This is equivalent to `PDescribe`.
+*/
+func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
+       describeTable(description, itBody, entries, true, false)
+       return true
+}
+
+/*
+You can mark a table as pending with `XDescribeTable`.  This is equivalent to `XDescribe`.
+*/
+func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
+       describeTable(description, itBody, entries, true, false)
+       return true
+}
+
+func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) {
+       itBodyValue := reflect.ValueOf(itBody)
+       if itBodyValue.Kind() != reflect.Func {
+               panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
+       }
+
+       if pending {
+               ginkgo.PDescribe(description, func() {
+                       for _, entry := range entries {
+                               entry.generateIt(itBodyValue)
+                       }
+               })
+       } else if focused {
+               ginkgo.FDescribe(description, func() {
+                       for _, entry := range entries {
+                               entry.generateIt(itBodyValue)
+                       }
+               })
+       } else {
+               ginkgo.Describe(description, func() {
+                       for _, entry := range entries {
+                               entry.generateIt(itBodyValue)
+                       }
+               })
+       }
+}
diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go
new file mode 100644 (file)
index 0000000..a6a9e3c
--- /dev/null
@@ -0,0 +1,72 @@
+package table
+
+import (
+       "reflect"
+
+       "github.com/onsi/ginkgo"
+)
+
+/*
+TableEntry represents an entry in a table test.  You generally use the `Entry` constructor.
+*/
+type TableEntry struct {
+       Description string
+       Parameters  []interface{}
+       Pending     bool
+       Focused     bool
+}
+
+func (t TableEntry) generateIt(itBody reflect.Value) {
+       if t.Pending {
+               ginkgo.PIt(t.Description)
+               return
+       }
+
+       values := []reflect.Value{}
+       for _, param := range t.Parameters {
+               values = append(values, reflect.ValueOf(param))
+       }
+
+       body := func() {
+               itBody.Call(values)
+       }
+
+       if t.Focused {
+               ginkgo.FIt(t.Description, body)
+       } else {
+               ginkgo.It(t.Description, body)
+       }
+}
+
+/*
+Entry constructs a TableEntry.
+
+The first argument is a required description (this becomes the content of the generated Ginkgo `It`).
+Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`.
+
+Each Entry ends up generating an individual Ginkgo It.
+*/
+func Entry(description string, parameters ...interface{}) TableEntry {
+       return TableEntry{description, parameters, false, false}
+}
+
+/*
+You can focus a particular entry with FEntry.  This is equivalent to FIt.
+*/
+func FEntry(description string, parameters ...interface{}) TableEntry {
+       return TableEntry{description, parameters, false, true}
+}
+
+/*
+You can mark a particular entry as pending with PEntry.  This is equivalent to PIt.
+*/
+func PEntry(description string, parameters ...interface{}) TableEntry {
+       return TableEntry{description, parameters, true, false}
+}
+
+/*
+You can mark a particular entry as pending with XEntry.  This is equivalent to XIt.
+*/
+func XEntry(description string, parameters ...interface{}) TableEntry {
+       return TableEntry{description, parameters, true, false}
+}
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 (file)
index 0000000..d804fe0
--- /dev/null
@@ -0,0 +1,182 @@
+package main
+
+import (
+       "bytes"
+       "flag"
+       "fmt"
+       "os"
+       "path/filepath"
+       "strings"
+       "text/template"
+
+       "go/build"
+
+       "github.com/onsi/ginkgo/ginkgo/nodot"
+)
+
+func BuildBootstrapCommand() *Command {
+       var agouti, noDot bool
+       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")
+
+       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)
+               },
+       }
+}
+
+var bootstrapText = `package {{.Package}}_test
+
+import (
+       {{.GinkgoImport}}
+       {{.GomegaImport}}
+
+       "testing"
+)
+
+func Test{{.FormattedName}}(t *testing.T) {
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "{{.FormattedName}} Suite")
+}
+`
+
+var agoutiBootstrapText = `package {{.Package}}_test
+
+import (
+       {{.GinkgoImport}}
+       {{.GomegaImport}}
+       "github.com/sclevine/agouti"
+
+       "testing"
+)
+
+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 fileExists(path string) bool {
+       _, err := os.Stat(path)
+       if err == nil {
+               return true
+       }
+       return false
+}
+
+func generateBootstrap(agouti bool, noDot bool) {
+       packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName()
+       data := bootstrapData{
+               Package:       packageName,
+               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 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 (file)
index 0000000..bbba8a1
--- /dev/null
@@ -0,0 +1,68 @@
+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, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, 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)
+               }
+
+               runner.CleanUp()
+       }
+
+       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 (file)
index 0000000..02e2b3b
--- /dev/null
@@ -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 (file)
index 0000000..e226196
--- /dev/null
@@ -0,0 +1,91 @@
+package convert
+
+import (
+       "errors"
+       "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 = errors.New(fmt.Sprintf("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 (file)
index 0000000..ed09c46
--- /dev/null
@@ -0,0 +1,127 @@
+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)
+       }
+       return
+}
+
+/*
+ * 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...) {
+               testfiles = append(testfiles, filepath.Join(pkg.Dir, file))
+       }
+
+       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) {
+       output, err := exec.Command("go", "fmt", pkg.ImportPath).Output()
+
+       if err != nil {
+               fmt.Printf("Warning: Error running 'go fmt %s'.\nstdout: %s\n%s\n", pkg.ImportPath, 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 (file)
index 0000000..b33595c
--- /dev/null
@@ -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 (file)
index 0000000..4b001a7
--- /dev/null
@@ -0,0 +1,163 @@
+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, 0)
+       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))
+       }
+
+       ioutil.WriteFile(pathToFile, buffer.Bytes(), fileInfo.Mode())
+       return
+}
+
+/*
+ * 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:]...)
+       return
+}
+
+/*
+ * 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 (file)
index 0000000..418cdc4
--- /dev/null
@@ -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 (file)
index 0000000..89e60d3
--- /dev/null
@@ -0,0 +1,44 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "github.com/onsi/ginkgo/ginkgo/convert"
+       "os"
+)
+
+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 (file)
index 0000000..7dd3b4d
--- /dev/null
@@ -0,0 +1,164 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "os"
+       "path/filepath"
+       "strings"
+       "text/template"
+)
+
+func BuildGenerateCommand() *Command {
+       var agouti, noDot 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")
+
+       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)
+               },
+       }
+}
+
+var specText = `package {{.Package}}_test
+
+import (
+       . "{{.PackageImportPath}}"
+
+       {{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
+       {{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
+)
+
+var _ = Describe("{{.Subject}}", func() {
+
+})
+`
+
+var agoutiSpecText = `package {{.Package}}_test
+
+import (
+       . "{{.PackageImportPath}}"
+
+       {{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
+       {{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
+       . "github.com/sclevine/agouti/matchers"
+       "github.com/sclevine/agouti"
+)
+
+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
+}
+
+func generateSpec(args []string, agouti, noDot bool) {
+       if len(args) == 0 {
+               err := generateSpecForSubject("", agouti, noDot)
+               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)
+               if err != nil {
+                       failed = true
+                       fmt.Println(err.Error())
+               }
+       }
+       fmt.Println("")
+       if failed {
+               os.Exit(1)
+       }
+}
+
+func generateSpecForSubject(subject string, agouti, noDot bool) error {
+       packageName, specFilePrefix, formattedName := getPackageAndFormattedName()
+       if subject != "" {
+               subject = strings.Split(subject, ".go")[0]
+               subject = strings.Split(subject, "_test")[0]
+               specFilePrefix = subject
+               formattedName = prettifyPackageName(subject)
+       }
+
+       data := specData{
+               Package:           packageName,
+               Subject:           formattedName,
+               PackageImportPath: getPackageImportPath(),
+               IncludeImports:    !noDot,
+       }
+
+       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 getPackageImportPath() string {
+       workingDir, err := os.Getwd()
+       if err != nil {
+               panic(err.Error())
+       }
+       sep := string(filepath.Separator)
+       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 (file)
index 0000000..a42d4f8
--- /dev/null
@@ -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 <COMAND>",
+               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 (file)
index 0000000..c15db0b
--- /dev/null
@@ -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, 0),
+       }
+
+       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 (file)
index 0000000..14c9421
--- /dev/null
@@ -0,0 +1,14 @@
+// +build freebsd openbsd netbsd dragonfly darwin linux
+
+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 (file)
index 0000000..7f4a50e
--- /dev/null
@@ -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 (file)
index 0000000..b031b80
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+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 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.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.Fprintf(os.Stderr, "Ginkgo Version %s\n\n", config.VERSION)
+       usageForCommand(DefaultCommand, false)
+       for _, command := range Commands {
+               fmt.Fprintf(os.Stderr, "\n")
+               usageForCommand(command, false)
+       }
+}
+
+func usageForCommand(command *Command, longForm bool) {
+       fmt.Fprintf(os.Stderr, "%s\n%s\n", command.UsageCommand, strings.Repeat("-", len(command.UsageCommand)))
+       fmt.Fprintf(os.Stderr, "%s\n", strings.Join(command.Usage, "\n"))
+       if command.SuppressFlagDocumentation && !longForm {
+               fmt.Fprintf(os.Stderr, "%s\n", strings.Join(command.FlagDocSubstitute, "\n  "))
+       } else {
+               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, recurse 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
+                               }
+                       }
+                       suites = append(suites, testsuite.SuitesInDir(arg, recurse)...)
+               }
+       } else {
+               suites = testsuite.SuitesInDir(".", recurse)
+       }
+
+       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) {
+       err := exec.Command("go", "fmt", path).Run()
+       if err != nil {
+               complainAndQuit("Could not fmt: " + err.Error())
+       }
+}
+
+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 (file)
index 0000000..3f7237c
--- /dev/null
@@ -0,0 +1,194 @@
+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:
+                       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 (file)
index 0000000..212235b
--- /dev/null
@@ -0,0 +1,76 @@
+package main
+
+import (
+       "bufio"
+       "flag"
+       "github.com/onsi/ginkgo/ginkgo/nodot"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "regexp"
+)
+
+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 (file)
index 0000000..368d61f
--- /dev/null
@@ -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 (file)
index 0000000..c5cf277
--- /dev/null
@@ -0,0 +1,192 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "math/rand"
+       "os"
+       "time"
+
+       "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.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, 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()
+       }
+
+       fmt.Printf("\nGinkgo ran %d %s in %s\n", numSuites, pluralizedWord("suite", "suites", numSuites), time.Since(t))
+
+       if runResult.Passed {
+               if runResult.HasProgrammaticFocus {
+                       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)
+       }
+}
+
+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",
+                       "Here we go 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 (file)
index 0000000..e6bcf36
--- /dev/null
@@ -0,0 +1,121 @@
+package main
+
+import (
+       "flag"
+       "runtime"
+
+       "github.com/onsi/ginkgo/config"
+)
+
+type RunWatchAndBuildCommandFlags struct {
+       Recurse     bool
+       Race        bool
+       Cover       bool
+       CoverPkg    string
+       SkipPackage string
+       Tags        string
+
+       //for run and watch commands
+       NumCPU         int
+       NumCompilers   int
+       ParallelStream bool
+       Notify         bool
+       AfterSuiteHook string
+       AutoNodes      bool
+
+       //only for run command
+       KeepGoing       bool
+       UntilItFails    bool
+       RandomizeSuites bool
+
+       //only for watch command
+       Depth int
+
+       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) flags(mode int) {
+       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.Race), "race", false, "Run tests with race detection enabled")
+       c.FlagSet.BoolVar(&(c.Cover), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory")
+       c.FlagSet.StringVar(&(c.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.Tags), "tags", "", "A list of build tags to consider satisfied during the build")
+
+       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")
+       }
+
+       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")
+       }
+}
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 (file)
index 0000000..7d56e5f
--- /dev/null
@@ -0,0 +1,172 @@
+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"
+)
+
+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.Printf("\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/log_writer.go b/vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
new file mode 100644 (file)
index 0000000..a73a6e3
--- /dev/null
@@ -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 (file)
index 0000000..5d472ac
--- /dev/null
@@ -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 (file)
index 0000000..a1e47ba
--- /dev/null
@@ -0,0 +1,460 @@
+package testrunner
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "regexp"
+       "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"
+       "github.com/onsi/ginkgo/types"
+)
+
+type TestRunner struct {
+       Suite testsuite.TestSuite
+
+       compiled              bool
+       compilationTargetPath string
+
+       numCPU         int
+       parallelStream bool
+       race           bool
+       cover          bool
+       coverPkg       string
+       tags           string
+       additionalArgs []string
+}
+
+func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, additionalArgs []string) *TestRunner {
+       runner := &TestRunner{
+               Suite:          suite,
+               numCPU:         numCPU,
+               parallelStream: parallelStream,
+               race:           race,
+               cover:          cover,
+               coverPkg:       coverPkg,
+               tags:           tags,
+               additionalArgs: additionalArgs,
+       }
+
+       if !suite.Precompiled {
+               dir, err := ioutil.TempDir("", "ginkgo")
+               if err != nil {
+                       panic(fmt.Sprintf("coulnd't create temporary directory... might be time to rm -rf:\n%s", err.Error()))
+               }
+               runner.compilationTargetPath = filepath.Join(dir, suite.PackageName+".test")
+       }
+
+       return runner
+}
+
+func (t *TestRunner) Compile() error {
+       return t.CompileTo(t.compilationTargetPath)
+}
+
+func (t *TestRunner) CompileTo(path string) error {
+       if t.compiled {
+               return nil
+       }
+
+       if t.Suite.Precompiled {
+               return nil
+       }
+
+       args := []string{"test", "-c", "-i", "-o", path}
+       if t.race {
+               args = append(args, "-race")
+       }
+       if t.cover || t.coverPkg != "" {
+               args = append(args, "-cover", "-covermode=atomic")
+       }
+       if t.coverPkg != "" {
+               args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
+       }
+       if t.tags != "" {
+               args = append(args, fmt.Sprintf("-tags=%s", t.tags))
+       }
+
+       cmd := exec.Command("go", args...)
+
+       cmd.Dir = t.Suite.Path
+
+       output, err := cmd.CombinedOutput()
+
+       if err != nil {
+               fixedOutput := fixCompilationOutput(string(output), t.Suite.Path)
+               if len(output) > 0 {
+                       return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
+               }
+               return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
+       }
+
+       if fileExists(path) == false {
+               compiledFile := filepath.Join(t.Suite.Path, 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) == false
+}
+
+// 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)
+}
+
+/*
+go test -c -i spits package.test out into the cwd. there's no way to change this.
+
+to make sure it doesn't generate conflicting .test files in the cwd, Compile() must switch the cwd to the test package.
+
+unfortunately, this causes go test's compile output to be expressed *relative to the test package* instead of the cwd.
+
+this makes it hard to reason about what failed, and also prevents iterm's Cmd+click from working.
+
+fixCompilationOutput..... rewrites the output to fix the paths.
+
+yeah......
+*/
+func fixCompilationOutput(output string, relToPath string) string {
+       re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
+       lines := strings.Split(output, "\n")
+       for i, line := range lines {
+               indices := re.FindStringSubmatchIndex(line)
+               if len(indices) == 0 {
+                       continue
+               }
+
+               path := line[indices[2]:indices[3]]
+               path = filepath.Join(relToPath, path)
+               lines[i] = path + line[indices[3]:]
+       }
+       return strings.Join(lines, "\n")
+}
+
+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.RemoveAll(filepath.Dir(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.cover || t.coverPkg != "" {
+               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)
+       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!  |
+       |                                                                   |
+        -------------------------------------------------------------------
+`)
+
+               os.Stdout.Sync()
+
+               for _, writer := range writers {
+                       writer.Close()
+               }
+
+               for _, report := range reports {
+                       fmt.Print(report.String())
+               }
+
+               os.Stdout.Sync()
+       }
+
+       if t.cover || t.coverPkg != "" {
+               t.combineCoverprofiles()
+       }
+
+       return res
+}
+
+func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
+       args := []string{"--test.timeout=24h"}
+       if t.cover || t.coverPkg != "" {
+               coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
+               if t.numCPU > 1 {
+                       coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
+               }
+               args = append(args, coverprofile)
+       }
+
+       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 = stream
+       cmd.Stdout = stream
+
+       return cmd
+}
+
+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)
+
+       return res
+}
+
+func (t *TestRunner) combineCoverprofiles() {
+       profiles := []string{}
+       for cpu := 1; cpu <= t.numCPU; cpu++ {
+               coverFile := fmt.Sprintf("%s.coverprofile.%d", t.Suite.PackageName, 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(string(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")
+       ioutil.WriteFile(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.coverprofile", t.Suite.PackageName)), []byte(finalOutput), 0666)
+}
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 (file)
index 0000000..cc7d2f4
--- /dev/null
@@ -0,0 +1,106 @@
+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{}
+       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))
+       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/unfocus_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
new file mode 100644 (file)
index 0000000..683c3a9
--- /dev/null
@@ -0,0 +1,38 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "os/exec"
+)
+
+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) {
+       unfocus("Describe")
+       unfocus("Context")
+       unfocus("It")
+       unfocus("Measure")
+       unfocus("DescribeTable")
+       unfocus("Entry")
+}
+
+func unfocus(component string) {
+       fmt.Printf("Removing F%s...\n", component)
+       cmd := exec.Command("gofmt", fmt.Sprintf("-r=F%s -> %s", component, component), "-w", ".")
+       out, _ := cmd.CombinedOutput()
+       if string(out) != "" {
+               println(string(out))
+       }
+}
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 (file)
index 0000000..cdca3a3
--- /dev/null
@@ -0,0 +1,23 @@
+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 (file)
index 0000000..6c485c5
--- /dev/null
@@ -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 (file)
index 0000000..452c07e
--- /dev/null
@@ -0,0 +1,71 @@
+package watch
+
+import (
+       "fmt"
+
+       "github.com/onsi/ginkgo/ginkgo/testsuite"
+)
+
+type SuiteErrors map[testsuite.TestSuite]error
+
+type DeltaTracker struct {
+       maxDepth      int
+       suites        map[string]*Suite
+       packageHashes *PackageHashes
+}
+
+func NewDeltaTracker(maxDepth int) *DeltaTracker {
+       return &DeltaTracker{
+               maxDepth:      maxDepth,
+               packageHashes: NewPackageHashes(),
+               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 (file)
index 0000000..82c25fa
--- /dev/null
@@ -0,0 +1,91 @@
+package watch
+
+import (
+       "go/build"
+       "regexp"
+)
+
+var ginkgoAndGomegaFilter = regexp.MustCompile(`github\.com/onsi/ginkgo|github\.com/onsi/gomega`)
+
+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 == false && !ginkgoAndGomegaFilter.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 (file)
index 0000000..eaf357c
--- /dev/null
@@ -0,0 +1,103 @@
+package watch
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+       "regexp"
+       "time"
+)
+
+var goRegExp = regexp.MustCompile(`\.go$`)
+var goTestRegExp = regexp.MustCompile(`_test\.go$`)
+
+type PackageHash struct {
+       CodeModifiedTime time.Time
+       TestModifiedTime time.Time
+       Deleted          bool
+
+       path     string
+       codeHash string
+       testHash string
+}
+
+func NewPackageHash(path string) *PackageHash {
+       p := &PackageHash{
+               path: path,
+       }
+
+       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 == false {
+                       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 goRegExp.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 (file)
index 0000000..262eaa8
--- /dev/null
@@ -0,0 +1,82 @@
+package watch
+
+import (
+       "path/filepath"
+       "sync"
+)
+
+type PackageHashes struct {
+       PackageHashes map[string]*PackageHash
+       usedPaths     map[string]bool
+       lock          *sync.Mutex
+}
+
+func NewPackageHashes() *PackageHashes {
+       return &PackageHashes{
+               PackageHashes: map[string]*PackageHash{},
+               usedPaths:     nil,
+               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)
+       }
+
+       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 (file)
index 0000000..5deaba7
--- /dev/null
@@ -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 (file)
index 0000000..03ea012
--- /dev/null
@@ -0,0 +1,172 @@
+package main
+
+import (
+       "flag"
+       "fmt"
+       "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"
+)
+
+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.Race, w.commandFlags.Cover, w.commandFlags.CoverPkg, w.commandFlags.Tags, 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)
+       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)
+
+                       suitesToRun := []testsuite.TestSuite{}
+
+                       if len(delta.NewSuites) > 0 {
+                               fmt.Printf(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.Println("  " + suite.Description())
+                               }
+                       }
+
+                       modifiedSuites := delta.ModifiedSuites()
+                       if len(modifiedSuites) > 0 {
+                               fmt.Println(greenColor + "\nDetected changes in:" + defaultStyle)
+                               for _, pkg := range delta.ModifiedPackages {
+                                       fmt.Println("  " + pkg)
+                               }
+                               fmt.Printf(greenColor+"Will run %d %s:\n"+defaultStyle, len(modifiedSuites), pluralizedWord("suite", "suites", len(modifiedSuites)))
+                               for _, suite := range modifiedSuites {
+                                       suitesToRun = append(suitesToRun, suite.Suite)
+                                       fmt.Println("  " + suite.Description())
+                               }
+                               fmt.Println("")
+                       }
+
+                       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.Println(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()
+       }
+}
diff --git a/vendor/github.com/onsi/ginkgo/integration/integration.go b/vendor/github.com/onsi/ginkgo/integration/integration.go
new file mode 100644 (file)
index 0000000..76ab1b7
--- /dev/null
@@ -0,0 +1 @@
+package integration