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"
7 #include "base/file_util.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/run_loop.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "chrome/browser/safe_browsing/download_feedback.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/test/mock_download_item.h"
14 #include "content/public/test/test_browser_thread_bundle.h"
15 #include "net/url_request/url_request_test_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 using ::testing::Return
;
21 using ::testing::SaveArg
;
23 namespace safe_browsing
{
27 class FakeDownloadFeedback
: public DownloadFeedback
{
29 FakeDownloadFeedback(net::URLRequestContextGetter
* request_context_getter
,
30 base::TaskRunner
* file_task_runner
,
31 const base::FilePath
& file_path
,
32 const std::string
& ping_request
,
33 const std::string
& ping_response
,
34 base::Closure deletion_callback
)
35 : ping_request_(ping_request
),
36 ping_response_(ping_response
),
37 deletion_callback_(deletion_callback
),
38 start_called_(false) {
41 virtual ~FakeDownloadFeedback() {
42 deletion_callback_
.Run();
45 virtual void Start(const base::Closure
& finish_callback
) OVERRIDE
{
47 finish_callback_
= finish_callback
;
50 virtual const std::string
& GetPingRequestForTesting() const OVERRIDE
{
54 virtual const std::string
& GetPingResponseForTesting() const OVERRIDE
{
55 return ping_response_
;
58 base::Closure
finish_callback() const {
59 return finish_callback_
;
62 bool start_called() const {
67 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
68 scoped_refptr
<base::TaskRunner
> file_task_runner_
;
69 base::FilePath file_path_
;
70 std::string ping_request_
;
71 std::string ping_response_
;
73 base::Closure finish_callback_
;
74 base::Closure deletion_callback_
;
78 class FakeDownloadFeedbackFactory
: public DownloadFeedbackFactory
{
80 virtual ~FakeDownloadFeedbackFactory() {}
82 virtual DownloadFeedback
* CreateDownloadFeedback(
83 net::URLRequestContextGetter
* request_context_getter
,
84 base::TaskRunner
* file_task_runner
,
85 const base::FilePath
& file_path
,
86 const std::string
& ping_request
,
87 const std::string
& ping_response
) OVERRIDE
{
88 FakeDownloadFeedback
* feedback
= new FakeDownloadFeedback(
89 request_context_getter
,
94 base::Bind(&FakeDownloadFeedbackFactory::DownloadFeedbackDeleted
,
95 base::Unretained(this),
97 feedbacks_
.push_back(feedback
);
101 void DownloadFeedbackDeleted(size_t n
) {
102 feedbacks_
[n
] = NULL
;
105 FakeDownloadFeedback
* feedback(size_t n
) const {
106 return feedbacks_
[n
];
109 size_t num_feedbacks() const {
110 return feedbacks_
.size();
114 std::vector
<FakeDownloadFeedback
*> feedbacks_
;
117 bool WillStorePings(DownloadProtectionService::DownloadCheckResult result
,
119 content::MockDownloadItem item
;
120 EXPECT_CALL(item
, GetReceivedBytes()).WillRepeatedly(Return(size
));
122 EXPECT_FALSE(DownloadFeedbackService::IsEnabledForDownload(item
));
123 DownloadFeedbackService::MaybeStorePingsForDownload(result
, &item
, "a", "b");
124 return DownloadFeedbackService::IsEnabledForDownload(item
);
129 class DownloadFeedbackServiceTest
: public testing::Test
{
131 DownloadFeedbackServiceTest()
132 : file_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
133 content::BrowserThread::FILE)),
134 io_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
135 content::BrowserThread::IO
)),
136 request_context_getter_(
137 new net::TestURLRequestContextGetter(io_task_runner_
)) {
140 virtual void SetUp() OVERRIDE
{
141 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
142 DownloadFeedback::RegisterFactory(&download_feedback_factory_
);
145 virtual void TearDown() OVERRIDE
{
146 DownloadFeedback::RegisterFactory(NULL
);
149 base::FilePath
CreateTestFile(int n
) const {
150 base::FilePath
upload_file_path(
151 temp_dir_
.path().AppendASCII("test file " + base::IntToString(n
)));
152 const std::string upload_file_data
= "data";
153 int wrote
= file_util::WriteFile(
154 upload_file_path
, upload_file_data
.data(), upload_file_data
.size());
155 EXPECT_EQ(static_cast<int>(upload_file_data
.size()), wrote
);
156 return upload_file_path
;
159 FakeDownloadFeedback
* feedback(size_t n
) const {
160 return download_feedback_factory_
.feedback(n
);
163 size_t num_feedbacks() const {
164 return download_feedback_factory_
.num_feedbacks();
167 base::ScopedTempDir temp_dir_
;
168 content::TestBrowserThreadBundle thread_bundle_
;
169 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
170 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
171 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
172 FakeDownloadFeedbackFactory download_feedback_factory_
;
176 TEST_F(DownloadFeedbackServiceTest
, MaybeStorePingsForDownload
) {
177 const int64 ok_size
= DownloadFeedback::kMaxUploadSize
;
178 const int64 bad_size
= DownloadFeedback::kMaxUploadSize
+ 1;
180 EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE
, ok_size
));
181 EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS
, ok_size
));
182 EXPECT_TRUE(WillStorePings(DownloadProtectionService::UNCOMMON
, ok_size
));
184 WillStorePings(DownloadProtectionService::DANGEROUS_HOST
, ok_size
));
186 EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE
, bad_size
));
187 EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS
, bad_size
));
188 EXPECT_FALSE(WillStorePings(DownloadProtectionService::UNCOMMON
, bad_size
));
190 WillStorePings(DownloadProtectionService::DANGEROUS_HOST
, bad_size
));
193 TEST_F(DownloadFeedbackServiceTest
, SingleFeedbackComplete
) {
194 const base::FilePath
file_path(CreateTestFile(0));
195 const std::string ping_request
= "ping";
196 const std::string ping_response
= "resp";
198 content::DownloadItem::AcquireFileCallback download_discarded_callback
;
200 content::MockDownloadItem item
;
201 EXPECT_CALL(item
, GetDangerType())
202 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
203 EXPECT_CALL(item
, GetReceivedBytes()).WillRepeatedly(Return(1000));
204 EXPECT_CALL(item
, StealDangerousDownload(_
))
205 .WillOnce(SaveArg
<0>(&download_discarded_callback
));
207 DownloadFeedbackService
service(request_context_getter_
.get(),
208 file_task_runner_
.get());
209 service
.MaybeStorePingsForDownload(
210 DownloadProtectionService::UNCOMMON
, &item
, ping_request
, ping_response
);
211 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
));
212 service
.BeginFeedbackForDownload(&item
);
213 ASSERT_FALSE(download_discarded_callback
.is_null());
214 EXPECT_EQ(0U, num_feedbacks());
216 download_discarded_callback
.Run(file_path
);
217 ASSERT_EQ(1U, num_feedbacks());
218 ASSERT_TRUE(feedback(0));
219 EXPECT_TRUE(feedback(0)->start_called());
220 EXPECT_EQ(ping_request
, feedback(0)->GetPingRequestForTesting());
221 EXPECT_EQ(ping_response
, feedback(0)->GetPingResponseForTesting());
223 feedback(0)->finish_callback().Run();
224 EXPECT_FALSE(feedback(0));
226 // File should still exist since our FakeDownloadFeedback does not delete it.
227 base::RunLoop().RunUntilIdle();
228 EXPECT_TRUE(base::PathExists(file_path
));
231 TEST_F(DownloadFeedbackServiceTest
, MultiplePendingFeedbackComplete
) {
232 const std::string ping_request
= "ping";
233 const std::string ping_response
= "resp";
234 const size_t num_downloads
= 3;
236 content::DownloadItem::AcquireFileCallback
237 download_discarded_callback
[num_downloads
];
239 base::FilePath file_path
[num_downloads
];
240 content::MockDownloadItem item
[num_downloads
];
241 for (size_t i
= 0; i
< num_downloads
; ++i
) {
242 file_path
[i
] = CreateTestFile(i
);
243 EXPECT_CALL(item
[i
], GetDangerType())
244 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
245 EXPECT_CALL(item
[i
], GetReceivedBytes()).WillRepeatedly(Return(1000));
246 EXPECT_CALL(item
[i
], StealDangerousDownload(_
))
247 .WillOnce(SaveArg
<0>(&download_discarded_callback
[i
]));
248 DownloadFeedbackService::MaybeStorePingsForDownload(
249 DownloadProtectionService::UNCOMMON
, &item
[i
], ping_request
,
251 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
[i
]));
255 DownloadFeedbackService
service(request_context_getter_
.get(),
256 file_task_runner_
.get());
257 for (size_t i
= 0; i
< num_downloads
; ++i
) {
259 service
.BeginFeedbackForDownload(&item
[i
]);
260 ASSERT_FALSE(download_discarded_callback
[i
].is_null());
262 EXPECT_EQ(0U, num_feedbacks());
264 for (size_t i
= 0; i
< num_downloads
; ++i
) {
265 download_discarded_callback
[i
].Run(file_path
[i
]);
268 ASSERT_EQ(3U, num_feedbacks());
269 EXPECT_TRUE(feedback(0)->start_called());
270 EXPECT_FALSE(feedback(1)->start_called());
271 EXPECT_FALSE(feedback(2)->start_called());
273 feedback(0)->finish_callback().Run();
275 EXPECT_FALSE(feedback(0));
276 EXPECT_TRUE(feedback(1)->start_called());
277 EXPECT_FALSE(feedback(2)->start_called());
279 feedback(1)->finish_callback().Run();
281 EXPECT_FALSE(feedback(0));
282 EXPECT_FALSE(feedback(1));
283 EXPECT_TRUE(feedback(2)->start_called());
285 feedback(2)->finish_callback().Run();
287 EXPECT_FALSE(feedback(0));
288 EXPECT_FALSE(feedback(1));
289 EXPECT_FALSE(feedback(2));
292 base::RunLoop().RunUntilIdle();
293 // These files should still exist since the FakeDownloadFeedback does not
295 EXPECT_TRUE(base::PathExists(file_path
[0]));
296 EXPECT_TRUE(base::PathExists(file_path
[1]));
297 EXPECT_TRUE(base::PathExists(file_path
[2]));
300 TEST_F(DownloadFeedbackServiceTest
, MultiFeedbackWithIncomplete
) {
301 const std::string ping_request
= "ping";
302 const std::string ping_response
= "resp";
303 const size_t num_downloads
= 3;
305 content::DownloadItem::AcquireFileCallback
306 download_discarded_callback
[num_downloads
];
308 base::FilePath file_path
[num_downloads
];
309 content::MockDownloadItem item
[num_downloads
];
310 for (size_t i
= 0; i
< num_downloads
; ++i
) {
311 file_path
[i
] = CreateTestFile(i
);
312 EXPECT_CALL(item
[i
], GetDangerType())
313 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
));
314 EXPECT_CALL(item
[i
], GetReceivedBytes()).WillRepeatedly(Return(1000));
315 EXPECT_CALL(item
[i
], StealDangerousDownload(_
))
316 .WillOnce(SaveArg
<0>(&download_discarded_callback
[i
]));
317 DownloadFeedbackService::MaybeStorePingsForDownload(
318 DownloadProtectionService::UNCOMMON
, &item
[i
], ping_request
,
320 ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item
[i
]));
324 DownloadFeedbackService
service(request_context_getter_
.get(),
325 file_task_runner_
.get());
326 for (size_t i
= 0; i
< num_downloads
; ++i
) {
328 service
.BeginFeedbackForDownload(&item
[i
]);
329 ASSERT_FALSE(download_discarded_callback
[i
].is_null());
331 EXPECT_EQ(0U, num_feedbacks());
333 download_discarded_callback
[0].Run(file_path
[0]);
334 ASSERT_EQ(1U, num_feedbacks());
335 ASSERT_TRUE(feedback(0));
336 EXPECT_TRUE(feedback(0)->start_called());
338 download_discarded_callback
[1].Run(file_path
[1]);
339 ASSERT_EQ(2U, num_feedbacks());
340 ASSERT_TRUE(feedback(1));
341 EXPECT_FALSE(feedback(1)->start_called());
343 feedback(0)->finish_callback().Run();
344 EXPECT_FALSE(feedback(0));
345 EXPECT_TRUE(feedback(1)->start_called());
348 EXPECT_EQ(2U, num_feedbacks());
349 for (size_t i
= 0; i
< num_feedbacks(); ++i
) {
351 EXPECT_FALSE(feedback(i
));
354 // Running a download acquired callback after the DownloadFeedbackService is
355 // destroyed should delete the file.
356 download_discarded_callback
[2].Run(file_path
[2]);
357 EXPECT_EQ(2U, num_feedbacks());
359 // File should still exist since the FileUtilProxy task hasn't run yet.
360 EXPECT_TRUE(base::PathExists(file_path
[2]));
362 base::RunLoop().RunUntilIdle();
363 // File should be deleted since the AcquireFileCallback ran after the service
365 EXPECT_FALSE(base::PathExists(file_path
[2]));
367 // These files should still exist since the FakeDownloadFeedback does not
369 EXPECT_TRUE(base::PathExists(file_path
[0]));
370 EXPECT_TRUE(base::PathExists(file_path
[1]));
373 } // namespace safe_browsing