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