]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/strings/builder_test.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / strings / builder_test.go
1 // Copyright 2017 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.
4
5 package strings_test
6
7 import (
8 "bytes"
9 "runtime"
10 . "strings"
11 "testing"
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 // Check that subsequent operations didn't change the returned strings.
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 // Ensure that writing after Reset doesn't alter
82 // previously returned strings.
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 if runtime.Compiler == "gccgo" {
92 t.Skip("skip for gccgo until escape analysis improves")
93 }
94 for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
95 p := bytes.Repeat([]byte{'a'}, growLen)
96 allocs := testing.AllocsPerRun(100, func() {
97 var b Builder
98 b.Grow(growLen) // should be only alloc, when growLen > 0
99 if b.Cap() < growLen {
100 t.Fatalf("growLen=%d: Cap() is lower than growLen", growLen)
101 }
102 b.Write(p)
103 if b.String() != string(p) {
104 t.Fatalf("growLen=%d: bad data written after Grow", growLen)
105 }
106 })
107 wantAllocs := 1
108 if growLen == 0 {
109 wantAllocs = 0
110 }
111 if g, w := int(allocs), wantAllocs; g != w {
112 t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w)
113 }
114 }
115 }
116
117 func TestBuilderWrite2(t *testing.T) {
118 const s0 = "hello 世界"
119 for _, tt := range []struct {
120 name string
121 fn func(b *Builder) (int, error)
122 n int
123 want string
124 }{
125 {
126 "Write",
127 func(b *Builder) (int, error) { return b.Write([]byte(s0)) },
128 len(s0),
129 s0,
130 },
131 {
132 "WriteRune",
133 func(b *Builder) (int, error) { return b.WriteRune('a') },
134 1,
135 "a",
136 },
137 {
138 "WriteRuneWide",
139 func(b *Builder) (int, error) { return b.WriteRune('世') },
140 3,
141 "世",
142 },
143 {
144 "WriteString",
145 func(b *Builder) (int, error) { return b.WriteString(s0) },
146 len(s0),
147 s0,
148 },
149 } {
150 t.Run(tt.name, func(t *testing.T) {
151 var b Builder
152 n, err := tt.fn(&b)
153 if err != nil {
154 t.Fatalf("first call: got %s", err)
155 }
156 if n != tt.n {
157 t.Errorf("first call: got n=%d; want %d", n, tt.n)
158 }
159 check(t, &b, tt.want)
160
161 n, err = tt.fn(&b)
162 if err != nil {
163 t.Fatalf("second call: got %s", err)
164 }
165 if n != tt.n {
166 t.Errorf("second call: got n=%d; want %d", n, tt.n)
167 }
168 check(t, &b, tt.want+tt.want)
169 })
170 }
171 }
172
173 func TestBuilderWriteByte(t *testing.T) {
174 var b Builder
175 if err := b.WriteByte('a'); err != nil {
176 t.Error(err)
177 }
178 if err := b.WriteByte(0); err != nil {
179 t.Error(err)
180 }
181 check(t, &b, "a\x00")
182 }
183
184 func TestBuilderAllocs(t *testing.T) {
185 if runtime.Compiler == "gccgo" {
186 t.Skip("skip for gccgo until escape analysis improves")
187 }
188 // Issue 23382; verify that copyCheck doesn't force the
189 // Builder to escape and be heap allocated.
190 n := testing.AllocsPerRun(10000, func() {
191 var b Builder
192 b.Grow(5)
193 b.WriteString("abcde")
194 _ = b.String()
195 })
196 if n != 1 {
197 t.Errorf("Builder allocs = %v; want 1", n)
198 }
199 }
200
201 func TestBuilderCopyPanic(t *testing.T) {
202 tests := []struct {
203 name string
204 fn func()
205 wantPanic bool
206 }{
207 {
208 name: "String",
209 wantPanic: false,
210 fn: func() {
211 var a Builder
212 a.WriteByte('x')
213 b := a
214 _ = b.String() // appease vet
215 },
216 },
217 {
218 name: "Len",
219 wantPanic: false,
220 fn: func() {
221 var a Builder
222 a.WriteByte('x')
223 b := a
224 b.Len()
225 },
226 },
227 {
228 name: "Cap",
229 wantPanic: false,
230 fn: func() {
231 var a Builder
232 a.WriteByte('x')
233 b := a
234 b.Cap()
235 },
236 },
237 {
238 name: "Reset",
239 wantPanic: false,
240 fn: func() {
241 var a Builder
242 a.WriteByte('x')
243 b := a
244 b.Reset()
245 b.WriteByte('y')
246 },
247 },
248 {
249 name: "Write",
250 wantPanic: true,
251 fn: func() {
252 var a Builder
253 a.Write([]byte("x"))
254 b := a
255 b.Write([]byte("y"))
256 },
257 },
258 {
259 name: "WriteByte",
260 wantPanic: true,
261 fn: func() {
262 var a Builder
263 a.WriteByte('x')
264 b := a
265 b.WriteByte('y')
266 },
267 },
268 {
269 name: "WriteString",
270 wantPanic: true,
271 fn: func() {
272 var a Builder
273 a.WriteString("x")
274 b := a
275 b.WriteString("y")
276 },
277 },
278 {
279 name: "WriteRune",
280 wantPanic: true,
281 fn: func() {
282 var a Builder
283 a.WriteRune('x')
284 b := a
285 b.WriteRune('y')
286 },
287 },
288 {
289 name: "Grow",
290 wantPanic: true,
291 fn: func() {
292 var a Builder
293 a.Grow(1)
294 b := a
295 b.Grow(2)
296 },
297 },
298 }
299 for _, tt := range tests {
300 didPanic := make(chan bool)
301 go func() {
302 defer func() { didPanic <- recover() != nil }()
303 tt.fn()
304 }()
305 if got := <-didPanic; got != tt.wantPanic {
306 t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic)
307 }
308 }
309 }
310
311 var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
312
313 var sinkS string
314
315 func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
316 b.Run("1Write_NoGrow", func(b *testing.B) {
317 b.ReportAllocs()
318 f(b, 1, false)
319 })
320 b.Run("3Write_NoGrow", func(b *testing.B) {
321 b.ReportAllocs()
322 f(b, 3, false)
323 })
324 b.Run("3Write_Grow", func(b *testing.B) {
325 b.ReportAllocs()
326 f(b, 3, true)
327 })
328 }
329
330 func BenchmarkBuildString_Builder(b *testing.B) {
331 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
332 for i := 0; i < b.N; i++ {
333 var buf Builder
334 if grow {
335 buf.Grow(len(someBytes) * numWrite)
336 }
337 for i := 0; i < numWrite; i++ {
338 buf.Write(someBytes)
339 }
340 sinkS = buf.String()
341 }
342 })
343 }
344
345 func BenchmarkBuildString_ByteBuffer(b *testing.B) {
346 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
347 for i := 0; i < b.N; i++ {
348 var buf bytes.Buffer
349 if grow {
350 buf.Grow(len(someBytes) * numWrite)
351 }
352 for i := 0; i < numWrite; i++ {
353 buf.Write(someBytes)
354 }
355 sinkS = buf.String()
356 }
357 })
358 }