1
2
3
4
5 package slog
6
7 import (
8 "strconv"
9 "strings"
10 "testing"
11 "time"
12
13 "golang.org/x/exp/slices"
14 )
15
16 func TestRecordAttrs(t *testing.T) {
17 as := []Attr{Int("k1", 1), String("k2", "foo"), Int("k3", 3),
18 Int64("k4", -1), Float64("f", 3.1), Uint64("u", 999)}
19 r := newRecordWithAttrs(as)
20 if g, w := r.NumAttrs(), len(as); g != w {
21 t.Errorf("NumAttrs: got %d, want %d", g, w)
22 }
23 if got := attrsSlice(r); !attrsEqual(got, as) {
24 t.Errorf("got %v, want %v", got, as)
25 }
26
27
28 var got []Attr
29 r.Attrs(func(a Attr) bool {
30 got = append(got, a)
31 return len(got) < 2
32 })
33 want := as[:2]
34 if !attrsEqual(got, want) {
35 t.Errorf("got %v, want %v", got, want)
36 }
37 }
38
39 func TestRecordSource(t *testing.T) {
40
41 for _, test := range []struct {
42 depth int
43 wantFunction string
44 wantFile string
45 wantLinePositive bool
46 }{
47 {0, "", "", false},
48 {-16, "", "", false},
49 {1, "golang.org/x/exp/slog.TestRecordSource", "record_test.go", true},
50 {2, "testing.tRunner", "testing.go", true},
51 } {
52 var pc uintptr
53 if test.depth > 0 {
54 pc = callerPC(test.depth + 1)
55 }
56 r := NewRecord(time.Time{}, 0, "", pc)
57 got := r.source()
58 if i := strings.LastIndexByte(got.File, '/'); i >= 0 {
59 got.File = got.File[i+1:]
60 }
61 if got.Function != test.wantFunction || got.File != test.wantFile || (got.Line > 0) != test.wantLinePositive {
62 t.Errorf("depth %d: got (%q, %q, %d), want (%q, %q, %t)",
63 test.depth,
64 got.Function, got.File, got.Line,
65 test.wantFunction, test.wantFile, test.wantLinePositive)
66 }
67 }
68 }
69
70 func TestAliasingAndClone(t *testing.T) {
71 intAttrs := func(from, to int) []Attr {
72 var as []Attr
73 for i := from; i < to; i++ {
74 as = append(as, Int("k", i))
75 }
76 return as
77 }
78
79 check := func(r Record, want []Attr) {
80 t.Helper()
81 got := attrsSlice(r)
82 if !attrsEqual(got, want) {
83 t.Errorf("got %v, want %v", got, want)
84 }
85 }
86
87
88
89 r1 := NewRecord(time.Time{}, 0, "", 0)
90 r1.AddAttrs(intAttrs(0, nAttrsInline+1)...)
91
92 b := make([]Attr, len(r1.back), len(r1.back)+1)
93 copy(b, r1.back)
94 r1.back = b
95
96 r2 := r1
97
98 r1.AddAttrs(Int("p", 0))
99 if !panics(func() { r2.AddAttrs(Int("p", 1)) }) {
100 t.Error("expected panic")
101 }
102 r1Attrs := attrsSlice(r1)
103
104 r2 = r1.Clone()
105 check(r2, r1Attrs)
106 r2.AddAttrs(Int("p", 2))
107 check(r1, r1Attrs)
108 check(r2, append(slices.Clip(r1Attrs), Int("p", 2)))
109 }
110
111 func newRecordWithAttrs(as []Attr) Record {
112 r := NewRecord(time.Now(), LevelInfo, "", 0)
113 r.AddAttrs(as...)
114 return r
115 }
116
117 func attrsSlice(r Record) []Attr {
118 s := make([]Attr, 0, r.NumAttrs())
119 r.Attrs(func(a Attr) bool { s = append(s, a); return true })
120 return s
121 }
122
123 func attrsEqual(as1, as2 []Attr) bool {
124 return slices.EqualFunc(as1, as2, Attr.Equal)
125 }
126
127
128
129 func BenchmarkPC(b *testing.B) {
130 for depth := 0; depth < 5; depth++ {
131 b.Run(strconv.Itoa(depth), func(b *testing.B) {
132 b.ReportAllocs()
133 var x uintptr
134 for i := 0; i < b.N; i++ {
135 x = callerPC(depth)
136 }
137 _ = x
138 })
139 }
140 }
141
142 func BenchmarkRecord(b *testing.B) {
143 const nAttrs = nAttrsInline * 10
144 var a Attr
145
146 for i := 0; i < b.N; i++ {
147 r := NewRecord(time.Time{}, LevelInfo, "", 0)
148 for j := 0; j < nAttrs; j++ {
149 r.AddAttrs(Int("k", j))
150 }
151 r.Attrs(func(b Attr) bool { a = b; return true })
152 }
153 _ = a
154 }
155
View as plain text