diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d5506ca29..663feb1eef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +## 2.23.3 + +### Fixes + +- allow `-` as a standalone argument [cfcc1a5] +- Bug Fix: Add GinkoTBWrapper.Chdir() and GinkoTBWrapper.Context() [feaf292] +- ignore exit code for symbol test on linux [88e2282] + +## 2.23.2 + +🎉🎉🎉 + +At long last, some long-standing performance gaps between `ginkgo` and `go test` have been resolved! + +Ginkgo operates by running `go test -c` to generate test binaries, and then running those binaries. It turns out that the compilation step of `go test -c` is slower than `go test`'s compilation step because `go test` strips out debug symbols (`ldflags=-w`) whereas `go test -c` does not. + +Ginkgo now passes the appropriate `ldflags` to `go test -c` when running specs to strip out symbols. This is only done when it is safe to do so and symbols are preferred when profiling is enabled and when `ginkgo build` is called explicitly. + +This, coupled, with the [instructions for disabling XProtect on MacOS](https://onsi.github.io/ginkgo/#if-you-are-running-on-macos) yields a much better performance experience with Ginkgo. + +## 2.23.1 + +## 🚨 For users on MacOS 🚨 + +A long-standing Ginkgo performance issue on MacOS seems to be due to mac's antimalware XProtect. You can follow the instructions [here](https://onsi.github.io/ginkgo/#if-you-are-running-on-macos) to disable it in your terminal. Doing so sped up Ginkgo's own test suite from 1m8s to 47s. + +### Fixes + +Ginkgo's CLI is now a bit clearer if you pass flags in incorrectly: + +- make it clearer that you need to pass a filename to the various profile flags, not an absolute directory [a0e52ff] +- emit an error and exit if the ginkgo invocation includes flags after positional arguments [b799d8d] + +This might cause existing CI builds to fail. If so then it's likely that your CI build was misconfigured and should be corrected. Open an issue if you need help. + ## 2.23.0 Ginkgo 2.23.0 adds a handful of methods to `GinkgoT()` to make it compatible with the `testing.TB` interface in Go 1.24. `GinkgoT().Context()`, in particular, is a useful shorthand for generating a new context that will clean itself up in a `DeferCleanup()`. This has subtle behavior differences from the golang implementation but should make sense in a Ginkgo... um... context. diff --git a/docs/index.md b/docs/index.md index fe201c5990..4a654af256 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,6 +48,18 @@ You should now be able to run `ginkgo version` at the command line and see the G **Note** you _must_ make sure the version of the `ginkgo` cli you install is the same as the version of Ginkgo in your `go.mod` file. You can do this by running `go install github.com/onsi/ginkgo/v2/ginkgo` from your package. +#### If you are running on MacOS + +Ginkgo runs by using `go test -c` to compile test binaries for each subpackage. It then invokes those compiled binaries. MacOS's XProtect malware detector slows this process down substantially. You can disable [XProtect for your terminal](https://stackoverflow.com/questions/60176405/macos-catalina-developer-tools-tab-is-hidden/65240575#65240575) by running: + +```bash +spctl developer-mode enable-terminal +``` + +and then opening up **System Settings** > **Privacy & Security** > **Developer Tools** and then adding your terminal to the list of developer tools. + +> Doing this on an M1 Max macbook pro resulted in Ginkgo's unit and integration suites running in 47s instead of 1m 8s. + #### Upgrading Ginkgo To upgrade Ginkgo run: @@ -1724,7 +1736,7 @@ Describe("handling requests", func() { }) ``` -all the infrastructure around generating table entry descriptions applies here as well - though the description will be the title of the generated container. Note that you **must** add subject nodes in the body function if you want `DescribeHandleSubtree` to add specs. +all the infrastructure around generating table entry descriptions applies here as well - though the description will be the title of the generated container. Note that you **must** add subject nodes in the body function if you want `DescribeTableSubtree` to add specs. ### Alternatives to Dot-Importing Ginkgo @@ -3733,7 +3745,7 @@ You can also specify the `--covermode` to be one of `set` ("was this code called When run with `--cover`, Ginkgo will generate a single `coverprofile.out` file that captures the coverage statistics of all the suites that ran. You can change the name of this file by specifying `-coverprofile=filename`. If you would like to keep separate coverprofiles for each suite use the `--keep-separate-coverprofiles` option. -Ginkgo also honors the `--output-dir` flag when generating coverprofiles. If you specify `--output-dir` the generated coverprofile will be placed in the requested directory. If you also specify `--keep-separate-coverprofiles` individual package coverprofiles will be placed in the requested directory and namespaced with a prefix that contains the name of the package in question. +Note that `-coverprofile` only takes a filename, not a path. To place the coverprofile in a particular path you should specify `--output-dir` the generated coverprofile will be placed in the requested directory. If you also specify `--keep-separate-coverprofiles` individual package coverprofiles will be placed in the requested directory and namespaced with a prefix that contains the name of the package in question. Finally, when running a suite that has [programmatically focused specs](#focused-specs) (i.e. specs with the `Focus` decorator or with nodes prefixed with an `F`) Ginkgo exits the suite early with a non-zero exit code. This interferes with `go test`'s profiling code and prevents profiles from being generated. Ginkgo will tell you this has happened. If you want to profile just a subset of your suite you'll need to use a different [mechanism](#filtering-specs) to filter your specs. diff --git a/ginkgo/build/build_command.go b/ginkgo/build/build_command.go index a071b8d091..2b36b2feb9 100644 --- a/ginkgo/build/build_command.go +++ b/ginkgo/build/build_command.go @@ -44,7 +44,7 @@ func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.Go internal.VerifyCLIAndFrameworkVersion(suites) opc := internal.NewOrderedParallelCompiler(cliConfig.ComputedNumCompilers()) - opc.StartCompiling(suites, goFlagsConfig) + opc.StartCompiling(suites, goFlagsConfig, true) for { suiteIdx, suite := opc.Next() diff --git a/ginkgo/command/command.go b/ginkgo/command/command.go index 12e0e56591..79b83a3af1 100644 --- a/ginkgo/command/command.go +++ b/ginkgo/command/command.go @@ -24,7 +24,11 @@ func (c Command) Run(args []string, additionalArgs []string) { if err != nil { AbortWithUsage(err.Error()) } - + for _, arg := range args { + if len(arg) > 1 && strings.HasPrefix(arg, "-") { + AbortWith(types.GinkgoErrors.FlagAfterPositionalParameter().Error()) + } + } c.Command(args, additionalArgs) } diff --git a/ginkgo/command/command_test.go b/ginkgo/command/command_test.go index 5104e9e18b..ececb86d1e 100644 --- a/ginkgo/command/command_test.go +++ b/ginkgo/command/command_test.go @@ -21,8 +21,12 @@ var _ = Describe("Command", func() { fs, err := types.NewGinkgoFlagSet( types.GinkgoFlags{ {Name: "contrabulaturally", KeyPath: "C", Usage: "with irridiacy"}, + {Name: "fillabluster", KeyPath: "F", Usage: "with grace"}, }, - &(struct{ C int }{C: 17}), + &(struct { + C int + F int + }{C: 17, F: 12}), types.GinkgoFlagSections{}, ) Ω(err).ShouldNot(HaveOccurred()) @@ -41,7 +45,7 @@ var _ = Describe("Command", func() { Context("when flags fails to parse", func() { It("aborts with usage", func() { Ω(func() { - c.Run([]string{"-not-a-flag=oops"}, []string{"additional", "args"}) + c.Run([]string{"-not-a-flag=oops"}, []string{"additional", "-args"}) }).Should(PanicWith(SatisfyAll( HaveField("ExitCode", 1), HaveField("Error", HaveOccurred()), @@ -54,14 +58,29 @@ var _ = Describe("Command", func() { Context("when flags parse", func() { It("runs the command", func() { - c.Run([]string{"-contrabulaturally=16", "and-an-arg", "and-another"}, []string{"additional", "args"}) + c.Run([]string{"-contrabulaturally=16", "and-an-arg", "and-another", "-"}, []string{"additional", "-args"}) Ω(rt).Should(HaveRun("enflabulate")) - Ω(rt.DataFor("enflabulate")["Args"]).Should(Equal([]string{"and-an-arg", "and-another"})) - Ω(rt.DataFor("enflabulate")["AdditionalArgs"]).Should(Equal([]string{"additional", "args"})) + Ω(rt.DataFor("enflabulate")["Args"]).Should(Equal([]string{"and-an-arg", "and-another", "-"})) + Ω(rt.DataFor("enflabulate")["AdditionalArgs"]).Should(Equal([]string{"additional", "-args"})) }) }) + + Context("when flags appear after named arguments", func() { + It("fails", func() { + Ω(func() { + c.Run([]string{"-contrabulaturally=16", "an-arg", "another-arg", "-fillabuster=10"}, []string{"additional", "-args"}) + }).Should(PanicWith(SatisfyAll( + HaveField("ExitCode", 1), + HaveField("Error", HaveOccurred()), + HaveField("EmitUsage", BeFalse()), + ))) + + Ω(rt).Should(HaveTrackedNothing()) + }) + }) + }) Describe("Usage", func() { @@ -85,6 +104,8 @@ var _ = Describe("Command", func() { "", " --contrabulaturally{{/}} [int] {{gray}}{{/}}", " {{light-gray}}with irridiacy{{/}}", + " --fillabluster{{/}} [int] {{gray}}{{/}}", + " {{light-gray}}with grace{{/}}", "", "", }, "\n") diff --git a/ginkgo/command/program_test.go b/ginkgo/command/program_test.go index 04b4bb9b86..f04ea94e61 100644 --- a/ginkgo/command/program_test.go +++ b/ginkgo/command/program_test.go @@ -117,11 +117,11 @@ var _ = Describe("Program", func() { Context("when passed arguments and additional arguments", func() { BeforeEach(func() { - program.RunAndExit([]string{"omicron", "gamma", "arg1", "-arg2", "--", "addArg1", "addArg2"}) + program.RunAndExit([]string{"omicron", "gamma", "arg1", "arg2", "--", "addArg1", "addArg2"}) }) It("passes both in", func() { Ω(rt).Should(HaveTracked("gamma", "exit")) - Ω(rt).Should(HaveRunWithData("gamma", "Args", []string{"arg1", "-arg2"}, "AdditionalArgs", []string{"addArg1", "addArg2"})) + Ω(rt).Should(HaveRunWithData("gamma", "Args", []string{"arg1", "arg2"}, "AdditionalArgs", []string{"addArg1", "addArg2"})) Ω(rt).Should(HaveRunWithData("exit", "Code", 0)) Ω(buf.Contents()).Should(BeEmpty()) }) diff --git a/ginkgo/internal/compile.go b/ginkgo/internal/compile.go index 48827cc5ef..7bbe6be0fc 100644 --- a/ginkgo/internal/compile.go +++ b/ginkgo/internal/compile.go @@ -11,7 +11,7 @@ import ( "github.com/onsi/ginkgo/v2/types" ) -func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite { +func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig, preserveSymbols bool) TestSuite { if suite.PathToCompiledTest != "" { return suite } @@ -46,7 +46,7 @@ func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite 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) + args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, "./", pathToInvocationPath, preserveSymbols) if err != nil { suite.State = TestSuiteStateFailedToCompile suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error()) @@ -120,7 +120,7 @@ func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler { } } -func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) { +func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig, preserveSymbols bool) { opc.stopped = false opc.idx = 0 opc.numSuites = len(suites) @@ -135,7 +135,7 @@ func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsCon stopped := opc.stopped opc.mutex.Unlock() if !stopped { - suite = CompileSuite(suite, goFlagsConfig) + suite = CompileSuite(suite, goFlagsConfig, preserveSymbols) } c <- suite } diff --git a/ginkgo/performance/performance_suite_test.go b/ginkgo/performance/performance_suite_test.go index adea40bd2c..1cc2ade3bf 100644 --- a/ginkgo/performance/performance_suite_test.go +++ b/ginkgo/performance/performance_suite_test.go @@ -51,8 +51,8 @@ var _ = SynchronizedAfterSuite(func() {}, func() { }) /* - GoModCacheManager sets up a new GOMODCACHE and knows how to clear it - This allows us to bust the go mod cache. +GoModCacheManager sets up a new GOMODCACHE and knows how to clear it +This allows us to bust the go mod cache. */ type GoModCacheManager struct { Path string @@ -302,7 +302,7 @@ func RunScenarioWithGinkgoInternals(stopwatch *gmeasure.Stopwatch, settings Scen for suite := range compile { if !suite.State.Is(internal.TestSuiteStateCompiled) { subStopwatch := stopwatch.NewStopwatch() - suite = internal.CompileSuite(suite, goFlagsConfig) + suite = internal.CompileSuite(suite, goFlagsConfig, false) subStopwatch.Record("compile-test: "+suite.PackageName, annotation) Ω(suite.CompilationError).Should(BeNil()) } diff --git a/ginkgo/run/run_command.go b/ginkgo/run/run_command.go index b7d77390bb..03875b9796 100644 --- a/ginkgo/run/run_command.go +++ b/ginkgo/run/run_command.go @@ -107,7 +107,7 @@ OUTER_LOOP: } opc := internal.NewOrderedParallelCompiler(r.cliConfig.ComputedNumCompilers()) - opc.StartCompiling(suites, r.goFlagsConfig) + opc.StartCompiling(suites, r.goFlagsConfig, false) SUITE_LOOP: for { diff --git a/ginkgo/watch/watch_command.go b/ginkgo/watch/watch_command.go index bde4193ce7..fe1ca30519 100644 --- a/ginkgo/watch/watch_command.go +++ b/ginkgo/watch/watch_command.go @@ -153,7 +153,7 @@ func (w *SpecWatcher) WatchSpecs(args []string, additionalArgs []string) { } func (w *SpecWatcher) compileAndRun(suite internal.TestSuite, additionalArgs []string) internal.TestSuite { - suite = internal.CompileSuite(suite, w.goFlagsConfig) + suite = internal.CompileSuite(suite, w.goFlagsConfig, false) if suite.State.Is(internal.TestSuiteStateFailedToCompile) { fmt.Println(suite.CompilationError.Error()) return suite diff --git a/ginkgo_t_dsl.go b/ginkgo_t_dsl.go index 0ca6e235b4..993279de29 100644 --- a/ginkgo_t_dsl.go +++ b/ginkgo_t_dsl.go @@ -130,6 +130,12 @@ type GinkgoTBWrapper struct { func (g *GinkgoTBWrapper) Cleanup(f func()) { g.GinkgoT.Cleanup(f) } +func (g *GinkgoTBWrapper) Chdir(dir string) { + g.GinkgoT.Chdir(dir) +} +func (g *GinkgoTBWrapper) Context() context.Context { + return g.GinkgoT.Context() +} func (g *GinkgoTBWrapper) Error(args ...any) { g.GinkgoT.Error(args...) } diff --git a/integration/_fixtures/symbol_fixture/symbol_fixture_suite_test.go b/integration/_fixtures/symbol_fixture/symbol_fixture_suite_test.go new file mode 100644 index 0000000000..e7c915982a --- /dev/null +++ b/integration/_fixtures/symbol_fixture/symbol_fixture_suite_test.go @@ -0,0 +1,21 @@ +package symbol_fixture_test + +import ( + "fmt" + "os/exec" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestSymbolFixture(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "SymbolFixture Suite") +} + +var _ = It("prints out its symbols", func() { + cmd := exec.Command("go", "tool", "nm", "symbol_fixture.test") + output, _ := cmd.CombinedOutput() + fmt.Println(string(output)) +}) diff --git a/integration/precompiled_test.go b/integration/precompiled_test.go index 2ca71601d1..29512d77b5 100644 --- a/integration/precompiled_test.go +++ b/integration/precompiled_test.go @@ -25,6 +25,14 @@ var _ = Describe("ginkgo build", func() { Ω(fm.PathTo("passing_ginkgo_tests", "passing_ginkgo_tests.test")).Should(BeAnExistingFile()) }) + It("should have the symbols in the compiled binary", func() { + cmd := exec.Command("go", "tool", "nm", fm.PathTo("passing_ginkgo_tests", "passing_ginkgo_tests.test")) + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Ω(err).ShouldNot(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + Ω(session).Should(gbytes.Say("github.com/onsi/ginkgo/v2.It")) // a symbol from ginkgo + }) + It("should be possible to run the test binary directly", func() { cmd := exec.Command("./passing_ginkgo_tests.test") cmd.Dir = fm.PathTo("passing_ginkgo_tests") diff --git a/integration/profiling_test.go b/integration/profiling_test.go index 96a9e57c3c..c4a9068acb 100644 --- a/integration/profiling_test.go +++ b/integration/profiling_test.go @@ -141,6 +141,12 @@ var _ = Describe("Profiling Specs", func() { Ω(fm.PathTo("coverage", "myprofile.out")).Should(BeAnExistingFile()) Ω(fm.PathTo("coverage", "coverprofile.out")).ShouldNot(BeAnExistingFile()) }) + + It("fails if the cover profile is not just a filename", func() { + session := startGinkgo(fm.PathTo("coverage"), "--no-color", "-coverprofile=/path/to/profile.out") + Eventually(session).Should(gexec.Exit(1)) + Ω(session.Err).Should(gbytes.Say(`--coverprofile expects a filename but was given a path: /path/to/profile.out`)) + }) }) Context("when multiple suites are tested", func() { diff --git a/integration/run_test.go b/integration/run_test.go index e59c2384ff..e15febacea 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -458,6 +458,27 @@ var _ = Describe("Running Specs", func() { }) }) + Describe("optimizing build times by stripping symbols", func() { + BeforeEach(func() { + fm.MountFixture("symbol") + }) + Context("when no symbols are required (i.e. no profiling)", func() { + It("should not have symbols", func() { + session := startGinkgo(fm.PathTo("symbol"), "--no-color") + Eventually(session).Should(gexec.Exit(0)) + Ω(session).ShouldNot(gbytes.Say("github.com/onsi/ginkgo/v2.It")) // a symbol from ginkgo + }) + }) + + Context("when symbols are required (i.e. with profiling)", func() { + It("should have symbols", func() { + session := startGinkgo(fm.PathTo("symbol"), "--no-color", "--cpuprofile=cpu.out") + Eventually(session).Should(gexec.Exit(0)) + Ω(session).Should(gbytes.Say("github.com/onsi/ginkgo/v2.It")) // a symbol from ginkgo + }) + }) + }) + Context("when there is a version mismatch between the cli and the test package", func() { It("emits a useful error and tries running", func() { fm.MountFixture(("version_mismatch")) diff --git a/types/config.go b/types/config.go index 9b28924321..ca837b0557 100644 --- a/types/config.go +++ b/types/config.go @@ -231,6 +231,10 @@ func (g GoFlagsConfig) BinaryMustBePreserved() bool { return g.BlockProfile != "" || g.CPUProfile != "" || g.MemProfile != "" || g.MutexProfile != "" } +func (g GoFlagsConfig) NeedsSymbols() bool { + return g.BinaryMustBePreserved() +} + // Configuration that were deprecated in 2.0 type deprecatedConfig struct { DebugParallel bool @@ -257,8 +261,12 @@ var FlagSections = GinkgoFlagSections{ {Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"}, {Key: "failure", Style: "{{red}}", Heading: "Failure Handling"}, {Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"}, - {Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis"}, - {Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis"}, + {Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis", + Description: "When generating a cover files, please pass a filename {{bold}}not{{/}} a path. To specify a different directory use {{magenta}}--output-dir{{/}}.", + }, + {Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis", + Description: "When generating profile files, please pass filenames {{bold}}not{{/}} a path. Ginkgo will generate a profile file with the given name in the package's directory. To specify a different directory use {{magenta}}--output-dir{{/}}.", + }, {Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests", Description: "In addition to these flags, Ginkgo supports a few debugging environment variables. To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}. To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."}, {Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"}, @@ -572,7 +580,7 @@ var GoBuildFlags = GinkgoFlags{ // GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI var GoRunFlags = GinkgoFlags{ {KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis", - Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover.`}, + Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover. Must be passed a filename, not a path. Use output-dir to control the location of the output.`}, {KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis", Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`}, {KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis", @@ -600,6 +608,22 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails()) } + if strings.ContainsRune(goFlagsConfig.CoverProfile, os.PathSeparator) { + errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--coverprofile", goFlagsConfig.CoverProfile)) + } + if strings.ContainsRune(goFlagsConfig.CPUProfile, os.PathSeparator) { + errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--cpuprofile", goFlagsConfig.CPUProfile)) + } + if strings.ContainsRune(goFlagsConfig.MemProfile, os.PathSeparator) { + errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--memprofile", goFlagsConfig.MemProfile)) + } + if strings.ContainsRune(goFlagsConfig.BlockProfile, os.PathSeparator) { + errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--blockprofile", goFlagsConfig.BlockProfile)) + } + if strings.ContainsRune(goFlagsConfig.MutexProfile, os.PathSeparator) { + errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--mutexprofile", goFlagsConfig.MutexProfile)) + } + //initialize the output directory if cliConfig.OutputDir != "" { err := os.MkdirAll(cliConfig.OutputDir, 0777) @@ -620,7 +644,7 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo } // GenerateGoTestCompileArgs is used by the Ginkgo CLI to generate command line arguments to pass to the go test -c command when compiling the test -func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild string, pathToInvocationPath string) ([]string, error) { +func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild string, pathToInvocationPath string, preserveSymbols bool) ([]string, error) { // if the user has set the CoverProfile run-time flag make sure to set the build-time cover flag to make sure // the built test binary can generate a coverprofile if goFlagsConfig.CoverProfile != "" { @@ -643,6 +667,10 @@ func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild strin goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",") } + if !goFlagsConfig.NeedsSymbols() && goFlagsConfig.LDFlags == "" && !preserveSymbols { + goFlagsConfig.LDFlags = "-w -s" + } + args := []string{"test", "-c", packageToBuild} goArgs, err := GenerateFlagArgs( GoBuildFlags, diff --git a/types/errors.go b/types/errors.go index 6e5eec9889..c3f562f776 100644 --- a/types/errors.go +++ b/types/errors.go @@ -629,6 +629,20 @@ func (g ginkgoErrors) BothRepeatAndUntilItFails() error { } } +func (g ginkgoErrors) ExpectFilenameNotPath(flag string, path string) error { + return GinkgoError{ + Heading: fmt.Sprintf("%s expects a filename but was given a path: %s", flag, path), + Message: fmt.Sprintf("%s takes a filename, not a path. Use --output-dir to specify a directory to collect all test outputs.", flag), + } +} + +func (g ginkgoErrors) FlagAfterPositionalParameter() error { + return GinkgoError{ + Heading: "Malformed arguments - detected a flag after the package liste", + Message: "Make sure all flags appear {{bold}}after{{/}} the Ginkgo subcommand and {{bold}}before{{/}} your list of packages (or './...').\n{{gray}}e.g. 'ginkgo run -p my_package' is valid but `ginkgo -p run my_package` is not.\n{{gray}}e.g. 'ginkgo -p -vet ./...' is valid but 'ginkgo -p ./... -vet' is not{{/}}", + } +} + /* Stack-Trace parsing errors */ func (g ginkgoErrors) FailedToParseStackTrace(message string) error { diff --git a/types/version.go b/types/version.go index 23c030cdb6..cfbba0d905 100644 --- a/types/version.go +++ b/types/version.go @@ -1,3 +1,3 @@ package types -const VERSION = "2.23.0" +const VERSION = "2.23.3"