• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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