golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/slog/record_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 "strconv" 9 "strings" 10 "testing" 11 "time" 12 13 "golang.org/x/exp/slices" 14 ) 15 16 func TestRecordAttrs(t *testing.T) { 17 as := []Attr{Int("k1", 1), String("k2", "foo"), Int("k3", 3), 18 Int64("k4", -1), Float64("f", 3.1), Uint64("u", 999)} 19 r := newRecordWithAttrs(as) 20 if g, w := r.NumAttrs(), len(as); g != w { 21 t.Errorf("NumAttrs: got %d, want %d", g, w) 22 } 23 if got := attrsSlice(r); !attrsEqual(got, as) { 24 t.Errorf("got %v, want %v", got, as) 25 } 26 27 // Early return. 28 var got []Attr 29 r.Attrs(func(a Attr) bool { 30 got = append(got, a) 31 return len(got) < 2 32 }) 33 want := as[:2] 34 if !attrsEqual(got, want) { 35 t.Errorf("got %v, want %v", got, want) 36 } 37 } 38 39 func TestRecordSource(t *testing.T) { 40 // Zero call depth => empty *Source. 41 for _, test := range []struct { 42 depth int 43 wantFunction string 44 wantFile string 45 wantLinePositive bool 46 }{ 47 {0, "", "", false}, 48 {-16, "", "", false}, 49 {1, "golang.org/x/exp/slog.TestRecordSource", "record_test.go", true}, // 1: caller of NewRecord 50 {2, "testing.tRunner", "testing.go", true}, 51 } { 52 var pc uintptr 53 if test.depth > 0 { 54 pc = callerPC(test.depth + 1) 55 } 56 r := NewRecord(time.Time{}, 0, "", pc) 57 got := r.source() 58 if i := strings.LastIndexByte(got.File, '/'); i >= 0 { 59 got.File = got.File[i+1:] 60 } 61 if got.Function != test.wantFunction || got.File != test.wantFile || (got.Line > 0) != test.wantLinePositive { 62 t.Errorf("depth %d: got (%q, %q, %d), want (%q, %q, %t)", 63 test.depth, 64 got.Function, got.File, got.Line, 65 test.wantFunction, test.wantFile, test.wantLinePositive) 66 } 67 } 68 } 69 70 func TestAliasingAndClone(t *testing.T) { 71 intAttrs := func(from, to int) []Attr { 72 var as []Attr 73 for i := from; i < to; i++ { 74 as = append(as, Int("k", i)) 75 } 76 return as 77 } 78 79 check := func(r Record, want []Attr) { 80 t.Helper() 81 got := attrsSlice(r) 82 if !attrsEqual(got, want) { 83 t.Errorf("got %v, want %v", got, want) 84 } 85 } 86 87 // Create a record whose Attrs overflow the inline array, 88 // creating a slice in r.back. 89 r1 := NewRecord(time.Time{}, 0, "", 0) 90 r1.AddAttrs(intAttrs(0, nAttrsInline+1)...) 91 // Ensure that r1.back's capacity exceeds its length. 92 b := make([]Attr, len(r1.back), len(r1.back)+1) 93 copy(b, r1.back) 94 r1.back = b 95 // Make a copy that shares state. 96 r2 := r1 97 // Adding to both should panic. 98 r1.AddAttrs(Int("p", 0)) 99 if !panics(func() { r2.AddAttrs(Int("p", 1)) }) { 100 t.Error("expected panic") 101 } 102 r1Attrs := attrsSlice(r1) 103 // Adding to a clone is fine. 104 r2 = r1.Clone() 105 check(r2, r1Attrs) 106 r2.AddAttrs(Int("p", 2)) 107 check(r1, r1Attrs) // r1 is unchanged 108 check(r2, append(slices.Clip(r1Attrs), Int("p", 2))) 109 } 110 111 func newRecordWithAttrs(as []Attr) Record { 112 r := NewRecord(time.Now(), LevelInfo, "", 0) 113 r.AddAttrs(as...) 114 return r 115 } 116 117 func attrsSlice(r Record) []Attr { 118 s := make([]Attr, 0, r.NumAttrs()) 119 r.Attrs(func(a Attr) bool { s = append(s, a); return true }) 120 return s 121 } 122 123 func attrsEqual(as1, as2 []Attr) bool { 124 return slices.EqualFunc(as1, as2, Attr.Equal) 125 } 126 127 // Currently, pc(2) takes over 400ns, which is too expensive 128 // to call it for every log message. 129 func BenchmarkPC(b *testing.B) { 130 for depth := 0; depth < 5; depth++ { 131 b.Run(strconv.Itoa(depth), func(b *testing.B) { 132 b.ReportAllocs() 133 var x uintptr 134 for i := 0; i < b.N; i++ { 135 x = callerPC(depth) 136 } 137 _ = x 138 }) 139 } 140 } 141 142 func BenchmarkRecord(b *testing.B) { 143 const nAttrs = nAttrsInline * 10 144 var a Attr 145 146 for i := 0; i < b.N; i++ { 147 r := NewRecord(time.Time{}, LevelInfo, "", 0) 148 for j := 0; j < nAttrs; j++ { 149 r.AddAttrs(Int("k", j)) 150 } 151 r.Attrs(func(b Attr) bool { a = b; return true }) 152 } 153 _ = a 154 }