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.
20 var testTime = time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC)
22 func TestTextHandler(t *testing.T) {
23 for _, test := range []struct {
26 wantKey, wantVal string
35 String("x = y", `qu"o`),
40 Any("name", name{"Ren", "Hoek"}),
41 `name`, `"Hoek, Ren"`,
45 Any("x", &struct{ A, b int }{A: 1, b: 2}),
50 Any("t", text{"abc"}),
51 `t`, `"text{\"abc\"}"`,
54 "TextMarshaler error",
56 `t`, `"!ERROR:text: empty string"`,
64 t.Run(test.name, func(t *testing.T) {
65 for _, opts := range []struct {
69 modKey func(string) string
74 `time=2000-01-02T03:04:05.000Z level=INFO msg="a message"`,
75 func(s string) string { return s },
79 HandlerOptions{ReplaceAttr: upperCaseKey},
80 `TIME=2000-01-02T03:04:05.000Z LEVEL=INFO MSG="a message"`,
84 t.Run(opts.name, func(t *testing.T) {
86 h := opts.opts.NewTextHandler(&buf)
87 r := NewRecord(testTime, LevelInfo, "a message", 0)
89 if err := h.Handle(context.Background(), r); err != nil {
93 // Remove final newline.
94 got = got[:len(got)-1]
95 want := opts.wantPrefix + " " + opts.modKey(test.wantKey) + "=" + test.wantVal
97 t.Errorf("\ngot %s\nwant %s", got, want)
105 // for testing fmt.Sprint
110 func (n name) String() string { return n.Last + ", " + n.First }
112 // for testing TextMarshaler
117 func (t text) String() string { return t.s } // should be ignored
119 func (t text) MarshalText() ([]byte, error) {
121 return nil, errors.New("text: empty string")
123 return []byte(fmt.Sprintf("text{%q}", t.s)), nil
126 func TestTextHandlerSource(t *testing.T) {
128 h := HandlerOptions{AddSource: true}.NewTextHandler(&buf)
129 r := NewRecord(testTime, LevelInfo, "m", callerPC(2))
130 if err := h.Handle(context.Background(), r); err != nil {
133 if got := buf.String(); !sourceRegexp.MatchString(got) {
134 t.Errorf("got\n%q\nwanted to match %s", got, sourceRegexp)
138 var sourceRegexp = regexp.MustCompile(`source="?([A-Z]:)?[^:]+text_handler_test\.go:\d+"? msg`)
140 func TestSourceRegexp(t *testing.T) {
141 for _, s := range []string{
142 `source=/tmp/path/to/text_handler_test.go:23 msg=m`,
143 `source=C:\windows\path\text_handler_test.go:23 msg=m"`,
144 `source="/tmp/tmp.XcGZ9cG9Xb/with spaces/exp/slog/text_handler_test.go:95" msg=m`,
146 if !sourceRegexp.MatchString(s) {
147 t.Errorf("failed to match %s", s)
152 func TestTextHandlerPreformatted(t *testing.T) {
154 var h Handler = NewTextHandler(&buf)
155 h = h.WithAttrs([]Attr{Duration("dur", time.Minute), Bool("b", true)})
156 // Also test omitting time.
157 r := NewRecord(time.Time{}, 0 /* 0 Level is INFO */, "m", 0)
158 r.AddAttrs(Int("a", 1))
159 if err := h.Handle(context.Background(), r); err != nil {
162 got := strings.TrimSuffix(buf.String(), "\n")
163 want := `level=INFO msg=m dur=1m0s b=true a=1`
165 t.Errorf("got %s, want %s", got, want)
169 func TestTextHandlerAlloc(t *testing.T) {
170 testenv.SkipIfOptimizationOff(t)
171 r := NewRecord(time.Now(), LevelInfo, "msg", 0)
172 for i := 0; i < 10; i++ {
173 r.AddAttrs(Int("x = y", i))
175 var h Handler = NewTextHandler(io.Discard)
176 wantAllocs(t, 0, func() { h.Handle(context.Background(), r) })
179 r.AddAttrs(Group("g", Int("a", 1)))
180 wantAllocs(t, 0, func() { h.Handle(context.Background(), r) })
183 func TestNeedsQuoting(t *testing.T) {
184 for _, test := range []struct {
196 got := needsQuoting(test.in)
197 if got != test.want {
198 t.Errorf("%q: got %t, want %t", test.in, got, test.want)