]> Cypherpunks.ru repositories - gostls13.git/blob - src/log/slog/value_test.go
log/slog: initial commit
[gostls13.git] / src / log / slog / value_test.go
1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package slog
6
7 import (
8         "fmt"
9         "reflect"
10         "testing"
11         "time"
12         "unsafe"
13 )
14
15 func TestValueEqual(t *testing.T) {
16         var x, y int
17         vals := []Value{
18                 {},
19                 Int64Value(1),
20                 Int64Value(2),
21                 Float64Value(3.5),
22                 Float64Value(3.7),
23                 BoolValue(true),
24                 BoolValue(false),
25                 TimeValue(testTime),
26                 AnyValue(&x),
27                 AnyValue(&y),
28                 GroupValue(Bool("b", true), Int("i", 3)),
29         }
30         for i, v1 := range vals {
31                 for j, v2 := range vals {
32                         got := v1.Equal(v2)
33                         want := i == j
34                         if got != want {
35                                 t.Errorf("%v.Equal(%v): got %t, want %t", v1, v2, got, want)
36                         }
37                 }
38         }
39 }
40
41 func panics(f func()) (b bool) {
42         defer func() {
43                 if x := recover(); x != nil {
44                         b = true
45                 }
46         }()
47         f()
48         return false
49 }
50
51 func TestValueString(t *testing.T) {
52         for _, test := range []struct {
53                 v    Value
54                 want string
55         }{
56                 {Int64Value(-3), "-3"},
57                 {Float64Value(.15), "0.15"},
58                 {BoolValue(true), "true"},
59                 {StringValue("foo"), "foo"},
60                 {TimeValue(testTime), "2000-01-02 03:04:05 +0000 UTC"},
61                 {AnyValue(time.Duration(3 * time.Second)), "3s"},
62         } {
63                 if got := test.v.String(); got != test.want {
64                         t.Errorf("%#v:\ngot  %q\nwant %q", test.v, got, test.want)
65                 }
66         }
67 }
68
69 func TestValueNoAlloc(t *testing.T) {
70         // Assign values just to make sure the compiler doesn't optimize away the statements.
71         var (
72                 i  int64
73                 u  uint64
74                 f  float64
75                 b  bool
76                 s  string
77                 x  any
78                 p  = &i
79                 d  time.Duration
80                 tm time.Time
81         )
82         a := int(testing.AllocsPerRun(5, func() {
83                 i = Int64Value(1).Int64()
84                 u = Uint64Value(1).Uint64()
85                 f = Float64Value(1).Float64()
86                 b = BoolValue(true).Bool()
87                 s = StringValue("foo").String()
88                 d = DurationValue(d).Duration()
89                 tm = TimeValue(testTime).Time()
90                 x = AnyValue(p).Any()
91         }))
92         if a != 0 {
93                 t.Errorf("got %d allocs, want zero", a)
94         }
95         _ = u
96         _ = f
97         _ = b
98         _ = s
99         _ = x
100         _ = tm
101 }
102
103 func TestAnyLevelAlloc(t *testing.T) {
104         // Because typical Levels are small integers,
105         // they are zero-alloc.
106         var a Value
107         x := LevelDebug + 100
108         wantAllocs(t, 0, func() { a = AnyValue(x) })
109         _ = a
110 }
111
112 func TestAnyValue(t *testing.T) {
113         for _, test := range []struct {
114                 in   any
115                 want Value
116         }{
117                 {1, IntValue(1)},
118                 {1.5, Float64Value(1.5)},
119                 {"s", StringValue("s")},
120                 {uint(2), Uint64Value(2)},
121                 {true, BoolValue(true)},
122                 {testTime, TimeValue(testTime)},
123                 {time.Hour, DurationValue(time.Hour)},
124                 {[]Attr{Int("i", 3)}, GroupValue(Int("i", 3))},
125                 {IntValue(4), IntValue(4)},
126         } {
127                 got := AnyValue(test.in)
128                 if !got.Equal(test.want) {
129                         t.Errorf("%v (%[1]T): got %v (kind %s), want %v (kind %s)",
130                                 test.in, got, got.Kind(), test.want, test.want.Kind())
131                 }
132         }
133 }
134
135 func TestValueAny(t *testing.T) {
136         for _, want := range []any{
137                 nil,
138                 LevelDebug + 100,
139                 time.UTC, // time.Locations treated specially...
140                 KindBool, // ...as are Kinds
141                 []Attr{Int("a", 1)},
142         } {
143                 v := AnyValue(want)
144                 got := v.Any()
145                 if !reflect.DeepEqual(got, want) {
146                         t.Errorf("got %v, want %v", got, want)
147                 }
148         }
149 }
150
151 func TestLogValue(t *testing.T) {
152         want := "replaced"
153         r := &replace{StringValue(want)}
154         v := AnyValue(r)
155         if g, w := v.Kind(), KindLogValuer; g != w {
156                 t.Errorf("got %s, want %s", g, w)
157         }
158         got := v.LogValuer().LogValue().Any()
159         if got != want {
160                 t.Errorf("got %#v, want %#v", got, want)
161         }
162
163         // Test Resolve.
164         got = v.Resolve().Any()
165         if got != want {
166                 t.Errorf("got %#v, want %#v", got, want)
167         }
168
169         // Test Resolve max iteration.
170         r.v = AnyValue(r) // create a cycle
171         got = AnyValue(r).Resolve().Any()
172         if _, ok := got.(error); !ok {
173                 t.Errorf("expected error, got %T", got)
174         }
175
176         // Test Resolve group.
177         r = &replace{GroupValue(
178                 Int("a", 1),
179                 Group("b", Any("c", &replace{StringValue("d")})),
180         )}
181         v = AnyValue(r)
182         got2 := v.Resolve().Any().([]Attr)
183         want2 := []Attr{Int("a", 1), Group("b", String("c", "d"))}
184         if !attrsEqual(got2, want2) {
185                 t.Errorf("got %v, want %v", got2, want2)
186         }
187
188 }
189
190 func TestZeroTime(t *testing.T) {
191         z := time.Time{}
192         got := TimeValue(z).Time()
193         if !got.IsZero() {
194                 t.Errorf("got %s (%#[1]v), not zero time (%#v)", got, z)
195         }
196 }
197
198 type replace struct {
199         v Value
200 }
201
202 func (r *replace) LogValue() Value { return r.v }
203
204 // A Value with "unsafe" strings is significantly faster:
205 // safe:  1785 ns/op, 0 allocs
206 // unsafe: 690 ns/op, 0 allocs
207
208 // Run this with and without -tags unsafe_kvs to compare.
209 func BenchmarkUnsafeStrings(b *testing.B) {
210         b.ReportAllocs()
211         dst := make([]Value, 100)
212         src := make([]Value, len(dst))
213         b.Logf("Value size = %d", unsafe.Sizeof(Value{}))
214         for i := range src {
215                 src[i] = StringValue(fmt.Sprintf("string#%d", i))
216         }
217         b.ResetTimer()
218         var d string
219         for i := 0; i < b.N; i++ {
220                 copy(dst, src)
221                 for _, a := range dst {
222                         d = a.String()
223                 }
224         }
225         _ = d
226 }