mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-08-08 16:19:40 +00:00
TUN-7227: Migrate to devincarr/quic-go
The lucas-clemente/quic-go package moved namespaces and our branch went stale, this new fork provides support for the new quic-go repo and applies the max datagram frame size change. Until the max datagram frame size support gets upstreamed into quic-go, this can be used to unblock go 1.20 support as the old lucas-clemente/quic-go will not get go 1.20 support.
This commit is contained in:
63
vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
Normal file
63
vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func BuildBuildCommand() command.Command {
|
||||
var cliConfig = types.NewDefaultCLIConfig()
|
||||
var goFlagsConfig = types.NewDefaultGoFlagsConfig()
|
||||
|
||||
flags, err := types.BuildBuildCommandFlagSet(&cliConfig, &goFlagsConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return command.Command{
|
||||
Name: "build",
|
||||
Flags: flags,
|
||||
Usage: "ginkgo build <FLAGS> <PACKAGES>",
|
||||
ShortDoc: "Build the passed in <PACKAGES> (or the package in the current directory if left blank).",
|
||||
DocLink: "precompiling-suites",
|
||||
Command: func(args []string, _ []string) {
|
||||
var errors []error
|
||||
cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig)
|
||||
command.AbortIfErrors("Ginkgo detected configuration issues:", errors)
|
||||
|
||||
buildSpecs(args, cliConfig, goFlagsConfig)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) {
|
||||
suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter)
|
||||
if len(suites) == 0 {
|
||||
command.AbortWith("Found no test suites")
|
||||
}
|
||||
|
||||
internal.VerifyCLIAndFrameworkVersion(suites)
|
||||
|
||||
opc := internal.NewOrderedParallelCompiler(cliConfig.ComputedNumCompilers())
|
||||
opc.StartCompiling(suites, goFlagsConfig)
|
||||
|
||||
for {
|
||||
suiteIdx, suite := opc.Next()
|
||||
if suiteIdx >= len(suites) {
|
||||
break
|
||||
}
|
||||
suites[suiteIdx] = suite
|
||||
if suite.State.Is(internal.TestSuiteStateFailedToCompile) {
|
||||
fmt.Println(suite.CompilationError.Error())
|
||||
} else {
|
||||
fmt.Printf("Compiled %s.test\n", suite.PackageName)
|
||||
}
|
||||
}
|
||||
|
||||
if suites.CountWithState(internal.TestSuiteStateFailedToCompile) > 0 {
|
||||
command.AbortWith("Failed to compile all tests")
|
||||
}
|
||||
}
|
61
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go
generated
vendored
Normal file
61
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package command
|
||||
|
||||
import "fmt"
|
||||
|
||||
type AbortDetails struct {
|
||||
ExitCode int
|
||||
Error error
|
||||
EmitUsage bool
|
||||
}
|
||||
|
||||
func Abort(details AbortDetails) {
|
||||
panic(details)
|
||||
}
|
||||
|
||||
func AbortGracefullyWith(format string, args ...interface{}) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 0,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
EmitUsage: false,
|
||||
})
|
||||
}
|
||||
|
||||
func AbortWith(format string, args ...interface{}) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
EmitUsage: false,
|
||||
})
|
||||
}
|
||||
|
||||
func AbortWithUsage(format string, args ...interface{}) {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf(format, args...),
|
||||
EmitUsage: true,
|
||||
})
|
||||
}
|
||||
|
||||
func AbortIfError(preamble string, err error) {
|
||||
if err != nil {
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf("%s\n%s", preamble, err.Error()),
|
||||
EmitUsage: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func AbortIfErrors(preamble string, errors []error) {
|
||||
if len(errors) > 0 {
|
||||
out := ""
|
||||
for _, err := range errors {
|
||||
out += err.Error()
|
||||
}
|
||||
Abort(AbortDetails{
|
||||
ExitCode: 1,
|
||||
Error: fmt.Errorf("%s\n%s", preamble, out),
|
||||
EmitUsage: false,
|
||||
})
|
||||
}
|
||||
}
|
50
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go
generated
vendored
Normal file
50
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Name string
|
||||
Flags types.GinkgoFlagSet
|
||||
Usage string
|
||||
ShortDoc string
|
||||
Documentation string
|
||||
DocLink string
|
||||
Command func(args []string, additionalArgs []string)
|
||||
}
|
||||
|
||||
func (c Command) Run(args []string, additionalArgs []string) {
|
||||
args, err := c.Flags.Parse(args)
|
||||
if err != nil {
|
||||
AbortWithUsage(err.Error())
|
||||
}
|
||||
|
||||
c.Command(args, additionalArgs)
|
||||
}
|
||||
|
||||
func (c Command) EmitUsage(writer io.Writer) {
|
||||
fmt.Fprintln(writer, formatter.F("{{bold}}"+c.Usage+"{{/}}"))
|
||||
fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(c.Usage))))
|
||||
if c.ShortDoc != "" {
|
||||
fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.ShortDoc))
|
||||
fmt.Fprintln(writer, "")
|
||||
}
|
||||
if c.Documentation != "" {
|
||||
fmt.Fprintln(writer, formatter.Fiw(0, formatter.COLS, c.Documentation))
|
||||
fmt.Fprintln(writer, "")
|
||||
}
|
||||
if c.DocLink != "" {
|
||||
fmt.Fprintln(writer, formatter.Fi(0, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}http://onsi.github.io/ginkgo/#%s{{/}}", c.DocLink))
|
||||
fmt.Fprintln(writer, "")
|
||||
}
|
||||
flagUsage := c.Flags.Usage()
|
||||
if flagUsage != "" {
|
||||
fmt.Fprintf(writer, formatter.F(flagUsage))
|
||||
}
|
||||
}
|
182
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go
generated
vendored
Normal file
182
vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
type Program struct {
|
||||
Name string
|
||||
Heading string
|
||||
Commands []Command
|
||||
DefaultCommand Command
|
||||
DeprecatedCommands []DeprecatedCommand
|
||||
|
||||
//For testing - leave as nil in production
|
||||
OutWriter io.Writer
|
||||
ErrWriter io.Writer
|
||||
Exiter func(code int)
|
||||
}
|
||||
|
||||
type DeprecatedCommand struct {
|
||||
Name string
|
||||
Deprecation types.Deprecation
|
||||
}
|
||||
|
||||
func (p Program) RunAndExit(osArgs []string) {
|
||||
var command Command
|
||||
deprecationTracker := types.NewDeprecationTracker()
|
||||
if p.Exiter == nil {
|
||||
p.Exiter = os.Exit
|
||||
}
|
||||
if p.OutWriter == nil {
|
||||
p.OutWriter = formatter.ColorableStdOut
|
||||
}
|
||||
if p.ErrWriter == nil {
|
||||
p.ErrWriter = formatter.ColorableStdErr
|
||||
}
|
||||
|
||||
defer func() {
|
||||
exitCode := 0
|
||||
|
||||
if r := recover(); r != nil {
|
||||
details, ok := r.(AbortDetails)
|
||||
if !ok {
|
||||
panic(r)
|
||||
}
|
||||
|
||||
if details.Error != nil {
|
||||
fmt.Fprintln(p.ErrWriter, formatter.F("{{red}}{{bold}}%s %s{{/}} {{red}}failed{{/}}", p.Name, command.Name))
|
||||
fmt.Fprintln(p.ErrWriter, formatter.Fi(1, details.Error.Error()))
|
||||
}
|
||||
if details.EmitUsage {
|
||||
if details.Error != nil {
|
||||
fmt.Fprintln(p.ErrWriter, "")
|
||||
}
|
||||
command.EmitUsage(p.ErrWriter)
|
||||
}
|
||||
exitCode = details.ExitCode
|
||||
}
|
||||
|
||||
command.Flags.ValidateDeprecations(deprecationTracker)
|
||||
if deprecationTracker.DidTrackDeprecations() {
|
||||
fmt.Fprintln(p.ErrWriter, deprecationTracker.DeprecationsReport())
|
||||
}
|
||||
p.Exiter(exitCode)
|
||||
return
|
||||
}()
|
||||
|
||||
args, additionalArgs := []string{}, []string{}
|
||||
|
||||
foundDelimiter := false
|
||||
for _, arg := range osArgs[1:] {
|
||||
if !foundDelimiter {
|
||||
if arg == "--" {
|
||||
foundDelimiter = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if foundDelimiter {
|
||||
additionalArgs = append(additionalArgs, arg)
|
||||
} else {
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
|
||||
command = p.DefaultCommand
|
||||
if len(args) > 0 {
|
||||
p.handleHelpRequestsAndExit(p.OutWriter, args)
|
||||
if command.Name == args[0] {
|
||||
args = args[1:]
|
||||
} else {
|
||||
for _, deprecatedCommand := range p.DeprecatedCommands {
|
||||
if deprecatedCommand.Name == args[0] {
|
||||
deprecationTracker.TrackDeprecation(deprecatedCommand.Deprecation)
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, tryCommand := range p.Commands {
|
||||
if tryCommand.Name == args[0] {
|
||||
command, args = tryCommand, args[1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command.Run(args, additionalArgs)
|
||||
}
|
||||
|
||||
func (p Program) handleHelpRequestsAndExit(writer io.Writer, args []string) {
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
matchesHelpFlag := func(args ...string) bool {
|
||||
for _, arg := range args {
|
||||
if arg == "--help" || arg == "-help" || arg == "-h" || arg == "--h" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if len(args) == 1 {
|
||||
if args[0] == "help" || matchesHelpFlag(args[0]) {
|
||||
p.EmitUsage(writer)
|
||||
Abort(AbortDetails{})
|
||||
}
|
||||
} else {
|
||||
var name string
|
||||
if args[0] == "help" || matchesHelpFlag(args[0]) {
|
||||
name = args[1]
|
||||
} else if matchesHelpFlag(args[1:]...) {
|
||||
name = args[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if p.DefaultCommand.Name == name || p.Name == name {
|
||||
p.DefaultCommand.EmitUsage(writer)
|
||||
Abort(AbortDetails{})
|
||||
}
|
||||
for _, command := range p.Commands {
|
||||
if command.Name == name {
|
||||
command.EmitUsage(writer)
|
||||
Abort(AbortDetails{})
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(writer, formatter.F("{{red}}Unknown Command: {{bold}}%s{{/}}", name))
|
||||
fmt.Fprintln(writer, "")
|
||||
p.EmitUsage(writer)
|
||||
Abort(AbortDetails{ExitCode: 1})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) EmitUsage(writer io.Writer) {
|
||||
fmt.Fprintln(writer, formatter.F(p.Heading))
|
||||
fmt.Fprintln(writer, formatter.F("{{gray}}%s{{/}}", strings.Repeat("-", len(p.Heading))))
|
||||
fmt.Fprintln(writer, formatter.F("For usage information for a command, run {{bold}}%s help COMMAND{{/}}.", p.Name))
|
||||
fmt.Fprintln(writer, formatter.F("For usage information for the default command, run {{bold}}%s help %s{{/}} or {{bold}}%s help %s{{/}}.", p.Name, p.Name, p.Name, p.DefaultCommand.Name))
|
||||
fmt.Fprintln(writer, "")
|
||||
fmt.Fprintln(writer, formatter.F("The following commands are available:"))
|
||||
|
||||
fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} or %s {{bold}}%s{{/}} - {{gray}}%s{{/}}", p.Name, p.Name, p.DefaultCommand.Name, p.DefaultCommand.Usage))
|
||||
if p.DefaultCommand.ShortDoc != "" {
|
||||
fmt.Fprintln(writer, formatter.Fi(2, p.DefaultCommand.ShortDoc))
|
||||
}
|
||||
|
||||
for _, command := range p.Commands {
|
||||
fmt.Fprintln(writer, formatter.Fi(1, "{{bold}}%s{{/}} - {{gray}}%s{{/}}", command.Name, command.Usage))
|
||||
if command.ShortDoc != "" {
|
||||
fmt.Fprintln(writer, formatter.Fi(2, command.ShortDoc))
|
||||
}
|
||||
}
|
||||
}
|
48
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go
generated
vendored
Normal file
48
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package generators
|
||||
|
||||
var bootstrapText = `package {{.Package}}
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
)
|
||||
|
||||
func Test{{.FormattedName}}(t *testing.T) {
|
||||
{{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail)
|
||||
{{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite")
|
||||
}
|
||||
`
|
||||
|
||||
var agoutiBootstrapText = `package {{.Package}}
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
"github.com/sclevine/agouti"
|
||||
)
|
||||
|
||||
func Test{{.FormattedName}}(t *testing.T) {
|
||||
{{.GomegaPackage}}RegisterFailHandler({{.GinkgoPackage}}Fail)
|
||||
{{.GinkgoPackage}}RunSpecs(t, "{{.FormattedName}} Suite")
|
||||
}
|
||||
|
||||
var agoutiDriver *agouti.WebDriver
|
||||
|
||||
var _ = {{.GinkgoPackage}}BeforeSuite(func() {
|
||||
// Choose a WebDriver:
|
||||
|
||||
agoutiDriver = agouti.PhantomJS()
|
||||
// agoutiDriver = agouti.Selenium()
|
||||
// agoutiDriver = agouti.ChromeDriver()
|
||||
|
||||
{{.GomegaPackage}}Expect(agoutiDriver.Start()).To({{.GomegaPackage}}Succeed())
|
||||
})
|
||||
|
||||
var _ = {{.GinkgoPackage}}AfterSuite(func() {
|
||||
{{.GomegaPackage}}Expect(agoutiDriver.Stop()).To({{.GomegaPackage}}Succeed())
|
||||
})
|
||||
`
|
113
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
Normal file
113
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func BuildBootstrapCommand() command.Command {
|
||||
conf := GeneratorsConfig{}
|
||||
flags, err := types.NewGinkgoFlagSet(
|
||||
types.GinkgoFlags{
|
||||
{Name: "agouti", KeyPath: "Agouti",
|
||||
Usage: "If set, bootstrap will generate a bootstrap file for writing Agouti tests"},
|
||||
{Name: "nodot", KeyPath: "NoDot",
|
||||
Usage: "If set, bootstrap will generate a bootstrap test file that does not dot-import ginkgo and gomega"},
|
||||
{Name: "internal", KeyPath: "Internal",
|
||||
Usage: "If set, bootstrap will generate a bootstrap test file that uses the regular package name (i.e. `package X`, not `package X_test`)"},
|
||||
{Name: "template", KeyPath: "CustomTemplate",
|
||||
UsageArgument: "template-file",
|
||||
Usage: "If specified, generate will use the contents of the file passed as the bootstrap template"},
|
||||
},
|
||||
&conf,
|
||||
types.GinkgoFlagSections{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return command.Command{
|
||||
Name: "bootstrap",
|
||||
Usage: "ginkgo bootstrap",
|
||||
ShortDoc: "Bootstrap a test suite for the current package",
|
||||
Documentation: `Tests written in Ginkgo and Gomega require a small amount of boilerplate to hook into Go's testing infrastructure.
|
||||
|
||||
{{bold}}ginkgo bootstrap{{/}} generates this boilerplate for you in a file named X_suite_test.go where X is the name of the package under test.`,
|
||||
DocLink: "generators",
|
||||
Flags: flags,
|
||||
Command: func(_ []string, _ []string) {
|
||||
generateBootstrap(conf)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type bootstrapData struct {
|
||||
Package string
|
||||
FormattedName string
|
||||
|
||||
GinkgoImport string
|
||||
GomegaImport string
|
||||
GinkgoPackage string
|
||||
GomegaPackage string
|
||||
}
|
||||
|
||||
func generateBootstrap(conf GeneratorsConfig) {
|
||||
packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName()
|
||||
|
||||
data := bootstrapData{
|
||||
Package: determinePackageName(packageName, conf.Internal),
|
||||
FormattedName: formattedName,
|
||||
|
||||
GinkgoImport: `. "github.com/onsi/ginkgo/v2"`,
|
||||
GomegaImport: `. "github.com/onsi/gomega"`,
|
||||
GinkgoPackage: "",
|
||||
GomegaPackage: "",
|
||||
}
|
||||
|
||||
if conf.NoDot {
|
||||
data.GinkgoImport = `"github.com/onsi/ginkgo/v2"`
|
||||
data.GomegaImport = `"github.com/onsi/gomega"`
|
||||
data.GinkgoPackage = `ginkgo.`
|
||||
data.GomegaPackage = `gomega.`
|
||||
}
|
||||
|
||||
targetFile := fmt.Sprintf("%s_suite_test.go", bootstrapFilePrefix)
|
||||
if internal.FileExists(targetFile) {
|
||||
command.AbortWith("{{bold}}%s{{/}} already exists", targetFile)
|
||||
} else {
|
||||
fmt.Printf("Generating ginkgo test suite bootstrap for %s in:\n\t%s\n", packageName, targetFile)
|
||||
}
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
command.AbortIfError("Failed to create file:", err)
|
||||
defer f.Close()
|
||||
|
||||
var templateText string
|
||||
if conf.CustomTemplate != "" {
|
||||
tpl, err := os.ReadFile(conf.CustomTemplate)
|
||||
command.AbortIfError("Failed to read custom bootstrap file:", err)
|
||||
templateText = string(tpl)
|
||||
} else if conf.Agouti {
|
||||
templateText = agoutiBootstrapText
|
||||
} else {
|
||||
templateText = bootstrapText
|
||||
}
|
||||
|
||||
bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Parse(templateText)
|
||||
command.AbortIfError("Failed to parse bootstrap template:", err)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
bootstrapTemplate.Execute(buf, data)
|
||||
|
||||
buf.WriteTo(f)
|
||||
|
||||
internal.GoFmt(targetFile)
|
||||
}
|
239
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
Normal file
239
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func BuildGenerateCommand() command.Command {
|
||||
conf := GeneratorsConfig{}
|
||||
flags, err := types.NewGinkgoFlagSet(
|
||||
types.GinkgoFlags{
|
||||
{Name: "agouti", KeyPath: "Agouti",
|
||||
Usage: "If set, generate will create a test file for writing Agouti tests"},
|
||||
{Name: "nodot", KeyPath: "NoDot",
|
||||
Usage: "If set, generate will create a test file that does not dot-import ginkgo and gomega"},
|
||||
{Name: "internal", KeyPath: "Internal",
|
||||
Usage: "If set, generate will create a test file that uses the regular package name (i.e. `package X`, not `package X_test`)"},
|
||||
{Name: "template", KeyPath: "CustomTemplate",
|
||||
UsageArgument: "template-file",
|
||||
Usage: "If specified, generate will use the contents of the file passed as the test file template"},
|
||||
},
|
||||
&conf,
|
||||
types.GinkgoFlagSections{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return command.Command{
|
||||
Name: "generate",
|
||||
Usage: "ginkgo generate <filename(s)>",
|
||||
ShortDoc: "Generate a test file named <filename>_test.go",
|
||||
Documentation: `If the optional <filename> argument is omitted, a file named after the package in the current directory will be created.
|
||||
|
||||
You can pass multiple <filename(s)> to generate multiple files simultaneously. The resulting files are named <filename>_test.go.
|
||||
|
||||
You can also pass a <filename> of the form "file.go" and generate will emit "file_test.go".`,
|
||||
DocLink: "generators",
|
||||
Flags: flags,
|
||||
Command: func(args []string, _ []string) {
|
||||
generateTestFiles(conf, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type specData struct {
|
||||
Package string
|
||||
Subject string
|
||||
PackageImportPath string
|
||||
ImportPackage bool
|
||||
|
||||
GinkgoImport string
|
||||
GomegaImport string
|
||||
GinkgoPackage string
|
||||
GomegaPackage string
|
||||
}
|
||||
|
||||
func generateTestFiles(conf GeneratorsConfig, args []string) {
|
||||
subjects := args
|
||||
if len(subjects) == 0 {
|
||||
subjects = []string{""}
|
||||
}
|
||||
for _, subject := range subjects {
|
||||
generateTestFileForSubject(subject, conf)
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestFileForSubject(subject string, conf GeneratorsConfig) {
|
||||
packageName, specFilePrefix, formattedName := getPackageAndFormattedName()
|
||||
if subject != "" {
|
||||
specFilePrefix = formatSubject(subject)
|
||||
formattedName = prettifyName(specFilePrefix)
|
||||
}
|
||||
|
||||
if conf.Internal {
|
||||
specFilePrefix = specFilePrefix + "_internal"
|
||||
}
|
||||
|
||||
data := specData{
|
||||
Package: determinePackageName(packageName, conf.Internal),
|
||||
Subject: formattedName,
|
||||
PackageImportPath: getPackageImportPath(),
|
||||
ImportPackage: !conf.Internal,
|
||||
|
||||
GinkgoImport: `. "github.com/onsi/ginkgo/v2"`,
|
||||
GomegaImport: `. "github.com/onsi/gomega"`,
|
||||
GinkgoPackage: "",
|
||||
GomegaPackage: "",
|
||||
}
|
||||
|
||||
if conf.NoDot {
|
||||
data.GinkgoImport = `"github.com/onsi/ginkgo/v2"`
|
||||
data.GomegaImport = `"github.com/onsi/gomega"`
|
||||
data.GinkgoPackage = `ginkgo.`
|
||||
data.GomegaPackage = `gomega.`
|
||||
}
|
||||
|
||||
targetFile := fmt.Sprintf("%s_test.go", specFilePrefix)
|
||||
if internal.FileExists(targetFile) {
|
||||
command.AbortWith("{{bold}}%s{{/}} already exists", targetFile)
|
||||
} else {
|
||||
fmt.Printf("Generating ginkgo test for %s in:\n %s\n", data.Subject, targetFile)
|
||||
}
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
command.AbortIfError("Failed to create test file:", err)
|
||||
defer f.Close()
|
||||
|
||||
var templateText string
|
||||
if conf.CustomTemplate != "" {
|
||||
tpl, err := os.ReadFile(conf.CustomTemplate)
|
||||
command.AbortIfError("Failed to read custom template file:", err)
|
||||
templateText = string(tpl)
|
||||
} else if conf.Agouti {
|
||||
templateText = agoutiSpecText
|
||||
} else {
|
||||
templateText = specText
|
||||
}
|
||||
|
||||
specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Parse(templateText)
|
||||
command.AbortIfError("Failed to read parse test template:", err)
|
||||
|
||||
specTemplate.Execute(f, data)
|
||||
internal.GoFmt(targetFile)
|
||||
}
|
||||
|
||||
func formatSubject(name string) string {
|
||||
name = strings.ReplaceAll(name, "-", "_")
|
||||
name = strings.ReplaceAll(name, " ", "_")
|
||||
name = strings.Split(name, ".go")[0]
|
||||
name = strings.Split(name, "_test")[0]
|
||||
return name
|
||||
}
|
||||
|
||||
// moduleName returns module name from go.mod from given module root directory
|
||||
func moduleName(modRoot string) string {
|
||||
modFile, err := os.Open(filepath.Join(modRoot, "go.mod"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
mod := make([]byte, 128)
|
||||
_, err = modFile.Read(mod)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
slashSlash := []byte("//")
|
||||
moduleStr := []byte("module")
|
||||
|
||||
for len(mod) > 0 {
|
||||
line := mod
|
||||
mod = nil
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, mod = line[:i], line[i+1:]
|
||||
}
|
||||
if i := bytes.Index(line, slashSlash); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, moduleStr) {
|
||||
continue
|
||||
}
|
||||
line = line[len(moduleStr):]
|
||||
n := len(line)
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == n || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if line[0] == '"' || line[0] == '`' {
|
||||
p, err := strconv.Unquote(string(line))
|
||||
if err != nil {
|
||||
return "" // malformed quoted string or multiline module path
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
return string(line)
|
||||
}
|
||||
|
||||
return "" // missing module path
|
||||
}
|
||||
|
||||
func findModuleRoot(dir string) (root string) {
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// Look for enclosing go.mod.
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getPackageImportPath() string {
|
||||
workingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
sep := string(filepath.Separator)
|
||||
|
||||
// Try go.mod file first
|
||||
modRoot := findModuleRoot(workingDir)
|
||||
if modRoot != "" {
|
||||
modName := moduleName(modRoot)
|
||||
if modName != "" {
|
||||
cd := strings.ReplaceAll(workingDir, modRoot, "")
|
||||
cd = strings.ReplaceAll(cd, sep, "/")
|
||||
return modName + cd
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to GOPATH structure
|
||||
paths := strings.Split(workingDir, sep+"src"+sep)
|
||||
if len(paths) == 1 {
|
||||
fmt.Printf("\nCouldn't identify package import path.\n\n\tginkgo generate\n\nMust be run within a package directory under $GOPATH/src/...\nYou're going to have to change UNKNOWN_PACKAGE_PATH in the generated file...\n\n")
|
||||
return "UNKNOWN_PACKAGE_PATH"
|
||||
}
|
||||
return filepath.ToSlash(paths[len(paths)-1])
|
||||
}
|
41
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go
generated
vendored
Normal file
41
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package generators
|
||||
|
||||
var specText = `package {{.Package}}
|
||||
|
||||
import (
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
|
||||
{{if .ImportPackage}}"{{.PackageImportPath}}"{{end}}
|
||||
)
|
||||
|
||||
var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() {
|
||||
|
||||
})
|
||||
`
|
||||
|
||||
var agoutiSpecText = `package {{.Package}}
|
||||
|
||||
import (
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
"github.com/sclevine/agouti"
|
||||
. "github.com/sclevine/agouti/matchers"
|
||||
|
||||
{{if .ImportPackage}}"{{.PackageImportPath}}"{{end}}
|
||||
)
|
||||
|
||||
var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() {
|
||||
var page *agouti.Page
|
||||
|
||||
{{.GinkgoPackage}}BeforeEach(func() {
|
||||
var err error
|
||||
page, err = agoutiDriver.NewPage()
|
||||
{{.GomegaPackage}}Expect(err).NotTo({{.GomegaPackage}}HaveOccurred())
|
||||
})
|
||||
|
||||
{{.GinkgoPackage}}AfterEach(func() {
|
||||
{{.GomegaPackage}}Expect(page.Destroy()).To({{.GomegaPackage}}Succeed())
|
||||
})
|
||||
})
|
||||
`
|
63
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
generated
vendored
Normal file
63
vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
)
|
||||
|
||||
type GeneratorsConfig struct {
|
||||
Agouti, NoDot, Internal bool
|
||||
CustomTemplate string
|
||||
}
|
||||
|
||||
func getPackageAndFormattedName() (string, string, string) {
|
||||
path, err := os.Getwd()
|
||||
command.AbortIfError("Could not get current working directory:", err)
|
||||
|
||||
dirName := strings.ReplaceAll(filepath.Base(path), "-", "_")
|
||||
dirName = strings.ReplaceAll(dirName, " ", "_")
|
||||
|
||||
pkg, err := build.ImportDir(path, 0)
|
||||
packageName := pkg.Name
|
||||
if err != nil {
|
||||
packageName = ensureLegalPackageName(dirName)
|
||||
}
|
||||
|
||||
formattedName := prettifyName(filepath.Base(path))
|
||||
return packageName, dirName, formattedName
|
||||
}
|
||||
|
||||
func ensureLegalPackageName(name string) string {
|
||||
if name == "_" {
|
||||
return "underscore"
|
||||
}
|
||||
if len(name) == 0 {
|
||||
return "empty"
|
||||
}
|
||||
n, isDigitErr := strconv.Atoi(string(name[0]))
|
||||
if isDigitErr == nil {
|
||||
return []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}[n] + name[1:]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func prettifyName(name string) string {
|
||||
name = strings.ReplaceAll(name, "-", " ")
|
||||
name = strings.ReplaceAll(name, "_", " ")
|
||||
name = strings.Title(name)
|
||||
name = strings.ReplaceAll(name, " ", "")
|
||||
return name
|
||||
}
|
||||
|
||||
func determinePackageName(name string, internal bool) string {
|
||||
if internal {
|
||||
return name
|
||||
}
|
||||
|
||||
return name + "_test"
|
||||
}
|
152
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
Normal file
152
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite {
|
||||
if suite.PathToCompiledTest != "" {
|
||||
return suite
|
||||
}
|
||||
|
||||
suite.CompilationError = nil
|
||||
|
||||
path, err := filepath.Abs(filepath.Join(suite.Path, suite.PackageName+".test"))
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compute compilation target path:\n%s", err.Error())
|
||||
return suite
|
||||
}
|
||||
|
||||
args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, path, "./")
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error())
|
||||
return suite
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = suite.Path
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if len(output) > 0 {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compile %s:\n\n%s", suite.PackageName, output)
|
||||
} else {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compile %s\n%s", suite.PackageName, err.Error())
|
||||
}
|
||||
return suite
|
||||
}
|
||||
|
||||
if strings.Contains(string(output), "[no test files]") {
|
||||
suite.State = TestSuiteStateSkippedDueToEmptyCompilation
|
||||
return suite
|
||||
}
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
|
||||
if !FileExists(path) {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compile %s:\nOutput file %s could not be found", suite.PackageName, path)
|
||||
return suite
|
||||
}
|
||||
|
||||
suite.State = TestSuiteStateCompiled
|
||||
suite.PathToCompiledTest = path
|
||||
return suite
|
||||
}
|
||||
|
||||
func Cleanup(goFlagsConfig types.GoFlagsConfig, suites ...TestSuite) {
|
||||
if goFlagsConfig.BinaryMustBePreserved() {
|
||||
return
|
||||
}
|
||||
for _, suite := range suites {
|
||||
if !suite.Precompiled {
|
||||
os.Remove(suite.PathToCompiledTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type parallelSuiteBundle struct {
|
||||
suite TestSuite
|
||||
compiled chan TestSuite
|
||||
}
|
||||
|
||||
type OrderedParallelCompiler struct {
|
||||
mutex *sync.Mutex
|
||||
stopped bool
|
||||
numCompilers int
|
||||
|
||||
idx int
|
||||
numSuites int
|
||||
completionChannels []chan TestSuite
|
||||
}
|
||||
|
||||
func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler {
|
||||
return &OrderedParallelCompiler{
|
||||
mutex: &sync.Mutex{},
|
||||
numCompilers: numCompilers,
|
||||
}
|
||||
}
|
||||
|
||||
func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) {
|
||||
opc.stopped = false
|
||||
opc.idx = 0
|
||||
opc.numSuites = len(suites)
|
||||
opc.completionChannels = make([]chan TestSuite, opc.numSuites)
|
||||
|
||||
toCompile := make(chan parallelSuiteBundle, opc.numCompilers)
|
||||
for compiler := 0; compiler < opc.numCompilers; compiler++ {
|
||||
go func() {
|
||||
for bundle := range toCompile {
|
||||
c, suite := bundle.compiled, bundle.suite
|
||||
opc.mutex.Lock()
|
||||
stopped := opc.stopped
|
||||
opc.mutex.Unlock()
|
||||
if !stopped {
|
||||
suite = CompileSuite(suite, goFlagsConfig)
|
||||
}
|
||||
c <- suite
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for idx, suite := range suites {
|
||||
opc.completionChannels[idx] = make(chan TestSuite, 1)
|
||||
toCompile <- parallelSuiteBundle{suite, opc.completionChannels[idx]}
|
||||
if idx == 0 { //compile first suite serially
|
||||
suite = <-opc.completionChannels[0]
|
||||
opc.completionChannels[0] <- suite
|
||||
}
|
||||
}
|
||||
|
||||
close(toCompile)
|
||||
}
|
||||
|
||||
func (opc *OrderedParallelCompiler) Next() (int, TestSuite) {
|
||||
if opc.idx >= opc.numSuites {
|
||||
return opc.numSuites, TestSuite{}
|
||||
}
|
||||
|
||||
idx := opc.idx
|
||||
suite := <-opc.completionChannels[idx]
|
||||
opc.idx = opc.idx + 1
|
||||
|
||||
return idx, suite
|
||||
}
|
||||
|
||||
func (opc *OrderedParallelCompiler) StopAndDrain() {
|
||||
opc.mutex.Lock()
|
||||
opc.stopped = true
|
||||
opc.mutex.Unlock()
|
||||
}
|
237
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
Normal file
237
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/pprof/profile"
|
||||
"github.com/onsi/ginkgo/v2/reporters"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func AbsPathForGeneratedAsset(assetName string, suite TestSuite, cliConfig types.CLIConfig, process int) string {
|
||||
suffix := ""
|
||||
if process != 0 {
|
||||
suffix = fmt.Sprintf(".%d", process)
|
||||
}
|
||||
if cliConfig.OutputDir == "" {
|
||||
return filepath.Join(suite.AbsPath(), assetName+suffix)
|
||||
}
|
||||
outputDir, _ := filepath.Abs(cliConfig.OutputDir)
|
||||
return filepath.Join(outputDir, suite.NamespacedName()+"_"+assetName+suffix)
|
||||
}
|
||||
|
||||
func FinalizeProfilesAndReportsForSuites(suites TestSuites, cliConfig types.CLIConfig, suiteConfig types.SuiteConfig, reporterConfig types.ReporterConfig, goFlagsConfig types.GoFlagsConfig) ([]string, error) {
|
||||
messages := []string{}
|
||||
suitesWithProfiles := suites.WithState(TestSuiteStatePassed, TestSuiteStateFailed) //anything else won't have actually run and generated a profile
|
||||
|
||||
// merge cover profiles if need be
|
||||
if goFlagsConfig.Cover && !cliConfig.KeepSeparateCoverprofiles {
|
||||
coverProfiles := []string{}
|
||||
for _, suite := range suitesWithProfiles {
|
||||
if !suite.HasProgrammaticFocus {
|
||||
coverProfiles = append(coverProfiles, AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0))
|
||||
}
|
||||
}
|
||||
|
||||
if len(coverProfiles) > 0 {
|
||||
dst := goFlagsConfig.CoverProfile
|
||||
if cliConfig.OutputDir != "" {
|
||||
dst = filepath.Join(cliConfig.OutputDir, goFlagsConfig.CoverProfile)
|
||||
}
|
||||
err := MergeAndCleanupCoverProfiles(coverProfiles, dst)
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
coverage, err := GetCoverageFromCoverProfile(dst)
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
if coverage == 0 {
|
||||
messages = append(messages, "composite coverage: [no statements]")
|
||||
} else if suitesWithProfiles.AnyHaveProgrammaticFocus() {
|
||||
messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements however some suites did not contribute because they included programatically focused specs", coverage))
|
||||
} else {
|
||||
messages = append(messages, fmt.Sprintf("composite coverage: %.1f%% of statements", coverage))
|
||||
}
|
||||
} else {
|
||||
messages = append(messages, "no composite coverage computed: all suites included programatically focused specs")
|
||||
}
|
||||
}
|
||||
|
||||
// copy binaries if need be
|
||||
for _, suite := range suitesWithProfiles {
|
||||
if goFlagsConfig.BinaryMustBePreserved() && cliConfig.OutputDir != "" {
|
||||
src := suite.PathToCompiledTest
|
||||
dst := filepath.Join(cliConfig.OutputDir, suite.NamespacedName()+".test")
|
||||
if suite.Precompiled {
|
||||
if err := CopyFile(src, dst); err != nil {
|
||||
return messages, err
|
||||
}
|
||||
} else {
|
||||
if err := os.Rename(src, dst); err != nil {
|
||||
return messages, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type reportFormat struct {
|
||||
ReportName string
|
||||
GenerateFunc func(types.Report, string) error
|
||||
MergeFunc func([]string, string) ([]string, error)
|
||||
}
|
||||
reportFormats := []reportFormat{}
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JSONReport, GenerateFunc: reporters.GenerateJSONReport, MergeFunc: reporters.MergeAndCleanupJSONReports})
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JUnitReport, GenerateFunc: reporters.GenerateJUnitReport, MergeFunc: reporters.MergeAndCleanupJUnitReports})
|
||||
}
|
||||
if reporterConfig.TeamcityReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.TeamcityReport, GenerateFunc: reporters.GenerateTeamcityReport, MergeFunc: reporters.MergeAndCleanupTeamcityReports})
|
||||
}
|
||||
|
||||
// Generate reports for suites that failed to run
|
||||
reportableSuites := suites.ThatAreGinkgoSuites()
|
||||
for _, suite := range reportableSuites.WithState(TestSuiteStateFailedToCompile, TestSuiteStateFailedDueToTimeout, TestSuiteStateSkippedDueToPriorFailures, TestSuiteStateSkippedDueToEmptyCompilation) {
|
||||
report := types.Report{
|
||||
SuitePath: suite.AbsPath(),
|
||||
SuiteConfig: suiteConfig,
|
||||
SuiteSucceeded: false,
|
||||
}
|
||||
switch suite.State {
|
||||
case TestSuiteStateFailedToCompile:
|
||||
report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, suite.CompilationError.Error())
|
||||
case TestSuiteStateFailedDueToTimeout:
|
||||
report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, TIMEOUT_ELAPSED_FAILURE_REASON)
|
||||
case TestSuiteStateSkippedDueToPriorFailures:
|
||||
report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, PRIOR_FAILURES_FAILURE_REASON)
|
||||
case TestSuiteStateSkippedDueToEmptyCompilation:
|
||||
report.SpecialSuiteFailureReasons = append(report.SpecialSuiteFailureReasons, EMPTY_SKIP_FAILURE_REASON)
|
||||
report.SuiteSucceeded = true
|
||||
}
|
||||
|
||||
for _, format := range reportFormats {
|
||||
format.GenerateFunc(report, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0))
|
||||
}
|
||||
}
|
||||
|
||||
// Merge reports unless we've been asked to keep them separate
|
||||
if !cliConfig.KeepSeparateReports {
|
||||
for _, format := range reportFormats {
|
||||
reports := []string{}
|
||||
for _, suite := range reportableSuites {
|
||||
reports = append(reports, AbsPathForGeneratedAsset(format.ReportName, suite, cliConfig, 0))
|
||||
}
|
||||
dst := format.ReportName
|
||||
if cliConfig.OutputDir != "" {
|
||||
dst = filepath.Join(cliConfig.OutputDir, format.ReportName)
|
||||
}
|
||||
mergeMessages, err := format.MergeFunc(reports, dst)
|
||||
messages = append(messages, mergeMessages...)
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
//loads each profile, combines them, deletes them, stores them in destination
|
||||
func MergeAndCleanupCoverProfiles(profiles []string, destination string) error {
|
||||
combined := &bytes.Buffer{}
|
||||
modeRegex := regexp.MustCompile(`^mode: .*\n`)
|
||||
for i, profile := range profiles {
|
||||
contents, err := os.ReadFile(profile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read coverage file %s:\n%s", profile, err.Error())
|
||||
}
|
||||
os.Remove(profile)
|
||||
|
||||
// remove the cover mode line from every file
|
||||
// except the first one
|
||||
if i > 0 {
|
||||
contents = modeRegex.ReplaceAll(contents, []byte{})
|
||||
}
|
||||
|
||||
_, err = combined.Write(contents)
|
||||
|
||||
// Add a newline to the end of every file if missing.
|
||||
if err == nil && len(contents) > 0 && contents[len(contents)-1] != '\n' {
|
||||
_, err = combined.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to append to coverprofile:\n%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(destination, combined.Bytes(), 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create combined cover profile:\n%s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetCoverageFromCoverProfile(profile string) (float64, error) {
|
||||
cmd := exec.Command("go", "tool", "cover", "-func", profile)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Could not process Coverprofile %s: %s", profile, err.Error())
|
||||
}
|
||||
re := regexp.MustCompile(`total:\s*\(statements\)\s*(\d*\.\d*)\%`)
|
||||
matches := re.FindStringSubmatch(string(output))
|
||||
if matches == nil {
|
||||
return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage")
|
||||
}
|
||||
coverageString := matches[1]
|
||||
coverage, err := strconv.ParseFloat(coverageString, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Could not parse Coverprofile to compute coverage percentage: %s", err.Error())
|
||||
}
|
||||
|
||||
return coverage, nil
|
||||
}
|
||||
|
||||
func MergeProfiles(profilePaths []string, destination string) error {
|
||||
profiles := []*profile.Profile{}
|
||||
for _, profilePath := range profilePaths {
|
||||
proFile, err := os.Open(profilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not open profile: %s\n%s", profilePath, err.Error())
|
||||
}
|
||||
prof, err := profile.Parse(proFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not parse profile: %s\n%s", profilePath, err.Error())
|
||||
}
|
||||
profiles = append(profiles, prof)
|
||||
os.Remove(profilePath)
|
||||
}
|
||||
|
||||
mergedProfile, err := profile.Merge(profiles)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not merge profiles:\n%s", err.Error())
|
||||
}
|
||||
|
||||
outFile, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not create merged profile %s:\n%s", destination, err.Error())
|
||||
}
|
||||
err = mergedProfile.Write(outFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not write merged profile %s:\n%s", destination, err.Error())
|
||||
}
|
||||
err = outFile.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not close merged profile %s:\n%s", destination, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
348
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
generated
vendored
Normal file
348
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/internal/parallel_support"
|
||||
"github.com/onsi/ginkgo/v2/reporters"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func RunCompiledSuite(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite {
|
||||
suite.State = TestSuiteStateFailed
|
||||
suite.HasProgrammaticFocus = false
|
||||
|
||||
if suite.PathToCompiledTest == "" {
|
||||
return suite
|
||||
}
|
||||
|
||||
if suite.IsGinkgo && cliConfig.ComputedProcs() > 1 {
|
||||
suite = runParallel(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs)
|
||||
} else if suite.IsGinkgo {
|
||||
suite = runSerial(suite, ginkgoConfig, reporterConfig, cliConfig, goFlagsConfig, additionalArgs)
|
||||
} else {
|
||||
suite = runGoTest(suite, cliConfig, goFlagsConfig)
|
||||
}
|
||||
runAfterRunHook(cliConfig.AfterRunHook, reporterConfig.NoColor, suite)
|
||||
return suite
|
||||
}
|
||||
|
||||
func buildAndStartCommand(suite TestSuite, args []string, pipeToStdout bool) (*exec.Cmd, *bytes.Buffer) {
|
||||
buf := &bytes.Buffer{}
|
||||
cmd := exec.Command(suite.PathToCompiledTest, args...)
|
||||
cmd.Dir = suite.Path
|
||||
if pipeToStdout {
|
||||
cmd.Stderr = io.MultiWriter(os.Stdout, buf)
|
||||
cmd.Stdout = os.Stdout
|
||||
} else {
|
||||
cmd.Stderr = buf
|
||||
cmd.Stdout = buf
|
||||
}
|
||||
err := cmd.Start()
|
||||
command.AbortIfError("Failed to start test suite", err)
|
||||
|
||||
return cmd, buf
|
||||
}
|
||||
|
||||
func checkForNoTestsWarning(buf *bytes.Buffer) bool {
|
||||
if strings.Contains(buf.String(), "warning: no tests to run") {
|
||||
fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runGoTest(suite TestSuite, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) TestSuite {
|
||||
args, err := types.GenerateGoTestRunArgs(goFlagsConfig)
|
||||
command.AbortIfError("Failed to generate test run arguments", err)
|
||||
cmd, buf := buildAndStartCommand(suite, args, true)
|
||||
|
||||
cmd.Wait()
|
||||
|
||||
exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
|
||||
passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed
|
||||
if passed {
|
||||
suite.State = TestSuiteStatePassed
|
||||
} else {
|
||||
suite.State = TestSuiteStateFailed
|
||||
}
|
||||
|
||||
return suite
|
||||
}
|
||||
|
||||
func runSerial(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite {
|
||||
if goFlagsConfig.Cover {
|
||||
goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)
|
||||
}
|
||||
if goFlagsConfig.BlockProfile != "" {
|
||||
goFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0)
|
||||
}
|
||||
if goFlagsConfig.CPUProfile != "" {
|
||||
goFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0)
|
||||
}
|
||||
if goFlagsConfig.MemProfile != "" {
|
||||
goFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0)
|
||||
}
|
||||
if goFlagsConfig.MutexProfile != "" {
|
||||
goFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.TeamcityReport != "" {
|
||||
reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0)
|
||||
}
|
||||
|
||||
args, err := types.GenerateGinkgoTestRunArgs(ginkgoConfig, reporterConfig, goFlagsConfig)
|
||||
command.AbortIfError("Failed to generate test run arguments", err)
|
||||
args = append([]string{"--test.timeout=0"}, args...)
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
cmd, buf := buildAndStartCommand(suite, args, true)
|
||||
|
||||
cmd.Wait()
|
||||
|
||||
exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
suite.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
|
||||
passed := (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
|
||||
passed = !(checkForNoTestsWarning(buf) && cliConfig.RequireSuite) && passed
|
||||
if passed {
|
||||
suite.State = TestSuiteStatePassed
|
||||
} else {
|
||||
suite.State = TestSuiteStateFailed
|
||||
}
|
||||
|
||||
if suite.HasProgrammaticFocus {
|
||||
if goFlagsConfig.Cover {
|
||||
fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused")
|
||||
}
|
||||
if goFlagsConfig.BlockProfile != "" {
|
||||
fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused")
|
||||
}
|
||||
if goFlagsConfig.CPUProfile != "" {
|
||||
fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused")
|
||||
}
|
||||
if goFlagsConfig.MemProfile != "" {
|
||||
fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused")
|
||||
}
|
||||
if goFlagsConfig.MutexProfile != "" {
|
||||
fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused")
|
||||
}
|
||||
}
|
||||
|
||||
return suite
|
||||
}
|
||||
|
||||
func runParallel(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig types.ReporterConfig, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig, additionalArgs []string) TestSuite {
|
||||
type procResult struct {
|
||||
passed bool
|
||||
hasProgrammaticFocus bool
|
||||
}
|
||||
|
||||
numProcs := cliConfig.ComputedProcs()
|
||||
procOutput := make([]*bytes.Buffer, numProcs)
|
||||
coverProfiles := []string{}
|
||||
|
||||
blockProfiles := []string{}
|
||||
cpuProfiles := []string{}
|
||||
memProfiles := []string{}
|
||||
mutexProfiles := []string{}
|
||||
|
||||
procResults := make(chan procResult)
|
||||
|
||||
server, err := parallel_support.NewServer(numProcs, reporters.NewDefaultReporter(reporterConfig, formatter.ColorableStdOut))
|
||||
command.AbortIfError("Failed to start parallel spec server", err)
|
||||
server.Start()
|
||||
defer server.Close()
|
||||
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.TeamcityReport != "" {
|
||||
reporterConfig.TeamcityReport = AbsPathForGeneratedAsset(reporterConfig.TeamcityReport, suite, cliConfig, 0)
|
||||
}
|
||||
|
||||
for proc := 1; proc <= numProcs; proc++ {
|
||||
procGinkgoConfig := ginkgoConfig
|
||||
procGinkgoConfig.ParallelProcess, procGinkgoConfig.ParallelTotal, procGinkgoConfig.ParallelHost = proc, numProcs, server.Address()
|
||||
|
||||
procGoFlagsConfig := goFlagsConfig
|
||||
if goFlagsConfig.Cover {
|
||||
procGoFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, proc)
|
||||
coverProfiles = append(coverProfiles, procGoFlagsConfig.CoverProfile)
|
||||
}
|
||||
if goFlagsConfig.BlockProfile != "" {
|
||||
procGoFlagsConfig.BlockProfile = AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, proc)
|
||||
blockProfiles = append(blockProfiles, procGoFlagsConfig.BlockProfile)
|
||||
}
|
||||
if goFlagsConfig.CPUProfile != "" {
|
||||
procGoFlagsConfig.CPUProfile = AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, proc)
|
||||
cpuProfiles = append(cpuProfiles, procGoFlagsConfig.CPUProfile)
|
||||
}
|
||||
if goFlagsConfig.MemProfile != "" {
|
||||
procGoFlagsConfig.MemProfile = AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, proc)
|
||||
memProfiles = append(memProfiles, procGoFlagsConfig.MemProfile)
|
||||
}
|
||||
if goFlagsConfig.MutexProfile != "" {
|
||||
procGoFlagsConfig.MutexProfile = AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, proc)
|
||||
mutexProfiles = append(mutexProfiles, procGoFlagsConfig.MutexProfile)
|
||||
}
|
||||
|
||||
args, err := types.GenerateGinkgoTestRunArgs(procGinkgoConfig, reporterConfig, procGoFlagsConfig)
|
||||
command.AbortIfError("Failed to generate test run arguments", err)
|
||||
args = append([]string{"--test.timeout=0"}, args...)
|
||||
args = append(args, additionalArgs...)
|
||||
|
||||
cmd, buf := buildAndStartCommand(suite, args, false)
|
||||
procOutput[proc-1] = buf
|
||||
server.RegisterAlive(proc, func() bool { return cmd.ProcessState == nil || !cmd.ProcessState.Exited() })
|
||||
|
||||
go func() {
|
||||
cmd.Wait()
|
||||
exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
procResults <- procResult{
|
||||
passed: (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE),
|
||||
hasProgrammaticFocus: exitStatus == types.GINKGO_FOCUS_EXIT_CODE,
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
passed := true
|
||||
for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ {
|
||||
result := <-procResults
|
||||
passed = passed && result.passed
|
||||
suite.HasProgrammaticFocus = suite.HasProgrammaticFocus || result.hasProgrammaticFocus
|
||||
}
|
||||
if passed {
|
||||
suite.State = TestSuiteStatePassed
|
||||
} else {
|
||||
suite.State = TestSuiteStateFailed
|
||||
}
|
||||
|
||||
select {
|
||||
case <-server.GetSuiteDone():
|
||||
fmt.Println("")
|
||||
case <-time.After(time.Second):
|
||||
//one of the nodes never finished reporting to the server. Something must have gone wrong.
|
||||
fmt.Fprint(formatter.ColorableStdErr, formatter.F("\n{{bold}}{{red}}Ginkgo timed out waiting for all parallel procs to report back{{/}}\n"))
|
||||
fmt.Fprint(formatter.ColorableStdErr, formatter.F("{{gray}}Test suite:{{/}} %s (%s)\n\n", suite.PackageName, suite.Path))
|
||||
fmt.Fprint(formatter.ColorableStdErr, formatter.Fiw(0, formatter.COLS, "This occurs if a parallel process exits before it reports its results to the Ginkgo CLI. The CLI will now print out all the stdout/stderr output it's collected from the running processes. However you may not see anything useful in these logs because the individual test processes usually intercept output to stdout/stderr in order to capture it in the spec reports.\n\nYou may want to try rerunning your test suite with {{light-gray}}--output-interceptor-mode=none{{/}} to see additional output here and debug your suite.\n"))
|
||||
fmt.Fprintln(formatter.ColorableStdErr, " ")
|
||||
for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ {
|
||||
fmt.Fprintf(formatter.ColorableStdErr, formatter.F("{{bold}}Output from proc %d:{{/}}\n", proc))
|
||||
fmt.Fprintln(os.Stderr, formatter.Fi(1, "%s", procOutput[proc-1].String()))
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "** End **")
|
||||
}
|
||||
|
||||
for proc := 1; proc <= cliConfig.ComputedProcs(); proc++ {
|
||||
output := procOutput[proc-1].String()
|
||||
if proc == 1 && checkForNoTestsWarning(procOutput[0]) && cliConfig.RequireSuite {
|
||||
suite.State = TestSuiteStateFailed
|
||||
}
|
||||
if strings.Contains(output, "deprecated Ginkgo functionality") {
|
||||
fmt.Fprintln(os.Stderr, output)
|
||||
}
|
||||
}
|
||||
|
||||
if len(coverProfiles) > 0 {
|
||||
if suite.HasProgrammaticFocus {
|
||||
fmt.Fprintln(os.Stdout, "coverage: no coverfile was generated because specs are programmatically focused")
|
||||
} else {
|
||||
coverProfile := AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)
|
||||
err := MergeAndCleanupCoverProfiles(coverProfiles, coverProfile)
|
||||
command.AbortIfError("Failed to combine cover profiles", err)
|
||||
|
||||
coverage, err := GetCoverageFromCoverProfile(coverProfile)
|
||||
command.AbortIfError("Failed to compute coverage", err)
|
||||
if coverage == 0 {
|
||||
fmt.Fprintln(os.Stdout, "coverage: [no statements]")
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, "coverage: %.1f%% of statements\n", coverage)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(blockProfiles) > 0 {
|
||||
if suite.HasProgrammaticFocus {
|
||||
fmt.Fprintln(os.Stdout, "no block profile was generated because specs are programmatically focused")
|
||||
} else {
|
||||
blockProfile := AbsPathForGeneratedAsset(goFlagsConfig.BlockProfile, suite, cliConfig, 0)
|
||||
err := MergeProfiles(blockProfiles, blockProfile)
|
||||
command.AbortIfError("Failed to combine blockprofiles", err)
|
||||
}
|
||||
}
|
||||
if len(cpuProfiles) > 0 {
|
||||
if suite.HasProgrammaticFocus {
|
||||
fmt.Fprintln(os.Stdout, "no cpu profile was generated because specs are programmatically focused")
|
||||
} else {
|
||||
cpuProfile := AbsPathForGeneratedAsset(goFlagsConfig.CPUProfile, suite, cliConfig, 0)
|
||||
err := MergeProfiles(cpuProfiles, cpuProfile)
|
||||
command.AbortIfError("Failed to combine cpuprofiles", err)
|
||||
}
|
||||
}
|
||||
if len(memProfiles) > 0 {
|
||||
if suite.HasProgrammaticFocus {
|
||||
fmt.Fprintln(os.Stdout, "no mem profile was generated because specs are programmatically focused")
|
||||
} else {
|
||||
memProfile := AbsPathForGeneratedAsset(goFlagsConfig.MemProfile, suite, cliConfig, 0)
|
||||
err := MergeProfiles(memProfiles, memProfile)
|
||||
command.AbortIfError("Failed to combine memprofiles", err)
|
||||
}
|
||||
}
|
||||
if len(mutexProfiles) > 0 {
|
||||
if suite.HasProgrammaticFocus {
|
||||
fmt.Fprintln(os.Stdout, "no mutex profile was generated because specs are programmatically focused")
|
||||
} else {
|
||||
mutexProfile := AbsPathForGeneratedAsset(goFlagsConfig.MutexProfile, suite, cliConfig, 0)
|
||||
err := MergeProfiles(mutexProfiles, mutexProfile)
|
||||
command.AbortIfError("Failed to combine mutexprofiles", err)
|
||||
}
|
||||
}
|
||||
|
||||
return suite
|
||||
}
|
||||
|
||||
func runAfterRunHook(command string, noColor bool, suite TestSuite) {
|
||||
if command == "" {
|
||||
return
|
||||
}
|
||||
f := formatter.NewWithNoColorBool(noColor)
|
||||
|
||||
// Allow for string replacement to pass input to the command
|
||||
passed := "[FAIL]"
|
||||
if suite.State.Is(TestSuiteStatePassed) {
|
||||
passed = "[PASS]"
|
||||
}
|
||||
command = strings.ReplaceAll(command, "(ginkgo-suite-passed)", passed)
|
||||
command = strings.ReplaceAll(command, "(ginkgo-suite-name)", suite.PackageName)
|
||||
|
||||
// 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.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{red}}{{bold}}After-run-hook failed:{{/}}"))
|
||||
fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{red}}%s{{/}}", output))
|
||||
} else {
|
||||
fmt.Fprintln(formatter.ColorableStdOut, f.Fi(0, "{{green}}{{bold}}After-run-hook succeeded:{{/}}"))
|
||||
fmt.Fprintln(formatter.ColorableStdOut, f.Fi(1, "{{green}}%s{{/}}", output))
|
||||
}
|
||||
}
|
283
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
Normal file
283
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
const TIMEOUT_ELAPSED_FAILURE_REASON = "Suite did not run because the timeout elapsed"
|
||||
const PRIOR_FAILURES_FAILURE_REASON = "Suite did not run because prior suites failed and --keep-going is not set"
|
||||
const EMPTY_SKIP_FAILURE_REASON = "Suite did not run go test reported that no test files were found"
|
||||
|
||||
type TestSuiteState uint
|
||||
|
||||
const (
|
||||
TestSuiteStateInvalid TestSuiteState = iota
|
||||
|
||||
TestSuiteStateUncompiled
|
||||
TestSuiteStateCompiled
|
||||
|
||||
TestSuiteStatePassed
|
||||
|
||||
TestSuiteStateSkippedDueToEmptyCompilation
|
||||
TestSuiteStateSkippedByFilter
|
||||
TestSuiteStateSkippedDueToPriorFailures
|
||||
|
||||
TestSuiteStateFailed
|
||||
TestSuiteStateFailedDueToTimeout
|
||||
TestSuiteStateFailedToCompile
|
||||
)
|
||||
|
||||
var TestSuiteStateFailureStates = []TestSuiteState{TestSuiteStateFailed, TestSuiteStateFailedDueToTimeout, TestSuiteStateFailedToCompile}
|
||||
|
||||
func (state TestSuiteState) Is(states ...TestSuiteState) bool {
|
||||
for _, suiteState := range states {
|
||||
if suiteState == state {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type TestSuite struct {
|
||||
Path string
|
||||
PackageName string
|
||||
IsGinkgo bool
|
||||
|
||||
Precompiled bool
|
||||
PathToCompiledTest string
|
||||
CompilationError error
|
||||
|
||||
HasProgrammaticFocus bool
|
||||
State TestSuiteState
|
||||
}
|
||||
|
||||
func (ts TestSuite) AbsPath() string {
|
||||
path, _ := filepath.Abs(ts.Path)
|
||||
return path
|
||||
}
|
||||
|
||||
func (ts TestSuite) NamespacedName() string {
|
||||
name := relPath(ts.Path)
|
||||
name = strings.TrimLeft(name, "."+string(filepath.Separator))
|
||||
name = strings.ReplaceAll(name, string(filepath.Separator), "_")
|
||||
name = strings.ReplaceAll(name, " ", "_")
|
||||
if name == "" {
|
||||
return ts.PackageName
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
type TestSuites []TestSuite
|
||||
|
||||
func (ts TestSuites) AnyHaveProgrammaticFocus() bool {
|
||||
for _, suite := range ts {
|
||||
if suite.HasProgrammaticFocus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ts TestSuites) ThatAreGinkgoSuites() TestSuites {
|
||||
out := TestSuites{}
|
||||
for _, suite := range ts {
|
||||
if suite.IsGinkgo {
|
||||
out = append(out, suite)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (ts TestSuites) CountWithState(states ...TestSuiteState) int {
|
||||
n := 0
|
||||
for _, suite := range ts {
|
||||
if suite.State.Is(states...) {
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (ts TestSuites) WithState(states ...TestSuiteState) TestSuites {
|
||||
out := TestSuites{}
|
||||
for _, suite := range ts {
|
||||
if suite.State.Is(states...) {
|
||||
out = append(out, suite)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (ts TestSuites) WithoutState(states ...TestSuiteState) TestSuites {
|
||||
out := TestSuites{}
|
||||
for _, suite := range ts {
|
||||
if !suite.State.Is(states...) {
|
||||
out = append(out, suite)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (ts TestSuites) ShuffledCopy(seed int64) TestSuites {
|
||||
out := make(TestSuites, len(ts))
|
||||
permutation := rand.New(rand.NewSource(seed)).Perm(len(ts))
|
||||
for i, j := range permutation {
|
||||
out[i] = ts[j]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func FindSuites(args []string, cliConfig types.CLIConfig, allowPrecompiled bool) TestSuites {
|
||||
suites := TestSuites{}
|
||||
|
||||
if len(args) > 0 {
|
||||
for _, arg := range args {
|
||||
if allowPrecompiled {
|
||||
suite, err := precompiledTestSuite(arg)
|
||||
if err == nil {
|
||||
suites = append(suites, suite)
|
||||
continue
|
||||
}
|
||||
}
|
||||
recurseForSuite := cliConfig.Recurse
|
||||
if strings.HasSuffix(arg, "/...") && arg != "/..." {
|
||||
arg = arg[:len(arg)-4]
|
||||
recurseForSuite = true
|
||||
}
|
||||
suites = append(suites, suitesInDir(arg, recurseForSuite)...)
|
||||
}
|
||||
} else {
|
||||
suites = suitesInDir(".", cliConfig.Recurse)
|
||||
}
|
||||
|
||||
if cliConfig.SkipPackage != "" {
|
||||
skipFilters := strings.Split(cliConfig.SkipPackage, ",")
|
||||
for idx := range suites {
|
||||
for _, skipFilter := range skipFilters {
|
||||
if strings.Contains(suites[idx].Path, skipFilter) {
|
||||
suites[idx].State = TestSuiteStateSkippedByFilter
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suites
|
||||
}
|
||||
|
||||
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" && filepath.Ext(path) != ".exe" {
|
||||
return TestSuite{}, errors.New("this is not a .test binary")
|
||||
}
|
||||
|
||||
if filepath.Ext(path) == ".test" && info.Mode()&0111 == 0 {
|
||||
return TestSuite{}, errors.New("this is not executable")
|
||||
}
|
||||
|
||||
dir := relPath(filepath.Dir(path))
|
||||
packageName := strings.TrimSuffix(filepath.Base(path), ".exe")
|
||||
packageName = strings.TrimSuffix(packageName, ".test")
|
||||
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
return TestSuite{}, err
|
||||
}
|
||||
|
||||
return TestSuite{
|
||||
Path: dir,
|
||||
PackageName: packageName,
|
||||
IsGinkgo: true,
|
||||
Precompiled: true,
|
||||
PathToCompiledTest: path,
|
||||
State: TestSuiteStateCompiled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func suitesInDir(dir string, recurse bool) TestSuites {
|
||||
suites := TestSuites{}
|
||||
|
||||
if path.Base(dir) == "vendor" {
|
||||
return suites
|
||||
}
|
||||
|
||||
files, _ := os.ReadDir(dir)
|
||||
re := regexp.MustCompile(`^[^._].*_test\.go$`)
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && re.Match([]byte(file.Name())) {
|
||||
suite := TestSuite{
|
||||
Path: relPath(dir),
|
||||
PackageName: packageNameForSuite(dir),
|
||||
IsGinkgo: filesHaveGinkgoSuite(dir, files),
|
||||
State: TestSuiteStateUncompiled,
|
||||
}
|
||||
suites = append(suites, suite)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if recurse {
|
||||
re = regexp.MustCompile(`^[._]`)
|
||||
for _, file := range files {
|
||||
if file.IsDir() && !re.Match([]byte(file.Name())) {
|
||||
suites = append(suites, suitesInDir(dir+"/"+file.Name(), recurse)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suites
|
||||
}
|
||||
|
||||
func relPath(dir string) string {
|
||||
dir, _ = filepath.Abs(dir)
|
||||
cwd, _ := os.Getwd()
|
||||
dir, _ = filepath.Rel(cwd, filepath.Clean(dir))
|
||||
|
||||
if string(dir[0]) != "." {
|
||||
dir = "." + string(filepath.Separator) + dir
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func packageNameForSuite(dir string) string {
|
||||
path, _ := filepath.Abs(dir)
|
||||
return filepath.Base(path)
|
||||
}
|
||||
|
||||
func filesHaveGinkgoSuite(dir string, files []os.DirEntry) bool {
|
||||
reTestFile := regexp.MustCompile(`_test\.go$`)
|
||||
reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"|\/ginkgo\/v2"|\/ginkgo\/v2/dsl/`)
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
|
||||
contents, _ := os.ReadFile(dir + "/" + file.Name())
|
||||
if reGinkgo.Match(contents) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
86
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go
generated
vendored
Normal file
86
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
)
|
||||
|
||||
func FileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func CopyFile(src string, dest string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcStat, err := srcFile.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dest); err == nil {
|
||||
os.Remove(dest)
|
||||
}
|
||||
|
||||
destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, srcStat.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(destFile, srcFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := srcFile.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return destFile.Close()
|
||||
}
|
||||
|
||||
func GoFmt(path string) {
|
||||
out, err := exec.Command("go", "fmt", path).CombinedOutput()
|
||||
if err != nil {
|
||||
command.AbortIfError(fmt.Sprintf("Could not fmt:\n%s\n", string(out)), err)
|
||||
}
|
||||
}
|
||||
|
||||
func PluralizedWord(singular, plural string, count int) string {
|
||||
if count == 1 {
|
||||
return singular
|
||||
}
|
||||
return plural
|
||||
}
|
||||
|
||||
func FailedSuitesReport(suites TestSuites, f formatter.Formatter) string {
|
||||
out := ""
|
||||
out += "There were failures detected in the following suites:\n"
|
||||
|
||||
maxPackageNameLength := 0
|
||||
for _, suite := range suites.WithState(TestSuiteStateFailureStates...) {
|
||||
if len(suite.PackageName) > maxPackageNameLength {
|
||||
maxPackageNameLength = len(suite.PackageName)
|
||||
}
|
||||
}
|
||||
|
||||
packageNameFormatter := fmt.Sprintf("%%%ds", maxPackageNameLength)
|
||||
for _, suite := range suites {
|
||||
switch suite.State {
|
||||
case TestSuiteStateFailed:
|
||||
out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s{{/}}\n", suite.PackageName, suite.Path)
|
||||
case TestSuiteStateFailedToCompile:
|
||||
out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{magenta}}[Compilation failure]{{/}}\n", suite.PackageName, suite.Path)
|
||||
case TestSuiteStateFailedDueToTimeout:
|
||||
out += f.Fi(1, "{{red}}"+packageNameFormatter+" {{gray}}%s {{orange}}[%s]{{/}}\n", suite.PackageName, suite.Path, TIMEOUT_ELAPSED_FAILURE_REASON)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
54
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go
generated
vendored
Normal file
54
vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
var versiorRe = regexp.MustCompile(`v(\d+\.\d+\.\d+)`)
|
||||
|
||||
func VerifyCLIAndFrameworkVersion(suites TestSuites) {
|
||||
cliVersion := types.VERSION
|
||||
mismatches := map[string][]string{}
|
||||
|
||||
for _, suite := range suites {
|
||||
cmd := exec.Command("go", "list", "-m", "github.com/onsi/ginkgo/v2")
|
||||
cmd.Dir = suite.Path
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
components := strings.Split(string(output), " ")
|
||||
if len(components) != 2 {
|
||||
continue
|
||||
}
|
||||
matches := versiorRe.FindStringSubmatch(components[1])
|
||||
if matches == nil || len(matches) != 2 {
|
||||
continue
|
||||
}
|
||||
libraryVersion := matches[1]
|
||||
if cliVersion != libraryVersion {
|
||||
mismatches[libraryVersion] = append(mismatches[libraryVersion], suite.PackageName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(mismatches) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(formatter.F("{{red}}{{bold}}Ginkgo detected a version mismatch between the Ginkgo CLI and the version of Ginkgo imported by your packages:{{/}}"))
|
||||
|
||||
fmt.Println(formatter.Fi(1, "Ginkgo CLI Version:"))
|
||||
fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}}", cliVersion))
|
||||
fmt.Println(formatter.Fi(1, "Mismatched package versions found:"))
|
||||
for version, packages := range mismatches {
|
||||
fmt.Println(formatter.Fi(2, "{{bold}}%s{{/}} used by %s", version, strings.Join(packages, ", ")))
|
||||
}
|
||||
fmt.Println("")
|
||||
fmt.Println(formatter.Fiw(1, formatter.COLS, "{{gray}}Ginkgo will continue to attempt to run but you may see errors (including flag parsing errors) and should either update your go.mod or your version of the Ginkgo CLI to match.\n\nTo install the matching version of the CLI run\n {{bold}}go install github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file. Alternatively you can use\n {{bold}}go run github.com/onsi/ginkgo/v2/ginkgo{{/}}{{gray}}\nfrom a path that contains a go.mod file to invoke the matching version of the Ginkgo CLI.\n\nIf you are attempting to test multiple packages that each have a different version of the Ginkgo library with a single Ginkgo CLI that is currently unsupported.\n{{/}}"))
|
||||
}
|
123
vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go
generated
vendored
Normal file
123
vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package labels
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
func BuildLabelsCommand() command.Command {
|
||||
var cliConfig = types.NewDefaultCLIConfig()
|
||||
|
||||
flags, err := types.BuildLabelsCommandFlagSet(&cliConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return command.Command{
|
||||
Name: "labels",
|
||||
Usage: "ginkgo labels <FLAGS> <PACKAGES>",
|
||||
Flags: flags,
|
||||
ShortDoc: "List labels detected in the passed-in packages (or the package in the current directory if left blank).",
|
||||
DocLink: "spec-labels",
|
||||
Command: func(args []string, _ []string) {
|
||||
ListLabels(args, cliConfig)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ListLabels(args []string, cliConfig types.CLIConfig) {
|
||||
suites := internal.FindSuites(args, cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter)
|
||||
if len(suites) == 0 {
|
||||
command.AbortWith("Found no test suites")
|
||||
}
|
||||
for _, suite := range suites {
|
||||
labels := fetchLabelsFromPackage(suite.Path)
|
||||
if len(labels) == 0 {
|
||||
fmt.Printf("%s: No labels found\n", suite.PackageName)
|
||||
} else {
|
||||
fmt.Printf("%s: [%s]\n", suite.PackageName, strings.Join(labels, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchLabelsFromPackage(packagePath string) []string {
|
||||
fset := token.NewFileSet()
|
||||
parsedPackages, err := parser.ParseDir(fset, packagePath, nil, 0)
|
||||
command.AbortIfError("Failed to parse package source:", err)
|
||||
|
||||
files := []*ast.File{}
|
||||
hasTestPackage := false
|
||||
for key, pkg := range parsedPackages {
|
||||
if strings.HasSuffix(key, "_test") {
|
||||
hasTestPackage = true
|
||||
for _, file := range pkg.Files {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasTestPackage {
|
||||
for _, pkg := range parsedPackages {
|
||||
for _, file := range pkg.Files {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
labels := []string{}
|
||||
ispr := inspector.New(files)
|
||||
ispr.Preorder([]ast.Node{&ast.CallExpr{}}, func(n ast.Node) {
|
||||
potentialLabels := fetchLabels(n.(*ast.CallExpr))
|
||||
for _, label := range potentialLabels {
|
||||
if !seen[label] {
|
||||
seen[label] = true
|
||||
labels = append(labels, strconv.Quote(label))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(labels)
|
||||
return labels
|
||||
}
|
||||
|
||||
func fetchLabels(callExpr *ast.CallExpr) []string {
|
||||
out := []string{}
|
||||
switch expr := callExpr.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
if expr.Name != "Label" {
|
||||
return out
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
if expr.Sel.Name != "Label" {
|
||||
return out
|
||||
}
|
||||
default:
|
||||
return out
|
||||
}
|
||||
for _, arg := range callExpr.Args {
|
||||
switch expr := arg.(type) {
|
||||
case *ast.BasicLit:
|
||||
if expr.Kind == token.STRING {
|
||||
unquoted, err := strconv.Unquote(expr.Value)
|
||||
if err != nil {
|
||||
unquoted = expr.Value
|
||||
}
|
||||
validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{})
|
||||
if err == nil {
|
||||
out = append(out, validated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
58
vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go
generated
vendored
Normal file
58
vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/build"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/generators"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/labels"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/outline"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/run"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/unfocus"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/watch"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
var program command.Program
|
||||
|
||||
func GenerateCommands() []command.Command {
|
||||
return []command.Command{
|
||||
watch.BuildWatchCommand(),
|
||||
build.BuildBuildCommand(),
|
||||
generators.BuildBootstrapCommand(),
|
||||
generators.BuildGenerateCommand(),
|
||||
labels.BuildLabelsCommand(),
|
||||
outline.BuildOutlineCommand(),
|
||||
unfocus.BuildUnfocusCommand(),
|
||||
BuildVersionCommand(),
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
program = command.Program{
|
||||
Name: "ginkgo",
|
||||
Heading: fmt.Sprintf("Ginkgo Version %s", types.VERSION),
|
||||
Commands: GenerateCommands(),
|
||||
DefaultCommand: run.BuildRunCommand(),
|
||||
DeprecatedCommands: []command.DeprecatedCommand{
|
||||
{Name: "convert", Deprecation: types.Deprecations.Convert()},
|
||||
{Name: "blur", Deprecation: types.Deprecations.Blur()},
|
||||
{Name: "nodot", Deprecation: types.Deprecations.Nodot()},
|
||||
},
|
||||
}
|
||||
|
||||
program.RunAndExit(os.Args)
|
||||
}
|
||||
|
||||
func BuildVersionCommand() command.Command {
|
||||
return command.Command{
|
||||
Name: "version",
|
||||
Usage: "ginkgo version",
|
||||
ShortDoc: "Print Ginkgo's version",
|
||||
Command: func(_ []string, _ []string) {
|
||||
fmt.Printf("Ginkgo Version %s\n", types.VERSION)
|
||||
},
|
||||
}
|
||||
}
|
218
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
Normal file
218
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// undefinedTextAlt is used if the spec/container text cannot be derived
|
||||
undefinedTextAlt = "undefined"
|
||||
)
|
||||
|
||||
// ginkgoMetadata holds useful bits of information for every entry in the outline
|
||||
type ginkgoMetadata struct {
|
||||
// Name is the spec or container function name, e.g. `Describe` or `It`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Text is the `text` argument passed to specs, and some containers
|
||||
Text string `json:"text"`
|
||||
|
||||
// Start is the position of first character of the spec or container block
|
||||
Start int `json:"start"`
|
||||
|
||||
// End is the position of first character immediately after the spec or container block
|
||||
End int `json:"end"`
|
||||
|
||||
Spec bool `json:"spec"`
|
||||
Focused bool `json:"focused"`
|
||||
Pending bool `json:"pending"`
|
||||
}
|
||||
|
||||
// ginkgoNode is used to construct the outline as a tree
|
||||
type ginkgoNode struct {
|
||||
ginkgoMetadata
|
||||
Nodes []*ginkgoNode `json:"nodes"`
|
||||
}
|
||||
|
||||
type walkFunc func(n *ginkgoNode)
|
||||
|
||||
func (n *ginkgoNode) PreOrder(f walkFunc) {
|
||||
f(n)
|
||||
for _, m := range n.Nodes {
|
||||
m.PreOrder(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ginkgoNode) PostOrder(f walkFunc) {
|
||||
for _, m := range n.Nodes {
|
||||
m.PostOrder(f)
|
||||
}
|
||||
f(n)
|
||||
}
|
||||
|
||||
func (n *ginkgoNode) Walk(pre, post walkFunc) {
|
||||
pre(n)
|
||||
for _, m := range n.Nodes {
|
||||
m.Walk(pre, post)
|
||||
}
|
||||
post(n)
|
||||
}
|
||||
|
||||
// PropagateInheritedProperties propagates the Pending and Focused properties
|
||||
// through the subtree rooted at n.
|
||||
func (n *ginkgoNode) PropagateInheritedProperties() {
|
||||
n.PreOrder(func(thisNode *ginkgoNode) {
|
||||
for _, descendantNode := range thisNode.Nodes {
|
||||
if thisNode.Pending {
|
||||
descendantNode.Pending = true
|
||||
descendantNode.Focused = false
|
||||
}
|
||||
if thisNode.Focused && !descendantNode.Pending {
|
||||
descendantNode.Focused = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BackpropagateUnfocus propagates the Focused property through the subtree
|
||||
// rooted at n. It applies the rule described in the Ginkgo docs:
|
||||
// > Nested programmatically focused specs follow a simple rule: if a
|
||||
// > leaf-node is marked focused, any of its ancestor nodes that are marked
|
||||
// > focus will be unfocused.
|
||||
func (n *ginkgoNode) BackpropagateUnfocus() {
|
||||
focusedSpecInSubtreeStack := []bool{}
|
||||
n.PostOrder(func(thisNode *ginkgoNode) {
|
||||
if thisNode.Spec {
|
||||
focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, thisNode.Focused)
|
||||
return
|
||||
}
|
||||
focusedSpecInSubtree := false
|
||||
for range thisNode.Nodes {
|
||||
focusedSpecInSubtree = focusedSpecInSubtree || focusedSpecInSubtreeStack[len(focusedSpecInSubtreeStack)-1]
|
||||
focusedSpecInSubtreeStack = focusedSpecInSubtreeStack[0 : len(focusedSpecInSubtreeStack)-1]
|
||||
}
|
||||
focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, focusedSpecInSubtree)
|
||||
if focusedSpecInSubtree {
|
||||
thisNode.Focused = false
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func packageAndIdentNamesFromCallExpr(ce *ast.CallExpr) (string, string, bool) {
|
||||
switch ex := ce.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
return "", ex.Name, true
|
||||
case *ast.SelectorExpr:
|
||||
pkgID, ok := ex.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
// A package identifier is top-level, so Obj must be nil
|
||||
if pkgID.Obj != nil {
|
||||
return "", "", false
|
||||
}
|
||||
if ex.Sel == nil {
|
||||
return "", "", false
|
||||
}
|
||||
return pkgID.Name, ex.Sel.Name, true
|
||||
default:
|
||||
return "", "", false
|
||||
}
|
||||
}
|
||||
|
||||
// absoluteOffsetsForNode derives the absolute character offsets of the node start and
|
||||
// end positions.
|
||||
func absoluteOffsetsForNode(fset *token.FileSet, n ast.Node) (start, end int) {
|
||||
return fset.PositionFor(n.Pos(), false).Offset, fset.PositionFor(n.End(), false).Offset
|
||||
}
|
||||
|
||||
// ginkgoNodeFromCallExpr derives an outline entry from a go AST subtree
|
||||
// corresponding to a Ginkgo container or spec.
|
||||
func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackageName *string) (*ginkgoNode, bool) {
|
||||
packageName, identName, ok := packageAndIdentNamesFromCallExpr(ce)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
n := ginkgoNode{}
|
||||
n.Name = identName
|
||||
n.Start, n.End = absoluteOffsetsForNode(fset, ce)
|
||||
n.Nodes = make([]*ginkgoNode, 0)
|
||||
switch identName {
|
||||
case "It", "Specify", "Entry":
|
||||
n.Spec = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "FIt", "FSpecify", "FEntry":
|
||||
n.Spec = true
|
||||
n.Focused = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "PIt", "PSpecify", "XIt", "XSpecify", "PEntry", "XEntry":
|
||||
n.Spec = true
|
||||
n.Pending = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "Context", "Describe", "When", "DescribeTable":
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "FContext", "FDescribe", "FWhen", "FDescribeTable":
|
||||
n.Focused = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable":
|
||||
n.Pending = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "By":
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "AfterEach", "BeforeEach":
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "JustAfterEach", "JustBeforeEach":
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "AfterSuite", "BeforeSuite":
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "SynchronizedAfterSuite", "SynchronizedBeforeSuite":
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// textOrAltFromCallExpr tries to derive the "text" of a Ginkgo spec or
|
||||
// container. If it cannot derive it, it returns the alt text.
|
||||
func textOrAltFromCallExpr(ce *ast.CallExpr, alt string) string {
|
||||
text, defined := textFromCallExpr(ce)
|
||||
if !defined {
|
||||
return alt
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// textFromCallExpr tries to derive the "text" of a Ginkgo spec or container. If
|
||||
// it cannot derive it, it returns false.
|
||||
func textFromCallExpr(ce *ast.CallExpr) (string, bool) {
|
||||
if len(ce.Args) < 1 {
|
||||
return "", false
|
||||
}
|
||||
text, ok := ce.Args[0].(*ast.BasicLit)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
switch text.Kind {
|
||||
case token.CHAR, token.STRING:
|
||||
// For token.CHAR and token.STRING, Value is quoted
|
||||
unquoted, err := strconv.Unquote(text.Value)
|
||||
if err != nil {
|
||||
// If unquoting fails, just use the raw Value
|
||||
return text.Value, true
|
||||
}
|
||||
return unquoted, true
|
||||
default:
|
||||
return text.Value, true
|
||||
}
|
||||
}
|
65
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
Normal file
65
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Most of the required functions were available in the
|
||||
// "golang.org/x/tools/go/ast/astutil" package, but not exported.
|
||||
// They were copied from https://github.com/golang/tools/blob/2b0845dc783e36ae26d683f4915a5840ef01ab0f/go/ast/astutil/imports.go
|
||||
|
||||
package outline
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// packageNameForImport returns the package name for the package. If the package
|
||||
// is not imported, it returns nil. "Package name" refers to `pkgname` in the
|
||||
// call expression `pkgname.ExportedIdentifier`. Examples:
|
||||
// (import path not found) -> nil
|
||||
// "import example.com/pkg/foo" -> "foo"
|
||||
// "import fooalias example.com/pkg/foo" -> "fooalias"
|
||||
// "import . example.com/pkg/foo" -> ""
|
||||
func packageNameForImport(f *ast.File, path string) *string {
|
||||
spec := importSpec(f, path)
|
||||
if spec == nil {
|
||||
return nil
|
||||
}
|
||||
name := spec.Name.String()
|
||||
if name == "<nil>" {
|
||||
// If the package name is not explicitly specified,
|
||||
// make an educated guess. This is not guaranteed to be correct.
|
||||
lastSlash := strings.LastIndex(path, "/")
|
||||
if lastSlash == -1 {
|
||||
name = path
|
||||
} else {
|
||||
name = path[lastSlash+1:]
|
||||
}
|
||||
}
|
||||
if name == "." {
|
||||
name = ""
|
||||
}
|
||||
return &name
|
||||
}
|
||||
|
||||
// importSpec returns the import spec if f imports path,
|
||||
// or nil otherwise.
|
||||
func importSpec(f *ast.File, path string) *ast.ImportSpec {
|
||||
for _, s := range f.Imports {
|
||||
if strings.HasPrefix(importPath(s), path) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// importPath returns the unquoted import path of s,
|
||||
// or "" if the path is not properly quoted.
|
||||
func importPath(s *ast.ImportSpec) string {
|
||||
t, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return t
|
||||
}
|
103
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
Normal file
103
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const (
|
||||
// ginkgoImportPath is the well-known ginkgo import path
|
||||
ginkgoImportPath = "github.com/onsi/ginkgo/v2"
|
||||
)
|
||||
|
||||
// FromASTFile returns an outline for a Ginkgo test source file
|
||||
func FromASTFile(fset *token.FileSet, src *ast.File) (*outline, error) {
|
||||
ginkgoPackageName := packageNameForImport(src, ginkgoImportPath)
|
||||
if ginkgoPackageName == nil {
|
||||
return nil, fmt.Errorf("file does not import %q", ginkgoImportPath)
|
||||
}
|
||||
|
||||
root := ginkgoNode{}
|
||||
stack := []*ginkgoNode{&root}
|
||||
ispr := inspector.New([]*ast.File{src})
|
||||
ispr.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool) bool {
|
||||
if push {
|
||||
// Pre-order traversal
|
||||
ce, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
// Because `Nodes` calls this function only when the node is an
|
||||
// ast.CallExpr, this should never happen
|
||||
panic(fmt.Errorf("node starting at %d, ending at %d is not an *ast.CallExpr", node.Pos(), node.End()))
|
||||
}
|
||||
gn, ok := ginkgoNodeFromCallExpr(fset, ce, ginkgoPackageName)
|
||||
if !ok {
|
||||
// Node is not a Ginkgo spec or container, continue
|
||||
return true
|
||||
}
|
||||
parent := stack[len(stack)-1]
|
||||
parent.Nodes = append(parent.Nodes, gn)
|
||||
stack = append(stack, gn)
|
||||
return true
|
||||
}
|
||||
// Post-order traversal
|
||||
start, end := absoluteOffsetsForNode(fset, node)
|
||||
lastVisitedGinkgoNode := stack[len(stack)-1]
|
||||
if start != lastVisitedGinkgoNode.Start || end != lastVisitedGinkgoNode.End {
|
||||
// Node is not a Ginkgo spec or container, so it was not pushed onto the stack, continue
|
||||
return true
|
||||
}
|
||||
stack = stack[0 : len(stack)-1]
|
||||
return true
|
||||
})
|
||||
if len(root.Nodes) == 0 {
|
||||
return &outline{[]*ginkgoNode{}}, nil
|
||||
}
|
||||
|
||||
// Derive the final focused property for all nodes. This must be done
|
||||
// _before_ propagating the inherited focused property.
|
||||
root.BackpropagateUnfocus()
|
||||
// Now, propagate inherited properties, including focused and pending.
|
||||
root.PropagateInheritedProperties()
|
||||
|
||||
return &outline{root.Nodes}, nil
|
||||
}
|
||||
|
||||
type outline struct {
|
||||
Nodes []*ginkgoNode `json:"nodes"`
|
||||
}
|
||||
|
||||
func (o *outline) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(o.Nodes)
|
||||
}
|
||||
|
||||
// String returns a CSV-formatted outline. Spec or container are output in
|
||||
// depth-first order.
|
||||
func (o *outline) String() string {
|
||||
return o.StringIndent(0)
|
||||
}
|
||||
|
||||
// StringIndent returns a CSV-formated outline, but every line is indented by
|
||||
// one 'width' of spaces for every level of nesting.
|
||||
func (o *outline) StringIndent(width int) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("Name,Text,Start,End,Spec,Focused,Pending\n")
|
||||
|
||||
currentIndent := 0
|
||||
pre := func(n *ginkgoNode) {
|
||||
b.WriteString(fmt.Sprintf("%*s", currentIndent, ""))
|
||||
b.WriteString(fmt.Sprintf("%s,%s,%d,%d,%t,%t,%t\n", n.Name, n.Text, n.Start, n.End, n.Spec, n.Focused, n.Pending))
|
||||
currentIndent += width
|
||||
}
|
||||
post := func(n *ginkgoNode) {
|
||||
currentIndent -= width
|
||||
}
|
||||
for _, n := range o.Nodes {
|
||||
n.Walk(pre, post)
|
||||
}
|
||||
return b.String()
|
||||
}
|
98
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go
generated
vendored
Normal file
98
vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// indentWidth is the width used by the 'indent' output
|
||||
indentWidth = 4
|
||||
// stdinAlias is a portable alias for stdin. This convention is used in
|
||||
// other CLIs, e.g., kubectl.
|
||||
stdinAlias = "-"
|
||||
usageCommand = "ginkgo outline <filename>"
|
||||
)
|
||||
|
||||
type outlineConfig struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func BuildOutlineCommand() command.Command {
|
||||
conf := outlineConfig{
|
||||
Format: "csv",
|
||||
}
|
||||
flags, err := types.NewGinkgoFlagSet(
|
||||
types.GinkgoFlags{
|
||||
{Name: "format", KeyPath: "Format",
|
||||
Usage: "Format of outline",
|
||||
UsageArgument: "one of 'csv', 'indent', or 'json'",
|
||||
UsageDefaultValue: conf.Format,
|
||||
},
|
||||
},
|
||||
&conf,
|
||||
types.GinkgoFlagSections{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return command.Command{
|
||||
Name: "outline",
|
||||
Usage: "ginkgo outline <filename>",
|
||||
ShortDoc: "Create an outline of Ginkgo symbols for a file",
|
||||
Documentation: "To read from stdin, use: `ginkgo outline -`",
|
||||
DocLink: "creating-an-outline-of-specs",
|
||||
Flags: flags,
|
||||
Command: func(args []string, _ []string) {
|
||||
outlineFile(args, conf.Format)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func outlineFile(args []string, format string) {
|
||||
if len(args) != 1 {
|
||||
command.AbortWithUsage("outline expects exactly one argument")
|
||||
}
|
||||
|
||||
filename := args[0]
|
||||
var src *os.File
|
||||
if filename == stdinAlias {
|
||||
src = os.Stdin
|
||||
} else {
|
||||
var err error
|
||||
src, err = os.Open(filename)
|
||||
command.AbortIfError("Failed to open file:", err)
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
|
||||
parsedSrc, err := parser.ParseFile(fset, filename, src, 0)
|
||||
command.AbortIfError("Failed to parse source:", err)
|
||||
|
||||
o, err := FromASTFile(fset, parsedSrc)
|
||||
command.AbortIfError("Failed to create outline:", err)
|
||||
|
||||
var oerr error
|
||||
switch format {
|
||||
case "csv":
|
||||
_, oerr = fmt.Print(o)
|
||||
case "indent":
|
||||
_, oerr = fmt.Print(o.StringIndent(indentWidth))
|
||||
case "json":
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
println(fmt.Sprintf("error marshalling to json: %s", err))
|
||||
}
|
||||
_, oerr = fmt.Println(string(b))
|
||||
default:
|
||||
command.AbortWith("Format %s not accepted", format)
|
||||
}
|
||||
command.AbortIfError("Failed to write outline:", oerr)
|
||||
}
|
232
vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go
generated
vendored
Normal file
232
vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/internal/interrupt_handler"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func BuildRunCommand() command.Command {
|
||||
var suiteConfig = types.NewDefaultSuiteConfig()
|
||||
var reporterConfig = types.NewDefaultReporterConfig()
|
||||
var cliConfig = types.NewDefaultCLIConfig()
|
||||
var goFlagsConfig = types.NewDefaultGoFlagsConfig()
|
||||
|
||||
flags, err := types.BuildRunCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
interruptHandler := interrupt_handler.NewInterruptHandler(nil)
|
||||
interrupt_handler.SwallowSigQuit()
|
||||
|
||||
return command.Command{
|
||||
Name: "run",
|
||||
Flags: flags,
|
||||
Usage: "ginkgo run <FLAGS> <PACKAGES> -- <PASS-THROUGHS>",
|
||||
ShortDoc: "Run the tests in the passed in <PACKAGES> (or the package in the current directory if left blank)",
|
||||
Documentation: "Any arguments after -- will be passed to the test.",
|
||||
DocLink: "running-tests",
|
||||
Command: func(args []string, additionalArgs []string) {
|
||||
var errors []error
|
||||
cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig)
|
||||
command.AbortIfErrors("Ginkgo detected configuration issues:", errors)
|
||||
|
||||
runner := &SpecRunner{
|
||||
cliConfig: cliConfig,
|
||||
goFlagsConfig: goFlagsConfig,
|
||||
suiteConfig: suiteConfig,
|
||||
reporterConfig: reporterConfig,
|
||||
flags: flags,
|
||||
|
||||
interruptHandler: interruptHandler,
|
||||
}
|
||||
|
||||
runner.RunSpecs(args, additionalArgs)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type SpecRunner struct {
|
||||
suiteConfig types.SuiteConfig
|
||||
reporterConfig types.ReporterConfig
|
||||
cliConfig types.CLIConfig
|
||||
goFlagsConfig types.GoFlagsConfig
|
||||
flags types.GinkgoFlagSet
|
||||
|
||||
interruptHandler *interrupt_handler.InterruptHandler
|
||||
}
|
||||
|
||||
func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
|
||||
suites := internal.FindSuites(args, r.cliConfig, true)
|
||||
skippedSuites := suites.WithState(internal.TestSuiteStateSkippedByFilter)
|
||||
suites = suites.WithoutState(internal.TestSuiteStateSkippedByFilter)
|
||||
|
||||
internal.VerifyCLIAndFrameworkVersion(suites)
|
||||
|
||||
if len(skippedSuites) > 0 {
|
||||
fmt.Println("Will skip:")
|
||||
for _, skippedSuite := range skippedSuites {
|
||||
fmt.Println(" " + skippedSuite.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if len(skippedSuites) > 0 && len(suites) == 0 {
|
||||
command.AbortGracefullyWith("All tests skipped! Exiting...")
|
||||
}
|
||||
|
||||
if len(suites) == 0 {
|
||||
command.AbortWith("Found no test suites")
|
||||
}
|
||||
|
||||
if len(suites) > 1 && !r.flags.WasSet("succinct") && r.reporterConfig.Verbosity().LT(types.VerbosityLevelVerbose) {
|
||||
r.reporterConfig.Succinct = true
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
var endTime time.Time
|
||||
if r.suiteConfig.Timeout > 0 {
|
||||
endTime = t.Add(r.suiteConfig.Timeout)
|
||||
}
|
||||
|
||||
iteration := 0
|
||||
OUTER_LOOP:
|
||||
for {
|
||||
if !r.flags.WasSet("seed") {
|
||||
r.suiteConfig.RandomSeed = time.Now().Unix()
|
||||
}
|
||||
if r.cliConfig.RandomizeSuites && len(suites) > 1 {
|
||||
suites = suites.ShuffledCopy(r.suiteConfig.RandomSeed)
|
||||
}
|
||||
|
||||
opc := internal.NewOrderedParallelCompiler(r.cliConfig.ComputedNumCompilers())
|
||||
opc.StartCompiling(suites, r.goFlagsConfig)
|
||||
|
||||
SUITE_LOOP:
|
||||
for {
|
||||
suiteIdx, suite := opc.Next()
|
||||
if suiteIdx >= len(suites) {
|
||||
break SUITE_LOOP
|
||||
}
|
||||
suites[suiteIdx] = suite
|
||||
|
||||
if r.interruptHandler.Status().Interrupted() {
|
||||
opc.StopAndDrain()
|
||||
break OUTER_LOOP
|
||||
}
|
||||
|
||||
if suites[suiteIdx].State.Is(internal.TestSuiteStateSkippedDueToEmptyCompilation) {
|
||||
fmt.Printf("Skipping %s (no test files)\n", suite.Path)
|
||||
continue SUITE_LOOP
|
||||
}
|
||||
|
||||
if suites[suiteIdx].State.Is(internal.TestSuiteStateFailedToCompile) {
|
||||
fmt.Println(suites[suiteIdx].CompilationError.Error())
|
||||
if !r.cliConfig.KeepGoing {
|
||||
opc.StopAndDrain()
|
||||
}
|
||||
continue SUITE_LOOP
|
||||
}
|
||||
|
||||
if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 && !r.cliConfig.KeepGoing {
|
||||
suites[suiteIdx].State = internal.TestSuiteStateSkippedDueToPriorFailures
|
||||
opc.StopAndDrain()
|
||||
continue SUITE_LOOP
|
||||
}
|
||||
|
||||
if !endTime.IsZero() {
|
||||
r.suiteConfig.Timeout = endTime.Sub(time.Now())
|
||||
if r.suiteConfig.Timeout <= 0 {
|
||||
suites[suiteIdx].State = internal.TestSuiteStateFailedDueToTimeout
|
||||
opc.StopAndDrain()
|
||||
continue SUITE_LOOP
|
||||
}
|
||||
}
|
||||
|
||||
suites[suiteIdx] = internal.RunCompiledSuite(suites[suiteIdx], r.suiteConfig, r.reporterConfig, r.cliConfig, r.goFlagsConfig, additionalArgs)
|
||||
}
|
||||
|
||||
if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 {
|
||||
if iteration > 0 {
|
||||
fmt.Printf("\nTests failed on attempt #%d\n\n", iteration+1)
|
||||
}
|
||||
break OUTER_LOOP
|
||||
}
|
||||
|
||||
if r.cliConfig.UntilItFails {
|
||||
fmt.Printf("\nAll tests passed...\nWill keep running them until they fail.\nThis was attempt #%d\n%s\n", iteration+1, orcMessage(iteration+1))
|
||||
} else if r.cliConfig.Repeat > 0 && iteration < r.cliConfig.Repeat {
|
||||
fmt.Printf("\nAll tests passed...\nThis was attempt %d of %d.\n", iteration+1, r.cliConfig.Repeat+1)
|
||||
} else {
|
||||
break OUTER_LOOP
|
||||
}
|
||||
iteration += 1
|
||||
}
|
||||
|
||||
internal.Cleanup(r.goFlagsConfig, suites...)
|
||||
|
||||
messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, r.cliConfig, r.suiteConfig, r.reporterConfig, r.goFlagsConfig)
|
||||
command.AbortIfError("could not finalize profiles:", err)
|
||||
for _, message := range messages {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
fmt.Printf("\nGinkgo ran %d %s in %s\n", len(suites), internal.PluralizedWord("suite", "suites", len(suites)), time.Since(t))
|
||||
|
||||
if suites.CountWithState(internal.TestSuiteStateFailureStates...) == 0 {
|
||||
if suites.AnyHaveProgrammaticFocus() && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
|
||||
fmt.Printf("Test Suite Passed\n")
|
||||
fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE)
|
||||
command.Abort(command.AbortDetails{ExitCode: types.GINKGO_FOCUS_EXIT_CODE})
|
||||
} else {
|
||||
fmt.Printf("Test Suite Passed\n")
|
||||
command.Abort(command.AbortDetails{})
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(formatter.ColorableStdOut, "")
|
||||
if len(suites) > 1 && suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 {
|
||||
fmt.Fprintln(formatter.ColorableStdOut,
|
||||
internal.FailedSuitesReport(suites, formatter.NewWithNoColorBool(r.reporterConfig.NoColor)))
|
||||
}
|
||||
fmt.Printf("Test Suite Failed\n")
|
||||
command.Abort(command.AbortDetails{ExitCode: 1})
|
||||
}
|
||||
}
|
||||
|
||||
func orcMessage(iteration int) string {
|
||||
if iteration < 10 {
|
||||
return ""
|
||||
} else if iteration < 30 {
|
||||
return []string{
|
||||
"If at first you succeed...",
|
||||
"...try, try again.",
|
||||
"Looking good!",
|
||||
"Still good...",
|
||||
"I think your tests are fine....",
|
||||
"Yep, still passing",
|
||||
"Oh boy, here I go testin' again!",
|
||||
"Even the gophers are getting bored",
|
||||
"Did you try -race?",
|
||||
"Maybe you should stop now?",
|
||||
"I'm getting tired...",
|
||||
"What if I just made you a sandwich?",
|
||||
"Hit ^C, hit ^C, please hit ^C",
|
||||
"Make it stop. Please!",
|
||||
"Come on! Enough is enough!",
|
||||
"Dave, this conversation can serve no purpose anymore. Goodbye.",
|
||||
"Just what do you think you're doing, Dave? ",
|
||||
"I, Sisyphus",
|
||||
"Insanity: doing the same thing over and over again and expecting different results. -Einstein",
|
||||
"I guess Einstein never tried to churn butter",
|
||||
}[iteration-10] + "\n"
|
||||
} else {
|
||||
return "No, seriously... you can probably stop now.\n"
|
||||
}
|
||||
}
|
186
vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go
generated
vendored
Normal file
186
vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
package unfocus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
)
|
||||
|
||||
func BuildUnfocusCommand() command.Command {
|
||||
return command.Command{
|
||||
Name: "unfocus",
|
||||
Usage: "ginkgo unfocus",
|
||||
ShortDoc: "Recursively unfocus any focused tests under the current directory",
|
||||
DocLink: "filtering-specs",
|
||||
Command: func(_ []string, _ []string) {
|
||||
unfocusSpecs()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func unfocusSpecs() {
|
||||
fmt.Println("Scanning for focus...")
|
||||
|
||||
goFiles := make(chan string)
|
||||
go func() {
|
||||
unfocusDir(goFiles, ".")
|
||||
close(goFiles)
|
||||
}()
|
||||
|
||||
const workers = 10
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(workers)
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
for path := range goFiles {
|
||||
unfocusFile(path)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func unfocusDir(goFiles chan string, path string) {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
switch {
|
||||
case f.IsDir() && shouldProcessDir(f.Name()):
|
||||
unfocusDir(goFiles, filepath.Join(path, f.Name()))
|
||||
case !f.IsDir() && shouldProcessFile(f.Name()):
|
||||
goFiles <- filepath.Join(path, f.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shouldProcessDir(basename string) bool {
|
||||
return basename != "vendor" && !strings.HasPrefix(basename, ".")
|
||||
}
|
||||
|
||||
func shouldProcessFile(basename string) bool {
|
||||
return strings.HasSuffix(basename, ".go")
|
||||
}
|
||||
|
||||
func unfocusFile(path string) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading file '%s': %s\n", path, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ast, err := parser.ParseFile(token.NewFileSet(), path, bytes.NewReader(data), parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Printf("error parsing file '%s': %s\n", path, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
eliminations := scanForFocus(ast)
|
||||
if len(eliminations) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("...updating %s\n", path)
|
||||
backup, err := writeBackup(path, data)
|
||||
if err != nil {
|
||||
fmt.Printf("error creating backup file: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateFile(path, data, eliminations); err != nil {
|
||||
fmt.Printf("error writing file '%s': %s\n", path, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
os.Remove(backup)
|
||||
}
|
||||
|
||||
func writeBackup(path string, data []byte) (string, error) {
|
||||
t, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path))
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating temporary file: %w", err)
|
||||
}
|
||||
defer t.Close()
|
||||
|
||||
if _, err := io.Copy(t, bytes.NewReader(data)); err != nil {
|
||||
return "", fmt.Errorf("error writing to temporary file: %w", err)
|
||||
}
|
||||
|
||||
return t.Name(), nil
|
||||
}
|
||||
|
||||
func updateFile(path string, data []byte, eliminations [][]int64) error {
|
||||
to, err := os.Create(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening file for writing '%s': %w\n", path, err)
|
||||
}
|
||||
defer to.Close()
|
||||
|
||||
from := bytes.NewReader(data)
|
||||
var cursor int64
|
||||
for _, eliminationRange := range eliminations {
|
||||
positionToEliminate, lengthToEliminate := eliminationRange[0]-1, eliminationRange[1]
|
||||
if _, err := io.CopyN(to, from, positionToEliminate-cursor); err != nil {
|
||||
return fmt.Errorf("error copying data: %w", err)
|
||||
}
|
||||
|
||||
cursor = positionToEliminate + lengthToEliminate
|
||||
|
||||
if _, err := from.Seek(lengthToEliminate, io.SeekCurrent); err != nil {
|
||||
return fmt.Errorf("error seeking to position in buffer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := io.Copy(to, from); err != nil {
|
||||
return fmt.Errorf("error copying end data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanForFocus(file *ast.File) (eliminations [][]int64) {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
if c, ok := n.(*ast.CallExpr); ok {
|
||||
if i, ok := c.Fun.(*ast.Ident); ok {
|
||||
if isFocus(i.Name) {
|
||||
eliminations = append(eliminations, []int64{int64(i.Pos()), 1})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := n.(*ast.Ident); ok {
|
||||
if i.Name == "Focus" {
|
||||
eliminations = append(eliminations, []int64{int64(i.Pos()), 6})
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return eliminations
|
||||
}
|
||||
|
||||
func isFocus(name string) bool {
|
||||
switch name {
|
||||
case "FDescribe", "FContext", "FIt", "FDescribeTable", "FEntry", "FSpecify", "FWhen":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
22
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go
generated
vendored
Normal file
22
vendor/github.com/onsi/ginkgo/v2/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
|
||||
}
|
75
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
75
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"regexp"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
)
|
||||
|
||||
type SuiteErrors map[internal.TestSuite]error
|
||||
|
||||
type DeltaTracker struct {
|
||||
maxDepth int
|
||||
watchRegExp *regexp.Regexp
|
||||
suites map[string]*Suite
|
||||
packageHashes *PackageHashes
|
||||
}
|
||||
|
||||
func NewDeltaTracker(maxDepth int, watchRegExp *regexp.Regexp) *DeltaTracker {
|
||||
return &DeltaTracker{
|
||||
maxDepth: maxDepth,
|
||||
watchRegExp: watchRegExp,
|
||||
packageHashes: NewPackageHashes(watchRegExp),
|
||||
suites: map[string]*Suite{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DeltaTracker) Delta(suites internal.TestSuites) (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 internal.TestSuite) error {
|
||||
s, ok := d.suites[suite.Path]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown suite %s", suite.Path)
|
||||
}
|
||||
|
||||
return s.MarkAsRunAndRecomputedDependencies(d.maxDepth)
|
||||
}
|
92
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go
generated
vendored
Normal file
92
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var ginkgoAndGomegaFilter = regexp.MustCompile(`github\.com/onsi/ginkgo|github\.com/onsi/gomega`)
|
||||
var ginkgoIntegrationTestFilter = regexp.MustCompile(`github\.com/onsi/ginkgo/integration`) //allow us to integration test this thing
|
||||
|
||||
type Dependencies struct {
|
||||
deps map[string]int
|
||||
}
|
||||
|
||||
func NewDependencies(path string, maxDepth int) (Dependencies, error) {
|
||||
d := Dependencies{
|
||||
deps: map[string]int{},
|
||||
}
|
||||
|
||||
if maxDepth == 0 {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
err := d.seedWithDepsForPackageAtPath(path)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
for depth := 1; depth < maxDepth; depth++ {
|
||||
n := len(d.deps)
|
||||
d.addDepsForDepth(depth)
|
||||
if n == len(d.deps) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d Dependencies) Dependencies() map[string]int {
|
||||
return d.deps
|
||||
}
|
||||
|
||||
func (d Dependencies) seedWithDepsForPackageAtPath(path string) error {
|
||||
pkg, err := build.ImportDir(path, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.resolveAndAdd(pkg.Imports, 1)
|
||||
d.resolveAndAdd(pkg.TestImports, 1)
|
||||
d.resolveAndAdd(pkg.XTestImports, 1)
|
||||
|
||||
delete(d.deps, pkg.Dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Dependencies) addDepsForDepth(depth int) {
|
||||
for dep, depDepth := range d.deps {
|
||||
if depDepth == depth {
|
||||
d.addDepsForDep(dep, depth+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d Dependencies) addDepsForDep(dep string, depth int) {
|
||||
pkg, err := build.ImportDir(dep, 0)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
d.resolveAndAdd(pkg.Imports, depth)
|
||||
}
|
||||
|
||||
func (d Dependencies) resolveAndAdd(deps []string, depth int) {
|
||||
for _, dep := range deps {
|
||||
pkg, err := build.Import(dep, ".", 0)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !pkg.Goroot && (!ginkgoAndGomegaFilter.Match([]byte(pkg.Dir)) || ginkgoIntegrationTestFilter.Match([]byte(pkg.Dir))) {
|
||||
d.addDepIfNotPresent(pkg.Dir, depth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d Dependencies) addDepIfNotPresent(dep string, depth int) {
|
||||
_, ok := d.deps[dep]
|
||||
if !ok {
|
||||
d.deps[dep] = depth
|
||||
}
|
||||
}
|
108
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
Normal file
108
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
var goTestRegExp = regexp.MustCompile(`_test\.go$`)
|
||||
|
||||
type PackageHash struct {
|
||||
CodeModifiedTime time.Time
|
||||
TestModifiedTime time.Time
|
||||
Deleted bool
|
||||
|
||||
path string
|
||||
codeHash string
|
||||
testHash string
|
||||
watchRegExp *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewPackageHash(path string, watchRegExp *regexp.Regexp) *PackageHash {
|
||||
p := &PackageHash{
|
||||
path: path,
|
||||
watchRegExp: watchRegExp,
|
||||
}
|
||||
|
||||
p.codeHash, _, p.testHash, _, p.Deleted = p.computeHashes()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PackageHash) CheckForChanges() bool {
|
||||
codeHash, codeModifiedTime, testHash, testModifiedTime, deleted := p.computeHashes()
|
||||
|
||||
if deleted {
|
||||
if !p.Deleted {
|
||||
t := time.Now()
|
||||
p.CodeModifiedTime = t
|
||||
p.TestModifiedTime = t
|
||||
}
|
||||
p.Deleted = true
|
||||
return true
|
||||
}
|
||||
|
||||
modified := false
|
||||
p.Deleted = false
|
||||
|
||||
if p.codeHash != codeHash {
|
||||
p.CodeModifiedTime = codeModifiedTime
|
||||
modified = true
|
||||
}
|
||||
if p.testHash != testHash {
|
||||
p.TestModifiedTime = testModifiedTime
|
||||
modified = true
|
||||
}
|
||||
|
||||
p.codeHash = codeHash
|
||||
p.testHash = testHash
|
||||
return modified
|
||||
}
|
||||
|
||||
func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Time, testHash string, testModifiedTime time.Time, deleted bool) {
|
||||
entries, err := os.ReadDir(p.path)
|
||||
|
||||
if err != nil {
|
||||
deleted = true
|
||||
return
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if goTestRegExp.Match([]byte(info.Name())) {
|
||||
testHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(testModifiedTime) {
|
||||
testModifiedTime = info.ModTime()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if p.watchRegExp.Match([]byte(info.Name())) {
|
||||
codeHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(codeModifiedTime) {
|
||||
codeModifiedTime = info.ModTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testHash += codeHash
|
||||
if codeModifiedTime.After(testModifiedTime) {
|
||||
testModifiedTime = codeModifiedTime
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *PackageHash) hashForFileInfo(info os.FileInfo) string {
|
||||
return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano())
|
||||
}
|
85
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go
generated
vendored
Normal file
85
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type PackageHashes struct {
|
||||
PackageHashes map[string]*PackageHash
|
||||
usedPaths map[string]bool
|
||||
watchRegExp *regexp.Regexp
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
func NewPackageHashes(watchRegExp *regexp.Regexp) *PackageHashes {
|
||||
return &PackageHashes{
|
||||
PackageHashes: map[string]*PackageHash{},
|
||||
usedPaths: nil,
|
||||
watchRegExp: watchRegExp,
|
||||
lock: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PackageHashes) CheckForChanges() []string {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
modified := []string{}
|
||||
|
||||
for _, packageHash := range p.PackageHashes {
|
||||
if packageHash.CheckForChanges() {
|
||||
modified = append(modified, packageHash.path)
|
||||
}
|
||||
}
|
||||
|
||||
return modified
|
||||
}
|
||||
|
||||
func (p *PackageHashes) Add(path string) *PackageHash {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
path, _ = filepath.Abs(path)
|
||||
_, ok := p.PackageHashes[path]
|
||||
if !ok {
|
||||
p.PackageHashes[path] = NewPackageHash(path, p.watchRegExp)
|
||||
}
|
||||
|
||||
if p.usedPaths != nil {
|
||||
p.usedPaths[path] = true
|
||||
}
|
||||
return p.PackageHashes[path]
|
||||
}
|
||||
|
||||
func (p *PackageHashes) Get(path string) *PackageHash {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
path, _ = filepath.Abs(path)
|
||||
if p.usedPaths != nil {
|
||||
p.usedPaths[path] = true
|
||||
}
|
||||
return p.PackageHashes[path]
|
||||
}
|
||||
|
||||
func (p *PackageHashes) StartTrackingUsage() {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.usedPaths = map[string]bool{}
|
||||
}
|
||||
|
||||
func (p *PackageHashes) StopTrackingUsageAndPrune() {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
for path := range p.PackageHashes {
|
||||
if !p.usedPaths[path] {
|
||||
delete(p.PackageHashes, path)
|
||||
}
|
||||
}
|
||||
|
||||
p.usedPaths = nil
|
||||
}
|
87
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go
generated
vendored
Normal file
87
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
)
|
||||
|
||||
type Suite struct {
|
||||
Suite internal.TestSuite
|
||||
RunTime time.Time
|
||||
Dependencies Dependencies
|
||||
|
||||
sharedPackageHashes *PackageHashes
|
||||
}
|
||||
|
||||
func NewSuite(suite internal.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)
|
||||
}
|
192
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go
generated
vendored
Normal file
192
vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/formatter"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/internal"
|
||||
"github.com/onsi/ginkgo/v2/internal/interrupt_handler"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
func BuildWatchCommand() command.Command {
|
||||
var suiteConfig = types.NewDefaultSuiteConfig()
|
||||
var reporterConfig = types.NewDefaultReporterConfig()
|
||||
var cliConfig = types.NewDefaultCLIConfig()
|
||||
var goFlagsConfig = types.NewDefaultGoFlagsConfig()
|
||||
|
||||
flags, err := types.BuildWatchCommandFlagSet(&suiteConfig, &reporterConfig, &cliConfig, &goFlagsConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
interruptHandler := interrupt_handler.NewInterruptHandler(nil)
|
||||
interrupt_handler.SwallowSigQuit()
|
||||
|
||||
return command.Command{
|
||||
Name: "watch",
|
||||
Flags: flags,
|
||||
Usage: "ginkgo watch <FLAGS> <PACKAGES> -- <PASS-THROUGHS>",
|
||||
ShortDoc: "Watch the passed in <PACKAGES> and runs their tests whenever changes occur.",
|
||||
Documentation: "Any arguments after -- will be passed to the test.",
|
||||
DocLink: "watching-for-changes",
|
||||
Command: func(args []string, additionalArgs []string) {
|
||||
var errors []error
|
||||
cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig)
|
||||
command.AbortIfErrors("Ginkgo detected configuration issues:", errors)
|
||||
|
||||
watcher := &SpecWatcher{
|
||||
cliConfig: cliConfig,
|
||||
goFlagsConfig: goFlagsConfig,
|
||||
suiteConfig: suiteConfig,
|
||||
reporterConfig: reporterConfig,
|
||||
flags: flags,
|
||||
|
||||
interruptHandler: interruptHandler,
|
||||
}
|
||||
|
||||
watcher.WatchSpecs(args, additionalArgs)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type SpecWatcher struct {
|
||||
suiteConfig types.SuiteConfig
|
||||
reporterConfig types.ReporterConfig
|
||||
cliConfig types.CLIConfig
|
||||
goFlagsConfig types.GoFlagsConfig
|
||||
flags types.GinkgoFlagSet
|
||||
|
||||
interruptHandler *interrupt_handler.InterruptHandler
|
||||
}
|
||||
|
||||
func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) {
|
||||
suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter)
|
||||
|
||||
internal.VerifyCLIAndFrameworkVersion(suites)
|
||||
|
||||
if len(suites) == 0 {
|
||||
command.AbortWith("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), internal.PluralizedWord("suite", "suites", len(suites)), w.cliConfig.Depth)
|
||||
deltaTracker := NewDeltaTracker(w.cliConfig.Depth, regexp.MustCompile(w.cliConfig.WatchRegExp))
|
||||
delta, errors := deltaTracker.Delta(suites)
|
||||
|
||||
fmt.Printf("Watching %d %s:\n", len(delta.NewSuites), internal.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 {
|
||||
w.updateSeed()
|
||||
w.compileAndRun(suites[0], additionalArgs)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
suites := internal.FindSuites(args, w.cliConfig, false).WithoutState(internal.TestSuiteStateSkippedByFilter)
|
||||
delta, _ := deltaTracker.Delta(suites)
|
||||
coloredStream := formatter.ColorableStdOut
|
||||
|
||||
suites = internal.TestSuites{}
|
||||
|
||||
if len(delta.NewSuites) > 0 {
|
||||
fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected %d new %s:{{/}}", len(delta.NewSuites), internal.PluralizedWord("suite", "suites", len(delta.NewSuites))))
|
||||
for _, suite := range delta.NewSuites {
|
||||
suites = append(suites, suite.Suite)
|
||||
fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description()))
|
||||
}
|
||||
}
|
||||
|
||||
modifiedSuites := delta.ModifiedSuites()
|
||||
if len(modifiedSuites) > 0 {
|
||||
fmt.Fprintln(coloredStream, formatter.F("{{green}}Detected changes in:{{/}}"))
|
||||
for _, pkg := range delta.ModifiedPackages {
|
||||
fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", pkg))
|
||||
}
|
||||
fmt.Fprintln(coloredStream, formatter.F("{{green}}Will run %d %s:{{/}}", len(modifiedSuites), internal.PluralizedWord("suite", "suites", len(modifiedSuites))))
|
||||
for _, suite := range modifiedSuites {
|
||||
suites = append(suites, suite.Suite)
|
||||
fmt.Fprintln(coloredStream, formatter.Fi(1, "%s", suite.Description()))
|
||||
}
|
||||
fmt.Fprintln(coloredStream, "")
|
||||
}
|
||||
|
||||
if len(suites) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
w.updateSeed()
|
||||
w.computeSuccinctMode(len(suites))
|
||||
for idx := range suites {
|
||||
if w.interruptHandler.Status().Interrupted() {
|
||||
return
|
||||
}
|
||||
deltaTracker.WillRun(suites[idx])
|
||||
suites[idx] = w.compileAndRun(suites[idx], additionalArgs)
|
||||
}
|
||||
color := "{{green}}"
|
||||
if suites.CountWithState(internal.TestSuiteStateFailureStates...) > 0 {
|
||||
color = "{{red}}"
|
||||
}
|
||||
fmt.Fprintln(coloredStream, formatter.F(color+"\nDone. Resuming watch...{{/}}"))
|
||||
|
||||
messages, err := internal.FinalizeProfilesAndReportsForSuites(suites, w.cliConfig, w.suiteConfig, w.reporterConfig, w.goFlagsConfig)
|
||||
command.AbortIfError("could not finalize profiles:", err)
|
||||
for _, message := range messages {
|
||||
fmt.Println(message)
|
||||
}
|
||||
case <-w.interruptHandler.Status().Channel:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SpecWatcher) compileAndRun(suite internal.TestSuite, additionalArgs []string) internal.TestSuite {
|
||||
suite = internal.CompileSuite(suite, w.goFlagsConfig)
|
||||
if suite.State.Is(internal.TestSuiteStateFailedToCompile) {
|
||||
fmt.Println(suite.CompilationError.Error())
|
||||
return suite
|
||||
}
|
||||
if w.interruptHandler.Status().Interrupted() {
|
||||
return suite
|
||||
}
|
||||
suite = internal.RunCompiledSuite(suite, w.suiteConfig, w.reporterConfig, w.cliConfig, w.goFlagsConfig, additionalArgs)
|
||||
internal.Cleanup(w.goFlagsConfig, suite)
|
||||
return suite
|
||||
}
|
||||
|
||||
func (w *SpecWatcher) computeSuccinctMode(numSuites int) {
|
||||
if w.reporterConfig.Verbosity().GTE(types.VerbosityLevelVerbose) {
|
||||
w.reporterConfig.Succinct = false
|
||||
return
|
||||
}
|
||||
|
||||
if w.flags.WasSet("succinct") {
|
||||
return
|
||||
}
|
||||
|
||||
if numSuites == 1 {
|
||||
w.reporterConfig.Succinct = false
|
||||
}
|
||||
|
||||
if numSuites > 1 {
|
||||
w.reporterConfig.Succinct = true
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SpecWatcher) updateSeed() {
|
||||
if !w.flags.WasSet("seed") {
|
||||
w.suiteConfig.RandomSeed = time.Now().Unix()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user