Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc

Issue 663023007: Include high-fidelity metadata about a download in incident reports. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git/+/master
Patch Set: added DCHECK Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/incident_reporting/download_metadata_mana ger.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chrome/common/safe_browsing/csd.pb.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/download_manager.h"
20 #include "content/public/test/mock_download_item.h"
21 #include "content/public/test/mock_download_manager.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using ::testing::AllOf;
27 using ::testing::Eq;
28 using ::testing::IsNull;
29 using ::testing::Ne;
30 using ::testing::NiceMock;
31 using ::testing::NotNull;
32 using ::testing::ResultOf;
33 using ::testing::Return;
34 using ::testing::SaveArg;
35 using ::testing::StrEq;
36
37 namespace safe_browsing {
38
39 namespace {
40
41 const uint32_t kTestDownloadId = 47;
42 const uint32_t kOtherDownloadId = 48;
43 const uint32_t kCrazyDowloadId = 655;
44 const int64 kTestDownloadTimeMsec = 84;
45 const char kTestUrl[] = "http://test.test/foo";
46 const uint64_t kTestDownloadLength = 1000;
47 const double kTestDownloadEndTimeMs = 1413514824057;
48
49 // A utility class suitable for mocking that exposes a
50 // GetDownloadDetailsCallback.
51 class DownloadDetailsGetter {
52 public:
53 virtual ~DownloadDetailsGetter() {}
54 virtual void OnDownloadDetails(
55 ClientIncidentReport_DownloadDetails* details) = 0;
56 DownloadMetadataManager::GetDownloadDetailsCallback GetCallback() {
57 return base::Bind(&DownloadDetailsGetter::DownloadDetailsCallback,
58 base::Unretained(this));
59 }
60
61 private:
62 void DownloadDetailsCallback(
63 scoped_ptr<ClientIncidentReport_DownloadDetails> details) {
64 OnDownloadDetails(details.get());
65 }
66 };
67
68 // A mock DownloadDetailsGetter.
69 class MockDownloadDetailsGetter : public DownloadDetailsGetter {
70 public:
71 MOCK_METHOD1(OnDownloadDetails, void(ClientIncidentReport_DownloadDetails*));
72 };
73
74 // A mock DownloadMetadataManager that can be used to map a BrowserContext to
75 // a DownloadManager.
76 class MockDownloadMetadataManager : public DownloadMetadataManager {
77 public:
78 MockDownloadMetadataManager(
79 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
80 : DownloadMetadataManager(task_runner) {}
81
82 MOCK_METHOD1(GetDownloadManagerForBrowserContext,
83 content::DownloadManager*(content::BrowserContext*));
84 };
85
86 // A helper function that returns the download URL from a DownloadDetails.
87 const std::string& GetDetailsDownloadUrl(
88 const ClientIncidentReport_DownloadDetails* details) {
89 return details->download().url();
90 }
91
92 // A helper function that returns the open time from a DownloadDetails.
93 int64_t GetDetailsOpenTime(
94 const ClientIncidentReport_DownloadDetails* details) {
95 return details->open_time_msec();
96 }
97
98 } // namespace
99
100 // The basis upon which unit tests of the DownloadMetadataManager are built.
101 class DownloadMetadataManagerTestBase : public ::testing::Test {
102 protected:
103 // Sets up a DownloadMetadataManager that will run tasks on the main test
104 // thread.
105 DownloadMetadataManagerTestBase()
106 : manager_(scoped_refptr<base::SequencedTaskRunner>(
107 base::ThreadTaskRunnerHandle::Get())),
108 download_manager_(),
109 dm_observer_() {}
110
111 // Returns the path to the test profile's DownloadMetadata file.
112 base::FilePath GetMetadataPath() const {
113 return profile_.GetPath().Append(FILE_PATH_LITERAL("DownloadMetadata"));
114 }
115
116 // Returns a new ClientDownloadRequest for the given download URL.
117 static scoped_ptr<ClientDownloadRequest> MakeTestRequest(const char* url) {
118 scoped_ptr<ClientDownloadRequest> request(new ClientDownloadRequest());
119 request->set_url(url);
120 request->mutable_digests();
121 request->set_length(kTestDownloadLength);
122 return request.Pass();
123 }
124
125 // Returns a new DownloadMetdata for the given download id.
126 static scoped_ptr<DownloadMetadata> GetTestMetadata(uint32_t download_id) {
127 scoped_ptr<DownloadMetadata> metadata(new DownloadMetadata());
128 metadata->set_download_id(download_id);
129 ClientIncidentReport_DownloadDetails* details =
130 metadata->mutable_download();
131 details->set_download_time_msec(kTestDownloadTimeMsec);
132 details->set_allocated_download(MakeTestRequest(kTestUrl).release());
133 return metadata.Pass();
134 }
135
136 // Writes a test DownloadMetadata file for the given download id to the
137 // test profile directory.
138 void WriteTestMetadataFileForItem(uint32_t download_id) {
139 std::string data;
140 ASSERT_TRUE(GetTestMetadata(download_id)->SerializeToString(&data));
141 ASSERT_TRUE(base::WriteFile(GetMetadataPath(), data.data(), data.size()));
142 }
143
144 // Writes a test DownloadMetadata file for kTestDownloadId to the test profile
145 // directory.
146 void WriteTestMetadataFile() {
147 WriteTestMetadataFileForItem(kTestDownloadId);
148 }
149
150 // Returns the DownloadMetadata read from the test profile's directory.
151 scoped_ptr<DownloadMetadata> ReadTestMetadataFile() const {
152 std::string data;
153 if (!base::ReadFileToString(GetMetadataPath(), &data))
154 return scoped_ptr<DownloadMetadata>();
155 scoped_ptr<DownloadMetadata> result(new DownloadMetadata);
156 EXPECT_TRUE(result->ParseFromString(data));
157 return result.Pass();
158 }
159
160 // Runs all tasks posted to the test thread's message loop.
161 void RunAllTasks() { base::MessageLoop::current()->RunUntilIdle(); }
162
163 // Adds a DownloadManager for the test profile. The DownloadMetadataManager's
164 // observer is stashed for later use. Only call once per call to
165 // ShutdownDownloadManager.
166 void AddDownloadManager() {
167 ASSERT_EQ(nullptr, dm_observer_);
168 // Shove the manager into the browser context.
169 ON_CALL(download_manager_, GetBrowserContext())
170 .WillByDefault(Return(&profile_));
171 ON_CALL(manager_, GetDownloadManagerForBrowserContext(Eq(&profile_)))
172 .WillByDefault(Return(&download_manager_));
173 // Capture the metadata manager's observer on the download manager.
174 EXPECT_CALL(download_manager_, AddObserver(&manager_))
175 .WillOnce(SaveArg<0>(&dm_observer_));
176 manager_.AddDownloadManager(&download_manager_);
177 }
178
179 // Shuts down the DownloadManager. Safe to call any number of times.
180 void ShutdownDownloadManager() {
181 if (dm_observer_) {
182 dm_observer_->ManagerGoingDown(&download_manager_);
183 dm_observer_ = nullptr;
184 }
185 }
186
187 // Adds two test DownloadItems to the DownloadManager.
188 void AddDownloadItems() {
189 ASSERT_NE(nullptr, dm_observer_);
190 // Add the item under test.
191 test_item_.reset(new NiceMock<content::MockDownloadItem>);
192 ON_CALL(*test_item_, GetId())
193 .WillByDefault(Return(kTestDownloadId));
194 ON_CALL(*test_item_, GetBrowserContext())
195 .WillByDefault(Return(&profile_));
196 ON_CALL(*test_item_, GetEndTime())
197 .WillByDefault(Return(base::Time::FromJsTime(kTestDownloadEndTimeMs)));
198 dm_observer_->OnDownloadCreated(&download_manager_, test_item_.get());
199
200 // Add another item.
201 other_item_.reset(new NiceMock<content::MockDownloadItem>);
202 ON_CALL(*other_item_, GetId())
203 .WillByDefault(Return(kOtherDownloadId));
204 ON_CALL(*other_item_, GetBrowserContext())
205 .WillByDefault(Return(&profile_));
206 ON_CALL(*test_item_, GetEndTime())
207 .WillByDefault(Return(base::Time::FromJsTime(kTestDownloadEndTimeMs)));
208 dm_observer_->OnDownloadCreated(&download_manager_, other_item_.get());
209 }
210
211 // Destroyes the DownloadItems added to the manager. Safe to call any number
212 // of times.
213 void DestroyDownloadItems() {
214 other_item_.reset();
215 test_item_.reset();
216 }
217
218 content::TestBrowserThreadBundle thread_bundle_;
219 NiceMock<MockDownloadMetadataManager> manager_;
220 TestingProfile profile_;
221 NiceMock<content::MockDownloadManager> download_manager_;
222 scoped_ptr<content::MockDownloadItem> test_item_;
223 scoped_ptr<content::MockDownloadItem> other_item_;
224 content::DownloadManager::Observer* dm_observer_;
225 };
226
227 // A parameterized test that exercises GetDownloadDetails. The parameters
228 // dictate the exact state of affairs leading up to the call as follows:
229 // 0: if "present", the profile has a pre-existing DownloadMetadata file.
230 // 1: if "managed", the profile's DownloadManager has been created.
231 // 2: the state of the DownloadItem prior to the call:
232 // "not_created": the DownloadItem has not been created.
233 // "created": the DownloadItem has been created.
234 // "opened": the DownloadItem has been opened.
235 // "removed": the DownloadItem has been removed.
236 // 3: if "loaded", the task to load the DownloadMetadata file is allowed to
237 // complete.
238 // 4: if "early_shutdown", the DownloadManager is shut down before the callback
239 // is allowed to complete.
240 class GetDetailsTest
241 : public DownloadMetadataManagerTestBase,
242 public ::testing::WithParamInterface<testing::tuple<const char*,
243 const char*,
244 const char*,
245 const char*,
246 const char*>> {
247 protected:
248 enum DownloadItemAction {
249 NOT_CREATED,
250 CREATED,
251 OPENED,
252 REMOVED,
253 };
254 GetDetailsTest()
255 : metadata_file_present_(),
256 manager_added_(),
257 item_action_(NOT_CREATED),
258 details_loaded_(),
259 early_shutdown_() {}
260
261 void SetUp() override {
262 DownloadMetadataManagerTestBase::SetUp();
263 metadata_file_present_ =
264 (std::string(testing::get<0>(GetParam())) == "present");
265 manager_added_ = (std::string(testing::get<1>(GetParam())) == "managed");
266 const std::string item_action(testing::get<2>(GetParam()));
267 item_action_ = (item_action == "not_created" ? NOT_CREATED :
268 (item_action == "created" ? CREATED :
269 (item_action == "opened" ? OPENED : REMOVED)));
270 details_loaded_ = (std::string(testing::get<3>(GetParam())) == "loaded");
271 early_shutdown_ =
272 (std::string(testing::get<4>(GetParam())) == "early_shutdown");
273
274 // Fixup combinations that don't make sense.
275 if (!manager_added_)
276 item_action_ = NOT_CREATED;
277 }
278
279 bool metadata_file_present_;
280 bool manager_added_;
281 DownloadItemAction item_action_;
282 bool details_loaded_;
283 bool early_shutdown_;
284 };
285
286 // Tests that DownloadMetadataManager::GetDownloadDetails works for all
287 // combinations of states.
288 TEST_P(GetDetailsTest, GetDownloadDetails) {
289 // Optionally put a metadata file in the profile directory.
290 if (metadata_file_present_)
291 WriteTestMetadataFile();
292
293 // Optionally add a download manager for the profile.
294 if (manager_added_)
295 AddDownloadManager();
296
297 // Optionally create download items and perform actions on the one under test.
298 if (item_action_ != NOT_CREATED)
299 AddDownloadItems();
300 if (item_action_ == OPENED)
301 test_item_->NotifyObserversDownloadOpened();
302 else if (item_action_ == REMOVED)
303 test_item_->NotifyObserversDownloadRemoved();
304
305 // Optionally allow the task to read the file to complete.
306 if (details_loaded_)
307 RunAllTasks();
308
309 MockDownloadDetailsGetter details_getter;
310 if (metadata_file_present_ && item_action_ != REMOVED) {
311 // The file is present, so expect that the callback is invoked with the
312 // details of the test download data written by WriteTestMetadataFile.
313 if (item_action_ == OPENED) {
314 EXPECT_CALL(details_getter,
315 OnDownloadDetails(
316 AllOf(ResultOf(GetDetailsDownloadUrl, StrEq(kTestUrl)),
317 ResultOf(GetDetailsOpenTime, Ne(0)))));
318 } else {
319 EXPECT_CALL(details_getter,
320 OnDownloadDetails(
321 AllOf(ResultOf(GetDetailsDownloadUrl, StrEq(kTestUrl)),
322 ResultOf(GetDetailsOpenTime, Eq(0)))));
323 }
324 } else {
325 // No file on disk, so expect that the callback is invoked with null.
326 EXPECT_CALL(details_getter, OnDownloadDetails(IsNull()));
327 }
328
329 // Fire in the hole!
330 manager_.GetDownloadDetails(&profile_, details_getter.GetCallback());
331
332 // Destroy download items and shutdown the download manager, if relevant.
333 if (early_shutdown_) {
334 DestroyDownloadItems();
335 ShutdownDownloadManager();
336 }
337
338 // Allow the read task and the response callback to run.
339 RunAllTasks();
340
341 // Destroy download items and shutdown the download manager, if relevant.
342 DestroyDownloadItems();
343 ShutdownDownloadManager();
344 }
345
346 INSTANTIATE_TEST_CASE_P(
347 DownloadMetadataManager,
348 GetDetailsTest,
349 testing::Combine(
350 testing::Values("absent", "present"),
351 testing::Values("not_managed", "managed"),
352 testing::Values("not_created", "created", "opened", "removed"),
353 testing::Values("waiting", "loaded"),
354 testing::Values("normal_shutdown", "early_shutdown")));
355
356 // A parameterized test that exercises SetRequest. The parameters dictate the
357 // exact state of affairs leading up to the call as follows:
358 // 0: the state of the DownloadMetadata file for the test profile:
359 // "absent": no file is present.
360 // "this": the file corresponds to the item being updated.
361 // "other": the file correponds to a different item.
362 // "unknown": the file corresponds to an item that has not been created.
363 // 1: if "pending", an operation is applied to the item being updated prior to
364 // the call.
365 // 2: if "pending", an operation is applied to a different item prior to the
366 // call.
367 // 3: if "loaded", the task to load the DownloadMetadata file is allowed to
368 // complete.
369 // 4: if "set", the call to SetRequest contains a new request; otherwise it
370 // does not, leading to removal of metadata.
371 class SetRequestTest
372 : public DownloadMetadataManagerTestBase,
373 public ::testing::WithParamInterface<testing::tuple<const char*,
374 const char*,
375 const char*,
376 const char*,
377 const char*>> {
378 protected:
379 enum MetadataFilePresent {
380 ABSENT,
381 PRESENT_FOR_THIS_ITEM,
382 PRESENT_FOR_OTHER_ITEM,
383 PRESENT_FOR_UNKNOWN_ITEM,
384 };
385 SetRequestTest()
386 : metadata_file_present_(ABSENT),
387 same_ops_(),
388 other_ops_(),
389 details_loaded_(),
390 set_request_() {}
391
392 void SetUp() override {
393 DownloadMetadataManagerTestBase::SetUp();
394 const std::string present(testing::get<0>(GetParam()));
395 metadata_file_present_ = (present == "absent" ? ABSENT :
396 (present == "this" ? PRESENT_FOR_THIS_ITEM :
397 (present == "other" ? PRESENT_FOR_OTHER_ITEM :
398 PRESENT_FOR_UNKNOWN_ITEM)));
399 same_ops_ = (std::string(testing::get<1>(GetParam())) == "pending");
400 other_ops_ = (std::string(testing::get<2>(GetParam())) == "pending");
401 details_loaded_ = (std::string(testing::get<3>(GetParam())) == "loaded");
402 set_request_ = (std::string(testing::get<4>(GetParam())) == "set");
403 }
404
405 MetadataFilePresent metadata_file_present_;
406 bool same_ops_;
407 bool other_ops_;
408 bool details_loaded_;
409 bool set_request_;
410 };
411
412 // Tests that DownloadMetadataManager::SetRequest works for all combinations of
413 // states.
414 TEST_P(SetRequestTest, SetRequest) {
415 // Optionally put a metadata file in the profile directory.
416 switch (metadata_file_present_) {
417 case ABSENT:
418 break;
419 case PRESENT_FOR_THIS_ITEM:
420 WriteTestMetadataFile();
421 break;
422 case PRESENT_FOR_OTHER_ITEM:
423 WriteTestMetadataFileForItem(kOtherDownloadId);
424 break;
425 case PRESENT_FOR_UNKNOWN_ITEM:
426 WriteTestMetadataFileForItem(kCrazyDowloadId);
427 break;
428 }
429
430 AddDownloadManager();
431 AddDownloadItems();
432
433 // Optionally allow the task to read the file to complete.
434 if (details_loaded_) {
435 RunAllTasks();
436 } else {
437 // Optionally add pending operations if the load is outstanding.
438 if (same_ops_)
439 test_item_->NotifyObserversDownloadOpened();
440 if (other_ops_)
441 other_item_->NotifyObserversDownloadOpened();
442 }
443
444 static const char kNewUrl[] = "http://blorf";
445 scoped_ptr<ClientDownloadRequest> request;
446 if (set_request_)
447 request = MakeTestRequest(kNewUrl).Pass();
448 else
449 request.reset();
450 manager_.SetRequest(test_item_.get(), request.get());
451
452 // Allow the write or remove task to run.
453 RunAllTasks();
454
455 MockDownloadDetailsGetter details_getter;
456 if (set_request_) {
457 // Expect that the callback is invoked with details for this item.
458 EXPECT_CALL(
459 details_getter,
460 OnDownloadDetails(ResultOf(GetDetailsDownloadUrl, StrEq(kNewUrl))));
461 } else {
462 // Expect that the callback is invoked with null to clear stale metadata.
463 EXPECT_CALL(details_getter, OnDownloadDetails(IsNull()));
464 }
465 manager_.GetDownloadDetails(&profile_, details_getter.GetCallback());
466
467 DestroyDownloadItems();
468 ShutdownDownloadManager();
469
470 scoped_ptr<DownloadMetadata> metadata(ReadTestMetadataFile());
471 if (set_request_) {
472 // Expect that the file contains metadata for the download.
473 ASSERT_TRUE(metadata);
474 EXPECT_EQ(kTestDownloadId, metadata->download_id());
475 EXPECT_STREQ(kNewUrl, metadata->download().download().url().c_str());
476 } else {
477 // Expect that the file is not present.
478 ASSERT_FALSE(metadata);
479 }
480 }
481
482 INSTANTIATE_TEST_CASE_P(
483 DownloadMetadataManager,
484 SetRequestTest,
485 testing::Combine(testing::Values("absent", "this", "other", "unknown"),
486 testing::Values("none", "pending"),
487 testing::Values("none", "pending"),
488 testing::Values("waiting", "loaded"),
489 testing::Values("clear", "set")));
490
491 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698