Add dummy test

This commit is contained in:
Eric Meyer
2017-10-19 09:22:12 -05:00
parent c041b2efc4
commit d28c64c59b
1746 changed files with 786339 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
/*
Package gbytes provides a buffer that supports incrementally detecting input.
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.
Subsequent matches against the buffer will only operate against data that appears *after* the read cursor.
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
access the entire buffer's contents with Contents().
*/
package gbytes
import (
"errors"
"fmt"
"io"
"regexp"
"sync"
"time"
)
/*
gbytes.Buffer implements an io.Writer and can be used with the gbytes.Say matcher.
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!
*/
type Buffer struct {
contents []byte
readCursor uint64
lock *sync.Mutex
detectCloser chan interface{}
closed bool
}
/*
NewBuffer returns a new gbytes.Buffer
*/
func NewBuffer() *Buffer {
return &Buffer{
lock: &sync.Mutex{},
}
}
/*
BufferWithBytes returns a new gbytes.Buffer seeded with the passed in bytes
*/
func BufferWithBytes(bytes []byte) *Buffer {
return &Buffer{
lock: &sync.Mutex{},
contents: bytes,
}
}
/*
BufferReader returns a new gbytes.Buffer that wraps a reader. The reader's contents are read into
the Buffer via io.Copy
*/
func BufferReader(reader io.Reader) *Buffer {
b := &Buffer{
lock: &sync.Mutex{},
}
go func() {
io.Copy(b, reader)
b.Close()
}()
return b
}
/*
Write implements the io.Writer interface
*/
func (b *Buffer) Write(p []byte) (n int, err error) {
b.lock.Lock()
defer b.lock.Unlock()
if b.closed {
return 0, errors.New("attempt to write to closed buffer")
}
b.contents = append(b.contents, p...)
return len(p), nil
}
/*
Read implements the io.Reader interface. It advances the
cursor as it reads.
Returns an error if called after Close.
*/
func (b *Buffer) Read(d []byte) (int, error) {
b.lock.Lock()
defer b.lock.Unlock()
if b.closed {
return 0, errors.New("attempt to read from closed buffer")
}
if uint64(len(b.contents)) <= b.readCursor {
return 0, io.EOF
}
n := copy(d, b.contents[b.readCursor:])
b.readCursor += uint64(n)
return n, nil
}
/*
Close signifies that the buffer will no longer be written to
*/
func (b *Buffer) Close() error {
b.lock.Lock()
defer b.lock.Unlock()
b.closed = true
return nil
}
/*
Closed returns true if the buffer has been closed
*/
func (b *Buffer) Closed() bool {
b.lock.Lock()
defer b.lock.Unlock()
return b.closed
}
/*
Contents returns all data ever written to the buffer.
*/
func (b *Buffer) Contents() []byte {
b.lock.Lock()
defer b.lock.Unlock()
contents := make([]byte, len(b.contents))
copy(contents, b.contents)
return contents
}
/*
Detect takes a regular expression and returns a channel.
The channel will receive true the first time data matching the regular expression is written to the buffer.
The channel is subsequently closed and the buffer's read-cursor is fast-forwarded to just after the matching region.
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
be branch and handle different outputs written to the buffer.
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.
You could do something like:
select {
case <-buffer.Detect("You are not logged in"):
//log in
case <-buffer.Detect("Success"):
//carry on
case <-time.After(time.Second):
//welp
}
buffer.CancelDetects()
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.
Finally, you can pass detect a format string followed by variadic arguments. This will construct the regexp using fmt.Sprintf.
*/
func (b *Buffer) Detect(desired string, args ...interface{}) chan bool {
formattedRegexp := desired
if len(args) > 0 {
formattedRegexp = fmt.Sprintf(desired, args...)
}
re := regexp.MustCompile(formattedRegexp)
b.lock.Lock()
defer b.lock.Unlock()
if b.detectCloser == nil {
b.detectCloser = make(chan interface{})
}
closer := b.detectCloser
response := make(chan bool)
go func() {
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
defer close(response)
for {
select {
case <-ticker.C:
b.lock.Lock()
data, cursor := b.contents[b.readCursor:], b.readCursor
loc := re.FindIndex(data)
b.lock.Unlock()
if loc != nil {
response <- true
b.lock.Lock()
newCursorPosition := cursor + uint64(loc[1])
if newCursorPosition >= b.readCursor {
b.readCursor = newCursorPosition
}
b.lock.Unlock()
return
}
case <-closer:
return
}
}
}()
return response
}
/*
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.
*/
func (b *Buffer) CancelDetects() {
b.lock.Lock()
defer b.lock.Unlock()
close(b.detectCloser)
b.detectCloser = nil
}
func (b *Buffer) didSay(re *regexp.Regexp) (bool, []byte) {
b.lock.Lock()
defer b.lock.Unlock()
unreadBytes := b.contents[b.readCursor:]
copyOfUnreadBytes := make([]byte, len(unreadBytes))
copy(copyOfUnreadBytes, unreadBytes)
loc := re.FindIndex(unreadBytes)
if loc != nil {
b.readCursor += uint64(loc[1])
return true, copyOfUnreadBytes
}
return false, copyOfUnreadBytes
}
+205
View File
@@ -0,0 +1,205 @@
package gbytes_test
import (
"io"
"time"
. "github.com/onsi/gomega/gbytes"
"bytes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type SlowReader struct {
R io.Reader
D time.Duration
}
func (s SlowReader) Read(p []byte) (int, error) {
time.Sleep(s.D)
return s.R.Read(p)
}
var _ = Describe("Buffer", func() {
var buffer *Buffer
BeforeEach(func() {
buffer = NewBuffer()
})
Describe("dumping the entire contents of the buffer", func() {
It("should return everything that's been written", func() {
buffer.Write([]byte("abc"))
buffer.Write([]byte("def"))
Ω(buffer.Contents()).Should(Equal([]byte("abcdef")))
Ω(buffer).Should(Say("bcd"))
Ω(buffer.Contents()).Should(Equal([]byte("abcdef")))
})
})
Describe("creating a buffer with bytes", func() {
It("should create the buffer with the cursor set to the beginning", func() {
buffer := BufferWithBytes([]byte("abcdef"))
Ω(buffer.Contents()).Should(Equal([]byte("abcdef")))
Ω(buffer).Should(Say("abc"))
Ω(buffer).ShouldNot(Say("abc"))
Ω(buffer).Should(Say("def"))
})
})
Describe("creating a buffer that wraps a reader", func() {
Context("for a well-behaved reader", func() {
It("should buffer the contents of the reader", func() {
reader := bytes.NewBuffer([]byte("abcdef"))
buffer := BufferReader(reader)
Eventually(buffer).Should(Say("abc"))
Ω(buffer).ShouldNot(Say("abc"))
Eventually(buffer).Should(Say("def"))
Eventually(buffer.Closed).Should(BeTrue())
})
})
Context("for a slow reader", func() {
It("should allow Eventually to time out", func() {
slowReader := SlowReader{
R: bytes.NewBuffer([]byte("abcdef")),
D: time.Second,
}
buffer := BufferReader(slowReader)
failures := InterceptGomegaFailures(func() {
Eventually(buffer, 100*time.Millisecond).Should(Say("abc"))
})
Ω(failures).ShouldNot(BeEmpty())
fastReader := SlowReader{
R: bytes.NewBuffer([]byte("abcdef")),
D: time.Millisecond,
}
buffer = BufferReader(fastReader)
Eventually(buffer, 100*time.Millisecond).Should(Say("abc"))
Eventually(buffer.Closed).Should(BeTrue())
})
})
})
Describe("reading from a buffer", func() {
It("should read the current contents of the buffer", func() {
buffer := BufferWithBytes([]byte("abcde"))
dest := make([]byte, 3)
n, err := buffer.Read(dest)
Ω(err).ShouldNot(HaveOccurred())
Ω(n).Should(Equal(3))
Ω(string(dest)).Should(Equal("abc"))
dest = make([]byte, 3)
n, err = buffer.Read(dest)
Ω(err).ShouldNot(HaveOccurred())
Ω(n).Should(Equal(2))
Ω(string(dest[:n])).Should(Equal("de"))
n, err = buffer.Read(dest)
Ω(err).Should(Equal(io.EOF))
Ω(n).Should(Equal(0))
})
Context("after the buffer has been closed", func() {
It("returns an error", func() {
buffer := BufferWithBytes([]byte("abcde"))
buffer.Close()
dest := make([]byte, 3)
n, err := buffer.Read(dest)
Ω(err).Should(HaveOccurred())
Ω(n).Should(Equal(0))
})
})
})
Describe("detecting regular expressions", func() {
It("should fire the appropriate channel when the passed in pattern matches, then close it", func(done Done) {
go func() {
time.Sleep(10 * time.Millisecond)
buffer.Write([]byte("abcde"))
}()
A := buffer.Detect("%s", "a.c")
B := buffer.Detect("def")
var gotIt bool
select {
case gotIt = <-A:
case <-B:
Fail("should not have gotten here")
}
Ω(gotIt).Should(BeTrue())
Eventually(A).Should(BeClosed())
buffer.Write([]byte("f"))
Eventually(B).Should(Receive())
Eventually(B).Should(BeClosed())
close(done)
})
It("should fast-forward the buffer upon detection", func(done Done) {
buffer.Write([]byte("abcde"))
<-buffer.Detect("abc")
Ω(buffer).ShouldNot(Say("abc"))
Ω(buffer).Should(Say("de"))
close(done)
})
It("should only fast-forward the buffer when the channel is read, and only if doing so would not rewind it", func(done Done) {
buffer.Write([]byte("abcde"))
A := buffer.Detect("abc")
time.Sleep(20 * time.Millisecond) //give the goroutine a chance to detect and write to the channel
Ω(buffer).Should(Say("abcd"))
<-A
Ω(buffer).ShouldNot(Say("d"))
Ω(buffer).Should(Say("e"))
Eventually(A).Should(BeClosed())
close(done)
})
It("should be possible to cancel a detection", func(done Done) {
A := buffer.Detect("abc")
B := buffer.Detect("def")
buffer.CancelDetects()
buffer.Write([]byte("abcdef"))
Eventually(A).Should(BeClosed())
Eventually(B).Should(BeClosed())
Ω(buffer).Should(Say("bcde"))
<-buffer.Detect("f")
close(done)
})
})
Describe("closing the buffer", func() {
It("should error when further write attempts are made", func() {
_, err := buffer.Write([]byte("abc"))
Ω(err).ShouldNot(HaveOccurred())
buffer.Close()
_, err = buffer.Write([]byte("def"))
Ω(err).Should(HaveOccurred())
Ω(buffer.Contents()).Should(Equal([]byte("abc")))
})
It("should be closed", func() {
Ω(buffer.Closed()).Should(BeFalse())
buffer.Close()
Ω(buffer.Closed()).Should(BeTrue())
})
})
})
+13
View File
@@ -0,0 +1,13 @@
package gbytes_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestGbytes(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Gbytes Suite")
}
+85
View File
@@ -0,0 +1,85 @@
package gbytes
import (
"errors"
"io"
"time"
)
// ErrTimeout is returned by TimeoutCloser, TimeoutReader, and TimeoutWriter when the underlying Closer/Reader/Writer does not return within the specified timeout
var ErrTimeout = errors.New("timeout occurred")
// TimeoutCloser returns an io.Closer that wraps the passed-in io.Closer. If the underlying Closer fails to close within the alloted timeout ErrTimeout is returned.
func TimeoutCloser(c io.Closer, timeout time.Duration) io.Closer {
return timeoutReaderWriterCloser{c: c, d: timeout}
}
// TimeoutReader returns an io.Reader that wraps the passed-in io.Reader. If the underlying Reader fails to read within the alloted timeout ErrTimeout is returned.
func TimeoutReader(r io.Reader, timeout time.Duration) io.Reader {
return timeoutReaderWriterCloser{r: r, d: timeout}
}
// TimeoutWriter returns an io.Writer that wraps the passed-in io.Writer. If the underlying Writer fails to write within the alloted timeout ErrTimeout is returned.
func TimeoutWriter(w io.Writer, timeout time.Duration) io.Writer {
return timeoutReaderWriterCloser{w: w, d: timeout}
}
type timeoutReaderWriterCloser struct {
c io.Closer
w io.Writer
r io.Reader
d time.Duration
}
func (t timeoutReaderWriterCloser) Close() error {
done := make(chan struct{})
var err error
go func() {
err = t.c.Close()
close(done)
}()
select {
case <-done:
return err
case <-time.After(t.d):
return ErrTimeout
}
}
func (t timeoutReaderWriterCloser) Read(p []byte) (int, error) {
done := make(chan struct{})
var n int
var err error
go func() {
n, err = t.r.Read(p)
close(done)
}()
select {
case <-done:
return n, err
case <-time.After(t.d):
return 0, ErrTimeout
}
}
func (t timeoutReaderWriterCloser) Write(p []byte) (int, error) {
done := make(chan struct{})
var n int
var err error
go func() {
n, err = t.w.Write(p)
close(done)
}()
select {
case <-done:
return n, err
case <-time.After(t.d):
return 0, ErrTimeout
}
}
+188
View File
@@ -0,0 +1,188 @@
package gbytes_test
import (
"fmt"
"io"
"time"
. "github.com/onsi/gomega/gbytes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type FakeCloser struct {
err error
duration time.Duration
}
func (f FakeCloser) Close() error {
time.Sleep(f.duration)
return f.err
}
type FakeReader struct {
err error
duration time.Duration
}
func (f FakeReader) Read(p []byte) (int, error) {
time.Sleep(f.duration)
if f.err != nil {
return 0, f.err
}
for i := 0; i < len(p); i++ {
p[i] = 'a'
}
return len(p), nil
}
type FakeWriter struct {
err error
duration time.Duration
}
func (f FakeWriter) Write(p []byte) (int, error) {
time.Sleep(f.duration)
if f.err != nil {
return 0, f.err
}
return len(p), nil
}
var _ = Describe("Io Wrappers", func() {
Describe("TimeoutCloser", func() {
var innerCloser io.Closer
var timeoutCloser io.Closer
JustBeforeEach(func() {
timeoutCloser = TimeoutCloser(innerCloser, 20*time.Millisecond)
})
Context("when the underlying Closer closes with no error", func() {
BeforeEach(func() {
innerCloser = FakeCloser{}
})
It("returns with no error", func() {
Ω(timeoutCloser.Close()).Should(Succeed())
})
})
Context("when the underlying Closer closes with an error", func() {
BeforeEach(func() {
innerCloser = FakeCloser{err: fmt.Errorf("boom")}
})
It("returns the error", func() {
Ω(timeoutCloser.Close()).Should(MatchError("boom"))
})
})
Context("when the underlying Closer hangs", func() {
BeforeEach(func() {
innerCloser = FakeCloser{
err: fmt.Errorf("boom"),
duration: time.Hour,
}
})
It("returns ErrTimeout", func() {
Ω(timeoutCloser.Close()).Should(MatchError(ErrTimeout))
})
})
})
Describe("TimeoutReader", func() {
var innerReader io.Reader
var timeoutReader io.Reader
JustBeforeEach(func() {
timeoutReader = TimeoutReader(innerReader, 20*time.Millisecond)
})
Context("when the underlying Reader returns no error", func() {
BeforeEach(func() {
innerReader = FakeReader{}
})
It("returns with no error", func() {
p := make([]byte, 5)
n, err := timeoutReader.Read(p)
Ω(n).Should(Equal(5))
Ω(err).ShouldNot(HaveOccurred())
Ω(p).Should(Equal([]byte("aaaaa")))
})
})
Context("when the underlying Reader returns an error", func() {
BeforeEach(func() {
innerReader = FakeReader{err: fmt.Errorf("boom")}
})
It("returns the error", func() {
p := make([]byte, 5)
_, err := timeoutReader.Read(p)
Ω(err).Should(MatchError("boom"))
})
})
Context("when the underlying Reader hangs", func() {
BeforeEach(func() {
innerReader = FakeReader{err: fmt.Errorf("boom"), duration: time.Hour}
})
It("returns ErrTimeout", func() {
p := make([]byte, 5)
_, err := timeoutReader.Read(p)
Ω(err).Should(MatchError(ErrTimeout))
})
})
})
Describe("TimeoutWriter", func() {
var innerWriter io.Writer
var timeoutWriter io.Writer
JustBeforeEach(func() {
timeoutWriter = TimeoutWriter(innerWriter, 20*time.Millisecond)
})
Context("when the underlying Writer returns no error", func() {
BeforeEach(func() {
innerWriter = FakeWriter{}
})
It("returns with no error", func() {
n, err := timeoutWriter.Write([]byte("aaaaa"))
Ω(n).Should(Equal(5))
Ω(err).ShouldNot(HaveOccurred())
})
})
Context("when the underlying Writer returns an error", func() {
BeforeEach(func() {
innerWriter = FakeWriter{err: fmt.Errorf("boom")}
})
It("returns the error", func() {
_, err := timeoutWriter.Write([]byte("aaaaa"))
Ω(err).Should(MatchError("boom"))
})
})
Context("when the underlying Writer hangs", func() {
BeforeEach(func() {
innerWriter = FakeWriter{err: fmt.Errorf("boom"), duration: time.Hour}
})
It("returns ErrTimeout", func() {
_, err := timeoutWriter.Write([]byte("aaaaa"))
Ω(err).Should(MatchError(ErrTimeout))
})
})
})
})
+105
View File
@@ -0,0 +1,105 @@
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:
Ω(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 {
formattedRegexp := expected
if len(args) > 0 {
formattedRegexp = fmt.Sprintf(expected, args...)
}
return &sayMatcher{
re: regexp.MustCompile(formattedRegexp),
}
}
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
}
}
+163
View File
@@ -0,0 +1,163 @@
package gbytes_test
import (
. "github.com/onsi/gomega/gbytes"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type speaker struct {
buffer *Buffer
}
func (s *speaker) Buffer() *Buffer {
return s.buffer
}
var _ = Describe("SayMatcher", func() {
var buffer *Buffer
BeforeEach(func() {
buffer = NewBuffer()
buffer.Write([]byte("abc"))
})
Context("when actual is not a gexec Buffer, or a BufferProvider", func() {
It("should error", func() {
failures := InterceptGomegaFailures(func() {
Ω("foo").Should(Say("foo"))
})
Ω(failures[0]).Should(ContainSubstring("*gbytes.Buffer"))
})
})
Context("when a match is found", func() {
It("should succeed", func() {
Ω(buffer).Should(Say("abc"))
})
It("should support printf-like formatting", func() {
Ω(buffer).Should(Say("a%sc", "b"))
})
It("should use a regular expression", func() {
Ω(buffer).Should(Say("a.c"))
})
It("should fastforward the buffer", func() {
buffer.Write([]byte("def"))
Ω(buffer).Should(Say("abcd"))
Ω(buffer).Should(Say("ef"))
Ω(buffer).ShouldNot(Say("[a-z]"))
})
})
Context("when no match is found", func() {
It("should not error", func() {
Ω(buffer).ShouldNot(Say("def"))
})
Context("when the buffer is closed", func() {
BeforeEach(func() {
buffer.Close()
})
It("should abort an eventually", func() {
t := time.Now()
failures := InterceptGomegaFailures(func() {
Eventually(buffer).Should(Say("def"))
})
Eventually(buffer).ShouldNot(Say("def"))
Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond))
Ω(failures).Should(HaveLen(1))
t = time.Now()
Eventually(buffer).Should(Say("abc"))
Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond))
})
It("should abort a consistently", func() {
t := time.Now()
Consistently(buffer, 2.0).ShouldNot(Say("def"))
Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond))
})
It("should not error with a synchronous matcher", func() {
Ω(buffer).ShouldNot(Say("def"))
Ω(buffer).Should(Say("abc"))
})
})
})
Context("when a positive match fails", func() {
It("should report where it got stuck", func() {
Ω(buffer).Should(Say("abc"))
buffer.Write([]byte("def"))
failures := InterceptGomegaFailures(func() {
Ω(buffer).Should(Say("abc"))
})
Ω(failures[0]).Should(ContainSubstring("Got stuck at:"))
Ω(failures[0]).Should(ContainSubstring("def"))
})
})
Context("when a negative match fails", func() {
It("should report where it got stuck", func() {
failures := InterceptGomegaFailures(func() {
Ω(buffer).ShouldNot(Say("abc"))
})
Ω(failures[0]).Should(ContainSubstring("Saw:"))
Ω(failures[0]).Should(ContainSubstring("Which matches the unexpected:"))
Ω(failures[0]).Should(ContainSubstring("abc"))
})
})
Context("when a match is not found", func() {
It("should not fastforward the buffer", func() {
Ω(buffer).ShouldNot(Say("def"))
Ω(buffer).Should(Say("abc"))
})
})
Context("a nice real-life example", func() {
It("should behave well", func() {
Ω(buffer).Should(Say("abc"))
go func() {
time.Sleep(10 * time.Millisecond)
buffer.Write([]byte("def"))
}()
Ω(buffer).ShouldNot(Say("def"))
Eventually(buffer).Should(Say("def"))
})
})
Context("when actual is a BufferProvider", func() {
It("should use actual's buffer", func() {
s := &speaker{
buffer: NewBuffer(),
}
Ω(s).ShouldNot(Say("abc"))
s.Buffer().Write([]byte("abc"))
Ω(s).Should(Say("abc"))
})
It("should abort an eventually", func() {
s := &speaker{
buffer: NewBuffer(),
}
s.buffer.Close()
t := time.Now()
failures := InterceptGomegaFailures(func() {
Eventually(s).Should(Say("def"))
})
Ω(failures).Should(HaveLen(1))
Ω(time.Since(t)).Should(BeNumerically("<", 500*time.Millisecond))
})
})
})