OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/safe_browsing/last_download_finder.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/callback.h" | |
12 #include "base/file_util.h" | |
13 #include "base/run_loop.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "base/strings/utf_string_conversions.h" | |
16 #include "chrome/browser/history/chrome_history_client.h" | |
17 #include "chrome/browser/history/chrome_history_client_factory.h" | |
18 #include "chrome/browser/history/download_row.h" | |
19 #include "chrome/browser/history/history_service.h" | |
20 #include "chrome/browser/history/history_service_factory.h" | |
21 #include "chrome/browser/history/web_history_service_factory.h" | |
22 #include "chrome/browser/prefs/browser_prefs.h" | |
23 #include "chrome/browser/profiles/profile_manager.h" | |
24 #include "chrome/common/chrome_constants.h" | |
25 #include "chrome/common/pref_names.h" | |
26 #include "chrome/common/safe_browsing/csd.pb.h" | |
27 #include "chrome/test/base/testing_browser_process.h" | |
28 #include "chrome/test/base/testing_pref_service_syncable.h" | |
29 #include "chrome/test/base/testing_profile.h" | |
30 #include "chrome/test/base/testing_profile_manager.h" | |
31 #include "content/public/test/test_browser_thread_bundle.h" | |
32 #include "content/public/test/test_utils.h" | |
33 #include "testing/gtest/include/gtest/gtest.h" | |
34 | |
35 namespace { | |
36 | |
37 // A BrowserContextKeyedServiceFactory::TestingFactoryFunction that creates a | |
38 // HistoryService for a TestingProfile. | |
39 KeyedService* BuildHistoryService(content::BrowserContext* context) { | |
40 TestingProfile* profile = static_cast<TestingProfile*>(context); | |
41 | |
42 // Delete the file before creating the service. | |
43 base::FilePath history_path( | |
44 profile->GetPath().Append(chrome::kHistoryFilename)); | |
45 if (!base::DeleteFile(history_path, false) || | |
46 base::PathExists(history_path)) { | |
47 ADD_FAILURE() << "failed to delete history db file " | |
48 << history_path.value(); | |
49 return NULL; | |
50 } | |
51 | |
52 HistoryService* history_service = new HistoryService( | |
53 ChromeHistoryClientFactory::GetForProfile(profile), profile); | |
54 if (history_service->Init(profile->GetPath())) | |
55 return history_service; | |
56 | |
57 ADD_FAILURE() << "failed to initialize history service"; | |
58 delete history_service; | |
59 return NULL; | |
60 } | |
61 | |
62 } // namespace | |
63 | |
64 class LastDownloadFinderTest : public testing::Test { | |
65 public: | |
66 void NeverCalled(scoped_ptr< | |
67 safe_browsing::ClientIncidentReport_DownloadDetails> download) { | |
68 FAIL(); | |
69 } | |
70 | |
71 // Creates a new profile that participates in safe browsing and adds a | |
72 // download to its history. | |
73 void CreateProfileWithDownload() { | |
74 TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN); | |
75 HistoryService* history_service = | |
76 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS); | |
77 history_service->CreateDownload( | |
78 CreateTestDownloadRow(), | |
79 base::Bind(&LastDownloadFinderTest::OnDownloadCreated, | |
80 base::Unretained(this))); | |
81 } | |
82 | |
83 // safe_browsing::LastDownloadFinder::LastDownloadCallback implementation that | |
84 // passes the found download to |result| and then runs a closure. | |
85 void OnLastDownload( | |
86 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>* result, | |
87 const base::Closure& quit_closure, | |
88 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> | |
89 download) { | |
90 *result = download.Pass(); | |
91 quit_closure.Run(); | |
92 } | |
93 | |
94 protected: | |
95 // A type for specifying whether or not a profile created by CreateProfile | |
96 // participates in safe browsing. | |
97 enum SafeBrowsingDisposition { | |
98 SAFE_BROWSING_OPT_OUT, | |
99 SAFE_BROWSING_OPT_IN, | |
100 }; | |
101 | |
102 LastDownloadFinderTest() : profile_number_() {} | |
103 | |
104 virtual void SetUp() OVERRIDE { | |
105 testing::Test::SetUp(); | |
106 profile_manager_.reset( | |
107 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); | |
108 ASSERT_TRUE(profile_manager_->SetUp()); | |
109 } | |
110 | |
111 virtual void TearDown() OVERRIDE { | |
112 // Shut down the history service on all profiles. | |
113 std::vector<Profile*> profiles( | |
114 profile_manager_->profile_manager()->GetLoadedProfiles()); | |
115 for (size_t i = 0; i < profiles.size(); ++i) { | |
116 profiles[0]->AsTestingProfile()->DestroyHistoryService(); | |
117 } | |
118 profile_manager_.reset(); | |
119 TestingBrowserProcess::DeleteInstance(); | |
120 testing::Test::TearDown(); | |
121 } | |
122 | |
123 TestingProfile* CreateProfile(SafeBrowsingDisposition safe_browsing_opt_in) { | |
124 std::string profile_name("profile"); | |
125 profile_name.append(base::IntToString(++profile_number_)); | |
126 | |
127 // Set up keyed service factories. | |
128 TestingProfile::TestingFactories factories; | |
129 // Build up a custom history service. | |
130 factories.push_back(std::make_pair(HistoryServiceFactory::GetInstance(), | |
131 &BuildHistoryService)); | |
132 // Suppress WebHistoryService since it makes network requests. | |
133 factories.push_back(std::make_pair( | |
134 WebHistoryServiceFactory::GetInstance(), | |
135 static_cast<BrowserContextKeyedServiceFactory::TestingFactoryFunction>( | |
136 NULL))); | |
137 | |
138 // Create prefs for the profile with safe browsing enabled or not. | |
139 scoped_ptr<TestingPrefServiceSyncable> prefs( | |
140 new TestingPrefServiceSyncable); | |
141 chrome::RegisterUserProfilePrefs(prefs->registry()); | |
142 prefs->SetBoolean(prefs::kSafeBrowsingEnabled, | |
143 safe_browsing_opt_in == SAFE_BROWSING_OPT_IN); | |
144 | |
145 TestingProfile* profile = profile_manager_->CreateTestingProfile( | |
146 profile_name, | |
147 prefs.PassAs<PrefServiceSyncable>(), | |
148 base::UTF8ToUTF16(profile_name), // user_name | |
149 0, // avatar_id | |
150 std::string(), // supervised_user_id | |
151 factories); | |
152 | |
153 return profile; | |
154 } | |
155 | |
156 void AddDownload(Profile* profile, const history::DownloadRow& download) { | |
157 base::RunLoop run_loop; | |
158 | |
159 HistoryService* history_service = | |
160 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS); | |
161 history_service->CreateDownload( | |
162 download, | |
163 base::Bind(&LastDownloadFinderTest::ContinueOnDownloadCreated, | |
164 base::Unretained(this), | |
165 run_loop.QuitClosure())); | |
166 run_loop.Run(); | |
167 } | |
168 | |
169 // Wait for the history backend thread to process any outstanding tasks. | |
170 // This is needed because HistoryService::QueryDownloads uses PostTaskAndReply | |
171 // to do work on the backend thread and then invoke the caller's callback on | |
172 // the originating thread. The PostTaskAndReplyRelay holds a reference to the | |
173 // backend until its RunReplyAndSelfDestruct is called on the originating | |
174 // thread. This reference MUST be released (on the originating thread, | |
175 // remember) _before_ calling DestroyHistoryService in TearDown(). See the | |
176 // giant comment in HistoryService::Cleanup explaining where the backend's | |
177 // dtor must be run. | |
178 void FlushHistoryBackend(Profile* profile) { | |
179 base::RunLoop run_loop; | |
180 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS) | |
181 ->FlushForTest(run_loop.QuitClosure()); | |
182 run_loop.Run(); | |
183 // Then make sure anything bounced back to the main thread has been handled. | |
184 base::RunLoop().RunUntilIdle(); | |
185 } | |
186 | |
187 // Runs the last download finder on all loaded profiles, returning the found | |
188 // download or an empty pointer if none was found. | |
189 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> | |
190 RunLastDownloadFinder() { | |
191 base::RunLoop run_loop; | |
192 | |
193 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> | |
194 last_download; | |
195 | |
196 scoped_ptr<safe_browsing::LastDownloadFinder> finder( | |
197 safe_browsing::LastDownloadFinder::Create( | |
198 base::Bind(&LastDownloadFinderTest::OnLastDownload, | |
199 base::Unretained(this), | |
200 &last_download, | |
201 run_loop.QuitClosure()))); | |
202 | |
203 if (finder) | |
204 run_loop.Run(); | |
205 | |
206 return last_download.Pass(); | |
207 } | |
208 | |
209 history::DownloadRow CreateTestDownloadRow() { | |
210 base::Time now(base::Time::Now()); | |
211 return history::DownloadRow( | |
212 base::FilePath(FILE_PATH_LITERAL("spam.exe")), | |
213 base::FilePath(FILE_PATH_LITERAL("spam.exe")), | |
214 std::vector<GURL>(1, GURL("http://www.google.com")), // url_chain | |
215 GURL(), // referrer | |
216 "application/octet-stream", // mime_type | |
217 "application/octet-stream", // original_mime_type | |
218 now - base::TimeDelta::FromMinutes(10), // start | |
219 now - base::TimeDelta::FromMinutes(9), // end | |
220 std::string(), // etag | |
221 std::string(), // last_modified | |
222 47LL, // received | |
223 47LL, // total | |
224 content::DownloadItem::COMPLETE, // download_state | |
225 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, // danger_type | |
226 content::DOWNLOAD_INTERRUPT_REASON_NONE, // interrupt_reason, | |
227 1, // id | |
228 false, // download_opened | |
229 std::string(), // ext_id | |
230 std::string()); // ext_name | |
231 } | |
232 | |
233 void ExpectNoDownloadFound(scoped_ptr< | |
234 safe_browsing::ClientIncidentReport_DownloadDetails> download) { | |
235 EXPECT_FALSE(download); | |
236 } | |
237 | |
238 void ExpectFoundTestDownload(scoped_ptr< | |
239 safe_browsing::ClientIncidentReport_DownloadDetails> download) { | |
240 ASSERT_TRUE(download); | |
241 } | |
242 | |
243 content::TestBrowserThreadBundle browser_thread_bundle_; | |
244 scoped_ptr<TestingProfileManager> profile_manager_; | |
245 | |
246 private: | |
247 // A HistoryService::DownloadCreateCallback that asserts that the download was | |
248 // created and runs |closure|. | |
249 void ContinueOnDownloadCreated(const base::Closure& closure, bool created) { | |
250 ASSERT_TRUE(created); | |
251 closure.Run(); | |
252 } | |
253 | |
254 // A HistoryService::DownloadCreateCallback that asserts that the download was | |
255 // created. | |
256 void OnDownloadCreated(bool created) { ASSERT_TRUE(created); } | |
257 | |
258 int profile_number_; | |
259 }; | |
260 | |
261 // Tests that nothing happens if there are no profiles at all. | |
262 TEST_F(LastDownloadFinderTest, NoProfiles) { | |
263 ExpectNoDownloadFound(RunLastDownloadFinder()); | |
264 } | |
265 | |
266 // Tests that nothing happens other than the callback being invoked if there are | |
267 // no profiles participating in safe browsing. | |
268 TEST_F(LastDownloadFinderTest, NoParticipatingProfiles) { | |
269 // Create a profile with a history service that is opted-out | |
270 TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_OUT); | |
271 | |
272 // Add a download. | |
273 AddDownload(profile, CreateTestDownloadRow()); | |
274 | |
275 ExpectNoDownloadFound(RunLastDownloadFinder()); | |
276 } | |
277 | |
278 // Tests that a download is found from a single profile. | |
279 TEST_F(LastDownloadFinderTest, SimpleEndToEnd) { | |
280 // Create a profile with a history service that is opted-in. | |
281 TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN); | |
282 | |
283 // Add a download. | |
284 AddDownload(profile, CreateTestDownloadRow()); | |
285 | |
286 ExpectFoundTestDownload(RunLastDownloadFinder()); | |
287 } | |
288 | |
289 // Tests that there is no crash if the finder is deleted before results arrive. | |
290 TEST_F(LastDownloadFinderTest, DeleteBeforeResults) { | |
291 // Create a profile with a history service that is opted-in. | |
292 TestingProfile* profile = CreateProfile(SAFE_BROWSING_OPT_IN); | |
293 | |
294 // Add a download. | |
295 AddDownload(profile, CreateTestDownloadRow()); | |
296 | |
297 // Start a finder and kill it before the search completes. | |
298 safe_browsing::LastDownloadFinder::Create( | |
299 base::Bind(&LastDownloadFinderTest::NeverCalled, base::Unretained(this))) | |
300 .reset(); | |
301 | |
302 // Flush tasks on the history backend thread. | |
303 FlushHistoryBackend(profile); | |
304 } | |
305 | |
306 // Tests that a download in profile added after the search is begun is found. | |
307 TEST_F(LastDownloadFinderTest, AddProfileAfterStarting) { | |
308 // Create a profile with a history service that is opted-in. | |
309 CreateProfile(SAFE_BROWSING_OPT_IN); | |
310 | |
311 scoped_ptr<safe_browsing::ClientIncidentReport_DownloadDetails> last_download; | |
312 base::RunLoop run_loop; | |
313 | |
314 // Post a task that will create a second profile once the main loop is run. | |
315 base::MessageLoop::current()->PostTask( | |
316 FROM_HERE, | |
317 base::Bind(&LastDownloadFinderTest::CreateProfileWithDownload, | |
318 base::Unretained(this))); | |
319 | |
320 // Create a finder that we expect will find a download in the second profile. | |
321 scoped_ptr<safe_browsing::LastDownloadFinder> finder( | |
322 safe_browsing::LastDownloadFinder::Create( | |
323 base::Bind(&LastDownloadFinderTest::OnLastDownload, | |
324 base::Unretained(this), | |
325 &last_download, | |
326 run_loop.QuitClosure()))); | |
327 | |
328 run_loop.Run(); | |
329 | |
330 ExpectFoundTestDownload(last_download.Pass()); | |
331 } | |
OLD | NEW |