Godeps: bump ginkgo for table extensions
This commit is contained in:
parent
5a39a168f7
commit
b91aec9a62
1
Godeps/Godeps.json
generated
1
Godeps/Godeps.json
generated
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/appc/cni",
|
"ImportPath": "github.com/appc/cni",
|
||||||
"GoVersion": "go1.6",
|
"GoVersion": "go1.6",
|
||||||
"GodepVersion": "v58",
|
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./..."
|
"./..."
|
||||||
],
|
],
|
||||||
|
98
vendor/github.com/onsi/ginkgo/extensions/table/table.go
generated
vendored
Normal file
98
vendor/github.com/onsi/ginkgo/extensions/table/table.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
72
vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go
generated
vendored
Normal file
72
vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go
generated
vendored
Normal file
@ -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}
|
||||||
|
}
|
182
vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
generated
vendored
Normal file
182
vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
68
vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
generated
vendored
Normal file
68
vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
123
vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
generated
vendored
Normal file
123
vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
generated
vendored
Normal file
@ -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"}
|
||||||
|
}
|
91
vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
generated
vendored
Normal file
91
vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
generated
vendored
Normal file
@ -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},
|
||||||
|
}
|
||||||
|
}
|
127
vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
generated
vendored
Normal file
127
vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
56
vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
generated
vendored
Normal file
56
vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
163
vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
generated
vendored
Normal file
163
vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
generated
vendored
Normal file
130
vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
generated
vendored
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
generated
vendored
Normal file
44
vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
generated
vendored
Normal file
@ -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])
|
||||||
|
}
|
164
vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
generated
vendored
Normal file
164
vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
generated
vendored
Normal file
@ -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])
|
||||||
|
}
|
31
vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
generated
vendored
Normal file
31
vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
52
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
generated
vendored
Normal file
52
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
generated
vendored
Normal file
@ -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()
|
||||||
|
}
|
14
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
generated
vendored
Normal file
14
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
7
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
generated
vendored
Normal file
7
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package interrupthandler
|
||||||
|
|
||||||
|
func SwallowSigQuit() {
|
||||||
|
//noop
|
||||||
|
}
|
291
vendor/github.com/onsi/ginkgo/ginkgo/main.go
generated
vendored
Normal file
291
vendor/github.com/onsi/ginkgo/ginkgo/main.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
194
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
generated
vendored
Normal file
194
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
76
vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
generated
vendored
Normal file
76
vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
141
vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
generated
vendored
Normal file
141
vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
192
vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
generated
vendored
Normal file
192
vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
generated
vendored
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
121
vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
generated
vendored
Normal file
121
vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
generated
vendored
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
}
|
172
vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
generated
vendored
Normal file
172
vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
generated
vendored
Normal file
52
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
27
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
generated
vendored
Normal file
27
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
generated
vendored
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
}
|
460
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
generated
vendored
Normal file
460
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
106
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
generated
vendored
Normal file
106
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
38
vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
generated
vendored
Normal file
38
vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
generated
vendored
Normal file
@ -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))
|
||||||
|
}
|
||||||
|
}
|
23
vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
generated
vendored
Normal file
23
vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
22
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
generated
vendored
Normal file
22
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
71
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
71
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
91
vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go
generated
vendored
Normal file
91
vendor/github.com/onsi/ginkgo/ginkgo/watch/dependencies.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
103
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go
generated
vendored
Normal file
103
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hash.go
generated
vendored
Normal file
@ -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())
|
||||||
|
}
|
82
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go
generated
vendored
Normal file
82
vendor/github.com/onsi/ginkgo/ginkgo/watch/package_hashes.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
87
vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go
generated
vendored
Normal file
87
vendor/github.com/onsi/ginkgo/ginkgo/watch/suite.go
generated
vendored
Normal file
@ -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)
|
||||||
|
}
|
172
vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go
generated
vendored
Normal file
172
vendor/github.com/onsi/ginkgo/ginkgo/watch_command.go
generated
vendored
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/onsi/ginkgo/integration/integration.go
generated
vendored
Normal file
1
vendor/github.com/onsi/ginkgo/integration/integration.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package integration
|
Loading…
x
Reference in New Issue
Block a user