1 /*
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 
30 using ::android::jsonpb::internal::FormatJson;
31 using ::testing::ElementsAre;
32 using ::testing::HasSubstr;
33 
34 namespace android {
35 namespace jsonpb {
36 
37 // Unit tests for libjsonpbverify.
38 
39 class LibJsonpbVerifyTest : public ::testing::Test {};
40 
41 class JsonKeyTest : public LibJsonpbVerifyTest {
42  public:
43   template <typename T>
GetFieldJsonName(const std::string & field_name)44   std::string GetFieldJsonName(const std::string& field_name) {
45     return T{}.GetDescriptor()->FindFieldByName(field_name)->json_name();
46   }
47 
48   template <typename T>
TestParseOkWithUnknownKey(const std::string & field_name,const std::string & json_key)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 
TEST_F(JsonKeyTest,WithJsonNameOk)66 TEST_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.
TEST_F(JsonKeyTest,WithJsonNameFooBar)88 TEST_F(JsonKeyTest, WithJsonNameFooBar) {
89   TestParseOkWithUnknownKey<WithJsonName>("foo_bar", "foo_bar");
90 }
91 
TEST_F(JsonKeyTest,WithJsonNameBarBaz)92 TEST_F(JsonKeyTest, WithJsonNameBarBaz) {
93   TestParseOkWithUnknownKey<WithJsonName>("barBaz", "barBaz");
94 }
95 
TEST_F(JsonKeyTest,WithJsonNameBazQux)96 TEST_F(JsonKeyTest, WithJsonNameBazQux) {
97   TestParseOkWithUnknownKey<WithJsonName>("BazQux", "BazQux");
98 }
99 
TEST_F(JsonKeyTest,WithJsonNameQuxQuux)100 TEST_F(JsonKeyTest, WithJsonNameQuxQuux) {
101   TestParseOkWithUnknownKey<WithJsonName>("QUX_QUUX", "QUX_QUUX");
102 }
103 
104 // JSON field name matches Proto field name
TEST_F(JsonKeyTest,NoJsonNameOk)105 TEST_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 lower/UpperCamelCase of Proto field name;
126 // AllFieldsAreKnown should return false. Although the lower/UpperCamelCase name
127 // is a valid key accepted by Protobuf's JSON parser, we explicitly disallow the
128 // behavior.
TEST_F(JsonKeyTest,NoJsonNameFooBar)129 TEST_F(JsonKeyTest, NoJsonNameFooBar) {
130   EXPECT_EQ("fooBar", GetFieldJsonName<NoJsonName>("foo_bar"));
131   TestParseOkWithUnknownKey<NoJsonName>("foo_bar", "fooBar");
132 }
133 
TEST_F(JsonKeyTest,NoJsonNameBarBaz)134 TEST_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 
TEST_F(JsonKeyTest,NoJsonNameBazQux)139 TEST_F(JsonKeyTest, NoJsonNameBazQux) {
140   EXPECT_EQ("BazQux", GetFieldJsonName<NoJsonName>("BazQux"));
141   // No test for BazQux because its JSON name is the same as field_name
142 }
143 
TEST_F(JsonKeyTest,NoJsonNameQuxQuux)144 TEST_F(JsonKeyTest, NoJsonNameQuxQuux) {
145   EXPECT_EQ("QUXQUUX", GetFieldJsonName<NoJsonName>("QUX_QUUX"));
146   TestParseOkWithUnknownKey<NoJsonName>("QUX_QUUX", "QUXQUUX");
147 }
148 
149 class EmbeddedJsonKeyTest : public LibJsonpbVerifyTest {
150  public:
TestEmbeddedError(const std::string & json,const std::string & unknown_key)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 
TEST_F(EmbeddedJsonKeyTest,Ok)164 TEST_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 
TEST_F(EmbeddedJsonKeyTest,FooBar)186 TEST_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 
TEST_F(EmbeddedJsonKeyTest,BarBaz)193 TEST_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 
TEST_F(EmbeddedJsonKeyTest,NoJsonName)201 TEST_F(EmbeddedJsonKeyTest, NoJsonName) {
202   auto object = TestEmbeddedError(
203       "{\"no_json_name\": {\"QUXQUUX\": \"test\"}}", "QUXQUUX");
204   ASSERT_TRUE(object.ok()) << object.error();
205   EXPECT_EQ("test", object->no_json_name().qux_quux());
206 }
207 
TEST_F(EmbeddedJsonKeyTest,QuxQuux)208 TEST_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 
216 class ScalarTest : public LibJsonpbVerifyTest {
217  public:
IsJsonEq(const std::string & l,const std::string & r)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 
EqReformattedJson(const std::string & json,std::string * error)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 
TEST_F(ScalarTest,Ok)246 TEST_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 
264 using ScalarTestErrorParam = std::tuple<const char*, const char*>;
265 class ScalarTestError
266     : public ScalarTest,
267       public ::testing::WithParamInterface<ScalarTestErrorParam> {};
268 
TEST_P(ScalarTestError,Test)269 TEST_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 
280 static 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 
290 INSTANTIATE_TEST_SUITE_P(, ScalarTestError,
291                          ::testing::ValuesIn(gScalarTestErrorParams));
292 
main(int argc,char ** argv)293 int 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
303