...

Source file src/strings/builder_test.go

Documentation: strings

		 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 *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  	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) // should be only alloc, when growLen > 0
		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  	// Issue 23382; verify that copyCheck doesn't force the
	 183  	// Builder to escape and be heap allocated.
	 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() // appease vet
	 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  	// Invalid runes, including negative ones, should be written as
	 307  	// utf8.RuneError.
	 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