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
114 func TestBuilderWrite2(t *testing.T) {
115 const s0 = "hello 世界"
116 for _, tt := range []struct {
117 name string
118 fn func(b *Builder) (int, error)
119 n int
120 want string
121 }{
122 {
123 "Write",
124 func(b *Builder) (int, error) { return b.Write([]byte(s0)) },
125 len(s0),
126 s0,
127 },
128 {
129 "WriteRune",
130 func(b *Builder) (int, error) { return b.WriteRune('a') },
131 1,
132 "a",
133 },
134 {
135 "WriteRuneWide",
136 func(b *Builder) (int, error) { return b.WriteRune('世') },
137 3,
138 "世",
139 },
140 {
141 "WriteString",
142 func(b *Builder) (int, error) { return b.WriteString(s0) },
143 len(s0),
144 s0,
145 },
146 } {
147 t.Run(tt.name, func(t *testing.T) {
148 var b Builder
149 n, err := tt.fn(&b)
150 if err != nil {
151 t.Fatalf("first call: got %s", err)
152 }
153 if n != tt.n {
154 t.Errorf("first call: got n=%d; want %d", n, tt.n)
155 }
156 check(t, &b, tt.want)
157
158 n, err = tt.fn(&b)
159 if err != nil {
160 t.Fatalf("second call: got %s", err)
161 }
162 if n != tt.n {
163 t.Errorf("second call: got n=%d; want %d", n, tt.n)
164 }
165 check(t, &b, tt.want+tt.want)
166 })
167 }
168 }
169
170 func TestBuilderWriteByte(t *testing.T) {
171 var b Builder
172 if err := b.WriteByte('a'); err != nil {
173 t.Error(err)
174 }
175 if err := b.WriteByte(0); err != nil {
176 t.Error(err)
177 }
178 check(t, &b, "a\x00")
179 }
180
181 func TestBuilderAllocs(t *testing.T) {
182
183
184 n := testing.AllocsPerRun(10000, func() {
185 var b Builder
186 b.Grow(5)
187 b.WriteString("abcde")
188 _ = b.String()
189 })
190 if n != 1 {
191 t.Errorf("Builder allocs = %v; want 1", n)
192 }
193 }
194
195 func TestBuilderCopyPanic(t *testing.T) {
196 tests := []struct {
197 name string
198 fn func()
199 wantPanic bool
200 }{
201 {
202 name: "String",
203 wantPanic: false,
204 fn: func() {
205 var a Builder
206 a.WriteByte('x')
207 b := a
208 _ = b.String()
209 },
210 },
211 {
212 name: "Len",
213 wantPanic: false,
214 fn: func() {
215 var a Builder
216 a.WriteByte('x')
217 b := a
218 b.Len()
219 },
220 },
221 {
222 name: "Cap",
223 wantPanic: false,
224 fn: func() {
225 var a Builder
226 a.WriteByte('x')
227 b := a
228 b.Cap()
229 },
230 },
231 {
232 name: "Reset",
233 wantPanic: false,
234 fn: func() {
235 var a Builder
236 a.WriteByte('x')
237 b := a
238 b.Reset()
239 b.WriteByte('y')
240 },
241 },
242 {
243 name: "Write",
244 wantPanic: true,
245 fn: func() {
246 var a Builder
247 a.Write([]byte("x"))
248 b := a
249 b.Write([]byte("y"))
250 },
251 },
252 {
253 name: "WriteByte",
254 wantPanic: true,
255 fn: func() {
256 var a Builder
257 a.WriteByte('x')
258 b := a
259 b.WriteByte('y')
260 },
261 },
262 {
263 name: "WriteString",
264 wantPanic: true,
265 fn: func() {
266 var a Builder
267 a.WriteString("x")
268 b := a
269 b.WriteString("y")
270 },
271 },
272 {
273 name: "WriteRune",
274 wantPanic: true,
275 fn: func() {
276 var a Builder
277 a.WriteRune('x')
278 b := a
279 b.WriteRune('y')
280 },
281 },
282 {
283 name: "Grow",
284 wantPanic: true,
285 fn: func() {
286 var a Builder
287 a.Grow(1)
288 b := a
289 b.Grow(2)
290 },
291 },
292 }
293 for _, tt := range tests {
294 didPanic := make(chan bool)
295 go func() {
296 defer func() { didPanic <- recover() != nil }()
297 tt.fn()
298 }()
299 if got := <-didPanic; got != tt.wantPanic {
300 t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic)
301 }
302 }
303 }
304
305 func TestBuilderWriteInvalidRune(t *testing.T) {
306
307
308 for _, r := range []rune{-1, utf8.MaxRune + 1} {
309 var b Builder
310 b.WriteRune(r)
311 check(t, &b, "\uFFFD")
312 }
313 }
314
315 var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
316
317 var sinkS string
318
319 func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
320 b.Run("1Write_NoGrow", func(b *testing.B) {
321 b.ReportAllocs()
322 f(b, 1, false)
323 })
324 b.Run("3Write_NoGrow", func(b *testing.B) {
325 b.ReportAllocs()
326 f(b, 3, false)
327 })
328 b.Run("3Write_Grow", func(b *testing.B) {
329 b.ReportAllocs()
330 f(b, 3, true)
331 })
332 }
333
334 func BenchmarkBuildString_Builder(b *testing.B) {
335 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
336 for i := 0; i < b.N; i++ {
337 var buf Builder
338 if grow {
339 buf.Grow(len(someBytes) * numWrite)
340 }
341 for i := 0; i < numWrite; i++ {
342 buf.Write(someBytes)
343 }
344 sinkS = buf.String()
345 }
346 })
347 }
348
349 func BenchmarkBuildString_ByteBuffer(b *testing.B) {
350 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
351 for i := 0; i < b.N; i++ {
352 var buf bytes.Buffer
353 if grow {
354 buf.Grow(len(someBytes) * numWrite)
355 }
356 for i := 0; i < numWrite; i++ {
357 buf.Write(someBytes)
358 }
359 sinkS = buf.String()
360 }
361 })
362 }
363
View as plain text