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

View as plain text