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