xref: /aosp_15_r20/external/icing/icing/testing/common-matchers.h (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
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 <algorithm>
19 #include <cinttypes>
20 #include <cmath>
21 #include <functional>
22 #include <string>
23 #include <vector>
24 
25 #include "icing/text_classifier/lib3/utils/base/status.h"
26 #include "icing/text_classifier/lib3/utils/base/status_macros.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "icing/absl_ports/str_join.h"
30 #include "icing/index/hit/doc-hit-info.h"
31 #include "icing/index/hit/hit.h"
32 #include "icing/index/iterator/doc-hit-info-iterator-test-util.h"
33 #include "icing/index/iterator/doc-hit-info-iterator.h"
34 #include "icing/legacy/core/icing-string-util.h"
35 #include "icing/portable/equals-proto.h"
36 #include "icing/proto/search.pb.h"
37 #include "icing/proto/status.pb.h"
38 #include "icing/schema/joinable-property.h"
39 #include "icing/schema/schema-store.h"
40 #include "icing/schema/scorable_property_manager.h"
41 #include "icing/schema/section.h"
42 #include "icing/scoring/scored-document-hit.h"
43 
44 namespace icing {
45 namespace lib {
46 
47 // Used to match Token(Token::Type type, std::string_view text)
48 MATCHER_P2(EqualsToken, type, text, "") {
49   std::string arg_string(arg.text.data(), arg.text.length());
50   if (arg.type != type || arg.text != text) {
51     *result_listener << IcingStringUtil::StringPrintf(
52         "(Expected: type=%d, text=\"%s\". Actual: type=%d, text=\"%s\")", type,
53         text, arg.type, arg_string.c_str());
54     return false;
55   }
56   return true;
57 }
58 
59 MATCHER_P(EqualsNormalizedTerm, text, "") {
60   std::string arg_string(arg.text.data(), arg.text.length());
61   if (arg.text != text) {
62     *result_listener << IcingStringUtil::StringPrintf(
63         "(Expected: text=\"%s\". Actual: text=\"%s\")", text,
64         arg_string.c_str());
65     return false;
66   }
67   return true;
68 }
69 
70 // Used to match a DocHitInfo
71 MATCHER_P2(EqualsDocHitInfo, document_id, section_ids, "") {
72   const DocHitInfo& actual = arg;
73   SectionIdMask section_mask = kSectionIdMaskNone;
74   for (SectionId section_id : section_ids) {
75     section_mask |= UINT64_C(1) << section_id;
76   }
77   *result_listener << IcingStringUtil::StringPrintf(
78       "(actual is {document_id=%d, section_mask=%" PRIu64
79       "}, but expected was "
80       "{document_id=%d, section_mask=%" PRIu64 "}.)",
81       actual.document_id(), actual.hit_section_ids_mask(), document_id,
82       section_mask);
83   return actual.document_id() == document_id &&
84          actual.hit_section_ids_mask() == section_mask;
85 }
86 
87 // Used to match a DocHitInfoIterator::CallStats
88 MATCHER_P5(EqualsDocHitInfoIteratorCallStats, num_leaf_advance_calls_lite_index,
89            num_leaf_advance_calls_main_index,
90            num_leaf_advance_calls_integer_index,
91            num_leaf_advance_calls_no_index, num_blocks_inspected, "") {
92   const DocHitInfoIterator::CallStats& actual = arg;
93   *result_listener << IcingStringUtil::StringPrintf(
94       "(actual is {num_leaf_advance_calls_lite_index=%d, "
95       "num_leaf_advance_calls_main_index=%d, "
96       "num_leaf_advance_calls_integer_index=%d, "
97       "num_leaf_advance_calls_no_index=%d, num_blocks_inspected=%d}, but "
98       "expected was {num_leaf_advance_calls_lite_index=%d, "
99       "num_leaf_advance_calls_main_index=%d, "
100       "num_leaf_advance_calls_integer_index=%d, "
101       "num_leaf_advance_calls_no_index=%d, num_blocks_inspected=%d}.)",
102       actual.num_leaf_advance_calls_lite_index,
103       actual.num_leaf_advance_calls_main_index,
104       actual.num_leaf_advance_calls_integer_index,
105       actual.num_leaf_advance_calls_no_index, actual.num_blocks_inspected,
106       num_leaf_advance_calls_lite_index, num_leaf_advance_calls_main_index,
107       num_leaf_advance_calls_integer_index, num_leaf_advance_calls_no_index,
108       num_blocks_inspected);
109   return actual.num_leaf_advance_calls_lite_index ==
110              num_leaf_advance_calls_lite_index &&
111          actual.num_leaf_advance_calls_main_index ==
112              num_leaf_advance_calls_main_index &&
113          actual.num_leaf_advance_calls_integer_index ==
114              num_leaf_advance_calls_integer_index &&
115          actual.num_leaf_advance_calls_no_index ==
116              num_leaf_advance_calls_no_index &&
117          actual.num_blocks_inspected == num_blocks_inspected;
118 }
119 
120 // Used to match a DocumentAssociatedScoreData
121 MATCHER_P5(EqualsDocumentAssociatedScoreData, corpus_id, document_score,
122            creation_timestamp_ms, length_in_tokens,
123            has_valid_scorable_property_cache_index, "") {
124   bool expected_has_valid_scorable_property_cache_index =
125       arg.scorable_property_cache_index() != -1;
126   return arg.corpus_id() == corpus_id &&
127          arg.document_score() == document_score &&
128          arg.creation_timestamp_ms() == creation_timestamp_ms &&
129          arg.length_in_tokens() == length_in_tokens &&
130          expected_has_valid_scorable_property_cache_index ==
131              has_valid_scorable_property_cache_index;
132 }
133 
134 // Used to match a ScorablePropertyManager::ScorablePropertyInfo
135 MATCHER_P2(EqualsScorablePropertyInfo, property_path, data_type, "") {
136   const ScorablePropertyManager::ScorablePropertyInfo& actual = arg;
137   return actual.property_path == property_path && actual.data_type == data_type;
138 }
139 
140 struct ExtractTermFrequenciesResult {
141   std::array<Hit::TermFrequency, kTotalNumSections> term_frequencies = {0};
142   SectionIdMask section_mask = kSectionIdMaskNone;
143 };
144 // Extracts the term frequencies represented by the section_ids_tf_map.
145 // Returns:
146 //   - a SectionIdMask representing all sections that appears as entries in the
147 //     map, even if they have an entry with term_frequency==0
148 //   - an array representing the term frequencies for each section. Sections not
149 //     present in section_ids_tf_map have a term frequency of 0.
150 ExtractTermFrequenciesResult ExtractTermFrequencies(
151     const std::unordered_map<SectionId, Hit::TermFrequency>&
152         section_ids_tf_map);
153 
154 struct CheckTermFrequencyResult {
155   std::string expected_term_frequencies_str;
156   std::string actual_term_frequencies_str;
157   bool term_frequencies_match = true;
158 };
159 // Checks that the term frequencies in actual_term_frequencies match those
160 // specified in expected_section_ids_tf_map. If there is no entry in
161 // expected_section_ids_tf_map, then it is assumed that the term frequency for
162 // that section is 0.
163 // Returns:
164 //   - a bool indicating if the term frequencies match
165 //   - debug strings representing the contents of the actual and expected term
166 //     term frequency arrays.
167 CheckTermFrequencyResult CheckTermFrequency(
168     const std::array<Hit::TermFrequency, kTotalNumSections>&
169         expected_term_frequencies,
170     const std::array<Hit::TermFrequency, kTotalNumSections>&
171         actual_term_frequencies);
172 
173 // Used to match a DocHitInfo
174 MATCHER_P2(EqualsDocHitInfoWithTermFrequency, document_id,
175            section_ids_to_term_frequencies_map, "") {
176   const DocHitInfoTermFrequencyPair& actual = arg;
177   std::array<Hit::TermFrequency, kTotalNumSections> actual_tf_array;
178   for (SectionId section_id = 0; section_id < kTotalNumSections; ++section_id) {
179     actual_tf_array[section_id] = actual.hit_term_frequency(section_id);
180   }
181   ExtractTermFrequenciesResult expected =
182       ExtractTermFrequencies(section_ids_to_term_frequencies_map);
183   CheckTermFrequencyResult check_tf_result =
184       CheckTermFrequency(expected.term_frequencies, actual_tf_array);
185 
186   *result_listener << IcingStringUtil::StringPrintf(
187       "(actual is {document_id=%d, section_mask=%" PRIu64
188       ", term_frequencies=%s}, but expected was "
189       "{document_id=%d, section_mask=%" PRIu64 ", term_frequencies=%s}.)",
190       actual.doc_hit_info().document_id(),
191       actual.doc_hit_info().hit_section_ids_mask(),
192       check_tf_result.actual_term_frequencies_str.c_str(), document_id,
193       expected.section_mask,
194       check_tf_result.expected_term_frequencies_str.c_str());
195   return actual.doc_hit_info().document_id() == document_id &&
196          actual.doc_hit_info().hit_section_ids_mask() ==
197              expected.section_mask &&
198          check_tf_result.term_frequencies_match;
199 }
200 
201 MATCHER_P2(EqualsTermMatchInfo, term, section_ids_to_term_frequencies_map, "") {
202   const TermMatchInfo& actual = arg;
203   std::string term_str(term);
204   ExtractTermFrequenciesResult expected =
205       ExtractTermFrequencies(section_ids_to_term_frequencies_map);
206   CheckTermFrequencyResult check_tf_result =
207       CheckTermFrequency(expected.term_frequencies, actual.term_frequencies);
208   *result_listener << IcingStringUtil::StringPrintf(
209       "(actual is {term=%s, section_mask=%" PRIu64
210       ", term_frequencies=%s}, but expected was "
211       "{term=%s, section_mask=%" PRIu64 ", term_frequencies=%s}.)",
212       actual.term.data(), actual.section_ids_mask,
213       check_tf_result.actual_term_frequencies_str.c_str(), term_str.data(),
214       expected.section_mask,
215       check_tf_result.expected_term_frequencies_str.c_str());
216   return actual.term == term &&
217          actual.section_ids_mask == expected.section_mask &&
218          check_tf_result.term_frequencies_match;
219 }
220 
221 class ScoredDocumentHitFormatter {
222  public:
operator()223   std::string operator()(const ScoredDocumentHit& scored_document_hit) {
224     return IcingStringUtil::StringPrintf(
225         "(document_id=%d, hit_section_id_mask=%" PRId64 ", score=%.2f)",
226         scored_document_hit.document_id(),
227         scored_document_hit.hit_section_id_mask(), scored_document_hit.score());
228   }
229 };
230 
231 class ScoredDocumentHitEqualComparator {
232  public:
operator()233   bool operator()(const ScoredDocumentHit& lhs,
234                   const ScoredDocumentHit& rhs) const {
235     bool additional_scores_match = true;
236     if (lhs.additional_scores() != nullptr &&
237         rhs.additional_scores() != nullptr) {
238       additional_scores_match =
239           *lhs.additional_scores() == *rhs.additional_scores();
240     } else {
241       additional_scores_match =
242           lhs.additional_scores() == rhs.additional_scores();
243     }
244     return lhs.document_id() == rhs.document_id() &&
245            lhs.hit_section_id_mask() == rhs.hit_section_id_mask() &&
246            std::fabs(lhs.score() - rhs.score()) < 1e-6 &&
247            additional_scores_match;
248   }
249 };
250 
251 // Used to match a ScoredDocumentHit
252 MATCHER_P(EqualsScoredDocumentHit, expected_scored_document_hit, "") {
253   ScoredDocumentHitEqualComparator equal_comparator;
254   if (!equal_comparator(arg, expected_scored_document_hit)) {
255     ScoredDocumentHitFormatter formatter;
256     *result_listener << "Expected: " << formatter(expected_scored_document_hit)
257                      << ". Actual: " << formatter(arg);
258     return false;
259   }
260   return true;
261 }
262 
263 // Used to match a JoinedScoredDocumentHit
264 MATCHER_P(EqualsJoinedScoredDocumentHit, expected_joined_scored_document_hit,
265           "") {
266   ScoredDocumentHitEqualComparator equal_comparator;
267   if (std::fabs(arg.final_score() -
268                 expected_joined_scored_document_hit.final_score()) > 1e-6 ||
269       !equal_comparator(
270           arg.parent_scored_document_hit(),
271           expected_joined_scored_document_hit.parent_scored_document_hit()) ||
272       arg.child_scored_document_hits().size() !=
273           expected_joined_scored_document_hit.child_scored_document_hits()
274               .size() ||
275       !std::equal(
276           arg.child_scored_document_hits().cbegin(),
277           arg.child_scored_document_hits().cend(),
278           expected_joined_scored_document_hit.child_scored_document_hits()
279               .cbegin(),
280           equal_comparator)) {
281     ScoredDocumentHitFormatter formatter;
282 
283     *result_listener << IcingStringUtil::StringPrintf(
284         "Expected: final_score=%.2f, parent_scored_document_hit=%s, "
285         "child_scored_document_hits=[%s]. Actual: final_score=%.2f, "
286         "parent_scored_document_hit=%s, child_scored_document_hits=[%s]",
287         expected_joined_scored_document_hit.final_score(),
288         formatter(
289             expected_joined_scored_document_hit.parent_scored_document_hit())
290             .c_str(),
291         absl_ports::StrJoin(
292             expected_joined_scored_document_hit.child_scored_document_hits(),
293             ",", formatter)
294             .c_str(),
295         arg.final_score(), formatter(arg.parent_scored_document_hit()).c_str(),
296         absl_ports::StrJoin(arg.child_scored_document_hits(), ",", formatter)
297             .c_str());
298     return false;
299   }
300   return true;
301 }
302 
303 MATCHER_P(EqualsSetSchemaResult, expected, "") {
304   const SchemaStore::SetSchemaResult& actual = arg;
305 
306   if (actual.success == expected.success &&
307       actual.old_schema_type_ids_changed ==
308           expected.old_schema_type_ids_changed &&
309       actual.schema_types_deleted_by_name ==
310           expected.schema_types_deleted_by_name &&
311       actual.schema_types_deleted_by_id ==
312           expected.schema_types_deleted_by_id &&
313       actual.schema_types_incompatible_by_name ==
314           expected.schema_types_incompatible_by_name &&
315       actual.schema_types_incompatible_by_id ==
316           expected.schema_types_incompatible_by_id &&
317       actual.schema_types_new_by_name == expected.schema_types_new_by_name &&
318       actual.schema_types_changed_fully_compatible_by_name ==
319           expected.schema_types_changed_fully_compatible_by_name &&
320       actual.schema_types_index_incompatible_by_name ==
321           expected.schema_types_index_incompatible_by_name &&
322       actual.schema_types_join_incompatible_by_name ==
323           expected.schema_types_join_incompatible_by_name &&
324       actual.schema_types_scorable_property_inconsistent_by_id ==
325           expected.schema_types_scorable_property_inconsistent_by_id) {
326     return true;
327   }
328 
329   // Format schema_type_ids_changed
330   std::string actual_old_schema_type_ids_changed = absl_ports::StrCat(
331       "[",
332       absl_ports::StrJoin(actual.old_schema_type_ids_changed, ",",
333                           absl_ports::NumberFormatter()),
334       "]");
335 
336   std::string expected_old_schema_type_ids_changed = absl_ports::StrCat(
337       "[",
338       absl_ports::StrJoin(expected.old_schema_type_ids_changed, ",",
339                           absl_ports::NumberFormatter()),
340       "]");
341 
342   // Format schema_types_deleted_by_name
343   std::string actual_schema_types_deleted_by_name = absl_ports::StrCat(
344       "[", absl_ports::StrJoin(actual.schema_types_deleted_by_name, ","), "]");
345 
346   std::string expected_schema_types_deleted_by_name = absl_ports::StrCat(
347       "[", absl_ports::StrJoin(expected.schema_types_deleted_by_name, ","),
348       "]");
349 
350   // Format schema_types_deleted_by_id
351   std::string actual_schema_types_deleted_by_id = absl_ports::StrCat(
352       "[",
353       absl_ports::StrJoin(actual.schema_types_deleted_by_id, ",",
354                           absl_ports::NumberFormatter()),
355       "]");
356 
357   std::string expected_schema_types_deleted_by_id = absl_ports::StrCat(
358       "[",
359       absl_ports::StrJoin(expected.schema_types_deleted_by_id, ",",
360                           absl_ports::NumberFormatter()),
361       "]");
362 
363   // Format schema_types_incompatible_by_name
364   std::string actual_schema_types_incompatible_by_name = absl_ports::StrCat(
365       "[", absl_ports::StrJoin(actual.schema_types_incompatible_by_name, ","),
366       "]");
367 
368   std::string expected_schema_types_incompatible_by_name = absl_ports::StrCat(
369       "[", absl_ports::StrJoin(expected.schema_types_incompatible_by_name, ","),
370       "]");
371 
372   // Format schema_types_incompatible_by_id
373   std::string actual_schema_types_incompatible_by_id = absl_ports::StrCat(
374       "[",
375       absl_ports::StrJoin(actual.schema_types_incompatible_by_id, ",",
376                           absl_ports::NumberFormatter()),
377       "]");
378 
379   std::string expected_schema_types_incompatible_by_id = absl_ports::StrCat(
380       "[",
381       absl_ports::StrJoin(expected.schema_types_incompatible_by_id, ",",
382                           absl_ports::NumberFormatter()),
383       "]");
384 
385   // Format schema_types_new_by_name
386   std::string actual_schema_types_new_by_name = absl_ports::StrCat(
387       "[", absl_ports::StrJoin(actual.schema_types_new_by_name, ","), "]");
388 
389   std::string expected_schema_types_new_by_name = absl_ports::StrCat(
390       "[", absl_ports::StrJoin(expected.schema_types_new_by_name, ","), "]");
391 
392   // Format schema_types_changed_fully_compatible_by_name
393   std::string actual_schema_types_changed_fully_compatible_by_name =
394       absl_ports::StrCat(
395           "[",
396           absl_ports::StrJoin(
397               actual.schema_types_changed_fully_compatible_by_name, ","),
398           "]");
399 
400   std::string expected_schema_types_changed_fully_compatible_by_name =
401       absl_ports::StrCat(
402           "[",
403           absl_ports::StrJoin(
404               expected.schema_types_changed_fully_compatible_by_name, ","),
405           "]");
406 
407   // Format schema_types_deleted_by_id
408   std::string actual_schema_types_index_incompatible_by_name =
409       absl_ports::StrCat(
410           "[",
411           absl_ports::StrJoin(actual.schema_types_index_incompatible_by_name,
412                               ","),
413           "]");
414 
415   std::string expected_schema_types_index_incompatible_by_name =
416       absl_ports::StrCat(
417           "[",
418           absl_ports::StrJoin(expected.schema_types_index_incompatible_by_name,
419                               ","),
420           "]");
421 
422   // Format schema_types_join_incompatible_by_name
423   std::string actual_schema_types_join_incompatible_by_name =
424       absl_ports::StrCat(
425           "[",
426           absl_ports::StrJoin(actual.schema_types_join_incompatible_by_name,
427                               ","),
428           "]");
429 
430   std::string expected_schema_types_join_incompatible_by_name =
431       absl_ports::StrCat(
432           "[",
433           absl_ports::StrJoin(expected.schema_types_join_incompatible_by_name,
434                               ","),
435           "]");
436 
437   *result_listener << IcingStringUtil::StringPrintf(
438       "\nExpected {\n"
439       "\tsuccess=%d,\n"
440       "\told_schema_type_ids_changed=%s,\n"
441       "\tschema_types_deleted_by_name=%s,\n"
442       "\tschema_types_deleted_by_id=%s,\n"
443       "\tschema_types_incompatible_by_name=%s,\n"
444       "\tschema_types_incompatible_by_id=%s\n"
445       "\tschema_types_new_by_name=%s,\n"
446       "\tschema_types_changed_fully_compatible_by_name=%s\n"
447       "\tschema_types_index_incompatible_by_name=%s,\n"
448       "\tschema_types_join_incompatible_by_name=%s\n"
449       "}\n"
450       "Actual {\n"
451       "\tsuccess=%d,\n"
452       "\told_schema_type_ids_changed=%s,\n"
453       "\tschema_types_deleted_by_name=%s,\n"
454       "\tschema_types_deleted_by_id=%s,\n"
455       "\tschema_types_incompatible_by_name=%s,\n"
456       "\tschema_types_incompatible_by_id=%s\n"
457       "\tschema_types_new_by_name=%s,\n"
458       "\tschema_types_changed_fully_compatible_by_name=%s\n"
459       "\tschema_types_index_incompatible_by_name=%s,\n"
460       "\tschema_types_join_incompatible_by_name=%s\n"
461       "}\n",
462       expected.success, expected_old_schema_type_ids_changed.c_str(),
463       expected_schema_types_deleted_by_name.c_str(),
464       expected_schema_types_deleted_by_id.c_str(),
465       expected_schema_types_incompatible_by_name.c_str(),
466       expected_schema_types_incompatible_by_id.c_str(),
467       expected_schema_types_new_by_name.c_str(),
468       expected_schema_types_changed_fully_compatible_by_name.c_str(),
469       expected_schema_types_index_incompatible_by_name.c_str(),
470       expected_schema_types_join_incompatible_by_name.c_str(), actual.success,
471       actual_old_schema_type_ids_changed.c_str(),
472       actual_schema_types_deleted_by_name.c_str(),
473       actual_schema_types_deleted_by_id.c_str(),
474       actual_schema_types_incompatible_by_name.c_str(),
475       actual_schema_types_incompatible_by_id.c_str(),
476       actual_schema_types_new_by_name.c_str(),
477       actual_schema_types_changed_fully_compatible_by_name.c_str(),
478       actual_schema_types_index_incompatible_by_name.c_str(),
479       actual_schema_types_join_incompatible_by_name.c_str());
480   return false;
481 }
482 
483 MATCHER_P3(EqualsSectionMetadata, expected_id, expected_property_path,
484            expected_property_config_proto, "") {
485   const SectionMetadata& actual = arg;
486   return actual.id == expected_id && actual.path == expected_property_path &&
487          actual.data_type == expected_property_config_proto.data_type() &&
488          actual.tokenizer ==
489              expected_property_config_proto.string_indexing_config()
490                  .tokenizer_type() &&
491          actual.term_match_type ==
492              expected_property_config_proto.string_indexing_config()
493                  .term_match_type() &&
494          actual.numeric_match_type ==
495              expected_property_config_proto.integer_indexing_config()
496                  .numeric_match_type() &&
497          actual.embedding_indexing_type ==
498              expected_property_config_proto.embedding_indexing_config()
499                  .embedding_indexing_type() &&
500          actual.quantization_type ==
501              expected_property_config_proto.embedding_indexing_config()
502                  .quantization_type();
503 }
504 
505 MATCHER_P3(EqualsJoinablePropertyMetadata, expected_id, expected_property_path,
506            expected_property_config_proto, "") {
507   const JoinablePropertyMetadata& actual = arg;
508   return actual.id == expected_id && actual.path == expected_property_path &&
509          actual.data_type == expected_property_config_proto.data_type() &&
510          actual.value_type ==
511              expected_property_config_proto.joinable_config().value_type() &&
512          actual.delete_propagation_type ==
513              expected_property_config_proto.joinable_config()
514                  .delete_propagation_type();
515 }
516 
517 std::string StatusCodeToString(libtextclassifier3::StatusCode code);
518 
519 std::string ProtoStatusCodeToString(StatusProto::Code code);
520 
521 MATCHER(IsOk, "") {
522   libtextclassifier3::StatusAdapter adapter(arg);
523   if (adapter.status().ok()) {
524     return true;
525   }
526   *result_listener << IcingStringUtil::StringPrintf(
527       "Expected OK, actual was (%s:%s)",
528       StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
529       adapter.status().error_message().c_str());
530   return false;
531 }
532 
533 MATCHER_P(IsOkAndHolds, matcher, "") {
534   if (!arg.ok()) {
535     *result_listener << IcingStringUtil::StringPrintf(
536         "Expected OK, actual was (%s:%s)",
537         StatusCodeToString(arg.status().CanonicalCode()).c_str(),
538         arg.status().error_message().c_str());
539     return false;
540   }
541   return ExplainMatchResult(matcher, arg.ValueOrDie(), result_listener);
542 }
543 
544 MATCHER_P(StatusIs, status_code, "") {
545   libtextclassifier3::StatusAdapter adapter(arg);
546   if (adapter.status().CanonicalCode() == status_code) {
547     return true;
548   }
549   *result_listener << IcingStringUtil::StringPrintf(
550       "Expected (%s:), actual was (%s:%s)",
551       StatusCodeToString(status_code).c_str(),
552       StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
553       adapter.status().error_message().c_str());
554   return false;
555 }
556 
557 MATCHER_P2(StatusIs, status_code, error_matcher, "") {
558   libtextclassifier3::StatusAdapter adapter(arg);
559   if (adapter.status().CanonicalCode() != status_code) {
560     *result_listener << IcingStringUtil::StringPrintf(
561         "Expected (%s:), actual was (%s:%s)",
562         StatusCodeToString(status_code).c_str(),
563         StatusCodeToString(adapter.status().CanonicalCode()).c_str(),
564         adapter.status().error_message().c_str());
565     return false;
566   }
567   return ExplainMatchResult(error_matcher, adapter.status().error_message(),
568                             result_listener);
569 }
570 
571 MATCHER(ProtoIsOk, "") {
572   if (arg.code() == StatusProto::OK) {
573     return true;
574   }
575   *result_listener << IcingStringUtil::StringPrintf(
576       "Expected OK, actual was (%s:%s)",
577       ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
578   return false;
579 }
580 
581 MATCHER_P(ProtoStatusIs, status_code, "") {
582   if (arg.code() == status_code) {
583     return true;
584   }
585   *result_listener << IcingStringUtil::StringPrintf(
586       "Expected (%s:), actual was (%s:%s)",
587       ProtoStatusCodeToString(status_code).c_str(),
588       ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
589   return false;
590 }
591 
592 MATCHER_P2(ProtoStatusIs, status_code, error_matcher, "") {
593   if (arg.code() != status_code) {
594     *result_listener << IcingStringUtil::StringPrintf(
595         "Expected (%s:), actual was (%s:%s)",
596         ProtoStatusCodeToString(status_code).c_str(),
597         ProtoStatusCodeToString(arg.code()).c_str(), arg.message().c_str());
598     return false;
599   }
600   return ExplainMatchResult(error_matcher, arg.message(), result_listener);
601 }
602 
603 MATCHER_P(EqualsSearchResultIgnoreStatsAndScores, expected, "") {
604   SearchResultProto actual_copy = arg;
605   actual_copy.clear_query_stats();
606   actual_copy.clear_debug_info();
607   for (SearchResultProto::ResultProto& result :
608        *actual_copy.mutable_results()) {
609     // Joined results
610     for (SearchResultProto::ResultProto& joined_result :
611          *result.mutable_joined_results()) {
612       joined_result.clear_score();
613     }
614     result.clear_score();
615   }
616 
617   SearchResultProto expected_copy = expected;
618   expected_copy.clear_query_stats();
619   expected_copy.clear_debug_info();
620   for (SearchResultProto::ResultProto& result :
621        *expected_copy.mutable_results()) {
622     // Joined results
623     for (SearchResultProto::ResultProto& joined_result :
624          *result.mutable_joined_results()) {
625       joined_result.clear_score();
626     }
627     result.clear_score();
628   }
629   return ExplainMatchResult(portable_equals_proto::EqualsProto(expected_copy),
630                             actual_copy, result_listener);
631 }
632 
633 MATCHER_P(EqualsHit, expected_hit, "") {
634   const Hit& actual = arg;
635   return actual.value() == expected_hit.value() &&
636          actual.flags() == expected_hit.flags() &&
637          actual.term_frequency() == expected_hit.term_frequency();
638 }
639 
640 MATCHER(EqualsHit, "") {
641   return ExplainMatchResult(EqualsHit(std::get<1>(arg)), std::get<0>(arg),
642                             result_listener);
643 }
644 
645 // TODO(tjbarron) Remove this once icing has switched to depend on TC3 Status
646 #define ICING_STATUS_MACROS_CONCAT_NAME(x, y) \
647   ICING_STATUS_MACROS_CONCAT_IMPL(x, y)
648 #define ICING_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
649 
650 #define ICING_EXPECT_OK(func) EXPECT_THAT(func, IsOk())
651 #define ICING_ASSERT_OK(func) ASSERT_THAT(func, IsOk())
652 #define ICING_ASSERT_OK_AND_ASSIGN(lhs, rexpr)                             \
653   ICING_ASSERT_OK_AND_ASSIGN_IMPL(                                         \
654       ICING_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
655       rexpr)
656 #define ICING_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
657   auto statusor = (rexpr);                                    \
658   ICING_ASSERT_OK(statusor.status());                         \
659   lhs = std::move(statusor).ValueOrDie()
660 
661 #define ICING_ASSERT_HAS_VALUE_AND_ASSIGN(lhs, rexpr) \
662   ASSERT_TRUE(rexpr);                                 \
663   lhs = rexpr.value()
664 
665 }  // namespace lib
666 }  // namespace icing
667 
668 #endif  // ICING_TESTING_COMMON_MATCHERS_H_
669