| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/drive/search_metadata.h" |
| |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/i18n/string_search.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" |
| #include "chrome/browser/chromeos/drive/file_cache.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| #include "chrome/browser/chromeos/drive/test_util.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace drive { |
| namespace internal { |
| |
| namespace { |
| |
| const int kDefaultAtMostNumMatches = 10; |
| const int64 kCacheEntriesLastAccessedTimeBase = 100; |
| |
| // A simple wrapper for testing FindAndHighlightWrapper(). It just converts the |
| // query text parameter to FixedPatternStringSearchIgnoringCaseAndAccents. |
| bool FindAndHighlightWrapper( |
| const std::string& text, |
| const std::string& query_text, |
| std::string* highlighted_text) { |
| base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query( |
| base::UTF8ToUTF16(query_text)); |
| return FindAndHighlight(text, &query, highlighted_text); |
| } |
| |
| // Generator of sequential fake data for ResourceEntry. |
| class MetadataInfoGenerator { |
| public: |
| // Constructor of EntryInfoGenerator. |prefix| is prefix of resource IDs and |
| // |last_accessed_base| is the first value to be generated as a last accessed |
| // time. |
| MetadataInfoGenerator(const std::string& prefix, |
| int last_accessed_base) : |
| prefix_(prefix), |
| id_counter_(0), |
| last_accessed_counter_(last_accessed_base) {} |
| |
| // Obtains resource ID that is consists of the prefix and a sequential |
| // number. |
| std::string GetId() const { |
| return base::StringPrintf("%s%d", prefix_.c_str(), id_counter_); |
| } |
| |
| // Obtains the fake last accessed time that is sequential number following |
| // |last_accessed_base| specified at the constructor. |
| int64 GetLastAccessed() const { |
| return last_accessed_counter_; |
| } |
| |
| // Advances counters to generate the next ID and last accessed time. |
| void Advance() { |
| ++id_counter_; |
| ++last_accessed_counter_; |
| } |
| |
| private: |
| std::string prefix_; |
| int id_counter_; |
| int64 last_accessed_counter_; |
| }; |
| |
| } // namespace |
| |
| class SearchMetadataTest : public testing::Test { |
| protected: |
| virtual void SetUp() OVERRIDE { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); |
| |
| metadata_storage_.reset(new ResourceMetadataStorage( |
| temp_dir_.path(), base::MessageLoopProxy::current().get())); |
| ASSERT_TRUE(metadata_storage_->Initialize()); |
| |
| cache_.reset(new FileCache(metadata_storage_.get(), |
| temp_dir_.path(), |
| base::MessageLoopProxy::current().get(), |
| fake_free_disk_space_getter_.get())); |
| ASSERT_TRUE(cache_->Initialize()); |
| |
| resource_metadata_.reset( |
| new ResourceMetadata(metadata_storage_.get(), |
| base::MessageLoopProxy::current())); |
| ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize()); |
| |
| AddEntriesToMetadata(); |
| } |
| |
| void AddEntriesToMetadata() { |
| ResourceEntry entry; |
| |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( |
| util::kDriveMyDriveRootDirName, "root", 100, |
| util::kDriveGrandRootSpecialResourceId))); |
| |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( |
| "Directory 1", "dir1", 1, "root"))); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry( |
| "SubDirectory File 1.txt", "file1a", 2, "dir1"))); |
| |
| entry = GetFileEntry( |
| "Shared To The Account Owner.txt", "file1b", 3, "dir1"); |
| entry.set_shared_with_me(true); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry)); |
| |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( |
| "Directory 2 excludeDir-test", "dir2", 4, "root"))); |
| |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry( |
| "Slash \xE2\x88\x95 in directory", "dir3", 5, "root"))); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry( |
| "Slash SubDir File.txt", "file3a", 6, "dir3"))); |
| |
| entry = GetFileEntry( |
| "Document 1 excludeDir-test", "doc1", 7, "root"); |
| entry.mutable_file_specific_info()->set_is_hosted_document(true); |
| entry.mutable_file_specific_info()->set_document_extension(".gdoc"); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry)); |
| } |
| |
| // Adds a directory at |path|. Parent directories are added if needed just |
| // like "mkdir -p" does. |
| std::string AddDirectoryToMetadataWithParents( |
| const base::FilePath& path, |
| MetadataInfoGenerator* generator) { |
| if (path == base::FilePath(base::FilePath::kCurrentDirectory)) |
| return "root"; |
| |
| { |
| ResourceEntry entry; |
| FileError error = resource_metadata_->GetResourceEntryByPath( |
| util::GetDriveMyDriveRootPath().Append(path), &entry); |
| if (error == FILE_ERROR_OK) |
| return entry.resource_id(); |
| } |
| |
| const std::string parent_id = |
| AddDirectoryToMetadataWithParents(path.DirName(), generator); |
| const std::string id = generator->GetId(); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry( |
| GetDirectoryEntry(path.BaseName().AsUTF8Unsafe(), |
| id, |
| generator->GetLastAccessed(), |
| parent_id))); |
| generator->Advance(); |
| return id; |
| } |
| |
| // Adds entries for |cache_resources| to |resource_metadata_|. The parent |
| // directories of |resources| is also added. |
| void AddEntriesToMetadataFromCache( |
| const std::vector<test_util::TestCacheResource>& cache_resources, |
| MetadataInfoGenerator* generator) { |
| for (size_t i = 0; i < cache_resources.size(); ++i) { |
| const test_util::TestCacheResource& resource = cache_resources[i]; |
| const base::FilePath path(resource.source_file); |
| const std::string parent_id = |
| AddDirectoryToMetadataWithParents(path.DirName(), generator); |
| EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry( |
| GetFileEntry(path.BaseName().AsUTF8Unsafe(), |
| resource.resource_id, |
| generator->GetLastAccessed(), |
| parent_id))); |
| generator->Advance(); |
| } |
| } |
| |
| ResourceEntry GetFileEntry(const std::string& name, |
| const std::string& resource_id, |
| int64 last_accessed, |
| const std::string& parent_local_id) { |
| ResourceEntry entry; |
| entry.set_title(name); |
| entry.set_resource_id(resource_id); |
| entry.set_parent_local_id(parent_local_id); |
| entry.mutable_file_info()->set_last_accessed(last_accessed); |
| return entry; |
| } |
| |
| ResourceEntry GetDirectoryEntry(const std::string& name, |
| const std::string& resource_id, |
| int64 last_accessed, |
| const std::string& parent_local_id) { |
| ResourceEntry entry; |
| entry.set_title(name); |
| entry.set_resource_id(resource_id); |
| entry.set_parent_local_id(parent_local_id); |
| entry.mutable_file_info()->set_last_accessed(last_accessed); |
| entry.mutable_file_info()->set_is_directory(true); |
| return entry; |
| } |
| |
| content::TestBrowserThreadBundle thread_bundle_; |
| base::ScopedTempDir temp_dir_; |
| scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; |
| scoped_ptr<ResourceMetadataStorage, |
| test_util::DestroyHelperForTests> metadata_storage_; |
| scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> |
| resource_metadata_; |
| scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; |
| }; |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_ZeroMatches) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "NonExistent", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(0U, result->size()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_RegularFile) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "SubDirectory File 1.txt", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| // This test checks if |FindAndHighlightWrapper| does case-insensitive search. |
| // Tricker test cases for |FindAndHighlightWrapper| can be found below. |
| TEST_F(SearchMetadataTest, SearchMetadata_CaseInsensitiveSearch) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| // The query is all in lower case. |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "subdirectory file 1.txt", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_RegularFiles) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "SubDir", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(2U, result->size()); |
| |
| // The results should be sorted by the last accessed time in descending order. |
| EXPECT_EQ(6, result->at(0).entry.file_info().last_accessed()); |
| EXPECT_EQ(2, result->at(1).entry.file_info().last_accessed()); |
| |
| // All base names should contain "File". |
| EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt", |
| result->at(0).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt", |
| result->at(1).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_AtMostOneFile) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| // There are two files matching "SubDir" but only one file should be |
| // returned. |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "SubDir", |
| SEARCH_METADATA_ALL, |
| 1, // at_most_num_matches |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_Directory) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "Directory 1", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| EXPECT_EQ("drive/root/Directory 1", result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_HostedDocument) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "Document", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| |
| EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_ExcludeHostedDocument) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "Document", |
| SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(0U, result->size()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_SharedWithMe) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "", |
| SEARCH_METADATA_SHARED_WITH_ME, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| EXPECT_EQ("drive/root/Directory 1/Shared To The Account Owner.txt", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_FileAndDirectory) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "excludeDir-test", |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(2U, result->size()); |
| |
| EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", |
| result->at(0).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/Directory 2 excludeDir-test", |
| result->at(1).path.AsUTF8Unsafe()); |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_ExcludeDirectory) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "excludeDir-test", |
| SEARCH_METADATA_EXCLUDE_DIRECTORIES, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_EQ(1U, result->size()); |
| |
| EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", |
| result->at(0).path.AsUTF8Unsafe()); |
| } |
| |
| // "drive", "drive/root", "drive/other" should be excluded. |
| TEST_F(SearchMetadataTest, SearchMetadata_ExcludeSpecialDirectories) { |
| const char* kQueries[] = { "drive", "root", "other" }; |
| for (size_t i = 0; i < arraysize(kQueries); ++i) { |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| const std::string query = kQueries[i]; |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| query, |
| SEARCH_METADATA_ALL, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_TRUE(result); |
| ASSERT_TRUE(result->empty()) << ": " << query << " should not match"; |
| } |
| } |
| |
| TEST_F(SearchMetadataTest, SearchMetadata_Offline) { |
| const std::vector<test_util::TestCacheResource> cache_resources = |
| test_util::GetDefaultTestCacheResources(); |
| ASSERT_TRUE(test_util::PrepareTestCacheResources(cache_.get(), |
| cache_resources)); |
| { |
| MetadataInfoGenerator generator("cache", kCacheEntriesLastAccessedTimeBase); |
| AddEntriesToMetadataFromCache(cache_resources, &generator); |
| } |
| FileError error = FILE_ERROR_FAILED; |
| scoped_ptr<MetadataSearchResultVector> result; |
| |
| SearchMetadata(base::MessageLoopProxy::current(), |
| resource_metadata_.get(), |
| "", |
| SEARCH_METADATA_OFFLINE, |
| kDefaultAtMostNumMatches, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &result)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| ASSERT_EQ(6U, result->size()); |
| |
| // Newer entries are listed earlier. So, the results come in the reverse |
| // order of addition written in test_util::GetDefaultTestCacheResources. |
| EXPECT_EQ("drive/root/pinned/dirty/cache.pdf", |
| result->at(0).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/dirty/cache.avi", |
| result->at(1).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/pinned/cache.mp3", |
| result->at(2).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/cache2.png", |
| result->at(3).path.AsUTF8Unsafe()); |
| EXPECT_EQ("drive/root/cache.txt", |
| result->at(4).path.AsUTF8Unsafe()); |
| // This is not included in the cache but is a hosted document. |
| EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc", |
| result->at(5).path.AsUTF8Unsafe()); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_ZeroMatches) { |
| std::string highlighted_text; |
| EXPECT_FALSE(FindAndHighlightWrapper("text", "query", &highlighted_text)); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_EmptyText) { |
| std::string highlighted_text; |
| EXPECT_FALSE(FindAndHighlightWrapper("", "query", &highlighted_text)); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_FullMatch) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("hello", "hello", &highlighted_text)); |
| EXPECT_EQ("<b>hello</b>", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_StartWith) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "hello", |
| &highlighted_text)); |
| EXPECT_EQ("<b>hello</b>, world", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_EndWith) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "world", |
| &highlighted_text)); |
| EXPECT_EQ("hello, <b>world</b>", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_InTheMiddle) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("yo hello, world", "hello", |
| &highlighted_text)); |
| EXPECT_EQ("yo <b>hello</b>, world", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_MultipeMatches) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("yoyoyoyoy", "yoy", &highlighted_text)); |
| // Only the first match is highlighted. |
| EXPECT_EQ("<b>yoy</b>oyoyoy", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCase) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("HeLLo", "hello", &highlighted_text)); |
| EXPECT_EQ("<b>HeLLo</b>", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCaseNonASCII) { |
| std::string highlighted_text; |
| |
| // Case and accent ignorance in Greek. Find "socra" in "Socra'tes". |
| EXPECT_TRUE(FindAndHighlightWrapper( |
| "\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC\xCF\x84\xCE\xB7\xCF\x82", |
| "\xCF\x83\xCF\x89\xCE\xBA\xCF\x81\xCE\xB1", &highlighted_text)); |
| EXPECT_EQ( |
| "<b>\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC</b>\xCF\x84\xCE\xB7\xCF\x82", |
| highlighted_text); |
| |
| // In Japanese characters. |
| // Find Hiragana "pi" + "(small)ya" in Katakana "hi" + semi-voiced-mark + "ya" |
| EXPECT_TRUE(FindAndHighlightWrapper( |
| "\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83\xE3\x83\xBC", |
| "\xE3\x83\x94\xE3\x83\xA4", |
| &highlighted_text)); |
| EXPECT_EQ( |
| "<b>\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83</b>\xE3\x83\xBC", |
| highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, MultiTextBySingleQuery) { |
| base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query( |
| base::UTF8ToUTF16("hello")); |
| |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlight("hello", &query, &highlighted_text)); |
| EXPECT_EQ("<b>hello</b>", highlighted_text); |
| EXPECT_FALSE(FindAndHighlight("goodbye", &query, &highlighted_text)); |
| EXPECT_TRUE(FindAndHighlight("1hello2", &query, &highlighted_text)); |
| EXPECT_EQ("1<b>hello</b>2", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_MetaChars) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("<hello>", "hello", &highlighted_text)); |
| EXPECT_EQ("<<b>hello</b>>", highlighted_text); |
| } |
| |
| TEST(SearchMetadataSimpleTest, FindAndHighlight_MoreMetaChars) { |
| std::string highlighted_text; |
| EXPECT_TRUE(FindAndHighlightWrapper("a&b&c&d", "b&c", &highlighted_text)); |
| EXPECT_EQ("a&<b>b&c</b>&d", highlighted_text); |
| } |
| |
| } // namespace internal |
| } // namespace drive |