Skip to content

Commit 15d4bdc

Browse files
Luap99onsi
andauthoredMay 3, 2023
fix hang with ginkgo -p (#1192)
* integration: build interceptor binary automatically Trying to run this locally the interceptor binary did not exists. Lets just build this in SynchronizedBeforeSuite() automatically so users do not have to figure this out. Signed-off-by: Paul Holzinger <pholzing@redhat.com> * integration: make interceptor test parallel safe Using the same file name for all test cause conflicts when run in parallel, this causes the tests to fail for me. To fix this use a filename suffix with GinkgoParallelProcess() which prevents conflicts in the file name. Signed-off-by: Paul Holzinger <pholzing@redhat.com> * fix hang with ginkgo -p When you run any child process that stay around longer than the test suite ginkgo currently hangs. This is because the dup stdout/err fds are leaked into the child thus read() will block on it as there is at least one process still having the write pipe open. From ginkgos POV it looks like it is done, you see the ginkgo result output printed but then it just hangs and doe snot exit because of it. To fix it we set FD_CLOEXEC on the dup-ed fds, this prevents them from ever being leaking into other processes that could outlive the suite. There is a dup3() call the could be uses to set the CLOEXEC option directly but this seem linux only and thus is less portable. The fcntl call should be good enough here, we do not need to be worried about the race conditions described in the man page. Ideally we should do some error handling in that function for both the fcntl calls and the existing dup() above, however this seems like a rather big change. I am not so sure about how to test it properly, I added a test which just executes `ginkgo run -p` and a test which only starts `sleep 60`. Ginkgo then should exit right way and keep this process running. Then I just make sure the gingo exits in under 15 seconds. As long as it is below 60s it should fulfil the purpose. Fixes #1191 Signed-off-by: Paul Holzinger <pholzing@redhat.com> * Rearrange new interceptor hang tests and confirm they cover the issue in question --------- Signed-off-by: Paul Holzinger <pholzing@redhat.com> Co-authored-by: Onsi Fakhouri <onsijoe@gmail.com>
1 parent 6c7f8bb commit 15d4bdc

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed
 

‎TODO

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Backlog
2+
[] @G fix hang with ginkgo -p
3+
https://github.com/onsi/ginkgo/issues/1192
4+
[] @B biloba needs to support "McDonald's"
5+
[] @G Document order of execution for setup nodes on the same level
6+
https://github.com/onsi/ginkgo/issues/1172
7+
[] @G fail fast may cause Serial spec or cleanup Node interrupted
8+
https://github.com/onsi/ginkgo/issues/1178
9+
10+
# Needs-Prioritization
11+
[] @G Pull JUnit config out of code and into flags/config files
12+
https://github.com/onsi/ginkgo/issues/1115
13+
[] @G -exec support for `ginkgo run`
14+
https://github.com/onsi/ginkgo/issues/869
15+
[] @G Add `indent` to gcustom
16+
[] @G HaveField should not panic in FailureMessage if the field is non-existant. It should return a meaningful failure message.
17+
[] @G allow NodeTimeout and GracePeriod to live on containers and propagate down
18+
[] @G Clean up ReportEntry decoding:
19+
func (entry ReportEntry) DecodeValue(p interface{}) error {
20+
// if raw is assignable to p, assign
21+
// else - parse AsJSON
22+
}
23+
[] @B Biloba lets you get all innerHTML and emit it on failure
24+
[] @B equivalent of puppeteer text selector?
25+
[] @B how do we invoke async functions? what does await look like for those? maybe time to actually read? goal: remove the separate muhasibPostLogin function.
26+
[] @B https://github.com/onsi/biloba/issues/2
27+
[] @B right now polling an element fails if the browser if ollowing redirects. so i'm using Eventually(b.Location) - instead of just Eventually("#el").Should(b.Exist()). I think we need a more robust way to ensure biloba.
28+
[] @B biloba support for gettign "SelectedOption" instead of just "Value" for select inputs (e.g. b.OptionByName(...) instead of value?)
29+
[] @B ginkgo interrupt should not show the whole stacktrace. it's just too much!
30+
[] @B add cookie support
31+
chromedp.Run(b.Context, chromedp.ActionFunc(func(ctx context.Context) error {
32+
return storage.ClearDataForOrigin("*", "all").Do(ctx)
33+
}))
34+
35+
chromedp.Run(b.Context, chromedp.ActionFunc(func(ctx context.Context) error {
36+
expr := cdp.TimeSinceEpoch(time.Date(2091, 28, 3, 1, 40, 45, 0, time.UTC))
37+
return storage.SetCookies([]*network.CookieParam{{
38+
Name: "rallly-session",
39+
Value: "Fe26.2*1*66a5cae1dd8728fc7be37a1a3c485557606e526b16b472329be78168ad4d48c2*Yb_O9pN2K3APF6LXt9S3zg*IZQ_c5aukJzt-AIW__lL19igVhpFMGH9cK0PyFenF-2ti94BgBsLDf325DB2rsKE*3825906104968*a06f3cfe6ef65db1a30b5177cb767c914ca38c8fc3e2456de89d5bea5641611e*6HNCfDeEzgfeQO88IRJ8TfdG5IDzDQtt6WaoGAg5i98~2", Expires: &expr,
40+
Domain: "rallly.co",
41+
Path: "/",
42+
HTTPOnly: true,
43+
Secure: true,
44+
SameSite: network.CookieSameSiteLax,
45+
}}).WithBrowserContextID(b.BrowserContextID()).Do(ctx)
46+
}))
47+
[] @Ω Gomega should have an error returning mode, then tell pohly
48+
[] @Ω Gomega submatcher description interface and bare-element interface (the former for any sort of matcher that takes a submatcher; the latter specifically for matchers like Consistently etc. that would replace equalMatchersToElements.
49+
50+
51+
[] @B JSSelector (is a function that returns one or many nodes)
52+
[] @B ScrollTo etc.
53+
[] @B support for esbuild? or something? consider the auth_login_scripts tests - what might make them better?
54+
55+
# Long-term Backlog
56+
- VSCode Extension
57+
- Ginkgo WebView
58+
- Suite configuration
59+
- Suite parallelization
60+
- With the special subcase of supporting shared singleton resources
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main_test
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"testing"
8+
9+
. "github.com/onsi/ginkgo/v2"
10+
. "github.com/onsi/gomega"
11+
)
12+
13+
func TestInterceptorSleepFixture(t *testing.T) {
14+
RegisterFailHandler(Fail)
15+
RunSpecs(t, "TestInterceptorSleepFixture Suite")
16+
}
17+
18+
var _ = Describe("Ensuring that ginkgo -p does not hang when output is intercepted", func() {
19+
It("ginkgo -p should not hang on this spec", func() {
20+
fmt.Fprintln(os.Stdout, "Some STDOUT output")
21+
fmt.Fprintln(os.Stderr, "Some STDERR output")
22+
cmd := exec.Command("sleep", "60")
23+
Ω(cmd.Start()).Should(Succeed())
24+
})
25+
})

‎integration/output_interceptor_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package integration_test
22

33
import (
44
"os/exec"
5+
"time"
56

67
. "github.com/onsi/ginkgo/v2"
78
. "github.com/onsi/gomega"
9+
"github.com/onsi/gomega/gbytes"
810
"github.com/onsi/gomega/gexec"
911
)
1012

@@ -47,4 +49,20 @@ var _ = Describe("OutputInterceptor", func() {
4749
Ω(report.SpecReports[0].CapturedStdOutErr).Should(Equal("CAPTURED OUTPUT A\nCAPTURED OUTPUT B\n"))
4850
})
4951
})
52+
53+
Context("ensuring Ginkgo does not hang when a child process does not exit: https://github.com/onsi/ginkgo/issues/1191", func() {
54+
BeforeEach(func() {
55+
fm.MountFixture("interceptor_sleep")
56+
})
57+
58+
It("exits without hanging", func() {
59+
sess := startGinkgo(fm.PathTo("interceptor_sleep"), "--no-color", "--procs=2")
60+
Eventually(sess).WithTimeout(time.Second * 15).Should(gexec.Exit(0))
61+
62+
Ω(sess).Should(gbytes.Say("Captured StdOut/StdErr Output >>"))
63+
Ω(sess).Should(gbytes.Say("Some STDOUT output"))
64+
Ω(sess).Should(gbytes.Say("Some STDERR output"))
65+
Ω(sess).Should(gbytes.Say("<< Captured StdOut/StdErr Output"))
66+
})
67+
})
5068
})

‎internal/output_interceptor_unix.go

+11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ func (impl *dupSyscallOutputInterceptorImpl) CreateStdoutStderrClones() (*os.Fil
2626
stdoutCloneFD, _ := unix.Dup(1)
2727
stderrCloneFD, _ := unix.Dup(2)
2828

29+
// Important, set the fds to FD_CLOEXEC to prevent them leaking into childs
30+
// https://github.com/onsi/ginkgo/issues/1191
31+
flags, err := unix.FcntlInt(uintptr(stdoutCloneFD), unix.F_GETFD, 0)
32+
if err == nil {
33+
unix.FcntlInt(uintptr(stdoutCloneFD), unix.F_SETFD, flags|unix.FD_CLOEXEC)
34+
}
35+
flags, err = unix.FcntlInt(uintptr(stderrCloneFD), unix.F_GETFD, 0)
36+
if err == nil {
37+
unix.FcntlInt(uintptr(stderrCloneFD), unix.F_SETFD, flags|unix.FD_CLOEXEC)
38+
}
39+
2940
// And then wrap the clone file descriptors in files.
3041
// One benefit of this (that we don't use yet) is that we can actually write
3142
// to these files to emit output to the console even though we're intercepting output

0 commit comments

Comments
 (0)
Please sign in to comment.