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/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 "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 
     27 // A simple wrapper for testing FindAndHighlightWrapper(). It just converts the
     28 // query text parameter to FixedPatternStringSearchIgnoringCaseAndAccents.
     29 bool FindAndHighlightWrapper(
     30     const std::string& text,
     31     const std::string& query_text,
     32     std::string* highlighted_text) {
     33   base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query(
     34       base::UTF8ToUTF16(query_text));
     35   return FindAndHighlight(text, &query, highlighted_text);
     36 }
     37 
     38 }  // namespace
     39 
     40 class SearchMetadataTest : public testing::Test {
     41  protected:
     42   virtual void SetUp() OVERRIDE {
     43     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     44     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
     45 
     46     metadata_storage_.reset(new ResourceMetadataStorage(
     47         temp_dir_.path(), base::MessageLoopProxy::current().get()));
     48     ASSERT_TRUE(metadata_storage_->Initialize());
     49 
     50     cache_.reset(new FileCache(metadata_storage_.get(),
     51                                temp_dir_.path(),
     52                                base::MessageLoopProxy::current().get(),
     53                                fake_free_disk_space_getter_.get()));
     54     ASSERT_TRUE(cache_->Initialize());
     55 
     56     resource_metadata_.reset(
     57         new ResourceMetadata(metadata_storage_.get(),
     58                              base::MessageLoopProxy::current()));
     59     ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
     60 
     61     AddEntriesToMetadata();
     62   }
     63 
     64   void AddEntriesToMetadata() {
     65     base::FilePath temp_file;
     66     EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &temp_file));
     67     const std::string temp_file_md5 = "md5";
     68 
     69     ResourceEntry entry;
     70     std::string local_id;
     71 
     72     // drive/root
     73     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
     74         util::kDriveMyDriveRootDirName, "root", 100,
     75         util::kDriveGrandRootLocalId), &local_id));
     76     const std::string root_local_id = local_id;
     77 
     78     // drive/root/Directory 1
     79     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
     80         "Directory 1", "dir1", 1, root_local_id), &local_id));
     81     const std::string dir1_local_id = local_id;
     82 
     83     // drive/root/Directory 1/SubDirectory File 1.txt
     84     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
     85         "SubDirectory File 1.txt", "file1a", 2, dir1_local_id), &local_id));
     86     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
     87         local_id, temp_file_md5, temp_file, FileCache::FILE_OPERATION_COPY));
     88 
     89     // drive/root/Directory 1/Shared To The Account Owner.txt
     90     entry = GetFileEntry(
     91         "Shared To The Account Owner.txt", "file1b", 3, dir1_local_id);
     92     entry.set_shared_with_me(true);
     93     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
     94 
     95     // drive/root/Directory 2 excludeDir-test
     96     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
     97         "Directory 2 excludeDir-test", "dir2", 4, root_local_id), &local_id));
     98 
     99     // drive/root/Slash \xE2\x88\x95 in directory
    100     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
    101         GetDirectoryEntry("Slash \xE2\x88\x95 in directory", "dir3", 5,
    102                           root_local_id), &local_id));
    103     const std::string dir3_local_id = local_id;
    104 
    105     // drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt
    106     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
    107         "Slash SubDir File.txt", "file3a", 6, dir3_local_id), &local_id));
    108 
    109     // drive/root/File 2.txt
    110     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
    111         "File 2.txt", "file2", 7, root_local_id), &local_id));
    112     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
    113         local_id, temp_file_md5, temp_file, FileCache::FILE_OPERATION_COPY));
    114 
    115     // drive/root/Document 1 excludeDir-test
    116     entry = GetFileEntry(
    117         "Document 1 excludeDir-test", "doc1", 8, root_local_id);
    118     entry.mutable_file_specific_info()->set_is_hosted_document(true);
    119     entry.mutable_file_specific_info()->set_document_extension(".gdoc");
    120     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
    121 
    122   }
    123 
    124   ResourceEntry GetFileEntry(const std::string& name,
    125                              const std::string& resource_id,
    126                              int64 last_accessed,
    127                              const std::string& parent_local_id) {
    128     ResourceEntry entry;
    129     entry.set_title(name);
    130     entry.set_resource_id(resource_id);
    131     entry.set_parent_local_id(parent_local_id);
    132     entry.mutable_file_info()->set_last_accessed(last_accessed);
    133     return entry;
    134   }
    135 
    136   ResourceEntry GetDirectoryEntry(const std::string& name,
    137                                   const std::string& resource_id,
    138                                   int64 last_accessed,
    139                                   const std::string& parent_local_id) {
    140     ResourceEntry entry;
    141     entry.set_title(name);
    142     entry.set_resource_id(resource_id);
    143     entry.set_parent_local_id(parent_local_id);
    144     entry.mutable_file_info()->set_last_accessed(last_accessed);
    145     entry.mutable_file_info()->set_is_directory(true);
    146     return entry;
    147   }
    148 
    149   content::TestBrowserThreadBundle thread_bundle_;
    150   base::ScopedTempDir temp_dir_;
    151   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
    152   scoped_ptr<ResourceMetadataStorage,
    153              test_util::DestroyHelperForTests> metadata_storage_;
    154   scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests>
    155       resource_metadata_;
    156   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
    157 };
    158 
    159 TEST_F(SearchMetadataTest, SearchMetadata_ZeroMatches) {
    160   FileError error = FILE_ERROR_FAILED;
    161   scoped_ptr<MetadataSearchResultVector> result;
    162 
    163   SearchMetadata(base::MessageLoopProxy::current(),
    164                  resource_metadata_.get(),
    165                  "NonExistent",
    166                  SEARCH_METADATA_ALL,
    167                  kDefaultAtMostNumMatches,
    168                  google_apis::test_util::CreateCopyResultCallback(
    169                      &error, &result));
    170   base::RunLoop().RunUntilIdle();
    171   EXPECT_EQ(FILE_ERROR_OK, error);
    172   ASSERT_TRUE(result);
    173   ASSERT_EQ(0U, result->size());
    174 }
    175 
    176 TEST_F(SearchMetadataTest, SearchMetadata_RegularFile) {
    177   FileError error = FILE_ERROR_FAILED;
    178   scoped_ptr<MetadataSearchResultVector> result;
    179 
    180   SearchMetadata(base::MessageLoopProxy::current(),
    181                  resource_metadata_.get(),
    182                  "SubDirectory File 1.txt",
    183                  SEARCH_METADATA_ALL,
    184                  kDefaultAtMostNumMatches,
    185                  google_apis::test_util::CreateCopyResultCallback(
    186                      &error, &result));
    187   base::RunLoop().RunUntilIdle();
    188   EXPECT_EQ(FILE_ERROR_OK, error);
    189   ASSERT_TRUE(result);
    190   ASSERT_EQ(1U, result->size());
    191   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    192             result->at(0).path.AsUTF8Unsafe());
    193 }
    194 
    195 // This test checks if |FindAndHighlightWrapper| does case-insensitive search.
    196 // Tricker test cases for |FindAndHighlightWrapper| can be found below.
    197 TEST_F(SearchMetadataTest, SearchMetadata_CaseInsensitiveSearch) {
    198   FileError error = FILE_ERROR_FAILED;
    199   scoped_ptr<MetadataSearchResultVector> result;
    200 
    201   // The query is all in lower case.
    202   SearchMetadata(base::MessageLoopProxy::current(),
    203                  resource_metadata_.get(),
    204                  "subdirectory file 1.txt",
    205                  SEARCH_METADATA_ALL,
    206                  kDefaultAtMostNumMatches,
    207                  google_apis::test_util::CreateCopyResultCallback(
    208                      &error, &result));
    209   base::RunLoop().RunUntilIdle();
    210   EXPECT_EQ(FILE_ERROR_OK, error);
    211   ASSERT_TRUE(result);
    212   ASSERT_EQ(1U, result->size());
    213   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    214             result->at(0).path.AsUTF8Unsafe());
    215 }
    216 
    217 TEST_F(SearchMetadataTest, SearchMetadata_RegularFiles) {
    218   FileError error = FILE_ERROR_FAILED;
    219   scoped_ptr<MetadataSearchResultVector> result;
    220 
    221   SearchMetadata(base::MessageLoopProxy::current(),
    222                  resource_metadata_.get(),
    223                  "SubDir",
    224                  SEARCH_METADATA_ALL,
    225                  kDefaultAtMostNumMatches,
    226                  google_apis::test_util::CreateCopyResultCallback(
    227                      &error, &result));
    228   base::RunLoop().RunUntilIdle();
    229   EXPECT_EQ(FILE_ERROR_OK, error);
    230   ASSERT_TRUE(result);
    231   ASSERT_EQ(2U, result->size());
    232 
    233   // The results should be sorted by the last accessed time in descending order.
    234   EXPECT_EQ(6, result->at(0).entry.file_info().last_accessed());
    235   EXPECT_EQ(2, result->at(1).entry.file_info().last_accessed());
    236 
    237   // All base names should contain "File".
    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