blob: 2c69d6c3633ab68dc9d15bc4b1c61a5c80a36ca7 [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
Yifan Hong4d68f662019-02-13 14:29:33 -080017#include <limits>
18
19#include <sstream>
20
21#include <gmock/gmock.h>
22#include <gtest/gtest.h>
23#include <json/writer.h>
24#include <jsonpb/jsonpb.h>
25#include <jsonpb/verify.h>
26
27#include "test.pb.h"
28
29using ::android::jsonpb::internal::FormatJson;
30using ::testing::ElementsAre;
31using ::testing::HasSubstr;
32
33namespace android {
34namespace jsonpb {
35
36// Unit tests for libjsonpbverify.
37
38class LibJsonpbVerifyTest : public ::testing::Test {};
39
40class JsonKeyTest : public LibJsonpbVerifyTest {
41 public:
42 template <typename T>
43 std::string GetFieldJsonName(const std::string& field_name) {
44 return T{}.GetDescriptor()->FindFieldByName(field_name)->json_name();
45 }
46
47 template <typename T>
Thiébaud Weksteenf5397482020-10-26 15:44:13 +010048 void TestParseOkWithUnknownKey(const std::string& field_name, const std::string& json_key) {
Yifan Hong4d68f662019-02-13 14:29:33 -080049 std::string json = "{\"" + json_key + "\": \"test\"}";
50 auto object = JsonStringToMessage<T>(json);
51 ASSERT_TRUE(object.ok()) << object.error();
Thiébaud Weksteenf5397482020-10-26 15:44:13 +010052 EXPECT_EQ("test", object->GetReflection()->GetString(
53 *object, object->GetDescriptor()->FindFieldByName(field_name)));
Yifan Hong4d68f662019-02-13 14:29:33 -080054 std::string error;
55 ASSERT_FALSE(AllFieldsAreKnown(*object, json, &error))
56 << "AllFieldsAreKnown should return false";
57 EXPECT_THAT(error, HasSubstr("unknown keys"));
58 EXPECT_THAT(error, HasSubstr(json_key));
59 }
60};
61
62TEST_F(JsonKeyTest, WithJsonNameOk) {
63 std::string json =
64 "{\n"
65 " \"FOOBAR\": \"foo_bar\",\n"
66 " \"BarBaz\": \"barBaz\",\n"
67 " \"baz_qux\": \"BazQux\",\n"
68 " \"quxQuux\": \"QUX_QUUX\"\n"
69 "\n}";
70 auto object = JsonStringToMessage<WithJsonName>(json);
71 ASSERT_TRUE(object.ok()) << object.error();
72
73 EXPECT_EQ("foo_bar", object->foo_bar());
74 EXPECT_EQ("barBaz", object->barbaz());
75 EXPECT_EQ("BazQux", object->bazqux());
76 EXPECT_EQ("QUX_QUUX", object->qux_quux());
77
78 std::string error;
79 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
80}
81
82// If Prototype field name as keys while json_name is present, AllFieldsAreKnown
83// should return false.
84TEST_F(JsonKeyTest, WithJsonNameFooBar) {
85 TestParseOkWithUnknownKey<WithJsonName>("foo_bar", "foo_bar");
86}
87
88TEST_F(JsonKeyTest, WithJsonNameBarBaz) {
89 TestParseOkWithUnknownKey<WithJsonName>("barBaz", "barBaz");
90}
91
92TEST_F(JsonKeyTest, WithJsonNameBazQux) {
93 TestParseOkWithUnknownKey<WithJsonName>("BazQux", "BazQux");
94}
95
96TEST_F(JsonKeyTest, WithJsonNameQuxQuux) {
97 TestParseOkWithUnknownKey<WithJsonName>("QUX_QUUX", "QUX_QUUX");
98}
99
100// JSON field name matches Proto field name
101TEST_F(JsonKeyTest, NoJsonNameOk) {
102 std::string json =
103 "{\n"
104 " \"foo_bar\": \"foo_bar\",\n"
105 " \"barBaz\": \"barBaz\",\n"
106 " \"BazQux\": \"BazQux\",\n"
107 " \"QUX_QUUX\": \"QUX_QUUX\"\n"
108 "\n}";
109 auto object = JsonStringToMessage<NoJsonName>(json);
110 ASSERT_TRUE(object.ok()) << object.error();
111
112 EXPECT_EQ("foo_bar", object->foo_bar());
113 EXPECT_EQ("barBaz", object->barbaz());
114 EXPECT_EQ("BazQux", object->bazqux());
115 EXPECT_EQ("QUX_QUUX", object->qux_quux());
116
117 std::string error;
118 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
119}
120
Yifan Hongf9929cb2019-08-27 13:51:42 -0700121// JSON field name is lower/UpperCamelCase of Proto field name;
122// AllFieldsAreKnown should return false. Although the lower/UpperCamelCase name
123// is a valid key accepted by Protobuf's JSON parser, we explicitly disallow the
Yifan Hong4d68f662019-02-13 14:29:33 -0800124// behavior.
125TEST_F(JsonKeyTest, NoJsonNameFooBar) {
126 EXPECT_EQ("fooBar", GetFieldJsonName<NoJsonName>("foo_bar"));
127 TestParseOkWithUnknownKey<NoJsonName>("foo_bar", "fooBar");
128}
129
130TEST_F(JsonKeyTest, NoJsonNameBarBaz) {
131 EXPECT_EQ("barBaz", GetFieldJsonName<NoJsonName>("barBaz"));
132 // No test for barBaz because its JSON name is the same as field_name
133}
134
135TEST_F(JsonKeyTest, NoJsonNameBazQux) {
Yifan Hongf9929cb2019-08-27 13:51:42 -0700136 EXPECT_EQ("BazQux", GetFieldJsonName<NoJsonName>("BazQux"));
137 // No test for BazQux because its JSON name is the same as field_name
Yifan Hong4d68f662019-02-13 14:29:33 -0800138}
139
140TEST_F(JsonKeyTest, NoJsonNameQuxQuux) {
Yifan Hongf9929cb2019-08-27 13:51:42 -0700141 EXPECT_EQ("QUXQUUX", GetFieldJsonName<NoJsonName>("QUX_QUUX"));
142 TestParseOkWithUnknownKey<NoJsonName>("QUX_QUUX", "QUXQUUX");
Yifan Hong4d68f662019-02-13 14:29:33 -0800143}
144
145class EmbeddedJsonKeyTest : public LibJsonpbVerifyTest {
146 public:
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100147 ErrorOr<Parent> TestEmbeddedError(const std::string& json, const std::string& unknown_key) {
Yifan Hong4d68f662019-02-13 14:29:33 -0800148 auto object = JsonStringToMessage<Parent>(json);
149 if (!object.ok()) return object;
150 std::string error;
151 EXPECT_FALSE(AllFieldsAreKnown(*object, json, &error))
152 << "AllFieldsAreKnown should return false";
153 EXPECT_THAT(error, HasSubstr("unknown keys"));
154 EXPECT_THAT(error, HasSubstr(unknown_key));
155 return object;
156 }
157};
158
159TEST_F(EmbeddedJsonKeyTest, Ok) {
160 std::string json =
161 "{"
162 " \"with_json_name\": {\"FOOBAR\": \"foo_bar\"},\n"
163 " \"repeated_with_json_name\": [{\"BarBaz\": \"barBaz\"}],\n"
164 " \"no_json_name\": {\"BazQux\": \"BazQux\"},\n"
165 " \"repeated_no_json_name\": [{\"QUX_QUUX\": \"QUX_QUUX\"}]\n"
166 "}";
167 auto object = JsonStringToMessage<Parent>(json);
168 ASSERT_TRUE(object.ok()) << object.error();
169
170 EXPECT_EQ("foo_bar", object->with_json_name().foo_bar());
171 ASSERT_EQ(1u, object->repeated_with_json_name().size());
172 EXPECT_EQ("barBaz", object->repeated_with_json_name().begin()->barbaz());
173 EXPECT_EQ("BazQux", object->no_json_name().bazqux());
174 ASSERT_EQ(1u, object->repeated_no_json_name().size());
175 EXPECT_EQ("QUX_QUUX", object->repeated_no_json_name().begin()->qux_quux());
176
177 std::string error;
178 EXPECT_TRUE(AllFieldsAreKnown(*object, json, &error)) << error;
179}
180
181TEST_F(EmbeddedJsonKeyTest, FooBar) {
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100182 auto object = TestEmbeddedError("{\"with_json_name\": {\"foo_bar\": \"test\"}}", "foo_bar");
Yifan Hong4d68f662019-02-13 14:29:33 -0800183 ASSERT_TRUE(object.ok()) << object.error();
184 EXPECT_EQ("test", object->with_json_name().foo_bar());
185}
186
187TEST_F(EmbeddedJsonKeyTest, BarBaz) {
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100188 auto object =
189 TestEmbeddedError("{\"repeated_with_json_name\": [{\"barBaz\": \"test\"}]}", "barBaz");
Yifan Hong4d68f662019-02-13 14:29:33 -0800190 ASSERT_TRUE(object.ok()) << object.error();
191 ASSERT_EQ(1u, object->repeated_with_json_name().size());
192 EXPECT_EQ("test", object->repeated_with_json_name().begin()->barbaz());
193}
194
Yifan Hongf9929cb2019-08-27 13:51:42 -0700195TEST_F(EmbeddedJsonKeyTest, NoJsonName) {
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100196 auto object = TestEmbeddedError("{\"no_json_name\": {\"QUXQUUX\": \"test\"}}", "QUXQUUX");
Yifan Hong4d68f662019-02-13 14:29:33 -0800197 ASSERT_TRUE(object.ok()) << object.error();
Yifan Hongf9929cb2019-08-27 13:51:42 -0700198 EXPECT_EQ("test", object->no_json_name().qux_quux());
Yifan Hong4d68f662019-02-13 14:29:33 -0800199}
200
201TEST_F(EmbeddedJsonKeyTest, QuxQuux) {
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100202 auto object =
203 TestEmbeddedError("{\"repeated_no_json_name\": [{\"QUXQUUX\": \"test\"}]}", "QUXQUUX");
Yifan Hong4d68f662019-02-13 14:29:33 -0800204 ASSERT_TRUE(object.ok()) << object.error();
205 ASSERT_EQ(1u, object->repeated_no_json_name().size());
206 EXPECT_EQ("test", object->repeated_no_json_name().begin()->qux_quux());
207}
208
209class ScalarTest : public LibJsonpbVerifyTest {
210 public:
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100211 ::testing::AssertionResult IsJsonEq(const std::string& l, const std::string& r) {
Haibo Huangc9cf7ab2021-02-24 17:43:12 -0800212 Json::CharReaderBuilder builder;
213 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Yifan Hong4d68f662019-02-13 14:29:33 -0800214 Json::Value lvalue;
Haibo Huangc9cf7ab2021-02-24 17:43:12 -0800215 std::string errorMessage;
216 if (!reader->parse(&*l.begin(), &*l.end(), &lvalue, &errorMessage))
217 return ::testing::AssertionFailure() << errorMessage;
Yifan Hong4d68f662019-02-13 14:29:33 -0800218 Json::Value rvalue;
Haibo Huangc9cf7ab2021-02-24 17:43:12 -0800219 if (!reader->parse(&*r.begin(), &*r.end(), &rvalue, &errorMessage))
220 return ::testing::AssertionFailure() << errorMessage;
221 Json::StreamWriterBuilder factory;
Yifan Hong4d68f662019-02-13 14:29:33 -0800222 return lvalue == rvalue
223 ? (::testing::AssertionSuccess() << "Both are \n"
Haibo Huangc9cf7ab2021-02-24 17:43:12 -0800224 << Json::writeString(factory, lvalue))
225 : (::testing::AssertionFailure() << Json::writeString(factory, lvalue)
226 << "\n does not equal \n"
227 << Json::writeString(factory, rvalue));
Yifan Hong4d68f662019-02-13 14:29:33 -0800228 }
229
230 bool EqReformattedJson(const std::string& json, std::string* error) {
231 return android::jsonpb::EqReformattedJson(json, &scalar_, error);
232 }
233
234 Scalar scalar_;
235 std::string error_;
236};
237
238TEST_F(ScalarTest, Ok) {
239 std::string json =
240 "{\n"
241 " \"i32\": 1,\n"
242 " \"si32\": 1,\n"
243 " \"i64\": \"1\",\n"
244 " \"si64\": \"1\",\n"
245 " \"f\": 1.5,\n"
246 " \"d\": 1.5,\n"
247 " \"e\": \"FOO\"\n"
248 "}";
249 auto formatted = FormatJson(json, &scalar_);
250 ASSERT_TRUE(formatted.ok()) << formatted.error();
251 EXPECT_TRUE(IsJsonEq(json, *formatted));
252
253 EXPECT_TRUE(EqReformattedJson(json, &error_)) << error_;
254}
255
256using ScalarTestErrorParam = std::tuple<const char*, const char*>;
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100257class ScalarTestError : public ScalarTest,
258 public ::testing::WithParamInterface<ScalarTestErrorParam> {};
Yifan Hong4d68f662019-02-13 14:29:33 -0800259
260TEST_P(ScalarTestError, Test) {
261 std::string json;
262 std::string message;
263 std::tie(json, message) = GetParam();
264 auto formatted = FormatJson(json, &scalar_);
265 ASSERT_TRUE(formatted.ok()) << formatted.error();
266 EXPECT_FALSE(IsJsonEq(json, *formatted)) << message;
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100267 EXPECT_FALSE(EqReformattedJson(json, &error_)) << "EqReformattedJson should return false";
Yifan Hong4d68f662019-02-13 14:29:33 -0800268}
269
270static const std::vector<ScalarTestErrorParam> gScalarTestErrorParams = {
271 {"{\"i32\": \"1\"}", "Should not allow int32 values to be quoted"},
272 {"{\"si32\": \"1\"}", "Should not allow sint32 values to be quoted"},
273 {"{\"i64\": 1}", "Should require int64 values to be quoted"},
274 {"{\"si64\": 1}", "Should require sint64 values to be quoted"},
275 {"{\"f\": \"1.5\"}", "Should not allow float values to be quoted"},
276 {"{\"d\": \"1.5\"}", "Should not allow double values to be quoted"},
277 {"{\"e\": 1}", "Should not allow integers for enums"},
278};
279
Thiébaud Weksteenf5397482020-10-26 15:44:13 +0100280INSTANTIATE_TEST_SUITE_P(Jsonpb, ScalarTestError, ::testing::ValuesIn(gScalarTestErrorParams));
Yifan Hong4d68f662019-02-13 14:29:33 -0800281
282int main(int argc, char** argv) {
283 using ::testing::AddGlobalTestEnvironment;
284 using ::testing::InitGoogleTest;
285
286 InitGoogleTest(&argc, argv);
287 return RUN_ALL_TESTS();
288}
289
290} // namespace jsonpb
291} // namespace android