ceremonyclient/go-libp2p/examples/testutils/logharness.go
2023-08-20 23:07:43 -05:00

121 lines
2.9 KiB
Go

package testutils
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"strings"
"testing"
)
// A LogHarness runs sets of assertions against the log output of a function. Assertions are grouped
// into sequences of messages that are expected to be found in the log output. Calling one of the Expect
// methods on the harness adds an expectation to the default sequence of messages. Additional sequences
// can be created by calling NewSequence.
type LogHarness struct {
buf bytes.Buffer
sequences []*Sequence
}
type Expectation interface {
IsMatch(line string) bool
String() string
}
// Run executes the function f and captures any output written using Go's standard log. Each sequence
// of expected messages is then
func (h *LogHarness) Run(t *testing.T, f func()) {
// Capture raw log output
fl := log.Flags()
log.SetFlags(0)
log.SetOutput(&h.buf)
f()
log.SetFlags(fl)
log.SetOutput(os.Stderr)
for _, seq := range h.sequences {
seq.Assert(t, bufio.NewScanner(bytes.NewReader(h.buf.Bytes())))
}
}
// Expect adds an expectation to the default sequence that the log contains a line equal to s
func (h *LogHarness) Expect(s string) {
if len(h.sequences) == 0 {
h.sequences = append(h.sequences, &Sequence{name: ""})
}
h.sequences[0].Expect(s)
}
// ExpectPrefix adds an to the default sequence expectation that the log contains a line starting with s
func (h *LogHarness) ExpectPrefix(s string) {
if len(h.sequences) == 0 {
h.sequences = append(h.sequences, &Sequence{name: ""})
}
h.sequences[0].ExpectPrefix(s)
}
// NewSequence creates a new sequence of expected log messages
func (h *LogHarness) NewSequence(name string) *Sequence {
seq := &Sequence{name: name}
h.sequences = append(h.sequences, seq)
return seq
}
type prefix string
func (p prefix) IsMatch(line string) bool {
return strings.HasPrefix(line, string(p))
}
func (p prefix) String() string {
return fmt.Sprintf("prefix %q", string(p))
}
type text string
func (t text) IsMatch(line string) bool {
return line == string(t)
}
func (t text) String() string {
return fmt.Sprintf("text %q", string(t))
}
type Sequence struct {
name string
exp []Expectation
}
func (seq *Sequence) Assert(t *testing.T, s *bufio.Scanner) {
var tag string
if seq.name != "" {
tag = fmt.Sprintf("[%s] ", seq.name)
}
// Match raw log lines against expectations
exploop:
for _, e := range seq.exp {
for s.Scan() {
if e.IsMatch(s.Text()) {
t.Logf("%ssaw: %s", tag, s.Text())
continue exploop
}
}
if s.Err() == nil {
t.Errorf("%sdid not see expected %s", tag, e.String())
return
}
}
}
// Expect adds an expectation that the log contains a line equal to s
func (seq *Sequence) Expect(s string) {
seq.exp = append(seq.exp, text(s))
}
// ExpectPrefix adds an expectation that the log contains a line starting with s
func (seq *Sequence) ExpectPrefix(s string) {
seq.exp = append(seq.exp, prefix(s))
}