Source file src/strconv/quote_test.go

     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  	{0xdead, `'�'`, `'\ufffd'`, `'�'`},
   135  	{0xfffd, `'�'`, `'\ufffd'`, `'�'`},
   136  	{0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`, `'\U0010ffff'`},
   137  	{0x0010ffff + 1, `'�'`, `'\ufffd'`, `'�'`},
   138  	{0x04, `'\x04'`, `'\x04'`, `'\x04'`},
   139  	// Some differences between graphic and printable. Note the last column is double-quoted.
   140  	{'\u00a0', `'\u00a0'`, `'\u00a0'`, "'\u00a0'"},
   141  	{'\u2000', `'\u2000'`, `'\u2000'`, "'\u2000'"},
   142  	{'\u3000', `'\u3000'`, `'\u3000'`, "'\u3000'"},
   143  }
   144  
   145  func TestQuoteRune(t *testing.T) {
   146  	for _, tt := range quoterunetests {
   147  		if out := QuoteRune(tt.in); out != tt.out {
   148  			t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out)
   149  		}
   150  		if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
   151  			t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
   152  		}
   153  	}
   154  }
   155  
   156  func TestQuoteRuneToASCII(t *testing.T) {
   157  	for _, tt := range quoterunetests {
   158  		if out := QuoteRuneToASCII(tt.in); out != tt.ascii {
   159  			t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii)
   160  		}
   161  		if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
   162  			t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
   163  		}
   164  	}
   165  }
   166  
   167  func TestQuoteRuneToGraphic(t *testing.T) {
   168  	for _, tt := range quoterunetests {
   169  		if out := QuoteRuneToGraphic(tt.in); out != tt.graphic {
   170  			t.Errorf("QuoteRuneToGraphic(%U) = %s, want %s", tt.in, out, tt.graphic)
   171  		}
   172  		if out := AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic {
   173  			t.Errorf("AppendQuoteRuneToGraphic(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic)
   174  		}
   175  	}
   176  }
   177  
   178  type canBackquoteTest struct {
   179  	in  string
   180  	out bool
   181  }
   182  
   183  var canbackquotetests = []canBackquoteTest{
   184  	{"`", false},
   185  	{string(rune(0)), false},
   186  	{string(rune(1)), false},
   187  	{string(rune(2)), false},
   188  	{string(rune(3)), false},
   189  	{string(rune(4)), false},
   190  	{string(rune(5)), false},
   191  	{string(rune(6)), false},
   192  	{string(rune(7)), false},
   193  	{string(rune(8)), false},
   194  	{string(rune(9)), true}, // \t
   195  	{string(rune(10)), false},
   196  	{string(rune(11)), false},
   197  	{string(rune(12)), false},
   198  	{string(rune(13)), false},
   199  	{string(rune(14)), false},
   200  	{string(rune(15)), false},
   201  	{string(rune(16)), false},
   202  	{string(rune(17)), false},
   203  	{string(rune(18)), false},
   204  	{string(rune(19)), false},
   205  	{string(rune(20)), false},
   206  	{string(rune(21)), false},
   207  	{string(rune(22)), false},
   208  	{string(rune(23)), false},
   209  	{string(rune(24)), false},
   210  	{string(rune(25)), false},
   211  	{string(rune(26)), false},
   212  	{string(rune(27)), false},
   213  	{string(rune(28)), false},
   214  	{string(rune(29)), false},
   215  	{string(rune(30)), false},
   216  	{string(rune(31)), false},
   217  	{string(rune(0x7F)), false},
   218  	{`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true},
   219  	{`0123456789`, true},
   220  	{`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true},
   221  	{`abcdefghijklmnopqrstuvwxyz`, true},
   222  	{`☺`, true},
   223  	{"\x80", false},
   224  	{"a\xe0\xa0z", false},
   225  	{"\ufeffabc", false},
   226  	{"a\ufeffz", false},
   227  }
   228  
   229  func TestCanBackquote(t *testing.T) {
   230  	for _, tt := range canbackquotetests {
   231  		if out := CanBackquote(tt.in); out != tt.out {
   232  			t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out)
   233  		}
   234  	}
   235  }
   236  
   237  type unQuoteTest struct {
   238  	in  string
   239  	out string
   240  }
   241  
   242  var unquotetests = []unQuoteTest{
   243  	{`""`, ""},
   244  	{`"a"`, "a"},
   245  	{`"abc"`, "abc"},
   246  	{`"☺"`, "☺"},
   247  	{`"hello world"`, "hello world"},
   248  	{`"\xFF"`, "\xFF"},
   249  	{`"\377"`, "\377"},
   250  	{`"\u1234"`, "\u1234"},
   251  	{`"\U00010111"`, "\U00010111"},
   252  	{`"\U0001011111"`, "\U0001011111"},
   253  	{`"\a\b\f\n\r\t\v\\\""`, "\a\b\f\n\r\t\v\\\""},
   254  	{`"'"`, "'"},
   255  
   256  	{`'a'`, "a"},
   257  	{`'☹'`, "☹"},
   258  	{`'\a'`, "\a"},
   259  	{`'\x10'`, "\x10"},
   260  	{`'\377'`, "\377"},
   261  	{`'\u1234'`, "\u1234"},
   262  	{`'\U00010111'`, "\U00010111"},
   263  	{`'\t'`, "\t"},
   264  	{`' '`, " "},
   265  	{`'\''`, "'"},
   266  	{`'"'`, "\""},
   267  
   268  	{"``", ``},
   269  	{"`a`", `a`},
   270  	{"`abc`", `abc`},
   271  	{"`☺`", `☺`},
   272  	{"`hello world`", `hello world`},
   273  	{"`\\xFF`", `\xFF`},
   274  	{"`\\377`", `\377`},
   275  	{"`\\`", `\`},
   276  	{"`\n`", "\n"},
   277  	{"`	`", `	`},
   278  	{"` `", ` `},
   279  	{"`a\rb`", "ab"},
   280  }
   281  
   282  var misquoted = []string{
   283  	``,
   284  	`"`,
   285  	`"a`,
   286  	`"'`,
   287  	`b"`,
   288  	`"\"`,
   289  	`"\9"`,
   290  	`"\19"`,
   291  	`"\129"`,
   292  	`'\'`,
   293  	`'\9'`,
   294  	`'\19'`,
   295  	`'\129'`,
   296  	`'ab'`,
   297  	`"\x1!"`,
   298  	`"\U12345678"`,
   299  	`"\z"`,
   300  	"`",
   301  	"`xxx",
   302  	"``x\r",
   303  	"`\"",
   304  	`"\'"`,
   305  	`'\"'`,
   306  	"\"\n\"",
   307  	"\"\\n\n\"",
   308  	"'\n'",
   309  	`"\udead"`,
   310  	`"\ud83d\ude4f"`,
   311  }
   312  
   313  func TestUnquote(t *testing.T) {
   314  	for _, tt := range unquotetests {
   315  		testUnquote(t, tt.in, tt.out, nil)
   316  	}
   317  	for _, tt := range quotetests {
   318  		testUnquote(t, tt.out, tt.in, nil)
   319  	}
   320  	for _, s := range misquoted {
   321  		testUnquote(t, s, "", ErrSyntax)
   322  	}
   323  }
   324  
   325  // Issue 23685: invalid UTF-8 should not go through the fast path.
   326  func TestUnquoteInvalidUTF8(t *testing.T) {
   327  	tests := []struct {
   328  		in string
   329  
   330  		// one of:
   331  		want    string
   332  		wantErr error
   333  	}{
   334  		{in: `"foo"`, want: "foo"},
   335  		{in: `"foo`, wantErr: ErrSyntax},
   336  		{in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"},
   337  		{in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"},
   338  		{in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"},
   339  	}
   340  	for _, tt := range tests {
   341  		testUnquote(t, tt.in, tt.want, tt.wantErr)
   342  	}
   343  }
   344  
   345  func testUnquote(t *testing.T, in, want string, wantErr error) {
   346  	// Test Unquote.
   347  	got, gotErr := Unquote(in)
   348  	if got != want || gotErr != wantErr {
   349  		t.Errorf("Unquote(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
   350  	}
   351  
   352  	// Test QuotedPrefix.
   353  	// Adding an arbitrary suffix should not change the result of QuotedPrefix
   354  	// assume that the suffix doesn't accidentally terminate a truncated input.
   355  	if gotErr == nil {
   356  		want = in
   357  	}
   358  	suffix := "\n\r\\\"`'" // special characters for quoted strings
   359  	if len(in) > 0 {
   360  		suffix = strings.ReplaceAll(suffix, in[:1], "")
   361  	}
   362  	in += suffix
   363  	got, gotErr = QuotedPrefix(in)
   364  	if gotErr == nil && wantErr != nil {
   365  		_, wantErr = Unquote(got) // original input had trailing junk, reparse with only valid prefix
   366  		want = got
   367  	}
   368  	if got != want || gotErr != wantErr {
   369  		t.Errorf("QuotedPrefix(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
   370  	}
   371  }
   372  
   373  func BenchmarkUnquoteEasy(b *testing.B) {
   374  	for i := 0; i < b.N; i++ {
   375  		Unquote(`"Give me a rock, paper and scissors and I will move the world."`)
   376  	}
   377  }
   378  
   379  func BenchmarkUnquoteHard(b *testing.B) {
   380  	for i := 0; i < b.N; i++ {
   381  		Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`)
   382  	}
   383  }
   384  

View as plain text