github.com/onsi/ginkgo@v1.16.6-0.20211118180735-4e1925ba4c95/internal/output_interceptor_test.go (about) 1 package internal_test 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "runtime" 8 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 "github.com/onsi/gomega/gbytes" 12 13 "github.com/onsi/ginkgo/internal" 14 ) 15 16 var _ = 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 })