github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/strings/builder_test.gno (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 "strings" 10 "testing" 11 "unicode/utf8" 12 ) 13 14 func check(t *testing.T, b *strings.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 strings.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 strings.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 strings.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 /* XXX disabled because testing.AllocsPerRun2 is broken 91 func TestBuilderGrow(t *testing.T) { 92 for _, growLen := range []int{0, 100, 1000, 10000, 100000} { 93 p := bytes.Repeat([]byte{'a'}, growLen) 94 allocs := testing.AllocsPerRun2(100, func() { 95 var b strings.Builder 96 b.Grow(growLen) // should be only alloc, when growLen > 0 97 if b.Cap() < growLen { 98 t.Fatalf("growLen=%d: Cap() is lower than growLen", growLen) 99 } 100 b.Write(p) 101 if b.String() != string(p) { 102 t.Fatalf("growLen=%d: bad data written after Grow", growLen) 103 } 104 }) 105 wantAllocs := 1 106 if growLen == 0 { 107 wantAllocs = 0 108 } 109 if g, w := int(allocs), wantAllocs; g != w { 110 t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w) 111 } 112 } 113 } 114 */ 115 116 /* XXX test.Run not yet implemented 117 func TestBuilderWrite2(t *testing.T) { 118 const s0 = "hello 世界" 119 for _, tt := range []struct { 120 name string 121 fn func(b *strings.Builder) (int, error) 122 n int 123 want string 124 }{ 125 { 126 "Write", 127 func(b *strings.Builder) (int, error) { return b.Write([]byte(s0)) }, 128 len(s0), 129 s0, 130 }, 131 { 132 "WriteRune", 133 func(b *strings.Builder) (int, error) { return b.WriteRune('a') }, 134 1, 135 "a", 136 }, 137 { 138 "WriteRuneWide", 139 func(b *strings.Builder) (int, error) { return b.WriteRune('世') }, 140 3, 141 "世", 142 }, 143 { 144 "WriteString", 145 func(b *strings.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 strings.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 174 func TestBuilderWriteByte(t *testing.T) { 175 var b strings.Builder 176 if err := b.WriteByte('a'); err != nil { 177 t.Error(err) 178 } 179 if err := b.WriteByte(0); err != nil { 180 t.Error(err) 181 } 182 check(t, &b, "a\x00") 183 } 184 185 /* testing.AllocsPerRun broken 186 func TestBuilderAllocs(t *testing.T) { 187 // Issue 23382; verify that copyCheck doesn't force the 188 // Builder to escape and be heap allocated. 189 n := testing.AllocsPerRun2(10000, func() { 190 var b strings.Builder 191 b.Grow(5) 192 b.WriteString("abcde") 193 _ = b.String() 194 }) 195 if n != 1 { 196 t.Errorf("Builder allocs = %v; want 1", n) 197 } 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 strings.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 strings.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 strings.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 strings.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 strings.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 strings.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 strings.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 strings.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 strings.Builder 293 a.Grow(1) 294 b := a 295 b.Grow(2) 296 }, 297 }, 298 } 299 for _, tt := range tests { 300 // XXX remove goroutine usage 301 // didPanic := make(chan bool) 302 var didPanic bool = false 303 // go func() { 304 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 // Invalid runes, including negative ones, should be written as 316 // utf8.RuneError. 317 for _, r := range []rune{-1, utf8.MaxRune + 1} { 318 var b strings.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 strings.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_ByteBuffer(b *testing.B) { 359 benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) { 360 for i := 0; i < b.N; i++ { 361 var buf bytes.Buffer 362 if grow { 363 buf.Grow(len(someBytes) * numWrite) 364 } 365 for i := 0; i < numWrite; i++ { 366 buf.Write(someBytes) 367 } 368 sinkS = buf.String() 369 } 370 }) 371 }