github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/strings/builder_test.go (about) 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 } 25 26 func TestBuilder(t *testing.T) { 27 var b Builder 28 check(t, &b, "") 29 n, err := b.WriteString("hello") 30 if err != nil || n != 5 { 31 t.Errorf("WriteString: got %d,%s; want 5,nil", n, err) 32 } 33 check(t, &b, "hello") 34 if err = b.WriteByte(' '); err != nil { 35 t.Errorf("WriteByte: %s", err) 36 } 37 check(t, &b, "hello ") 38 n, err = b.WriteString("world") 39 if err != nil || n != 5 { 40 t.Errorf("WriteString: got %d,%s; want 5,nil", n, err) 41 } 42 check(t, &b, "hello world") 43 } 44 45 func TestBuilderString(t *testing.T) { 46 var b Builder 47 b.WriteString("alpha") 48 check(t, &b, "alpha") 49 s1 := b.String() 50 b.WriteString("beta") 51 check(t, &b, "alphabeta") 52 s2 := b.String() 53 b.WriteString("gamma") 54 check(t, &b, "alphabetagamma") 55 s3 := b.String() 56 57 // Check that subsequent operations didn't change the returned strings. 58 if want := "alpha"; s1 != want { 59 t.Errorf("first String result is now %q; want %q", s1, want) 60 } 61 if want := "alphabeta"; s2 != want { 62 t.Errorf("second String result is now %q; want %q", s2, want) 63 } 64 if want := "alphabetagamma"; s3 != want { 65 t.Errorf("third String result is now %q; want %q", s3, want) 66 } 67 } 68 69 func TestBuilderReset(t *testing.T) { 70 var b Builder 71 check(t, &b, "") 72 b.WriteString("aaa") 73 s := b.String() 74 check(t, &b, "aaa") 75 b.Reset() 76 check(t, &b, "") 77 78 // Ensure that writing after Reset doesn't alter 79 // previously returned strings. 80 b.WriteString("bbb") 81 check(t, &b, "bbb") 82 if want := "aaa"; s != want { 83 t.Errorf("previous String result changed after Reset: got %q; want %q", s, want) 84 } 85 } 86 87 func TestBuilderGrow(t *testing.T) { 88 for _, growLen := range []int{0, 100, 1000, 10000, 100000} { 89 var b Builder 90 b.Grow(growLen) 91 p := bytes.Repeat([]byte{'a'}, growLen) 92 allocs := numAllocs(func() { b.Write(p) }) 93 if allocs > 0 { 94 t.Errorf("growLen=%d: allocation occurred during write", growLen) 95 } 96 if b.String() != string(p) { 97 t.Errorf("growLen=%d: bad data written after Grow", growLen) 98 } 99 } 100 } 101 102 func TestBuilderWrite2(t *testing.T) { 103 const s0 = "hello 世界" 104 for _, tt := range []struct { 105 name string 106 fn func(b *Builder) (int, error) 107 n int 108 want string 109 }{ 110 { 111 "Write", 112 func(b *Builder) (int, error) { return b.Write([]byte(s0)) }, 113 len(s0), 114 s0, 115 }, 116 { 117 "WriteRune", 118 func(b *Builder) (int, error) { return b.WriteRune('a') }, 119 1, 120 "a", 121 }, 122 { 123 "WriteRuneWide", 124 func(b *Builder) (int, error) { return b.WriteRune('世') }, 125 3, 126 "世", 127 }, 128 { 129 "WriteString", 130 func(b *Builder) (int, error) { return b.WriteString(s0) }, 131 len(s0), 132 s0, 133 }, 134 } { 135 t.Run(tt.name, func(t *testing.T) { 136 var b Builder 137 n, err := tt.fn(&b) 138 if err != nil { 139 t.Fatalf("first call: got %s", err) 140 } 141 if n != tt.n { 142 t.Errorf("first call: got n=%d; want %d", n, tt.n) 143 } 144 check(t, &b, tt.want) 145 146 n, err = tt.fn(&b) 147 if err != nil { 148 t.Fatalf("second call: got %s", err) 149 } 150 if n != tt.n { 151 t.Errorf("second call: got n=%d; want %d", n, tt.n) 152 } 153 check(t, &b, tt.want+tt.want) 154 }) 155 } 156 } 157 158 func TestBuilderWriteByte(t *testing.T) { 159 var b Builder 160 if err := b.WriteByte('a'); err != nil { 161 t.Error(err) 162 } 163 if err := b.WriteByte(0); err != nil { 164 t.Error(err) 165 } 166 check(t, &b, "a\x00") 167 } 168 169 func TestBuilderAllocs(t *testing.T) { 170 var b Builder 171 b.Grow(5) 172 var s string 173 allocs := numAllocs(func() { 174 b.WriteString("hello") 175 s = b.String() 176 }) 177 if want := "hello"; s != want { 178 t.Errorf("String: got %#q; want %#q", s, want) 179 } 180 if allocs > 0 { 181 t.Fatalf("got %d alloc(s); want 0", allocs) 182 } 183 184 // Issue 23382; verify that copyCheck doesn't force the 185 // Builder to escape and be heap allocated. 186 n := testing.AllocsPerRun(10000, func() { 187 var b Builder 188 b.Grow(5) 189 b.WriteString("abcde") 190 _ = b.String() 191 }) 192 if n != 1 { 193 t.Errorf("Builder allocs = %v; want 1", n) 194 } 195 } 196 197 func numAllocs(fn func()) uint64 { 198 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 199 var m1, m2 runtime.MemStats 200 runtime.ReadMemStats(&m1) 201 fn() 202 runtime.ReadMemStats(&m2) 203 return m2.Mallocs - m1.Mallocs 204 } 205 206 func TestBuilderCopyPanic(t *testing.T) { 207 tests := []struct { 208 name string 209 fn func() 210 wantPanic bool 211 }{ 212 { 213 name: "String", 214 wantPanic: false, 215 fn: func() { 216 var a Builder 217 a.WriteByte('x') 218 b := a 219 _ = b.String() // appease vet 220 }, 221 }, 222 { 223 name: "Len", 224 wantPanic: false, 225 fn: func() { 226 var a Builder 227 a.WriteByte('x') 228 b := a 229 b.Len() 230 }, 231 }, 232 { 233 name: "Reset", 234 wantPanic: false, 235 fn: func() { 236 var a Builder 237 a.WriteByte('x') 238 b := a 239 b.Reset() 240 b.WriteByte('y') 241 }, 242 }, 243 { 244 name: "Write", 245 wantPanic: true, 246 fn: func() { 247 var a Builder 248 a.Write([]byte("x")) 249 b := a 250 b.Write([]byte("y")) 251 }, 252 }, 253 { 254 name: "WriteByte", 255 wantPanic: true, 256 fn: func() { 257 var a Builder 258 a.WriteByte('x') 259 b := a 260 b.WriteByte('y') 261 }, 262 }, 263 { 264 name: "WriteString", 265 wantPanic: true, 266 fn: func() { 267 var a Builder 268 a.WriteString("x") 269 b := a 270 b.WriteString("y") 271 }, 272 }, 273 { 274 name: "WriteRune", 275 wantPanic: true, 276 fn: func() { 277 var a Builder 278 a.WriteRune('x') 279 b := a 280 b.WriteRune('y') 281 }, 282 }, 283 { 284 name: "Grow", 285 wantPanic: true, 286 fn: func() { 287 var a Builder 288 a.Grow(1) 289 b := a 290 b.Grow(2) 291 }, 292 }, 293 } 294 for _, tt := range tests { 295 didPanic := make(chan bool) 296 go func() { 297 defer func() { didPanic <- recover() != nil }() 298 tt.fn() 299 }() 300 if got := <-didPanic; got != tt.wantPanic { 301 t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic) 302 } 303 } 304 }