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