• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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 
29 using ::android::jsonpb::internal::FormatJson;
30 using ::testing::ElementsAre;
31 using ::testing::HasSubstr;
32 
33 namespace android {
34 namespace jsonpb {
35 
36 // Unit tests for libjsonpbverify.
37 
38 class LibJsonpbVerifyTest : public ::testing::Test {};
39 
40 class JsonKeyTest : public LibJsonpbVerifyTest {
41  public:
42   template <typename T>
GetFieldJsonName(const std::string & field_name)43   std::string GetFieldJsonName(const std::string& field_name) {
44     return T{}.GetDescriptor()->FindFieldByName(field_name)->json_name();
45   }
46 
47   template <typename T>
TestParseOkWithUnknownKey(const std::string & field_name,const std::string & json_key)48   void TestParseOkWithUnknownKey(const std::string& field_name, const std::string& json_key) {
49     std::string json = "{\"" + json_key + "\": \"test\"}";
50     auto object = JsonStringToMessage<T>(json);
51     ASSERT_TRUE(object.ok()) << object.error();
52     EXPECT_EQ("test", object->GetReflection()->GetString(
53                           *object, object->GetDescriptor()->FindFieldByName(field_name)));
54     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 
TEST_F(JsonKeyTest,WithJsonNameOk)62 TEST_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.
TEST_F(JsonKeyTest,WithJsonNameFooBar)84 TEST_F(JsonKeyTest, WithJsonNameFooBar) {
85   TestParseOkWithUnknownKey<WithJsonName>("foo_bar", "foo_bar");
86 }
87 
TEST_F(JsonKeyTest,WithJsonNameBarBaz)88 TEST_F(JsonKeyTest, WithJsonNameBarBaz) {
89   TestParseOkWithUnknownKey<WithJsonName>("barBaz", "barBaz");
90 }
91 
TEST_F(JsonKeyTest,WithJsonNameBazQux)92 TEST_F(JsonKeyTest, WithJsonNameBazQux) {
93   TestParseOkWithUnknownKey<WithJsonName>("BazQux", "BazQux");
94 }
95 
TEST_F(JsonKeyTest,WithJsonNameQuxQuux)96 TEST_F(JsonKeyTest, WithJsonNameQuxQuux) {
97   TestParseOkWithUnknownKey<WithJsonName>("QUX_QUUX", "QUX_QUUX");
98 }
99 
100 // JSON field name matches Proto field name
TEST_F(JsonKeyTest,NoJsonNameOk)101 TEST_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 
121 // 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
124 // behavior.
TEST_F(JsonKeyTest,NoJsonNameFooBar)125 TEST_F(JsonKeyTest, NoJsonNameFooBar) {
126   EXPECT_EQ("fooBar", GetFieldJsonName<NoJsonName>("foo_bar"));
127   TestParseOkWithUnknownKey<NoJsonName>("foo_bar", "fooBar");
128 }
129 
TEST_F(JsonKeyTest,NoJsonNameBarBaz)130 TEST_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 
TEST_F(JsonKeyTest,NoJsonNameBazQux)135 TEST_F(JsonKeyTest, NoJsonNameBazQux) {
136   EXPECT_EQ("BazQux", GetFieldJsonName<NoJsonName>("BazQux"));
137   // No test for BazQux because its JSON name is the same as field_name
138 }
139 
TEST_F(JsonKeyTest,NoJsonNameQuxQuux)140 TEST_F(JsonKeyTest, NoJsonNameQuxQuux) {
141   EXPECT_EQ("QUXQUUX", GetFieldJsonName<NoJsonName>("QUX_QUUX"));
142   TestParseOkWithUnknownKey<NoJsonName>("QUX_QUUX", "QUXQUUX");
143 }
144 
145 class EmbeddedJsonKeyTest : public LibJsonpbVerifyTest {
146  public:
TestEmbeddedError(const std::string & json,const std::string & unknown_key)147   ErrorOr<Parent> TestEmbeddedError(const std::string& json, const std::string& unknown_key) {
148     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 
TEST_F(EmbeddedJsonKeyTest,Ok)159 TEST_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 
TEST_F(EmbeddedJsonKeyTest,FooBar)181 TEST_F(EmbeddedJsonKeyTest, FooBar) {
182   auto object = TestEmbeddedError("{\"with_json_name\": {\"foo_bar\": \"test\"}}", "foo_bar");
183   ASSERT_TRUE(object.ok()) << object.error();
184   EXPECT_EQ("test", object->with_json_name().foo_bar());
185 }
186 
TEST_F(EmbeddedJsonKeyTest,BarBaz)187 TEST_F(EmbeddedJsonKeyTest, BarBaz) {
188   auto object =
189       TestEmbeddedError("{\"repeated_with_json_name\": [{\"barBaz\": \"test\"}]}", "barBaz");
190   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 
TEST_F(EmbeddedJsonKeyTest,NoJsonName)195 TEST_F(EmbeddedJsonKeyTest, NoJsonName) {
196   auto object = TestEmbeddedError("{\"no_json_name\": {\"QUXQUUX\": \"test\"}}", "QUXQUUX");
197   ASSERT_TRUE(object.ok()) << object.error();
198   EXPECT_EQ("test", object->no_json_name().qux_quux());
199 }
200 
TEST_F(EmbeddedJsonKeyTest,QuxQuux)201 TEST_F(EmbeddedJsonKeyTest, QuxQuux) {
202   auto object =
203       TestEmbeddedError("{\"repeated_no_json_name\": [{\"QUXQUUX\": \"test\"}]}", "QUXQUUX");
204   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 
209 class ScalarTest : public LibJsonpbVerifyTest {
210  public:
IsJsonEq(const std::string & l,const std::string & r)211   ::testing::AssertionResult IsJsonEq(const std::string& l, const std::string& r) {
212     Json::CharReaderBuilder builder;
213     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
214     Json::Value lvalue;
215     std::string errorMessage;
216     if (!reader->parse(&*l.begin(), &*l.end(), &lvalue, &errorMessage))
217       return ::testing::AssertionFailure() << errorMessage;
218     Json::Value rvalue;
219     if (!reader->parse(&*r.begin(), &*r.end(), &rvalue, &errorMessage))
220       return ::testing::AssertionFailure() << errorMessage;
221     Json::StreamWriterBuilder factory;
222     return lvalue == rvalue
223                ? (::testing::AssertionSuccess() << "Both are \n"
224                                                 << Json::writeString(factory, lvalue))
225                : (::testing::AssertionFailure() << Json::writeString(factory, lvalue)
226                                                 << "\n does not equal \n"
227                                                 << Json::writeString(factory, rvalue));
228   }
229 
EqReformattedJson(const std::string & json,std::string * error)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 
TEST_F(ScalarTest,Ok)238 TEST_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 
256 using ScalarTestErrorParam = std::tuple<const char*, const char*>;
257 class ScalarTestError : public ScalarTest,
258                         public ::testing::WithParamInterface<ScalarTestErrorParam> {};
259 
TEST_P(ScalarTestError,Test)260 TEST_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;
267   EXPECT_FALSE(EqReformattedJson(json, &error_)) << "EqReformattedJson should return false";
268 }
269 
270 static 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 
280 INSTANTIATE_TEST_SUITE_P(Jsonpb, ScalarTestError, ::testing::ValuesIn(gScalarTestErrorParams));
281 
main(int argc,char ** argv)282 int 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
292