1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ICING_TESTING_COMMON_MATCHERS_H_
16 #define ICING_TESTING_COMMON_MATCHERS_H_
17
18 #include <cmath>
19
20 #include "icing/text_classifier/lib3/utils/base/status.h"
21 #include "icing/text_classifier/lib3/utils/base/status_macros.h"
22 #include "icing/text_classifier/lib3/utils/base/statusor.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "icing/absl_ports/str_join.h"
26 #include "icing/index/hit/doc-hit-info.h"
27 #include "icing/legacy/core/icing-string-util.h"
28 #include "icing/proto/search.pb.h"
29 #include "icing/schema/schema-store.h"
30 #include "icing/schema/section.h"
31 #include "icing/util/status-macros.h"
32
33 namespace icing {
34 namespace lib {
35
36 // Used to match Token(Token::Type type, std::string_view text)
37 MATCHER_P2(EqualsToken, type, text, "") {
38 std::string arg_string(arg.text.data(), arg.text.length());
39 if (arg.type != type || arg.text != text) {
40 *result_listener << IcingStringUtil::StringPrintf(
41 "(Expected: type=%d, text=\"%s\". Actual: type=%d, text=\"%s\")", type,
42 text, arg.type, arg_string.c_str());
43 return false;
44 }
45 return true;
46 }
47
48 // Used to match a DocHitInfo
49 MATCHER_P2(EqualsDocHitInfo, document_id, section_ids, "") {
50 const DocHitInfo& actual = arg;
51 SectionIdMask section_mask = kSectionIdMaskNone;
52 for (SectionId section_id : section_ids) {
53 section_mask |= 1U << section_id;
54 }
55 *result_listener << IcingStringUtil::StringPrintf(
56 "(actual is {document_id=%d, section_mask=%d}, but expected was "
57 "{document_id=%d, section_mask=%d}.)",
58 actual.document_id(), actual.hit_section_ids_mask(), document_id,
59 section_mask);
60 return actual.document_id() == document_id &&
61 actual.hit_section_ids_mask() == section_mask;
62 }
63
64 // Used to match a DocHitInfo
65 MATCHER_P2(EqualsDocHitInfoWithTermFrequency, document_id,
66 section_ids_to_term_frequencies_map, "") {
67 const DocHitInfo& actual = arg;
68 SectionIdMask section_mask = kSectionIdMaskNone;
69
70 bool term_frequency_as_expected = true;
71 std::vector<Hit::TermFrequency> expected_tfs;
72 std::vector<Hit::TermFrequency> actual_tfs;
73 for (auto itr = section_ids_to_term_frequencies_map.begin();
74 itr != section_ids_to_term_frequencies_map.end(); itr++) {
75 SectionId section_id = itr->first;
76 section_mask |= 1U << section_id;
77 expected_tfs.push_back(itr->second);
78 actual_tfs.push_back(actual.hit_term_frequency(section_id));
79 if (actual.hit_term_frequency(section_id) != itr->second) {
80 term_frequency_as_expected = false;
81 }
82 }
83 std::string actual_term_frequencies = absl_ports::StrCat(
84 "[", absl_ports::StrJoin(actual_tfs, ",", absl_ports::NumberFormatter()),
85 "]");
86 std::string expected_term_frequencies = absl_ports::StrCat(
87 "[",
88 absl_ports::StrJoin(expected_tfs, ",", absl_ports::NumberFormatter()),
89 "]");
90 *result_listener << IcingStringUtil::StringPrintf(
91 "(actual is {document_id=%d, section_mask=%d, term_frequencies=%s}, but "
92 "expected was "
93 "{document_id=%d, section_mask=%d, term_frequencies=%s}.)",
94 actual.document_id(), actual.hit_section_ids_mask(),
95 actual_term_frequencies.c_str(), document_id, section_mask,
96 expected_term_frequencies.c_str());
97 return actual.document_id() == document_id &&
98 actual.hit_section_ids_mask() == section_mask &&
99 term_frequency_as_expected;
100 }
101
102 // Used to match a ScoredDocumentHit
103 MATCHER_P(EqualsScoredDocumentHit, expected_scored_document_hit, "") {
104 if (arg.document_id() != expected_scored_document_hit.document_id() ||
105 arg.hit_section_id_mask() !=
106 expected_scored_document_hit.hit_section_id_mask() ||
107 std::fabs(arg.score() - expected_scored_document_hit.score()) > 1e-6) {
108 *result_listener << IcingStringUtil::StringPrintf(
109 "Expected: document_id=%d, hit_section_id_mask=%d, score=%.2f. Actual: "
110 "document_id=%d, hit_section_id_mask=%d, score=%.2f",
111 expected_scored_document_hit.document_id(),
112 expected_scored_document_hit.hit_section_id_mask(),
113 expected_scored_document_hit.score(), arg.document_id(),
114 arg.hit_section_id_mask(), arg.score());
115 return false;
116 }
117 return true;
118 }
119
120 MATCHER_P(EqualsSetSchemaResult, expected, "") {
121 const SchemaStore::SetSchemaResult& actual = arg;
122
123 if (actual.success == expected.success &&
124 actual.old_schema_type_ids_changed ==
125 expected.old_schema_type_ids_changed &&
126 actual.schema_types_deleted_by_name ==
127 expected.schema_types_deleted_by_name &&
128 actual.schema_types_deleted_by_id ==
129 expected.schema_types_deleted_by_id &&
130 actual.schema_types_incompatible_by_name ==
131 expected.schema_types_incompatible_by_name &&
132 actual.schema_types_incompatible_by_id ==
133 expected.schema_types_incompatible_by_id &&
134 actual.schema_types_new_by_name == expected.schema_types_new_by_name &&
135 actual.schema_types_changed_fully_compatible_by_name ==
136 expected.schema_types_changed_fully_compatible_by_name &&
137 actual.schema_types_index_incompatible_by_name ==
138 expected.schema_types_index_incompatible_by_name) {
139 return true;
140 }
141
142 // Format schema_type_ids_changed
143 std::string actual_old_schema_type_ids_changed = absl_ports::StrCat(
144 "[",
145 absl_ports::StrJoin(actual.old_schema_type_ids_changed, ",",
146 absl_ports::NumberFormatter()),
147 "]");
148
149 std::string expected_old_schema_type_ids_changed = absl_ports::StrCat(
150 "[",
151 absl_ports::StrJoin(expected.old_schema_type_ids_changed, ",",
152 absl_ports::NumberFormatter()),
153 "]");
154
155 // Format schema_types_deleted_by_name
156 std::string actual_schema_types_deleted_by_name = absl_ports::StrCat(
157 "[", absl_ports::StrJoin(actual.schema_types_deleted_by_name, ","), "]");
158
159 std::string expected_schema_types_deleted_by_name = absl_ports::StrCat(
160 "[", absl_ports::StrJoin(expected.schema_types_deleted_by_name, ","),
161 "]");
162
163 // Format schema_types_deleted_by_id
164 std::string actual_schema_types_deleted_by_id = absl_ports::StrCat(
165 "[",
166 absl_ports::StrJoin(actual.schema_types_deleted_by_id, ",",
167 absl_ports::NumberFormatter()),
168 "]");
169
170 std::string expected_schema_types_deleted_by_id = absl_ports::StrCat(
171 "[",
172 absl_ports::StrJoin(expected.schema_types_deleted_by_id, ",",
173 absl_ports::NumberFormatter()),
174 "]");
175
176 // Format schema_types_incompatible_by_name
177 std::string actual_schema_types_incompatible_by_name = absl_ports::StrCat(
178 "[", absl_ports::StrJoin(actual.schema_types_incompatible_by_name, ","),
179 "]");
180
181 std::string expected_schema_types_incompatible_by_name = absl_ports::StrCat(
182 "[", absl_ports::StrJoin(expected.schema_types_incompatible_by_name, ","),
183 "]");
184
185 // Format schema_types_incompatible_by_id
186 std::string actual_schema_types_incompatible_by_id = absl_ports::StrCat(
187 "[",
188 absl_ports::StrJoin(actual.schema_types_incompatible_by_id, ",",
189 absl_ports::NumberFormatter()),
190 "]");
191
192 std::string expected_schema_types_incompatible_by_id = absl_ports::StrCat(
193 "[",
194 absl_ports::StrJoin(expected.schema_types_incompatible_by_id, ",",
195 absl_ports::NumberFormatter()),
196 "]");
197
198 // Format schema_types_new_by_name
199 std::string actual_schema_types_new_by_name = absl_ports::StrCat(
200 "[", absl_ports::StrJoin(actual.schema_types_new_by_name, ","), "]");
201
202 std::string expected_schema_types_new_by_name = absl_ports::StrCat(
203 "[", absl_ports::StrJoin(expected.schema_types_new_by_name, ","), "]");
204
205 // Format schema_types_changed_fully_compatible_by_name
206 std::string actual_schema_types_changed_fully_compatible_by_name =
207 absl_ports::StrCat(
208 "[",
209 absl_ports::StrJoin(
210 actual.schema_types_changed_fully_compatible_by_name, ","),
211 "]");
212
213 std::string expected_schema_types_changed_fully_compatible_by_name =
214 absl_ports::StrCat(
215 "[",
216 absl_ports::StrJoin(
217 expected.schema_types_changed_fully_compatible_by_name, ","),
218 "]");
219
220 // Format schema_types_deleted_by_id
221 std::string actual_schema_types_index_incompatible_by_name =
222 absl_ports::StrCat(
223 "[",
224 absl_ports::StrJoin(actual.schema_types_index_incompatible_by_name,
225 ","),
226 "]");
227
228 std::string expected_schema_types_index_incompatible_by_name =
229 absl_ports::StrCat(
230 "[",
231 absl_ports::StrJoin(expected.schema_types_index_incompatible_by_name,
232 ","),
233 "]");
234
235 *result_listener << IcingStringUtil::StringPrintf(
236 "\nExpected {\n"
237 "\tsuccess=%d,\n"
238 "\told_schema_type_ids_changed=%s,\n"
239 "\tschema_types_deleted_by_name=%s,\n"
240 "\tschema_types_deleted_by_id=%s,\n"
241 "\tschema_types_incompatible_by_name=%s,\n"
242 "\tschema_types_incompatible_by_id=%s\n"
243 "\tschema_types_new_by_name=%s,\n"
244 "\tschema_types_index_incompatible_by_name=%s,\n"
245 "\tschema_types_changed_fully_compatible_by_name=%s\n"
246 "}\n"
247 "Actual {\n"
248 "\tsuccess=%d,\n"
249 "\told_schema_type_ids_changed=%s,\n"
250 "\tschema_types_deleted_by_name=%s,\n"
251 "\tschema_types_deleted_by_id=%s,\n"
252 "\tschema_types_incompatible_by_name=%s,\n"
253 "\tschema_types_incompatible_by_id=%s\n"
254 "\tschema_types_new_by_name=%s,\n"
255 "\tschema_types_index_incompatible_by_name=%s,\n"
256 "\tschema_types_changed_fully_compatible_by_name=%s\n"
257 "}\n",
258 expected.success, expected_old_schema_type_ids_changed.c_str(),
259 expected_schema_types_deleted_by_name.c_str(),
260 expected_schema_types_deleted_by_id.c_str(),
261 expected_schema_types_incompatible_by_name.c_str(),
262 expected_schema_types_incompatible_by_id.c_str(),
263 expected_schema_types_new_by_name.c_str(),
264 expected_schema_types_changed_fully_compatible_by_name.c_str(),
265 expected_schema_types_index_incompatible_by_name.c_str(), actual.success,
266 actual_old_schema_type_ids_changed.c_str(),
267 actual_schema_types_deleted_by_name.c_str(),
268 actual_schema_types_deleted_by_id.c_str(),
269 actual_schema_types_incompatible_by_name.c_str(),
270 actual_schema_types_incompatible_by_id.c_str(),
271 actual_schema_types_new_by_name.c_str(),
272 actual_schema_types_changed_fully_compatible_by_name.c_str(),
273 actual_schema_types_index_incompatible_by_name.c_str());
274 return false;
275 }
276
StatusCodeToString(libtextclassifier3::StatusCode code)277 std::string StatusCodeToString(libtextclassifier3::StatusCode code) {
278 switch (code) {
279 case libtextclassifier3::StatusCode::OK:
280 return "OK";
281 case libtextclassifier3::StatusCode::CANCELLED:
282 return "CANCELLED";
283 case libtextclassifier3::StatusCode::UNKNOWN:
284 return "UNKNOWN";
285 case libtextclassifier3::StatusCode::INVALID_ARGUMENT:
286 return "INVALID_ARGUMENT";
287 case libtextclassifier3::StatusCode::DEADLINE_EXCEEDED:
288 return "DEADLINE_EXCEEDED";
289 case libtextclassifier3::StatusCode::NOT_FOUND:
290 return "NOT_FOUND";
291 case libtextclassifier3::StatusCode::ALREADY_EXISTS:
292 return "ALREADY_EXISTS";
293 case libtextclassifier3::StatusCode::PERMISSION_DENIED:
294 return "PERMISSION_DENIED";
295 case libtextclassifier3::StatusCode::RESOURCE_EXHAUSTED:
296 return "RESOURCE_EXHAUSTED";
297 case libtextclassifier3::StatusCode::FAILED_PRECONDITION:
298 return "FAILED_PRECONDITION";
299 case libtextclassifier3::StatusCode::ABORTED:
300 return "ABORTED";
301 case libtextclassifier3::StatusCode::OUT_OF_RANGE:
302 return "OUT_OF_RANGE";
303 case libtextclassifier3::StatusCode::UNIMPLEMENTED:
304 return "UNIMPLEMENTED";
305 case libtextclassifier3::StatusCode::INTERNAL:
306 return "INTERNAL";
307 case libtextclassifier3::StatusCode::UNAVAILABLE:
308 return "UNAVAILABLE";
309 case libtextclassifier3::StatusCode::DATA_LOSS:
310 return "DATA_LOSS";
311 case libtextclassifier3::StatusCode::UNAUTHENTICATED:
312 return "UNAUTHENTICATED";
313 default:
314 return "";
315 }
316 }
317
ProtoStatusCodeToString(StatusProto::Code code)318 std::string ProtoStatusCodeToString(StatusProto::Code code) {
319 switch (code) {
320 case StatusProto::OK:
321 return "OK";
322 case StatusProto::UNKNOWN:
323 return "UNKNOWN";
324 case StatusProto::INVALID_ARGUMENT:
325 return "INVALID_ARGUMENT";
326 case StatusProto::NOT_FOUND:
327 return "NOT_FOUND";
328 case StatusProto::ALREADY_EXISTS:
329 return "ALREADY_EXISTS";
330 case StatusProto::OUT_OF_SPACE:
331 return "OUT_OF_SPACE";
332 case StatusProto::FAILED_PRECONDITION:
333 return "FAILED_PRECONDITION";
334 case StatusProto::ABORTED:
335 return "ABORTED";
336 case StatusProto::INTERNAL:
337 return "INTERNAL";
338 case StatusProto::WARNING_DATA_LOSS:
339 return "WARNING_DATA_LOSS";
340 default:
341 return "";
342 }
343 }
344
345 MATCHER(IsOk, "") {
346 libtextclassifier3::StatusAdapter adapter(arg);
347 if (adapter.status().ok()) {
348 return true;
349 }
350 *result_listener << IcingStringUtil::StringPrintf(
351 "Expected OK, actual was (%s:%s)",
352 StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
353 adapter.status().error_message().c_str());
354 return false;
355 }
356
357 MATCHER_P(IsOkAndHolds, matcher, "") {
358 if (!arg.ok()) {
359 *result_listener << IcingStringUtil::StringPrintf(
360 "Expected OK, actual was (%s:%s)",
361 StatusCodeToString(arg.status().CanonicalCode()).c_str(),
362 arg.status().error_message().c_str());
363 return false;
364 }
365 return ExplainMatchResult(matcher, arg.ValueOrDie(), result_listener);
366 }
367
368 MATCHER_P(StatusIs, status_code, "") {
369 libtextclassifier3::StatusAdapter adapter(arg);
370 if (adapter.status().CanonicalCode() == status_code) {
371 return true;
372 }
373 *result_listener << IcingStringUtil::StringPrintf(
374 "Expected (%s:), actual was (%s:%s)",
375 StatusCodeToString(status_code).c_str(),
376 StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
377 adapter.status().error_message().c_str());
378 return false;
379 }
380
381 MATCHER_P2(StatusIs, status_code, error_matcher, "") {
382 libtextclassifier3::StatusAdapter adapter(arg);
383 if (adapter.status().CanonicalCode() != status_code) {
384 *result_listener << IcingStringUtil::StringPrintf(
385 "Expected (%s:), actual was (%s:%s)",
386 StatusCodeToString(status_code).c_str(),
387 StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
388 adapter.status().error_message().c_str());
389 return false;
390 }
391 return ExplainMatchResult(error_matcher, adapter.status().error_message(),
392 result_listener);
393 }
394
395 MATCHER(ProtoIsOk, "") {
396 if (arg.code() == StatusProto::OK) {
397 return true;
398 }
399 *result_listener << IcingStringUtil::StringPrintf(
400 "Expected OK, actual was (%s:%s)",
401 ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
402 return false;
403 }
404
405 MATCHER_P(ProtoStatusIs, status_code, "") {
406 if (arg.code() == status_code) {
407 return true;
408 }
409 *result_listener << IcingStringUtil::StringPrintf(
410 "Expected (%s:), actual was (%s:%s)",
411 ProtoStatusCodeToString(status_code).c_str(),
412 ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
413 return false;
414 }
415
416 MATCHER_P2(ProtoStatusIs, status_code, error_matcher, "") {
417 if (arg.code() != status_code) {
418 *result_listener << IcingStringUtil::StringPrintf(
419 "Expected (%s:), actual was (%s:%s)",
420 ProtoStatusCodeToString(status_code).c_str(),
421 ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
422 return false;
423 }
424 return ExplainMatchResult(error_matcher, arg.message(), result_listener);
425 }
426
427 MATCHER_P(EqualsSearchResultIgnoreStatsAndScores, expected, "") {
428 SearchResultProto actual_copy = arg;
429 actual_copy.clear_query_stats();
430 actual_copy.clear_debug_info();
431 for (SearchResultProto::ResultProto& result :
432 *actual_copy.mutable_results()) {
433 result.clear_score();
434 }
435
436 SearchResultProto expected_copy = expected;
437 expected_copy.clear_query_stats();
438 expected_copy.clear_debug_info();
439 for (SearchResultProto::ResultProto& result :
440 *expected_copy.mutable_results()) {
441 result.clear_score();
442 }
443 return ExplainMatchResult(testing::EqualsProto(expected_copy), actual_copy,
444 result_listener);
445 }
446
447 // TODO(tjbarron) Remove this once icing has switched to depend on TC3 Status
448 #define ICING_STATUS_MACROS_CONCAT_NAME(x, y) \
449 ICING_STATUS_MACROS_CONCAT_IMPL(x, y)
450 #define ICING_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
451
452 #define ICING_EXPECT_OK(func) EXPECT_THAT(func, IsOk())
453 #define ICING_ASSERT_OK(func) ASSERT_THAT(func, IsOk())
454 #define ICING_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
455 ICING_ASSERT_OK_AND_ASSIGN_IMPL( \
456 ICING_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
457 rexpr)
458 #define ICING_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
459 auto statusor = (rexpr); \
460 ICING_ASSERT_OK(statusor.status()); \
461 lhs = std::move(statusor).ValueOrDie()
462
463 } // namespace lib
464 } // namespace icing
465
466 #endif // ICING_TESTING_COMMON_MATCHERS_H_
467