1*635a8641SAndroid Build Coastguard Worker // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker
5*635a8641SAndroid Build Coastguard Worker #include "base/json/string_escape.h"
6*635a8641SAndroid Build Coastguard Worker
7*635a8641SAndroid Build Coastguard Worker #include <stddef.h>
8*635a8641SAndroid Build Coastguard Worker
9*635a8641SAndroid Build Coastguard Worker #include "base/macros.h"
10*635a8641SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
11*635a8641SAndroid Build Coastguard Worker #include "base/strings/utf_string_conversions.h"
12*635a8641SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
13*635a8641SAndroid Build Coastguard Worker
14*635a8641SAndroid Build Coastguard Worker namespace base {
15*635a8641SAndroid Build Coastguard Worker
TEST(JSONStringEscapeTest,EscapeUTF8)16*635a8641SAndroid Build Coastguard Worker TEST(JSONStringEscapeTest, EscapeUTF8) {
17*635a8641SAndroid Build Coastguard Worker const struct {
18*635a8641SAndroid Build Coastguard Worker const char* to_escape;
19*635a8641SAndroid Build Coastguard Worker const char* escaped;
20*635a8641SAndroid Build Coastguard Worker } cases[] = {
21*635a8641SAndroid Build Coastguard Worker {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
22*635a8641SAndroid Build Coastguard Worker {"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
23*635a8641SAndroid Build Coastguard Worker {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit.
24*635a8641SAndroid Build Coastguard Worker "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
25*635a8641SAndroid Build Coastguard Worker {"c<>d", "c\\u003C>d"},
26*635a8641SAndroid Build Coastguard Worker {"Hello\xe2\x80\xa8world", "Hello\\u2028world"},
27*635a8641SAndroid Build Coastguard Worker {"\xe2\x80\xa9purple", "\\u2029purple"},
28*635a8641SAndroid Build Coastguard Worker {"\xF3\xBF\xBF\xBF", "\xEF\xBF\xBD"},
29*635a8641SAndroid Build Coastguard Worker };
30*635a8641SAndroid Build Coastguard Worker
31*635a8641SAndroid Build Coastguard Worker for (size_t i = 0; i < arraysize(cases); ++i) {
32*635a8641SAndroid Build Coastguard Worker const char* in_ptr = cases[i].to_escape;
33*635a8641SAndroid Build Coastguard Worker std::string in_str = in_ptr;
34*635a8641SAndroid Build Coastguard Worker
35*635a8641SAndroid Build Coastguard Worker std::string out;
36*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in_ptr, false, &out);
37*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(std::string(cases[i].escaped), out);
38*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
39*635a8641SAndroid Build Coastguard Worker
40*635a8641SAndroid Build Coastguard Worker out.erase();
41*635a8641SAndroid Build Coastguard Worker bool convert_ok = EscapeJSONString(in_str, false, &out);
42*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(std::string(cases[i].escaped), out);
43*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
44*635a8641SAndroid Build Coastguard Worker
45*635a8641SAndroid Build Coastguard Worker if (convert_ok) {
46*635a8641SAndroid Build Coastguard Worker std::string fooout = GetQuotedJSONString(in_str);
47*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", fooout);
48*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
49*635a8641SAndroid Build Coastguard Worker }
50*635a8641SAndroid Build Coastguard Worker }
51*635a8641SAndroid Build Coastguard Worker
52*635a8641SAndroid Build Coastguard Worker std::string in = cases[0].to_escape;
53*635a8641SAndroid Build Coastguard Worker std::string out;
54*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, false, &out);
55*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
56*635a8641SAndroid Build Coastguard Worker
57*635a8641SAndroid Build Coastguard Worker // test quoting
58*635a8641SAndroid Build Coastguard Worker std::string out_quoted;
59*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, true, &out_quoted);
60*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(out.length() + 2, out_quoted.length());
61*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(out_quoted.find(out), 1U);
62*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out_quoted));
63*635a8641SAndroid Build Coastguard Worker
64*635a8641SAndroid Build Coastguard Worker // now try with a NULL in the string
65*635a8641SAndroid Build Coastguard Worker std::string null_prepend = "test";
66*635a8641SAndroid Build Coastguard Worker null_prepend.push_back(0);
67*635a8641SAndroid Build Coastguard Worker in = null_prepend + in;
68*635a8641SAndroid Build Coastguard Worker std::string expected = "test\\u0000";
69*635a8641SAndroid Build Coastguard Worker expected += cases[0].escaped;
70*635a8641SAndroid Build Coastguard Worker out.clear();
71*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, false, &out);
72*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(expected, out);
73*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
74*635a8641SAndroid Build Coastguard Worker }
75*635a8641SAndroid Build Coastguard Worker
TEST(JSONStringEscapeTest,EscapeUTF16)76*635a8641SAndroid Build Coastguard Worker TEST(JSONStringEscapeTest, EscapeUTF16) {
77*635a8641SAndroid Build Coastguard Worker const struct {
78*635a8641SAndroid Build Coastguard Worker const wchar_t* to_escape;
79*635a8641SAndroid Build Coastguard Worker const char* escaped;
80*635a8641SAndroid Build Coastguard Worker } cases[] = {
81*635a8641SAndroid Build Coastguard Worker {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"},
82*635a8641SAndroid Build Coastguard Worker {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
83*635a8641SAndroid Build Coastguard Worker {L"a\b\f\n\r\t\v\1\\.\"z",
84*635a8641SAndroid Build Coastguard Worker "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
85*635a8641SAndroid Build Coastguard Worker {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"},
86*635a8641SAndroid Build Coastguard Worker {L"c<>d", "c\\u003C>d"},
87*635a8641SAndroid Build Coastguard Worker {L"Hello\u2028world", "Hello\\u2028world"},
88*635a8641SAndroid Build Coastguard Worker {L"\u2029purple", "\\u2029purple"},
89*635a8641SAndroid Build Coastguard Worker };
90*635a8641SAndroid Build Coastguard Worker
91*635a8641SAndroid Build Coastguard Worker for (size_t i = 0; i < arraysize(cases); ++i) {
92*635a8641SAndroid Build Coastguard Worker string16 in = WideToUTF16(cases[i].to_escape);
93*635a8641SAndroid Build Coastguard Worker
94*635a8641SAndroid Build Coastguard Worker std::string out;
95*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, false, &out);
96*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(std::string(cases[i].escaped), out);
97*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
98*635a8641SAndroid Build Coastguard Worker
99*635a8641SAndroid Build Coastguard Worker out = GetQuotedJSONString(in);
100*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", out);
101*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
102*635a8641SAndroid Build Coastguard Worker }
103*635a8641SAndroid Build Coastguard Worker
104*635a8641SAndroid Build Coastguard Worker string16 in = WideToUTF16(cases[0].to_escape);
105*635a8641SAndroid Build Coastguard Worker std::string out;
106*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, false, &out);
107*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
108*635a8641SAndroid Build Coastguard Worker
109*635a8641SAndroid Build Coastguard Worker // test quoting
110*635a8641SAndroid Build Coastguard Worker std::string out_quoted;
111*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, true, &out_quoted);
112*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(out.length() + 2, out_quoted.length());
113*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(out_quoted.find(out), 1U);
114*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
115*635a8641SAndroid Build Coastguard Worker
116*635a8641SAndroid Build Coastguard Worker // now try with a NULL in the string
117*635a8641SAndroid Build Coastguard Worker string16 null_prepend = WideToUTF16(L"test");
118*635a8641SAndroid Build Coastguard Worker null_prepend.push_back(0);
119*635a8641SAndroid Build Coastguard Worker in = null_prepend + in;
120*635a8641SAndroid Build Coastguard Worker std::string expected = "test\\u0000";
121*635a8641SAndroid Build Coastguard Worker expected += cases[0].escaped;
122*635a8641SAndroid Build Coastguard Worker out.clear();
123*635a8641SAndroid Build Coastguard Worker EscapeJSONString(in, false, &out);
124*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(expected, out);
125*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(IsStringUTF8(out));
126*635a8641SAndroid Build Coastguard Worker }
127*635a8641SAndroid Build Coastguard Worker
TEST(JSONStringEscapeTest,EscapeUTF16OutsideBMP)128*635a8641SAndroid Build Coastguard Worker TEST(JSONStringEscapeTest, EscapeUTF16OutsideBMP) {
129*635a8641SAndroid Build Coastguard Worker {
130*635a8641SAndroid Build Coastguard Worker // {a, U+10300, !}, SMP.
131*635a8641SAndroid Build Coastguard Worker string16 test;
132*635a8641SAndroid Build Coastguard Worker test.push_back('a');
133*635a8641SAndroid Build Coastguard Worker test.push_back(0xD800);
134*635a8641SAndroid Build Coastguard Worker test.push_back(0xDF00);
135*635a8641SAndroid Build Coastguard Worker test.push_back('!');
136*635a8641SAndroid Build Coastguard Worker std::string actual;
137*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(EscapeJSONString(test, false, &actual));
138*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("a\xF0\x90\x8C\x80!", actual);
139*635a8641SAndroid Build Coastguard Worker }
140*635a8641SAndroid Build Coastguard Worker {
141*635a8641SAndroid Build Coastguard Worker // {U+20021, U+2002B}, SIP.
142*635a8641SAndroid Build Coastguard Worker string16 test;
143*635a8641SAndroid Build Coastguard Worker test.push_back(0xD840);
144*635a8641SAndroid Build Coastguard Worker test.push_back(0xDC21);
145*635a8641SAndroid Build Coastguard Worker test.push_back(0xD840);
146*635a8641SAndroid Build Coastguard Worker test.push_back(0xDC2B);
147*635a8641SAndroid Build Coastguard Worker std::string actual;
148*635a8641SAndroid Build Coastguard Worker EXPECT_TRUE(EscapeJSONString(test, false, &actual));
149*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("\xF0\xA0\x80\xA1\xF0\xA0\x80\xAB", actual);
150*635a8641SAndroid Build Coastguard Worker }
151*635a8641SAndroid Build Coastguard Worker {
152*635a8641SAndroid Build Coastguard Worker // {?, U+D800, @}, lone surrogate.
153*635a8641SAndroid Build Coastguard Worker string16 test;
154*635a8641SAndroid Build Coastguard Worker test.push_back('?');
155*635a8641SAndroid Build Coastguard Worker test.push_back(0xD800);
156*635a8641SAndroid Build Coastguard Worker test.push_back('@');
157*635a8641SAndroid Build Coastguard Worker std::string actual;
158*635a8641SAndroid Build Coastguard Worker EXPECT_FALSE(EscapeJSONString(test, false, &actual));
159*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("?\xEF\xBF\xBD@", actual);
160*635a8641SAndroid Build Coastguard Worker }
161*635a8641SAndroid Build Coastguard Worker }
162*635a8641SAndroid Build Coastguard Worker
TEST(JSONStringEscapeTest,EscapeBytes)163*635a8641SAndroid Build Coastguard Worker TEST(JSONStringEscapeTest, EscapeBytes) {
164*635a8641SAndroid Build Coastguard Worker const struct {
165*635a8641SAndroid Build Coastguard Worker const char* to_escape;
166*635a8641SAndroid Build Coastguard Worker const char* escaped;
167*635a8641SAndroid Build Coastguard Worker } cases[] = {
168*635a8641SAndroid Build Coastguard Worker {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
169*635a8641SAndroid Build Coastguard Worker {"\xe5\xc4\x4f\x05\xb6\xfd", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"},
170*635a8641SAndroid Build Coastguard Worker };
171*635a8641SAndroid Build Coastguard Worker
172*635a8641SAndroid Build Coastguard Worker for (size_t i = 0; i < arraysize(cases); ++i) {
173*635a8641SAndroid Build Coastguard Worker std::string in = std::string(cases[i].to_escape);
174*635a8641SAndroid Build Coastguard Worker EXPECT_FALSE(IsStringUTF8(in));
175*635a8641SAndroid Build Coastguard Worker
176*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(std::string(cases[i].escaped),
177*635a8641SAndroid Build Coastguard Worker EscapeBytesAsInvalidJSONString(in, false));
178*635a8641SAndroid Build Coastguard Worker EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"",
179*635a8641SAndroid Build Coastguard Worker EscapeBytesAsInvalidJSONString(in, true));
180*635a8641SAndroid Build Coastguard Worker }
181*635a8641SAndroid Build Coastguard Worker
182*635a8641SAndroid Build Coastguard Worker const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' };
183*635a8641SAndroid Build Coastguard Worker std::string in(kEmbedNull, arraysize(kEmbedNull));
184*635a8641SAndroid Build Coastguard Worker EXPECT_FALSE(IsStringUTF8(in));
185*635a8641SAndroid Build Coastguard Worker EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"),
186*635a8641SAndroid Build Coastguard Worker EscapeBytesAsInvalidJSONString(in, false));
187*635a8641SAndroid Build Coastguard Worker }
188*635a8641SAndroid Build Coastguard Worker
189*635a8641SAndroid Build Coastguard Worker } // namespace base
190