mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +00:00
internal/testlog: refactor test-case to be table-driven. adapt the test-logger to take T as an interface to enable log output capture in the unit test.
This commit is contained in:
parent
8f1a56bb4e
commit
8c2236e586
3 changed files with 73 additions and 13 deletions
|
|
@ -21,23 +21,30 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
termTimeFormat = "01-02|15:04:05.000"
|
termTimeFormat = "01-02|15:04:05.000"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// T wraps methods from testing.T used by the test logger into an interface.
|
||||||
|
// It is specified so that unit tests can instantiate the logger with an
|
||||||
|
// implementation of T which can capture the output of logging statements
|
||||||
|
// from T.Logf, as this cannot be using testing.T.
|
||||||
|
type T interface {
|
||||||
|
Logf(format string, args ...any)
|
||||||
|
Helper()
|
||||||
|
}
|
||||||
|
|
||||||
// logger implements log.Logger such that all output goes to the unit test log via
|
// logger implements log.Logger such that all output goes to the unit test log via
|
||||||
// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test
|
// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test
|
||||||
// helpers, so the file and line number in unit test output correspond to the call site
|
// helpers, so the file and line number in unit test output correspond to the call site
|
||||||
// which emitted the log message.
|
// which emitted the log message.
|
||||||
type logger struct {
|
type logger struct {
|
||||||
t *testing.T
|
t T
|
||||||
l log.Logger
|
l log.Logger
|
||||||
mu *sync.Mutex
|
mu *sync.Mutex
|
||||||
h *bufHandler
|
h *bufHandler
|
||||||
|
|
@ -78,7 +85,7 @@ func (h *bufHandler) WithGroup(_ string) slog.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logger returns a logger which logs to the unit test log of t.
|
// Logger returns a logger which logs to the unit test log of t.
|
||||||
func Logger(t *testing.T, level slog.Level) log.Logger {
|
func Logger(t T, level slog.Level) log.Logger {
|
||||||
handler := bufHandler{
|
handler := bufHandler{
|
||||||
buf: []slog.Record{},
|
buf: []slog.Record{},
|
||||||
attrs: []slog.Attr{},
|
attrs: []slog.Attr{},
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,69 @@
|
||||||
package testlog
|
package testlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mockT struct {
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mockT) Helper() {
|
||||||
|
// noop for the purposes of unit tests
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mockT) Logf(format string, args ...any) {
|
||||||
|
// we could gate this operation in a mutex, but because testlogger
|
||||||
|
// only calls Logf with its internal mutex held, we just write output here
|
||||||
|
var lineBuf bytes.Buffer
|
||||||
|
if _, err := fmt.Fprintf(&lineBuf, fmt.Sprintf(format, args...)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sanitized := strings.Split(lineBuf.String(), "]")[1]
|
||||||
|
if _, err := t.out.Write([]byte(sanitized)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLogging(t *testing.T) {
|
func TestLogging(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expected string
|
||||||
|
run func(t *mockT)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"SubLogger",
|
||||||
|
` Visible
|
||||||
|
Hide and seek foobar=123
|
||||||
|
Also visible
|
||||||
|
`,
|
||||||
|
func(t *mockT) {
|
||||||
l := Logger(t, log.LevelInfo)
|
l := Logger(t, log.LevelInfo)
|
||||||
subLogger := l.New("foobar", 123)
|
subLogger := l.New("foobar", 123)
|
||||||
|
|
||||||
l.Info("Visible")
|
l.Info("Visible")
|
||||||
subLogger.Info("Hide and seek") // this log is erroneously hidden in master, but fixed with this PR
|
subLogger.Info("Hide and seek")
|
||||||
l.Info("Also visible")
|
l.Info("Also visible")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
outp := bytes.Buffer{}
|
||||||
|
mock := mockT{&outp}
|
||||||
|
tc.run(&mock)
|
||||||
|
if outp.String() != tc.expected {
|
||||||
|
fmt.Println("mismatch")
|
||||||
|
fmt.Printf("'%s'\n", outp.String())
|
||||||
|
fmt.Println("----")
|
||||||
|
fmt.Printf("'%s'\n", tc.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 81862e4848585a438d64f911a19b3825f0f4cd95
|
Subproject commit faf33b471465d3c6cdc3d04fbd690895f78d33f2
|
||||||
Loading…
Reference in a new issue