github.com/mitranim/gg@v0.1.17/text_test.go (about)

     1  package gg_test
     2  
     3  import (
     4  	"math"
     5  	"regexp"
     6  	"strings"
     7  	"testing"
     8  	u "unsafe"
     9  
    10  	"github.com/mitranim/gg"
    11  	"github.com/mitranim/gg/gtest"
    12  )
    13  
    14  func TestTextDat(t *testing.T) {
    15  	defer gtest.Catch(t)
    16  
    17  	const init = `hello world`
    18  	var sliced string = init[:0]
    19  	var empty string
    20  
    21  	gtest.NotZero(init)
    22  	gtest.Zero(sliced)
    23  	gtest.Zero(empty)
    24  
    25  	gtest.NotZero(gg.TextDat(init))
    26  	gtest.NotZero(gg.TextDat(sliced))
    27  	gtest.Zero(gg.TextDat(empty))
    28  
    29  	gtest.Eq(gg.TextDat(sliced), gg.TextDat(init))
    30  }
    31  
    32  func TestToText(t *testing.T) {
    33  	defer gtest.Catch(t)
    34  
    35  	testToString(gg.ToText[string, []byte])
    36  	testToBytes(gg.ToText[[]byte, string])
    37  
    38  	t.Run(`between_byte_slices`, func(t *testing.T) {
    39  		defer gtest.Catch(t)
    40  
    41  		type Src []byte
    42  
    43  		src := Src(`one_two`)[:len(`one`)]
    44  		gtest.TextEq(src, Src(`one`))
    45  		gtest.Len(src, 3)
    46  		gtest.Cap(src, 7)
    47  
    48  		type Tar []byte
    49  
    50  		gtest.Is(Src(gg.ToText[Tar](src)), src)
    51  		gtest.Is(gg.ToText[Src](src), src)
    52  		gtest.Is(gg.ToText[[]byte](src), []byte(src))
    53  		gtest.Is([]byte(gg.ToText[Tar](src)), []byte(src))
    54  	})
    55  }
    56  
    57  func testToString(fun func([]byte) string) {
    58  	test := func(src []byte) {
    59  		tar := fun(src)
    60  		gtest.Eq(string(tar), string(src))
    61  		gtest.Eq(gg.TextDat(src), gg.TextDat(tar))
    62  	}
    63  
    64  	test(nil)
    65  	test([]byte{})
    66  	test([]byte(`one`))
    67  	test([]byte(`two`))
    68  	test([]byte(`three`))
    69  }
    70  
    71  func testToBytes(fun func(string) []byte) {
    72  	test := func(src string) {
    73  		tar := fun(src)
    74  		gtest.Eq(string(tar), string(src))
    75  		gtest.Eq(gg.TextDat(src), gg.TextDat(tar))
    76  		gtest.Len(tar, len(src))
    77  		gtest.Cap(tar, len(src))
    78  	}
    79  
    80  	test(``)
    81  	test(`one`)
    82  	test(`two`)
    83  	test(`three`)
    84  }
    85  
    86  func BenchmarkTextDat_string(b *testing.B) {
    87  	defer gtest.Catch(b)
    88  
    89  	for ind := 0; ind < b.N; ind++ {
    90  		gg.Nop1(gg.TextDat(`b5c298d773c048b2a88e7fe4a2bd5b0d`))
    91  	}
    92  }
    93  
    94  func BenchmarkTextDat_bytes(b *testing.B) {
    95  	defer gtest.Catch(b)
    96  
    97  	src := []byte(`b5c298d773c048b2a88e7fe4a2bd5b0d`)
    98  	b.ResetTimer()
    99  
   100  	for ind := 0; ind < b.N; ind++ {
   101  		gg.Nop1(gg.TextDat(src))
   102  	}
   103  }
   104  
   105  func BenchmarkToText_string_to_string(b *testing.B) {
   106  	defer gtest.Catch(b)
   107  
   108  	type Src string
   109  	type Out string
   110  	src := Src(`742af97969e845408f0261c213a4c01f`)
   111  
   112  	for ind := 0; ind < b.N; ind++ {
   113  		gg.ToText[Out](src)
   114  	}
   115  }
   116  
   117  func BenchmarkToText_string_to_bytes(b *testing.B) {
   118  	defer gtest.Catch(b)
   119  
   120  	type Src string
   121  	type Out []byte
   122  	src := Src(`742af97969e845408f0261c213a4c01f`)
   123  
   124  	for ind := 0; ind < b.N; ind++ {
   125  		gg.ToText[Out](src)
   126  	}
   127  }
   128  
   129  func BenchmarkToText_bytes_to_string(b *testing.B) {
   130  	defer gtest.Catch(b)
   131  
   132  	type Src []byte
   133  	type Out string
   134  	src := Src(`742af97969e845408f0261c213a4c01f`)
   135  
   136  	for ind := 0; ind < b.N; ind++ {
   137  		gg.ToText[Out](src)
   138  	}
   139  }
   140  
   141  func BenchmarkToText_bytes_to_bytes(b *testing.B) {
   142  	defer gtest.Catch(b)
   143  
   144  	type Src []byte
   145  	type Out []byte
   146  	src := Src(`742af97969e845408f0261c213a4c01f`)
   147  
   148  	for ind := 0; ind < b.N; ind++ {
   149  		gg.ToText[Out](src)
   150  	}
   151  }
   152  
   153  // TODO dedup with `TestBuf_String`.
   154  func TestToString(t *testing.T) {
   155  	defer gtest.Catch(t)
   156  
   157  	testToString(gg.ToString[[]byte])
   158  
   159  	t.Run(`mutation`, func(t *testing.T) {
   160  		defer gtest.Catch(t)
   161  
   162  		src := []byte(`abc`)
   163  		tar := gg.ToString(src)
   164  		gtest.Eq(tar, `abc`)
   165  
   166  		src[0] = 'd'
   167  		gtest.Eq(tar, `dbc`)
   168  	})
   169  }
   170  
   171  func TestToBytes(t *testing.T) {
   172  	defer gtest.Catch(t)
   173  
   174  	testToBytes(gg.ToBytes[string])
   175  }
   176  
   177  func TestTextPop(t *testing.T) {
   178  	defer gtest.Catch(t)
   179  
   180  	rem := `{one,two,,three}`
   181  
   182  	gtest.Eq(gg.TextPop(&rem, `,`), `{one`)
   183  	gtest.Eq(rem, `two,,three}`)
   184  
   185  	gtest.Eq(gg.TextPop(&rem, `,`), `two`)
   186  	gtest.Eq(rem, `,three}`)
   187  
   188  	gtest.Eq(gg.TextPop(&rem, `,`), ``)
   189  	gtest.Eq(rem, `three}`)
   190  
   191  	gtest.Eq(gg.TextPop(&rem, `,`), `three}`)
   192  	gtest.Eq(rem, ``)
   193  }
   194  
   195  func TestJoinAny(t *testing.T) {
   196  	gtest.Catch(t)
   197  
   198  	gtest.Zero(gg.JoinAny(nil, ``))
   199  	gtest.Zero(gg.JoinAny([]any{}, ``))
   200  	gtest.Zero(gg.JoinAny([]any{}, `_`))
   201  
   202  	gtest.Zero(gg.JoinAny([]any{``}, ``))
   203  	gtest.Zero(gg.JoinAny([]any{``, ``}, ``))
   204  	gtest.Zero(gg.JoinAny([]any{``, ``, ``}, ``))
   205  
   206  	gtest.Eq(gg.JoinAny([]any{``}, `_`), ``)
   207  	gtest.Eq(gg.JoinAny([]any{``, ``}, `_`), `_`)
   208  	gtest.Eq(gg.JoinAny([]any{``, ``, ``}, `_`), `__`)
   209  
   210  	gtest.Eq(gg.JoinAny([]any{12}, ``), `12`)
   211  	gtest.Eq(gg.JoinAny([]any{12}, `_`), `12`)
   212  
   213  	gtest.Eq(gg.JoinAny([]any{12, 34}, ``), `1234`)
   214  	gtest.Eq(gg.JoinAny([]any{12, 34}, `_`), `12_34`)
   215  
   216  	gtest.Eq(gg.JoinAny([]any{12, 34, 56}, ``), `123456`)
   217  	gtest.Eq(gg.JoinAny([]any{12, 34, 56}, `_`), `12_34_56`)
   218  
   219  	gtest.Eq(gg.JoinAny([]any{12, `str`}, ``), `12str`)
   220  	gtest.Eq(gg.JoinAny([]any{12, `str`}, `_`), `12_str`)
   221  
   222  	gtest.Eq(gg.JoinAny([]any{`one`, ``, `two`, ``, `three`}, ``), `onetwothree`)
   223  	gtest.Eq(gg.JoinAny([]any{`one`, ``, `two`, ``, `three`}, `_`), `one__two__three`)
   224  }
   225  
   226  func BenchmarkJoinAny(b *testing.B) {
   227  	src := gg.Map(gg.Span(128), gg.ToAny[int])
   228  	b.ResetTimer()
   229  
   230  	for ind := 0; ind < b.N; ind++ {
   231  		gg.Nop1(gg.JoinAny(src, ` `))
   232  	}
   233  }
   234  
   235  func TestJoinAnyOpt(t *testing.T) {
   236  	gtest.Catch(t)
   237  
   238  	gtest.Zero(gg.JoinAnyOpt(nil, ``))
   239  	gtest.Zero(gg.JoinAnyOpt([]any{}, ``))
   240  	gtest.Zero(gg.JoinAnyOpt([]any{}, `_`))
   241  
   242  	gtest.Zero(gg.JoinAnyOpt([]any{``}, ``))
   243  	gtest.Zero(gg.JoinAnyOpt([]any{``, ``}, ``))
   244  	gtest.Zero(gg.JoinAnyOpt([]any{``, ``, ``}, ``))
   245  
   246  	gtest.Zero(gg.JoinAnyOpt([]any{``}, `_`))
   247  	gtest.Zero(gg.JoinAnyOpt([]any{``, ``}, `_`))
   248  	gtest.Zero(gg.JoinAnyOpt([]any{``, ``, ``}, `_`))
   249  
   250  	gtest.Eq(gg.JoinAnyOpt([]any{12}, ``), `12`)
   251  	gtest.Eq(gg.JoinAnyOpt([]any{12}, `_`), `12`)
   252  
   253  	gtest.Eq(gg.JoinAnyOpt([]any{12, 34}, ``), `1234`)
   254  	gtest.Eq(gg.JoinAnyOpt([]any{12, 34}, `_`), `12_34`)
   255  
   256  	gtest.Eq(gg.JoinAnyOpt([]any{12, 34, 56}, ``), `123456`)
   257  	gtest.Eq(gg.JoinAnyOpt([]any{12, 34, 56}, `_`), `12_34_56`)
   258  
   259  	gtest.Eq(gg.JoinAnyOpt([]any{12, `str`}, ``), `12str`)
   260  	gtest.Eq(gg.JoinAnyOpt([]any{12, `str`}, `_`), `12_str`)
   261  
   262  	gtest.Eq(gg.JoinAnyOpt([]any{`one`, ``, `two`, ``, `three`}, ``), `onetwothree`)
   263  	gtest.Eq(gg.JoinAnyOpt([]any{`one`, ``, `two`, ``, `three`}, `_`), `one_two_three`)
   264  }
   265  
   266  func BenchmarkJoinAnyOpt(b *testing.B) {
   267  	src := gg.Map(gg.Span(128), gg.ToAny[int])
   268  	b.ResetTimer()
   269  
   270  	for ind := 0; ind < b.N; ind++ {
   271  		gg.Nop1(gg.JoinAnyOpt(src, ` `))
   272  	}
   273  }
   274  
   275  func Benchmark_strings_Join(b *testing.B) {
   276  	src := gg.Map(gg.Span(128), gg.String[int])
   277  	b.ResetTimer()
   278  
   279  	for ind := 0; ind < b.N; ind++ {
   280  		gg.Nop1(strings.Join(src, ` `))
   281  	}
   282  }
   283  
   284  func BenchmarkJoin(b *testing.B) {
   285  	src := gg.Map(gg.Span(128), gg.String[int])
   286  	b.ResetTimer()
   287  
   288  	for ind := 0; ind < b.N; ind++ {
   289  		gg.Nop1(gg.Join(src, ` `))
   290  	}
   291  }
   292  
   293  func BenchmarkJoinOpt(b *testing.B) {
   294  	src := gg.Map(gg.Span(128), gg.String[int])
   295  	b.ResetTimer()
   296  
   297  	for ind := 0; ind < b.N; ind++ {
   298  		gg.Nop1(gg.JoinOpt(src, ` `))
   299  	}
   300  }
   301  
   302  // TODO test Unicode.
   303  func TestToWords(t *testing.T) {
   304  	defer gtest.Catch(t)
   305  
   306  	test := func(src string, exp gg.Words) { gtest.Equal(gg.ToWords(src), exp) }
   307  
   308  	test(``, nil)
   309  	test(` `, nil)
   310  
   311  	test(`one`, gg.Words{`one`})
   312  	test(`one two`, gg.Words{`one`, `two`})
   313  	test(`one two three`, gg.Words{`one`, `two`, `three`})
   314  	test(`one  two  three`, gg.Words{`one`, `two`, `three`})
   315  	test(`One Two Three`, gg.Words{`One`, `Two`, `Three`})
   316  	test(`ONE TWO THREE`, gg.Words{`ONE`, `TWO`, `THREE`})
   317  	test(`one12 two34 three56`, gg.Words{`one12`, `two34`, `three56`})
   318  	test(`One12 Two34 Three56`, gg.Words{`One12`, `Two34`, `Three56`})
   319  	test(`ONE12 TWO34 THREE56`, gg.Words{`ONE12`, `TWO34`, `THREE56`})
   320  
   321  	test(`one_two_three`, gg.Words{`one`, `two`, `three`})
   322  	test(`one_Two_Three`, gg.Words{`one`, `Two`, `Three`})
   323  	test(`One_Two_Three`, gg.Words{`One`, `Two`, `Three`})
   324  	test(`ONE_TWO_THREE`, gg.Words{`ONE`, `TWO`, `THREE`})
   325  	test(`one12_two34_three56`, gg.Words{`one12`, `two34`, `three56`})
   326  	test(`one12_Two34_Three56`, gg.Words{`one12`, `Two34`, `Three56`})
   327  	test(`One12_Two34_Three56`, gg.Words{`One12`, `Two34`, `Three56`})
   328  	test(`ONE12_TWO34_THREE56`, gg.Words{`ONE12`, `TWO34`, `THREE56`})
   329  
   330  	test(`oneTwoThree`, gg.Words{`one`, `Two`, `Three`})
   331  	test(`OneTwoThree`, gg.Words{`One`, `Two`, `Three`})
   332  	test(`one12Two34Three56`, gg.Words{`one12`, `Two34`, `Three56`})
   333  	test(`One12Two34Three56`, gg.Words{`One12`, `Two34`, `Three56`})
   334  
   335  	test(`one-two-three`, gg.Words{`one`, `two`, `three`})
   336  	test(`one-Two-Three`, gg.Words{`one`, `Two`, `Three`})
   337  	test(`One-Two-Three`, gg.Words{`One`, `Two`, `Three`})
   338  	test(`ONE-TWO-THREE`, gg.Words{`ONE`, `TWO`, `THREE`})
   339  	test(`one12-two34-three56`, gg.Words{`one12`, `two34`, `three56`})
   340  	test(`one12-Two34-Three56`, gg.Words{`one12`, `Two34`, `Three56`})
   341  	test(`One12-Two34-Three56`, gg.Words{`One12`, `Two34`, `Three56`})
   342  	test(`ONE12-TWO34-THREE56`, gg.Words{`ONE12`, `TWO34`, `THREE56`})
   343  
   344  	test(`one&two|three`, gg.Words{`one`, `two`, `three`})
   345  	test(`one&two|three:four`, gg.Words{`one`, `two`, `three`, `four`})
   346  
   347  	test(`12a33fe0-4b4a-48e5-bb90-53a068ad376b`, gg.Words{`12a33fe0`, `4b4a`, `48e5`, `bb90`, `53a068ad376b`})
   348  	test(`2af310ac-f7b8-470d-b04c-98a286b8bf3f`, gg.Words{`2af310ac`, `f7b8`, `470d`, `b04c`, `98a286b8bf3f`})
   349  	test(`154cd2d1-fade-4cab-ab01-a7df8b760569`, gg.Words{`154cd2d1`, `fade`, `4cab`, `ab01`, `a7df8b760569`})
   350  	test(`c7ddacf8-4117-49c3-8a7e-a3c9627dc199`, gg.Words{`c7ddacf8`, `4117`, `49c3`, `8a7e`, `a3c9627dc199`})
   351  	test(`cf1a86ed-6db7-4c6f-aad3-c8e6cb1cb2f2`, gg.Words{`cf1a86ed`, `6db7`, `4c6f`, `aad3`, `c8e6cb1cb2f2`})
   352  	test(`78437a9e-45fd-4007-8a04-bace0147df30`, gg.Words{`78437a9e`, `45fd`, `4007`, `8a04`, `bace0147df30`})
   353  }
   354  
   355  // TODO test Unicode.
   356  func TestWords(t *testing.T) {
   357  	defer gtest.Catch(t)
   358  
   359  	src := func() gg.Words { return gg.Words{`one`, `two`, `three`} }
   360  
   361  	gtest.Equal(gg.ToWords(`one two`).Lower(), gg.Words{`one`, `two`})
   362  	gtest.Equal(gg.ToWords(`One Two`).Lower(), gg.Words{`one`, `two`})
   363  	gtest.Equal(gg.ToWords(`ONE TWO`).Lower(), gg.Words{`one`, `two`})
   364  
   365  	gtest.Equal(src().Lower(), gg.Words{`one`, `two`, `three`})
   366  	gtest.Equal(src().Upper(), gg.Words{`ONE`, `TWO`, `THREE`})
   367  	gtest.Equal(src().Title(), gg.Words{`One`, `Two`, `Three`})
   368  	gtest.Equal(src().Sentence(), gg.Words{`One`, `two`, `three`})
   369  	gtest.Equal(src().Camel(), gg.Words{`one`, `Two`, `Three`})
   370  
   371  	gtest.Eq(src().Dense(), `onetwothree`)
   372  	gtest.Eq(src().Spaced(), `one two three`)
   373  	gtest.Eq(src().Snake(), `one_two_three`)
   374  	gtest.Eq(src().Kebab(), `one-two-three`)
   375  	gtest.Eq(src().Comma(), `one,two,three`)
   376  	gtest.Eq(src().Piped(), `one|two|three`)
   377  
   378  	gtest.Eq(src().Lower().Dense(), `onetwothree`)
   379  	gtest.Eq(src().Lower().Spaced(), `one two three`)
   380  	gtest.Eq(src().Lower().Snake(), `one_two_three`)
   381  	gtest.Eq(src().Lower().Kebab(), `one-two-three`)
   382  	gtest.Eq(src().Lower().Comma(), `one,two,three`)
   383  	gtest.Eq(src().Lower().Piped(), `one|two|three`)
   384  
   385  	gtest.Eq(src().Upper().Dense(), `ONETWOTHREE`)
   386  	gtest.Eq(src().Upper().Spaced(), `ONE TWO THREE`)
   387  	gtest.Eq(src().Upper().Snake(), `ONE_TWO_THREE`)
   388  	gtest.Eq(src().Upper().Kebab(), `ONE-TWO-THREE`)
   389  	gtest.Eq(src().Upper().Comma(), `ONE,TWO,THREE`)
   390  	gtest.Eq(src().Upper().Piped(), `ONE|TWO|THREE`)
   391  
   392  	gtest.Eq(src().Title().Dense(), `OneTwoThree`)
   393  	gtest.Eq(src().Title().Spaced(), `One Two Three`)
   394  	gtest.Eq(src().Title().Snake(), `One_Two_Three`)
   395  	gtest.Eq(src().Title().Kebab(), `One-Two-Three`)
   396  	gtest.Eq(src().Title().Comma(), `One,Two,Three`)
   397  	gtest.Eq(src().Title().Piped(), `One|Two|Three`)
   398  
   399  	gtest.Eq(src().Sentence().Dense(), `Onetwothree`)
   400  	gtest.Eq(src().Sentence().Spaced(), `One two three`)
   401  	gtest.Eq(src().Sentence().Snake(), `One_two_three`)
   402  	gtest.Eq(src().Sentence().Kebab(), `One-two-three`)
   403  	gtest.Eq(src().Sentence().Comma(), `One,two,three`)
   404  	gtest.Eq(src().Sentence().Piped(), `One|two|three`)
   405  
   406  	gtest.Eq(src().Camel().Dense(), `oneTwoThree`)
   407  	gtest.Eq(src().Camel().Spaced(), `one Two Three`)
   408  	gtest.Eq(src().Camel().Snake(), `one_Two_Three`)
   409  	gtest.Eq(src().Camel().Kebab(), `one-Two-Three`)
   410  	gtest.Eq(src().Camel().Comma(), `one,Two,Three`)
   411  	gtest.Eq(src().Camel().Piped(), `one|Two|Three`)
   412  }
   413  
   414  func BenchmarkReWord_init(b *testing.B) {
   415  	src := gg.ReWord.Get().String()
   416  
   417  	for ind := 0; ind < b.N; ind++ {
   418  		regexp.MustCompile(src)
   419  	}
   420  }
   421  
   422  func BenchmarkReWord_reuse(b *testing.B) {
   423  	gg.Nop1(gg.ReWord.Get())
   424  
   425  	for ind := 0; ind < b.N; ind++ {
   426  		gg.Nop1(gg.ReWord.Get())
   427  	}
   428  }
   429  
   430  func TestSplitLines(t *testing.T) {
   431  	defer gtest.Catch(t)
   432  
   433  	var Split = gg.SplitLines[string]
   434  
   435  	gtest.Empty(Split(``))
   436  	gtest.Equal(Split(` `), []string{` `})
   437  
   438  	gtest.Equal(Split("\n"), []string{``, ``})
   439  	gtest.Equal(Split("\r"), []string{``, ``})
   440  	gtest.Equal(Split("\r\n"), []string{``, ``})
   441  
   442  	gtest.Equal(Split("one"), []string{`one`})
   443  	gtest.Equal(Split("one\n"), []string{`one`, ``})
   444  	gtest.Equal(Split("one\r"), []string{`one`, ``})
   445  	gtest.Equal(Split("one\r\n"), []string{`one`, ``})
   446  
   447  	gtest.Equal(Split("\none"), []string{``, `one`})
   448  	gtest.Equal(Split("\rone"), []string{``, `one`})
   449  	gtest.Equal(Split("\r\none"), []string{``, `one`})
   450  
   451  	gtest.Equal(Split("\none\n"), []string{``, `one`, ``})
   452  	gtest.Equal(Split("\rone\r"), []string{``, `one`, ``})
   453  	gtest.Equal(Split("\r\none\r\n"), []string{``, `one`, ``})
   454  
   455  	gtest.Equal(Split("one\ntwo"), []string{`one`, `two`})
   456  	gtest.Equal(Split("one\rtwo"), []string{`one`, `two`})
   457  	gtest.Equal(Split("one\r\ntwo"), []string{`one`, `two`})
   458  
   459  	gtest.Equal(Split("one\ntwo\n"), []string{`one`, `two`, ``})
   460  	gtest.Equal(Split("one\rtwo\r"), []string{`one`, `two`, ``})
   461  	gtest.Equal(Split("one\r\ntwo\r\n"), []string{`one`, `two`, ``})
   462  
   463  	gtest.Equal(Split("\none\ntwo"), []string{``, `one`, `two`})
   464  	gtest.Equal(Split("\rone\rtwo"), []string{``, `one`, `two`})
   465  	gtest.Equal(Split("\r\none\r\ntwo"), []string{``, `one`, `two`})
   466  
   467  	gtest.Equal(Split("\none\ntwo\n"), []string{``, `one`, `two`, ``})
   468  	gtest.Equal(Split("\rone\rtwo\r"), []string{``, `one`, `two`, ``})
   469  	gtest.Equal(Split("\r\none\r\ntwo\r\n"), []string{``, `one`, `two`, ``})
   470  }
   471  
   472  func BenchmarkSplitLines(b *testing.B) {
   473  	defer gtest.Catch(b)
   474  	src := makeLines()
   475  	b.ResetTimer()
   476  
   477  	for ind := 0; ind < b.N; ind++ {
   478  		gg.Nop1(gg.SplitLines(src))
   479  	}
   480  }
   481  
   482  func makeLines() []byte {
   483  	var buf gg.Buf
   484  	buf.GrowLen(1024 * 1024)
   485  	for ind := range buf {
   486  		if ind%64 == 0 {
   487  			buf[ind] = '\n'
   488  		} else {
   489  			buf[ind] = ' '
   490  		}
   491  	}
   492  	return buf
   493  }
   494  
   495  func TestSplitLines2(t *testing.T) {
   496  	defer gtest.Catch(t)
   497  
   498  	test := func(src, oneExp, twoExp string, sizeExp int) {
   499  		one, two, size := gg.SplitLines2(src)
   500  		gtest.Eq([2]string{one, two}, [2]string{oneExp, twoExp})
   501  		gtest.Eq(size, sizeExp)
   502  	}
   503  
   504  	test(``, ``, ``, 0)
   505  	test("one\n", `one`, ``, 1)
   506  	test("one\ntwo", `one`, `two`, 1)
   507  	test("one\r\ntwo", `one`, `two`, 2)
   508  	test("one\rtwo", `one`, `two`, 1)
   509  	test("\ntwo", ``, `two`, 1)
   510  	test("\r\ntwo", ``, `two`, 2)
   511  	test("\rtwo", ``, `two`, 1)
   512  	test("one\ntwo\nthree", `one`, "two\nthree", 1)
   513  }
   514  
   515  func TestTextCut_ours(t *testing.T) {
   516  	defer gtest.Catch(t)
   517  	testTextCut(gg.TextCut[string])
   518  }
   519  
   520  func TestTextCut_alternate(t *testing.T) {
   521  	defer gtest.Catch(t)
   522  	testTextCut(TextCutRuneSlice[string])
   523  }
   524  
   525  func testTextCut(fun func(string, int, int) string) {
   526  	const src = `🐒🐴🦖🦔🐲🐈`
   527  
   528  	gtest.Zero(fun(src, 0, 0))
   529  	gtest.Eq(fun(src, 0, 1), `🐒`)
   530  	gtest.Eq(fun(src, 0, 2), `🐒🐴`)
   531  	gtest.Eq(fun(src, 0, 3), `🐒🐴🦖`)
   532  	gtest.Eq(fun(src, 0, 4), `🐒🐴🦖🦔`)
   533  	gtest.Eq(fun(src, 0, 5), `🐒🐴🦖🦔🐲`)
   534  	gtest.Eq(fun(src, 0, 6), `🐒🐴🦖🦔🐲🐈`)
   535  	gtest.Eq(fun(src, 0, 7), `🐒🐴🦖🦔🐲🐈`)
   536  	gtest.Eq(fun(src, 0, 8), `🐒🐴🦖🦔🐲🐈`)
   537  
   538  	gtest.Eq(fun(src, -1, 1), `🐒`)
   539  	gtest.Eq(fun(src, -1, 6), `🐒🐴🦖🦔🐲🐈`)
   540  
   541  	gtest.Zero(fun(src, 1, 0))
   542  	gtest.Zero(fun(src, 1, 1))
   543  	gtest.Eq(fun(src, 1, 2), `🐴`)
   544  	gtest.Eq(fun(src, 1, 6), `🐴🦖🦔🐲🐈`)
   545  
   546  	gtest.Eq(fun(`one two three four`, 4, 13), `two three`)
   547  }
   548  
   549  // Alternate implementation for comparison with ours.
   550  func TextCutRuneSlice[A ~string](src A, start, end int) A {
   551  	if !(end > start) {
   552  		return ``
   553  	}
   554  
   555  	runes := []rune(src)
   556  	size := len(runes)
   557  
   558  	if start < 0 {
   559  		start = 0
   560  	} else if start > size {
   561  		start = size
   562  	}
   563  	if end < 0 {
   564  		end = 0
   565  	} else if end > size {
   566  		end = size
   567  	}
   568  
   569  	return A(runes[start:end])
   570  }
   571  
   572  func BenchmarkTextCut_ours(b *testing.B) {
   573  	const src = `🐒🐴🦖🦔🐲🐈`
   574  
   575  	for ind := 0; ind < b.N; ind++ {
   576  		for start := range gg.Iter(3) {
   577  			start--
   578  			for end := range gg.Iter(6) {
   579  				gg.Nop1(gg.TextCut(src, start, end))
   580  			}
   581  		}
   582  	}
   583  }
   584  
   585  func BenchmarkTextCut_alternate(b *testing.B) {
   586  	const src = `🐒🐴🦖🦔🐲🐈`
   587  
   588  	for ind := 0; ind < b.N; ind++ {
   589  		for start := range gg.Iter(3) {
   590  			start--
   591  			for end := range gg.Iter(6) {
   592  				gg.Nop1(TextCutRuneSlice(src, start, end))
   593  			}
   594  		}
   595  	}
   596  }
   597  
   598  func TestStr(t *testing.T) {
   599  	defer gtest.Catch(t)
   600  
   601  	gtest.Zero(gg.Str())
   602  	gtest.Zero(gg.Str(nil))
   603  	gtest.Zero(gg.Str(nil, nil))
   604  	gtest.Zero(gg.Str(``))
   605  	gtest.Zero(gg.Str(``, nil))
   606  	gtest.Zero(gg.Str(``, nil, ``))
   607  	gtest.Zero(gg.Str(``, nil, ``, nil))
   608  
   609  	gtest.Eq(gg.Str(0), `0`)
   610  	gtest.Eq(gg.Str(0, 0), `00`)
   611  	gtest.Eq(gg.Str(0, 10), `010`)
   612  	gtest.Eq(gg.Str(0, 10, 20), `01020`)
   613  	gtest.Eq(gg.Str(`one`), `one`)
   614  	gtest.Eq(gg.Str(`one`, ``), `one`)
   615  	gtest.Eq(gg.Str(`one`, ``, `two`), `onetwo`)
   616  	gtest.Eq(gg.Str(`one`, `_`, `two`), `one_two`)
   617  	gtest.Eq(gg.Str(10, `_`, 20), `10_20`)
   618  }
   619  
   620  func BenchmarkStr_0(b *testing.B) {
   621  	for ind := 0; ind < b.N; ind++ {
   622  		gg.Str()
   623  	}
   624  }
   625  
   626  func BenchmarkStr_1(b *testing.B) {
   627  	for ind := 0; ind < b.N; ind++ {
   628  		gg.Str(`one`)
   629  	}
   630  }
   631  
   632  func BenchmarkStr_2(b *testing.B) {
   633  	for ind := 0; ind < b.N; ind++ {
   634  		gg.Str(`one`, `two`)
   635  	}
   636  }
   637  
   638  func BenchmarkStr_3(b *testing.B) {
   639  	for ind := 0; ind < b.N; ind++ {
   640  		gg.Str(`one`, `two`, `three`)
   641  	}
   642  }
   643  
   644  func TestTextEllipsis(t *testing.T) {
   645  	defer gtest.Catch(t)
   646  
   647  	const src = `🐒🐴🦖🦔🐲🐈`
   648  
   649  	gtest.Zero(gg.TextEllipsis(src, 0))
   650  	gtest.Eq(gg.TextEllipsis(src, 1), `…`)
   651  	gtest.Eq(gg.TextEllipsis(src, 2), `🐒…`)
   652  	gtest.Eq(gg.TextEllipsis(src, 3), `🐒🐴…`)
   653  	gtest.Eq(gg.TextEllipsis(src, 4), `🐒🐴🦖…`)
   654  	gtest.Eq(gg.TextEllipsis(src, 5), `🐒🐴🦖🦔…`)
   655  	gtest.Eq(gg.TextEllipsis(src, 6), `🐒🐴🦖🦔🐲🐈`)
   656  	gtest.Eq(gg.TextEllipsis(src, 7), `🐒🐴🦖🦔🐲🐈`)
   657  	gtest.Eq(gg.TextEllipsis(src, 8), `🐒🐴🦖🦔🐲🐈`)
   658  	gtest.Eq(gg.TextEllipsis(src, 9), `🐒🐴🦖🦔🐲🐈`)
   659  	gtest.Eq(gg.TextEllipsis(src, math.MaxUint), `🐒🐴🦖🦔🐲🐈`)
   660  }
   661  
   662  func BenchmarkTextEllipsis_changed(b *testing.B) {
   663  	for ind := 0; ind < b.N; ind++ {
   664  		gg.TextEllipsis(`🐒🐴🦖🦔🐲🐈`, 4)
   665  	}
   666  }
   667  
   668  func BenchmarkTextEllipsis_unchanged(b *testing.B) {
   669  	for ind := 0; ind < b.N; ind++ {
   670  		gg.TextEllipsis(`🐒🐴🦖🦔🐲🐈`, 6)
   671  	}
   672  }
   673  
   674  func BenchmarkTextTrunc_changed(b *testing.B) {
   675  	for ind := 0; ind < b.N; ind++ {
   676  		gg.TextTrunc(`🐒🐴🦖🦔🐲🐈`, 4)
   677  	}
   678  }
   679  
   680  func BenchmarkTextTrunc_unchanged(b *testing.B) {
   681  	for ind := 0; ind < b.N; ind++ {
   682  		gg.TextTrunc(`🐒🐴🦖🦔🐲🐈`, 6)
   683  	}
   684  }
   685  
   686  func TestTextHeadChar(t *testing.T) {
   687  	defer gtest.Catch(t)
   688  
   689  	test := func(src string, valExp rune, sizeExp int) {
   690  		val, size := gg.TextHeadChar(src)
   691  		gtest.Eq(val, valExp, `matching char`)
   692  		gtest.Eq(size, sizeExp, `matching size`)
   693  	}
   694  
   695  	test(``, 0, 0)
   696  	test(`one`, 'o', 1)
   697  	test(`🐒🐴🦖🦔🐲🐈`, '🐒', 4)
   698  }
   699  
   700  func TestAppendNewlineOpt(t *testing.T) {
   701  	defer gtest.Catch(t)
   702  
   703  	testAppendNewlineOptSame(``)
   704  	testAppendNewlineOptSame([]byte(nil))
   705  	testAppendNewlineOptSame([]byte{})
   706  	testAppendNewlineOptSame("\n")
   707  	testAppendNewlineOptSame("one\n")
   708  	testAppendNewlineOptSame("one\r")
   709  	testAppendNewlineOptSame("one\r\n")
   710  	testAppendNewlineOptSame([]byte("\n"))
   711  	testAppendNewlineOptSame([]byte("one\n"))
   712  	testAppendNewlineOptSame([]byte("one\r"))
   713  	testAppendNewlineOptSame([]byte("one\r\n"))
   714  
   715  	gtest.TextEq(gg.AppendNewlineOpt(`one`), "one\n")
   716  	gtest.TextEq(gg.AppendNewlineOpt([]byte(`one`)), []byte("one\n"))
   717  
   718  	{
   719  		src := []byte("one two")
   720  		tar := src[:len(`one`)]
   721  		out := gg.AppendNewlineOpt(tar)
   722  
   723  		gtest.TextEq(src, []byte("one\ntwo"))
   724  		gtest.TextEq(out, []byte("one\n"))
   725  		gtest.Eq(u.SliceData(src), u.SliceData(tar))
   726  		gtest.Eq(u.SliceData(src), u.SliceData(out))
   727  	}
   728  }
   729  
   730  func testAppendNewlineOptSame[A gg.Text](src A) {
   731  	gtest.Is(gg.AppendNewlineOpt(src), src)
   732  }