github.com/StingNevermore/go@v0.0.0-20180120041312-3810f5bfed72/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  	"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  }