Source file
src/strings/builder_test.go
1
2
3
4
5 package strings_test
6
7 import (
8 "bytes"
9 . "strings"
10 "testing"
11 "unicode/utf8"
12 )
13
14 func check(t *testing.T, b *Builder, want string) {
15 t.Helper()
16 got := b.String()
17 if got != want {
18 t.Errorf("String: got %#q; want %#q", got, want)
19 return
20 }
21 if n := b.Len(); n != len(got) {
22 t.Errorf("Len: got %d; but len(String()) is %d", n, len(got))
23 }
24 if n := b.Cap(); n < len(got) {
25 t.Errorf("Cap: got %d; but len(String()) is %d", n, len(got))
26 }
27 }
28
29 func TestBuilder(t *testing.T) {
30 var b Builder
31 check(t, &b, "")
32 n, err := b.WriteString("hello")
33 if err != nil || n != 5 {
34 t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
35 }
36 check(t, &b, "hello")
37 if err = b.WriteByte(' '); err != nil {
38 t.Errorf("WriteByte: %s", err)
39 }
40 check(t, &b, "hello ")
41 n, err = b.WriteString("world")
42 if err != nil || n != 5 {
43 t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
44 }
45 check(t, &b, "hello world")
46 }
47
48 func TestBuilderString(t *testing.T) {
49 var b Builder
50 b.WriteString("alpha")
51 check(t, &b, "alpha")
52 s1 := b.String()
53 b.WriteString("beta")
54 check(t, &b, "alphabeta")
55 s2 := b.String()
56 b.WriteString("gamma")
57 check(t, &b, "alphabetagamma")
58 s3 := b.String()
59
60
61 if want := "alpha"; s1 != want {
62 t.Errorf("first String result is now %q; want %q", s1, want)
63 }
64 if want := "alphabeta"; s2 != want {
65 t.Errorf("second String result is now %q; want %q", s2, want)
66 }
67 if want := "alphabetagamma"; s3 != want {
68 t.Errorf("third String result is now %q; want %q", s3, want)
69 }
70 }
71
72 func TestBuilderReset(t *testing.T) {
73 var b Builder
74 check(t, &b, "")
75 b.WriteString("aaa")
76 s := b.String()
77 check(t, &b, "aaa")
78 b.Reset()
79 check(t, &b, "")
80
81
82
83 b.WriteString("bbb")
84 check(t, &b, "bbb")
85 if want := "aaa"; s != want {
86 t.Errorf("previous String result changed after Reset: got %q; want %q", s, want)
87 }
88 }
89
90 func TestBuilderGrow(t *testing.T) {
91 for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
92 p := bytes.Repeat([]byte{'a'}, growLen)
93 allocs := testing.AllocsPerRun(100, func() {
94 var b Builder
95 b.Grow(growLen)
96 if b.Cap() < growLen {
97 t.Fatalf("growLen=%d: Cap() is lower than growLen", growLen)
98 }
99 b.Write(p)
100 if b.String() != string(p) {
101 t.Fatalf("growLen=%d: bad data written after Grow", growLen)
102 }
103 })
104 wantAllocs := 1
105 if growLen == 0 {
106 wantAllocs = 0
107 }
108 if g, w := int(allocs), wantAllocs; g != w {
109 t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w)
110 }
111 }
112
113 var a Builder
114 n := -1
115 defer func() {
116 if r := recover(); r == nil {
117 t.Errorf("a.Grow(%d) should panic()", n)
118 }
119 }()
120 a.Grow(n)
121 }
122
123 func TestBuilderWrite2(t *testing.T) {
124 const s0 = "hello 世界"
125 for _, tt := range []struct {
126 name string
127 fn func(b *Builder) (int, error)
128 n int
129 want string
130 }{
131 {
132 "Write",
133 func(b *Builder) (int, error) { return b.Write([]byte(s0)) },
134 len(s0),
135 s0,
136 },
137 {
138 "WriteRune",
139 func(b *Builder) (int, error) { return b.WriteRune('a') },
140 1,
141 "a",
142 },
143 {
144 "WriteRuneWide",
145 func(b *Builder) (int, error) { return b.WriteRune('世') },
146 3,
147 "世",
148 },
149 {
150 "WriteString",
151 func(b *Builder) (int, error) { return b.WriteString(s0) },
152 len(s0),
153 s0,
154 },
155 } {
156 t.Run(tt.name, func(t *testing.T) {
157 var b Builder
158 n, err := tt.fn(&b)
159 if err != nil {
160 t.Fatalf("first call: got %s", err)
161 }
162 if n != tt.n {
163 t.Errorf("first call: got n=%d; want %d", n, tt.n)
164 }
165 check(t, &b, tt.want)
166
167 n, err = tt.fn(&b)
168 if err != nil {
169 t.Fatalf("second call: got %s", err)
170 }
171 if n != tt.n {
172 t.Errorf("second call: got n=%d; want %d", n, tt.n)
173 }
174 check(t, &b, tt.want+tt.want)
175 })
176 }
177 }
178
179 func TestBuilderWriteByte(t *testing.T) {
180 var b Builder
181 if err := b.WriteByte('a'); err != nil {
182 t.Error(err)
183 }
184 if err := b.WriteByte(0); err != nil {
185 t.Error(err)
186 }
187 check(t, &b, "a\x00")
188 }
189
190 func TestBuilderAllocs(t *testing.T) {
191
192
193 n := testing.AllocsPerRun(10000, func() {
194 var b Builder
195 b.Grow(5)
196 b.WriteString("abcde")
197 _ = b.String()
198 })
199 if n != 1 {
200 t.Errorf("Builder allocs = %v; want 1", n)
201 }
202 }
203
204 func TestBuilderCopyPanic(t *testing.T) {
205 tests := []struct {
206 name string
207 fn func()
208 wantPanic bool
209 }{
210 {
211 name: "String",
212 wantPanic: false,
213 fn: func() {
214 var a Builder
215 a.WriteByte('x')
216 b := a
217 _ = b.String()
218 },
219 },
220 {
221 name: "Len",
222 wantPanic: false,
223 fn: func() {
224 var a Builder
225 a.WriteByte('x')
226 b := a
227 b.Len()
228 },
229 },
230 {
231 name: "Cap",
232 wantPanic: false,
233 fn: func() {
234 var a Builder
235 a.WriteByte('x')
236 b := a
237 b.Cap()
238 },
239 },
240 {
241 name: "Reset",
242 wantPanic: false,
243 fn: func() {
244 var a Builder
245 a.WriteByte('x')
246 b := a
247 b.Reset()
248 b.WriteByte('y')
249 },
250 },
251 {
252 name: "Write",
253 wantPanic: true,
254 fn: func() {
255 var a Builder
256 a.Write([]byte("x"))
257 b := a
258 b.Write([]byte("y"))
259 },
260 },
261 {
262 name: "WriteByte",
263 wantPanic: true,
264 fn: func() {
265 var a Builder
266 a.WriteByte('x')
267 b := a
268 b.WriteByte('y')
269 },
270 },
271 {
272 name: "WriteString",
273 wantPanic: true,
274 fn: func() {
275 var a Builder
276 a.WriteString("x")
277 b := a
278 b.WriteString("y")
279 },
280 },
281 {
282 name: "WriteRune",
283 wantPanic: true,
284 fn: func() {
285 var a Builder
286 a.WriteRune('x')
287 b := a
288 b.WriteRune('y')
289 },
290 },
291 {
292 name: "Grow",
293 wantPanic: true,
294 fn: func() {
295 var a Builder
296 a.Grow(1)
297 b := a
298 b.Grow(2)
299 },
300 },
301 }
302 for _, tt := range tests {
303 didPanic := make(chan bool)
304 go func() {
305 defer func() { didPanic <- recover() != nil }()
306 tt.fn()
307 }()
308 if got := <-didPanic; got != tt.wantPanic {
309 t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic)
310 }
311 }
312 }
313
314 func TestBuilderWriteInvalidRune(t *testing.T) {
315
316
317 for _, r := range []rune{-1, utf8.MaxRune + 1} {
318 var b Builder
319 b.WriteRune(r)
320 check(t, &b, "\uFFFD")
321 }
322 }
323
324 var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
325
326 var sinkS string
327
328 func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
329 b.Run("1Write_NoGrow", func(b *testing.B) {
330 b.ReportAllocs()
331 f(b, 1, false)
332 })
333 b.Run("3Write_NoGrow", func(b *testing.B) {
334 b.ReportAllocs()
335 f(b, 3, false)
336 })
337 b.Run("3Write_Grow", func(b *testing.B) {
338 b.ReportAllocs()
339 f(b, 3, true)
340 })
341 }
342
343 func BenchmarkBuildString_Builder(b *testing.B) {
344 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
345 for i := 0; i < b.N; i++ {
346 var buf Builder
347 if grow {
348 buf.Grow(len(someBytes) * numWrite)
349 }
350 for i := 0; i < numWrite; i++ {
351 buf.Write(someBytes)
352 }
353 sinkS = buf.String()
354 }
355 })
356 }
357
358 func BenchmarkBuildString_WriteString(b *testing.B) {
359 someString := string(someBytes)
360 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
361 for i := 0; i < b.N; i++ {
362 var buf Builder
363 if grow {
364 buf.Grow(len(someString) * numWrite)
365 }
366 for i := 0; i < numWrite; i++ {
367 buf.WriteString(someString)
368 }
369 sinkS = buf.String()
370 }
371 })
372 }
373
374 func BenchmarkBuildString_ByteBuffer(b *testing.B) {
375 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
376 for i := 0; i < b.N; i++ {
377 var buf bytes.Buffer
378 if grow {
379 buf.Grow(len(someBytes) * numWrite)
380 }
381 for i := 0; i < numWrite; i++ {
382 buf.Write(someBytes)
383 }
384 sinkS = buf.String()
385 }
386 })
387 }
388
389 func TestBuilderGrowSizeclasses(t *testing.T) {
390 s := Repeat("a", 19)
391 allocs := testing.AllocsPerRun(100, func() {
392 var b Builder
393 b.Grow(18)
394 b.WriteString(s)
395 _ = b.String()
396 })
397 if allocs > 1 {
398 t.Fatalf("unexpected amount of allocations: %v, want: 1", allocs)
399 }
400 }
401
View as plain text