121 lines
3.0 KiB
Go
121 lines
3.0 KiB
Go
|
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package plog
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"io"
|
||
|
"math"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/go-logr/logr"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
"go.uber.org/zap"
|
||
|
"go.uber.org/zap/zapcore"
|
||
|
"k8s.io/utils/clock"
|
||
|
clocktesting "k8s.io/utils/clock/testing"
|
||
|
)
|
||
|
|
||
|
// contextKey type is unexported to prevent collisions.
|
||
|
type contextKey int
|
||
|
|
||
|
const zapOverridesKey contextKey = iota
|
||
|
|
||
|
func TestZapOverrides(ctx context.Context, t *testing.T, w io.Writer, f func(*zap.Config), opts ...zap.Option) context.Context {
|
||
|
t.Helper() // discourage use outside of tests
|
||
|
|
||
|
overrides := &testOverrides{
|
||
|
t: t,
|
||
|
w: w,
|
||
|
f: f,
|
||
|
opts: opts,
|
||
|
}
|
||
|
return context.WithValue(ctx, zapOverridesKey, overrides)
|
||
|
}
|
||
|
|
||
|
func TestLogger(t *testing.T, w io.Writer) Logger {
|
||
|
t.Helper()
|
||
|
|
||
|
return New().withLogrMod(func(l logr.Logger) logr.Logger {
|
||
|
return l.WithSink(TestZapr(t, w).GetSink())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestZapr(t *testing.T, w io.Writer) logr.Logger {
|
||
|
t.Helper()
|
||
|
|
||
|
now, err := time.Parse(time.RFC3339Nano, "2099-08-08T13:57:36.123456789Z")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
t.Cleanup(cancel)
|
||
|
|
||
|
ctx = TestZapOverrides(ctx, t, w,
|
||
|
func(config *zap.Config) {
|
||
|
config.Level = zap.NewAtomicLevelAt(math.MinInt8) // log everything during tests
|
||
|
|
||
|
// make test assertions less painful to write while keeping them as close to the real thing as possible
|
||
|
config.EncoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
|
||
|
trimmed := caller.TrimmedPath()
|
||
|
if idx := strings.LastIndexByte(trimmed, ':'); idx != -1 {
|
||
|
trimmed = trimmed[:idx+1] + "<line>"
|
||
|
}
|
||
|
enc.AppendString(trimmed + funcEncoder(caller))
|
||
|
}
|
||
|
},
|
||
|
zap.WithClock(ZapClock(clocktesting.NewFakeClock(now))), // have the clock be static during tests
|
||
|
zap.AddStacktrace(nopLevelEnabler{}), // do not log stacktraces
|
||
|
)
|
||
|
|
||
|
// there is no buffering so we can ignore flush
|
||
|
zl, _, err := newLogr(ctx, "json", 0)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
return zl
|
||
|
}
|
||
|
|
||
|
var _ zapcore.Clock = &clockAdapter{}
|
||
|
|
||
|
type clockAdapter struct {
|
||
|
clock clock.Clock
|
||
|
}
|
||
|
|
||
|
func (c *clockAdapter) Now() time.Time {
|
||
|
return c.clock.Now()
|
||
|
}
|
||
|
|
||
|
func (c *clockAdapter) NewTicker(duration time.Duration) *time.Ticker {
|
||
|
return &time.Ticker{C: c.clock.Tick(duration)}
|
||
|
}
|
||
|
|
||
|
func ZapClock(c clock.Clock) zapcore.Clock {
|
||
|
return &clockAdapter{clock: c}
|
||
|
}
|
||
|
|
||
|
var _ zap.Sink = nopCloserSink{}
|
||
|
|
||
|
type nopCloserSink struct{ zapcore.WriteSyncer }
|
||
|
|
||
|
func (nopCloserSink) Close() error { return nil }
|
||
|
|
||
|
// newSink returns a wrapper around the input writer that is safe for concurrent use.
|
||
|
func newSink(w io.Writer) zap.Sink {
|
||
|
return nopCloserSink{WriteSyncer: zapcore.Lock(zapcore.AddSync(w))}
|
||
|
}
|
||
|
|
||
|
var _ zapcore.LevelEnabler = nopLevelEnabler{}
|
||
|
|
||
|
type nopLevelEnabler struct{}
|
||
|
|
||
|
func (nopLevelEnabler) Enabled(_ zapcore.Level) bool { return false }
|
||
|
|
||
|
type testOverrides struct {
|
||
|
t *testing.T
|
||
|
w io.Writer
|
||
|
f func(*zap.Config)
|
||
|
opts []zap.Option
|
||
|
}
|