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.
5 #include "chrome/browser/autocomplete/shortcuts_provider.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/browser/autocomplete/autocomplete_input.h"
21 #include "chrome/browser/autocomplete/autocomplete_match.h"
22 #include "chrome/browser/autocomplete/autocomplete_provider.h"
23 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
24 #include "chrome/browser/autocomplete/autocomplete_result.h"
25 #include "chrome/browser/chrome_notification_types.h"
26 #include "chrome/browser/history/history_service.h"
27 #include "chrome/browser/history/in_memory_url_index.h"
28 #include "chrome/browser/history/shortcuts_backend.h"
29 #include "chrome/browser/history/shortcuts_backend_factory.h"
30 #include "chrome/browser/history/url_database.h"
31 #include "chrome/common/extensions/extension.h"
32 #include "chrome/common/extensions/extension_builder.h"
33 #include "chrome/common/extensions/value_builder.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/test/base/testing_profile.h"
36 #include "content/public/browser/notification_service.h"
37 #include "content/public/test/test_browser_thread.h"
38 #include "testing/gtest/include/gtest/gtest.h"
41 // TestShortcutInfo -----------------------------------------------------------
45 struct TestShortcutInfo {
48 std::string fill_into_edit;
49 std::string destination_url;
51 std::string contents_class;
52 std::string description;
53 std::string description_class;
54 content::PageTransition transition;
55 AutocompleteMatch::Type type;
59 } shortcut_test_db[] = {
60 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E0", "goog", "www.google.com",
61 "http://www.google.com/", "Google", "0,1,4,0", "Google", "0,3,4,1",
62 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_URL, "", 1,
64 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E1", "slash", "slashdot.org",
65 "http://slashdot.org/", "slashdot.org", "0,3,5,1",
66 "Slashdot - News for nerds, stuff that matters", "0,2,5,0",
67 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_URL, "", 0,
69 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E2", "news", "slashdot.org",
70 "http://slashdot.org/", "slashdot.org", "0,1",
71 "Slashdot - News for nerds, stuff that matters", "0,0,11,2,15,0",
72 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_TITLE, "", 0,
74 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E3", "news", "sports.yahoo.com",
75 "http://sports.yahoo.com/", "sports.yahoo.com", "0,1",
76 "Yahoo! Sports - Sports News, Scores, Rumors, Fantasy Games, and more",
77 "0,0,23,2,27,0", content::PAGE_TRANSITION_TYPED,
78 AutocompleteMatchType::HISTORY_TITLE, "", 2, 5 },
79 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E4", "news weather",
80 "www.cnn.com/index.html", "http://www.cnn.com/index.html",
81 "www.cnn.com/index.html", "0,1",
82 "CNN.com - Breaking News, U.S., World, Weather, Entertainment & Video",
83 "0,0,19,2,23,0,38,2,45,0", content::PAGE_TRANSITION_TYPED,
84 AutocompleteMatchType::HISTORY_TITLE, "", 1, 10 },
85 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E5", "nhl scores", "sports.yahoo.com",
86 "http://sports.yahoo.com/", "sports.yahoo.com", "0,1",
87 "Yahoo! Sports - Sports News, Scores, Rumors, Fantasy Games, and more",
88 "0,0,29,2,35,0", content::PAGE_TRANSITION_TYPED,
89 AutocompleteMatchType::HISTORY_BODY, "", 1, 10 },
90 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E6", "nhl scores",
91 "www.nhl.com/scores/index.html", "http://www.nhl.com/scores/index.html",
92 "www.nhl.com/scores/index.html", "0,1,4,3,7,1",
93 "January 13, 2010 - NHL.com - Scores", "0,0,19,2,22,0,29,2,35,0",
94 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_URL, "", 5,
96 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E7", "just", "www.testsite.com/a.html",
97 "http://www.testsite.com/a.html", "www.testsite.com/a.html", "0,1",
98 "Test - site - just a test", "0,0,14,2,18,0",
99 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_TITLE, "", 5,
101 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E8", "just", "www.testsite.com/b.html",
102 "http://www.testsite.com/b.html", "www.testsite.com/b.html", "0,1",
103 "Test - site - just a test", "0,0,14,2,18,0",
104 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_TITLE, "", 5,
106 { "BD85DBA2-8C29-49F9-84AE-48E1E90880E9", "just", "www.testsite.com/c.html",
107 "http://www.testsite.com/c.html", "www.testsite.com/c.html", "0,1",
108 "Test - site - just a test", "0,0,14,2,18,0",
109 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_TITLE, "", 8,
111 { "BD85DBA2-8C29-49F9-84AE-48E1E90880EA", "just a", "www.testsite.com/d.html",
112 "http://www.testsite.com/d.html", "www.testsite.com/d.html", "0,1",
113 "Test - site - just a test", "0,0,14,2,18,0",
114 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_TITLE, "",
116 { "BD85DBA2-8C29-49F9-84AE-48E1E90880EB", "just a t",
117 "www.testsite.com/e.html", "http://www.testsite.com/e.html",
118 "www.testsite.com/e.html", "0,1", "Test - site - just a test",
119 "0,0,14,2,18,0", content::PAGE_TRANSITION_TYPED,
120 AutocompleteMatchType::HISTORY_TITLE, "", 12, 1 },
121 { "BD85DBA2-8C29-49F9-84AE-48E1E90880EC", "just a te",
122 "www.testsite.com/f.html", "http://www.testsite.com/f.html",
123 "www.testsite.com/f.html", "0,1", "Test - site - just a test",
124 "0,0,14,2,18,0", content::PAGE_TRANSITION_TYPED,
125 AutocompleteMatchType::HISTORY_TITLE, "", 12, 1 },
126 { "BD85DBA2-8C29-49F9-84AE-48E1E90880ED", "ago", "www.daysagotest.com/a.html",
127 "http://www.daysagotest.com/a.html", "www.daysagotest.com/a.html",
128 "0,1,8,3,11,1", "Test - site", "0,0", content::PAGE_TRANSITION_TYPED,
129 AutocompleteMatchType::HISTORY_URL, "", 1, 1 },
130 { "BD85DBA2-8C29-49F9-84AE-48E1E90880EE", "ago", "www.daysagotest.com/b.html",
131 "http://www.daysagotest.com/b.html", "www.daysagotest.com/b.html",
132 "0,1,8,3,11,1", "Test - site", "0,0", content::PAGE_TRANSITION_TYPED,
133 AutocompleteMatchType::HISTORY_URL, "", 2, 1 },
134 { "BD85DBA2-8C29-49F9-84AE-48E1E90880EF", "ago", "www.daysagotest.com/c.html",
135 "http://www.daysagotest.com/c.html", "www.daysagotest.com/c.html",
136 "0,1,8,3,11,1", "Test - site", "0,0", content::PAGE_TRANSITION_TYPED,
137 AutocompleteMatchType::HISTORY_URL, "", 3, 1 },
138 { "BD85DBA2-8C29-49F9-84AE-48E1E90880F0", "ago", "www.daysagotest.com/d.html",
139 "http://www.daysagotest.com/d.html", "www.daysagotest.com/d.html",
140 "0,1,8,3,11,1", "Test - site", "0,0", content::PAGE_TRANSITION_TYPED,
141 AutocompleteMatchType::HISTORY_URL, "", 4, 1 },
142 { "BD85DBA2-8C29-49F9-84AE-48E1E90880F1", "echo echo", "echo echo",
143 "chrome-extension://cedabbhfglmiikkmdgcpjdkocfcmbkee/?q=echo",
144 "Run Echo command: echo", "0,0", "Echo", "0,4",
145 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::EXTENSION_APP,
152 // ClassifyTest ---------------------------------------------------------------
154 // Helper class to make running tests of ClassifyAllMatchesInString() more
158 ClassifyTest(const string16& text, ACMatchClassifications matches);
161 ACMatchClassifications RunTest(const string16& find_text);
164 const string16 text_;
165 const ACMatchClassifications matches_;
168 ClassifyTest::ClassifyTest(const string16& text, ACMatchClassifications matches)
173 ClassifyTest::~ClassifyTest() {
176 ACMatchClassifications ClassifyTest::RunTest(const string16& find_text) {
177 return ShortcutsProvider::ClassifyAllMatchesInString(find_text,
178 ShortcutsProvider::CreateWordMapForString(find_text), text_, matches_);
184 // ShortcutsProviderTest ------------------------------------------------------
186 class ShortcutsProviderTest : public testing::Test,
187 public AutocompleteProviderListener {
189 ShortcutsProviderTest();
191 // AutocompleteProviderListener:
192 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
195 class SetShouldContain
196 : public std::unary_function<const std::string&, std::set<std::string> > {
198 explicit SetShouldContain(const ACMatches& matched_urls);
200 void operator()(const std::string& expected);
201 std::set<std::string> Leftovers() const { return matches_; }
204 std::set<std::string> matches_;
207 typedef std::vector<std::string> URLs;
209 virtual void SetUp();
210 virtual void TearDown();
212 // Fills test data into the provider.
213 void FillData(TestShortcutInfo* db, size_t db_size);
215 // Runs an autocomplete query on |text| and checks to see that the returned
216 // results' destination URLs match those provided. |expected_urls| does not
217 // need to be in sorted order.
218 void RunTest(const string16 text,
219 const URLs& expected_urls,
220 std::string expected_top_result);
222 // Passthrough to the private function in provider_.
223 int CalculateScore(const std::string& terms,
224 const ShortcutsBackend::Shortcut& shortcut,
227 base::MessageLoopForUI message_loop_;
228 content::TestBrowserThread ui_thread_;
229 content::TestBrowserThread file_thread_;
231 TestingProfile profile_;
233 ACMatches ac_matches_; // The resulting matches after running RunTest.
235 scoped_refptr<ShortcutsBackend> backend_;
236 scoped_refptr<ShortcutsProvider> provider_;
239 ShortcutsProviderTest::ShortcutsProviderTest()
240 : ui_thread_(content::BrowserThread::UI, &message_loop_),
241 file_thread_(content::BrowserThread::FILE, &message_loop_) {
244 void ShortcutsProviderTest::OnProviderUpdate(bool updated_matches) {}
246 void ShortcutsProviderTest::SetUp() {
247 ShortcutsBackendFactory::GetInstance()->SetTestingFactoryAndUse(
248 &profile_, &ShortcutsBackendFactory::BuildProfileNoDatabaseForTesting);
249 backend_ = ShortcutsBackendFactory::GetForProfile(&profile_);
250 ASSERT_TRUE(backend_.get());
251 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
252 provider_ = new ShortcutsProvider(this, &profile_);
253 FillData(shortcut_test_db, arraysize(shortcut_test_db));
256 void ShortcutsProviderTest::TearDown() {
257 // Run all pending tasks or else some threads hold on to the message loop
258 // and prevent it from being deleted.
259 message_loop_.RunUntilIdle();
263 void ShortcutsProviderTest::FillData(TestShortcutInfo* db, size_t db_size) {
264 DCHECK(provider_.get());
265 size_t expected_size = backend_->shortcuts_map().size() + db_size;
266 for (size_t i = 0; i < db_size; ++i) {
267 const TestShortcutInfo& cur = db[i];
268 ShortcutsBackend::Shortcut shortcut(
269 cur.guid, ASCIIToUTF16(cur.text),
270 ShortcutsBackend::Shortcut::MatchCore(
271 ASCIIToUTF16(cur.fill_into_edit), GURL(cur.destination_url),
272 ASCIIToUTF16(cur.contents),
273 AutocompleteMatch::ClassificationsFromString(cur.contents_class),
274 ASCIIToUTF16(cur.description),
275 AutocompleteMatch::ClassificationsFromString(cur.description_class),
276 cur.transition, cur.type, ASCIIToUTF16(cur.keyword)),
277 base::Time::Now() - base::TimeDelta::FromDays(cur.days_from_now),
279 backend_->AddShortcut(shortcut);
281 EXPECT_EQ(expected_size, backend_->shortcuts_map().size());
284 ShortcutsProviderTest::SetShouldContain::SetShouldContain(
285 const ACMatches& matched_urls) {
286 for (ACMatches::const_iterator iter = matched_urls.begin();
287 iter != matched_urls.end(); ++iter)
288 matches_.insert(iter->destination_url.spec());
291 void ShortcutsProviderTest::SetShouldContain::operator()(
292 const std::string& expected) {
293 EXPECT_EQ(1U, matches_.erase(expected));
296 void ShortcutsProviderTest::RunTest(const string16 text,
297 const URLs& expected_urls,
298 std::string expected_top_result) {
299 base::MessageLoop::current()->RunUntilIdle();
300 AutocompleteInput input(text, string16::npos, string16(), GURL(),
301 AutocompleteInput::INVALID_SPEC, false, false, true,
302 AutocompleteInput::ALL_MATCHES);
303 provider_->Start(input, false);
304 EXPECT_TRUE(provider_->done());
306 ac_matches_ = provider_->matches();
308 // We should have gotten back at most AutocompleteProvider::kMaxMatches.
309 EXPECT_LE(ac_matches_.size(), AutocompleteProvider::kMaxMatches);
311 // If the number of expected and actual matches aren't equal then we need
312 // test no further, but let's do anyway so that we know which URLs failed.
313 EXPECT_EQ(expected_urls.size(), ac_matches_.size());
315 // Verify that all expected URLs were found and that all found URLs
317 std::set<std::string> Leftovers =
318 for_each(expected_urls.begin(), expected_urls.end(),
319 SetShouldContain(ac_matches_)).Leftovers();
320 EXPECT_EQ(0U, Leftovers.size());
322 // See if we got the expected top scorer.
323 if (!ac_matches_.empty()) {
324 std::partial_sort(ac_matches_.begin(), ac_matches_.begin() + 1,
325 ac_matches_.end(), AutocompleteMatch::MoreRelevant);
326 EXPECT_EQ(expected_top_result, ac_matches_[0].destination_url.spec());
329 // No shortcuts matches are allowed to be inlined no matter how highly
331 for (ACMatches::const_iterator it = ac_matches_.begin();
332 it != ac_matches_.end(); ++it)
333 EXPECT_FALSE(it->allowed_to_be_default_match);
336 int ShortcutsProviderTest::CalculateScore(
337 const std::string& terms,
338 const ShortcutsBackend::Shortcut& shortcut,
340 return provider_->CalculateScore(ASCIIToUTF16(terms), shortcut,
345 // Actual tests ---------------------------------------------------------------
347 TEST_F(ShortcutsProviderTest, SimpleSingleMatch) {
348 string16 text(ASCIIToUTF16("go"));
349 std::string expected_url("http://www.google.com/");
351 expected_urls.push_back(expected_url);
352 RunTest(text, expected_urls, expected_url);
355 TEST_F(ShortcutsProviderTest, MultiMatch) {
356 string16 text(ASCIIToUTF16("NEWS"));
358 // Scores high because of completion length.
359 expected_urls.push_back("http://slashdot.org/");
360 // Scores high because of visit count.
361 expected_urls.push_back("http://sports.yahoo.com/");
362 // Scores high because of visit count but less match span,
363 // which is more important.
364 expected_urls.push_back("http://www.cnn.com/index.html");
365 RunTest(text, expected_urls, "http://slashdot.org/");
368 TEST_F(ShortcutsProviderTest, TypedCountMatches) {
369 string16 text(ASCIIToUTF16("just"));
371 expected_urls.push_back("http://www.testsite.com/b.html");
372 expected_urls.push_back("http://www.testsite.com/a.html");
373 expected_urls.push_back("http://www.testsite.com/c.html");
374 RunTest(text, expected_urls, "http://www.testsite.com/b.html");
377 TEST_F(ShortcutsProviderTest, FragmentLengthMatches) {
378 string16 text(ASCIIToUTF16("just a"));
380 expected_urls.push_back("http://www.testsite.com/d.html");
381 expected_urls.push_back("http://www.testsite.com/e.html");
382 expected_urls.push_back("http://www.testsite.com/f.html");
383 RunTest(text, expected_urls, "http://www.testsite.com/d.html");
386 TEST_F(ShortcutsProviderTest, DaysAgoMatches) {
387 string16 text(ASCIIToUTF16("ago"));
389 expected_urls.push_back("http://www.daysagotest.com/a.html");
390 expected_urls.push_back("http://www.daysagotest.com/b.html");
391 expected_urls.push_back("http://www.daysagotest.com/c.html");
392 RunTest(text, expected_urls, "http://www.daysagotest.com/a.html");
395 TEST_F(ShortcutsProviderTest, ClassifyAllMatchesInString) {
396 ACMatchClassifications matches =
397 AutocompleteMatch::ClassificationsFromString("0,0");
398 ClassifyTest classify_test(ASCIIToUTF16("A man, a plan, a canal Panama"),
401 ACMatchClassifications spans_a = classify_test.RunTest(ASCIIToUTF16("man"));
402 // ACMatch spans should be: '--MMM------------------------'
403 EXPECT_EQ("0,0,2,2,5,0", AutocompleteMatch::ClassificationsToString(spans_a));
405 ACMatchClassifications spans_b = classify_test.RunTest(ASCIIToUTF16("man p"));
406 // ACMatch spans should be: '--MMM----M-------------M-----'
407 EXPECT_EQ("0,0,2,2,5,0,9,2,10,0,23,2,24,0",
408 AutocompleteMatch::ClassificationsToString(spans_b));
410 ACMatchClassifications spans_c =
411 classify_test.RunTest(ASCIIToUTF16("man plan panama"));
412 // ACMatch spans should be:'--MMM----MMMM----------MMMMMM'
413 EXPECT_EQ("0,0,2,2,5,0,9,2,13,0,23,2",
414 AutocompleteMatch::ClassificationsToString(spans_c));
416 ClassifyTest classify_test2(ASCIIToUTF16("Yahoo! Sports - Sports News, "
417 "Scores, Rumors, Fantasy Games, and more"), matches);
419 ACMatchClassifications spans_d = classify_test2.RunTest(ASCIIToUTF16("ne"));
420 // ACMatch spans should match first two letters of the "news".
421 EXPECT_EQ("0,0,23,2,25,0",
422 AutocompleteMatch::ClassificationsToString(spans_d));
424 ACMatchClassifications spans_e =
425 classify_test2.RunTest(ASCIIToUTF16("news r"));
426 EXPECT_EQ("0,0,10,2,11,0,19,2,20,0,23,2,27,0,32,2,33,0,37,2,38,0,41,2,42,0,"
427 "66,2,67,0", AutocompleteMatch::ClassificationsToString(spans_e));
429 matches = AutocompleteMatch::ClassificationsFromString("0,1");
430 ClassifyTest classify_test3(ASCIIToUTF16("livescore.goal.com"), matches);
432 ACMatchClassifications spans_f = classify_test3.RunTest(ASCIIToUTF16("go"));
433 // ACMatch spans should match first two letters of the "goal".
434 EXPECT_EQ("0,1,10,3,12,1",
435 AutocompleteMatch::ClassificationsToString(spans_f));
437 matches = AutocompleteMatch::ClassificationsFromString("0,0,13,1");
438 ClassifyTest classify_test4(ASCIIToUTF16("Email login: mail.somecorp.com"),
441 ACMatchClassifications spans_g = classify_test4.RunTest(ASCIIToUTF16("ail"));
442 EXPECT_EQ("0,0,2,2,5,0,13,1,14,3,17,1",
443 AutocompleteMatch::ClassificationsToString(spans_g));
445 ACMatchClassifications spans_h =
446 classify_test4.RunTest(ASCIIToUTF16("lo log"));
447 EXPECT_EQ("0,0,6,2,9,0,13,1",
448 AutocompleteMatch::ClassificationsToString(spans_h));
450 ACMatchClassifications spans_i =
451 classify_test4.RunTest(ASCIIToUTF16("ail em"));
452 // 'Email' and 'ail' should be matched.
453 EXPECT_EQ("0,2,5,0,13,1,14,3,17,1",
454 AutocompleteMatch::ClassificationsToString(spans_i));
456 // Some web sites do not have a description. If the string being searched is
457 // empty, the classifications must also be empty: http://crbug.com/148647
458 // Extra parens in the next line hack around C++03's "most vexing parse".
459 class ClassifyTest classify_test5((string16()), ACMatchClassifications());
460 ACMatchClassifications spans_j = classify_test5.RunTest(ASCIIToUTF16("man"));
461 ASSERT_EQ(0U, spans_j.size());
463 // Matches which end at beginning of classification merge properly.
464 matches = AutocompleteMatch::ClassificationsFromString("0,4,9,0");
465 ClassifyTest classify_test6(ASCIIToUTF16("html password example"), matches);
467 // Extra space in the next string avoids having the string be a prefix of the
468 // text above, which would allow for two different valid classification sets,
469 // one of which uses two spans (the first of which would mark all of "html
470 // pass" as a match) and one which uses four (which marks the individual words
471 // as matches but not the space between them). This way only the latter is
473 ACMatchClassifications spans_k =
474 classify_test6.RunTest(ASCIIToUTF16("html pass"));
475 EXPECT_EQ("0,6,4,4,5,6,9,0",
476 AutocompleteMatch::ClassificationsToString(spans_k));
478 // Multiple matches with both beginning and end at beginning of
479 // classifications merge properly.
480 matches = AutocompleteMatch::ClassificationsFromString("0,1,11,0");
481 ClassifyTest classify_test7(ASCIIToUTF16("http://a.co is great"), matches);
483 ACMatchClassifications spans_l =
484 classify_test7.RunTest(ASCIIToUTF16("ht co"));
485 EXPECT_EQ("0,3,2,1,9,3,11,0",
486 AutocompleteMatch::ClassificationsToString(spans_l));
489 TEST_F(ShortcutsProviderTest, CalculateScore) {
490 ShortcutsBackend::Shortcut shortcut(
491 std::string(), ASCIIToUTF16("test"),
492 ShortcutsBackend::Shortcut::MatchCore(
493 ASCIIToUTF16("www.test.com"), GURL("http://www.test.com"),
494 ASCIIToUTF16("www.test.com"),
495 AutocompleteMatch::ClassificationsFromString("0,1,4,3,8,1"),
496 ASCIIToUTF16("A test"),
497 AutocompleteMatch::ClassificationsFromString("0,0,2,2"),
498 content::PAGE_TRANSITION_TYPED, AutocompleteMatchType::HISTORY_URL,
500 base::Time::Now(), 1);
503 const int max_relevance = AutocompleteResult::kLowestDefaultScore - 1;
504 const int kMaxScore = CalculateScore("test", shortcut, max_relevance);
506 // Score decreases as percent of the match is decreased.
507 int score_three_quarters = CalculateScore("tes", shortcut, max_relevance);
508 EXPECT_LT(score_three_quarters, kMaxScore);
509 int score_one_half = CalculateScore("te", shortcut, max_relevance);
510 EXPECT_LT(score_one_half, score_three_quarters);
511 int score_one_quarter = CalculateScore("t", shortcut, max_relevance);
512 EXPECT_LT(score_one_quarter, score_one_half);
514 // Should decay with time - one week.
515 shortcut.last_access_time = base::Time::Now() - base::TimeDelta::FromDays(7);
516 int score_week_old = CalculateScore("test", shortcut, max_relevance);
517 EXPECT_LT(score_week_old, kMaxScore);
519 // Should decay more in two weeks.
520 shortcut.last_access_time = base::Time::Now() - base::TimeDelta::FromDays(14);
521 int score_two_weeks_old = CalculateScore("test", shortcut, max_relevance);
522 EXPECT_LT(score_two_weeks_old, score_week_old);
524 // But not if it was activly clicked on. 2 hits slow decaying power.
525 shortcut.number_of_hits = 2;
526 shortcut.last_access_time = base::Time::Now() - base::TimeDelta::FromDays(14);
527 int score_popular_two_weeks_old =
528 CalculateScore("test", shortcut, max_relevance);
529 EXPECT_LT(score_two_weeks_old, score_popular_two_weeks_old);
530 // But still decayed.
531 EXPECT_LT(score_popular_two_weeks_old, kMaxScore);
533 // 3 hits slow decaying power even more.
534 shortcut.number_of_hits = 3;
535 shortcut.last_access_time = base::Time::Now() - base::TimeDelta::FromDays(14);
536 int score_more_popular_two_weeks_old =
537 CalculateScore("test", shortcut, max_relevance);
538 EXPECT_LT(score_two_weeks_old, score_more_popular_two_weeks_old);
539 EXPECT_LT(score_popular_two_weeks_old, score_more_popular_two_weeks_old);
540 // But still decayed.
541 EXPECT_LT(score_more_popular_two_weeks_old, kMaxScore);
544 TEST_F(ShortcutsProviderTest, DeleteMatch) {
545 TestShortcutInfo shortcuts_to_test_delete[] = {
546 { "BD85DBA2-8C29-49F9-84AE-48E1E90881F1", "delete", "www.deletetest.com/1",
547 "http://www.deletetest.com/1", "http://www.deletetest.com/1", "0,2",
548 "Erase this shortcut!", "0,0", content::PAGE_TRANSITION_TYPED,
549 AutocompleteMatchType::HISTORY_URL, "", 1, 1},
550 { "BD85DBA2-8C29-49F9-84AE-48E1E90881F2", "erase", "www.deletetest.com/1",
551 "http://www.deletetest.com/1", "http://www.deletetest.com/1", "0,2",
552 "Erase this shortcut!", "0,0", content::PAGE_TRANSITION_TYPED,
553 AutocompleteMatchType::HISTORY_TITLE, "", 1, 1},
554 { "BD85DBA2-8C29-49F9-84AE-48E1E90881F3", "keep", "www.deletetest.com/1/2",
555 "http://www.deletetest.com/1/2", "http://www.deletetest.com/1/2", "0,2",
556 "Keep this shortcut!", "0,0", content::PAGE_TRANSITION_TYPED,
557 AutocompleteMatchType::HISTORY_TITLE, "", 1, 1},
558 { "BD85DBA2-8C29-49F9-84AE-48E1E90881F4", "delete", "www.deletetest.com/2",
559 "http://www.deletetest.com/2", "http://www.deletetest.com/2", "0,2",
560 "Erase this shortcut!", "0,0", content::PAGE_TRANSITION_TYPED,
561 AutocompleteMatchType::HISTORY_URL, "", 1, 1},
564 size_t original_shortcuts_count = backend_->shortcuts_map().size();
566 FillData(shortcuts_to_test_delete, arraysize(shortcuts_to_test_delete));
568 EXPECT_EQ(original_shortcuts_count + 4, backend_->shortcuts_map().size());
569 EXPECT_FALSE(backend_->shortcuts_map().end() ==
570 backend_->shortcuts_map().find(ASCIIToUTF16("delete")));
571 EXPECT_FALSE(backend_->shortcuts_map().end() ==
572 backend_->shortcuts_map().find(ASCIIToUTF16("erase")));
574 AutocompleteMatch match(
575 provider_.get(), 1200, true, AutocompleteMatchType::HISTORY_TITLE);
577 match.destination_url = GURL(shortcuts_to_test_delete[0].destination_url);
578 match.contents = ASCIIToUTF16(shortcuts_to_test_delete[0].contents);
579 match.description = ASCIIToUTF16(shortcuts_to_test_delete[0].description);
581 provider_->DeleteMatch(match);
583 // shortcuts_to_test_delete[0] and shortcuts_to_test_delete[1] should be
584 // deleted, but not shortcuts_to_test_delete[2] or
585 // shortcuts_to_test_delete[3], which have different URLs.
586 EXPECT_EQ(original_shortcuts_count + 2, backend_->shortcuts_map().size());
587 EXPECT_FALSE(backend_->shortcuts_map().end() ==
588 backend_->shortcuts_map().find(ASCIIToUTF16("delete")));
589 EXPECT_TRUE(backend_->shortcuts_map().end() ==
590 backend_->shortcuts_map().find(ASCIIToUTF16("erase")));
592 match.destination_url = GURL(shortcuts_to_test_delete[3].destination_url);
593 match.contents = ASCIIToUTF16(shortcuts_to_test_delete[3].contents);
594 match.description = ASCIIToUTF16(shortcuts_to_test_delete[3].description);
596 provider_->DeleteMatch(match);
597 EXPECT_EQ(original_shortcuts_count + 1, backend_->shortcuts_map().size());
598 EXPECT_TRUE(backend_->shortcuts_map().end() ==
599 backend_->shortcuts_map().find(ASCIIToUTF16("delete")));
602 TEST_F(ShortcutsProviderTest, Extension) {
603 // Try an input string that matches an extension URL.
604 string16 text(ASCIIToUTF16("echo"));
605 std::string expected_url(
606 "chrome-extension://cedabbhfglmiikkmdgcpjdkocfcmbkee/?q=echo");
608 expected_urls.push_back(expected_url);
609 RunTest(text, expected_urls, expected_url);
611 // Claim the extension has been unloaded.
612 scoped_refptr<const extensions::Extension> extension =
613 extensions::ExtensionBuilder()
614 .SetManifest(extensions::DictionaryBuilder()
616 .Set("version", "1.0"))
617 .SetID("cedabbhfglmiikkmdgcpjdkocfcmbkee")
619 extensions::UnloadedExtensionInfo details(
620 extension.get(), extensions::UnloadedExtensionInfo::REASON_UNINSTALL);
621 content::NotificationService::current()->Notify(
622 chrome::NOTIFICATION_EXTENSION_UNLOADED,
623 content::Source<Profile>(&profile_),
624 content::Details<extensions::UnloadedExtensionInfo>(&details));
626 // Now the URL should have disappeared.
627 RunTest(text, URLs(), std::string());
630 } // namespace history