1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "testing/base/public/testing_util.h"
31 
32 #include <memory>
33 
34 #include "base/logging.h"
35 #include "base/protobuf/message.h"
36 #include "base/protobuf/text_format.h"
37 
38 namespace mozc {
39 namespace testing {
40 
41 using ::mozc::protobuf::Descriptor;
42 using ::mozc::protobuf::FieldDescriptor;
43 using ::mozc::protobuf::Message;
44 using ::mozc::protobuf::Reflection;
45 using ::mozc::protobuf::TextFormat;
46 
47 namespace internal {
48 
49 namespace {
50 
51 bool EqualsProtoInternal(
52     const Message &message1, const Message &message2, bool is_partial);
53 
54 // Compares (non-repeated) filed of the given messages.
EqualsField(const FieldDescriptor * field,const Reflection * reflection,const Message & message1,const Message & message2,bool is_partial)55 bool EqualsField(
56     const FieldDescriptor *field, const Reflection *reflection,
57     const Message &message1, const Message &message2, bool is_partial) {
58   const bool has_field = reflection->HasField(message1, field);
59   if (is_partial && !has_field) {
60     // Don't check empty fields for partial equality check.
61     return true;
62   }
63 
64   if (has_field != reflection->HasField(message2, field)) {
65     return false;
66   }
67 
68 // Use macro for boilerplate code generation.
69 #define MOZC_PROTO_FIELD_EQ_CASE(cpptype, method) \
70   case FieldDescriptor::cpptype: \
71     if (reflection->method(message1, field) != \
72         reflection->method(message2, field)) { \
73       return false; \
74     } \
75     break
76 
77   switch (field->cpp_type()) {
78     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_INT32, GetInt32);
79     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_INT64, GetInt64);
80     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_UINT32, GetUInt32);
81     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_UINT64, GetUInt64);
82     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_DOUBLE, GetDouble);
83     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_FLOAT, GetFloat);
84     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_BOOL, GetBool);
85     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_ENUM, GetEnum);
86     MOZC_PROTO_FIELD_EQ_CASE(CPPTYPE_STRING, GetString);
87     case FieldDescriptor::CPPTYPE_MESSAGE:
88       if (!EqualsProtoInternal(
89               reflection->GetMessage(message1, field),
90               reflection->GetMessage(message2, field),
91               is_partial)) {
92         return false;
93       }
94       break;
95     default:
96       LOG(ERROR) << "Unknown cpp_type: " << field->cpp_type();
97       return false;
98   }
99 #undef MOZC_PROTO_FIELD_EQ_CASE
100 
101   return true;
102 }
103 
EqualsRepeatedField(const FieldDescriptor * field,const Reflection * reflection,const Message & message1,const Message & message2,bool is_partial)104 bool EqualsRepeatedField(
105     const FieldDescriptor *field, const Reflection *reflection,
106     const Message &message1, const Message &message2, bool is_partial) {
107   const int field_size = reflection->FieldSize(message1, field);
108   if (is_partial && field_size == 0) {
109     // Don't check empty fields for partial equality check.
110     return true;
111   }
112 
113   if (field_size != reflection->FieldSize(message2, field)) {
114     return false;
115   }
116 
117 // Use macro for boilerplate code generation.
118 #define MOZC_PROTO_REPEATED_FIELD_EQ_CASE(cpptype, method) \
119   case FieldDescriptor::cpptype: \
120     for (int i = 0; i < field_size; ++i) { \
121       if (reflection->method(message1, field, i) != \
122           reflection->method(message2, field, i)) { \
123         return false;\
124       } \
125     } \
126     break
127 
128   switch (field->cpp_type()) {
129     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_INT32, GetRepeatedInt32);
130     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_INT64, GetRepeatedInt64);
131     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_UINT32, GetRepeatedUInt32);
132     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_UINT64, GetRepeatedUInt64);
133     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_DOUBLE, GetRepeatedDouble);
134     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_FLOAT, GetRepeatedFloat);
135     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_BOOL, GetRepeatedBool);
136     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_ENUM, GetRepeatedEnum);
137     MOZC_PROTO_REPEATED_FIELD_EQ_CASE(CPPTYPE_STRING, GetRepeatedString);
138     case FieldDescriptor::CPPTYPE_MESSAGE:
139       for (int i = 0; i < field_size; ++i) {
140         if (!EqualsProtoInternal(
141                 reflection->GetRepeatedMessage(message1, field, i),
142                 reflection->GetRepeatedMessage(message2, field, i),
143                 is_partial)) {
144           return false;
145         }
146       }
147       break;
148     default:
149       LOG(ERROR) << "Unknown cpp_type: " << field->cpp_type();
150       return false;
151   }
152 #undef MOZC_PROTO_REPEATED_FIELD_EQ_CASE
153 
154   return true;
155 }
156 
EqualsProtoInternal(const Message & message1,const Message & message2,bool is_partial)157 bool EqualsProtoInternal(
158     const Message &message1, const Message &message2, bool is_partial) {
159   const Descriptor *descriptor = message1.GetDescriptor();
160   CHECK(descriptor == message2.GetDescriptor());
161 
162   const Reflection *reflection = message1.GetReflection();
163   CHECK(reflection == message2.GetReflection());
164 
165   for (int i = 0; i < descriptor->field_count(); ++i) {
166     const FieldDescriptor *field = descriptor->field(i);
167     CHECK(field != nullptr);
168     if (field->is_repeated()) {
169       if (!EqualsRepeatedField(
170               field, reflection, message1, message2, is_partial)) {
171         return false;
172       }
173     } else {
174       if (!EqualsField(field, reflection, message1, message2, is_partial)) {
175         return false;
176       }
177     }
178   }
179 
180   return true;
181 }
182 
183 }  // namespace
184 
EqualsProtoFormat(const char * expect_string,const char * actual_string,const Message & expect,const Message & actual,bool is_partial)185 ::testing::AssertionResult EqualsProtoFormat(
186     const char *expect_string, const char *actual_string,
187     const Message &expect, const Message &actual,
188     bool is_partial) {
189   if (EqualsProtoInternal(expect, actual, is_partial)) {
190     return ::testing::AssertionSuccess();
191   }
192 
193   return ::testing::AssertionFailure()
194       << "EXPECT_PROTO_" << (is_partial ? "P" : "") << "EQ("
195       << expect_string << ", " << actual_string << ")"
196       << " evaluates to false, where\n"
197       << expect_string << " evaluates to " << expect.Utf8DebugString() << "\n"
198       << actual_string << " evaluates to " << actual.Utf8DebugString();
199 }
200 
201 }  // namespace internal
202 
203 namespace {
EqualsProtoWithParse(const char * expect_string,const char * actual_string,const char * expect,const Message & actual,bool is_partial)204 ::testing::AssertionResult EqualsProtoWithParse(
205     const char *expect_string, const char *actual_string,
206     const char *expect, const Message &actual,
207     bool is_partial) {
208   // Note: Message::New returns an instance of the actual type,
209   // so we can convert the string representation of the "actual"'s type,
210   // by simply parsing it.
211   std::unique_ptr<Message> expect_message(actual.New());
212   TextFormat::Parser parser;
213   parser.AllowPartialMessage(is_partial);
214   CHECK(parser.ParseFromString(expect, expect_message.get()))
215       << "Failed to parse message: " << expect;
216 
217   return internal::EqualsProtoFormat(
218       expect_string, actual_string, *expect_message, actual, is_partial);
219 }
220 }  // namespace
221 
EqualsProto(const char * expect_string,const char * actual_string,const char * expect,const Message & actual)222 ::testing::AssertionResult EqualsProto(
223     const char *expect_string, const char *actual_string,
224     const char *expect, const Message &actual) {
225   return EqualsProtoWithParse(
226       expect_string, actual_string, expect, actual, false);
227 }
228 
PartiallyEqualsProto(const char * expect_string,const char * actual_string,const char * expect,const Message & actual)229 ::testing::AssertionResult PartiallyEqualsProto(
230     const char *expect_string, const char *actual_string,
231     const char *expect, const Message &actual) {
232   return EqualsProtoWithParse(
233       expect_string, actual_string, expect, actual, true);
234 }
235 
236 }  // namespace testing
237 }  // namespace mozc
238