| 23 | ) |
| 24 | |
| 25 | func New(t *testing.T, r io.Reader, name string) *Expecter { |
| 26 | // Use pipe for logging. |
| 27 | logDone := make(chan struct{}) |
| 28 | logr, logw := io.Pipe() |
| 29 | |
| 30 | // Write to log and output buffer. |
| 31 | copyDone := make(chan struct{}) |
| 32 | out := newStdbuf() |
| 33 | w := io.MultiWriter(logw, out) |
| 34 | |
| 35 | ex := &Expecter{ |
| 36 | t: t, |
| 37 | out: out, |
| 38 | name: atomic.NewString(name), |
| 39 | |
| 40 | runeReader: bufio.NewReaderSize(out, utf8.UTFMax), |
| 41 | logDone: logDone, |
| 42 | copyDone: copyDone, |
| 43 | logr: logr, |
| 44 | logw: logw, |
| 45 | } |
| 46 | |
| 47 | go func() { |
| 48 | defer close(copyDone) |
| 49 | _, err := io.Copy(w, r) |
| 50 | ex.Logf("copy done: %v", err) |
| 51 | ex.Logf("closing out") |
| 52 | err = out.closeErr(err) |
| 53 | ex.Logf("closed out: %v", err) |
| 54 | }() |
| 55 | |
| 56 | // Log all output as part of test for easier debugging on errors. |
| 57 | go func() { |
| 58 | defer close(logDone) |
| 59 | s := bufio.NewScanner(logr) |
| 60 | for s.Scan() { |
| 61 | ex.Logf("%q", stripansi.Strip(s.Text())) |
| 62 | } |
| 63 | // Surface non-EOF scanner errors; otherwise they're invisible. |
| 64 | if err := s.Err(); err != nil { |
| 65 | ex.Logf("log scanner stopped: %v", err) |
| 66 | } |
| 67 | }() |
| 68 | |
| 69 | return ex |
| 70 | } |
| 71 | |
| 72 | func NewAttachedToInvocation(t *testing.T, invocation *serpent.Invocation) *Expecter { |
| 73 | r, w := io.Pipe() |