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/predictors/autocomplete_action_predictor.h"
7 #include "base/auto_reset.h"
8 #include "base/command_line.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/history/history_service_factory.h"
16 #include "chrome/browser/prerender/prerender_field_trial.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/history/core/browser/history_service.h"
20 #include "components/history/core/browser/in_memory_database.h"
21 #include "components/history/core/browser/url_database.h"
22 #include "components/omnibox/browser/autocomplete_match.h"
23 #include "content/public/test/test_browser_thread.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using base::ASCIIToUTF16
;
27 using content::BrowserThread
;
28 using predictors::AutocompleteActionPredictor
;
36 base::string16 user_text
;
39 AutocompleteActionPredictor::Action expected_action
;
41 { GURL("http://www.testsite.com/a.html"),
42 ASCIIToUTF16("Test - site - just a test"), 1,
43 ASCIIToUTF16("j"), 5, 0,
44 AutocompleteActionPredictor::ACTION_PRERENDER
},
45 { GURL("http://www.testsite.com/b.html"),
46 ASCIIToUTF16("Test - site - just a test"), 1,
47 ASCIIToUTF16("ju"), 3, 0,
48 AutocompleteActionPredictor::ACTION_PRERENDER
},
49 { GURL("http://www.testsite.com/c.html"),
50 ASCIIToUTF16("Test - site - just a test"), 5,
51 ASCIIToUTF16("just"), 3, 1,
52 AutocompleteActionPredictor::ACTION_PRECONNECT
},
53 { GURL("http://www.testsite.com/d.html"),
54 ASCIIToUTF16("Test - site - just a test"), 5,
55 ASCIIToUTF16("just"), 3, 0,
56 AutocompleteActionPredictor::ACTION_PRERENDER
},
57 { GURL("http://www.testsite.com/e.html"),
58 ASCIIToUTF16("Test - site - just a test"), 8,
59 ASCIIToUTF16("just"), 3, 1,
60 AutocompleteActionPredictor::ACTION_PRECONNECT
},
61 { GURL("http://www.testsite.com/f.html"),
62 ASCIIToUTF16("Test - site - just a test"), 8,
63 ASCIIToUTF16("just"), 3, 0,
64 AutocompleteActionPredictor::ACTION_PRERENDER
},
65 { GURL("http://www.testsite.com/g.html"),
66 ASCIIToUTF16("Test - site - just a test"), 12,
67 base::string16(), 5, 0,
68 AutocompleteActionPredictor::ACTION_NONE
},
69 { GURL("http://www.testsite.com/h.html"),
70 ASCIIToUTF16("Test - site - just a test"), 21,
71 ASCIIToUTF16("just a test"), 2, 0,
72 AutocompleteActionPredictor::ACTION_NONE
},
73 { GURL("http://www.testsite.com/i.html"),
74 ASCIIToUTF16("Test - site - just a test"), 28,
75 ASCIIToUTF16("just a test"), 2, 0,
76 AutocompleteActionPredictor::ACTION_NONE
}
81 namespace predictors
{
83 class AutocompleteActionPredictorTest
: public testing::Test
{
85 AutocompleteActionPredictorTest()
86 : ui_thread_(BrowserThread::UI
, &loop_
),
87 db_thread_(BrowserThread::DB
, &loop_
),
88 file_thread_(BrowserThread::FILE, &loop_
),
89 profile_(new TestingProfile()),
90 predictor_(new AutocompleteActionPredictor(profile_
.get())) {
93 ~AutocompleteActionPredictorTest() override
{
94 predictor_
.reset(NULL
);
99 void SetUp() override
{
100 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
101 switches::kPrerenderFromOmnibox
,
102 switches::kPrerenderFromOmniboxSwitchValueEnabled
);
104 predictor_
->CreateLocalCachesFromDatabase();
105 ASSERT_TRUE(profile_
->CreateHistoryService(true, false));
106 profile_
->BlockUntilHistoryProcessesPendingRequests();
108 ASSERT_TRUE(predictor_
->initialized_
);
109 ASSERT_TRUE(db_cache()->empty());
110 ASSERT_TRUE(db_id_cache()->empty());
113 void TearDown() override
{
114 profile_
->DestroyHistoryService();
115 predictor_
->Shutdown();
119 typedef AutocompleteActionPredictor::DBCacheKey DBCacheKey
;
120 typedef AutocompleteActionPredictor::DBCacheValue DBCacheValue
;
121 typedef AutocompleteActionPredictor::DBCacheMap DBCacheMap
;
122 typedef AutocompleteActionPredictor::DBIdCacheMap DBIdCacheMap
;
124 void AddAllRowsToHistory() {
125 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
)
126 ASSERT_TRUE(AddRowToHistory(test_url_db
[i
]));
129 history::URLID
AddRowToHistory(const TestUrlInfo
& test_row
) {
130 history::HistoryService
* history
= HistoryServiceFactory::GetForProfile(
131 profile_
.get(), ServiceAccessType::EXPLICIT_ACCESS
);
133 history::URLDatabase
* url_db
= history
->InMemoryDatabase();
136 const base::Time visit_time
=
137 base::Time::Now() - base::TimeDelta::FromDays(
138 test_row
.days_from_now
);
140 history::URLRow
row(test_row
.url
);
141 row
.set_title(test_row
.title
);
142 row
.set_last_visit(visit_time
);
144 return url_db
->AddURL(row
);
147 AutocompleteActionPredictorTable::Row
CreateRowFromTestUrlInfo(
148 const TestUrlInfo
& test_row
) const {
149 AutocompleteActionPredictorTable::Row row
;
150 row
.id
= base::GenerateGUID();
151 row
.user_text
= test_row
.user_text
;
152 row
.url
= test_row
.url
;
153 row
.number_of_hits
= test_row
.number_of_hits
;
154 row
.number_of_misses
= test_row
.number_of_misses
;
159 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
)
160 AddRow(test_url_db
[i
]);
163 std::string
AddRow(const TestUrlInfo
& test_row
) {
164 AutocompleteActionPredictorTable::Row row
=
165 CreateRowFromTestUrlInfo(test_row
);
166 predictor_
->AddAndUpdateRows(
167 AutocompleteActionPredictorTable::Rows(1, row
),
168 AutocompleteActionPredictorTable::Rows());
173 void UpdateRow(const AutocompleteActionPredictorTable::Row
& row
) {
174 AutocompleteActionPredictor::DBCacheKey key
= { row
.user_text
, row
.url
};
175 ASSERT_TRUE(db_cache()->find(key
) != db_cache()->end());
176 predictor_
->AddAndUpdateRows(
177 AutocompleteActionPredictorTable::Rows(),
178 AutocompleteActionPredictorTable::Rows(1, row
));
181 void DeleteAllRows() {
182 predictor_
->DeleteAllRows();
185 void DeleteRowsWithURLs(const history::URLRows
& rows
) {
186 predictor_
->DeleteRowsWithURLs(rows
);
189 void DeleteOldIdsFromCaches(
190 std::vector
<AutocompleteActionPredictorTable::Row::Id
>* id_list
) {
191 history::HistoryService
* history_service
=
192 HistoryServiceFactory::GetForProfile(
193 profile_
.get(), ServiceAccessType::EXPLICIT_ACCESS
);
194 ASSERT_TRUE(history_service
);
196 history::URLDatabase
* url_db
= history_service
->InMemoryDatabase();
199 // Reset the predictor's |initialized_| flag for the life of this call,
200 // since outside of testing this function is only supposed to be reached
201 // before initialization is completed.
202 base::AutoReset
<bool> initialized_reset(&predictor_
->initialized_
, false);
203 predictor_
->DeleteOldIdsFromCaches(url_db
, id_list
);
206 AutocompleteActionPredictor
* predictor() { return predictor_
.get(); }
208 DBCacheMap
* db_cache() { return &predictor_
->db_cache_
; }
209 DBIdCacheMap
* db_id_cache() { return &predictor_
->db_id_cache_
; }
211 static int maximum_days_to_keep_entry() {
212 return AutocompleteActionPredictor::kMaximumDaysToKeepEntry
;
216 base::MessageLoop loop_
;
217 content::TestBrowserThread ui_thread_
;
218 content::TestBrowserThread db_thread_
;
219 content::TestBrowserThread file_thread_
;
220 scoped_ptr
<TestingProfile
> profile_
;
221 scoped_ptr
<AutocompleteActionPredictor
> predictor_
;
225 TEST_F(AutocompleteActionPredictorTest
, AddRow
) {
226 // Add a test entry to the predictor.
227 std::string guid
= AddRow(test_url_db
[0]);
229 // Get the data back out of the cache.
230 const DBCacheKey key
= { test_url_db
[0].user_text
, test_url_db
[0].url
};
231 DBCacheMap::const_iterator it
= db_cache()->find(key
);
232 EXPECT_TRUE(it
!= db_cache()->end());
234 const DBCacheValue value
= { test_url_db
[0].number_of_hits
,
235 test_url_db
[0].number_of_misses
};
236 EXPECT_EQ(value
.number_of_hits
, it
->second
.number_of_hits
);
237 EXPECT_EQ(value
.number_of_misses
, it
->second
.number_of_misses
);
239 DBIdCacheMap::const_iterator id_it
= db_id_cache()->find(key
);
240 EXPECT_TRUE(id_it
!= db_id_cache()->end());
241 EXPECT_EQ(guid
, id_it
->second
);
244 TEST_F(AutocompleteActionPredictorTest
, UpdateRow
) {
245 ASSERT_NO_FATAL_FAILURE(AddAllRows());
247 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
248 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
250 // Get the data back out of the cache.
251 const DBCacheKey key
= { test_url_db
[0].user_text
, test_url_db
[0].url
};
252 DBCacheMap::const_iterator it
= db_cache()->find(key
);
253 EXPECT_TRUE(it
!= db_cache()->end());
255 DBIdCacheMap::const_iterator id_it
= db_id_cache()->find(key
);
256 EXPECT_TRUE(id_it
!= db_id_cache()->end());
258 AutocompleteActionPredictorTable::Row update_row
;
259 update_row
.id
= id_it
->second
;
260 update_row
.user_text
= key
.user_text
;
261 update_row
.url
= key
.url
;
262 update_row
.number_of_hits
= it
->second
.number_of_hits
+ 1;
263 update_row
.number_of_misses
= it
->second
.number_of_misses
+ 2;
265 UpdateRow(update_row
);
267 // Get the updated version.
268 DBCacheMap::const_iterator update_it
= db_cache()->find(key
);
269 EXPECT_TRUE(update_it
!= db_cache()->end());
271 EXPECT_EQ(update_row
.number_of_hits
, update_it
->second
.number_of_hits
);
272 EXPECT_EQ(update_row
.number_of_misses
, update_it
->second
.number_of_misses
);
274 DBIdCacheMap::const_iterator update_id_it
= db_id_cache()->find(key
);
275 EXPECT_TRUE(update_id_it
!= db_id_cache()->end());
277 EXPECT_EQ(id_it
->second
, update_id_it
->second
);
280 TEST_F(AutocompleteActionPredictorTest
, DeleteAllRows
) {
281 ASSERT_NO_FATAL_FAILURE(AddAllRows());
283 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
284 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
288 EXPECT_TRUE(db_cache()->empty());
289 EXPECT_TRUE(db_id_cache()->empty());
292 TEST_F(AutocompleteActionPredictorTest
, DeleteRowsWithURLs
) {
293 ASSERT_NO_FATAL_FAILURE(AddAllRows());
295 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
296 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
298 history::URLRows rows
;
299 for (size_t i
= 0; i
< 2; ++i
)
300 rows
.push_back(history::URLRow(test_url_db
[i
].url
));
302 DeleteRowsWithURLs(rows
);
304 EXPECT_EQ(arraysize(test_url_db
) - 2, db_cache()->size());
305 EXPECT_EQ(arraysize(test_url_db
) - 2, db_id_cache()->size());
307 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
308 DBCacheKey key
= { test_url_db
[i
].user_text
, test_url_db
[i
].url
};
310 bool deleted
= (i
< 2);
311 EXPECT_EQ(deleted
, db_cache()->find(key
) == db_cache()->end());
312 EXPECT_EQ(deleted
, db_id_cache()->find(key
) == db_id_cache()->end());
316 TEST_F(AutocompleteActionPredictorTest
, DeleteOldIdsFromCaches
) {
317 std::vector
<AutocompleteActionPredictorTable::Row::Id
> expected
;
318 std::vector
<AutocompleteActionPredictorTable::Row::Id
> all_ids
;
320 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
321 std::string row_id
= AddRow(test_url_db
[i
]);
322 all_ids
.push_back(row_id
);
325 base::StartsWith(test_url_db
[i
].url
.path(), "/d",
326 base::CompareCase::SENSITIVE
) ||
327 (test_url_db
[i
].days_from_now
> maximum_days_to_keep_entry());
330 expected
.push_back(row_id
);
332 ASSERT_TRUE(AddRowToHistory(test_url_db
[i
]));
335 std::vector
<AutocompleteActionPredictorTable::Row::Id
> id_list
;
336 DeleteOldIdsFromCaches(&id_list
);
337 EXPECT_EQ(expected
.size(), id_list
.size());
338 EXPECT_EQ(all_ids
.size() - expected
.size(), db_cache()->size());
339 EXPECT_EQ(all_ids
.size() - expected
.size(), db_id_cache()->size());
341 for (std::vector
<AutocompleteActionPredictorTable::Row::Id
>::iterator it
=
343 it
!= all_ids
.end(); ++it
) {
345 (std::find(expected
.begin(), expected
.end(), *it
) != expected
.end());
347 (std::find(id_list
.begin(), id_list
.end(), *it
) != id_list
.end());
348 EXPECT_EQ(in_expected
, in_list
);
352 TEST_F(AutocompleteActionPredictorTest
, RecommendActionURL
) {
353 ASSERT_NO_FATAL_FAILURE(AddAllRows());
355 AutocompleteMatch match
;
356 match
.type
= AutocompleteMatchType::HISTORY_URL
;
358 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
359 match
.destination_url
= GURL(test_url_db
[i
].url
);
360 EXPECT_EQ(test_url_db
[i
].expected_action
,
361 predictor()->RecommendAction(test_url_db
[i
].user_text
, match
))
362 << "Unexpected action for " << match
.destination_url
;
366 TEST_F(AutocompleteActionPredictorTest
, RecommendActionSearch
) {
367 ASSERT_NO_FATAL_FAILURE(AddAllRows());
369 AutocompleteMatch match
;
370 match
.type
= AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED
;
372 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
373 match
.destination_url
= GURL(test_url_db
[i
].url
);
374 AutocompleteActionPredictor::Action expected_action
=
375 (test_url_db
[i
].expected_action
==
376 AutocompleteActionPredictor::ACTION_PRERENDER
) ?
377 AutocompleteActionPredictor::ACTION_PRECONNECT
:
378 test_url_db
[i
].expected_action
;
379 EXPECT_EQ(expected_action
,
380 predictor()->RecommendAction(test_url_db
[i
].user_text
, match
))
381 << "Unexpected action for " << match
.destination_url
;
385 } // namespace predictors