105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
|
package gbytes
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"regexp"
|
||
|
|
||
|
"github.com/onsi/gomega/format"
|
||
|
)
|
||
|
|
||
|
//Objects satisfying the BufferProvider can be used with the Say matcher.
|
||
|
type BufferProvider interface {
|
||
|
Buffer() *Buffer
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Say is a Gomega matcher that operates on gbytes.Buffers:
|
||
|
|
||
|
Expect(buffer).Should(Say("something"))
|
||
|
|
||
|
will succeed if the unread portion of the buffer matches the regular expression "something".
|
||
|
|
||
|
When Say succeeds, it fast forwards the gbytes.Buffer's read cursor to just after the succesful match.
|
||
|
Thus, subsequent calls to Say will only match against the unread portion of the buffer
|
||
|
|
||
|
Say pairs very well with Eventually. To assert that a buffer eventually receives data matching "[123]-star" within 3 seconds you can:
|
||
|
|
||
|
Eventually(buffer, 3).Should(Say("[123]-star"))
|
||
|
|
||
|
Ditto with consistently. To assert that a buffer does not receive data matching "never-see-this" for 1 second you can:
|
||
|
|
||
|
Consistently(buffer, 1).ShouldNot(Say("never-see-this"))
|
||
|
|
||
|
In addition to bytes.Buffers, Say can operate on objects that implement the gbytes.BufferProvider interface.
|
||
|
In such cases, Say simply operates on the *gbytes.Buffer returned by Buffer()
|
||
|
|
||
|
If the buffer is closed, the Say matcher will tell Eventually to abort.
|
||
|
*/
|
||
|
func Say(expected string, args ...interface{}) *sayMatcher {
|
||
|
if len(args) > 0 {
|
||
|
expected = fmt.Sprintf(expected, args...)
|
||
|
}
|
||
|
return &sayMatcher{
|
||
|
re: regexp.MustCompile(expected),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type sayMatcher struct {
|
||
|
re *regexp.Regexp
|
||
|
receivedSayings []byte
|
||
|
}
|
||
|
|
||
|
func (m *sayMatcher) buffer(actual interface{}) (*Buffer, bool) {
|
||
|
var buffer *Buffer
|
||
|
|
||
|
switch x := actual.(type) {
|
||
|
case *Buffer:
|
||
|
buffer = x
|
||
|
case BufferProvider:
|
||
|
buffer = x.Buffer()
|
||
|
default:
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
return buffer, true
|
||
|
}
|
||
|
|
||
|
func (m *sayMatcher) Match(actual interface{}) (success bool, err error) {
|
||
|
buffer, ok := m.buffer(actual)
|
||
|
if !ok {
|
||
|
return false, fmt.Errorf("Say must be passed a *gbytes.Buffer or BufferProvider. Got:\n%s", format.Object(actual, 1))
|
||
|
}
|
||
|
|
||
|
didSay, sayings := buffer.didSay(m.re)
|
||
|
m.receivedSayings = sayings
|
||
|
|
||
|
return didSay, nil
|
||
|
}
|
||
|
|
||
|
func (m *sayMatcher) FailureMessage(actual interface{}) (message string) {
|
||
|
return fmt.Sprintf(
|
||
|
"Got stuck at:\n%s\nWaiting for:\n%s",
|
||
|
format.IndentString(string(m.receivedSayings), 1),
|
||
|
format.IndentString(m.re.String(), 1),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (m *sayMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||
|
return fmt.Sprintf(
|
||
|
"Saw:\n%s\nWhich matches the unexpected:\n%s",
|
||
|
format.IndentString(string(m.receivedSayings), 1),
|
||
|
format.IndentString(m.re.String(), 1),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (m *sayMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
|
||
|
switch x := actual.(type) {
|
||
|
case *Buffer:
|
||
|
return !x.Closed()
|
||
|
case BufferProvider:
|
||
|
return !x.Buffer().Closed()
|
||
|
default:
|
||
|
return true
|
||
|
}
|
||
|
}
|