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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1539fe025d572886a7ea71240a6b5e8b6f849439
--- /dev/null
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
@@ -0,0 +1,491 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "chrome/common/safe_browsing/csd.pb.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/test/mock_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Ne;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::ResultOf;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::StrEq;
+
+namespace safe_browsing {
+
+namespace {
+
+const uint32_t kTestDownloadId = 47;
+const uint32_t kOtherDownloadId = 48;
+const uint32_t kCrazyDowloadId = 655;
+const int64 kTestDownloadTimeMsec = 84;
+const char kTestUrl[] = "http://test.test/foo";
+const uint64_t kTestDownloadLength = 1000;
+const double kTestDownloadEndTimeMs = 1413514824057;
+
+// A utility class suitable for mocking that exposes a
+// GetDownloadDetailsCallback.
+class DownloadDetailsGetter {
+ public:
+ virtual ~DownloadDetailsGetter() {}
+ virtual void OnDownloadDetails(
+ ClientIncidentReport_DownloadDetails* details) = 0;
+ DownloadMetadataManager::GetDownloadDetailsCallback GetCallback() {
+ return base::Bind(&DownloadDetailsGetter::DownloadDetailsCallback,
+ base::Unretained(this));
+ }
+
+ private:
+ void DownloadDetailsCallback(
+ scoped_ptr<ClientIncidentReport_DownloadDetails> details) {
+ OnDownloadDetails(details.get());
+ }
+};
+
+// A mock DownloadDetailsGetter.
+class MockDownloadDetailsGetter : public DownloadDetailsGetter {
+ public:
+ MOCK_METHOD1(OnDownloadDetails, void(ClientIncidentReport_DownloadDetails*));
+};
+
+// A mock DownloadMetadataManager that can be used to map a BrowserContext to
+// a DownloadManager.
+class MockDownloadMetadataManager : public DownloadMetadataManager {
+ public:
+ MockDownloadMetadataManager(
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+ : DownloadMetadataManager(task_runner) {}
+
+ MOCK_METHOD1(GetDownloadManagerForBrowserContext,
+ content::DownloadManager*(content::BrowserContext*));
+};
+
+// A helper function that returns the download URL from a DownloadDetails.
+const std::string& GetDetailsDownloadUrl(
+ const ClientIncidentReport_DownloadDetails* details) {
+ return details->download().url();
+}
+
+// A helper function that returns the open time from a DownloadDetails.
+int64_t GetDetailsOpenTime(
+ const ClientIncidentReport_DownloadDetails* details) {
+ return details->open_time_msec();
+}
+
+} // namespace
+
+// The basis upon which unit tests of the DownloadMetadataManager are built.
+class DownloadMetadataManagerTestBase : public ::testing::Test {
+ protected:
+ // Sets up a DownloadMetadataManager that will run tasks on the main test
+ // thread.
+ DownloadMetadataManagerTestBase()
+ : manager_(scoped_refptr<base::SequencedTaskRunner>(
+ base::ThreadTaskRunnerHandle::Get())),
+ download_manager_(),
+ dm_observer_() {}
+
+ // Returns the path to the test profile's DownloadMetadata file.
+ base::FilePath GetMetadataPath() const {
+ return profile_.GetPath().Append(FILE_PATH_LITERAL("DownloadMetadata"));
+ }
+
+ // Returns a new ClientDownloadRequest for the given download URL.
+ static scoped_ptr<ClientDownloadRequest> MakeTestRequest(const char* url) {
+ scoped_ptr<ClientDownloadRequest> request(new ClientDownloadRequest());
+ request->set_url(url);
+ request->mutable_digests();
+ request->set_length(kTestDownloadLength);
+ return request.Pass();
+ }
+
+ // Returns a new DownloadMetdata for the given download id.
+ static scoped_ptr<DownloadMetadata> GetTestMetadata(uint32_t download_id) {
+ scoped_ptr<DownloadMetadata> metadata(new DownloadMetadata());
+ metadata->set_download_id(download_id);
+ ClientIncidentReport_DownloadDetails* details =
+ metadata->mutable_download();
+ details->set_download_time_msec(kTestDownloadTimeMsec);
+ details->set_allocated_download(MakeTestRequest(kTestUrl).release());
+ return metadata.Pass();
+ }
+
+ // Writes a test DownloadMetadata file for the given download id to the
+ // test profile directory.
+ void WriteTestMetadataFileForItem(uint32_t download_id) {
+ std::string data;
+ ASSERT_TRUE(GetTestMetadata(download_id)->SerializeToString(&data));
+ ASSERT_TRUE(base::WriteFile(GetMetadataPath(), data.data(), data.size()));
+ }
+
+ // Writes a test DownloadMetadata file for kTestDownloadId to the test profile
+ // directory.
+ void WriteTestMetadataFile() {
+ WriteTestMetadataFileForItem(kTestDownloadId);
+ }
+
+ // Returns the DownloadMetadata read from the test profile's directory.
+ scoped_ptr<DownloadMetadata> ReadTestMetadataFile() const {
+ std::string data;
+ if (!base::ReadFileToString(GetMetadataPath(), &data))
+ return scoped_ptr<DownloadMetadata>();
+ scoped_ptr<DownloadMetadata> result(new DownloadMetadata);
+ EXPECT_TRUE(result->ParseFromString(data));
+ return result.Pass();
+ }
+
+ // Runs all tasks posted to the test thread's message loop.
+ void RunAllTasks() { base::MessageLoop::current()->RunUntilIdle(); }
+
+ // Adds a DownloadManager for the test profile. The DownloadMetadataManager's
+ // observer is stashed for later use. Only call once per call to
+ // ShutdownDownloadManager.
+ void AddDownloadManager() {
+ ASSERT_EQ(nullptr, dm_observer_);
+ // Shove the manager into the browser context.
+ ON_CALL(download_manager_, GetBrowserContext())
+ .WillByDefault(Return(&profile_));
+ ON_CALL(manager_, GetDownloadManagerForBrowserContext(Eq(&profile_)))
+ .WillByDefault(Return(&download_manager_));
+ // Capture the metadata manager's observer on the download manager.
+ EXPECT_CALL(download_manager_, AddObserver(&manager_))
+ .WillOnce(SaveArg<0>(&dm_observer_));
+ manager_.AddDownloadManager(&download_manager_);
+ }
+
+ // Shuts down the DownloadManager. Safe to call any number of times.
+ void ShutdownDownloadManager() {
+ if (dm_observer_) {
+ dm_observer_->ManagerGoingDown(&download_manager_);
+ dm_observer_ = nullptr;
+ }
+ }
+
+ // Adds two test DownloadItems to the DownloadManager.
+ void AddDownloadItems() {
+ ASSERT_NE(nullptr, dm_observer_);
+ // Add the item under test.
+ test_item_.reset(new NiceMock<content::MockDownloadItem>);
+ ON_CALL(*test_item_, GetId())
+ .WillByDefault(Return(kTestDownloadId));
+ ON_CALL(*test_item_, GetBrowserContext())
+ .WillByDefault(Return(&profile_));
+ ON_CALL(*test_item_, GetEndTime())
+ .WillByDefault(Return(base::Time::FromJsTime(kTestDownloadEndTimeMs)));
+ dm_observer_->OnDownloadCreated(&download_manager_, test_item_.get());
+
+ // Add another item.
+ other_item_.reset(new NiceMock<content::MockDownloadItem>);
+ ON_CALL(*other_item_, GetId())
+ .WillByDefault(Return(kOtherDownloadId));
+ ON_CALL(*other_item_, GetBrowserContext())
+ .WillByDefault(Return(&profile_));
+ ON_CALL(*test_item_, GetEndTime())
+ .WillByDefault(Return(base::Time::FromJsTime(kTestDownloadEndTimeMs)));
+ dm_observer_->OnDownloadCreated(&download_manager_, other_item_.get());
+ }
+
+ // Destroyes the DownloadItems added to the manager. Safe to call any number
+ // of times.
+ void DestroyDownloadItems() {
+ other_item_.reset();
+ test_item_.reset();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ NiceMock<MockDownloadMetadataManager> manager_;
+ TestingProfile profile_;
+ NiceMock<content::MockDownloadManager> download_manager_;
+ scoped_ptr<content::MockDownloadItem> test_item_;
+ scoped_ptr<content::MockDownloadItem> other_item_;
+ content::DownloadManager::Observer* dm_observer_;
+};
+
+// A parameterized test that exercises GetDownloadDetails. The parameters
+// dictate the exact state of affairs leading up to the call as follows:
+// 0: if "present", the profile has a pre-existing DownloadMetadata file.
+// 1: if "managed", the profile's DownloadManager has been created.
+// 2: the state of the DownloadItem prior to the call:
+// "not_created": the DownloadItem has not been created.
+// "created": the DownloadItem has been created.
+// "opened": the DownloadItem has been opened.
+// "removed": the DownloadItem has been removed.
+// 3: if "loaded", the task to load the DownloadMetadata file is allowed to
+// complete.
+// 4: if "early_shutdown", the DownloadManager is shut down before the callback
+// is allowed to complete.
+class GetDetailsTest
+ : public DownloadMetadataManagerTestBase,
+ public ::testing::WithParamInterface<testing::tuple<const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*>> {
+ protected:
+ enum DownloadItemAction {
+ NOT_CREATED,
+ CREATED,
+ OPENED,
+ REMOVED,
+ };
+ GetDetailsTest()
+ : metadata_file_present_(),
+ manager_added_(),
+ item_action_(NOT_CREATED),
+ details_loaded_(),
+ early_shutdown_() {}
+
+ void SetUp() override {
+ DownloadMetadataManagerTestBase::SetUp();
+ metadata_file_present_ =
+ (std::string(testing::get<0>(GetParam())) == "present");
+ manager_added_ = (std::string(testing::get<1>(GetParam())) == "managed");
+ const std::string item_action(testing::get<2>(GetParam()));
+ item_action_ = (item_action == "not_created" ? NOT_CREATED :
+ (item_action == "created" ? CREATED :
+ (item_action == "opened" ? OPENED : REMOVED)));
+ details_loaded_ = (std::string(testing::get<3>(GetParam())) == "loaded");
+ early_shutdown_ =
+ (std::string(testing::get<4>(GetParam())) == "early_shutdown");
+
+ // Fixup combinations that don't make sense.
+ if (!manager_added_)
+ item_action_ = NOT_CREATED;
+ }
+
+ bool metadata_file_present_;
+ bool manager_added_;
+ DownloadItemAction item_action_;
+ bool details_loaded_;
+ bool early_shutdown_;
+};
+
+// Tests that DownloadMetadataManager::GetDownloadDetails works for all
+// combinations of states.
+TEST_P(GetDetailsTest, GetDownloadDetails) {
+ // Optionally put a metadata file in the profile directory.
+ if (metadata_file_present_)
+ WriteTestMetadataFile();
+
+ // Optionally add a download manager for the profile.
+ if (manager_added_)
+ AddDownloadManager();
+
+ // Optionally create download items and perform actions on the one under test.
+ if (item_action_ != NOT_CREATED)
+ AddDownloadItems();
+ if (item_action_ == OPENED)
+ test_item_->NotifyObserversDownloadOpened();
+ else if (item_action_ == REMOVED)
+ test_item_->NotifyObserversDownloadRemoved();
+
+ // Optionally allow the task to read the file to complete.
+ if (details_loaded_)
+ RunAllTasks();
+
+ MockDownloadDetailsGetter details_getter;
+ if (metadata_file_present_ && item_action_ != REMOVED) {
+ // The file is present, so expect that the callback is invoked with the
+ // details of the test download data written by WriteTestMetadataFile.
+ if (item_action_ == OPENED) {
+ EXPECT_CALL(details_getter,
+ OnDownloadDetails(
+ AllOf(ResultOf(GetDetailsDownloadUrl, StrEq(kTestUrl)),
+ ResultOf(GetDetailsOpenTime, Ne(0)))));
+ } else {
+ EXPECT_CALL(details_getter,
+ OnDownloadDetails(
+ AllOf(ResultOf(GetDetailsDownloadUrl, StrEq(kTestUrl)),
+ ResultOf(GetDetailsOpenTime, Eq(0)))));
+ }
+ } else {
+ // No file on disk, so expect that the callback is invoked with null.
+ EXPECT_CALL(details_getter, OnDownloadDetails(IsNull()));
+ }
+
+ // Fire in the hole!
+ manager_.GetDownloadDetails(&profile_, details_getter.GetCallback());
+
+ // Destroy download items and shutdown the download manager, if relevant.
+ if (early_shutdown_) {
+ DestroyDownloadItems();
+ ShutdownDownloadManager();
+ }
+
+ // Allow the read task and the response callback to run.
+ RunAllTasks();
+
+ // Destroy download items and shutdown the download manager, if relevant.
+ DestroyDownloadItems();
+ ShutdownDownloadManager();
+}
+
+INSTANTIATE_TEST_CASE_P(
+ DownloadMetadataManager,
+ GetDetailsTest,
+ testing::Combine(
+ testing::Values("absent", "present"),
+ testing::Values("not_managed", "managed"),
+ testing::Values("not_created", "created", "opened", "removed"),
+ testing::Values("waiting", "loaded"),
+ testing::Values("normal_shutdown", "early_shutdown")));
+
+// A parameterized test that exercises SetRequest. The parameters dictate the
+// exact state of affairs leading up to the call as follows:
+// 0: the state of the DownloadMetadata file for the test profile:
+// "absent": no file is present.
+// "this": the file corresponds to the item being updated.
+// "other": the file correponds to a different item.
+// "unknown": the file corresponds to an item that has not been created.
+// 1: if "pending", an operation is applied to the item being updated prior to
+// the call.
+// 2: if "pending", an operation is applied to a different item prior to the
+// call.
+// 3: if "loaded", the task to load the DownloadMetadata file is allowed to
+// complete.
+// 4: if "set", the call to SetRequest contains a new request; otherwise it
+// does not, leading to removal of metadata.
+class SetRequestTest
+ : public DownloadMetadataManagerTestBase,
+ public ::testing::WithParamInterface<testing::tuple<const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*>> {
+ protected:
+ enum MetadataFilePresent {
+ ABSENT,
+ PRESENT_FOR_THIS_ITEM,
+ PRESENT_FOR_OTHER_ITEM,
+ PRESENT_FOR_UNKNOWN_ITEM,
+ };
+ SetRequestTest()
+ : metadata_file_present_(ABSENT),
+ same_ops_(),
+ other_ops_(),
+ details_loaded_(),
+ set_request_() {}
+
+ void SetUp() override {
+ DownloadMetadataManagerTestBase::SetUp();
+ const std::string present(testing::get<0>(GetParam()));
+ metadata_file_present_ = (present == "absent" ? ABSENT :
+ (present == "this" ? PRESENT_FOR_THIS_ITEM :
+ (present == "other" ? PRESENT_FOR_OTHER_ITEM :
+ PRESENT_FOR_UNKNOWN_ITEM)));
+ same_ops_ = (std::string(testing::get<1>(GetParam())) == "pending");
+ other_ops_ = (std::string(testing::get<2>(GetParam())) == "pending");
+ details_loaded_ = (std::string(testing::get<3>(GetParam())) == "loaded");
+ set_request_ = (std::string(testing::get<4>(GetParam())) == "set");
+ }
+
+ MetadataFilePresent metadata_file_present_;
+ bool same_ops_;
+ bool other_ops_;
+ bool details_loaded_;
+ bool set_request_;
+};
+
+// Tests that DownloadMetadataManager::SetRequest works for all combinations of
+// states.
+TEST_P(SetRequestTest, SetRequest) {
+ // Optionally put a metadata file in the profile directory.
+ switch (metadata_file_present_) {
+ case ABSENT:
+ break;
+ case PRESENT_FOR_THIS_ITEM:
+ WriteTestMetadataFile();
+ break;
+ case PRESENT_FOR_OTHER_ITEM:
+ WriteTestMetadataFileForItem(kOtherDownloadId);
+ break;
+ case PRESENT_FOR_UNKNOWN_ITEM:
+ WriteTestMetadataFileForItem(kCrazyDowloadId);
+ break;
+ }
+
+ AddDownloadManager();
+ AddDownloadItems();
+
+ // Optionally allow the task to read the file to complete.
+ if (details_loaded_) {
+ RunAllTasks();
+ } else {
+ // Optionally add pending operations if the load is outstanding.
+ if (same_ops_)
+ test_item_->NotifyObserversDownloadOpened();
+ if (other_ops_)
+ other_item_->NotifyObserversDownloadOpened();
+ }
+
+ static const char kNewUrl[] = "http://blorf";
+ scoped_ptr<ClientDownloadRequest> request;
+ if (set_request_)
+ request = MakeTestRequest(kNewUrl).Pass();
+ else
+ request.reset();
+ manager_.SetRequest(test_item_.get(), request.get());
+
+ // Allow the write or remove task to run.
+ RunAllTasks();
+
+ MockDownloadDetailsGetter details_getter;
+ if (set_request_) {
+ // Expect that the callback is invoked with details for this item.
+ EXPECT_CALL(
+ details_getter,
+ OnDownloadDetails(ResultOf(GetDetailsDownloadUrl, StrEq(kNewUrl))));
+ } else {
+ // Expect that the callback is invoked with null to clear stale metadata.
+ EXPECT_CALL(details_getter, OnDownloadDetails(IsNull()));
+ }
+ manager_.GetDownloadDetails(&profile_, details_getter.GetCallback());
+
+ DestroyDownloadItems();
+ ShutdownDownloadManager();
+
+ scoped_ptr<DownloadMetadata> metadata(ReadTestMetadataFile());
+ if (set_request_) {
+ // Expect that the file contains metadata for the download.
+ ASSERT_TRUE(metadata);
+ EXPECT_EQ(kTestDownloadId, metadata->download_id());
+ EXPECT_STREQ(kNewUrl, metadata->download().download().url().c_str());
+ } else {
+ // Expect that the file is not present.
+ ASSERT_FALSE(metadata);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ DownloadMetadataManager,
+ SetRequestTest,
+ testing::Combine(testing::Values("absent", "this", "other", "unknown"),
+ testing::Values("none", "pending"),
+ testing::Values("none", "pending"),
+ testing::Values("waiting", "loaded"),
+ testing::Values("clear", "set")));
+
+} // namespace safe_browsing

Powered by Google App Engine
This is Rietveld 408576698