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.h"
12 #include "base/string_util.h"
13 #include "base/time.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/autocomplete/autocomplete_match.h"
16 #include "chrome/browser/history/history_service.h"
17 #include "chrome/browser/history/history_service_factory.h"
18 #include "chrome/browser/history/in_memory_database.h"
19 #include "chrome/browser/history/url_database.h"
20 #include "chrome/browser/prerender/prerender_field_trial.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "content/public/test/test_browser_thread.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using content::BrowserThread
;
27 using predictors::AutocompleteActionPredictor
;
38 AutocompleteActionPredictor::Action expected_action
;
40 { GURL("http://www.testsite.com/a.html"),
41 ASCIIToUTF16("Test - site - just a test"), 1,
42 ASCIIToUTF16("j"), 5, 0,
43 AutocompleteActionPredictor::ACTION_PRERENDER
},
44 { GURL("http://www.testsite.com/b.html"),
45 ASCIIToUTF16("Test - site - just a test"), 1,
46 ASCIIToUTF16("ju"), 3, 0,
47 AutocompleteActionPredictor::ACTION_PRERENDER
},
48 { GURL("http://www.testsite.com/c.html"),
49 ASCIIToUTF16("Test - site - just a test"), 5,
50 ASCIIToUTF16("just"), 3, 1,
51 AutocompleteActionPredictor::ACTION_PRECONNECT
},
52 { GURL("http://www.testsite.com/d.html"),
53 ASCIIToUTF16("Test - site - just a test"), 5,
54 ASCIIToUTF16("just"), 3, 0,
55 AutocompleteActionPredictor::ACTION_PRERENDER
},
56 { GURL("http://www.testsite.com/e.html"),
57 ASCIIToUTF16("Test - site - just a test"), 8,
58 ASCIIToUTF16("just"), 3, 1,
59 AutocompleteActionPredictor::ACTION_PRECONNECT
},
60 { GURL("http://www.testsite.com/f.html"),
61 ASCIIToUTF16("Test - site - just a test"), 8,
62 ASCIIToUTF16("just"), 3, 0,
63 AutocompleteActionPredictor::ACTION_PRERENDER
},
64 { GURL("http://www.testsite.com/g.html"),
65 ASCIIToUTF16("Test - site - just a test"), 12,
67 AutocompleteActionPredictor::ACTION_NONE
},
68 { GURL("http://www.testsite.com/h.html"),
69 ASCIIToUTF16("Test - site - just a test"), 21,
70 ASCIIToUTF16("just a test"), 2, 0,
71 AutocompleteActionPredictor::ACTION_NONE
},
72 { GURL("http://www.testsite.com/i.html"),
73 ASCIIToUTF16("Test - site - just a test"), 28,
74 ASCIIToUTF16("just a test"), 2, 0,
75 AutocompleteActionPredictor::ACTION_NONE
}
80 namespace predictors
{
82 class AutocompleteActionPredictorTest
: public testing::Test
{
84 AutocompleteActionPredictorTest()
85 : loop_(MessageLoop::TYPE_DEFAULT
),
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 virtual ~AutocompleteActionPredictorTest() {
94 predictor_
.reset(NULL
);
99 virtual void SetUp() {
100 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
101 switches::kPrerenderFromOmnibox
,
102 switches::kPrerenderFromOmniboxSwitchValueEnabled
);
104 predictor_
->CreateLocalCachesFromDatabase();
105 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 virtual void TearDown() {
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 HistoryService
* history
=
131 HistoryServiceFactory::GetForProfile(profile_
.get(),
132 Profile::EXPLICIT_ACCESS
);
134 history::URLDatabase
* url_db
= history
->InMemoryDatabase();
137 const base::Time visit_time
=
138 base::Time::Now() - base::TimeDelta::FromDays(
139 test_row
.days_from_now
);
141 history::URLRow
row(test_row
.url
);
142 row
.set_title(test_row
.title
);
143 row
.set_last_visit(visit_time
);
145 return url_db
->AddURL(row
);
148 AutocompleteActionPredictorTable::Row
CreateRowFromTestUrlInfo(
149 const TestUrlInfo
& test_row
) const {
150 AutocompleteActionPredictorTable::Row row
;
151 row
.id
= base::GenerateGUID();
152 row
.user_text
= test_row
.user_text
;
153 row
.url
= test_row
.url
;
154 row
.number_of_hits
= test_row
.number_of_hits
;
155 row
.number_of_misses
= test_row
.number_of_misses
;
160 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
)
161 AddRow(test_url_db
[i
]);
164 std::string
AddRow(const TestUrlInfo
& test_row
) {
165 AutocompleteActionPredictorTable::Row row
=
166 CreateRowFromTestUrlInfo(test_row
);
167 predictor_
->AddAndUpdateRows(
168 AutocompleteActionPredictorTable::Rows(1, row
),
169 AutocompleteActionPredictorTable::Rows());
174 void UpdateRow(const AutocompleteActionPredictorTable::Row
& row
) {
175 AutocompleteActionPredictor::DBCacheKey key
= { row
.user_text
, row
.url
};
176 ASSERT_TRUE(db_cache()->find(key
) != db_cache()->end());
177 predictor_
->AddAndUpdateRows(
178 AutocompleteActionPredictorTable::Rows(),
179 AutocompleteActionPredictorTable::Rows(1, row
));
182 void DeleteAllRows() {
183 predictor_
->DeleteAllRows();
186 void DeleteRowsWithURLs(const history::URLRows
& rows
) {
187 predictor_
->DeleteRowsWithURLs(rows
);
190 void DeleteOldIdsFromCaches(
191 std::vector
<AutocompleteActionPredictorTable::Row::Id
>* id_list
) {
192 HistoryService
* history_service
=
193 HistoryServiceFactory::GetForProfile(profile_
.get(),
194 Profile::EXPLICIT_ACCESS
);
195 ASSERT_TRUE(history_service
);
197 history::URLDatabase
* url_db
= history_service
->InMemoryDatabase();
200 // Reset the predictor's |initialized_| flag for the life of this call,
201 // since outside of testing this function is only supposed to be reached
202 // before initialization is completed.
203 base::AutoReset
<bool> initialized_reset(&predictor_
->initialized_
, false);
204 predictor_
->DeleteOldIdsFromCaches(url_db
, id_list
);
207 AutocompleteActionPredictor
* predictor() { return predictor_
.get(); }
209 DBCacheMap
* db_cache() { return &predictor_
->db_cache_
; }
210 DBIdCacheMap
* db_id_cache() { return &predictor_
->db_id_cache_
; }
212 static int maximum_days_to_keep_entry() {
213 return AutocompleteActionPredictor::kMaximumDaysToKeepEntry
;
218 content::TestBrowserThread ui_thread_
;
219 content::TestBrowserThread db_thread_
;
220 content::TestBrowserThread file_thread_
;
221 scoped_ptr
<TestingProfile
> profile_
;
222 scoped_ptr
<AutocompleteActionPredictor
> predictor_
;
226 TEST_F(AutocompleteActionPredictorTest
, AddRow
) {
227 // Add a test entry to the predictor.
228 std::string guid
= AddRow(test_url_db
[0]);
230 // Get the data back out of the cache.
231 const DBCacheKey key
= { test_url_db
[0].user_text
, test_url_db
[0].url
};
232 DBCacheMap::const_iterator it
= db_cache()->find(key
);
233 EXPECT_TRUE(it
!= db_cache()->end());
235 const DBCacheValue value
= { test_url_db
[0].number_of_hits
,
236 test_url_db
[0].number_of_misses
};
237 EXPECT_EQ(value
.number_of_hits
, it
->second
.number_of_hits
);
238 EXPECT_EQ(value
.number_of_misses
, it
->second
.number_of_misses
);
240 DBIdCacheMap::const_iterator id_it
= db_id_cache()->find(key
);
241 EXPECT_TRUE(id_it
!= db_id_cache()->end());
242 EXPECT_EQ(guid
, id_it
->second
);
245 TEST_F(AutocompleteActionPredictorTest
, UpdateRow
) {
246 ASSERT_NO_FATAL_FAILURE(AddAllRows());
248 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
249 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
251 // Get the data back out of the cache.
252 const DBCacheKey key
= { test_url_db
[0].user_text
, test_url_db
[0].url
};
253 DBCacheMap::const_iterator it
= db_cache()->find(key
);
254 EXPECT_TRUE(it
!= db_cache()->end());
256 DBIdCacheMap::const_iterator id_it
= db_id_cache()->find(key
);
257 EXPECT_TRUE(id_it
!= db_id_cache()->end());
259 AutocompleteActionPredictorTable::Row update_row
;
260 update_row
.id
= id_it
->second
;
261 update_row
.user_text
= key
.user_text
;
262 update_row
.url
= key
.url
;
263 update_row
.number_of_hits
= it
->second
.number_of_hits
+ 1;
264 update_row
.number_of_misses
= it
->second
.number_of_misses
+ 2;
266 UpdateRow(update_row
);
268 // Get the updated version.
269 DBCacheMap::const_iterator update_it
= db_cache()->find(key
);
270 EXPECT_TRUE(update_it
!= db_cache()->end());
272 EXPECT_EQ(update_row
.number_of_hits
, update_it
->second
.number_of_hits
);
273 EXPECT_EQ(update_row
.number_of_misses
, update_it
->second
.number_of_misses
);
275 DBIdCacheMap::const_iterator update_id_it
= db_id_cache()->find(key
);
276 EXPECT_TRUE(update_id_it
!= db_id_cache()->end());
278 EXPECT_EQ(id_it
->second
, update_id_it
->second
);
281 TEST_F(AutocompleteActionPredictorTest
, DeleteAllRows
) {
282 ASSERT_NO_FATAL_FAILURE(AddAllRows());
284 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
285 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
289 EXPECT_TRUE(db_cache()->empty());
290 EXPECT_TRUE(db_id_cache()->empty());
293 TEST_F(AutocompleteActionPredictorTest
, DeleteRowsWithURLs
) {
294 ASSERT_NO_FATAL_FAILURE(AddAllRows());
296 EXPECT_EQ(arraysize(test_url_db
), db_cache()->size());
297 EXPECT_EQ(arraysize(test_url_db
), db_id_cache()->size());
299 history::URLRows rows
;
300 for (size_t i
= 0; i
< 2; ++i
)
301 rows
.push_back(history::URLRow(test_url_db
[i
].url
));
303 DeleteRowsWithURLs(rows
);
305 EXPECT_EQ(arraysize(test_url_db
) - 2, db_cache()->size());
306 EXPECT_EQ(arraysize(test_url_db
) - 2, db_id_cache()->size());
308 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
309 DBCacheKey key
= { test_url_db
[i
].user_text
, test_url_db
[i
].url
};
311 bool deleted
= (i
< 2);
312 EXPECT_EQ(deleted
, db_cache()->find(key
) == db_cache()->end());
313 EXPECT_EQ(deleted
, db_id_cache()->find(key
) == db_id_cache()->end());
317 TEST_F(AutocompleteActionPredictorTest
, DeleteOldIdsFromCaches
) {
318 std::vector
<AutocompleteActionPredictorTable::Row::Id
> expected
;
319 std::vector
<AutocompleteActionPredictorTable::Row::Id
> all_ids
;
321 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
322 std::string row_id
= AddRow(test_url_db
[i
]);
323 all_ids
.push_back(row_id
);
325 bool exclude_url
= StartsWithASCII(test_url_db
[i
].url
.path(), "/d", true) ||
326 (test_url_db
[i
].days_from_now
> maximum_days_to_keep_entry());
329 expected
.push_back(row_id
);
331 ASSERT_TRUE(AddRowToHistory(test_url_db
[i
]));
334 std::vector
<AutocompleteActionPredictorTable::Row::Id
> id_list
;
335 DeleteOldIdsFromCaches(&id_list
);
336 EXPECT_EQ(expected
.size(), id_list
.size());
337 EXPECT_EQ(all_ids
.size() - expected
.size(), db_cache()->size());
338 EXPECT_EQ(all_ids
.size() - expected
.size(), db_id_cache()->size());
340 for (std::vector
<AutocompleteActionPredictorTable::Row::Id
>::iterator it
=
342 it
!= all_ids
.end(); ++it
) {
344 (std::find(expected
.begin(), expected
.end(), *it
) != expected
.end());
346 (std::find(id_list
.begin(), id_list
.end(), *it
) != id_list
.end());
347 EXPECT_EQ(in_expected
, in_list
);
351 TEST_F(AutocompleteActionPredictorTest
, RecommendActionURL
) {
352 ASSERT_NO_FATAL_FAILURE(AddAllRows());
354 AutocompleteMatch match
;
355 match
.type
= AutocompleteMatch::HISTORY_URL
;
357 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_url_db
); ++i
) {
358 match
.destination_url
= GURL(test_url_db
[i
].url
);
359 EXPECT_EQ(test_url_db
[i
].expected_action
,
360 predictor()->RecommendAction(test_url_db
[i
].user_text
, match
))
361 << "Unexpected action for " << match
.destination_url
;
365 TEST_F(AutocompleteActionPredictorTest
, RecommendActionSearch
) {
366 ASSERT_NO_FATAL_FAILURE(AddAllRows());
368 AutocompleteMatch match
;
369 match
.type
= AutocompleteMatch::SEARCH_WHAT_YOU_TYPED
;
371 for (size_t i
= 0; i
< arraysize(test_url_db
); ++i
) {
372 match
.destination_url
= GURL(test_url_db
[i
].url
);
373 AutocompleteActionPredictor::Action expected_action
=
374 (test_url_db
[i
].expected_action
==
375 AutocompleteActionPredictor::ACTION_PRERENDER
) ?
376 AutocompleteActionPredictor::ACTION_PRECONNECT
:
377 test_url_db
[i
].expected_action
;
378 EXPECT_EQ(expected_action
,
379 predictor()->RecommendAction(test_url_db
[i
].user_text
, match
))
380 << "Unexpected action for " << match
.destination_url
;
384 } // namespace predictors