mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
76
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
Normal file
76
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"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 {
|
||||
if len(goFlagsConfig.O) == 0 {
|
||||
goFlagsConfig.O = path.Join(suite.Path, suite.PackageName+".test")
|
||||
} else {
|
||||
stat, err := os.Stat(goFlagsConfig.O)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if stat.IsDir() {
|
||||
goFlagsConfig.O += "/" + suite.PackageName + ".test"
|
||||
}
|
||||
}
|
||||
fmt.Printf("Compiled %s\n", goFlagsConfig.O)
|
||||
}
|
||||
}
|
||||
|
||||
if suites.CountWithState(internal.TestSuiteStateFailedToCompile) > 0 {
|
||||
command.AbortWith("Failed to compile all tests")
|
||||
}
|
||||
}
|
61
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.go
generated
vendored
Normal file
61
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.go
generated
vendored
Normal file
50
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.go
generated
vendored
Normal file
182
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.go
generated
vendored
Normal file
48
e2e/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())
|
||||
})
|
||||
`
|
133
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
Normal file
133
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig/v3"
|
||||
"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"},
|
||||
{Name: "template-data", KeyPath: "CustomTemplateData",
|
||||
UsageArgument: "template-data-file",
|
||||
Usage: "If specified, generate will use the contents of the file passed as data to be rendered in 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
|
||||
CustomData map[string]any
|
||||
}
|
||||
|
||||
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)
|
||||
if conf.CustomTemplateData != "" {
|
||||
var tplCustomDataMap map[string]any
|
||||
tplCustomData, err := os.ReadFile(conf.CustomTemplateData)
|
||||
command.AbortIfError("Failed to read custom boostrap data file:", err)
|
||||
if !json.Valid([]byte(tplCustomData)) {
|
||||
command.AbortWith("Invalid JSON object in custom data file.")
|
||||
}
|
||||
//create map from the custom template data
|
||||
json.Unmarshal(tplCustomData, &tplCustomDataMap)
|
||||
data.CustomData = tplCustomDataMap
|
||||
}
|
||||
} else if conf.Agouti {
|
||||
templateText = agoutiBootstrapText
|
||||
} else {
|
||||
templateText = bootstrapText
|
||||
}
|
||||
|
||||
//Setting the option to explicitly fail if template is rendered trying to access missing key
|
||||
bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText)
|
||||
command.AbortIfError("Failed to parse bootstrap template:", err)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
//Being explicit about failing sooner during template rendering
|
||||
//when accessing custom data rather than during the go fmt command
|
||||
err = bootstrapTemplate.Execute(buf, data)
|
||||
command.AbortIfError("Failed to render bootstrap template:", err)
|
||||
|
||||
buf.WriteTo(f)
|
||||
|
||||
internal.GoFmt(targetFile)
|
||||
}
|
265
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
Normal file
265
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
generated
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig/v3"
|
||||
"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"},
|
||||
{Name: "template-data", KeyPath: "CustomTemplateData",
|
||||
UsageArgument: "template-data-file",
|
||||
Usage: "If specified, generate will use the contents of the file passed as data to be rendered in the test file template"},
|
||||
{Name: "tags", KeyPath: "Tags",
|
||||
UsageArgument: "build-tags",
|
||||
Usage: "If specified, generate will create a test file that uses the given build tags (i.e. `--tags e2e,!unit` will add `//go:build e2e,!unit`)"},
|
||||
},
|
||||
&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 {
|
||||
BuildTags string
|
||||
Package string
|
||||
Subject string
|
||||
PackageImportPath string
|
||||
ImportPackage bool
|
||||
|
||||
GinkgoImport string
|
||||
GomegaImport string
|
||||
GinkgoPackage string
|
||||
GomegaPackage string
|
||||
CustomData map[string]any
|
||||
}
|
||||
|
||||
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{
|
||||
BuildTags: getBuildTags(conf.Tags),
|
||||
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)
|
||||
if conf.CustomTemplateData != "" {
|
||||
var tplCustomDataMap map[string]any
|
||||
tplCustomData, err := os.ReadFile(conf.CustomTemplateData)
|
||||
command.AbortIfError("Failed to read custom template data file:", err)
|
||||
if !json.Valid([]byte(tplCustomData)) {
|
||||
command.AbortWith("Invalid JSON object in custom data file.")
|
||||
}
|
||||
//create map from the custom template data
|
||||
json.Unmarshal(tplCustomData, &tplCustomDataMap)
|
||||
data.CustomData = tplCustomDataMap
|
||||
}
|
||||
} else if conf.Agouti {
|
||||
templateText = agoutiSpecText
|
||||
} else {
|
||||
templateText = specText
|
||||
}
|
||||
|
||||
//Setting the option to explicitly fail if template is rendered trying to access missing key
|
||||
specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText)
|
||||
command.AbortIfError("Failed to read parse test template:", err)
|
||||
|
||||
//Being explicit about failing sooner during template rendering
|
||||
//when accessing custom data rather than during the go fmt command
|
||||
err = specTemplate.Execute(f, data)
|
||||
command.AbortIfError("Failed to render bootstrap template:", err)
|
||||
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 ""
|
||||
}
|
||||
defer modFile.Close()
|
||||
|
||||
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])
|
||||
}
|
43
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go
generated
vendored
Normal file
43
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package generators
|
||||
|
||||
var specText = `{{.BuildTags}}
|
||||
package {{.Package}}
|
||||
|
||||
import (
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
|
||||
{{if .ImportPackage}}"{{.PackageImportPath}}"{{end}}
|
||||
)
|
||||
|
||||
var _ = {{.GinkgoPackage}}Describe("{{.Subject}}", func() {
|
||||
|
||||
})
|
||||
`
|
||||
|
||||
var agoutiSpecText = `{{.BuildTags}}
|
||||
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())
|
||||
})
|
||||
})
|
||||
`
|
76
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
generated
vendored
Normal file
76
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
)
|
||||
|
||||
type GeneratorsConfig struct {
|
||||
Agouti, NoDot, Internal bool
|
||||
CustomTemplate string
|
||||
CustomTemplateData string
|
||||
Tags 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"
|
||||
}
|
||||
|
||||
// getBuildTags returns the resultant string to be added.
|
||||
// If the input string is not empty, then returns a `//go:build {}` string,
|
||||
// otherwise returns an empty string.
|
||||
func getBuildTags(tags string) string {
|
||||
if tags != "" {
|
||||
return fmt.Sprintf("//go:build %s\n", tags)
|
||||
}
|
||||
return ""
|
||||
}
|
173
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
Normal file
173
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
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
|
||||
}
|
||||
|
||||
if len(goFlagsConfig.O) > 0 {
|
||||
userDefinedPath, err := filepath.Abs(goFlagsConfig.O)
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to compute compilation target path %s:\n%s", goFlagsConfig.O, err.Error())
|
||||
return suite
|
||||
}
|
||||
path = userDefinedPath
|
||||
}
|
||||
|
||||
goFlagsConfig.O = path
|
||||
|
||||
ginkgoInvocationPath, _ := os.Getwd()
|
||||
ginkgoInvocationPath, _ = filepath.Abs(ginkgoInvocationPath)
|
||||
packagePath := suite.AbsPath()
|
||||
pathToInvocationPath, err := filepath.Rel(packagePath, ginkgoInvocationPath)
|
||||
if err != nil {
|
||||
suite.State = TestSuiteStateFailedToCompile
|
||||
suite.CompilationError = fmt.Errorf("Failed to get relative path from package to the current working directory:\n%s", err.Error())
|
||||
return suite
|
||||
}
|
||||
args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, "./", pathToInvocationPath)
|
||||
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()
|
||||
}
|
129
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.go
generated
vendored
Normal file
129
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2015, Wade Simmons
|
||||
// All rights reserved.
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package gocovmerge takes the results from multiple `go test -coverprofile`
|
||||
// runs and merges them into one profile
|
||||
|
||||
// this file was originally taken from the gocovmerge project
|
||||
// see also: https://go.shabbyrobe.org/gocovmerge
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func AddCoverProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
|
||||
i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
|
||||
if i < len(profiles) && profiles[i].FileName == p.FileName {
|
||||
MergeCoverProfiles(profiles[i], p)
|
||||
} else {
|
||||
profiles = append(profiles, nil)
|
||||
copy(profiles[i+1:], profiles[i:])
|
||||
profiles[i] = p
|
||||
}
|
||||
return profiles
|
||||
}
|
||||
|
||||
func DumpCoverProfiles(profiles []*cover.Profile, out io.Writer) error {
|
||||
if len(profiles) == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range profiles {
|
||||
for _, b := range p.Blocks {
|
||||
if _, err := fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MergeCoverProfiles(into *cover.Profile, merge *cover.Profile) error {
|
||||
if into.Mode != merge.Mode {
|
||||
return fmt.Errorf("cannot merge profiles with different modes")
|
||||
}
|
||||
// Since the blocks are sorted, we can keep track of where the last block
|
||||
// was inserted and only look at the blocks after that as targets for merge
|
||||
startIndex := 0
|
||||
for _, b := range merge.Blocks {
|
||||
var err error
|
||||
startIndex, err = mergeProfileBlock(into, b, startIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) (int, error) {
|
||||
sortFunc := func(i int) bool {
|
||||
pi := p.Blocks[i+startIndex]
|
||||
return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
|
||||
}
|
||||
|
||||
i := 0
|
||||
if sortFunc(i) != true {
|
||||
i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
|
||||
}
|
||||
|
||||
i += startIndex
|
||||
if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
|
||||
if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
|
||||
return i, fmt.Errorf("gocovmerge: overlapping merge %v %v %v", p.FileName, p.Blocks[i], pb)
|
||||
}
|
||||
switch p.Mode {
|
||||
case "set":
|
||||
p.Blocks[i].Count |= pb.Count
|
||||
case "count", "atomic":
|
||||
p.Blocks[i].Count += pb.Count
|
||||
default:
|
||||
return i, fmt.Errorf("gocovmerge: unsupported covermode '%s'", p.Mode)
|
||||
}
|
||||
|
||||
} else {
|
||||
if i > 0 {
|
||||
pa := p.Blocks[i-1]
|
||||
if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
|
||||
return i, fmt.Errorf("gocovmerge: overlap before %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
if i < len(p.Blocks)-1 {
|
||||
pa := p.Blocks[i+1]
|
||||
if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
|
||||
return i, fmt.Errorf("gocovmerge: overlap after %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
p.Blocks = append(p.Blocks, cover.ProfileBlock{})
|
||||
copy(p.Blocks[i+1:], p.Blocks[i:])
|
||||
p.Blocks[i] = pb
|
||||
}
|
||||
|
||||
return i + 1, nil
|
||||
}
|
227
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
Normal file
227
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"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"
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
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, merges them, deletes them, stores them in destination
|
||||
func MergeAndCleanupCoverProfiles(profiles []string, destination string) error {
|
||||
var merged []*cover.Profile
|
||||
for _, file := range profiles {
|
||||
parsedProfiles, err := cover.ParseProfiles(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Remove(file)
|
||||
for _, p := range parsedProfiles {
|
||||
merged = AddCoverProfile(merged, p)
|
||||
}
|
||||
}
|
||||
dst, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
err = DumpCoverProfiles(merged, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 - %s", profile, err.Error(), string(output))
|
||||
}
|
||||
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)
|
||||
_ = proFile.Close()
|
||||
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
|
||||
}
|
355
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
generated
vendored
Normal file
355
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
generated
vendored
Normal file
@ -0,0 +1,355 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"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 {
|
||||
// As we run the go test from the suite directory, make sure the cover profile is absolute
|
||||
// and placed into the expected output directory when one is configured.
|
||||
if goFlagsConfig.Cover && !filepath.IsAbs(goFlagsConfig.CoverProfile) {
|
||||
goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
284
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
Normal file
284
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.go
generated
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"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" && runtime.GOOS != "windows" && 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.MatchString(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.MatchString(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.MatchString(file.Name()) {
|
||||
contents, _ := os.ReadFile(dir + "/" + file.Name())
|
||||
if reGinkgo.Match(contents) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
86
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.go
generated
vendored
Normal file
86
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.go
generated
vendored
Normal file
54
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.go
generated
vendored
Normal file
123
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/main.go
generated
vendored
Normal file
58
e2e/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)
|
||||
},
|
||||
}
|
||||
}
|
301
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
Normal file
301
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
generated
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
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"`
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
// 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)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
n.Pending = pendingFromCallExpr(ce)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "FIt", "FSpecify", "FEntry":
|
||||
n.Spec = true
|
||||
n.Focused = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "PIt", "PSpecify", "XIt", "XSpecify", "PEntry", "XEntry":
|
||||
n.Spec = true
|
||||
n.Pending = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "Context", "Describe", "When", "DescribeTable":
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
n.Pending = pendingFromCallExpr(ce)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "FContext", "FDescribe", "FWhen", "FDescribeTable":
|
||||
n.Focused = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
|
||||
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable":
|
||||
n.Pending = true
|
||||
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
|
||||
n.Labels = labelFromCallExpr(ce)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func labelFromCallExpr(ce *ast.CallExpr) []string {
|
||||
|
||||
labels := []string{}
|
||||
if len(ce.Args) < 2 {
|
||||
return labels
|
||||
}
|
||||
|
||||
for _, arg := range ce.Args[1:] {
|
||||
switch expr := arg.(type) {
|
||||
case *ast.CallExpr:
|
||||
id, ok := expr.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
// to skip over cases where the expr.Fun. is actually *ast.SelectorExpr
|
||||
continue
|
||||
}
|
||||
if id.Name == "Label" {
|
||||
ls := extractLabels(expr)
|
||||
labels = append(labels, ls...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
func extractLabels(expr *ast.CallExpr) []string {
|
||||
out := []string{}
|
||||
for _, arg := range expr.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
|
||||
}
|
||||
|
||||
func pendingFromCallExpr(ce *ast.CallExpr) bool {
|
||||
|
||||
pending := false
|
||||
if len(ce.Args) < 2 {
|
||||
return pending
|
||||
}
|
||||
|
||||
for _, arg := range ce.Args[1:] {
|
||||
switch expr := arg.(type) {
|
||||
case *ast.CallExpr:
|
||||
id, ok := expr.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
// to skip over cases where the expr.Fun. is actually *ast.SelectorExpr
|
||||
continue
|
||||
}
|
||||
if id.Name == "Pending" {
|
||||
pending = true
|
||||
}
|
||||
case *ast.Ident:
|
||||
if expr.Name == "Pending" {
|
||||
pending = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return pending
|
||||
}
|
58
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
Normal file
58
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// 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>" {
|
||||
name = "ginkgo"
|
||||
}
|
||||
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
|
||||
}
|
130
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
Normal file
130
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"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 bytes.Buffer
|
||||
b.WriteString("Name,Text,Start,End,Spec,Focused,Pending,Labels\n")
|
||||
|
||||
csvWriter := csv.NewWriter(&b)
|
||||
|
||||
currentIndent := 0
|
||||
pre := func(n *ginkgoNode) {
|
||||
b.WriteString(fmt.Sprintf("%*s", currentIndent, ""))
|
||||
var labels string
|
||||
if len(n.Labels) == 1 {
|
||||
labels = n.Labels[0]
|
||||
} else {
|
||||
labels = strings.Join(n.Labels, ", ")
|
||||
}
|
||||
|
||||
row := []string{
|
||||
n.Name,
|
||||
n.Text,
|
||||
strconv.Itoa(n.Start),
|
||||
strconv.Itoa(n.End),
|
||||
strconv.FormatBool(n.Spec),
|
||||
strconv.FormatBool(n.Focused),
|
||||
strconv.FormatBool(n.Pending),
|
||||
labels,
|
||||
}
|
||||
csvWriter.Write(row)
|
||||
|
||||
// Ensure we write to `b' before the next `b.WriteString()', which might be adding indentation
|
||||
csvWriter.Flush()
|
||||
|
||||
currentIndent += width
|
||||
}
|
||||
post := func(n *ginkgoNode) {
|
||||
currentIndent -= width
|
||||
}
|
||||
for _, n := range o.Nodes {
|
||||
n.Walk(pre, post)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
98
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.go
generated
vendored
Normal file
98
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.go
generated
vendored
Normal file
232
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.go
generated
vendored
Normal file
186
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.go
generated
vendored
Normal file
22
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
75
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.go
generated
vendored
Normal file
92
e2e/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.MatchString(pkg.Dir) || ginkgoIntegrationTestFilter.MatchString(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
|
||||
}
|
||||
}
|
117
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
Normal file
117
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"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 isHiddenFile(info) {
|
||||
continue
|
||||
}
|
||||
|
||||
if goTestRegExp.MatchString(info.Name()) {
|
||||
testHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(testModifiedTime) {
|
||||
testModifiedTime = info.ModTime()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if p.watchRegExp.MatchString(info.Name()) {
|
||||
codeHash += p.hashForFileInfo(info)
|
||||
if info.ModTime().After(codeModifiedTime) {
|
||||
codeModifiedTime = info.ModTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testHash += codeHash
|
||||
if codeModifiedTime.After(testModifiedTime) {
|
||||
testModifiedTime = codeModifiedTime
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func isHiddenFile(info os.FileInfo) bool {
|
||||
return strings.HasPrefix(info.Name(), ".") || strings.HasPrefix(info.Name(), "_")
|
||||
}
|
||||
|
||||
func (p *PackageHash) hashForFileInfo(info os.FileInfo) string {
|
||||
return fmt.Sprintf("%s_%d_%d", info.Name(), info.Size(), info.ModTime().UnixNano())
|
||||
}
|
85
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.go
generated
vendored
Normal file
85
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.go
generated
vendored
Normal file
87
e2e/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
e2e/vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.go
generated
vendored
Normal file
192
e2e/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