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                              cache_.get(),
     59                              base::MessageLoopProxy::current()));
     60     ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
     61 
     62     AddEntriesToMetadata();
     63   }
     64 
     65   void AddEntriesToMetadata() {
     66     base::FilePath temp_file;
     67     EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &temp_file));
     68     const std::string temp_file_md5 = "md5";
     69 
     70     ResourceEntry entry;
     71     std::string local_id;
     72 
     73     // drive/root
     74     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetIdByPath(
     75         util::GetDriveMyDriveRootPath(), &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   // All base names should contain "File". The results should be sorted by the
    234   // last accessed time in descending order.
    235   EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt",
    236             result->at(0).path.AsUTF8Unsafe());
    237   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    238             result->at(1).path.AsUTF8Unsafe());
    239 }
    240 
    241 TEST_F(SearchMetadataTest, SearchMetadata_AtMostOneFile) {
    242   FileError error = FILE_ERROR_FAILED;
    243   scoped_ptr<MetadataSearchResultVector> result;
    244 
    245   // There are two files matching "SubDir" but only one file should be
    246   // returned.
    247   SearchMetadata(base::MessageLoopProxy::current(),
    248                  resource_metadata_.get(),
    249                  "SubDir",
    250                  SEARCH_METADATA_ALL,
    251                  1,  // at_most_num_matches
    252                  google_apis::test_util::CreateCopyResultCallback(
    253                      &error, &result));
    254   base::RunLoop().RunUntilIdle();
    255   EXPECT_EQ(FILE_ERROR_OK, error);
    256   ASSERT_TRUE(result);
    257   ASSERT_EQ(1U, result->size());
    258   EXPECT_EQ("drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt",
    259             result->at(0).path.AsUTF8Unsafe());
    260 }
    261 
    262 TEST_F(SearchMetadataTest, SearchMetadata_Directory) {
    263   FileError error = FILE_ERROR_FAILED;
    264   scoped_ptr<MetadataSearchResultVector> result;
    265 
    266   SearchMetadata(base::MessageLoopProxy::current(),
    267                  resource_metadata_.get(),
    268                  "Directory 1",
    269                  SEARCH_METADATA_ALL,
    270                  kDefaultAtMostNumMatches,
    271                  google_apis::test_util::CreateCopyResultCallback(
    272                      &error, &result));
    273   base::RunLoop().RunUntilIdle();
    274   EXPECT_EQ(FILE_ERROR_OK, error);
    275   ASSERT_TRUE(result);
    276   ASSERT_EQ(1U, result->size());
    277   EXPECT_EQ("drive/root/Directory 1", result->at(0).path.AsUTF8Unsafe());
    278 }
    279 
    280 TEST_F(SearchMetadataTest, SearchMetadata_HostedDocument) {
    281   FileError error = FILE_ERROR_FAILED;
    282   scoped_ptr<MetadataSearchResultVector> result;
    283 
    284   SearchMetadata(base::MessageLoopProxy::current(),
    285                  resource_metadata_.get(),
    286                  "Document",
    287                  SEARCH_METADATA_ALL,
    288                  kDefaultAtMostNumMatches,
    289                  google_apis::test_util::CreateCopyResultCallback(
    290                      &error, &result));
    291   base::RunLoop().RunUntilIdle();
    292   EXPECT_EQ(FILE_ERROR_OK, error);
    293   ASSERT_TRUE(result);
    294   ASSERT_EQ(1U, result->size());
    295 
    296   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    297             result->at(0).path.AsUTF8Unsafe());
    298 }
    299 
    300 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeHostedDocument) {
    301   FileError error = FILE_ERROR_FAILED;
    302   scoped_ptr<MetadataSearchResultVector> result;
    303 
    304   SearchMetadata(base::MessageLoopProxy::current(),
    305                  resource_metadata_.get(),
    306                  "Document",
    307                  SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS,
    308                  kDefaultAtMostNumMatches,
    309                  google_apis::test_util::CreateCopyResultCallback(
    310                      &error, &result));
    311   base::RunLoop().RunUntilIdle();
    312   EXPECT_EQ(FILE_ERROR_OK, error);
    313   ASSERT_TRUE(result);
    314   ASSERT_EQ(0U, result->size());
    315 }
    316 
    317 TEST_F(SearchMetadataTest, SearchMetadata_SharedWithMe) {
    318   FileError error = FILE_ERROR_FAILED;
    319   scoped_ptr<MetadataSearchResultVector> result;
    320 
    321   SearchMetadata(base::MessageLoopProxy::current(),
    322                  resource_metadata_.get(),
    323                  "",
    324                  SEARCH_METADATA_SHARED_WITH_ME,
    325                  kDefaultAtMostNumMatches,
    326                  google_apis::test_util::CreateCopyResultCallback(
    327                      &error, &result));
    328   base::RunLoop().RunUntilIdle();
    329   EXPECT_EQ(FILE_ERROR_OK, error);
    330   ASSERT_TRUE(result);
    331   ASSERT_EQ(1U, result->size());
    332   EXPECT_EQ("drive/root/Directory 1/Shared To The Account Owner.txt",
    333             result->at(0).path.AsUTF8Unsafe());
    334 }
    335 
    336 TEST_F(SearchMetadataTest, SearchMetadata_FileAndDirectory) {
    337   FileError error = FILE_ERROR_FAILED;
    338   scoped_ptr<MetadataSearchResultVector> result;
    339 
    340   SearchMetadata(base::MessageLoopProxy::current(),
    341                  resource_metadata_.get(),
    342                  "excludeDir-test",
    343                  SEARCH_METADATA_ALL,
    344                  kDefaultAtMostNumMatches,
    345                  google_apis::test_util::CreateCopyResultCallback(
    346                      &error, &result));
    347 
    348   base::RunLoop().RunUntilIdle();
    349   EXPECT_EQ(FILE_ERROR_OK, error);
    350   ASSERT_TRUE(result);
    351   ASSERT_EQ(2U, result->size());
    352 
    353   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    354             result->at(0).path.AsUTF8Unsafe());
    355   EXPECT_EQ("drive/root/Directory 2 excludeDir-test",
    356             result->at(1).path.AsUTF8Unsafe());
    357 }
    358 
    359 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeDirectory) {
    360   FileError error = FILE_ERROR_FAILED;
    361   scoped_ptr<MetadataSearchResultVector> result;
    362 
    363   SearchMetadata(base::MessageLoopProxy::current(),
    364                  resource_metadata_.get(),
    365                  "excludeDir-test",
    366                  SEARCH_METADATA_EXCLUDE_DIRECTORIES,
    367                  kDefaultAtMostNumMatches,
    368                  google_apis::test_util::CreateCopyResultCallback(
    369                      &error, &result));
    370 
    371   base::RunLoop().RunUntilIdle();
    372   EXPECT_EQ(FILE_ERROR_OK, error);
    373   ASSERT_TRUE(result);
    374   ASSERT_EQ(1U, result->size());
    375 
    376   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    377             result->at(0).path.AsUTF8Unsafe());
    378 }
    379 
    380 // "drive", "drive/root", "drive/other" should be excluded.
    381 TEST_F(SearchMetadataTest, SearchMetadata_ExcludeSpecialDirectories) {
    382   const char* kQueries[] = { "drive", "root", "other" };
    383   for (size_t i = 0; i < arraysize(kQueries); ++i) {
    384     FileError error = FILE_ERROR_FAILED;
    385     scoped_ptr<MetadataSearchResultVector> result;
    386 
    387     const std::string query = kQueries[i];
    388     SearchMetadata(base::MessageLoopProxy::current(),
    389                    resource_metadata_.get(),
    390                    query,
    391                    SEARCH_METADATA_ALL,
    392                    kDefaultAtMostNumMatches,
    393                    google_apis::test_util::CreateCopyResultCallback(
    394                        &error, &result));
    395 
    396     base::RunLoop().RunUntilIdle();
    397     EXPECT_EQ(FILE_ERROR_OK, error);
    398     ASSERT_TRUE(result);
    399     ASSERT_TRUE(result->empty()) << ": " << query << " should not match";
    400   }
    401 }
    402 
    403 TEST_F(SearchMetadataTest, SearchMetadata_Offline) {
    404   FileError error = FILE_ERROR_FAILED;
    405   scoped_ptr<MetadataSearchResultVector> result;
    406 
    407   SearchMetadata(base::MessageLoopProxy::current(),
    408                  resource_metadata_.get(),
    409                  "",
    410                  SEARCH_METADATA_OFFLINE,
    411                  kDefaultAtMostNumMatches,
    412                  google_apis::test_util::CreateCopyResultCallback(
    413                      &error, &result));
    414   base::RunLoop().RunUntilIdle();
    415   EXPECT_EQ(FILE_ERROR_OK, error);
    416   ASSERT_EQ(3U, result->size());
    417 
    418   // This is not included in the cache but is a hosted document.
    419   EXPECT_EQ("drive/root/Document 1 excludeDir-test.gdoc",
    420             result->at(0).path.AsUTF8Unsafe());
    421 
    422   EXPECT_EQ("drive/root/File 2.txt",
    423             result->at(1).path.AsUTF8Unsafe());
    424   EXPECT_EQ("drive/root/Directory 1/SubDirectory File 1.txt",
    425             result->at(2).path.AsUTF8Unsafe());
    426 }
    427 
    428 TEST(SearchMetadataSimpleTest, FindAndHighlight_ZeroMatches) {
    429   std::string highlighted_text;
    430   EXPECT_FALSE(FindAndHighlightWrapper("text", "query", &highlighted_text));
    431 }
    432 
    433 TEST(SearchMetadataSimpleTest, FindAndHighlight_EmptyText) {
    434   std::string highlighted_text;
    435   EXPECT_FALSE(FindAndHighlightWrapper("", "query", &highlighted_text));
    436 }
    437 
    438 TEST(SearchMetadataSimpleTest, FindAndHighlight_FullMatch) {
    439   std::string highlighted_text;
    440   EXPECT_TRUE(FindAndHighlightWrapper("hello", "hello", &highlighted_text));
    441   EXPECT_EQ("<b>hello</b>", highlighted_text);
    442 }
    443 
    444 TEST(SearchMetadataSimpleTest, FindAndHighlight_StartWith) {
    445   std::string highlighted_text;
    446   EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "hello",
    447                                      &highlighted_text));
    448   EXPECT_EQ("<b>hello</b>, world", highlighted_text);
    449 }
    450 
    451 TEST(SearchMetadataSimpleTest, FindAndHighlight_EndWith) {
    452   std::string highlighted_text;
    453   EXPECT_TRUE(FindAndHighlightWrapper("hello, world", "world",
    454                                      &highlighted_text));
    455   EXPECT_EQ("hello, <b>world</b>", highlighted_text);
    456 }
    457 
    458 TEST(SearchMetadataSimpleTest, FindAndHighlight_InTheMiddle) {
    459   std::string highlighted_text;
    460   EXPECT_TRUE(FindAndHighlightWrapper("yo hello, world", "hello",
    461                                      &highlighted_text));
    462   EXPECT_EQ("yo <b>hello</b>, world", highlighted_text);
    463 }
    464 
    465 TEST(SearchMetadataSimpleTest, FindAndHighlight_MultipeMatches) {
    466   std::string highlighted_text;
    467   EXPECT_TRUE(FindAndHighlightWrapper("yoyoyoyoy", "yoy", &highlighted_text));
    468   // Only the first match is highlighted.
    469   EXPECT_EQ("<b>yoy</b>oyoyoy", highlighted_text);
    470 }
    471 
    472 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCase) {
    473   std::string highlighted_text;
    474   EXPECT_TRUE(FindAndHighlightWrapper("HeLLo", "hello", &highlighted_text));
    475   EXPECT_EQ("<b>HeLLo</b>", highlighted_text);
    476 }
    477 
    478 TEST(SearchMetadataSimpleTest, FindAndHighlight_IgnoreCaseNonASCII) {
    479   std::string highlighted_text;
    480 
    481   // Case and accent ignorance in Greek. Find "socra" in "Socra'tes".
    482   EXPECT_TRUE(FindAndHighlightWrapper(
    483       "\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC\xCF\x84\xCE\xB7\xCF\x82",
    484       "\xCF\x83\xCF\x89\xCE\xBA\xCF\x81\xCE\xB1", &highlighted_text));
    485   EXPECT_EQ(
    486       "<b>\xCE\xA3\xCF\x89\xCE\xBA\xCF\x81\xCE\xAC</b>\xCF\x84\xCE\xB7\xCF\x82",
    487       highlighted_text);
    488 
    489   // In Japanese characters.
    490   // Find Hiragana "pi" + "(small)ya" in Katakana "hi" + semi-voiced-mark + "ya"
    491   EXPECT_TRUE(FindAndHighlightWrapper(
    492       "\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83\xE3\x83\xBC",
    493       "\xE3\x83\x94\xE3\x83\xA4",
    494       &highlighted_text));
    495   EXPECT_EQ(
    496       "<b>\xE3\x81\xB2\xE3\x82\x9A\xE3\x82\x83</b>\xE3\x83\xBC",
    497       highlighted_text);
    498 }
    499 
    500 TEST(SearchMetadataSimpleTest, MultiTextBySingleQuery) {
    501   base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query(
    502       base::UTF8ToUTF16("hello"));
    503 
    504   std::string highlighted_text;
    505   EXPECT_TRUE(FindAndHighlight("hello", &query, &highlighted_text));
    506   EXPECT_EQ("<b>hello</b>", highlighted_text);
    507   EXPECT_FALSE(FindAndHighlight("goodbye", &query, &highlighted_text));
    508   EXPECT_TRUE(FindAndHighlight("1hello2", &query, &highlighted_text));
    509   EXPECT_EQ("1<b>hello</b>2", highlighted_text);
    510 }
    511 
    512 TEST(SearchMetadataSimpleTest, FindAndHighlight_MetaChars) {
    513   std::string highlighted_text;
    514   EXPECT_TRUE(FindAndHighlightWrapper("<hello>", "hello", &highlighted_text));
    515   EXPECT_EQ("&lt;<b>hello</b>&gt;", highlighted_text);
    516 }
    517 
    518 TEST(SearchMetadataSimpleTest, FindAndHighlight_MoreMetaChars) {
    519   std::string highlighted_text;
    520   EXPECT_TRUE(FindAndHighlightWrapper("a&b&c&d", "b&c", &highlighted_text));
    521   EXPECT_EQ("a&amp;<b>b&amp;c</b>&amp;d", highlighted_text);
    522 }
    523 
    524 }  // namespace internal
    525 }  // namespace drive
    526