github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/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 "errors" 10 "io" 11 "runtime" 12 . "strings" 13 "testing" 14 "testing/iotest" 15 ) 16 17 func check(t *testing.T, b *Builder, want string) { 18 t.Helper() 19 got := b.String() 20 if got != want { 21 t.Errorf("String: got %#q; want %#q", got, want) 22 return 23 } 24 if n := b.Len(); n != len(got) { 25 t.Errorf("Len: 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 for _, growLen := range []int{0, 100, 1000, 10000, 100000} { 92 var b Builder 93 b.Grow(growLen) 94 p := bytes.Repeat([]byte{'a'}, growLen) 95 allocs := numAllocs(func() { b.Write(p) }) 96 if allocs > 0 { 97 t.Errorf("growLen=%d: allocation occurred during write", growLen) 98 } 99 if b.String() != string(p) { 100 t.Errorf("growLen=%d: bad data written after Grow", growLen) 101 } 102 } 103 } 104 105 func TestBuilderWrite2(t *testing.T) { 106 const s0 = "hello 世界" 107 for _, tt := range []struct { 108 name string 109 fn func(b *Builder) (int, error) 110 n int 111 want string 112 }{ 113 { 114 "Write", 115 func(b *Builder) (int, error) { return b.Write([]byte(s0)) }, 116 len(s0), 117 s0, 118 }, 119 { 120 "WriteRune", 121 func(b *Builder) (int, error) { return b.WriteRune('a') }, 122 1, 123 "a", 124 }, 125 { 126 "WriteRuneWide", 127 func(b *Builder) (int, error) { return b.WriteRune('世') }, 128 3, 129 "世", 130 }, 131 { 132 "WriteString", 133 func(b *Builder) (int, error) { return b.WriteString(s0) }, 134 len(s0), 135 s0, 136 }, 137 } { 138 t.Run(tt.name, func(t *testing.T) { 139 var b Builder 140 n, err := tt.fn(&b) 141 if err != nil { 142 t.Fatalf("first call: got %s", err) 143 } 144 if n != tt.n { 145 t.Errorf("first call: got n=%d; want %d", n, tt.n) 146 } 147 check(t, &b, tt.want) 148 149 n, err = tt.fn(&b) 150 if err != nil { 151 t.Fatalf("second call: got %s", err) 152 } 153 if n != tt.n { 154 t.Errorf("second call: got n=%d; want %d", n, tt.n) 155 } 156 check(t, &b, tt.want+tt.want) 157 }) 158 } 159 } 160 161 func TestBuilderWriteByte(t *testing.T) { 162 var b Builder 163 if err := b.WriteByte('a'); err != nil { 164 t.Error(err) 165 } 166 if err := b.WriteByte(0); err != nil { 167 t.Error(err) 168 } 169 check(t, &b, "a\x00") 170 } 171 172 func TestBuilderReadFrom(t *testing.T) { 173 for _, tt := range []struct { 174 name string 175 fn func(io.Reader) io.Reader 176 }{ 177 {"Reader", func(r io.Reader) io.Reader { return r }}, 178 {"DataErrReader", iotest.DataErrReader}, 179 {"OneByteReader", iotest.OneByteReader}, 180 } { 181 t.Run(tt.name, func(t *testing.T) { 182 var b Builder 183 184 r := tt.fn(NewReader("hello")) 185 n, err := b.ReadFrom(r) 186 if err != nil { 187 t.Fatalf("first call: got %s", err) 188 } 189 if n != 5 { 190 t.Errorf("first call: got n=%d; want 5", n) 191 } 192 check(t, &b, "hello") 193 194 r = tt.fn(NewReader(" world")) 195 n, err = b.ReadFrom(r) 196 if err != nil { 197 t.Fatalf("first call: got %s", err) 198 } 199 if n != 6 { 200 t.Errorf("first call: got n=%d; want 6", n) 201 } 202 check(t, &b, "hello world") 203 }) 204 } 205 } 206 207 var errRead = errors.New("boom") 208 209 // errorReader sends reads to the underlying reader 210 // but returns errRead instead of io.EOF. 211 type errorReader struct { 212 r io.Reader 213 } 214 215 func (r errorReader) Read(b []byte) (int, error) { 216 n, err := r.r.Read(b) 217 if err == io.EOF { 218 err = errRead 219 } 220 return n, err 221 } 222 223 func TestBuilderReadFromError(t *testing.T) { 224 var b Builder 225 r := errorReader{NewReader("hello")} 226 n, err := b.ReadFrom(r) 227 if n != 5 { 228 t.Errorf("got n=%d; want 5", n) 229 } 230 if err != errRead { 231 t.Errorf("got err=%q; want %q", err, errRead) 232 } 233 check(t, &b, "hello") 234 } 235 236 type negativeReader struct{} 237 238 func (r negativeReader) Read([]byte) (int, error) { return -1, nil } 239 240 func TestBuilderReadFromNegativeReader(t *testing.T) { 241 var b Builder 242 defer func() { 243 switch err := recover().(type) { 244 case nil: 245 t.Fatal("ReadFrom didn't panic") 246 case error: 247 wantErr := "strings.Builder: reader returned negative count from Read" 248 if err.Error() != wantErr { 249 t.Fatalf("recovered panic: got %v; want %v", err.Error(), wantErr) 250 } 251 default: 252 t.Fatalf("unexpected panic value: %#v", err) 253 } 254 }() 255 256 b.ReadFrom(negativeReader{}) 257 } 258 259 func TestBuilderAllocs(t *testing.T) { 260 var b Builder 261 b.Grow(5) 262 var s string 263 allocs := numAllocs(func() { 264 b.WriteString("hello") 265 s = b.String() 266 }) 267 if want := "hello"; s != want { 268 t.Errorf("String: got %#q; want %#q", s, want) 269 } 270 if allocs > 0 { 271 t.Fatalf("got %d alloc(s); want 0", allocs) 272 } 273 } 274 275 func numAllocs(fn func()) uint64 { 276 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 277 var m1, m2 runtime.MemStats 278 runtime.ReadMemStats(&m1) 279 fn() 280 runtime.ReadMemStats(&m2) 281 return m2.Mallocs - m1.Mallocs 282 }