bitbucket.org/ai69/amoy@v0.2.3/replace_test.go (about)

     1  package amoy
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/1set/gut/yrand"
     9  )
    10  
    11  func BenchmarkReplaceString_Single_CaseSensitive_Literal(b *testing.B) {
    12  	s := "aloha Hello hello word WORLD World 123456"
    13  	replace := map[string]string{"hello": "Goodbye"}
    14  	opt := ReplaceStringOptions{replace, false, false}
    15  	b.ResetTimer()
    16  	for i := 0; i < b.N; i++ {
    17  		_ = ReplaceString(s, opt)
    18  	}
    19  }
    20  
    21  func BenchmarkReplaceString_Multiple_CaseSensitive_Literal(b *testing.B) {
    22  	s := "aloha Hello hello word WORLD World 123456"
    23  	replace := map[string]string{"hello": "Goodbye", "world": "Earth"}
    24  	opt := ReplaceStringOptions{replace, false, false}
    25  	b.ResetTimer()
    26  	for i := 0; i < b.N; i++ {
    27  		_ = ReplaceString(s, opt)
    28  	}
    29  }
    30  
    31  func BenchmarkReplaceString_Single_CaseInsensitive_Literal(b *testing.B) {
    32  	s := "aloha Hello hello word WORLD World 123456"
    33  	replace := map[string]string{"hello": "Goodbye"}
    34  	opt := ReplaceStringOptions{replace, true, false}
    35  	b.ResetTimer()
    36  	for i := 0; i < b.N; i++ {
    37  		_ = ReplaceString(s, opt)
    38  	}
    39  }
    40  
    41  func BenchmarkReplaceString_Multiple_CaseInsensitive_Literal(b *testing.B) {
    42  	s := "aloha Hello hello word WORLD World 123456"
    43  	replace := map[string]string{"hello": "Goodbye", "world": "Earth"}
    44  	opt := ReplaceStringOptions{replace, true, false}
    45  	b.ResetTimer()
    46  	for i := 0; i < b.N; i++ {
    47  		_ = ReplaceString(s, opt)
    48  	}
    49  }
    50  
    51  func BenchmarkReplaceString_Single_CaseInsensitive_Imitation(b *testing.B) {
    52  	s := "aloha Hello hello word WORLD World 123456"
    53  	replace := map[string]string{"hello": "Goodbye"}
    54  	opt := ReplaceStringOptions{replace, true, true}
    55  	b.ResetTimer()
    56  	for i := 0; i < b.N; i++ {
    57  		_ = ReplaceString(s, opt)
    58  	}
    59  }
    60  
    61  func BenchmarkReplaceString_Multiple_CaseInsensitive_Imitation(b *testing.B) {
    62  	s := "aloha Hello hello word WORLD World 123456"
    63  	replace := map[string]string{"hello": "Goodbye", "world": "Earth"}
    64  	opt := ReplaceStringOptions{replace, true, true}
    65  	b.ResetTimer()
    66  	for i := 0; i < b.N; i++ {
    67  		_ = ReplaceString(s, opt)
    68  	}
    69  }
    70  
    71  func TestReplaceString(t *testing.T) {
    72  	optsNilReplace := ReplaceStringOptions{}
    73  	optsEmptyReplace := ReplaceStringOptions{
    74  		Replacements:    map[string]string{},
    75  		CaseInsensitive: false,
    76  		ImitateResult:   false,
    77  	}
    78  	optsInvalidReplace := ReplaceStringOptions{
    79  		Replacements:    map[string]string{"": "123"},
    80  		CaseInsensitive: false,
    81  		ImitateResult:   false,
    82  	}
    83  	optsMixedReplace := ReplaceStringOptions{
    84  		Replacements:    map[string]string{"": "456", "hello": "goodbye"},
    85  		CaseInsensitive: false,
    86  		ImitateResult:   false,
    87  	}
    88  	optsSingleReplace := ReplaceStringOptions{
    89  		Replacements:    map[string]string{"hello": "goodbye"},
    90  		CaseInsensitive: false,
    91  		ImitateResult:   false,
    92  	}
    93  	s0 := "Hello hello word WORLD"
    94  	replace0 := map[string]string{"hello": "Goodbye"}
    95  	s1 := "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD"
    96  	s2 := "abcdefghijklmnopqrstuvwxyzstuvwxabd"
    97  	replace1 := map[string]string{
    98  		"ABC":    "123",
    99  		"STUVWX": "888ABCGH",
   100  		"GH":     "6789",
   101  		"STUVW":  "999",
   102  	}
   103  	replace2 := map[string]string{
   104  		"ABC":    "11",
   105  		"ABCDEF": "22222",
   106  		"DEF":    "3",
   107  		"GH":     "6789",
   108  	}
   109  	replace3 := map[string]string{
   110  		`standard`: `{{ .AppName }}`,
   111  		`bitbucket.org/ai69/keiki/kgengo/standard`: `{{ .ModulePath }}`,
   112  		`Copyright (c) 2022 Hyori`:                 `Copyright (c) {{ .Now.UTC.Year }} {{ .Author }}`,
   113  	}
   114  	replace4 := map[string]string{
   115  		"":               "888",
   116  		"abc":            "123",
   117  		"hello-world":    "good-bye",
   118  		"github.com":     "gitlab.com",
   119  		"github.com/abc": "bitbucket.org/def",
   120  	}
   121  	tests := []struct {
   122  		name string
   123  		s    string
   124  		opt  ReplaceStringOptions
   125  		want string
   126  	}{
   127  		{"Default Options", "aloha", optsNilReplace, "aloha"},
   128  		{"Empty Replace", "aloha", optsEmptyReplace, "aloha"},
   129  		{"Empty Src", "", optsSingleReplace, ""},
   130  		{"Invalid Replace", "aloha", optsInvalidReplace, "aloha"},
   131  		{"Single Replace", "hello world", optsSingleReplace, "goodbye world"},
   132  		{"Mixed Replace", "hello world", optsMixedReplace, "goodbye world"},
   133  		{"No Match", s1, optsSingleReplace, s1},
   134  		{"Single Case-sensitive and Imitation", s0, ReplaceStringOptions{replace0, false, true}, "Hello goodbye word WORLD"},
   135  		{"Single Case-insensitive and Literal", s0, ReplaceStringOptions{replace0, true, false}, "Goodbye Goodbye word WORLD"},
   136  		{"Multiple Case-sensitive and Literal - Replace 1", s1, ReplaceStringOptions{replace1, false, false}, "123DEF6789IJKLMNOPQR888ABCGHYZ888ABCGHABD"},
   137  		{"Multiple Case-sensitive and Literal - Replace 2", s1, ReplaceStringOptions{replace2, false, false}, "222226789IJKLMNOPQRSTUVWXYZSTUVWXABD"},
   138  		{"Multiple Case-sensitive and Imitation - Miss 1", s2, ReplaceStringOptions{replace1, false, true}, s2},
   139  		{"Multiple Case-sensitive and Imitation - Miss 2", s2, ReplaceStringOptions{replace2, false, true}, s2},
   140  		{"Multiple Case-insensitive and Literal - Replace 1", s2, ReplaceStringOptions{replace1, true, false}, "123def6789ijklmnopqr888ABCGHyz888ABCGHabd"},
   141  		{"Multiple Case-insensitive and Literal - Replace 2", s2, ReplaceStringOptions{replace2, true, false}, "222226789ijklmnopqrstuvwxyzstuvwxabd"},
   142  		{"Multiple Case-insensitive and Imitation - Replace 1", s2, ReplaceStringOptions{replace1, true, true}, "123def6789ijklmnopqr888abcghyz888abcghabd"},
   143  		{"Multiple Case-insensitive and Imitation - Replace 2", s2, ReplaceStringOptions{replace2, true, true}, "222226789ijklmnopqrstuvwxyzstuvwxabd"},
   144  		{"Real Case 1", "# standard\n\n```bash\ngo install bitbucket.org/ai69/keiki/kgengo/standard\n```\n", ReplaceStringOptions{replace3, true, true}, "# {{ .appname }}\n\n```bash\ngo install {{ .modulepath }}\n```\n"},
   145  		{"Real Case 2", "https://gitlab.com/ab3C/repo\n", ReplaceStringOptions{replace4, true, true}, "https://gitlab.com/ab3C/repo\n"},
   146  		{"Real Case 3", "https://github.com/abc/repo\n", ReplaceStringOptions{replace4, true, true}, "https://bitbucket.org/def/repo\n"},
   147  		{"Real Case 4", "https://github.com/Hello-World", ReplaceStringOptions{replace4, true, true}, "https://gitlab.com/Good-Bye"},
   148  	}
   149  	for _, tt := range tests {
   150  		t.Run(tt.name, func(t *testing.T) {
   151  			if got := ReplaceString(tt.s, tt.opt); got != tt.want {
   152  				t.Errorf("ReplaceString() = %v, want %v", got, tt.want)
   153  			}
   154  		})
   155  	}
   156  }
   157  
   158  func Test_replaceMultipleString(t *testing.T) {
   159  	rps1 := []*replacePair{
   160  		&replacePair{"STUVWX", "888ABCGH"},
   161  		&replacePair{"STUVW", "999"},
   162  		&replacePair{"ABC", "123"},
   163  		&replacePair{"GH", "6789"},
   164  	}
   165  	rps2 := []*replacePair{
   166  		&replacePair{"ABC", "11"},
   167  		&replacePair{"ABCDEF", "22222"},
   168  		&replacePair{"DEF", "3"},
   169  		&replacePair{"GH", "6789"},
   170  	}
   171  	rps3 := []*replacePair{
   172  		&replacePair{"ABCDEF", "22222"},
   173  		&replacePair{"ABC", "11"},
   174  		&replacePair{"DEF", "3"},
   175  		&replacePair{"GH", "6789"},
   176  	}
   177  	tests := []struct {
   178  		name       string
   179  		s          string
   180  		rps        []*replacePair
   181  		ignoreCase bool
   182  		imitateOld bool
   183  		want       string
   184  	}{
   185  		{"Nil Pairs", "", nil, false, false, ""},
   186  		{"Empty Pairs", "", []*replacePair{}, false, false, ""},
   187  		{"Empty Src", "", []*replacePair{&replacePair{"abc", "123"}}, false, false, ""},
   188  		{"Empty Old", "Aloha", []*replacePair{&replacePair{"", "123"}}, false, false, "Aloha"},
   189  		{"Empty New", "Aloha 123", []*replacePair{&replacePair{"Aloha", ""}}, false, false, " 123"},
   190  		{"Case-sensitive and Literal - Miss", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, false, false, "Hello World"},
   191  		{"Case-sensitive and Literal - Hit", "Hello World", []*replacePair{&replacePair{"Hello", "aloha"}}, false, false, "aloha World"},
   192  		{"Case-sensitive and Imitation - Miss", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, false, true, "Hello World"},
   193  		{"Case-sensitive and Imitation - Hit", "Hello World", []*replacePair{&replacePair{"Hello", "aloha"}}, false, true, "Aloha World"},
   194  		{"Case-insensitive and Literal - Miss", "Hello World", []*replacePair{&replacePair{"halo", "aloha"}}, true, false, "Hello World"},
   195  		{"Case-insensitive and Literal - Hit", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, true, false, "aloha World"},
   196  		{"Case-insensitive and Imitation - Miss", "Hello World", []*replacePair{&replacePair{"halo", "aloha"}}, true, true, "Hello World"},
   197  		{"Case-insensitive and Imitation - Hit", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, true, true, "Aloha World"},
   198  		{"Case-insensitive and Imitation - Order 1", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps1, true, true, "123DEF6789IJKLMNOPQR888ABCGHYZ888ABCGHABD"},
   199  		{"Case-insensitive and Imitation - Order 2", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps2, true, true, "1136789IJKLMNOPQRSTUVWXYZSTUVWXABD"},
   200  		{"Case-insensitive and Imitation - Order 3", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps3, true, true, "222226789IJKLMNOPQRSTUVWXYZSTUVWXABD"},
   201  	}
   202  	for _, tt := range tests {
   203  		t.Run(tt.name, func(t *testing.T) {
   204  			if got := replaceMultipleString(tt.s, tt.rps, tt.ignoreCase, tt.imitateOld); got != tt.want {
   205  				t.Errorf("replaceMultipleString() = %v, want %v", got, tt.want)
   206  			}
   207  		})
   208  	}
   209  }
   210  
   211  func Test_replaceMultipleString_FuzzVerify(t *testing.T) {
   212  	const (
   213  		targetTimes = 50
   214  		srcSize     = 4096
   215  		pairSize    = 10
   216  	)
   217  	gen := func() (string, []string, []*replacePair) {
   218  		s, _ := yrand.StringBase62(srcSize)
   219  		bp := make([]string, 0, pairSize*2)
   220  		rp := make([]*replacePair, 0, pairSize)
   221  		for i := pairSize; i > 0; i-- {
   222  			olds, _ := yrand.StringBase62(i*2 - 1)
   223  			news, _ := yrand.StringBase62(i*2 - 2)
   224  			bp = append(bp, olds, news)
   225  			rp = append(rp, &replacePair{olds, news})
   226  		}
   227  		return s, bp, rp
   228  	}
   229  	for i := 0; i < targetTimes; i++ {
   230  		t.Run(fmt.Sprintf("Try%d", i+1), func(t *testing.T) {
   231  			s, bp, rp := gen()
   232  			replacer := strings.NewReplacer(bp...)
   233  			res1 := replacer.Replace(s)
   234  			res2 := replaceMultipleString(s, rp, false, false)
   235  			if res1 != res2 {
   236  				t.Errorf("replaceMultipleString(%q, %v) got = %q, want %q", s, bp, res2, res1)
   237  			}
   238  		})
   239  	}
   240  }
   241  
   242  func Test_replaceSingleString(t *testing.T) {
   243  	tests := []struct {
   244  		name       string
   245  		s          string
   246  		rp         *replacePair
   247  		ignoreCase bool
   248  		imitateOld bool
   249  		want       string
   250  	}{
   251  		{"Nil Pair", "aloha", nil, false, false, "aloha"},
   252  		{"Empty Src", "", &replacePair{"abc", "123"}, false, false, ""},
   253  		{"Empty Pair", "aloha", &replacePair{"", ""}, false, false, "aloha"},
   254  		{"Empty Old", "aloha", &replacePair{"", "123"}, false, false, "aloha"},
   255  		{"Empty New", "aloha 123", &replacePair{"aloha", ""}, false, false, " 123"},
   256  		{"Case-sensitive and Literal - Miss", "Hello World", &replacePair{"hello", "aloha"}, false, false, "Hello World"},
   257  		{"Case-sensitive and Literal - Hit", "Hello World", &replacePair{"Hello", "aloha"}, false, false, "aloha World"},
   258  		{"Case-insensitive and Literal - Miss", "Hello World", &replacePair{"halo", "aloha"}, true, false, "Hello World"},
   259  		{"Case-insensitive and Literal - Hit", "Hello World", &replacePair{"hello", "aloha"}, true, false, "aloha World"},
   260  		{"Case-sensitive and Imitation - Miss", "Hello World", &replacePair{"hello", "aloha"}, false, true, "Hello World"},
   261  		{"Case-sensitive and Imitation - Hit", "Hello World", &replacePair{"Hello", "aloha"}, false, true, "Aloha World"},
   262  		{"Case-insensitive and Imitation - Miss", "Hello World", &replacePair{"halo", "aloha"}, true, true, "Hello World"},
   263  		{"Case-insensitive and Imitation - Hit", "Hello World", &replacePair{"hello", "aloha"}, true, true, "Aloha World"},
   264  	}
   265  	for _, tt := range tests {
   266  		t.Run(tt.name, func(t *testing.T) {
   267  			if got := replaceSingleString(tt.s, tt.rp, tt.ignoreCase, tt.imitateOld); got != tt.want {
   268  				t.Errorf("replaceSingleString() = %v, want %v", got, tt.want)
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func Test_imitateString(t *testing.T) {
   275  	tests := []struct {
   276  		name string
   277  		src  string
   278  		dest string
   279  		want string
   280  	}{
   281  		{"Empty", "", "", ""},
   282  		{"Empty old", "", "Aloha", "Aloha"},
   283  		{"Empty new", "Aloha", "", ""},
   284  		{"Lower case", "aloha", "HELLO", "hello"},
   285  		{"Upper case", "ALOHA", "hello", "HELLO"},
   286  		{"Title case", "Aloha", "hello", "Hello"},
   287  		{"Title words 1", "Aloha", "hello world", "Hello World"},
   288  		{"Title words 2", "Aloha", "good-job", "Good-Job"},
   289  		{"Title words 3", "Aloha", "loco.moco", "Loco.Moco"},
   290  	}
   291  	for _, tt := range tests {
   292  		t.Run(tt.name, func(t *testing.T) {
   293  			if got := imitateString(tt.src, tt.dest); got != tt.want {
   294  				t.Errorf("imitateString() = %v, want %v", got, tt.want)
   295  			}
   296  		})
   297  	}
   298  }
   299  
   300  func Test_getStringCaseType(t *testing.T) {
   301  	tests := []struct {
   302  		name string
   303  		s    string
   304  		want stringCaseType
   305  	}{
   306  		{"Empty", "", stringCaseMisc},
   307  		{"Single Lower", "a", stringCaseLower},
   308  		{"Word Lower", "aloha", stringCaseLower},
   309  		{"Single Upper", "A", stringCaseUpper},
   310  		{"Word Upper", "ALOHA", stringCaseUpper},
   311  		{"One Word Title", "Aloha", stringCaseTitle},
   312  		{"Two Words Title", "Aloha World", stringCaseTitle},
   313  		{"Dashed Title", "Aloha-World", stringCaseTitle},
   314  		{"Mixed Word 1", "AlohA", stringCaseMisc},
   315  		{"Mixed Word 2", "ALOHa", stringCaseMisc},
   316  		{"Mixed Word 3", "alohA", stringCaseMisc},
   317  		{"Mixed Title 1", "Aloha WORLD", stringCaseMisc},
   318  		{"Mixed Title 2", "Aloha world", stringCaseMisc},
   319  		{"Mixed Title 3", "aloha World", stringCaseMisc},
   320  		{"Mixed Title 4", "ALOHA World", stringCaseMisc},
   321  		{"Mixed Title 5", "AlOha World", stringCaseMisc},
   322  		{"Mixed Title 6", "ALOHA world", stringCaseMisc},
   323  		{"Non-word", "123$%^", stringCaseMisc},
   324  	}
   325  	for _, tt := range tests {
   326  		t.Run(tt.name, func(t *testing.T) {
   327  			if got := getStringCaseType(tt.s); got != tt.want {
   328  				t.Errorf("getStringCaseType(%q) = %v, want %v", tt.s, got, tt.want)
   329  			}
   330  		})
   331  	}
   332  }