1d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/string_escape.h"
6d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
7d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)#include "base/strings/string_util.h"
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)TEST(JSONStringEscapeTest, EscapeUTF8) {
14d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  const struct {
15d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* to_escape;
16d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* escaped;
17d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  } cases[] = {
18d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
19d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"a\b\f\n\r\t\v\1\\.\"z",
20d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
21d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"b\x0f\x7f\xf0\xff!",  // \xf0\xff is not a valid UTF-8 unit.
22d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
23d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"c<>d", "c\\u003C>d"},
24d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  };
25d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
26d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
27d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* in_ptr = cases[i].to_escape;
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    std::string in_str = in_ptr;
29d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string out;
31d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EscapeJSONString(in_ptr, false, &out);
32d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ(std::string(cases[i].escaped), out);
33d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(IsStringUTF8(out));
34d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    out.erase();
36d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    bool convert_ok = EscapeJSONString(in_str, false, &out);
37d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ(std::string(cases[i].escaped), out);
38d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(IsStringUTF8(out));
39d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
40d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    if (convert_ok) {
41d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)      std::string fooout = GetQuotedJSONString(in_str);
42d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)      EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", fooout);
43d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)      EXPECT_TRUE(IsStringUTF8(out));
44d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    }
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  std::string in = cases[0].to_escape;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string out;
49d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, false, &out);
50d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out));
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // test quoting
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string out_quoted;
54d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, true, &out_quoted);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(out.length() + 2, out_quoted.length());
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(out_quoted.find(out), 1U);
57d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out_quoted));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // now try with a NULL in the string
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string null_prepend = "test";
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  null_prepend.push_back(0);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in = null_prepend + in;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string expected = "test\\u0000";
64d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  expected += cases[0].escaped;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  out.clear();
66d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, false, &out);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(expected, out);
68d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out));
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
71d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)TEST(JSONStringEscapeTest, EscapeUTF16) {
72d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  const struct {
73d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const wchar_t* to_escape;
74d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* escaped;
75d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  } cases[] = {
76d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"},
77d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
78d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {L"a\b\f\n\r\t\v\1\\.\"z",
79d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
80d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"},
81d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {L"c<>d", "c\\u003C>d"},
82d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  };
83d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
84d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
85d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    string16 in = WideToUTF16(cases[i].to_escape);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string out;
88d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EscapeJSONString(in, false, &out);
89d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ(std::string(cases[i].escaped), out);
90d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(IsStringUTF8(out));
91d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
92d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    out = GetQuotedJSONString(in);
93d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", out);
94d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(IsStringUTF8(out));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
97d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  string16 in = WideToUTF16(cases[0].to_escape);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string out;
99d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, false, &out);
100d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out));
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // test quoting
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string out_quoted;
104d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, true, &out_quoted);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(out.length() + 2, out_quoted.length());
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(out_quoted.find(out), 1U);
107d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out));
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // now try with a NULL in the string
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 null_prepend = WideToUTF16(L"test");
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  null_prepend.push_back(0);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in = null_prepend + in;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string expected = "test\\u0000";
114d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  expected += cases[0].escaped;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  out.clear();
116d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EscapeJSONString(in, false, &out);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(expected, out);
118d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_TRUE(IsStringUTF8(out));
119d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)}
120d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
121d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)TEST(JSONStringEscapeTest, EscapeUTF16OutsideBMP) {
122d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  {
123d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    // {a, U+10300, !}, SMP.
124d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    string16 test;
125d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back('a');
126d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xD800);
127d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xDF00);
128d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back('!');
129d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    std::string actual;
130d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(EscapeJSONString(test, false, &actual));
131d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ("a\xF0\x90\x8C\x80!", actual);
132d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
133d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  {
134d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    // {U+20021, U+2002B}, SIP.
135d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    string16 test;
136d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xD840);
137d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xDC21);
138d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xD840);
139d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xDC2B);
140d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    std::string actual;
141d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_TRUE(EscapeJSONString(test, false, &actual));
142d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ("\xF0\xA0\x80\xA1\xF0\xA0\x80\xAB", actual);
143d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
144d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  {
145d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    // {?, U+D800, @}, lone surrogate.
146d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    string16 test;
147d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back('?');
148d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back(0xD800);
149d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    test.push_back('@');
150d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    std::string actual;
151d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_FALSE(EscapeJSONString(test, false, &actual));
152d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ("?\xEF\xBF\xBD@", actual);
153d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
154d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)}
155d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
156d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)TEST(JSONStringEscapeTest, EscapeBytes) {
157d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  const struct {
158d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* to_escape;
159d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const char* escaped;
160d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  } cases[] = {
161d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
162d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    {"\xe5\xc4\x4f\x05\xb6\xfd\0", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"},
163d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  };
164d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
165d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
166d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    std::string in = std::string(cases[i].to_escape);
167d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_FALSE(IsStringUTF8(in));
168d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
169d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ(std::string(cases[i].escaped),
170d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        EscapeBytesAsInvalidJSONString(in, false));
171d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"",
172d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        EscapeBytesAsInvalidJSONString(in, true));
173d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
174d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
175d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' };
176d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  std::string in(kEmbedNull, ARRAYSIZE_UNSAFE(kEmbedNull));
177d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_FALSE(IsStringUTF8(in));
178d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"),
179d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)            EscapeBytesAsInvalidJSONString(in, false));
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace base
183