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