1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#include <google/protobuf/util/internal/json_objectwriter.h>
32
33#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
34#include <google/protobuf/util/internal/utility.h>
35#include <gtest/gtest.h>
36
37namespace google {
38namespace protobuf {
39namespace util {
40namespace converter {
41
42using google::protobuf::io::CodedOutputStream;
43using google::protobuf::io::StringOutputStream;
44
45class JsonObjectWriterTest : public ::testing::Test {
46 protected:
47  JsonObjectWriterTest()
48      : str_stream_(new StringOutputStream(&output_)),
49        out_stream_(new CodedOutputStream(str_stream_)),
50        ow_(NULL) {}
51
52  virtual ~JsonObjectWriterTest() {
53    delete ow_;
54    delete out_stream_;
55    delete str_stream_;
56  }
57
58  string output_;
59  StringOutputStream* const str_stream_;
60  CodedOutputStream* const out_stream_;
61  JsonObjectWriter* ow_;
62};
63
64TEST_F(JsonObjectWriterTest, EmptyRootObject) {
65  ow_ = new JsonObjectWriter("", out_stream_);
66  ow_->StartObject("")->EndObject();
67  EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount()));
68}
69
70TEST_F(JsonObjectWriterTest, EmptyObject) {
71  ow_ = new JsonObjectWriter("", out_stream_);
72  ow_->StartObject("")
73      ->RenderString("test", "value")
74      ->StartObject("empty")
75      ->EndObject()
76      ->EndObject();
77  EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}",
78            output_.substr(0, out_stream_->ByteCount()));
79}
80
81TEST_F(JsonObjectWriterTest, EmptyRootList) {
82  ow_ = new JsonObjectWriter("", out_stream_);
83  ow_->StartList("")->EndList();
84  EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount()));
85}
86
87TEST_F(JsonObjectWriterTest, EmptyList) {
88  ow_ = new JsonObjectWriter("", out_stream_);
89  ow_->StartObject("")
90      ->RenderString("test", "value")
91      ->StartList("empty")
92      ->EndList()
93      ->EndObject();
94  EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}",
95            output_.substr(0, out_stream_->ByteCount()));
96}
97
98TEST_F(JsonObjectWriterTest, ObjectInObject) {
99  ow_ = new JsonObjectWriter("", out_stream_);
100  ow_->StartObject("")
101      ->StartObject("nested")
102      ->RenderString("field", "value")
103      ->EndObject()
104      ->EndObject();
105  EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}",
106            output_.substr(0, out_stream_->ByteCount()));
107}
108
109TEST_F(JsonObjectWriterTest, ListInObject) {
110  ow_ = new JsonObjectWriter("", out_stream_);
111  ow_->StartObject("")
112      ->StartList("nested")
113      ->RenderString("", "value")
114      ->EndList()
115      ->EndObject();
116  EXPECT_EQ("{\"nested\":[\"value\"]}",
117            output_.substr(0, out_stream_->ByteCount()));
118}
119
120TEST_F(JsonObjectWriterTest, ObjectInList) {
121  ow_ = new JsonObjectWriter("", out_stream_);
122  ow_->StartList("")
123      ->StartObject("")
124      ->RenderString("field", "value")
125      ->EndObject()
126      ->EndList();
127  EXPECT_EQ("[{\"field\":\"value\"}]",
128            output_.substr(0, out_stream_->ByteCount()));
129}
130
131TEST_F(JsonObjectWriterTest, ListInList) {
132  ow_ = new JsonObjectWriter("", out_stream_);
133  ow_->StartList("")
134      ->StartList("")
135      ->RenderString("", "value")
136      ->EndList()
137      ->EndList();
138  EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount()));
139}
140
141TEST_F(JsonObjectWriterTest, RenderPrimitives) {
142  ow_ = new JsonObjectWriter("", out_stream_);
143  ow_->StartObject("")
144      ->RenderBool("bool", true)
145      ->RenderDouble("double", std::numeric_limits<double>::max())
146      ->RenderFloat("float", std::numeric_limits<float>::max())
147      ->RenderInt32("int", std::numeric_limits<int32>::min())
148      ->RenderInt64("long", std::numeric_limits<int64>::min())
149      ->RenderBytes("bytes", "abracadabra")
150      ->RenderString("string", "string")
151      ->RenderBytes("emptybytes", "")
152      ->RenderString("emptystring", string())
153      ->EndObject();
154  EXPECT_EQ(
155      "{\"bool\":true,"
156      "\"double\":" +
157          ValueAsString<double>(std::numeric_limits<double>::max()) +
158          ","
159          "\"float\":" +
160          ValueAsString<float>(std::numeric_limits<float>::max()) +
161          ","
162          "\"int\":-2147483648,"
163          "\"long\":\"-9223372036854775808\","
164          "\"bytes\":\"YWJyYWNhZGFicmE=\","
165          "\"string\":\"string\","
166          "\"emptybytes\":\"\","
167          "\"emptystring\":\"\"}",
168      output_.substr(0, out_stream_->ByteCount()));
169}
170
171TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) {
172  string s;
173  s.push_back('\377');
174  s.push_back('\357');
175  ow_ = new JsonObjectWriter("", out_stream_);
176  ow_->StartObject("")->RenderBytes("bytes", s)->EndObject();
177  // Non-web-safe would encode this as "/+8="
178  EXPECT_EQ("{\"bytes\":\"/+8=\"}",
179            output_.substr(0, out_stream_->ByteCount()));
180}
181
182TEST_F(JsonObjectWriterTest, PrettyPrintList) {
183  ow_ = new JsonObjectWriter(" ", out_stream_);
184  ow_->StartObject("")
185      ->StartList("items")
186      ->RenderString("", "item1")
187      ->RenderString("", "item2")
188      ->RenderString("", "item3")
189      ->EndList()
190      ->StartList("empty")
191      ->EndList()
192      ->EndObject();
193  EXPECT_EQ(
194      "{\n"
195      " \"items\": [\n"
196      "  \"item1\",\n"
197      "  \"item2\",\n"
198      "  \"item3\"\n"
199      " ],\n"
200      " \"empty\": []\n"
201      "}\n",
202      output_.substr(0, out_stream_->ByteCount()));
203}
204
205TEST_F(JsonObjectWriterTest, PrettyPrintObject) {
206  ow_ = new JsonObjectWriter(" ", out_stream_);
207  ow_->StartObject("")
208      ->StartObject("items")
209      ->RenderString("key1", "item1")
210      ->RenderString("key2", "item2")
211      ->RenderString("key3", "item3")
212      ->EndObject()
213      ->StartObject("empty")
214      ->EndObject()
215      ->EndObject();
216  EXPECT_EQ(
217      "{\n"
218      " \"items\": {\n"
219      "  \"key1\": \"item1\",\n"
220      "  \"key2\": \"item2\",\n"
221      "  \"key3\": \"item3\"\n"
222      " },\n"
223      " \"empty\": {}\n"
224      "}\n",
225      output_.substr(0, out_stream_->ByteCount()));
226}
227
228TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) {
229  ow_ = new JsonObjectWriter(" ", out_stream_);
230  ow_->StartObject("")
231      ->StartList("list")
232      ->StartObject("")
233      ->EndObject()
234      ->EndList()
235      ->EndObject();
236  EXPECT_EQ(
237      "{\n"
238      " \"list\": [\n"
239      "  {}\n"
240      " ]\n"
241      "}\n",
242      output_.substr(0, out_stream_->ByteCount()));
243}
244
245TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) {
246  ow_ = new JsonObjectWriter("  ", out_stream_);
247  ow_->StartObject("")
248      ->RenderBool("bool", true)
249      ->RenderInt32("int", 42)
250      ->EndObject();
251  EXPECT_EQ(
252      "{\n"
253      "  \"bool\": true,\n"
254      "  \"int\": 42\n"
255      "}\n",
256      output_.substr(0, out_stream_->ByteCount()));
257}
258
259TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
260  ow_ = new JsonObjectWriter("", out_stream_);
261  ow_->StartObject("")->RenderString("string", "'<>&amp;\\\"\r\n")->EndObject();
262  EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&amp;\\\\\\\"\\r\\n\"}",
263            output_.substr(0, out_stream_->ByteCount()));
264}
265
266TEST_F(JsonObjectWriterTest, Stringification) {
267  ow_ = new JsonObjectWriter("", out_stream_);
268  ow_->StartObject("")
269      ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
270      ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
271      ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
272      ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
273      ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
274      ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
275      ->EndObject();
276  EXPECT_EQ(
277      "{\"double_nan\":\"NaN\","
278      "\"float_nan\":\"NaN\","
279      "\"double_pos\":\"Infinity\","
280      "\"float_pos\":\"Infinity\","
281      "\"double_neg\":\"-Infinity\","
282      "\"float_neg\":\"-Infinity\"}",
283      output_.substr(0, out_stream_->ByteCount()));
284}
285
286TEST_F(JsonObjectWriterTest, TestRegularByteEncoding) {
287  ow_ = new JsonObjectWriter("", out_stream_);
288  ow_->StartObject("")
289      ->RenderBytes("bytes", "\x03\xef\xc0")
290      ->EndObject();
291
292  // Test that we get regular (non websafe) base64 encoding on byte fields by
293  // default.
294  EXPECT_EQ("{\"bytes\":\"A+/A\"}",
295            output_.substr(0, out_stream_->ByteCount()));
296}
297
298TEST_F(JsonObjectWriterTest, TestWebsafeByteEncoding) {
299  ow_ = new JsonObjectWriter("", out_stream_);
300  ow_->set_use_websafe_base64_for_bytes(true);
301  ow_->StartObject("")
302      ->RenderBytes("bytes", "\x03\xef\xc0")
303      ->EndObject();
304
305  // Test that we get websafe base64 encoding when explicitly asked.
306  EXPECT_EQ("{\"bytes\":\"A-_A\"}",
307            output_.substr(0, out_stream_->ByteCount()));
308}
309
310}  // namespace converter
311}  // namespace util
312}  // namespace protobuf
313}  // namespace google
314