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