Home | History | Annotate | Download | only in drive
      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/file_util.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/i18n/string_search.h"
     10 #include "base/message_loop/message_loop_proxy.h"
     11 #include "base/run_loop.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 "chrome/browser/drive/drive_api_util.h"
     18 #include "content/public/test/test_browser_thread_bundle.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace drive {
     22 namespace internal {
     23 
     24 namespace {
     25 
     26 const int kDefaultAtMostNumMatches = 10;
     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 }  // namespace
     40 
     41 class SearchMetadataTest : public testing::Test {
     42  protected:
     43   virtual void SetUp() OVERRIDE {
     44     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     45     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
     46 
     47     metadata_storage_.reset(new ResourceMetadataStorage(
     48         temp_dir_.path(), base::MessageLoopProxy::current().get()));
     49     ASSERT_TRUE(metadata_storage_->Initialize());
     50 
     51     cache_.reset(new FileCache(metadata_storage_.get(),
     52                                temp_dir_.path(),
     53                                base::MessageLoopProxy::current().get(),
     54                                fake_free_disk_space_getter_.get()));
     55     ASSERT_TRUE(cache_->Initialize());
     56 
     57     resource_metadata_.reset(
     58         new ResourceMetadata(metadata_storage_.get(),
     59                              cache_.get(),
     60                              base::MessageLoopProxy::current()));
     61     ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
     62 
     63     AddEntriesToMetadata();
     64   }
     65 
     66   void AddEntriesToMetadata() {
     67     base::FilePath temp_file;
     68     EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &temp_file));
     69     const std::string temp_file_md5 = "md5";
     70 
     71     ResourceEntry entry;
     72     std::string local_id;
     73 
     74     // drive/root
     75     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
     76         util::GetDriveMyDriveRootPath(), &local_id));
     77     const std::string root_local_id = local_id;
     78 
     79     // drive/root/Directory 1
     80     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
     81         "Directory 1", "dir1", 1, root_local_id), &local_id));
     82     const std::string dir1_local_id = local_id;
     83 
     84     // drive/root/Directory 1/SubDirectory File 1.txt
     85     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
     86         "SubDirectory File 1.txt", "file1a", 2, dir1_local_id), &local_id));
     87     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
     88         local_id, temp_file_md5, temp_file, FileCache::FILE_OPERATION_COPY));
     89 
     90     // drive/root/Directory 1/Shared To The Account Owner.txt
     91     entry = GetFileEntry(
     92         "Shared To The Account Owner.txt", "file1b", 3, dir1_local_id);
     93     entry.set_shared_with_me(true);
     94     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
     95 
     96     // drive/root/Directory 2 excludeDir-test
     97     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
     98         "Directory 2 excludeDir-test", "dir2", 4, root_local_id), &local_id));
     99 
    100     // drive/root/Slash \xE2\x88\x95 in directory
    101     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
    102         GetDirectoryEntry("Slash \xE2\x88\x95 in directory", "dir3", 5,
    103                           root_local_id), &local_id));
    104     const std::string dir3_local_id = local_id;
    105 
    106     // drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt
    107     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
    108         "Slash SubDir File.txt", "file3a", 6, dir3_local_id), &local_id));
    109 
    110     // drive/root/File 2.txt
    111     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
    112         "File 2.txt", "file2", 7, root_local_id), &local_id));
    113     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
    114         local_id, temp_file_md5, temp_file, FileCache::FILE_OPERATION_COPY));
    115 
    116     // drive/root/Document 1 excludeDir-test
    117     entry = GetFileEntry(
    118         "Document 1 excludeDir-test", "doc1", 8, root_local_id);
    119     entry.mutable_file_specific_info()->set_is_hosted_document(true);
    120     entry.mutable_file_specific_info()->set_document_extension(".gdoc");
    121     entry.mutable_file_specific_info()->set_content_mime_type(
    122         drive::util::kGoogleDocumentMimeType);
    123     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
    124 
    125   }
    126 
    127   ResourceEntry GetFileEntry(const std::string& name,
    128                              const std::string& resource_id,
    129                              int64 last_accessed,
    130                              const std::string& parent_local_id) {
    131     ResourceEntry entry;
    132     entry.set_title(name);
    133     entry.set_resource_id(resource_id);
    134     entry.set_parent_local_id(parent_local_id);
    135     entry.mutable_file_info()->set_last_accessed(last_accessed);
    136     return entry;
    137   }
    138 
    139   ResourceEntry GetDirectoryEntry(const std::string& name,
    140                                   const std::string& resource_id,
    141                                   int64 last_accessed,
    142                                   const std::string& parent_local_id) {
    143     ResourceEntry entry;
    144     entry.set_title(name);
    145     entry.set_resource_id(resource_id);
    146     entry.set_parent_local_id(parent_local_id);
    147     entry.mutable_file_info()->set_last_accessed(last_accessed);
    148     entry.mutable_file_info()->set_is_directory(true);
    149     return entry;
    150   }
    151 
    152   content::TestBrowserThreadBundle thread_bundle_;
    153   base::ScopedTempDir temp_dir_;
    154   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
    155   scoped_ptr<ResourceMetadataStorage,
    156              test_util::DestroyHelperForTests> metadata_storage_;
    157   scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests>
    158       resource_metadata_;
    159   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
    160 };
    161 
    162 TEST_F(SearchMetadataTest, SearchMetadata_ZeroMatches) {
    163   FileError error = FILE_ERROR_FAILED;
    164   scoped_ptr<MetadataSearchResultVector> result;
    165 
    166   SearchMetadata(base::MessageLoopProxy::current(),
    167                  resource_metadata_.get(),
    168                  "NonExistent",
    169                  SEARCH_METADATA_ALL,
    170                  kDefaultAtMostNumMatches,
    171                  google_apis::test_util::CreateCopyResultCallback(
    172                      &error, &result));
    173   base::RunLoop().RunUntilIdle();
    174   EXPECT_EQ(FILE_ERROR_OK, error);
    175   ASSERT_TRUE(result);
    176   ASSERT_EQ(0U, result->size());
    177 }
    178 
    179 TEST_F(SearchMetadataTest, SearchMetadata_RegularFile) {
    180   FileError error = FILE_ERROR_FAILED;
    181   scoped_ptr<MetadataSearchResultVector> result;
    182 
    183   SearchMetadata(base::MessageLoopProxy::current(),
    184                  resource_metadata_.get(),
    185                  "SubDirectory File 1.txt",
    186                  SEARCH_METADATA_ALL,
    187                  kDefaultAtMostNumMatches,
    188                  google_apis::test_util::CreateCopyResultCallback(
    189                      &error, &result));
    190   base::RunLoop().RunUntilIdle();
    191   EXPECT_EQ(FILE_ERROR_OK, error);
    192   ASSERT_TRUE(result);
    193   ASSERT_EQ(1U, result->size());
    194   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    195             result->at(0).path.AsUTF8Unsafe());
    196 }
    197 
    198 // This test checks if |FindAndHighlightWrapper| does case-insensitive search.
    199 // Tricker test cases for |FindAndHighlightWrapper| can be found below.
    200 TEST_F(SearchMetadataTest, SearchMetadata_CaseInsensitiveSearch) {
    201   FileError error = FILE_ERROR_FAILED;
    202   scoped_ptr<MetadataSearchResultVector> result;
    203 
    204   // The query is all in lower case.
    205   SearchMetadata(base::MessageLoopProxy::current(),
    206                  resource_metadata_.get(),
    207                  "subdirectory file 1.txt",
    208                  SEARCH_METADATA_ALL,
    209                  kDefaultAtMostNumMatches,
    210                  google_apis::test_util::CreateCopyResultCallback(
    211                      &error, &result));
    212   base::RunLoop().RunUntilIdle();
    213   EXPECT_EQ(FILE_ERROR_OK, error);
    214   ASSERT_TRUE(result);
    215   ASSERT_EQ(1U, result->size());
    216   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    217             result->at(0).path.AsUTF8Unsafe());
    218 }
    219 
    220 TEST_F(SearchMetadataTest, SearchMetadata_RegularFiles) {
    221   FileError error = FILE_ERROR_FAILED;
    222   scoped_ptr<MetadataSearchResultVector> result;
    223 
    224   SearchMetadata(base::MessageLoopProxy::current(),
    225                  resource_metadata_.get(),
    226                  "SubDir",
    227                  SEARCH_METADATA_ALL,
    228                  kDefaultAtMostNumMatches,
    229                  google_apis::test_util::CreateCopyResultCallback(
    230                      &error, &result));
    231   base::RunLoop().RunUntilIdle();
    232   EXPECT_EQ(FILE_ERROR_OK, error);
    233   ASSERT_TRUE(result);
    234   ASSERT_EQ(2U, result->size());
    235 
    236   // All base names should contain "File". The results should be sorted by the
    237   // last accessed time in descending order.
    238   EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt",
    239             result->at(0).path.AsUTF8Unsafe());
    240   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    241             result->at(1).path.AsUTF8Unsafe());
    242 }
    243 
    244 TEST_F(SearchMetadataTest, SearchMetadata_AtMostOneFile) {
    245   FileError error = FILE_ERROR_FAILED;
    246   scoped_ptr<MetadataSearchResultVector> result;
    247 
    248   // There are two files matching "SubDir" but only one file should be
    249   // returned.
    250   SearchMetadata(base::MessageLoopProxy::current(),
    251                  resource_metadata_.get(),
    252                  "SubDir",
    253                  SEARCH_METADATA_ALL,
    254                  1,  // at_most_num_matches
    255                  google_apis::test_util::CreateCopyResultCallback(
    256                      &error, &result));
    257   base::RunLoop().RunUntilIdle();
    258   EXPECT_EQ(FILE_ERROR_OK, error);
    259   ASSERT_TRUE(result);
    260   ASSERT_EQ(1U, result->size());
    261   EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt",
    262             result->at(0).path.AsUTF8Unsafe());
    263 }
    264 
    265 TEST_F(SearchMetadataTest, SearchMetadata_Directory) {
    266   FileError error = FILE_ERROR_FAILED;
    267   scoped_ptr<MetadataSearchResultVector> result;
    268 
    269   SearchMetadata(base::MessageLoopProxy::current(),
    270                  resource_metadata_.get(),
    271                  "Directory 1",
    272                  SEARCH_METADATA_ALL,
    273                  kDefaultAtMostNumMatches,
    274                  google_apis::test_util::CreateCopyResultCallback(
    275                      &error, &result));
    276   base::RunLoop().RunUntilIdle();
    277   EXPECT_EQ(FILE_ERROR_OK, error);
    278   ASSERT_TRUE(result);
    279   ASSERT_EQ(1U, result->size());
    280   EXPECT_EQ("drive/root/Directory 1", result->at(0).path.AsUTF8Unsafe());
    281 }
    282 
    283 TEST_F(SearchMetadataTest, SearchMetadata_HostedDocument) {
    284   FileError error = FILE_ERROR_FAILED;
    285   scoped_ptr<MetadataSearchResultVector> result;
    286 
    287   SearchMetadata(base::MessageLoopProxy::current(),
    288                  resource_metadata_.get(),
    289                  "Document",
    290                  SEARCH_METADATA_ALL,
    291                  kDefaultAtMostNumMatches,
    292                  google_apis::test_util::CreateCopyResultCallback(
    293                      &error, &result));
    294   base::RunLoop().RunUntilIdle();
    295   EXPECT_EQ(FILE_ERROR_OK, error);
    296   ASSERT_TRUE(result);
    297   ASSERT_EQ(1U, result->size());
    298 
    299   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    300             result->at(0).path.AsUTF8Unsafe());
    301 }
    302 
    303 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeHostedDocument) {
    304   FileError error = FILE_ERROR_FAILED;
    305   scoped_ptr<MetadataSearchResultVector> result;
    306 
    307   SearchMetadata(base::MessageLoopProxy::current(),
    308                  resource_metadata_.get(),
    309                  "Document",
    310                  SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS,
    311                  kDefaultAtMostNumMatches,
    312                  google_apis::test_util::CreateCopyResultCallback(
    313                      &error, &result));
    314   base::RunLoop().RunUntilIdle();
    315   EXPECT_EQ(FILE_ERROR_OK, error);
    316   ASSERT_TRUE(result);
    317   ASSERT_EQ(0U, result->size());
    318 }
    319 
    320 TEST_F(SearchMetadataTest, SearchMetadata_SharedWithMe) {
    321   FileError error = FILE_ERROR_FAILED;
    322   scoped_ptr<MetadataSearchResultVector> result;
    323 
    324   SearchMetadata(base::MessageLoopProxy::current(),
    325                  resource_metadata_.get(),
    326                  "",
    327                  SEARCH_METADATA_SHARED_WITH_ME,
    328                  kDefaultAtMostNumMatches,
    329                  google_apis::test_util::CreateCopyResultCallback(
    330                      &error, &result));
    331   base::RunLoop().RunUntilIdle();
    332   EXPECT_EQ(FILE_ERROR_OK, error);
    333   ASSERT_TRUE(result);
    334   ASSERT_EQ(1U, result->size());
    335   EXPECT_EQ("drive/root/Directory 1/Shared To The Account Owner.txt",
    336             result->at(0).path.AsUTF8Unsafe());
    337 }
    338 
    339 TEST_F(SearchMetadataTest, SearchMetadata_FileAndDirectory) {
    340   FileError error = FILE_ERROR_FAILED;
    341   scoped_ptr<MetadataSearchResultVector> result;
    342 
    343   SearchMetadata(base::MessageLoopProxy::current(),
    344                  resource_metadata_.get(),
    345                  "excludeDir-test",
    346                  SEARCH_METADATA_ALL,
    347                  kDefaultAtMostNumMatches,
    348                  google_apis::test_util::CreateCopyResultCallback(
    349                      &error, &result));
    350 
    351   base::RunLoop().RunUntilIdle();
    352   EXPECT_EQ(FILE_ERROR_OK, error);
    353   ASSERT_TRUE(result);
    354   ASSERT_EQ(2U, result->size());
    355 
    356   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    357             result->at(0).path.AsUTF8Unsafe());
    358   EXPECT_EQ("drive/root/Directory 2 excludeDir-test",
    359             result->at(1).path.AsUTF8Unsafe());
    360 }
    361 
    362 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeDirectory) {
    363   FileError error = FILE_ERROR_FAILED;
    364   scoped_ptr<MetadataSearchResultVector> result;
    365 
    366   SearchMetadata(base::MessageLoopProxy::current(),
    367                  resource_metadata_.get(),
    368                  "excludeDir-test",
    369                  SEARCH_METADATA_EXCLUDE_DIRECTORIES,
    370                  kDefaultAtMostNumMatches,
    371                  google_apis::test_util::CreateCopyResultCallback(
    372                      &error, &result));
    373 
    374   base::RunLoop().RunUntilIdle();
    375   EXPECT_EQ(FILE_ERROR_OK, error);
    376   ASSERT_TRUE(result);
    377   ASSERT_EQ(1U, result->size());
    378 
    379   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    380             result->at(0).path.AsUTF8Unsafe());
    381 }
    382 
    383 // "drive", "drive/root", "drive/other" should be excluded.
    384 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeSpecialDirectories) {
    385   const char* kQueries[] = { "drive", "root", "other" };
    386   for (size_t i = 0; i < arraysize(kQueries); ++i) {
    387     FileError error = FILE_ERROR_FAILED;
    388     scoped_ptr<MetadataSearchResultVector> result;
    389 
    390     const std::string query = kQueries[i];
    391     SearchMetadata(base::MessageLoopProxy::current(),
    392                    resource_metadata_.get(),
    393                    query,
    394                    SEARCH_METADATA_ALL,
    395                    kDefaultAtMostNumMatches,
    396                    google_apis::test_util::CreateCopyResultCallback(
    397                        &error, &result));
    398 
    399     base::RunLoop().RunUntilIdle();
    400     EXPECT_EQ(FILE_ERROR_OK, error);
    401     ASSERT_TRUE(result);
    402     ASSERT_TRUE(result->empty()) << ": " << query << " should not match";
    403   }
    404 }
    405 
    406 TEST_F(SearchMetadataTest, SearchMetadata_Offline) {
    407   FileError error = FILE_ERROR_FAILED;
    408   scoped_ptr<MetadataSearchResultVector> result;
    409 
    410   SearchMetadata(base::MessageLoopProxy::current(),
    411                  resource_metadata_.get(),
    412                  "",
    413                  SEARCH_METADATA_OFFLINE,
    414                  kDefaultAtMostNumMatches,
    415                  google_apis::test_util::CreateCopyResultCallback(
    416                      &error, &result));
    417   base::RunLoop().RunUntilIdle();
    418   EXPECT_EQ(FILE_ERROR_OK, error);
    419   ASSERT_EQ(3U, result->size());
    420 
    421   // This is not included in the cache but is a hosted document.
    422   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    423             result->at(0).path.AsUTF8Unsafe());
    424 
    425   EXPECT_EQ("drive/root/File 2.txt",
    426             result->at(1).path.AsUTF8Unsafe());
    427   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    428             result->at(2).path.AsUTF8Unsafe());
    429 }
    430 
    431 TEST(SearchMetadataSimpleTest, FindAndHighlight_ZeroMatches) {
    432   std::string highlighted_text;
    433   EXPECT_FALSE(FindAndHighlightWrapper("text", "query", &highlighted_text));
    434 }
    435 
    436 TEST(SearchMetadataSimpleTest, FindAndHighlight_EmptyText) {
    437   std::string highlighted_text;
    438   EXPECT_FALSE(FindAndHighlightWrapper("", "query", &highlighted_text));
    439 }
    440 
    441 TEST(SearchMetadataSimpleTest, FindAndHighlight_FullMatch) {
    442   std::string highlighted_text;
    443   EXPECT_TRUE(FindAndHighlightWrapper("hello", "hello", &highlighted_text));
    444   EXPECT_EQ("<b>hello</b>", highlighted_text);
    445 }
    446 
    447 TEST(SearchMetadataSimpleTest, FindAndHighlight_StartWith) {
    448   std::string highlighted_text;
    449   EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "hello",
    450                                      &highlighted_text));
    451   EXPECT_EQ("<b>hello</b>, world", highlighted_text);
    452 }
    453 
    454 TEST(SearchMetadataSimpleTest, FindAndHighlight_EndWith) {
    455   std::string highlighted_text;
    456   EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "world",
    457                                      &highlighted_text));
    458   EXPECT_EQ("hello, <b>world</b>", highlighted_text);
    459 }
    460 
    461 TEST(SearchMetadataSimpleTest, FindAndHighlight_InTheMiddle) {
    462   std::string highlighted_text;
    463   EXPECT_TRUE(FindAndHighlightWrapper("yo hello, world", "hello",
    464                                      &highlighted_text));
    465   EXPECT_EQ("yo <b>hello</b>, world", highlighted_text);
    466 }
    467 
    468 TEST(SearchMetadataSimpleTest, FindAndHighlight_MultipeMatches) {
    469   std::string highlighted_text;
    470   EXPECT_TRUE(FindAndHighlightWrapper("yoyoyoyoy", "yoy", &highlighted_text));
    471   // Only the first match is highlighted.
    472   EXPECT_EQ("<b>yoy</b>oyoyoy", highlighted_text);
    473 }
    474 
    475 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCase) {
    476   std::string highlighted_text;
    477   EXPECT_TRUE(FindAndHighlightWrapper("HeLLo", "hello", &highlighted_text));
    478   EXPECT_EQ("<b>HeLLo</b>", highlighted_text);
    479 }
    480 
    481 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCaseNonASCII) {
    482   std::string highlighted_text;
    483 
    484   // Case and accent ignorance in Greek. Find "socra" in "Socra'tes".
    485   EXPECT_TRUE(FindAndHighlightWrapper(
    486       "\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC\xCF\x84\xCE\xB7\xCF\x82",
    487       "\xCF\x83\xCF\x89\xCE\xBA\xCF\x81\xCE\xB1", &highlighted_text));
    488   EXPECT_EQ(
    489       "<b>\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC</b>\xCF\x84\xCE\xB7\xCF\x82",
    490       highlighted_text);
    491 
    492   // In Japanese characters.
    493   // Find Hiragana "pi" + "(small)ya" in Katakana "hi" + semi-voiced-mark + "ya"
    494   EXPECT_TRUE(FindAndHighlightWrapper(
    495       "\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83\xE3\x83\xBC",
    496       "\xE3\x83\x94\xE3\x83\xA4",
    497       &highlighted_text));
    498   EXPECT_EQ(
    499       "<b>\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83</b>\xE3\x83\xBC",
    500       highlighted_text);
    501 }
    502 
    503 TEST(SearchMetadataSimpleTest, MultiTextBySingleQuery) {
    504   base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query(
    505       base::UTF8ToUTF16("hello"));
    506 
    507   std::string highlighted_text;
    508   EXPECT_TRUE(FindAndHighlight("hello", &query, &highlighted_text));
    509   EXPECT_EQ("<b>hello</b>", highlighted_text);
    510   EXPECT_FALSE(FindAndHighlight("goodbye", &query, &highlighted_text));
    511   EXPECT_TRUE(FindAndHighlight("1hello2", &query, &highlighted_text));
    512   EXPECT_EQ("1<b>hello</b>2", highlighted_text);
    513 }
    514 
    515 TEST(SearchMetadataSimpleTest, FindAndHighlight_MetaChars) {
    516   std::string highlighted_text;
    517   EXPECT_TRUE(FindAndHighlightWrapper("<hello>", "hello", &highlighted_text));
    518   EXPECT_EQ("&lt;<b>hello</b>&gt;", highlighted_text);
    519 }
    520 
    521 TEST(SearchMetadataSimpleTest, FindAndHighlight_MoreMetaChars) {
    522   std::string highlighted_text;
    523   EXPECT_TRUE(FindAndHighlightWrapper("a&b&c&d", "b&c", &highlighted_text));
    524   EXPECT_EQ("a&amp;<b>b&amp;c</b>&amp;d", highlighted_text);
    525 }
    526 
    527 }  // namespace internal
    528 }  // namespace drive
    529