| 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
|
|
|