Black Lives Matter. Support the Equal Justice Initiative.

Source file src/strconv/quote_test.go

Documentation: strconv

     1  // Copyright 2009 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 strconv_test
     6  
     7  import (
     8  	. "strconv"
     9  	"strings"
    10  	"testing"
    11  	"unicode"
    12  )
    13  
    14  // Verify that our IsPrint agrees with unicode.IsPrint.
    15  func TestIsPrint(t *testing.T) {
    16  	n := 0
    17  	for r := rune(0); r <= unicode.MaxRune; r++ {
    18  		if IsPrint(r) != unicode.IsPrint(r) {
    19  			t.Errorf("IsPrint(%U)=%t incorrect", r, IsPrint(r))
    20  			n++
    21  			if n > 10 {
    22  				return
    23  			}
    24  		}
    25  	}
    26  }
    27  
    28  // Verify that our IsGraphic agrees with unicode.IsGraphic.
    29  func TestIsGraphic(t *testing.T) {
    30  	n := 0
    31  	for r := rune(0); r <= unicode.MaxRune; r++ {
    32  		if IsGraphic(r) != unicode.IsGraphic(r) {
    33  			t.Errorf("IsGraphic(%U)=%t incorrect", r, IsGraphic(r))
    34  			n++
    35  			if n > 10 {
    36  				return
    37  			}
    38  		}
    39  	}
    40  }
    41  
    42  type quoteTest struct {
    43  	in      string
    44  	out     string
    45  	ascii   string
    46  	graphic string
    47  }
    48  
    49  var quotetests = []quoteTest{
    50  	{"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`},
    51  	{"\\", `"\\"`, `"\\"`, `"\\"`},
    52  	{"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`, `"abc\xffdef"`},
    53  	{"\u263a", `"☺"`, `"\u263a"`, `"☺"`},
    54  	{"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`, `"\U0010ffff"`},
    55  	{"\x04", `"\x04"`, `"\x04"`, `"\x04"`},
    56  	// Some non-printable but graphic runes. Final column is double-quoted.
    57  	{"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""},
    58  }
    59  
    60  func TestQuote(t *testing.T) {
    61  	for _, tt := range quotetests {
    62  		if out := Quote(tt.in); out != tt.out {
    63  			t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out)
    64  		}
    65  		if out := AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
    66  			t.Errorf("AppendQuote(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
    67  		}
    68  	}
    69  }
    70  
    71  func TestQuoteToASCII(t *testing.T) {
    72  	for _, tt := range quotetests {
    73  		if out := QuoteToASCII(tt.in); out != tt.ascii {
    74  			t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii)
    75  		}
    76  		if out := AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
    77  			t.Errorf("AppendQuoteToASCII(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
    78  		}
    79  	}
    80  }
    81  
    82  func TestQuoteToGraphic(t *testing.T) {
    83  	for _, tt := range quotetests {
    84  		if out := QuoteToGraphic(tt.in); out != tt.graphic {
    85  			t.Errorf("QuoteToGraphic(%s) = %s, want %s", tt.in, out, tt.graphic)
    86  		}
    87  		if out := AppendQuoteToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic {
    88  			t.Errorf("AppendQuoteToGraphic(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic)
    89  		}
    90  	}
    91  }
    92  
    93  func BenchmarkQuote(b *testing.B) {
    94  	for i := 0; i < b.N; i++ {
    95  		Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
    96  	}
    97  }
    98  
    99  func BenchmarkQuoteRune(b *testing.B) {
   100  	for i := 0; i < b.N; i++ {
   101  		QuoteRune('\a')
   102  	}
   103  }
   104  
   105  var benchQuoteBuf []byte
   106  
   107  func BenchmarkAppendQuote(b *testing.B) {
   108  	for i := 0; i < b.N; i++ {
   109  		benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
   110  	}
   111  }
   112  
   113  var benchQuoteRuneBuf []byte
   114  
   115  func BenchmarkAppendQuoteRune(b *testing.B) {
   116  	for i := 0; i < b.N; i++ {
   117  		benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a')
   118  	}
   119  }
   120  
   121  type quoteRuneTest struct {
   122  	in      rune
   123  	out     string
   124  	ascii   string
   125  	graphic string
   126  }
   127  
   128  var quoterunetests = []quoteRuneTest{
   129  	{'a', `'a'`, `'a'`, `'a'`},
   130  	{'\a', `'\a'`, `'\a'`, `'\a'`},
   131  	{'\\', `'\\'`, `'\\'`, `'\\'`},
   132  	{0xFF, `'ÿ'`, `'\u00ff'`, `'ÿ'`},
   133  	{0x263a, `'☺'`, `'\u263a'`, `'☺'`},
   134  	{0xfffd, `'�'`, `'\ufffd'`, `'�'`},
   135  	{0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`, `'\U0010ffff'`},
   136  	{0x0010ffff + 1, `'�'`, `'\ufffd'`, `'�'`},
   137  	{0x04, `'\x04'`, `'\x04'`, `'\x04'`},
   138  	// Some differences between graphic and printable. Note the last column is double-quoted.
   139  	{'\u00a0', `'\u00a0'`, `'\u00a0'`, "'\u00a0'"},
   140  	{'\u2000', `'\u2000'`, `'\u2000'`, "'\u2000'"},
   141  	{'\u3000', `'\u3000'`, `'\u3000'`, "'\u3000'"},
   142  }
   143  
   144  func TestQuoteRune(t *testing.T) {
   145  	for _, tt := range quoterunetests {
   146  		if out := QuoteRune(tt.in); out != tt.out {
   147  			t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out)
   148  		}
   149  		if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
   150  			t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
   151  		}
   152  	}
   153  }
   154  
   155  func TestQuoteRuneToASCII(t *testing.T) {
   156  	for _, tt := range quoterunetests {
   157  		if out := QuoteRuneToASCII(tt.in); out != tt.ascii {
   158  			t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii)
   159  		}
   160  		if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
   161  			t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
   162  		}
   163  	}
   164  }
   165  
   166  func TestQuoteRuneToGraphic(t *testing.T) {
   167  	for _, tt := range quoterunetests {
   168  		if out := QuoteRuneToGraphic(tt.in); out != tt.graphic {
   169  			t.Errorf("QuoteRuneToGraphic(%U) = %s, want %s", tt.in, out, tt.graphic)
   170  		}
   171  		if out := AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic {
   172  			t.Errorf("AppendQuoteRuneToGraphic(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic)
   173  		}
   174  	}
   175  }
   176  
   177  type canBackquoteTest struct {
   178  	in  string
   179  	out bool
   180  }
   181  
   182  var canbackquotetests = []canBackquoteTest{
   183  	{"`", false},
   184  	{string(rune(0)), false},
   185  	{string(rune(1)), false},
   186  	{string(rune(2)), false},
   187  	{string(rune(3)), false},
   188  	{string(rune(4)), false},
   189  	{string(rune(5)), false},
   190  	{string(rune(6)), false},
   191  	{string(rune(7)), false},
   192  	{string(rune(8)), false},
   193  	{string(rune(9)), true}, // \t
   194  	{string(rune(10)), false},
   195  	{string(rune(11)), false},
   196  	{string(rune(12)), false},
   197  	{string(rune(13)), false},
   198  	{string(rune(14)), false},
   199  	{string(rune(15)), false},
   200  	{string(rune(16)), false},
   201  	{string(rune(17)), false},
   202  	{string(rune(18)), false},
   203  	{string(rune(19)), false},
   204  	{string(rune(20)), false},
   205  	{string(rune(21)), false},
   206  	{string(rune(22)), false},
   207  	{string(rune(23)), false},
   208  	{string(rune(24)), false},
   209  	{string(rune(25)), false},
   210  	{string(rune(26)), false},
   211  	{string(rune(27)), false},
   212  	{string(rune(28)), false},
   213  	{string(rune(29)), false},
   214  	{string(rune(30)), false},
   215  	{string(rune(31)), false},
   216  	{string(rune(0x7F)), false},
   217  	{`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true},
   218  	{`0123456789`, true},
   219  	{`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true},
   220  	{`abcdefghijklmnopqrstuvwxyz`, true},
   221  	{`☺`, true},
   222  	{"\x80", false},
   223  	{"a\xe0\xa0z", false},
   224  	{"\ufeffabc", false},
   225  	{"a\ufeffz", false},
   226  }
   227  
   228  func TestCanBackquote(t *testing.T) {
   229  	for _, tt := range canbackquotetests {
   230  		if out := CanBackquote(tt.in); out != tt.out {
   231  			t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out)
   232  		}
   233  	}
   234  }
   235  
   236  type unQuoteTest struct {
   237  	in  string
   238  	out string
   239  }
   240  
   241  var unquotetests = []unQuoteTest{
   242  	{`""`, ""},
   243  	{`"a"`, "a"},
   244  	{`"abc"`, "abc"},
   245  	{`"☺"`, "☺"},
   246  	{`"hello world"`, "hello world"},
   247  	{`"\xFF"`, "\xFF"},
   248  	{`"\377"`, "\377"},
   249  	{`"\u1234"`, "\u1234"},
   250  	{`"\U00010111"`, "\U00010111"},
   251  	{`"\U0001011111"`, "\U0001011111"},
   252  	{`"\a\b\f\n\r\t\v\\\""`, "\a\b\f\n\r\t\v\\\""},
   253  	{`"'"`, "'"},
   254  
   255  	{`'a'`, "a"},
   256  	{`'☹'`, "☹"},
   257  	{`'\a'`, "\a"},
   258  	{`'\x10'`, "\x10"},
   259  	{`'\377'`, "\377"},
   260  	{`'\u1234'`, "\u1234"},
   261  	{`'\U00010111'`, "\U00010111"},
   262  	{`'\t'`, "\t"},
   263  	{`' '`, " "},
   264  	{`'\''`, "'"},
   265  	{`'"'`, "\""},
   266  
   267  	{"``", ``},
   268  	{"`a`", `a`},
   269  	{"`abc`", `abc`},
   270  	{"`☺`", `☺`},
   271  	{"`hello world`", `hello world`},
   272  	{"`\\xFF`", `\xFF`},
   273  	{"`\\377`", `\377`},
   274  	{"`\\`", `\`},
   275  	{"`\n`", "\n"},
   276  	{"`	`", `	`},
   277  	{"` `", ` `},
   278  	{"`a\rb`", "ab"},
   279  }
   280  
   281  var misquoted = []string{
   282  	``,
   283  	`"`,
   284  	`"a`,
   285  	`"'`,
   286  	`b"`,
   287  	`"\"`,
   288  	`"\9"`,
   289  	`"\19"`,
   290  	`"\129"`,
   291  	`'\'`,
   292  	`'\9'`,
   293  	`'\19'`,
   294  	`'\129'`,
   295  	`'ab'`,
   296  	`"\x1!"`,
   297  	`"\U12345678"`,
   298  	`"\z"`,
   299  	"`",
   300  	"`xxx",
   301  	"``x\r",
   302  	"`\"",
   303  	`"\'"`,
   304  	`'\"'`,
   305  	"\"\n\"",
   306  	"\"\\n\n\"",
   307  	"'\n'",
   308  }
   309  
   310  func TestUnquote(t *testing.T) {
   311  	for _, tt := range unquotetests {
   312  		testUnquote(t, tt.in, tt.out, nil)
   313  	}
   314  	for _, tt := range quotetests {
   315  		testUnquote(t, tt.out, tt.in, nil)
   316  	}
   317  	for _, s := range misquoted {
   318  		testUnquote(t, s, "", ErrSyntax)
   319  	}
   320  }
   321  
   322  // Issue 23685: invalid UTF-8 should not go through the fast path.
   323  func TestUnquoteInvalidUTF8(t *testing.T) {
   324  	tests := []struct {
   325  		in string
   326  
   327  		// one of:
   328  		want    string
   329  		wantErr error
   330  	}{
   331  		{in: `"foo"`, want: "foo"},
   332  		{in: `"foo`, wantErr: ErrSyntax},
   333  		{in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"},
   334  		{in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"},
   335  		{in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"},
   336  	}
   337  	for _, tt := range tests {
   338  		testUnquote(t, tt.in, tt.want, tt.wantErr)
   339  	}
   340  }
   341  
   342  func testUnquote(t *testing.T, in, want string, wantErr error) {
   343  	// Test Unquote.
   344  	got, gotErr := Unquote(in)
   345  	if got != want || gotErr != wantErr {
   346  		t.Errorf("Unquote(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
   347  	}
   348  
   349  	// Test QuotedPrefix.
   350  	// Adding an arbitrary suffix should not change the result of QuotedPrefix
   351  	// assume that the suffix doesn't accidentally terminate a truncated input.
   352  	if gotErr == nil {
   353  		want = in
   354  	}
   355  	suffix := "\n\r\\\"`'" // special characters for quoted strings
   356  	if len(in) > 0 {
   357  		suffix = strings.ReplaceAll(suffix, in[:1], "")
   358  	}
   359  	in += suffix
   360  	got, gotErr = QuotedPrefix(in)
   361  	if gotErr == nil && wantErr != nil {
   362  		_, wantErr = Unquote(got) // original input had trailing junk, reparse with only valid prefix
   363  		want = got
   364  	}
   365  	if got != want || gotErr != wantErr {
   366  		t.Errorf("QuotedPrefix(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
   367  	}
   368  }
   369  
   370  func BenchmarkUnquoteEasy(b *testing.B) {
   371  	for i := 0; i < b.N; i++ {
   372  		Unquote(`"Give me a rock, paper and scissors and I will move the world."`)
   373  	}
   374  }
   375  
   376  func BenchmarkUnquoteHard(b *testing.B) {
   377  	for i := 0; i < b.N; i++ {
   378  		Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`)
   379  	}
   380  }
   381  

View as plain text