1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/drive/search_metadata.h" 6 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/i18n/string_search.h" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/run_loop.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" 14 #include "chrome/browser/chromeos/drive/file_cache.h" 15 #include "chrome/browser/chromeos/drive/file_system_util.h" 16 #include "chrome/browser/chromeos/drive/test_util.h" 17 #include "content/public/test/test_browser_thread_bundle.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace drive { 21 namespace internal { 22 23 namespace { 24 25 const int kDefaultAtMostNumMatches = 10; 26 const int64 kCacheEntriesLastAccessedTimeBase = 100; 27 28 // A simple wrapper for testing FindAndHighlightWrapper(). It just converts the 29 // query text parameter to FixedPatternStringSearchIgnoringCaseAndAccents. 30 bool FindAndHighlightWrapper( 31 const std::string& text, 32 const std::string& query_text, 33 std::string* highlighted_text) { 34 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query( 35 base::UTF8ToUTF16(query_text)); 36 return FindAndHighlight(text, &query, highlighted_text); 37 } 38 39 // Generator of sequential fake data for ResourceEntry. 40 class MetadataInfoGenerator { 41 public: 42 // Constructor of EntryInfoGenerator. |prefix| is prefix of resource IDs and 43 // |last_accessed_base| is the first value to be generated as a last accessed 44 // time. 45 MetadataInfoGenerator(const std::string& prefix, 46 int last_accessed_base) : 47 prefix_(prefix), 48 id_counter_(0), 49 last_accessed_counter_(last_accessed_base) {} 50 51 // Obtains resource ID that is consists of the prefix and a sequential 52 // number. 53 std::string GetId() const { 54 return base::StringPrintf("%s%d", prefix_.c_str(), id_counter_); 55 } 56 57 // Obtains the fake last accessed time that is sequential number following 58 // |last_accessed_base| specified at the constructor. 59 int64 GetLastAccessed() const { 60 return last_accessed_counter_; 61 } 62 63 // Advances counters to generate the next ID and last accessed time. 64 void Advance() { 65 ++id_counter_; 66 ++last_accessed_counter_; 67 } 68 69 private: 70 std::string prefix_; 71 int id_counter_; 72 int64 last_accessed_counter_; 73 }; 74 75 } // namespace 76 77 class SearchMetadataTest : public testing::Test { 78 protected: 79 virtual void SetUp() OVERRIDE { 80 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 81 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); 82 83 metadata_storage_.reset(new ResourceMetadataStorage( 84 temp_dir_.path(), base::MessageLoopProxy::current().get())); 85 ASSERT_TRUE(metadata_storage_->Initialize()); 86 87 cache_.reset(new FileCache(metadata_storage_.get(), 88 temp_dir_.path(), 89 base::MessageLoopProxy::current().get(), 90 fake_free_disk_space_getter_.get())); 91 ASSERT_TRUE(cache_->Initialize()); 92 93 resource_metadata_.reset( 94 new ResourceMetadata(metadata_storage_.get(), 95 base::MessageLoopProxy::current())); 96 ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize()); 97 98 AddEntriesToMetadata(); 99 } 100 101 void AddEntriesToMetadata() { 102 ResourceEntry entry; 103 104 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( 105 util::kDriveMyDriveRootDirName, "root", 100, 106 util::kDriveGrandRootSpecialResourceId))); 107 108 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( 109 "Directory 1", "dir1", 1, "root"))); 110 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry( 111 "SubDirectory File 1.txt", "file1a", 2, "dir1"))); 112 113 entry = GetFileEntry( 114 "Shared To The Account Owner.txt", "file1b", 3, "dir1"); 115 entry.set_shared_with_me(true); 116 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry)); 117 118 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( 119 "Directory 2 excludeDir-test", "dir2", 4, "root"))); 120 121 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( 122 "Slash \xE2\x88\x95 in directory", "dir3", 5, "root"))); 123 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry( 124 "Slash SubDir File.txt", "file3a", 6, "dir3"))); 125 126 entry = GetFileEntry( 127 "Document 1 excludeDir-test", "doc1", 7, "root"); 128 entry.mutable_file_specific_info()->set_is_hosted_document(true); 129 entry.mutable_file_specific_info()->set_document_extension(".gdoc"); 130 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry)); 131 } 132 133 // Adds a directory at |path|. Parent directories are added if needed just 134 // like "mkdir -p" does. 135 std::string AddDirectoryToMetadataWithParents( 136 const base::FilePath& path, 137 MetadataInfoGenerator* generator) { 138 if (path == base::FilePath(base::FilePath::kCurrentDirectory)) 139 return "root"; 140 141 { 142 ResourceEntry entry; 143 FileError error = resource_metadata_->GetResourceEntryByPath( 144 util::GetDriveMyDriveRootPath().Append(path), &entry); 145 if (error == FILE_ERROR_OK) 146 return entry.resource_id(); 147 } 148 149 const std::string parent_id = 150 AddDirectoryToMetadataWithParents(path.DirName(), generator); 151 const std::string id = generator->GetId(); 152 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry( 153 GetDirectoryEntry(path.BaseName().AsUTF8Unsafe(), 154 id, 155 generator->GetLastAccessed(), 156 parent_id))); 157 generator->Advance(); 158 return id; 159 } 160 161 // Adds entries for |cache_resources| to |resource_metadata_|. The parent 162 // directories of |resources| is also added. 163 void AddEntriesToMetadataFromCache( 164 const std::vector<test_util::TestCacheResource>& cache_resources, 165 MetadataInfoGenerator* generator) { 166 for (size_t i = 0; i < cache_resources.size(); ++i) { 167 const test_util::TestCacheResource& resource = cache_resources[i]; 168 const base::FilePath path(resource.source_file); 169 const std::string parent_id = 170 AddDirectoryToMetadataWithParents(path.DirName(), generator); 171 EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry( 172 GetFileEntry(path.BaseName().AsUTF8Unsafe(), 173 resource.resource_id, 174 generator->GetLastAccessed(), 175 parent_id))); 176 generator->Advance(); 177 } 178 } 179 180 ResourceEntry GetFileEntry(const std::string& name, 181 const std::string& resource_id, 182 int64 last_accessed, 183 const std::string& parent_resource_id) { 184 ResourceEntry entry; 185 entry.set_title(name); 186 entry.set_resource_id(resource_id); 187 entry.set_parent_resource_id(parent_resource_id); 188 entry.mutable_file_info()->set_last_accessed(last_accessed); 189 return entry; 190 } 191 192 ResourceEntry GetDirectoryEntry(const std::string& name, 193 const std::string& resource_id, 194 int64 last_accessed, 195 const std::string& parent_resource_id) { 196 ResourceEntry entry; 197 entry.set_title(name); 198 entry.set_resource_id(resource_id); 199 entry.set_parent_resource_id(parent_resource_id); 200 entry.mutable_file_info()->set_last_accessed(last_accessed); 201 entry.mutable_file_info()->set_is_directory(true); 202 return entry; 203 } 204 205 content::TestBrowserThreadBundle thread_bundle_; 206 base::ScopedTempDir temp_dir_; 207 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; 208 scoped_ptr<ResourceMetadataStorage, 209 test_util::DestroyHelperForTests> metadata_storage_; 210 scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> 211 resource_metadata_; 212 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 213 }; 214 215 TEST_F(SearchMetadataTest, SearchMetadata_ZeroMatches) { 216 FileError error = FILE_ERROR_FAILED; 217 scoped_ptr<MetadataSearchResultVector> result; 218 219 SearchMetadata(base::MessageLoopProxy::current(), 220 resource_metadata_.get(), 221 "NonExistent", 222 SEARCH_METADATA_ALL, 223 kDefaultAtMostNumMatches, 224 google_apis::test_util::CreateCopyResultCallback( 225 &error, &result)); 226 base::RunLoop().RunUntilIdle(); 227 EXPECT_EQ(FILE_ERROR_OK, error); 228 ASSERT_TRUE(result); 229 ASSERT_EQ(0U, result->size()); 230 } 231 232 TEST_F(SearchMetadataTest, SearchMetadata_RegularFile) { 233 FileError error = FILE_ERROR_FAILED; 234 scoped_ptr<MetadataSearchResultVector> result; 235 236 SearchMetadata(base::MessageLoopProxy::current(), 237 resource_metadata_.get(), 238 "SubDirectory File 1.txt", 239 SEARCH_METADATA_ALL, 240 kDefaultAtMostNumMatches, 241 google_apis::test_util::CreateCopyResultCallback( 242 &error, &result)); 243 base::RunLoop().RunUntilIdle(); 244 EXPECT_EQ(FILE_ERROR_OK, error); 245 ASSERT_TRUE(result); 246 ASSERT_EQ(1U, result->size()); 247 EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", 248 result->at(0).path.AsUTF8Unsafe()); 249 } 250 251 // This test checks if |FindAndHighlightWrapper| does case-insensitive search. 252 // Tricker test cases for |FindAndHighlightWrapper| can be found below. 253 TEST_F(SearchMetadataTest, SearchMetadata_CaseInsensitiveSearch) { 254 FileError error = FILE_ERROR_FAILED; 255 scoped_ptr<MetadataSearchResultVector> result; 256 257 // The query is all in lower case. 258 SearchMetadata(base::MessageLoopProxy::current(), 259 resource_metadata_.get(), 260 "subdirectory file 1.txt", 261 SEARCH_METADATA_ALL, 262 kDefaultAtMostNumMatches, 263 google_apis::test_util::CreateCopyResultCallback( 264 &error, &result)); 265 base::RunLoop().RunUntilIdle(); 266 EXPECT_EQ(FILE_ERROR_OK, error); 267 ASSERT_TRUE(result); 268 ASSERT_EQ(1U, result->size()); 269 EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", 270 result->at(0).path.AsUTF8Unsafe()); 271 } 272 273 TEST_F(SearchMetadataTest, SearchMetadata_RegularFiles) { 274 FileError error = FILE_ERROR_FAILED; 275 scoped_ptr<MetadataSearchResultVector> result; 276 277 SearchMetadata(base::MessageLoopProxy::current(), 278 resource_metadata_.get(), 279 "SubDir", 280 SEARCH_METADATA_ALL, 281 kDefaultAtMostNumMatches, 282 google_apis::test_util::CreateCopyResultCallback( 283 &error, &result)); 284 base::RunLoop().RunUntilIdle(); 285 EXPECT_EQ(FILE_ERROR_OK, error); 286 ASSERT_TRUE(result); 287 ASSERT_EQ(2U, result->size()); 288 289 // The results should be sorted by the last accessed time in descending order. 290 EXPECT_EQ(6, result->at(0).entry.file_info().last_accessed()); 291 EXPECT_EQ(2, result->at(1).entry.file_info().last_accessed()); 292 293 // All base names should contain "File". 294 EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt", 295 result->at(0).path.AsUTF8Unsafe()); 296 EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", 297 result->at(1).path.AsUTF8Unsafe()); 298 } 299 300 TEST_F(SearchMetadataTest, SearchMetadata_AtMostOneFile) { 301 FileError error = FILE_ERROR_FAILED; 302 scoped_ptr<MetadataSearchResultVector> result; 303 304 // There are two files matching "SubDir" but only one file should be 305 // returned. 306 SearchMetadata(base::MessageLoopProxy::current(), 307 resource_metadata_.get(), 308 "SubDir", 309 SEARCH_METADATA_ALL, 310 1, // at_most_num_matches 311 google_apis::test_util::CreateCopyResultCallback( 312 &error, &result)); 313 base::RunLoop().RunUntilIdle(); 314 EXPECT_EQ(FILE_ERROR_OK, error); 315 ASSERT_TRUE(result); 316 ASSERT_EQ(1U, result->size()); 317 EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt", 318 result->at(0).path.AsUTF8Unsafe()); 319 } 320 321 TEST_F(SearchMetadataTest, SearchMetadata_Directory) { 322 FileError error = FILE_ERROR_FAILED; 323 scoped_ptr<MetadataSearchResultVector> result; 324 325 SearchMetadata(base::MessageLoopProxy::current(), 326 resource_metadata_.get(), 327 "Directory 1", 328 SEARCH_METADATA_ALL, 329 kDefaultAtMostNumMatches, 330 google_apis::test_util::CreateCopyResultCallback( 331 &error, &result)); 332 base::RunLoop().RunUntilIdle(); 333 EXPECT_EQ(FILE_ERROR_OK, error); 334 ASSERT_TRUE(result); 335 ASSERT_EQ(1U, result->size()); 336 EXPECT_EQ("drive/root/Directory 1", result->at(0).path.AsUTF8Unsafe()); 337 } 338 339 TEST_F(SearchMetadataTest, SearchMetadata_HostedDocument) { 340 FileError error = FILE_ERROR_FAILED; 341 scoped_ptr<MetadataSearchResultVector> result; 342 343 SearchMetadata(base::MessageLoopProxy::current(), 344 resource_metadata_.get(), 345 "Document", 346 SEARCH_METADATA_ALL, 347 kDefaultAtMostNumMatches, 348 google_apis::test_util::CreateCopyResultCallback( 349 &error, &result)); 350 base::RunLoop().RunUntilIdle(); 351 EXPECT_EQ(FILE_ERROR_OK, error); 352 ASSERT_TRUE(result); 353 ASSERT_EQ(1U, result->size()); 354 355 EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", 356 result->at(0).path.AsUTF8Unsafe()); 357 } 358 359 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeHostedDocument) { 360 FileError error = FILE_ERROR_FAILED; 361 scoped_ptr<MetadataSearchResultVector> result; 362 363 SearchMetadata(base::MessageLoopProxy::current(), 364 resource_metadata_.get(), 365 "Document", 366 SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS, 367 kDefaultAtMostNumMatches, 368 google_apis::test_util::CreateCopyResultCallback( 369 &error, &result)); 370 base::RunLoop().RunUntilIdle(); 371 EXPECT_EQ(FILE_ERROR_OK, error); 372 ASSERT_TRUE(result); 373 ASSERT_EQ(0U, result->size()); 374 } 375 376 TEST_F(SearchMetadataTest, SearchMetadata_SharedWithMe) { 377 FileError error = FILE_ERROR_FAILED; 378 scoped_ptr<MetadataSearchResultVector> result; 379 380 SearchMetadata(base::MessageLoopProxy::current(), 381 resource_metadata_.get(), 382 "", 383 SEARCH_METADATA_SHARED_WITH_ME, 384 kDefaultAtMostNumMatches, 385 google_apis::test_util::CreateCopyResultCallback( 386 &error, &result)); 387 base::RunLoop().RunUntilIdle(); 388 EXPECT_EQ(FILE_ERROR_OK, error); 389 ASSERT_TRUE(result); 390 ASSERT_EQ(1U, result->size()); 391 EXPECT_EQ("drive/root/Directory 1/Shared To The Account Owner.txt", 392 result->at(0).path.AsUTF8Unsafe()); 393 } 394 395 TEST_F(SearchMetadataTest, SearchMetadata_FileAndDirectory) { 396 FileError error = FILE_ERROR_FAILED; 397 scoped_ptr<MetadataSearchResultVector> result; 398 399 SearchMetadata(base::MessageLoopProxy::current(), 400 resource_metadata_.get(), 401 "excludeDir-test", 402 SEARCH_METADATA_ALL, 403 kDefaultAtMostNumMatches, 404 google_apis::test_util::CreateCopyResultCallback( 405 &error, &result)); 406 407 base::RunLoop().RunUntilIdle(); 408 EXPECT_EQ(FILE_ERROR_OK, error); 409 ASSERT_TRUE(result); 410 ASSERT_EQ(2U, result->size()); 411 412 EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", 413 result->at(0).path.AsUTF8Unsafe()); 414 EXPECT_EQ("drive/root/Directory 2 excludeDir-test", 415 result->at(1).path.AsUTF8Unsafe()); 416 } 417 418 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeDirectory) { 419 FileError error = FILE_ERROR_FAILED; 420 scoped_ptr<MetadataSearchResultVector> result; 421 422 SearchMetadata(base::MessageLoopProxy::current(), 423 resource_metadata_.get(), 424 "excludeDir-test", 425 SEARCH_METADATA_EXCLUDE_DIRECTORIES, 426 kDefaultAtMostNumMatches, 427 google_apis::test_util::CreateCopyResultCallback( 428 &error, &result)); 429 430 base::RunLoop().RunUntilIdle(); 431 EXPECT_EQ(FILE_ERROR_OK, error); 432 ASSERT_TRUE(result); 433 ASSERT_EQ(1U, result->size()); 434 435 EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", 436 result->at(0).path.AsUTF8Unsafe()); 437 } 438 439 // "drive", "drive/root", "drive/other" should be excluded. 440 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeSpecialDirectories) { 441 const char* kQueries[] = { "drive", "root", "other" }; 442 for (size_t i = 0; i < arraysize(kQueries); ++i) { 443 FileError error = FILE_ERROR_FAILED; 444 scoped_ptr<MetadataSearchResultVector> result; 445 446 const std::string query = kQueries[i]; 447 SearchMetadata(base::MessageLoopProxy::current(), 448 resource_metadata_.get(), 449 query, 450 SEARCH_METADATA_ALL, 451 kDefaultAtMostNumMatches, 452 google_apis::test_util::CreateCopyResultCallback( 453 &error, &result)); 454 455 base::RunLoop().RunUntilIdle(); 456 EXPECT_EQ(FILE_ERROR_OK, error); 457 ASSERT_TRUE(result); 458 ASSERT_TRUE(result->empty()) << ": " << query << " should not match"; 459 } 460 } 461 462 TEST_F(SearchMetadataTest, SearchMetadata_Offline) { 463 const std::vector<test_util::TestCacheResource> cache_resources = 464 test_util::GetDefaultTestCacheResources(); 465 ASSERT_TRUE(test_util::PrepareTestCacheResources(cache_.get(), 466 cache_resources)); 467 { 468 MetadataInfoGenerator generator("cache", kCacheEntriesLastAccessedTimeBase); 469 AddEntriesToMetadataFromCache(cache_resources, &generator); 470 } 471 FileError error = FILE_ERROR_FAILED; 472 scoped_ptr<MetadataSearchResultVector> result; 473 474 SearchMetadata(base::MessageLoopProxy::current(), 475 resource_metadata_.get(), 476 "", 477 SEARCH_METADATA_OFFLINE, 478 kDefaultAtMostNumMatches, 479 google_apis::test_util::CreateCopyResultCallback( 480 &error, &result)); 481 base::RunLoop().RunUntilIdle(); 482 EXPECT_EQ(FILE_ERROR_OK, error); 483 ASSERT_EQ(6U, result->size()); 484 485 // Newer entries are listed earlier. So, the results come in the reverse 486 // order of addition written in test_util::GetDefaultTestCacheResources. 487 EXPECT_EQ("drive/root/pinned/dirty/cache.pdf", 488 result->at(0).path.AsUTF8Unsafe()); 489 EXPECT_EQ("drive/root/dirty/cache.avi", 490 result->at(1).path.AsUTF8Unsafe()); 491 EXPECT_EQ("drive/root/pinned/cache.mp3", 492 result->at(2).path.AsUTF8Unsafe()); 493 EXPECT_EQ("drive/root/cache2.png", 494 result->at(3).path.AsUTF8Unsafe()); 495 EXPECT_EQ("drive/root/cache.txt", 496 result->at(4).path.AsUTF8Unsafe()); 497 // This is not included in the cache but is a hosted document. 498 EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", 499 result->at(5).path.AsUTF8Unsafe()); 500 } 501 502 TEST(SearchMetadataSimpleTest, FindAndHighlight_ZeroMatches) { 503 std::string highlighted_text; 504 EXPECT_FALSE(FindAndHighlightWrapper("text", "query", &highlighted_text)); 505 } 506 507 TEST(SearchMetadataSimpleTest, FindAndHighlight_EmptyText) { 508 std::string highlighted_text; 509 EXPECT_FALSE(FindAndHighlightWrapper("", "query", &highlighted_text)); 510 } 511 512 TEST(SearchMetadataSimpleTest, FindAndHighlight_FullMatch) { 513 std::string highlighted_text; 514 EXPECT_TRUE(FindAndHighlightWrapper("hello", "hello", &highlighted_text)); 515 EXPECT_EQ("<b>hello</b>", highlighted_text); 516 } 517 518 TEST(SearchMetadataSimpleTest, FindAndHighlight_StartWith) { 519 std::string highlighted_text; 520 EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "hello", 521 &highlighted_text)); 522 EXPECT_EQ("<b>hello</b>, world", highlighted_text); 523 } 524 525 TEST(SearchMetadataSimpleTest, FindAndHighlight_EndWith) { 526 std::string highlighted_text; 527 EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "world", 528 &highlighted_text)); 529 EXPECT_EQ("hello, <b>world</b>", highlighted_text); 530 } 531 532 TEST(SearchMetadataSimpleTest, FindAndHighlight_InTheMiddle) { 533 std::string highlighted_text; 534 EXPECT_TRUE(FindAndHighlightWrapper("yo hello, world", "hello", 535 &highlighted_text)); 536 EXPECT_EQ("yo <b>hello</b>, world", highlighted_text); 537 } 538 539 TEST(SearchMetadataSimpleTest, FindAndHighlight_MultipeMatches) { 540 std::string highlighted_text; 541 EXPECT_TRUE(FindAndHighlightWrapper("yoyoyoyoy", "yoy", &highlighted_text)); 542 // Only the first match is highlighted. 543 EXPECT_EQ("<b>yoy</b>oyoyoy", highlighted_text); 544 } 545 546 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCase) { 547 std::string highlighted_text; 548 EXPECT_TRUE(FindAndHighlightWrapper("HeLLo", "hello", &highlighted_text)); 549 EXPECT_EQ("<b>HeLLo</b>", highlighted_text); 550 } 551 552 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCaseNonASCII) { 553 std::string highlighted_text; 554 555 // Case and accent ignorance in Greek. Find "socra" in "Socra'tes". 556 EXPECT_TRUE(FindAndHighlightWrapper( 557 "\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC\xCF\x84\xCE\xB7\xCF\x82", 558 "\xCF\x83\xCF\x89\xCE\xBA\xCF\x81\xCE\xB1", &highlighted_text)); 559 EXPECT_EQ( 560 "<b>\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC</b>\xCF\x84\xCE\xB7\xCF\x82", 561 highlighted_text); 562 563 // In Japanese characters. 564 // Find Hiragana "pi" + "(small)ya" in Katakana "hi" + semi-voiced-mark + "ya" 565 EXPECT_TRUE(FindAndHighlightWrapper( 566 "\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83\xE3\x83\xBC", 567 "\xE3\x83\x94\xE3\x83\xA4", 568 &highlighted_text)); 569 EXPECT_EQ( 570 "<b>\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83</b>\xE3\x83\xBC", 571 highlighted_text); 572 } 573 574 TEST(SearchMetadataSimpleTest, MultiTextBySingleQuery) { 575 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query( 576 base::UTF8ToUTF16("hello")); 577 578 std::string highlighted_text; 579 EXPECT_TRUE(FindAndHighlight("hello", &query, &highlighted_text)); 580 EXPECT_EQ("<b>hello</b>", highlighted_text); 581 EXPECT_FALSE(FindAndHighlight("goodbye", &query, &highlighted_text)); 582 EXPECT_TRUE(FindAndHighlight("1hello2", &query, &highlighted_text)); 583 EXPECT_EQ("1<b>hello</b>2", highlighted_text); 584 } 585 586 TEST(SearchMetadataSimpleTest, FindAndHighlight_MetaChars) { 587 std::string highlighted_text; 588 EXPECT_TRUE(FindAndHighlightWrapper("<hello>", "hello", &highlighted_text)); 589 EXPECT_EQ("<<b>hello</b>>", highlighted_text); 590 } 591 592 TEST(SearchMetadataSimpleTest, FindAndHighlight_MoreMetaChars) { 593 std::string highlighted_text; 594 EXPECT_TRUE(FindAndHighlightWrapper("a&b&c&d", "b&c", &highlighted_text)); 595 EXPECT_EQ("a&<b>b&c</b>&d", highlighted_text); 596 } 597 598 } // namespace internal 599 } // namespace drive 600