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  }