2 Package gbytes provides a buffer that supports incrementally detecting input.
4 You use gbytes.Buffer with the gbytes.Say matcher. When Say finds a match, it fastforwards the buffer's read cursor to the end of that match.
6 Subsequent matches against the buffer will only operate against data that appears *after* the read cursor.
8 The read cursor is an opaque implementation detail that you cannot access. You should use the Say matcher to sift through the buffer. You can always
9 access the entire buffer's contents with Contents().
24 gbytes.Buffer implements an io.Writer and can be used with the gbytes.Say matcher.
26 You should only use a gbytes.Buffer in test code. It stores all writes in an in-memory buffer - behavior that is inappropriate for production code!
32 detectCloser chan interface{}
37 NewBuffer returns a new gbytes.Buffer
39 func NewBuffer() *Buffer {
46 BufferWithBytes returns a new gbytes.Buffer seeded with the passed in bytes
48 func BufferWithBytes(bytes []byte) *Buffer {
56 Write implements the io.Writer interface
58 func (b *Buffer) Write(p []byte) (n int, err error) {
63 return 0, errors.New("attempt to write to closed buffer")
66 b.contents = append(b.contents, p...)
71 Read implements the io.Reader interface. It advances the
74 Returns an error if called after Close.
76 func (b *Buffer) Read(d []byte) (int, error) {
81 return 0, errors.New("attempt to read from closed buffer")
84 if uint64(len(b.contents)) <= b.readCursor {
88 n := copy(d, b.contents[b.readCursor:])
89 b.readCursor += uint64(n)
95 Close signifies that the buffer will no longer be written to
97 func (b *Buffer) Close() error {
107 Closed returns true if the buffer has been closed
109 func (b *Buffer) Closed() bool {
111 defer b.lock.Unlock()
117 Contents returns all data ever written to the buffer.
119 func (b *Buffer) Contents() []byte {
121 defer b.lock.Unlock()
123 contents := make([]byte, len(b.contents))
124 copy(contents, b.contents)
129 Detect takes a regular expression and returns a channel.
131 The channel will receive true the first time data matching the regular expression is written to the buffer.
132 The channel is subsequently closed and the buffer's read-cursor is fast-forwarded to just after the matching region.
134 You typically don't need to use Detect and should use the ghttp.Say matcher instead. Detect is useful, however, in cases where your code must
135 be branch and handle different outputs written to the buffer.
137 For example, consider a buffer hooked up to the stdout of a client library. You may (or may not, depending on state outside of your control) need to authenticate the client library.
139 You could do something like:
142 case <-buffer.Detect("You are not logged in"):
144 case <-buffer.Detect("Success"):
146 case <-time.After(time.Second):
149 buffer.CancelDetects()
151 You should always call CancelDetects after using Detect. This will close any channels that have not detected and clean up the goroutines that were spawned to support them.
153 Finally, you can pass detect a format string followed by variadic arguments. This will construct the regexp using fmt.Sprintf.
155 func (b *Buffer) Detect(desired string, args ...interface{}) chan bool {
156 formattedRegexp := desired
158 formattedRegexp = fmt.Sprintf(desired, args...)
160 re := regexp.MustCompile(formattedRegexp)
163 defer b.lock.Unlock()
165 if b.detectCloser == nil {
166 b.detectCloser = make(chan interface{})
169 closer := b.detectCloser
170 response := make(chan bool)
172 ticker := time.NewTicker(10 * time.Millisecond)
174 defer close(response)
179 data, cursor := b.contents[b.readCursor:], b.readCursor
180 loc := re.FindIndex(data)
186 newCursorPosition := cursor + uint64(loc[1])
187 if newCursorPosition >= b.readCursor {
188 b.readCursor = newCursorPosition
203 CancelDetects cancels any pending detects and cleans up their goroutines. You should always call this when you're done with a set of Detect channels.
205 func (b *Buffer) CancelDetects() {
207 defer b.lock.Unlock()
209 close(b.detectCloser)
213 func (b *Buffer) didSay(re *regexp.Regexp) (bool, []byte) {
215 defer b.lock.Unlock()
217 unreadBytes := b.contents[b.readCursor:]
218 copyOfUnreadBytes := make([]byte, len(unreadBytes))
219 copy(copyOfUnreadBytes, unreadBytes)
221 loc := re.FindIndex(unreadBytes)
224 b.readCursor += uint64(loc[1])
225 return true, copyOfUnreadBytes
227 return false, copyOfUnreadBytes