OLD | NEW |
| (Empty) |
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(base::UTF8ToUTF16(utf8_text), | |
69 base::string16::npos, | |
70 base::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 } | |
OLD | NEW |