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