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  }