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