github.com/AndrienkoAleksandr/go@v0.0.19/src/log/slog/value_test.go (about) 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 "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 // Assign values just to make sure the compiler doesn't optimize away the statements. 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 // Because typical Levels are small integers, 114 // they are zero-alloc. 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, // time.Locations treated specially... 159 KindBool, // ...as are Kinds 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 // Test Resolve. 189 got = v.Resolve().Any() 190 if got != want { 191 t.Errorf("got %#v, want %#v", got, want) 192 } 193 194 // Test Resolve max iteration. 195 r.v = AnyValue(r) // create a cycle 196 got = AnyValue(r).Resolve().Any() 197 if _, ok := got.(error); !ok { 198 t.Errorf("expected error, got %T", got) 199 } 200 201 // Groups are not recursively resolved. 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 // Verify that panics in Resolve are caught and turn into errors. 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 // The error should provide some context information. 218 // We'll just check that this function name appears in it. 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 type replace struct { 233 v Value 234 } 235 236 func (r *replace) LogValue() Value { return r.v } 237 238 type panickingLogValue struct{} 239 240 func (panickingLogValue) LogValue() Value { panic("bad") } 241 242 // A Value with "unsafe" strings is significantly faster: 243 // safe: 1785 ns/op, 0 allocs 244 // unsafe: 690 ns/op, 0 allocs 245 246 // Run this with and without -tags unsafe_kvs to compare. 247 func BenchmarkUnsafeStrings(b *testing.B) { 248 b.ReportAllocs() 249 dst := make([]Value, 100) 250 src := make([]Value, len(dst)) 251 b.Logf("Value size = %d", unsafe.Sizeof(Value{})) 252 for i := range src { 253 src[i] = StringValue(fmt.Sprintf("string#%d", i)) 254 } 255 b.ResetTimer() 256 var d string 257 for i := 0; i < b.N; i++ { 258 copy(dst, src) 259 for _, a := range dst { 260 d = a.String() 261 } 262 } 263 _ = d 264 }