diff options
Diffstat (limited to 'slog/record_test.go')
-rw-r--r-- | slog/record_test.go | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/slog/record_test.go b/slog/record_test.go new file mode 100644 index 0000000..f68af75 --- /dev/null +++ b/slog/record_test.go @@ -0,0 +1,154 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slog + +import ( + "strconv" + "strings" + "testing" + "time" + + "golang.org/x/exp/slices" +) + +func TestRecordAttrs(t *testing.T) { + as := []Attr{Int("k1", 1), String("k2", "foo"), Int("k3", 3), + Int64("k4", -1), Float64("f", 3.1), Uint64("u", 999)} + r := newRecordWithAttrs(as) + if g, w := r.NumAttrs(), len(as); g != w { + t.Errorf("NumAttrs: got %d, want %d", g, w) + } + if got := attrsSlice(r); !attrsEqual(got, as) { + t.Errorf("got %v, want %v", got, as) + } + + // Early return. + var got []Attr + r.Attrs(func(a Attr) bool { + got = append(got, a) + return len(got) < 2 + }) + want := as[:2] + if !attrsEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestRecordSource(t *testing.T) { + // Zero call depth => empty *Source. + for _, test := range []struct { + depth int + wantFunction string + wantFile string + wantLinePositive bool + }{ + {0, "", "", false}, + {-16, "", "", false}, + {1, "golang.org/x/exp/slog.TestRecordSource", "record_test.go", true}, // 1: caller of NewRecord + {2, "testing.tRunner", "testing.go", true}, + } { + var pc uintptr + if test.depth > 0 { + pc = callerPC(test.depth + 1) + } + r := NewRecord(time.Time{}, 0, "", pc) + got := r.source() + if i := strings.LastIndexByte(got.File, '/'); i >= 0 { + got.File = got.File[i+1:] + } + if got.Function != test.wantFunction || got.File != test.wantFile || (got.Line > 0) != test.wantLinePositive { + t.Errorf("depth %d: got (%q, %q, %d), want (%q, %q, %t)", + test.depth, + got.Function, got.File, got.Line, + test.wantFunction, test.wantFile, test.wantLinePositive) + } + } +} + +func TestAliasingAndClone(t *testing.T) { + intAttrs := func(from, to int) []Attr { + var as []Attr + for i := from; i < to; i++ { + as = append(as, Int("k", i)) + } + return as + } + + check := func(r Record, want []Attr) { + t.Helper() + got := attrsSlice(r) + if !attrsEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } + } + + // Create a record whose Attrs overflow the inline array, + // creating a slice in r.back. + r1 := NewRecord(time.Time{}, 0, "", 0) + r1.AddAttrs(intAttrs(0, nAttrsInline+1)...) + // Ensure that r1.back's capacity exceeds its length. + b := make([]Attr, len(r1.back), len(r1.back)+1) + copy(b, r1.back) + r1.back = b + // Make a copy that shares state. + r2 := r1 + // Adding to both should panic. + r1.AddAttrs(Int("p", 0)) + if !panics(func() { r2.AddAttrs(Int("p", 1)) }) { + t.Error("expected panic") + } + r1Attrs := attrsSlice(r1) + // Adding to a clone is fine. + r2 = r1.Clone() + check(r2, r1Attrs) + r2.AddAttrs(Int("p", 2)) + check(r1, r1Attrs) // r1 is unchanged + check(r2, append(slices.Clip(r1Attrs), Int("p", 2))) +} + +func newRecordWithAttrs(as []Attr) Record { + r := NewRecord(time.Now(), LevelInfo, "", 0) + r.AddAttrs(as...) + return r +} + +func attrsSlice(r Record) []Attr { + s := make([]Attr, 0, r.NumAttrs()) + r.Attrs(func(a Attr) bool { s = append(s, a); return true }) + return s +} + +func attrsEqual(as1, as2 []Attr) bool { + return slices.EqualFunc(as1, as2, Attr.Equal) +} + +// Currently, pc(2) takes over 400ns, which is too expensive +// to call it for every log message. +func BenchmarkPC(b *testing.B) { + for depth := 0; depth < 5; depth++ { + b.Run(strconv.Itoa(depth), func(b *testing.B) { + b.ReportAllocs() + var x uintptr + for i := 0; i < b.N; i++ { + x = callerPC(depth) + } + _ = x + }) + } +} + +func BenchmarkRecord(b *testing.B) { + const nAttrs = nAttrsInline * 10 + var a Attr + + for i := 0; i < b.N; i++ { + r := NewRecord(time.Time{}, LevelInfo, "", 0) + for j := 0; j < nAttrs; j++ { + r.AddAttrs(Int("k", j)) + } + r.Attrs(func(b Attr) bool { a = b; return true }) + } + _ = a +} |