blob: 2ffc923abfc33200ce50bf37c5229f0e33904fd9 [file] [log] [blame]
Yifan Hong4d68f662019-02-13 14:29:33 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18#include <limits>
19
20#include <sstream>
21
22#include <gmock/gmock.h>
23#include <gtest/gtest.h>
24#include <json/writer.h>
25#include <jsonpb/jsonpb.h>
26#include <jsonpb/verify.h>
27
28#include "test.pb.h"
29
30using ::android::jsonpb::internal::FormatJson;
31using ::testing::ElementsAre;
32using ::testing::HasSubstr;
33
34namespace android {
35namespace jsonpb {
36
37// Unit tests for libjsonpbverify.
38
39class LibJsonpbVerifyTest : public ::testing::Test {};
40
41class JsonKeyTest : public LibJsonpbVerifyTest {
42 public:
43 template <typename T>
44 std::string GetFieldJsonName(const std::string& field_name) {
45 return T{}.GetDescriptor()->FindFieldByName(field_name)->json_name();
46 }
47
48 template <typename T>
49 void TestParseOkWithUnknownKey(const std::string& field_name,
50 const std::string& json_key) {
51 std::string json = "{\"" + json_key + "\": \"test\"}";
52 auto object = JsonStringToMessage<T>(json);
53 ASSERT_TRUE(object.ok()) << object.error();
54 EXPECT_EQ(
55 "test",
56 object->GetReflection()->GetString(
57 *object, object->GetDescriptor()->FindFieldByName(field_name)));
58 std::string error;
59 ASSERT_FALSE(AllFieldsAreKnown(*object, json, &error))
60 << "AllFieldsAreKnown should return false";
61 EXPECT_THAT(error, HasSubstr("unknown keys"));
62 EXPECT_THAT(error, HasSubstr(json_key));
63 }
64};
65
66TEST_F(JsonKeyTest, WithJsonNameOk) {
67 std::string json =
68 "{\n"
69 " \"FOOBAR\": \"foo_bar\",\n"
70 " \"BarBaz\": \"barBaz\",\n"
71 " \"baz_qux\": \"BazQux\",\n"
72 " \"quxQuux\": \"QUX_QUUX\"\n"
73 "\n}";
74 auto object = JsonStringToMessage<WithJsonName>(json);
75 ASSERT_TRUE(object.ok()) << object.error();
76
77 EXPECT_EQ("foo_bar", object->foo_bar());
78 EXPECT_EQ("barBaz", object->barbaz());
79 EXPECT_EQ("BazQux", object->bazqux());
80 EXPECT_EQ("QUX_QUUX", object->qux_quux());
81
82 std::string error;
83 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
84}
85
86// If Prototype field name as keys while json_name is present, AllFieldsAreKnown
87// should return false.
88TEST_F(JsonKeyTest, WithJsonNameFooBar) {
89 TestParseOkWithUnknownKey<WithJsonName>("foo_bar", "foo_bar");
90}
91
92TEST_F(JsonKeyTest, WithJsonNameBarBaz) {
93 TestParseOkWithUnknownKey<WithJsonName>("barBaz", "barBaz");
94}
95
96TEST_F(JsonKeyTest, WithJsonNameBazQux) {
97 TestParseOkWithUnknownKey<WithJsonName>("BazQux", "BazQux");
98}
99
100TEST_F(JsonKeyTest, WithJsonNameQuxQuux) {
101 TestParseOkWithUnknownKey<WithJsonName>("QUX_QUUX", "QUX_QUUX");
102}
103
104// JSON field name matches Proto field name
105TEST_F(JsonKeyTest, NoJsonNameOk) {
106 std::string json =
107 "{\n"
108 " \"foo_bar\": \"foo_bar\",\n"
109 " \"barBaz\": \"barBaz\",\n"
110 " \"BazQux\": \"BazQux\",\n"
111 " \"QUX_QUUX\": \"QUX_QUUX\"\n"
112 "\n}";
113 auto object = JsonStringToMessage<NoJsonName>(json);
114 ASSERT_TRUE(object.ok()) << object.error();
115
116 EXPECT_EQ("foo_bar", object->foo_bar());
117 EXPECT_EQ("barBaz", object->barbaz());
118 EXPECT_EQ("BazQux", object->bazqux());
119 EXPECT_EQ("QUX_QUUX", object->qux_quux());
120
121 std::string error;
122 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
123}
124
125// JSON field name is lowerCamelCase of Proto field name;
126// AllFieldsAreKnown should return false. Although the lowerCamelCase name is a
127// valid key accepted by Protobuf's JSON parser, we explicitly disallow the
128// behavior.
129TEST_F(JsonKeyTest, NoJsonNameFooBar) {
130 EXPECT_EQ("fooBar", GetFieldJsonName<NoJsonName>("foo_bar"));
131 TestParseOkWithUnknownKey<NoJsonName>("foo_bar", "fooBar");
132}
133
134TEST_F(JsonKeyTest, NoJsonNameBarBaz) {
135 EXPECT_EQ("barBaz", GetFieldJsonName<NoJsonName>("barBaz"));
136 // No test for barBaz because its JSON name is the same as field_name
137}
138
139TEST_F(JsonKeyTest, NoJsonNameBazQux) {
140 EXPECT_EQ("bazQux", GetFieldJsonName<NoJsonName>("BazQux"));
141 TestParseOkWithUnknownKey<NoJsonName>("BazQux", "bazQux");
142}
143
144TEST_F(JsonKeyTest, NoJsonNameQuxQuux) {
145 EXPECT_EQ("qUXQUUX", GetFieldJsonName<NoJsonName>("QUX_QUUX"));
146 TestParseOkWithUnknownKey<NoJsonName>("QUX_QUUX", "qUXQUUX");
147}
148
149class EmbeddedJsonKeyTest : public LibJsonpbVerifyTest {
150 public:
151 ErrorOr<Parent> TestEmbeddedError(const std::string& json,
152 const std::string& unknown_key) {
153 auto object = JsonStringToMessage<Parent>(json);
154 if (!object.ok()) return object;
155 std::string error;
156 EXPECT_FALSE(AllFieldsAreKnown(*object, json, &error))
157 << "AllFieldsAreKnown should return false";
158 EXPECT_THAT(error, HasSubstr("unknown keys"));
159 EXPECT_THAT(error, HasSubstr(unknown_key));
160 return object;
161 }
162};
163
164TEST_F(EmbeddedJsonKeyTest, Ok) {
165 std::string json =
166 "{"
167 " \"with_json_name\": {\"FOOBAR\": \"foo_bar\"},\n"
168 " \"repeated_with_json_name\": [{\"BarBaz\": \"barBaz\"}],\n"
169 " \"no_json_name\": {\"BazQux\": \"BazQux\"},\n"
170 " \"repeated_no_json_name\": [{\"QUX_QUUX\": \"QUX_QUUX\"}]\n"
171 "}";
172 auto object = JsonStringToMessage<Parent>(json);
173 ASSERT_TRUE(object.ok()) << object.error();
174
175 EXPECT_EQ("foo_bar", object->with_json_name().foo_bar());
176 ASSERT_EQ(1u, object->repeated_with_json_name().size());
177 EXPECT_EQ("barBaz", object->repeated_with_json_name().begin()->barbaz());
178 EXPECT_EQ("BazQux", object->no_json_name().bazqux());
179 ASSERT_EQ(1u, object->repeated_no_json_name().size());
180 EXPECT_EQ("QUX_QUUX", object->repeated_no_json_name().begin()->qux_quux());
181
182 std::string error;
183 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
184}
185
186TEST_F(EmbeddedJsonKeyTest, FooBar) {
187 auto object = TestEmbeddedError(
188 "{\"with_json_name\": {\"foo_bar\": \"test\"}}", "foo_bar");
189 ASSERT_TRUE(object.ok()) << object.error();
190 EXPECT_EQ("test", object->with_json_name().foo_bar());
191}
192
193TEST_F(EmbeddedJsonKeyTest, BarBaz) {
194 auto object = TestEmbeddedError(
195 "{\"repeated_with_json_name\": [{\"barBaz\": \"test\"}]}", "barBaz");
196 ASSERT_TRUE(object.ok()) << object.error();
197 ASSERT_EQ(1u, object->repeated_with_json_name().size());
198 EXPECT_EQ("test", object->repeated_with_json_name().begin()->barbaz());
199}
200
201TEST_F(EmbeddedJsonKeyTest, BazQux) {
202 auto object =
203 TestEmbeddedError("{\"no_json_name\": {\"bazQux\": \"test\"}}", "bazQux");
204 ASSERT_TRUE(object.ok()) << object.error();
205 EXPECT_EQ("test", object->no_json_name().bazqux());
206}
207
208TEST_F(EmbeddedJsonKeyTest, QuxQuux) {
209 auto object = TestEmbeddedError(
210 "{\"repeated_no_json_name\": [{\"qUXQUUX\": \"test\"}]}", "qUXQUUX");
211 ASSERT_TRUE(object.ok()) << object.error();
212 ASSERT_EQ(1u, object->repeated_no_json_name().size());
213 EXPECT_EQ("test", object->repeated_no_json_name().begin()->qux_quux());
214}
215
216class ScalarTest : public LibJsonpbVerifyTest {
217 public:
218 ::testing::AssertionResult IsJsonEq(const std::string& l,
219 const std::string& r) {
220 Json::Reader reader;
221 Json::Value lvalue;
222 if (!reader.parse(l, lvalue))
223 return ::testing::AssertionFailure()
224 << reader.getFormattedErrorMessages();
225 Json::Value rvalue;
226 if (!reader.parse(r, rvalue))
227 return ::testing::AssertionFailure()
228 << reader.getFormattedErrorMessages();
229 Json::StyledWriter writer;
230 return lvalue == rvalue
231 ? (::testing::AssertionSuccess() << "Both are \n"
232 << writer.write(lvalue))
233 : (::testing::AssertionFailure()
234 << writer.write(lvalue) << "\n does not equal \n"
235 << writer.write(rvalue));
236 }
237
238 bool EqReformattedJson(const std::string& json, std::string* error) {
239 return android::jsonpb::EqReformattedJson(json, &scalar_, error);
240 }
241
242 Scalar scalar_;
243 std::string error_;
244};
245
246TEST_F(ScalarTest, Ok) {
247 std::string json =
248 "{\n"
249 " \"i32\": 1,\n"
250 " \"si32\": 1,\n"
251 " \"i64\": \"1\",\n"
252 " \"si64\": \"1\",\n"
253 " \"f\": 1.5,\n"
254 " \"d\": 1.5,\n"
255 " \"e\": \"FOO\"\n"
256 "}";
257 auto formatted = FormatJson(json, &scalar_);
258 ASSERT_TRUE(formatted.ok()) << formatted.error();
259 EXPECT_TRUE(IsJsonEq(json, *formatted));
260
261 EXPECT_TRUE(EqReformattedJson(json, &error_)) << error_;
262}
263
264using ScalarTestErrorParam = std::tuple<const char*, const char*>;
265class ScalarTestError
266 : public ScalarTest,
267 public ::testing::WithParamInterface<ScalarTestErrorParam> {};
268
269TEST_P(ScalarTestError, Test) {
270 std::string json;
271 std::string message;
272 std::tie(json, message) = GetParam();
273 auto formatted = FormatJson(json, &scalar_);
274 ASSERT_TRUE(formatted.ok()) << formatted.error();
275 EXPECT_FALSE(IsJsonEq(json, *formatted)) << message;
276 EXPECT_FALSE(EqReformattedJson(json, &error_))
277 << "EqReformattedJson should return false";
278}
279
280static const std::vector<ScalarTestErrorParam> gScalarTestErrorParams = {
281 {"{\"i32\": \"1\"}", "Should not allow int32 values to be quoted"},
282 {"{\"si32\": \"1\"}", "Should not allow sint32 values to be quoted"},
283 {"{\"i64\": 1}", "Should require int64 values to be quoted"},
284 {"{\"si64\": 1}", "Should require sint64 values to be quoted"},
285 {"{\"f\": \"1.5\"}", "Should not allow float values to be quoted"},
286 {"{\"d\": \"1.5\"}", "Should not allow double values to be quoted"},
287 {"{\"e\": 1}", "Should not allow integers for enums"},
288};
289
290INSTANTIATE_TEST_SUITE_P(, ScalarTestError,
291 ::testing::ValuesIn(gScalarTestErrorParams));
292
293int main(int argc, char** argv) {
294 using ::testing::AddGlobalTestEnvironment;
295 using ::testing::InitGoogleTest;
296
297 InitGoogleTest(&argc, argv);
298 return RUN_ALL_TESTS();
299}
300
301} // namespace jsonpb
302} // namespace android