Home | History | Annotate | Download | only in autocomplete
      1 // Copyright (c) 2012 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/autocomplete/contact_provider_chromeos.h"
      6 
      7 #include <cmath>
      8 #include <map>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/string16.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "chrome/browser/autocomplete/autocomplete_input.h"
     18 #include "chrome/browser/autocomplete/autocomplete_match.h"
     19 #include "chrome/browser/autocomplete/autocomplete_provider.h"
     20 #include "chrome/browser/chromeos/contacts/contact.pb.h"
     21 #include "chrome/browser/chromeos/contacts/contact_manager_stub.h"
     22 #include "chrome/browser/chromeos/contacts/contact_test_util.h"
     23 #include "chrome/test/base/testing_browser_process.h"
     24 #include "chrome/test/base/testing_profile.h"
     25 #include "chrome/test/base/testing_profile_manager.h"
     26 #include "content/public/browser/browser_thread.h"
     27 #include "content/public/test/test_browser_thread.h"
     28 #include "testing/gtest/include/gtest/gtest.h"
     29 
     30 using content::BrowserThread;
     31 
     32 namespace {
     33 
     34 // Initializes |contact| with the passed-in data.
     35 void InitContact(const std::string& contact_id,
     36                  const std::string& full_name,
     37                  const std::string& given_name,
     38                  const std::string& family_name,
     39                  contacts::Contact* contact) {
     40   contact->set_contact_id(contact_id);
     41   contact->set_full_name(full_name);
     42   contact->set_given_name(given_name);
     43   contact->set_family_name(family_name);
     44 }
     45 
     46 }  // namespace
     47 
     48 class ContactProviderTest : public testing::Test {
     49  public:
     50   ContactProviderTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
     51   virtual ~ContactProviderTest() {}
     52 
     53  protected:
     54   // testing::Test implementation.
     55   virtual void SetUp() OVERRIDE {
     56     profile_manager_.reset(
     57         new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
     58     ASSERT_TRUE(profile_manager_->SetUp());
     59     profile_ = profile_manager_->CreateTestingProfile("test_profile");
     60     contact_manager_.reset(new contacts::ContactManagerStub(profile_));
     61     contact_provider_ =
     62         new ContactProvider(NULL, profile_, contact_manager_->GetWeakPtr());
     63   }
     64 
     65   // Starts a (synchronous) query for |utf8_text| in |contact_provider_|.
     66   void StartQuery(const std::string& utf8_text) {
     67     contact_provider_->Start(
     68         AutocompleteInput(UTF8ToUTF16(utf8_text),
     69                           string16::npos,
     70                           string16(),
     71                           GURL(),
     72                           AutocompleteInput::INVALID_SPEC,
     73                           false,
     74                           false,
     75                           false,
     76                           AutocompleteInput::ALL_MATCHES),
     77         false);  // minimal_changes
     78   }
     79 
     80   // Returns the contact ID in |match|'s additional info, or an empty string if
     81   // no ID is present.
     82   std::string GetContactIdFromMatch(const AutocompleteMatch& match) {
     83     AutocompleteMatch::AdditionalInfo::const_iterator it =
     84         match.additional_info.find(ContactProvider::kMatchContactIdKey);
     85     return it != match.additional_info.end() ? it->second : std::string();
     86   }
     87 
     88   // Returns pointers to all of the Contact objects referenced in
     89   // |contact_provider_|'s current results.
     90   contacts::ContactPointers GetMatchedContacts() {
     91     contacts::ContactPointers contacts;
     92     const ACMatches& matches = contact_provider_->matches();
     93     for (size_t i = 0; i < matches.size(); ++i) {
     94       const contacts::Contact* contact = contact_manager_->GetContactById(
     95           profile_, GetContactIdFromMatch(matches[i]));
     96       DCHECK(contact) << "Unable to find contact for match " << i;
     97       contacts.push_back(contact);
     98     }
     99     return contacts;
    100   }
    101 
    102   // Returns a semicolon-separated string containing string representations (as
    103   // provided by AutocompleteMatch::ClassificationsToString()) of the
    104   // |contents_class| fields of all current matches.  Results are sorted by
    105   // contact ID.
    106   std::string GetMatchClassifications() {
    107     typedef std::map<std::string, std::string> StringMap;
    108     StringMap contact_id_classifications;
    109     const ACMatches& matches = contact_provider_->matches();
    110     for (size_t i = 0; i < matches.size(); ++i) {
    111       std::string id = GetContactIdFromMatch(matches[i]);
    112       DCHECK(!id.empty()) << "Match " << i << " lacks contact ID";
    113       contact_id_classifications[id] = AutocompleteMatch::
    114           ClassificationsToString(matches[i].contents_class);
    115     }
    116 
    117     std::string result;
    118     for (StringMap::const_iterator it = contact_id_classifications.begin();
    119          it != contact_id_classifications.end(); ++it) {
    120       if (!result.empty())
    121         result += ";";
    122       result += it->second;
    123     }
    124     return result;
    125   }
    126 
    127   base::MessageLoopForUI message_loop_;
    128   content::TestBrowserThread ui_thread_;
    129 
    130   scoped_ptr<TestingProfileManager> profile_manager_;
    131   TestingProfile* profile_;
    132 
    133   scoped_ptr<contacts::ContactManagerStub> contact_manager_;
    134   scoped_refptr<ContactProvider> contact_provider_;
    135 };
    136 
    137 TEST_F(ContactProviderTest, BasicMatching) {
    138   const std::string kContactId1 = "contact_1";
    139   scoped_ptr<contacts::Contact> contact1(new contacts::Contact);
    140   InitContact(kContactId1, "Bob Smith", "Bob", "Smith", contact1.get());
    141 
    142   const std::string kContactId2 = "contact_2";
    143   scoped_ptr<contacts::Contact> contact2(new contacts::Contact);
    144   InitContact(kContactId2, "Dr. Jane Smith", "Jane", "Smith", contact2.get());
    145 
    146   contacts::ContactPointers contacts;
    147   contacts.push_back(contact1.get());
    148   contacts.push_back(contact2.get());
    149   contact_manager_->SetContacts(contacts);
    150   contact_manager_->NotifyObserversAboutUpdatedContacts();
    151 
    152   StartQuery("b");
    153   EXPECT_EQ(
    154       contacts::test::VarContactsToString(1, contact1.get()),
    155       contacts::test::ContactsToString(GetMatchedContacts()));
    156   EXPECT_EQ("0,2,1,0", GetMatchClassifications());
    157 
    158   StartQuery("bob");
    159   EXPECT_EQ(
    160       contacts::test::VarContactsToString(1, contact1.get()),
    161       contacts::test::ContactsToString(GetMatchedContacts()));
    162   EXPECT_EQ("0,2,3,0", GetMatchClassifications());
    163 
    164   StartQuery("bob smith");
    165   EXPECT_EQ(
    166       contacts::test::VarContactsToString(1, contact1.get()),
    167       contacts::test::ContactsToString(GetMatchedContacts()));
    168   EXPECT_EQ("0,2", GetMatchClassifications());
    169 
    170   StartQuery("sm");
    171   EXPECT_EQ(
    172       contacts::test::VarContactsToString(2, contact1.get(), contact2.get()),
    173       contacts::test::ContactsToString(GetMatchedContacts()));
    174   EXPECT_EQ("0,0,4,2,6,0;" "0,0,9,2,11,0", GetMatchClassifications());
    175 
    176   StartQuery("smith");
    177   EXPECT_EQ(
    178       contacts::test::VarContactsToString(2, contact1.get(), contact2.get()),
    179       contacts::test::ContactsToString(GetMatchedContacts()));
    180   EXPECT_EQ("0,0,4,2;" "0,0,9,2", GetMatchClassifications());
    181 
    182   StartQuery("smIth BOb");
    183   EXPECT_EQ(
    184       contacts::test::VarContactsToString(1, contact1.get()),
    185       contacts::test::ContactsToString(GetMatchedContacts()));
    186   EXPECT_EQ("0,2,3,0,4,2", GetMatchClassifications());
    187 
    188   StartQuery("bobo");
    189   EXPECT_EQ("", contacts::test::ContactsToString(GetMatchedContacts()));
    190   EXPECT_EQ("", GetMatchClassifications());
    191 
    192   StartQuery("mith");
    193   EXPECT_EQ("", contacts::test::ContactsToString(GetMatchedContacts()));
    194   EXPECT_EQ("", GetMatchClassifications());
    195 
    196   StartQuery("dr");
    197   EXPECT_EQ(
    198       contacts::test::VarContactsToString(1, contact2.get()),
    199       contacts::test::ContactsToString(GetMatchedContacts()));
    200   EXPECT_EQ("0,2,2,0", GetMatchClassifications());
    201 
    202   StartQuery("dr. j");
    203   EXPECT_EQ(
    204       contacts::test::VarContactsToString(1, contact2.get()),
    205       contacts::test::ContactsToString(GetMatchedContacts()));
    206   EXPECT_EQ("0,2,5,0", GetMatchClassifications());
    207 
    208   StartQuery("jane");
    209   EXPECT_EQ(
    210       contacts::test::VarContactsToString(1, contact2.get()),
    211       contacts::test::ContactsToString(GetMatchedContacts()));
    212   EXPECT_EQ("0,0,4,2,8,0", GetMatchClassifications());
    213 }
    214 
    215 TEST_F(ContactProviderTest, Collation) {
    216   scoped_ptr<contacts::Contact> contact(new contacts::Contact);
    217   InitContact("1", "Bj\xC3\xB6rn Adelsv\xC3\xA4rd",
    218               "Bj\xC3\xB6rn", "Adelsv\xC3\xA4rd",
    219               contact.get());
    220 
    221   contacts::ContactPointers contacts;
    222   contacts.push_back(contact.get());
    223   contact_manager_->SetContacts(contacts);
    224   contact_manager_->NotifyObserversAboutUpdatedContacts();
    225 
    226   StartQuery("bjorn");
    227   EXPECT_EQ(
    228       contacts::test::VarContactsToString(1, contact.get()),
    229       contacts::test::ContactsToString(GetMatchedContacts()));
    230   EXPECT_EQ("0,2,5,0", GetMatchClassifications());
    231 
    232   StartQuery("adelsvard");
    233   EXPECT_EQ(
    234       contacts::test::VarContactsToString(1, contact.get()),
    235       contacts::test::ContactsToString(GetMatchedContacts()));
    236   EXPECT_EQ("0,0,6,2", GetMatchClassifications());
    237 }
    238 
    239 TEST_F(ContactProviderTest, Relevance) {
    240   // Create more contacts than the maximum number of results that an
    241   // AutocompleteProvider should return.  Give them all the same family name and
    242   // ascending affinities from 0.0 to 1.0.
    243   const size_t kNumContacts = AutocompleteProvider::kMaxMatches + 1;
    244   const std::string kFamilyName = "Jones";
    245 
    246   ScopedVector<contacts::Contact> contacts;
    247   contacts::ContactPointers contact_pointers;
    248   for (size_t i = 0; i < kNumContacts; ++i) {
    249     contacts::Contact* contact = new contacts::Contact;
    250     std::string id_string = base::IntToString(static_cast<int>(i));
    251     InitContact(id_string, id_string, kFamilyName,
    252                 id_string + " " + kFamilyName, contact);
    253     contact->set_affinity(static_cast<float>(i) / kNumContacts);
    254     contacts.push_back(contact);
    255     contact_pointers.push_back(contact);
    256   }
    257 
    258   contact_manager_->SetContacts(contact_pointers);
    259   contact_manager_->NotifyObserversAboutUpdatedContacts();
    260 
    261   // Do a search for the family name and check that the total number of results
    262   // is limited as expected and that the results are ordered by descending
    263   // affinity.
    264   StartQuery(kFamilyName);
    265   const ACMatches& matches = contact_provider_->matches();
    266   ASSERT_EQ(AutocompleteProvider::kMaxMatches, matches.size());
    267 
    268   int previous_relevance = 0;
    269   for (size_t i = 0; i < matches.size(); ++i) {
    270     const contacts::Contact& exp_contact =
    271         *(contacts[kNumContacts - 1 - i]);
    272     std::string match_id = GetContactIdFromMatch(matches[i]);
    273     EXPECT_EQ(exp_contact.contact_id(), match_id)
    274         << "Expected contact ID " << exp_contact.contact_id()
    275         << " for match " << i << " but got " << match_id << " instead";
    276     if (i > 0) {
    277       EXPECT_LE(matches[i].relevance, previous_relevance)
    278           << "Match " << i << " has greater relevance than previous match";
    279     }
    280     EXPECT_FALSE(matches[i].allowed_to_be_default_match);
    281     previous_relevance = matches[i].relevance;
    282   }
    283 }
    284