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