1 // Copyright 2013 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/safe_browsing/download_feedback_service.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/safe_browsing/download_feedback.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/test/mock_download_item.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "net/url_request/url_request_test_util.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 using ::testing::Return
;
23 using ::testing::SaveArg
;
25 namespace safe_browsing
{
29 class FakeDownloadFeedback
: public DownloadFeedback
{
31 FakeDownloadFeedback(net::URLRequestContextGetter
* request_context_getter
,
32 base::TaskRunner
* file_task_runner
,
33 const base::FilePath
& file_path
,
34 const std::string
& ping_request
,
35 const std::string
& ping_response
,
36 base::Closure deletion_callback
)
37 : ping_request_(ping_request
),
38 ping_response_(ping_response
),
39 deletion_callback_(deletion_callback
),
40 start_called_(false) {
43 virtual ~FakeDownloadFeedback() {
44 deletion_callback_
.Run();
47 virtual void Start(const base::Closure
& finish_callback
) override
{
49 finish_callback_
= finish_callback
;
52 virtual const std::string
& GetPingRequestForTesting() const override
{
56 virtual const std::string
& GetPingResponseForTesting() const override
{
57 return ping_response_
;
60 base::Closure
finish_callback() const {
61 return finish_callback_
;
64 bool start_called() const {
69 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
70 scoped_refptr
<base::TaskRunner
> file_task_runner_
;
71 base::FilePath file_path_
;
72 std::string ping_request_
;
73 std::string ping_response_
;
75 base::Closure finish_callback_
;
76 base::Closure deletion_callback_
;
80 class FakeDownloadFeedbackFactory
: public DownloadFeedbackFactory
{
82 virtual ~FakeDownloadFeedbackFactory() {}
84 virtual DownloadFeedback
* CreateDownloadFeedback(
85 net::URLRequestContextGetter
* request_context_getter
,
86 base::TaskRunner
* file_task_runner
,
87 const base::FilePath
& file_path
,
88 const std::string
& ping_request
,
89 const std::string
& ping_response
) override
{
90 FakeDownloadFeedback
* feedback
= new FakeDownloadFeedback(
91 request_context_getter
,
96 base::Bind(&FakeDownloadFeedbackFactory::DownloadFeedbackDeleted
,
97 base::Unretained(this),
99 feedbacks_
.push_back(feedback
);
103 void DownloadFeedbackDeleted(size_t n
) {
104 feedbacks_
[n
] = NULL
;
107 FakeDownloadFeedback
* feedback(size_t n
) const {
108 return feedbacks_
[n
];
111 size_t num_feedbacks() const {
112 return feedbacks_
.size();
116 std::vector
<FakeDownloadFeedback
*> feedbacks_
;
119 bool WillStorePings(DownloadProtectionService::DownloadCheckResult result
,
121 content::MockDownloadItem item
;
122 EXPECT_CALL(item
, GetReceivedBytes()).WillRepeatedly(Return(size
));
124 EXPECT_FALSE(DownloadFeedbackService::IsEnabledForDownload(item
));
125 DownloadFeedbackService::MaybeStorePingsForDownload(result
, &item
, "a", "b");
126 return DownloadFeedbackService::IsEnabledForDownload(item
);
131 class DownloadFeedbackServiceTest
: public testing::Test
{
133 DownloadFeedbackServiceTest()
134 : file_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
135 content::BrowserThread::FILE)),
136 io_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
137 content::BrowserThread::IO
)),
138 request_context_getter_(
139 new net::TestURLRequestContextGetter(io_task_runner_
)) {
142 virtual void SetUp() override
{
143 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
144 DownloadFeedback::RegisterFactory(&download_feedback_factory_
);
147 virtual void TearDown() override
{
148 DownloadFeedback::RegisterFactory(NULL
);
151 base::FilePath
CreateTestFile(int n
) const {
152 base::FilePath
upload_file_path(
153 temp_dir_
.path().AppendASCII("test file " + base::IntToString(n
)));
154 const std::string upload_file_data
= "data";
155 int wrote
= base::WriteFile(
156 upload_file_path
, upload_file_data
.data(), upload_file_data
.size());
157 EXPECT_EQ(static_cast<int>(upload_file_data
.size()), wrote
);
158 return upload_file_path
;
161 FakeDownloadFeedback
* feedback(size_t n
) const {
162 return download_feedback_factory_
.feedback(n
);
165 size_t num_feedbacks() const {
166 return download_feedback_factory_
.num_feedbacks();
170 base::ScopedTempDir temp_dir_
;
171 content::TestBrowserThreadBundle thread_bundle_
;
172 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
173 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
174 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
175 FakeDownloadFeedbackFactory download_feedback_factory_
;
178 TEST_F(DownloadFeedbackServiceTest
, MaybeStorePingsForDownload
) {
179 const int64 ok_size
= DownloadFeedback::kMaxUploadSize
;
180 const int64 bad_size
= DownloadFeedback::kMaxUploadSize
+ 1;
182 EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE
, ok_size
));
183 EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS
, ok_size
));
184 EXPECT_TRUE(WillStorePings(DownloadProtectionService::UNCOMMON
, ok_size
));
186 WillStorePings(DownloadProtectionService::DANGEROUS_HOST
, ok_size
));
188 EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE
, bad_size
));
189 EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS
, bad_size
));
190 EXPECT_FALSE(WillStorePings(DownloadProtectionService::UNCOMMON
, bad_size
));
192 WillStorePings(DownloadProtectionService::DANGEROUS_HOST
, bad_size
));
195 TEST_F(DownloadFeedbackServiceTest
, SingleFeedbackComplete
) {
196 const base::FilePath
file_path(CreateTestFile(0));
197 const std::string ping_request
= "ping";
198 const std::string ping_response
= "resp";
200 content::DownloadItem::AcquireFileCallback download_discarded_callback
;
202 content::MockDownloadItem item
;
203 EXPECT_CALL(item
, GetDangerType())
204 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
205 EXPECT_CALL(item
, GetReceivedBytes()).WillRepeatedly(Return(1000));
206 EXPECT_CALL(item
, StealDangerousDownload(_
))
207 .WillOnce(SaveArg
<0>(&download_discarded_callback
));
209 DownloadFeedbackService
service(request_context_getter_
.get(),
210 file_task_runner_
.get());
211 service
.MaybeStorePingsForDownload(
212 DownloadProtectionService::UNCOMMON
, &item
, ping_request
, ping_response
);
213 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
));
214 service
.BeginFeedbackForDownload(&item
);
215 ASSERT_FALSE(download_discarded_callback
.is_null());
216 EXPECT_EQ(0U, num_feedbacks());
218 download_discarded_callback
.Run(file_path
);
219 ASSERT_EQ(1U, num_feedbacks());
220 ASSERT_TRUE(feedback(0));
221 EXPECT_TRUE(feedback(0)->start_called());
222 EXPECT_EQ(ping_request
, feedback(0)->GetPingRequestForTesting());
223 EXPECT_EQ(ping_response
, feedback(0)->GetPingResponseForTesting());
225 feedback(0)->finish_callback().Run();
226 EXPECT_FALSE(feedback(0));
228 // File should still exist since our FakeDownloadFeedback does not delete it.
229 base::RunLoop().RunUntilIdle();
230 EXPECT_TRUE(base::PathExists(file_path
));
233 TEST_F(DownloadFeedbackServiceTest
, MultiplePendingFeedbackComplete
) {
234 const std::string ping_request
= "ping";
235 const std::string ping_response
= "resp";
236 const size_t num_downloads
= 3;
238 content::DownloadItem::AcquireFileCallback
239 download_discarded_callback
[num_downloads
];
241 base::FilePath file_path
[num_downloads
];
242 content::MockDownloadItem item
[num_downloads
];
243 for (size_t i
= 0; i
< num_downloads
; ++i
) {
244 file_path
[i
] = CreateTestFile(i
);
245 EXPECT_CALL(item
[i
], GetDangerType())
246 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
247 EXPECT_CALL(item
[i
], GetReceivedBytes()).WillRepeatedly(Return(1000));
248 EXPECT_CALL(item
[i
], StealDangerousDownload(_
))
249 .WillOnce(SaveArg
<0>(&download_discarded_callback
[i
]));
250 DownloadFeedbackService::MaybeStorePingsForDownload(
251 DownloadProtectionService::UNCOMMON
, &item
[i
], ping_request
,
253 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
[i
]));
257 DownloadFeedbackService
service(request_context_getter_
.get(),
258 file_task_runner_
.get());
259 for (size_t i
= 0; i
< num_downloads
; ++i
) {
261 service
.BeginFeedbackForDownload(&item
[i
]);
262 ASSERT_FALSE(download_discarded_callback
[i
].is_null());
264 EXPECT_EQ(0U, num_feedbacks());
266 for (size_t i
= 0; i
< num_downloads
; ++i
) {
267 download_discarded_callback
[i
].Run(file_path
[i
]);
270 ASSERT_EQ(3U, num_feedbacks());
271 EXPECT_TRUE(feedback(0)->start_called());
272 EXPECT_FALSE(feedback(1)->start_called());
273 EXPECT_FALSE(feedback(2)->start_called());
275 feedback(0)->finish_callback().Run();
277 EXPECT_FALSE(feedback(0));
278 EXPECT_TRUE(feedback(1)->start_called());
279 EXPECT_FALSE(feedback(2)->start_called());
281 feedback(1)->finish_callback().Run();
283 EXPECT_FALSE(feedback(0));
284 EXPECT_FALSE(feedback(1));
285 EXPECT_TRUE(feedback(2)->start_called());
287 feedback(2)->finish_callback().Run();
289 EXPECT_FALSE(feedback(0));
290 EXPECT_FALSE(feedback(1));
291 EXPECT_FALSE(feedback(2));
294 base::RunLoop().RunUntilIdle();
295 // These files should still exist since the FakeDownloadFeedback does not
297 EXPECT_TRUE(base::PathExists(file_path
[0]));
298 EXPECT_TRUE(base::PathExists(file_path
[1]));
299 EXPECT_TRUE(base::PathExists(file_path
[2]));
302 TEST_F(DownloadFeedbackServiceTest
, MultiFeedbackWithIncomplete
) {
303 const std::string ping_request
= "ping";
304 const std::string ping_response
= "resp";
305 const size_t num_downloads
= 3;
307 content::DownloadItem::AcquireFileCallback
308 download_discarded_callback
[num_downloads
];
310 base::FilePath file_path
[num_downloads
];
311 content::MockDownloadItem item
[num_downloads
];
312 for (size_t i
= 0; i
< num_downloads
; ++i
) {
313 file_path
[i
] = CreateTestFile(i
);
314 EXPECT_CALL(item
[i
], GetDangerType())
315 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
316 EXPECT_CALL(item
[i
], GetReceivedBytes()).WillRepeatedly(Return(1000));
317 EXPECT_CALL(item
[i
], StealDangerousDownload(_
))
318 .WillOnce(SaveArg
<0>(&download_discarded_callback
[i
]));
319 DownloadFeedbackService::MaybeStorePingsForDownload(
320 DownloadProtectionService::UNCOMMON
, &item
[i
], ping_request
,
322 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
[i
]));
326 DownloadFeedbackService
service(request_context_getter_
.get(),
327 file_task_runner_
.get());
328 for (size_t i
= 0; i
< num_downloads
; ++i
) {
330 service
.BeginFeedbackForDownload(&item
[i
]);
331 ASSERT_FALSE(download_discarded_callback
[i
].is_null());
333 EXPECT_EQ(0U, num_feedbacks());
335 download_discarded_callback
[0].Run(file_path
[0]);
336 ASSERT_EQ(1U, num_feedbacks());
337 ASSERT_TRUE(feedback(0));
338 EXPECT_TRUE(feedback(0)->start_called());
340 download_discarded_callback
[1].Run(file_path
[1]);
341 ASSERT_EQ(2U, num_feedbacks());
342 ASSERT_TRUE(feedback(1));
343 EXPECT_FALSE(feedback(1)->start_called());
345 feedback(0)->finish_callback().Run();
346 EXPECT_FALSE(feedback(0));
347 EXPECT_TRUE(feedback(1)->start_called());
350 EXPECT_EQ(2U, num_feedbacks());
351 for (size_t i
= 0; i
< num_feedbacks(); ++i
) {
353 EXPECT_FALSE(feedback(i
));
356 // Running a download acquired callback after the DownloadFeedbackService is
357 // destroyed should delete the file.
358 download_discarded_callback
[2].Run(file_path
[2]);
359 EXPECT_EQ(2U, num_feedbacks());
361 // File should still exist since the FileUtilProxy task hasn't run yet.
362 EXPECT_TRUE(base::PathExists(file_path
[2]));
364 base::RunLoop().RunUntilIdle();
365 // File should be deleted since the AcquireFileCallback ran after the service
367 EXPECT_FALSE(base::PathExists(file_path
[2]));
369 // These files should still exist since the FakeDownloadFeedback does not
371 EXPECT_TRUE(base::PathExists(file_path
[0]));
372 EXPECT_TRUE(base::PathExists(file_path
[1]));
375 } // namespace safe_browsing