1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package zapio
22
23 import (
24 "io"
25 "testing"
26
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29 "go.uber.org/zap"
30 "go.uber.org/zap/zapcore"
31 "go.uber.org/zap/zaptest/observer"
32 )
33
34 func TestWriter(t *testing.T) {
35 t.Parallel()
36
37 tests := []struct {
38 desc string
39 level zapcore.Level
40 writes []string
41 want []zapcore.Entry
42 }{
43 {
44 desc: "simple",
45 writes: []string{
46 "foo\n",
47 "bar\n",
48 "baz\n",
49 },
50 want: []zapcore.Entry{
51 {Level: zap.InfoLevel, Message: "foo"},
52 {Level: zap.InfoLevel, Message: "bar"},
53 {Level: zap.InfoLevel, Message: "baz"},
54 },
55 },
56 {
57 desc: "level too low",
58 level: zap.DebugLevel,
59 writes: []string{
60 "foo\n",
61 "bar\n",
62 },
63 want: []zapcore.Entry{},
64 },
65 {
66 desc: "multiple newlines in a message",
67 level: zap.WarnLevel,
68 writes: []string{
69 "foo\nbar\n",
70 "baz\n",
71 "qux\nquux\n",
72 },
73 want: []zapcore.Entry{
74 {Level: zap.WarnLevel, Message: "foo"},
75 {Level: zap.WarnLevel, Message: "bar"},
76 {Level: zap.WarnLevel, Message: "baz"},
77 {Level: zap.WarnLevel, Message: "qux"},
78 {Level: zap.WarnLevel, Message: "quux"},
79 },
80 },
81 {
82 desc: "message split across multiple writes",
83 level: zap.ErrorLevel,
84 writes: []string{
85 "foo",
86 "bar\nbaz",
87 "qux",
88 },
89 want: []zapcore.Entry{
90 {Level: zap.ErrorLevel, Message: "foobar"},
91 {Level: zap.ErrorLevel, Message: "bazqux"},
92 },
93 },
94 {
95 desc: "blank lines in the middle",
96 writes: []string{
97 "foo\n\nbar\nbaz",
98 },
99 want: []zapcore.Entry{
100 {Level: zap.InfoLevel, Message: "foo"},
101 {Level: zap.InfoLevel, Message: ""},
102 {Level: zap.InfoLevel, Message: "bar"},
103 {Level: zap.InfoLevel, Message: "baz"},
104 },
105 },
106 {
107 desc: "blank line at the end",
108 writes: []string{
109 "foo\nbar\nbaz\n",
110 },
111 want: []zapcore.Entry{
112 {Level: zap.InfoLevel, Message: "foo"},
113 {Level: zap.InfoLevel, Message: "bar"},
114 {Level: zap.InfoLevel, Message: "baz"},
115 },
116 },
117 {
118 desc: "multiple blank line at the end",
119 writes: []string{
120 "foo\nbar\nbaz\n\n",
121 },
122 want: []zapcore.Entry{
123 {Level: zap.InfoLevel, Message: "foo"},
124 {Level: zap.InfoLevel, Message: "bar"},
125 {Level: zap.InfoLevel, Message: "baz"},
126 {Level: zap.InfoLevel, Message: ""},
127 },
128 },
129 }
130
131 for _, tt := range tests {
132 tt := tt
133 t.Run(tt.desc, func(t *testing.T) {
134 t.Parallel()
135
136 core, observed := observer.New(zap.InfoLevel)
137
138 w := Writer{
139 Log: zap.New(core),
140 Level: tt.level,
141 }
142
143 for _, s := range tt.writes {
144 _, err := io.WriteString(&w, s)
145 require.NoError(t, err, "Writer.Write failed.")
146 }
147
148 assert.NoError(t, w.Close(), "Writer.Close failed.")
149
150
151 got := make([]zapcore.Entry, observed.Len())
152 for i, ent := range observed.AllUntimed() {
153 got[i] = ent.Entry
154 }
155 assert.Equal(t, tt.want, got, "Logged entries do not match.")
156 })
157 }
158 }
159
160 func TestWrite_Sync(t *testing.T) {
161 t.Parallel()
162
163 core, observed := observer.New(zap.InfoLevel)
164
165 w := Writer{
166 Log: zap.New(core),
167 Level: zap.InfoLevel,
168 }
169
170 io.WriteString(&w, "foo")
171 io.WriteString(&w, "bar")
172
173 t.Run("no sync", func(t *testing.T) {
174 assert.Zero(t, observed.Len(), "Expected no logs yet")
175 })
176
177 t.Run("sync", func(t *testing.T) {
178 defer observed.TakeAll()
179
180 require.NoError(t, w.Sync(), "Sync must not fail")
181
182 assert.Equal(t, []observer.LoggedEntry{
183 {Entry: zapcore.Entry{Message: "foobar"}, Context: []zapcore.Field{}},
184 }, observed.AllUntimed(), "Log messages did not match")
185 })
186
187 t.Run("sync on empty", func(t *testing.T) {
188 require.NoError(t, w.Sync(), "Sync must not fail")
189 assert.Zero(t, observed.Len(), "Expected no logs yet")
190 })
191 }
192
193 func BenchmarkWriter(b *testing.B) {
194 tests := []struct {
195 name string
196 writes [][]byte
197 }{
198 {
199 name: "single",
200 writes: [][]byte{
201 []byte("foobar\n"),
202 []byte("bazqux\n"),
203 },
204 },
205 {
206 name: "splits",
207 writes: [][]byte{
208 []byte("foo"),
209 []byte("bar\nbaz"),
210 []byte("qux\n"),
211 },
212 },
213 }
214
215 writer := Writer{
216 Log: zap.New(new(partiallyNopCore)),
217 Level: zapcore.DebugLevel,
218 }
219
220 for _, tt := range tests {
221 b.Run(tt.name, func(b *testing.B) {
222 b.ResetTimer()
223
224 for i := 0; i < b.N; i++ {
225 for _, bs := range tt.writes {
226 writer.Write(bs)
227 }
228 }
229 })
230 }
231 }
232
233
234
235
236
237
238 type partiallyNopCore struct{}
239
240 func (*partiallyNopCore) Enabled(zapcore.Level) bool { return true }
241
242 func (c *partiallyNopCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
243 return ce.AddCore(ent, c)
244 }
245
246 func (c *partiallyNopCore) With([]zapcore.Field) zapcore.Core { return c }
247 func (*partiallyNopCore) Write(zapcore.Entry, []zapcore.Field) error { return nil }
248 func (*partiallyNopCore) Sync() error { return nil }
249
View as plain text