github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 TimeValue(time.Date(2001, 1, 2, 3, 4, 5, 0, time.UTC)), 34 AnyValue(&x), 35 AnyValue(&y), 36 GroupValue(Bool("b", true), Int("i", 3)), 37 GroupValue(Bool("b", true), Int("i", 4)), 38 GroupValue(Bool("b", true), Int("j", 4)), 39 DurationValue(3 * time.Second), 40 DurationValue(2 * time.Second), 41 StringValue("foo"), 42 StringValue("fuu"), 43 } 44 for i, v1 := range vals { 45 for j, v2 := range vals { 46 got := v1.Equal(v2) 47 want := i == j 48 if got != want { 49 t.Errorf("%v.Equal(%v): got %t, want %t", v1, v2, got, want) 50 } 51 } 52 } 53 } 54 55 func panics(f func()) (b bool) { 56 defer func() { 57 if x := recover(); x != nil { 58 b = true 59 } 60 }() 61 f() 62 return false 63 } 64 65 func TestValueString(t *testing.T) { 66 for _, test := range []struct { 67 v Value 68 want string 69 }{ 70 {Int64Value(-3), "-3"}, 71 {Uint64Value(1), "1"}, 72 {Float64Value(.15), "0.15"}, 73 {BoolValue(true), "true"}, 74 {StringValue("foo"), "foo"}, 75 {TimeValue(testTime), "2000-01-02 03:04:05 +0000 UTC"}, 76 {AnyValue(time.Duration(3 * time.Second)), "3s"}, 77 {GroupValue(Int("a", 1), Bool("b", true)), "[a=1 b=true]"}, 78 } { 79 if got := test.v.String(); got != test.want { 80 t.Errorf("%#v:\ngot %q\nwant %q", test.v, got, test.want) 81 } 82 } 83 } 84 85 func TestValueNoAlloc(t *testing.T) { 86 // Assign values just to make sure the compiler doesn't optimize away the statements. 87 var ( 88 i int64 89 u uint64 90 f float64 91 b bool 92 s string 93 x any 94 p = &i 95 d time.Duration 96 tm time.Time 97 ) 98 a := int(testing.AllocsPerRun(5, func() { 99 i = Int64Value(1).Int64() 100 u = Uint64Value(1).Uint64() 101 f = Float64Value(1).Float64() 102 b = BoolValue(true).Bool() 103 s = StringValue("foo").String() 104 d = DurationValue(d).Duration() 105 tm = TimeValue(testTime).Time() 106 x = AnyValue(p).Any() 107 })) 108 if a != 0 { 109 t.Errorf("got %d allocs, want zero", a) 110 } 111 _ = u 112 _ = f 113 _ = b 114 _ = s 115 _ = x 116 _ = tm 117 } 118 119 func TestAnyLevelAlloc(t *testing.T) { 120 // Because typical Levels are small integers, 121 // they are zero-alloc. 122 var a Value 123 x := LevelDebug + 100 124 wantAllocs(t, 0, func() { a = AnyValue(x) }) 125 _ = a 126 } 127 128 func TestAnyValue(t *testing.T) { 129 for _, test := range []struct { 130 in any 131 want Value 132 }{ 133 {1, IntValue(1)}, 134 {1.5, Float64Value(1.5)}, 135 {float32(2.5), Float64Value(2.5)}, 136 {"s", StringValue("s")}, 137 {true, BoolValue(true)}, 138 {testTime, TimeValue(testTime)}, 139 {time.Hour, DurationValue(time.Hour)}, 140 {[]Attr{Int("i", 3)}, GroupValue(Int("i", 3))}, 141 {IntValue(4), IntValue(4)}, 142 {uint(2), Uint64Value(2)}, 143 {uint8(3), Uint64Value(3)}, 144 {uint16(4), Uint64Value(4)}, 145 {uint32(5), Uint64Value(5)}, 146 {uint64(6), Uint64Value(6)}, 147 {uintptr(7), Uint64Value(7)}, 148 {int8(8), Int64Value(8)}, 149 {int16(9), Int64Value(9)}, 150 {int32(10), Int64Value(10)}, 151 {int64(11), Int64Value(11)}, 152 } { 153 got := AnyValue(test.in) 154 if !got.Equal(test.want) { 155 t.Errorf("%v (%[1]T): got %v (kind %s), want %v (kind %s)", 156 test.in, got, got.Kind(), test.want, test.want.Kind()) 157 } 158 } 159 } 160 161 func TestValueAny(t *testing.T) { 162 for _, want := range []any{ 163 nil, 164 LevelDebug + 100, 165 time.UTC, // time.Locations treated specially... 166 KindBool, // ...as are Kinds 167 []Attr{Int("a", 1)}, 168 int64(2), 169 uint64(3), 170 true, 171 time.Minute, 172 time.Time{}, 173 3.14, 174 "foo", 175 } { 176 v := AnyValue(want) 177 got := v.Any() 178 if !reflect.DeepEqual(got, want) { 179 t.Errorf("got %v, want %v", got, want) 180 } 181 } 182 } 183 184 func TestLogValue(t *testing.T) { 185 want := "replaced" 186 r := &replace{StringValue(want)} 187 v := AnyValue(r) 188 if g, w := v.Kind(), KindLogValuer; g != w { 189 t.Errorf("got %s, want %s", g, w) 190 } 191 got := v.LogValuer().LogValue().Any() 192 if got != want { 193 t.Errorf("got %#v, want %#v", got, want) 194 } 195 196 // Test Resolve. 197 got = v.Resolve().Any() 198 if got != want { 199 t.Errorf("got %#v, want %#v", got, want) 200 } 201 202 // Test Resolve max iteration. 203 r.v = AnyValue(r) // create a cycle 204 got = AnyValue(r).Resolve().Any() 205 if _, ok := got.(error); !ok { 206 t.Errorf("expected error, got %T", got) 207 } 208 209 // Groups are not recursively resolved. 210 c := Any("c", &replace{StringValue("d")}) 211 v = AnyValue(&replace{GroupValue(Int("a", 1), Group("b", c))}) 212 got2 := v.Resolve().Any().([]Attr) 213 want2 := []Attr{Int("a", 1), Group("b", c)} 214 if !attrsEqual(got2, want2) { 215 t.Errorf("got %v, want %v", got2, want2) 216 } 217 218 // Verify that panics in Resolve are caught and turn into errors. 219 v = AnyValue(panickingLogValue{}) 220 got = v.Resolve().Any() 221 gotErr, ok := got.(error) 222 if !ok { 223 t.Errorf("expected error, got %T", got) 224 } 225 // The error should provide some context information. 226 // We'll just check that this function name appears in it. 227 if got, want := gotErr.Error(), "TestLogValue"; !strings.Contains(got, want) { 228 t.Errorf("got %q, want substring %q", got, want) 229 } 230 } 231 232 func TestZeroTime(t *testing.T) { 233 z := time.Time{} 234 got := TimeValue(z).Time() 235 if !got.IsZero() { 236 t.Errorf("got %s (%#[1]v), not zero time (%#v)", got, z) 237 } 238 } 239 240 func TestEmptyGroup(t *testing.T) { 241 g := GroupValue( 242 Int("a", 1), 243 Group("g1", Group("g2")), 244 Group("g3", Group("g4", Int("b", 2)))) 245 got := g.Group() 246 want := []Attr{Int("a", 1), Group("g3", Group("g4", Int("b", 2)))} 247 if !attrsEqual(got, want) { 248 t.Errorf("\ngot %v\nwant %v", got, want) 249 } 250 } 251 252 type replace struct { 253 v Value 254 } 255 256 func (r *replace) LogValue() Value { return r.v } 257 258 type panickingLogValue struct{} 259 260 func (panickingLogValue) LogValue() Value { panic("bad") } 261 262 // A Value with "unsafe" strings is significantly faster: 263 // safe: 1785 ns/op, 0 allocs 264 // unsafe: 690 ns/op, 0 allocs 265 266 // Run this with and without -tags unsafe_kvs to compare. 267 func BenchmarkUnsafeStrings(b *testing.B) { 268 b.ReportAllocs() 269 dst := make([]Value, 100) 270 src := make([]Value, len(dst)) 271 b.Logf("Value size = %d", unsafe.Sizeof(Value{})) 272 for i := range src { 273 src[i] = StringValue(fmt.Sprintf("string#%d", i)) 274 } 275 b.ResetTimer() 276 var d string 277 for i := 0; i < b.N; i++ { 278 copy(dst, src) 279 for _, a := range dst { 280 d = a.String() 281 } 282 } 283 _ = d 284 }