Source file
src/log/slog/value_test.go
1
2
3
4
5 package slog
6
7 import (
8 "fmt"
9 "internal/asan"
10 "reflect"
11 "strings"
12 "testing"
13 "time"
14 "unsafe"
15 )
16
17 func TestKindString(t *testing.T) {
18 if got, want := KindGroup.String(), "Group"; got != want {
19 t.Errorf("got %q, want %q", got, want)
20 }
21 }
22
23 func TestValueEqual(t *testing.T) {
24 var x, y int
25 vals := []Value{
26 {},
27 Int64Value(1),
28 Int64Value(2),
29 Float64Value(3.5),
30 Float64Value(3.7),
31 BoolValue(true),
32 BoolValue(false),
33 TimeValue(testTime),
34 TimeValue(time.Time{}),
35 TimeValue(time.Date(2001, 1, 2, 3, 4, 5, 0, time.UTC)),
36 TimeValue(time.Date(2300, 1, 1, 0, 0, 0, 0, time.UTC)),
37 TimeValue(time.Date(1715, 6, 13, 0, 25, 26, 290448384, time.UTC)),
38 AnyValue(&x),
39 AnyValue(&y),
40 GroupValue(Bool("b", true), Int("i", 3)),
41 GroupValue(Bool("b", true), Int("i", 4)),
42 GroupValue(Bool("b", true), Int("j", 4)),
43 DurationValue(3 * time.Second),
44 DurationValue(2 * time.Second),
45 StringValue("foo"),
46 StringValue("fuu"),
47 }
48 for i, v1 := range vals {
49 for j, v2 := range vals {
50 got := v1.Equal(v2)
51 want := i == j
52 if got != want {
53 t.Errorf("%v.Equal(%v): got %t, want %t", v1, v2, got, want)
54 }
55 }
56 }
57 }
58
59 func panics(f func()) (b bool) {
60 defer func() {
61 if x := recover(); x != nil {
62 b = true
63 }
64 }()
65 f()
66 return false
67 }
68
69 func TestValueString(t *testing.T) {
70 for _, test := range []struct {
71 v Value
72 want string
73 }{
74 {Int64Value(-3), "-3"},
75 {Uint64Value(1), "1"},
76 {Float64Value(.15), "0.15"},
77 {BoolValue(true), "true"},
78 {StringValue("foo"), "foo"},
79 {TimeValue(testTime), "2000-01-02 03:04:05 +0000 UTC"},
80 {AnyValue(time.Duration(3 * time.Second)), "3s"},
81 {GroupValue(Int("a", 1), Bool("b", true)), "[a=1 b=true]"},
82 } {
83 if got := test.v.String(); got != test.want {
84 t.Errorf("%#v:\ngot %q\nwant %q", test.v, got, test.want)
85 }
86 }
87 }
88
89 func TestValueNoAlloc(t *testing.T) {
90 if asan.Enabled {
91 t.Skip("test allocates more with -asan; see #70079")
92 }
93
94
95 var (
96 i int64
97 u uint64
98 f float64
99 b bool
100 s string
101 x any
102 p = &i
103 d time.Duration
104 tm time.Time
105 )
106 a := int(testing.AllocsPerRun(5, func() {
107 i = Int64Value(1).Int64()
108 u = Uint64Value(1).Uint64()
109 f = Float64Value(1).Float64()
110 b = BoolValue(true).Bool()
111 s = StringValue("foo").String()
112 d = DurationValue(d).Duration()
113 tm = TimeValue(testTime).Time()
114 x = AnyValue(p).Any()
115 }))
116 if a != 0 {
117 t.Errorf("got %d allocs, want zero", a)
118 }
119 _ = u
120 _ = f
121 _ = b
122 _ = s
123 _ = x
124 _ = tm
125 }
126
127 func TestAnyLevelAlloc(t *testing.T) {
128
129
130 var a Value
131 x := LevelDebug + 100
132 wantAllocs(t, 0, func() { a = AnyValue(x) })
133 _ = a
134 }
135
136 func TestAnyValue(t *testing.T) {
137 for _, test := range []struct {
138 in any
139 want Value
140 }{
141 {1, IntValue(1)},
142 {1.5, Float64Value(1.5)},
143 {float32(2.5), Float64Value(2.5)},
144 {"s", StringValue("s")},
145 {true, BoolValue(true)},
146 {testTime, TimeValue(testTime)},
147 {time.Hour, DurationValue(time.Hour)},
148 {[]Attr{Int("i", 3)}, GroupValue(Int("i", 3))},
149 {IntValue(4), IntValue(4)},
150 {uint(2), Uint64Value(2)},
151 {uint8(3), Uint64Value(3)},
152 {uint16(4), Uint64Value(4)},
153 {uint32(5), Uint64Value(5)},
154 {uint64(6), Uint64Value(6)},
155 {uintptr(7), Uint64Value(7)},
156 {int8(8), Int64Value(8)},
157 {int16(9), Int64Value(9)},
158 {int32(10), Int64Value(10)},
159 {int64(11), Int64Value(11)},
160 } {
161 got := AnyValue(test.in)
162 if !got.Equal(test.want) {
163 t.Errorf("%v (%[1]T): got %v (kind %s), want %v (kind %s)",
164 test.in, got, got.Kind(), test.want, test.want.Kind())
165 }
166 }
167 }
168
169 func TestValueAny(t *testing.T) {
170 for _, want := range []any{
171 nil,
172 LevelDebug + 100,
173 time.UTC,
174 KindBool,
175 []Attr{Int("a", 1)},
176 int64(2),
177 uint64(3),
178 true,
179 time.Minute,
180 time.Time{},
181 3.14,
182 "foo",
183 } {
184 v := AnyValue(want)
185 got := v.Any()
186 if !reflect.DeepEqual(got, want) {
187 t.Errorf("got %v, want %v", got, want)
188 }
189 }
190 }
191
192 func TestLogValue(t *testing.T) {
193 want := "replaced"
194 r := &replace{StringValue(want)}
195 v := AnyValue(r)
196 if g, w := v.Kind(), KindLogValuer; g != w {
197 t.Errorf("got %s, want %s", g, w)
198 }
199 got := v.LogValuer().LogValue().Any()
200 if got != want {
201 t.Errorf("got %#v, want %#v", got, want)
202 }
203
204
205 got = v.Resolve().Any()
206 if got != want {
207 t.Errorf("got %#v, want %#v", got, want)
208 }
209
210
211 r.v = AnyValue(r)
212 got = AnyValue(r).Resolve().Any()
213 if _, ok := got.(error); !ok {
214 t.Errorf("expected error, got %T", got)
215 }
216
217
218 c := Any("c", &replace{StringValue("d")})
219 v = AnyValue(&replace{GroupValue(Int("a", 1), Group("b", c))})
220 got2 := v.Resolve().Any().([]Attr)
221 want2 := []Attr{Int("a", 1), Group("b", c)}
222 if !attrsEqual(got2, want2) {
223 t.Errorf("got %v, want %v", got2, want2)
224 }
225
226
227 v = AnyValue(panickingLogValue{})
228 got = v.Resolve().Any()
229 gotErr, ok := got.(error)
230 if !ok {
231 t.Errorf("expected error, got %T", got)
232 }
233
234
235 if got, want := gotErr.Error(), "TestLogValue"; !strings.Contains(got, want) {
236 t.Errorf("got %q, want substring %q", got, want)
237 }
238 }
239
240 func TestValueTime(t *testing.T) {
241
242 for _, tm := range []time.Time{
243 time.Time{},
244 time.Unix(0, 1e15),
245 time.Date(2300, 1, 1, 0, 0, 0, 0, time.UTC),
246 } {
247 got := TimeValue(tm).Time()
248 if !got.Equal(tm) {
249 t.Errorf("got %s (%#[1]v), want %s (%#[2]v)", got, tm)
250 }
251 if g, w := got.Location(), tm.Location(); g != w {
252 t.Errorf("%s: location: got %v, want %v", tm, g, w)
253 }
254 }
255 }
256
257 func TestEmptyGroup(t *testing.T) {
258 g := GroupValue(
259 Int("a", 1),
260 Group("g1", Group("g2")),
261 Group("g3", Group("g4", Int("b", 2))))
262 got := g.Group()
263 want := []Attr{Int("a", 1), Group("g3", Group("g4", Int("b", 2)))}
264 if !attrsEqual(got, want) {
265 t.Errorf("\ngot %v\nwant %v", got, want)
266 }
267 }
268
269 type replace struct {
270 v Value
271 }
272
273 func (r *replace) LogValue() Value { return r.v }
274
275 type panickingLogValue struct{}
276
277 func (panickingLogValue) LogValue() Value { panic("bad") }
278
279
280
281
282
283
284 func BenchmarkUnsafeStrings(b *testing.B) {
285 b.ReportAllocs()
286 dst := make([]Value, 100)
287 src := make([]Value, len(dst))
288 b.Logf("Value size = %d", unsafe.Sizeof(Value{}))
289 for i := range src {
290 src[i] = StringValue(fmt.Sprintf("string#%d", i))
291 }
292 b.ResetTimer()
293 var d string
294 for i := 0; i < b.N; i++ {
295 copy(dst, src)
296 for _, a := range dst {
297 d = a.String()
298 }
299 }
300 _ = d
301 }
302
View as plain text