github.com/thediveo/gons@v0.9.9/reexec/testing/pritipratel.go (about) 1 // Copyright 2020 Harald Albrecht. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package testing 16 17 import ( 18 "bufio" 19 "io" 20 "os" 21 "strings" 22 ) 23 24 // pritiPratel runs function f and passes only harmless error output destined 25 // to os.Stderr really on to os.Stderr. All dangerous talk about unwanted 26 // error truths will be sent into early retirement. Such as testing-related 27 // messages, which we silently drop. This is necessary as some applications 28 // using gons/reexec expect the re-executed child to return their results via 29 // stdout without any stderr output, so we don't want Golang's testing output 30 // to interfere here. 31 func pritiPratel(f func()) { 32 realStderr := os.Stderr 33 // Unfortunately, we cannot make use of the in-memory io.Pipe()s here, as 34 // os.Stderr is a *os.File, so it needs to have a file descriptor. In 35 // consequence, that leaves us with the sole option of a "real" pipe. 36 reader, writer, err := os.Pipe() 37 if err != nil { 38 panic("gons/reexec/testing: cannot create filtering pipe: " + err.Error()) 39 } 40 os.Stderr = writer 41 defer func() { 42 os.Stderr = realStderr 43 reader.Close() 44 writer.Close() 45 }() 46 // The stdout filter for filtering out unwanted Golang testing messages. 47 // It runs as a separate Go routine, which only terminates on (real) read 48 // errors. 49 done := make(chan struct{}) 50 go prattlepiper(reader, realStderr, done) 51 // Now run the desired function f() while scanning its output for unwanted 52 // rogue lies which we need to suppress. Definitely a Johnson function 53 // here. 54 f() 55 writer.Close() 56 <-done 57 } 58 59 var hide = []string{ 60 "coverage:", 61 "testing:", 62 } 63 64 // prattlepiper reads from the specified reader, passing the read prattle to 65 // the specified writer, but excluding certain topics, as specified in the 66 // hide list. 67 func prattlepiper(reader io.Reader, writer io.Writer, done chan struct{}) { 68 defer close(done) 69 r := bufio.NewReaderSize(reader, 1024) 70 new_line: 71 for { 72 // Assume that we're starting with a new line here, so sort out 73 // any output beginning with "coverage:" or "testing:". 74 line, isprefix, err := r.ReadLine() 75 if err != nil { 76 // bufio.Reader.ReadLine() either returns a non-nil line or it 77 // returns an error, never both. 78 return 79 } 80 for _, h := range hide { 81 if strings.HasPrefix(string(line), h) { 82 // This line of output should be dropped. In case this 83 // line is longer than the buffer, drop until the end of 84 // line chunk by chunk. 85 for isprefix { 86 _, isprefix, err = r.ReadLine() 87 if err != nil { 88 return 89 } 90 } 91 continue new_line 92 } 93 } 94 // It's output we should better pass on... 95 if _, err := writer.Write(line); err != nil { 96 return 97 } 98 for isprefix { 99 line, isprefix, err = r.ReadLine() 100 if err != nil { 101 return 102 } 103 if _, err := writer.Write(line); err != nil { 104 return 105 } 106 } 107 // Handle the case where we have read the final line before EOF, 108 // which doesn't end in \n: in this situation, we must not append 109 // any \n to the output. 110 if err := r.UnreadByte(); err != nil { 111 return 112 } 113 if b, err := r.ReadByte(); err == nil && b == '\n' { 114 if _, err := writer.Write([]byte{'\n'}); err != nil { 115 return 116 } 117 } 118 } 119 // unreachable 120 }