blob: 72baf4e0dd9a5faf5d72aa431b5c807e67715168 [file] [log] [blame]
// 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 (
"strings"
"testing"
"time"
"golang.org/x/exp/slices"
"golang.org/x/exp/slog/internal/buffer"
)
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 := r.Attrs(); !attrsEqual(got, as) {
t.Errorf("got %v, want %v", got, as)
}
}
func TestRecordSourceLine(t *testing.T) {
// Zero call depth => empty file/line
for _, test := range []struct {
depth int
wantFile string
wantLinePositive bool
}{
{0, "", false},
{-16, "", false},
{1, "record.go", true},
} {
r := MakeRecord(time.Time{}, 0, "", test.depth)
gotFile, gotLine := r.SourceLine()
if i := strings.LastIndexByte(gotFile, '/'); i >= 0 {
gotFile = gotFile[i+1:]
}
if gotFile != test.wantFile || (gotLine > 0) != test.wantLinePositive {
t.Errorf("depth %d: got (%q, %d), want (%q, %t)",
test.depth, gotFile, gotLine, test.wantFile, test.wantLinePositive)
}
}
}
func TestAliasing(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 := r.Attrs()
if !attrsEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
}
r1 := MakeRecord(time.Time{}, 0, "", 0)
for i := 0; i < nAttrsInline+3; i++ {
r1.AddAttr(Int("k", i))
}
check(&r1, intAttrs(0, nAttrsInline+3))
r2 := r1
check(&r2, intAttrs(0, nAttrsInline+3))
// if cap(r1.attrs2) <= len(r1.attrs2) {
// t.Fatal("cap not greater than len")
// }
r1.AddAttr(Int("k", nAttrsInline+3))
r2.AddAttr(Int("k", -1))
check(&r1, intAttrs(0, nAttrsInline+4))
check(&r2, append(intAttrs(0, nAttrsInline+3), Int("k", -1)))
}
func newRecordWithAttrs(as []Attr) Record {
r := MakeRecord(time.Now(), InfoLevel, "", 0)
for _, a := range as {
r.AddAttr(a)
}
return r
}
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) {
b.ReportAllocs()
var x uintptr
for i := 0; i < b.N; i++ {
x = pc(3)
}
_ = x
}
func BenchmarkSourceLine(b *testing.B) {
r := MakeRecord(time.Now(), InfoLevel, "", 5)
b.Run("alone", func(b *testing.B) {
for i := 0; i < b.N; i++ {
file, line := r.SourceLine()
_ = file
_ = line
}
})
b.Run("stringifying", func(b *testing.B) {
for i := 0; i < b.N; i++ {
file, line := r.SourceLine()
buf := buffer.New()
buf.WriteString(file)
buf.WriteByte(':')
itoa((*[]byte)(buf), line, -1)
s := string(*buf)
buf.Free()
_ = s
}
})
}
func BenchmarkRecord(b *testing.B) {
const nAttrs = nAttrsInline * 10
var a Attr
for i := 0; i < b.N; i++ {
r := MakeRecord(time.Time{}, InfoLevel, "", 0)
for j := 0; j < nAttrs; j++ {
r.AddAttr(Int("k", j))
}
for j := 0; j < nAttrs; j++ {
a = r.Attr(j)
}
}
_ = a
}