How to use PauseIntercepting method of internal Package

Best Ginkgo code snippet using internal.PauseIntercepting

Run Ginkgo automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

output_interceptor_test.go

Source: output_interceptor_test.go Github

copy
1package internal_test
2
3import (
4	"fmt"
5	"os"
6	"os/exec"
7	"runtime"
8
9	. "github.com/onsi/ginkgo/v2"
10	. "github.com/onsi/gomega"
11	"github.com/onsi/gomega/gbytes"
12
13	"github.com/onsi/ginkgo/v2/internal"
14)
15
16var _ = Describe("OutputInterceptor", func() {
17	var interceptor internal.OutputInterceptor
18
19	sharedInterceptorTests := func() {
20		It("intercepts output", func() {
21			for i := 0; i < 2048; i++ { //we loop here to stress test and make sure we aren't leaking any file descriptors
22				interceptor.StartInterceptingOutput()
23				fmt.Println("hi stdout")
24				fmt.Fprintln(os.Stderr, "hi stderr")
25				output := interceptor.StopInterceptingAndReturnOutput()
26				Ω(output).Should(Equal("hi stdout\nhi stderr\n"))
27			}
28		})
29
30		It("can forward intercepted output to a buffer", func() {
31			buffer := gbytes.NewBuffer()
32			interceptor.StartInterceptingOutputAndForwardTo(buffer)
33			fmt.Println("hi stdout")
34			fmt.Fprintln(os.Stderr, "hi stderr")
35			output := interceptor.StopInterceptingAndReturnOutput()
36			Ω(output).Should(Equal("hi stdout\nhi stderr\n"))
37			Ω(buffer).Should(gbytes.Say("hi stdout\nhi stderr\n"))
38		})
39
40		It("is stable across multiple shutdowns", func() {
41			numRoutines := runtime.NumGoroutine()
42			for i := 0; i < 2048; i++ { //we loop here to stress test and make sure we aren't leaking any file descriptors
43				interceptor.StartInterceptingOutput()
44				fmt.Println("hi stdout")
45				fmt.Fprintln(os.Stderr, "hi stderr")
46				output := interceptor.StopInterceptingAndReturnOutput()
47				Ω(output).Should(Equal("hi stdout\nhi stderr\n"))
48				interceptor.Shutdown()
49			}
50			Eventually(runtime.NumGoroutine).Should(BeNumerically("~", numRoutines, 10))
51		})
52
53		It("can bail out if stdout and stderr are tied up by an external process", func() {
54			// See GitHub issue #851: https://github.com/onsi/ginkgo/issues/851
55			interceptor.StartInterceptingOutput()
56			cmd := exec.Command("sleep", "60")
57			//by threading stdout and stderr through, the sleep process will hold them open and prevent the interceptor from stopping:
58			cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
59			Ω(cmd.Start()).Should(Succeed())
60			fmt.Println("hi stdout")
61			fmt.Fprintln(os.Stderr, "hi stderr")
62
63			// we try to stop here and see that we bail out eventually:
64			outputChan := make(chan string)
65			go func() {
66				outputChan <- interceptor.StopInterceptingAndReturnOutput()
67			}()
68			var output string
69			Eventually(outputChan, internal.BAILOUT_TIME*2).Should(Receive(&output))
70			Ω(output).Should(Equal(internal.BAILOUT_MESSAGE))
71
72			//subsequent attempts should be fine
73			interceptor.StartInterceptingOutput()
74			fmt.Println("hi stdout, again")
75			fmt.Fprintln(os.Stderr, "hi stderr, again")
76			output = interceptor.StopInterceptingAndReturnOutput()
77			Ω(output).Should(Equal("hi stdout, again\nhi stderr, again\n"))
78
79			cmd.Process.Kill()
80
81			interceptor.StartInterceptingOutput()
82			fmt.Println("hi stdout, once more")
83			fmt.Fprintln(os.Stderr, "hi stderr, once more")
84			output = interceptor.StopInterceptingAndReturnOutput()
85			Ω(output).Should(Equal("hi stdout, once more\nhi stderr, once more\n"))
86		})
87
88		It("doesn't get stuck if it's paused and resumed before starting an external process that attaches to stdout/stderr", func() {
89			// See GitHub issue #851: https://github.com/onsi/ginkgo/issues/851
90			interceptor.StartInterceptingOutput()
91			interceptor.PauseIntercepting()
92			cmd := exec.Command("sleep", "60")
93			//by threading stdout and stderr through, the sleep process will hold them open and prevent the interceptor from stopping:
94			cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
95			Ω(cmd.Start()).Should(Succeed())
96
97			interceptor.ResumeIntercepting()
98			fmt.Println("hi stdout")
99			fmt.Fprintln(os.Stderr, "hi stderr")
100			output := interceptor.StopInterceptingAndReturnOutput()
101
102			Ω(output).Should(Equal("hi stdout\nhi stderr\n"))
103			Ω(output).ShouldNot(ContainSubstring(internal.BAILOUT_MESSAGE))
104			cmd.Process.Kill()
105		})
106
107		It("can start/stop/pause/resume correctly", func() {
108			interceptor.StartInterceptingOutput()
109			fmt.Fprint(os.Stdout, "O-A")
110			fmt.Fprint(os.Stderr, "E-A")
111			interceptor.PauseIntercepting()
112			fmt.Fprint(os.Stdout, "O-B")
113			fmt.Fprint(os.Stderr, "E-B")
114			interceptor.ResumeIntercepting()
115			fmt.Fprint(os.Stdout, "O-C")
116			fmt.Fprint(os.Stderr, "E-C")
117			interceptor.ResumeIntercepting() //noop
118			fmt.Fprint(os.Stdout, "O-D")
119			fmt.Fprint(os.Stderr, "E-D")
120			interceptor.PauseIntercepting()
121			fmt.Fprint(os.Stdout, "O-E")
122			fmt.Fprint(os.Stderr, "E-E")
123			interceptor.PauseIntercepting() //noop
124			fmt.Fprint(os.Stdout, "O-F")
125			fmt.Fprint(os.Stderr, "E-F")
126			interceptor.ResumeIntercepting()
127			fmt.Fprint(os.Stdout, "O-G")
128			fmt.Fprint(os.Stderr, "E-G")
129			interceptor.StartInterceptingOutput() //noop
130			fmt.Fprint(os.Stdout, "O-H")
131			fmt.Fprint(os.Stderr, "E-H")
132			interceptor.PauseIntercepting()
133			output := interceptor.StopInterceptingAndReturnOutput()
134			Ω(output).Should(Equal("O-AE-AO-CE-CO-DE-DO-GE-GO-HE-H"))
135		})
136	}
137
138	Context("the OutputInterceptor for this OS", func() {
139		BeforeEach(func() {
140			interceptor = internal.NewOutputInterceptor()
141			DeferCleanup(interceptor.Shutdown)
142		})
143		sharedInterceptorTests()
144	})
145
146	Context("the OSGlobalReassigningOutputInterceptor used on windows", func() {
147		BeforeEach(func() {
148			interceptor = internal.NewOSGlobalReassigningOutputInterceptor()
149			DeferCleanup(interceptor.Shutdown)
150		})
151
152		sharedInterceptorTests()
153	})
154
155})
156
Full Screen

output_interceptor.go

Source: output_interceptor.go Github

copy
1package internal
2
3import (
4	"bytes"
5	"io"
6	"os"
7	"time"
8)
9
10const BAILOUT_TIME = 1 * time.Second
11const BAILOUT_MESSAGE = `Ginkgo detected an issue while intercepting output.
12
13When running in parallel, Ginkgo captures stdout and stderr output
14and attaches it to the running spec.  It looks like that process is getting
15stuck for this suite.
16
17This usually happens if you, or a library you are using, spin up an external
18process and set cmd.Stdout = os.Stdout and/or cmd.Stderr = os.Stderr.  This
19causes the external process to keep Ginkgo's output interceptor pipe open and
20causes output interception to hang.
21
22Ginkgo has detected this and shortcircuited the capture process.  The specs
23will continue running after this message however output from the external
24process that caused this issue will not be captured.
25
26You have several options to fix this.  In preferred order they are:
27
281. Pass GinkgoWriter instead of os.Stdout or os.Stderr to your process.
292. Ensure your process exits before the current spec completes.  If your
30process is long-lived and must cross spec boundaries, this option won't
31work for you.
323. Pause Ginkgo's output interceptor before starting your process and then
33resume it after.  Use PauseOutputInterception() and ResumeOutputInterception()
34to do this.
354. Set --output-interceptor-mode=none when running your Ginkgo suite.  This will
36turn off all output interception but allow specs to run in parallel without this
37issue.  You may miss important output if you do this including output from Go's
38race detector.
39
40More details on issue #851 - https://github.com/onsi/ginkgo/issues/851
41`
42
43/*
44The OutputInterceptor is used by to
45intercept and capture all stdin and stderr output during a test run.
46*/
47type OutputInterceptor interface {
48	StartInterceptingOutput()
49	StartInterceptingOutputAndForwardTo(io.Writer)
50	StopInterceptingAndReturnOutput() string
51
52	PauseIntercepting()
53	ResumeIntercepting()
54
55	Shutdown()
56}
57
58func NewOutputInterceptor() OutputInterceptor {
59	return NewOSGlobalReassigningOutputInterceptor()
60}
61
62type NoopOutputInterceptor struct{}
63
64func (interceptor NoopOutputInterceptor) StartInterceptingOutput()                      {}
65func (interceptor NoopOutputInterceptor) StartInterceptingOutputAndForwardTo(io.Writer) {}
66func (interceptor NoopOutputInterceptor) StopInterceptingAndReturnOutput() string       { return "" }
67func (interceptor NoopOutputInterceptor) PauseIntercepting()                            {}
68func (interceptor NoopOutputInterceptor) ResumeIntercepting()                           {}
69func (interceptor NoopOutputInterceptor) Shutdown()                                     {}
70
71type pipePair struct {
72	reader *os.File
73	writer *os.File
74}
75
76func startPipeFactory(pipeChannel chan pipePair, shutdown chan interface{}) {
77	for {
78		//make the next pipe...
79		pair := pipePair{}
80		pair.reader, pair.writer, _ = os.Pipe()
81		select {
82		//...and provide it to the next consumer (they are responsible for closing the files)
83		case pipeChannel <- pair:
84			continue
85		//...or close the files if we were told to shutdown
86		case <-shutdown:
87			pair.reader.Close()
88			pair.writer.Close()
89			return
90		}
91	}
92}
93
94type interceptorImplementation interface {
95	CreateStdoutStderrClones() (*os.File, *os.File)
96	ConnectPipeToStdoutStderr(*os.File)
97	RestoreStdoutStderrFromClones(*os.File, *os.File)
98	ShutdownClones(*os.File, *os.File)
99}
100
101type genericOutputInterceptor struct {
102	intercepting bool
103
104	stdoutClone *os.File
105	stderrClone *os.File
106	pipe        pipePair
107
108	shutdown           chan interface{}
109	emergencyBailout   chan interface{}
110	pipeChannel        chan pipePair
111	interceptedContent chan string
112
113	forwardTo         io.Writer
114	accumulatedOutput string
115
116	implementation interceptorImplementation
117}
118
119func (interceptor *genericOutputInterceptor) StartInterceptingOutput() {
120	interceptor.StartInterceptingOutputAndForwardTo(io.Discard)
121}
122
123func (interceptor *genericOutputInterceptor) StartInterceptingOutputAndForwardTo(w io.Writer) {
124	if interceptor.intercepting {
125		return
126	}
127	interceptor.accumulatedOutput = ""
128	interceptor.forwardTo = w
129	interceptor.ResumeIntercepting()
130}
131
132func (interceptor *genericOutputInterceptor) StopInterceptingAndReturnOutput() string {
133	if interceptor.intercepting {
134		interceptor.PauseIntercepting()
135	}
136	return interceptor.accumulatedOutput
137}
138
139func (interceptor *genericOutputInterceptor) ResumeIntercepting() {
140	if interceptor.intercepting {
141		return
142	}
143	interceptor.intercepting = true
144	if interceptor.stdoutClone == nil {
145		interceptor.stdoutClone, interceptor.stderrClone = interceptor.implementation.CreateStdoutStderrClones()
146		interceptor.shutdown = make(chan interface{})
147		go startPipeFactory(interceptor.pipeChannel, interceptor.shutdown)
148	}
149
150	// Now we make a pipe, we'll use this to redirect the input to the 1 and 2 file descriptors (this is how everything else in the world is tring to log to stdout and stderr)
151	// we get the pipe from our pipe factory.  it runs in the background so we can request the next pipe while the spec being intercepted is running
152	interceptor.pipe = <-interceptor.pipeChannel
153
154	interceptor.emergencyBailout = make(chan interface{})
155
156	//Spin up a goroutine to copy data from the pipe into a buffer, this is how we capture any output the user is emitting
157	go func() {
158		buffer := &bytes.Buffer{}
159		destination := io.MultiWriter(buffer, interceptor.forwardTo)
160		copyFinished := make(chan interface{})
161		reader := interceptor.pipe.reader
162		go func() {
163			io.Copy(destination, reader)
164			reader.Close() // close the read end of the pipe so we don't leak a file descriptor
165			close(copyFinished)
166		}()
167		select {
168		case <-copyFinished:
169			interceptor.interceptedContent <- buffer.String()
170		case <-interceptor.emergencyBailout:
171			interceptor.interceptedContent <- ""
172		}
173	}()
174
175	interceptor.implementation.ConnectPipeToStdoutStderr(interceptor.pipe.writer)
176}
177
178func (interceptor *genericOutputInterceptor) PauseIntercepting() {
179	if !interceptor.intercepting {
180		return
181	}
182	// first we have to close the write end of the pipe.  To do this we have to close all file descriptors pointing
183	// to the write end.  So that would be the pipewriter itself, and FD #1 and FD #2 if we've Dup2'd them
184	interceptor.pipe.writer.Close() // the pipewriter itself
185
186	// we also need to stop intercepting. we do that by reconnecting the stdout and stderr file descriptions back to their respective #1 and #2 file descriptors;
187	// this also closes #1 and #2 before it points that their original stdout and stderr file descriptions
188	interceptor.implementation.RestoreStdoutStderrFromClones(interceptor.stdoutClone, interceptor.stderrClone)
189
190	var content string
191	select {
192	case content = <-interceptor.interceptedContent:
193	case <-time.After(BAILOUT_TIME):
194		/*
195			By closing all the pipe writer's file descriptors associated with the pipe writer's file description the io.Copy reading from the reader
196			should eventually receive an EOF and exit.
197
198			**However**, if the user has spun up an external process and passed in os.Stdout/os.Stderr to cmd.Stdout/cmd.Stderr then the external process
199			will have a file descriptor pointing to the pipe writer's file description and it will not close until the external process exits.
200
201			That would leave us hanging here waiting for the io.Copy to close forever.  Instead we invoke this emergency escape valve.  This returns whatever
202			content we've got but leaves the io.Copy running.  This ensures the external process can continue writing without hanging at the cost of leaking a goroutine
203			and file descriptor (those these will be cleaned up when the process exits).
204
205			We tack on a message to notify the user that they've hit this edgecase and encourage them to address it.
206		*/
207		close(interceptor.emergencyBailout)
208		content = <-interceptor.interceptedContent + BAILOUT_MESSAGE
209	}
210
211	interceptor.accumulatedOutput += content
212	interceptor.intercepting = false
213}
214
215func (interceptor *genericOutputInterceptor) Shutdown() {
216	interceptor.PauseIntercepting()
217
218	if interceptor.stdoutClone != nil {
219		close(interceptor.shutdown)
220		interceptor.implementation.ShutdownClones(interceptor.stdoutClone, interceptor.stderrClone)
221		interceptor.stdoutClone = nil
222		interceptor.stderrClone = nil
223	}
224}
225
226/* This is used on windows builds but included here so it can be explicitly tested on unix systems too */
227func NewOSGlobalReassigningOutputInterceptor() OutputInterceptor {
228	return &genericOutputInterceptor{
229		interceptedContent: make(chan string),
230		pipeChannel:        make(chan pipePair),
231		shutdown:           make(chan interface{}),
232		implementation:     &osGlobalReassigningOutputInterceptorImpl{},
233	}
234}
235
236type osGlobalReassigningOutputInterceptorImpl struct{}
237
238func (impl *osGlobalReassigningOutputInterceptorImpl) CreateStdoutStderrClones() (*os.File, *os.File) {
239	return os.Stdout, os.Stderr
240}
241
242func (impl *osGlobalReassigningOutputInterceptorImpl) ConnectPipeToStdoutStderr(pipeWriter *os.File) {
243	os.Stdout = pipeWriter
244	os.Stderr = pipeWriter
245}
246
247func (impl *osGlobalReassigningOutputInterceptorImpl) RestoreStdoutStderrFromClones(stdoutClone *os.File, stderrClone *os.File) {
248	os.Stdout = stdoutClone
249	os.Stderr = stderrClone
250}
251
252func (impl *osGlobalReassigningOutputInterceptorImpl) ShutdownClones(_ *os.File, _ *os.File) {
253	//noop
254}
255
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Most used method in

Trigger PauseIntercepting code on LambdaTest Cloud Grid

Execute automation tests with PauseIntercepting on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)