github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/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  	. "strings"
    10  	"testing"
    11  )
    12  
    13  func check(t *testing.T, b *Builder, want string) {
    14  	t.Helper()
    15  	got := b.String()
    16  	if got != want {
    17  		t.Errorf("String: got %#q; want %#q", got, want)
    18  		return
    19  	}
    20  	if n := b.Len(); n != len(got) {
    21  		t.Errorf("Len: got %d; but len(String()) is %d", n, len(got))
    22  	}
    23  	if n := b.Cap(); n < len(got) {
    24  		t.Errorf("Cap: got %d; but len(String()) is %d", n, len(got))
    25  	}
    26  }
    27  
    28  func TestBuilder(t *testing.T) {
    29  	var b Builder
    30  	check(t, &b, "")
    31  	n, err := b.WriteString("hello")
    32  	if err != nil || n != 5 {
    33  		t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
    34  	}
    35  	check(t, &b, "hello")
    36  	if err = b.WriteByte(' '); err != nil {
    37  		t.Errorf("WriteByte: %s", err)
    38  	}
    39  	check(t, &b, "hello ")
    40  	n, err = b.WriteString("world")
    41  	if err != nil || n != 5 {
    42  		t.Errorf("WriteString: got %d,%s; want 5,nil", n, err)
    43  	}
    44  	check(t, &b, "hello world")
    45  }
    46  
    47  func TestBuilderString(t *testing.T) {
    48  	var b Builder
    49  	b.WriteString("alpha")
    50  	check(t, &b, "alpha")
    51  	s1 := b.String()
    52  	b.WriteString("beta")
    53  	check(t, &b, "alphabeta")
    54  	s2 := b.String()
    55  	b.WriteString("gamma")
    56  	check(t, &b, "alphabetagamma")
    57  	s3 := b.String()
    58  
    59  	// Check that subsequent operations didn't change the returned strings.
    60  	if want := "alpha"; s1 != want {
    61  		t.Errorf("first String result is now %q; want %q", s1, want)
    62  	}
    63  	if want := "alphabeta"; s2 != want {
    64  		t.Errorf("second String result is now %q; want %q", s2, want)
    65  	}
    66  	if want := "alphabetagamma"; s3 != want {
    67  		t.Errorf("third String result is now %q; want %q", s3, want)
    68  	}
    69  }
    70  
    71  func TestBuilderReset(t *testing.T) {
    72  	var b Builder
    73  	check(t, &b, "")
    74  	b.WriteString("aaa")
    75  	s := b.String()
    76  	check(t, &b, "aaa")
    77  	b.Reset()
    78  	check(t, &b, "")
    79  
    80  	// Ensure that writing after Reset doesn't alter
    81  	// previously returned strings.
    82  	b.WriteString("bbb")
    83  	check(t, &b, "bbb")
    84  	if want := "aaa"; s != want {
    85  		t.Errorf("previous String result changed after Reset: got %q; want %q", s, want)
    86  	}
    87  }
    88  
    89  func TestBuilderGrow(t *testing.T) {
    90  	for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
    91  		p := bytes.Repeat([]byte{'a'}, growLen)
    92  		allocs := testing.AllocsPerRun(100, func() {
    93  			var b Builder
    94  			b.Grow(growLen) // should be only alloc, when growLen > 0
    95  			if b.Cap() < growLen {
    96  				t.Fatalf("growLen=%d: Cap() is lower than growLen", growLen)
    97  			}
    98  			b.Write(p)
    99  			if b.String() != string(p) {
   100  				t.Fatalf("growLen=%d: bad data written after Grow", growLen)
   101  			}
   102  		})
   103  		wantAllocs := 1
   104  		if growLen == 0 {
   105  			wantAllocs = 0
   106  		}
   107  		if g, w := int(allocs), wantAllocs; g != w {
   108  			t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w)
   109  		}
   110  	}
   111  }
   112  
   113  func TestBuilderWrite2(t *testing.T) {
   114  	const s0 = "hello 世界"
   115  	for _, tt := range []struct {
   116  		name string
   117  		fn   func(b *Builder) (int, error)
   118  		n    int
   119  		want string
   120  	}{
   121  		{
   122  			"Write",
   123  			func(b *Builder) (int, error) { return b.Write([]byte(s0)) },
   124  			len(s0),
   125  			s0,
   126  		},
   127  		{
   128  			"WriteRune",
   129  			func(b *Builder) (int, error) { return b.WriteRune('a') },
   130  			1,
   131  			"a",
   132  		},
   133  		{
   134  			"WriteRuneWide",
   135  			func(b *Builder) (int, error) { return b.WriteRune('世') },
   136  			3,
   137  			"世",
   138  		},
   139  		{
   140  			"WriteString",
   141  			func(b *Builder) (int, error) { return b.WriteString(s0) },
   142  			len(s0),
   143  			s0,
   144  		},
   145  	} {
   146  		t.Run(tt.name, func(t *testing.T) {
   147  			var b Builder
   148  			n, err := tt.fn(&b)
   149  			if err != nil {
   150  				t.Fatalf("first call: got %s", err)
   151  			}
   152  			if n != tt.n {
   153  				t.Errorf("first call: got n=%d; want %d", n, tt.n)
   154  			}
   155  			check(t, &b, tt.want)
   156  
   157  			n, err = tt.fn(&b)
   158  			if err != nil {
   159  				t.Fatalf("second call: got %s", err)
   160  			}
   161  			if n != tt.n {
   162  				t.Errorf("second call: got n=%d; want %d", n, tt.n)
   163  			}
   164  			check(t, &b, tt.want+tt.want)
   165  		})
   166  	}
   167  }
   168  
   169  func TestBuilderWriteByte(t *testing.T) {
   170  	var b Builder
   171  	if err := b.WriteByte('a'); err != nil {
   172  		t.Error(err)
   173  	}
   174  	if err := b.WriteByte(0); err != nil {
   175  		t.Error(err)
   176  	}
   177  	check(t, &b, "a\x00")
   178  }
   179  
   180  func TestBuilderAllocs(t *testing.T) {
   181  	var b Builder
   182  	const msg = "hello"
   183  	b.Grow(len(msg) * 2) // because AllocsPerRun does an extra "warm-up" iteration
   184  	var s string
   185  	allocs := int(testing.AllocsPerRun(1, func() {
   186  		b.WriteString("hello")
   187  		s = b.String()
   188  	}))
   189  	if want := msg + msg; s != want {
   190  		t.Errorf("String: got %#q; want %#q", s, want)
   191  	}
   192  	if allocs > 0 {
   193  		t.Fatalf("got %d alloc(s); want 0", allocs)
   194  	}
   195  
   196  	// Issue 23382; verify that copyCheck doesn't force the
   197  	// Builder to escape and be heap allocated.
   198  	n := testing.AllocsPerRun(10000, func() {
   199  		var b Builder
   200  		b.Grow(5)
   201  		b.WriteString("abcde")
   202  		_ = b.String()
   203  	})
   204  	if n != 1 {
   205  		t.Errorf("Builder allocs = %v; want 1", n)
   206  	}
   207  }
   208  
   209  func TestBuilderCopyPanic(t *testing.T) {
   210  	tests := []struct {
   211  		name      string
   212  		fn        func()
   213  		wantPanic bool
   214  	}{
   215  		{
   216  			name:      "String",
   217  			wantPanic: false,
   218  			fn: func() {
   219  				var a Builder
   220  				a.WriteByte('x')
   221  				b := a
   222  				_ = b.String() // appease vet
   223  			},
   224  		},
   225  		{
   226  			name:      "Len",
   227  			wantPanic: false,
   228  			fn: func() {
   229  				var a Builder
   230  				a.WriteByte('x')
   231  				b := a
   232  				b.Len()
   233  			},
   234  		},
   235  		{
   236  			name:      "Cap",
   237  			wantPanic: false,
   238  			fn: func() {
   239  				var a Builder
   240  				a.WriteByte('x')
   241  				b := a
   242  				b.Cap()
   243  			},
   244  		},
   245  		{
   246  			name:      "Reset",
   247  			wantPanic: false,
   248  			fn: func() {
   249  				var a Builder
   250  				a.WriteByte('x')
   251  				b := a
   252  				b.Reset()
   253  				b.WriteByte('y')
   254  			},
   255  		},
   256  		{
   257  			name:      "Write",
   258  			wantPanic: true,
   259  			fn: func() {
   260  				var a Builder
   261  				a.Write([]byte("x"))
   262  				b := a
   263  				b.Write([]byte("y"))
   264  			},
   265  		},
   266  		{
   267  			name:      "WriteByte",
   268  			wantPanic: true,
   269  			fn: func() {
   270  				var a Builder
   271  				a.WriteByte('x')
   272  				b := a
   273  				b.WriteByte('y')
   274  			},
   275  		},
   276  		{
   277  			name:      "WriteString",
   278  			wantPanic: true,
   279  			fn: func() {
   280  				var a Builder
   281  				a.WriteString("x")
   282  				b := a
   283  				b.WriteString("y")
   284  			},
   285  		},
   286  		{
   287  			name:      "WriteRune",
   288  			wantPanic: true,
   289  			fn: func() {
   290  				var a Builder
   291  				a.WriteRune('x')
   292  				b := a
   293  				b.WriteRune('y')
   294  			},
   295  		},
   296  		{
   297  			name:      "Grow",
   298  			wantPanic: true,
   299  			fn: func() {
   300  				var a Builder
   301  				a.Grow(1)
   302  				b := a
   303  				b.Grow(2)
   304  			},
   305  		},
   306  	}
   307  	for _, tt := range tests {
   308  		didPanic := make(chan bool)
   309  		go func() {
   310  			defer func() { didPanic <- recover() != nil }()
   311  			tt.fn()
   312  		}()
   313  		if got := <-didPanic; got != tt.wantPanic {
   314  			t.Errorf("%s: panicked = %v; want %v", tt.name, got, tt.wantPanic)
   315  		}
   316  	}
   317  }
   318  
   319  var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
   320  
   321  var sinkS string
   322  
   323  func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
   324  	b.Run("1Write_NoGrow", func(b *testing.B) {
   325  		b.ReportAllocs()
   326  		f(b, 1, false)
   327  	})
   328  	b.Run("3Write_NoGrow", func(b *testing.B) {
   329  		b.ReportAllocs()
   330  		f(b, 3, false)
   331  	})
   332  	b.Run("3Write_Grow", func(b *testing.B) {
   333  		b.ReportAllocs()
   334  		f(b, 3, true)
   335  	})
   336  }
   337  
   338  func BenchmarkBuildString_Builder(b *testing.B) {
   339  	benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
   340  		for i := 0; i < b.N; i++ {
   341  			var buf Builder
   342  			if grow {
   343  				buf.Grow(len(someBytes) * numWrite)
   344  			}
   345  			for i := 0; i < numWrite; i++ {
   346  				buf.Write(someBytes)
   347  			}
   348  			sinkS = buf.String()
   349  		}
   350  	})
   351  }
   352  
   353  func BenchmarkBuildString_ByteBuffer(b *testing.B) {
   354  	benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
   355  		for i := 0; i < b.N; i++ {
   356  			var buf bytes.Buffer
   357  			if grow {
   358  				buf.Grow(len(someBytes) * numWrite)
   359  			}
   360  			for i := 0; i < numWrite; i++ {
   361  				buf.Write(someBytes)
   362  			}
   363  			sinkS = buf.String()
   364  		}
   365  	})
   366  }