github.com/segmentio/encoding@v0.4.0/ascii/ascii_test.go (about)

     1  package ascii
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  	"unicode/utf8"
     9  )
    10  
    11  var testStrings = [...]string{
    12  	"",
    13  	"a",
    14  	"ab",
    15  	"abc",
    16  	"abcd",
    17  	"hello",
    18  	"Hello World!",
    19  	"Hello\"World!",
    20  	"Hello\\World!",
    21  	"Hello\nWorld!",
    22  	"Hello\rWorld!",
    23  	"Hello\tWorld!",
    24  	"Hello\bWorld!",
    25  	"Hello\fWorld!",
    26  	"H~llo World!",
    27  	"H~llo",
    28  	"你好",
    29  	"~",
    30  	"\x80",
    31  	"\x7F",
    32  	"\xFF",
    33  	"\x1fxxx",
    34  	"\x1fxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    35  	"a string of 16B.",
    36  	"an invalid string of 32B. \x00......",
    37  	"some kind of long string with only ascii characters.",
    38  	"some kind of long string with a non-ascii character at the end.\xff",
    39  	strings.Repeat("1234567890", 1000),
    40  }
    41  
    42  var testStringsUTF8 []string
    43  
    44  func init() {
    45  	for _, test := range testStrings {
    46  		if utf8.ValidString(test) {
    47  			testStringsUTF8 = append(testStringsUTF8, test)
    48  		}
    49  	}
    50  }
    51  
    52  func testString(s string, f func(byte) bool) bool {
    53  	for i := range s {
    54  		if !f(s[i]) {
    55  			return false
    56  		}
    57  	}
    58  	return true
    59  }
    60  
    61  func testValid(s string) bool {
    62  	return testString(s, ValidByte)
    63  }
    64  
    65  func testValidPrint(s string) bool {
    66  	return testString(s, ValidPrintByte)
    67  }
    68  
    69  func TestValid(t *testing.T) {
    70  	testValidationFunction(t, testValid, ValidString)
    71  }
    72  
    73  func TestValidPrint(t *testing.T) {
    74  	testValidationFunction(t, testValidPrint, ValidPrintString)
    75  }
    76  
    77  func testValidationFunction(t *testing.T, reference, function func(string) bool) {
    78  	for _, test := range testStrings {
    79  		t.Run(limit(test), func(t *testing.T) {
    80  			expect := reference(test)
    81  
    82  			if valid := function(test); expect != valid {
    83  				t.Errorf("expected %t but got %t", expect, valid)
    84  			}
    85  		})
    86  	}
    87  }
    88  
    89  func BenchmarkValid(b *testing.B) {
    90  	benchmarkValidationFunction(b, ValidString)
    91  }
    92  
    93  func BenchmarkValidPrint(b *testing.B) {
    94  	benchmarkValidationFunction(b, ValidPrintString)
    95  }
    96  
    97  func benchmarkValidationFunction(b *testing.B, function func(string) bool) {
    98  	for _, test := range testStrings {
    99  		b.Run(limit(test), func(b *testing.B) {
   100  			for i := 0; i < b.N; i++ {
   101  				_ = function(test)
   102  			}
   103  			b.SetBytes(int64(len(test)))
   104  		})
   105  	}
   106  }
   107  
   108  func limit(s string) string {
   109  	if len(s) > 17 {
   110  		return s[:17] + "..."
   111  	}
   112  	return s
   113  }
   114  
   115  func TestHasPrefixFold(t *testing.T) {
   116  	for _, test := range testStringsUTF8 {
   117  		t.Run(limit(test), func(t *testing.T) {
   118  			prefix := test
   119  			if len(prefix) > 0 {
   120  				prefix = prefix[:len(prefix)/2]
   121  			}
   122  			upper := strings.ToUpper(prefix)
   123  			lower := strings.ToLower(prefix)
   124  
   125  			if !HasPrefixFoldString(test, prefix) {
   126  				t.Errorf("%q does not match %q", test, prefix)
   127  			}
   128  
   129  			if !HasPrefixFoldString(test, upper) {
   130  				t.Errorf("%q does not match %q", test, upper)
   131  			}
   132  
   133  			if !HasPrefixFoldString(test, lower) {
   134  				t.Errorf("%q does not match %q", test, lower)
   135  			}
   136  		})
   137  	}
   138  }
   139  
   140  func TestHasSuffixFold(t *testing.T) {
   141  	for _, test := range testStringsUTF8 {
   142  		t.Run(limit(test), func(t *testing.T) {
   143  			suffix := test
   144  			if len(suffix) > 0 {
   145  				suffix = suffix[len(suffix)/2:]
   146  			}
   147  			upper := strings.ToUpper(suffix)
   148  			lower := strings.ToLower(suffix)
   149  
   150  			if !HasSuffixFoldString(test, suffix) {
   151  				t.Errorf("%q does not match %q", test, suffix)
   152  			}
   153  
   154  			if !HasSuffixFoldString(test, upper) {
   155  				t.Errorf("%q does not match %q", test, upper)
   156  			}
   157  
   158  			if !HasSuffixFoldString(test, lower) {
   159  				t.Errorf("%q does not match %q", test, lower)
   160  			}
   161  		})
   162  	}
   163  }
   164  
   165  func TestEqualFoldASCII(t *testing.T) {
   166  	pairs := [...][2]byte{
   167  		{0, ' '},
   168  		{'@', '`'},
   169  		{'[', '{'},
   170  		{'_', 127},
   171  	}
   172  
   173  	for _, pair := range pairs {
   174  		t.Run(fmt.Sprintf("0x%02x=0x%02x", pair[0], pair[1]), func(t *testing.T) {
   175  			for i := 1; i <= 256; i++ {
   176  				a := bytes.Repeat([]byte{'x'}, i)
   177  				b := bytes.Repeat([]byte{'X'}, i)
   178  
   179  				if !EqualFold(a, b) {
   180  					t.Errorf("%q does not match %q", a, b)
   181  					break
   182  				}
   183  
   184  				a[0] = pair[0]
   185  				b[0] = pair[1]
   186  
   187  				if EqualFold(a, b) {
   188  					t.Errorf("%q matches %q", a, b)
   189  					break
   190  				}
   191  			}
   192  		})
   193  	}
   194  }
   195  
   196  func TestEqualFold(t *testing.T) {
   197  	// Only test valid UTF-8 otherwise ToUpper/ToLower will convert invalid
   198  	// characters to UTF-8 placeholders, which breaks the case-insensitive
   199  	// equality.
   200  	for _, test := range testStringsUTF8 {
   201  		t.Run(limit(test), func(t *testing.T) {
   202  			upper := strings.ToUpper(test)
   203  			lower := strings.ToLower(test)
   204  
   205  			if !EqualFoldString(test, test) {
   206  				t.Errorf("%q does not match %q", test, test)
   207  			}
   208  
   209  			if !EqualFoldString(test, upper) {
   210  				t.Errorf("%q does not match %q", test, upper)
   211  			}
   212  
   213  			if !EqualFoldString(test, lower) {
   214  				t.Errorf("%q does not match %q", test, lower)
   215  			}
   216  
   217  			if len(test) > 1 {
   218  				reverse := make([]byte, len(test))
   219  				for i := range reverse {
   220  					reverse[i] = test[len(test)-(i+1)]
   221  				}
   222  
   223  				if EqualFoldString(test, string(reverse)) {
   224  					t.Errorf("%q matches %q", test, reverse)
   225  				}
   226  			}
   227  		})
   228  	}
   229  }
   230  
   231  func BenchmarkEqualFold(b *testing.B) {
   232  	for _, test := range testStringsUTF8 {
   233  		b.Run(limit(test), func(b *testing.B) {
   234  			other := test + "_" // not the same pointer
   235  
   236  			for i := 0; i < b.N; i++ {
   237  				_ = EqualFoldString(test, other[:len(test)]) // same length
   238  			}
   239  
   240  			b.SetBytes(int64(len(test)))
   241  		})
   242  	}
   243  }