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