| | |
| | |
| | |
| |
|
| | package slog |
| |
|
| | import ( |
| | "bytes" |
| | "context" |
| | "errors" |
| | "fmt" |
| | "internal/testenv" |
| | "io" |
| | "strings" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | var testTime = time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC) |
| |
|
| | func TestTextHandler(t *testing.T) { |
| | for _, test := range []struct { |
| | name string |
| | attr Attr |
| | wantKey, wantVal string |
| | }{ |
| | { |
| | "unquoted", |
| | Int("a", 1), |
| | "a", "1", |
| | }, |
| | { |
| | "quoted", |
| | String("x = y", `qu"o`), |
| | `"x = y"`, `"qu\"o"`, |
| | }, |
| | { |
| | "String method", |
| | Any("name", name{"Ren", "Hoek"}), |
| | `name`, `"Hoek, Ren"`, |
| | }, |
| | { |
| | "struct", |
| | Any("x", &struct{ A, b int }{A: 1, b: 2}), |
| | `x`, `"&{A:1 b:2}"`, |
| | }, |
| | { |
| | "TextMarshaler", |
| | Any("t", text{"abc"}), |
| | `t`, `"text{\"abc\"}"`, |
| | }, |
| | { |
| | "TextMarshaler error", |
| | Any("t", text{""}), |
| | `t`, `"!ERROR:text: empty string"`, |
| | }, |
| | { |
| | "nil value", |
| | Any("a", nil), |
| | `a`, `<nil>`, |
| | }, |
| | } { |
| | t.Run(test.name, func(t *testing.T) { |
| | for _, opts := range []struct { |
| | name string |
| | opts HandlerOptions |
| | wantPrefix string |
| | modKey func(string) string |
| | }{ |
| | { |
| | "none", |
| | HandlerOptions{}, |
| | `time=2000-01-02T03:04:05.000Z level=INFO msg="a message"`, |
| | func(s string) string { return s }, |
| | }, |
| | { |
| | "replace", |
| | HandlerOptions{ReplaceAttr: upperCaseKey}, |
| | `TIME=2000-01-02T03:04:05.000Z LEVEL=INFO MSG="a message"`, |
| | strings.ToUpper, |
| | }, |
| | } { |
| | t.Run(opts.name, func(t *testing.T) { |
| | var buf bytes.Buffer |
| | h := NewTextHandler(&buf, &opts.opts) |
| | r := NewRecord(testTime, LevelInfo, "a message", 0) |
| | r.AddAttrs(test.attr) |
| | if err := h.Handle(context.Background(), r); err != nil { |
| | t.Fatal(err) |
| | } |
| | got := buf.String() |
| | |
| | got = got[:len(got)-1] |
| | want := opts.wantPrefix + " " + opts.modKey(test.wantKey) + "=" + test.wantVal |
| | if got != want { |
| | t.Errorf("\ngot %s\nwant %s", got, want) |
| | } |
| | }) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | type name struct { |
| | First, Last string |
| | } |
| |
|
| | func (n name) String() string { return n.Last + ", " + n.First } |
| |
|
| | |
| | type text struct { |
| | s string |
| | } |
| |
|
| | func (t text) String() string { return t.s } |
| |
|
| | func (t text) MarshalText() ([]byte, error) { |
| | if t.s == "" { |
| | return nil, errors.New("text: empty string") |
| | } |
| | return []byte(fmt.Sprintf("text{%q}", t.s)), nil |
| | } |
| |
|
| | func TestTextHandlerPreformatted(t *testing.T) { |
| | var buf bytes.Buffer |
| | var h Handler = NewTextHandler(&buf, nil) |
| | h = h.WithAttrs([]Attr{Duration("dur", time.Minute), Bool("b", true)}) |
| | |
| | r := NewRecord(time.Time{}, 0 , "m", 0) |
| | r.AddAttrs(Int("a", 1)) |
| | if err := h.Handle(context.Background(), r); err != nil { |
| | t.Fatal(err) |
| | } |
| | got := strings.TrimSuffix(buf.String(), "\n") |
| | want := `level=INFO msg=m dur=1m0s b=true a=1` |
| | if got != want { |
| | t.Errorf("got %s, want %s", got, want) |
| | } |
| | } |
| |
|
| | func TestTextHandlerAlloc(t *testing.T) { |
| | testenv.SkipIfOptimizationOff(t) |
| | r := NewRecord(time.Now(), LevelInfo, "msg", 0) |
| | for i := 0; i < 10; i++ { |
| | r.AddAttrs(Int("x = y", i)) |
| | } |
| | var h Handler = NewTextHandler(io.Discard, nil) |
| | wantAllocs(t, 0, func() { h.Handle(context.Background(), r) }) |
| |
|
| | h = h.WithGroup("s") |
| | r.AddAttrs(Group("g", Int("a", 1))) |
| | wantAllocs(t, 0, func() { h.Handle(context.Background(), r) }) |
| | } |
| |
|
| | func TestNeedsQuoting(t *testing.T) { |
| | for _, test := range []struct { |
| | in string |
| | want bool |
| | }{ |
| | {"", true}, |
| | {"ab", false}, |
| | {"a=b", true}, |
| | {`"ab"`, true}, |
| | {"\a\b", true}, |
| | {"a\tb", true}, |
| | {"µåπ", false}, |
| | {"a b", true}, |
| | {"badutf8\xF6", true}, |
| | } { |
| | got := needsQuoting(test.in) |
| | if got != test.want { |
| | t.Errorf("%q: got %t, want %t", test.in, got, test.want) |
| | } |
| | } |
| | } |
| |
|