Chromium Code Reviews| Index: chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc |
| diff --git a/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f1631c1c02ba98335cc604a5b206fa75d698c1da |
| --- /dev/null |
| +++ b/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc |
| @@ -0,0 +1,340 @@ |
| +// Copyright 2016 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 <stddef.h> |
| +#include <algorithm> |
| +#include <memory> |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/files/file_util.h" |
| +#include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/run_loop.h" |
| +#include "base/stl_util.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h" |
| +#include "chrome/test/base/testing_profile.h" |
| +#include "content/public/browser/storage_partition.h" |
| +#include "content/public/test/test_browser_thread.h" |
| +#include "content/public/test/test_browser_thread_bundle.h" |
| +#include "ppapi/shared_impl/ppapi_constants.h" |
| +#include "storage/browser/fileapi/async_file_util.h" |
| +#include "storage/browser/fileapi/file_system_context.h" |
| +#include "storage/browser/fileapi/file_system_operation_context.h" |
| +#include "storage/browser/fileapi/file_system_url.h" |
| +#include "storage/browser/fileapi/isolated_context.h" |
| +#include "storage/browser/quota/quota_manager.h" |
| +#include "storage/common/fileapi/file_system_types.h" |
| +#include "storage/common/fileapi/file_system_util.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using content::BrowserContext; |
| +using content::BrowserThread; |
| + |
| +namespace { |
| + |
| +// We'll use these three distinct origins for testing, both as strings and as |
| +// GURLs in appropriate contexts. |
| +const char kTestOrigin1[] = "http://host1:1/"; |
| +const char kTestOrigin2[] = "http://host2:2/"; |
| +const char kTestOrigin3[] = "http://host3:1/"; |
| + |
| +const GURL kOrigin1(kTestOrigin1); |
| +const GURL kOrigin2(kTestOrigin2); |
| +const GURL kOrigin3(kTestOrigin3); |
| + |
| +const char kWidevineCdmPluginId[] = "application_x-ppapi-widevine-cdm"; |
| +const char kClearKeyCdmPluginId[] = "application_x-ppapi-clearkey-cdm"; |
| + |
| +class AwaitCompletionHelper { |
| + public: |
| + AwaitCompletionHelper() : start_(false), already_quit_(false) {} |
| + virtual ~AwaitCompletionHelper() {} |
| + |
| + void BlockUntilNotified() { |
| + if (!already_quit_) { |
| + DCHECK(!start_); |
| + start_ = true; |
| + base::RunLoop().Run(); |
| + } else { |
| + DCHECK(!start_); |
| + already_quit_ = false; |
| + } |
| + } |
| + |
| + base::Closure NotifyClosure() { |
| + return base::Bind(&AwaitCompletionHelper::Notify, base::Unretained(this)); |
| + } |
| + |
| + private: |
| + void Notify() { |
| + if (start_) { |
| + DCHECK(!already_quit_); |
| + base::MessageLoop::current()->QuitWhenIdle(); |
| + start_ = false; |
| + } else { |
| + DCHECK(!already_quit_); |
| + already_quit_ = true; |
| + } |
| + } |
| + |
| + // Helps prevent from running message_loop, if the callback invoked |
| + // immediately. |
| + bool start_; |
| + bool already_quit_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AwaitCompletionHelper); |
| +}; |
| + |
| +// The FileSystem APIs are all asynchronous; this testing class wraps up the |
| +// boilerplate code necessary to deal with waiting for responses. In a nutshell, |
| +// any async call whose response we want to test ought to be followed by a call |
| +// to BlockUntilNotified(), which will block until Notify() is called. |
| +class BrowsingDataMediaLicenseHelperTest : public testing::Test { |
| + public: |
| + BrowsingDataMediaLicenseHelperTest() { |
| + now_ = base::Time::Now(); |
| + profile_.reset(new TestingProfile()); |
| + filesystem_context_ = |
| + BrowserContext::GetDefaultStoragePartition(profile_.get()) |
| + ->GetFileSystemContext(); |
| + helper_ = BrowsingDataMediaLicenseHelper::Create(filesystem_context_); |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + ~BrowsingDataMediaLicenseHelperTest() override { |
| + // Avoid memory leaks. |
| + profile_.reset(); |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + // Calls StartFetching() on the test's BrowsingDataMediaLicenseHelper |
| + // object, then blocks until the callback is executed. |
| + void FetchMediaLicenses() { |
| + AwaitCompletionHelper await_completion; |
| + helper_->StartFetching( |
| + base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFetchMediaLicenses, |
| + base::Unretained(this), await_completion.NotifyClosure())); |
| + await_completion.BlockUntilNotified(); |
| + } |
| + |
| + // Callback that should be executed in response to StartFetching(), and stores |
| + // found file systems locally so that they are available via GetFileSystems(). |
| + void OnFetchMediaLicenses( |
| + const base::Closure& done_cb, |
| + const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>& |
| + media_license_info_list) { |
| + media_license_info_list_.reset( |
| + new std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>( |
| + media_license_info_list)); |
| + done_cb.Run(); |
| + } |
| + |
| + // Add some files to the PluginPrivateFileSystem. They are created as follows: |
| + // kOrigin1 - ClearKey - 1 file - timestamp 10 days ago |
| + // kOrigin2 - Widevine - 2 files - timestamps now and 60 days ago |
| + // kOrigin3 - Widevine - 2 files - timestamps 20 and 30 days ago |
| + virtual void PopulateTestMediaLicenseData() { |
| + const base::Time ten_days_ago = now_ - base::TimeDelta::FromDays(10); |
| + const base::Time twenty_days_ago = now_ - base::TimeDelta::FromDays(20); |
| + const base::Time thirty_days_ago = now_ - base::TimeDelta::FromDays(30); |
| + const base::Time sixty_days_ago = now_ - base::TimeDelta::FromDays(60); |
| + |
| + std::string clearkey_fsid = |
| + CreateFileSystem(kClearKeyCdmPluginId, kOrigin1); |
| + storage::FileSystemURL clearkey_file = |
| + CreateFile(kOrigin1, clearkey_fsid, "foo"); |
| + SetFileTimestamp(clearkey_file, ten_days_ago); |
| + |
| + std::string widevine_fsid = |
| + CreateFileSystem(kWidevineCdmPluginId, kOrigin2); |
| + storage::FileSystemURL widevine_file1 = |
| + CreateFile(kOrigin2, widevine_fsid, "bar1"); |
| + storage::FileSystemURL widevine_file2 = |
| + CreateFile(kOrigin2, widevine_fsid, "bar2"); |
| + SetFileTimestamp(widevine_file1, now_); |
| + SetFileTimestamp(widevine_file2, sixty_days_ago); |
| + |
| + std::string widevine_fsid2 = |
| + CreateFileSystem(kWidevineCdmPluginId, kOrigin3); |
| + storage::FileSystemURL widevine_file3 = |
| + CreateFile(kOrigin3, widevine_fsid2, "test1"); |
| + storage::FileSystemURL widevine_file4 = |
| + CreateFile(kOrigin3, widevine_fsid2, "test2"); |
| + SetFileTimestamp(widevine_file3, twenty_days_ago); |
| + SetFileTimestamp(widevine_file4, thirty_days_ago); |
| + } |
| + |
| + // Creates a PluginPrivateFileSystem for the |plugin_name| and |origin| |
| + // provided. Returns the file system ID for the created |
| + // PluginPrivateFileSystem. |
| + std::string CreateFileSystem(const std::string& plugin_name, |
|
msramek
2016/09/26 18:28:05
nit: This method and everything below looks like i
jrummell
2016/09/27 18:21:15
Done.
|
| + const GURL& origin) { |
| + AwaitCompletionHelper await_completion; |
| + std::string fsid = storage::IsolatedContext::GetInstance() |
| + ->RegisterFileSystemForVirtualPath( |
| + storage::kFileSystemTypePluginPrivate, |
| + ppapi::kPluginPrivateRootName, base::FilePath()); |
| + EXPECT_TRUE(storage::ValidateIsolatedFileSystemId(fsid)); |
| + filesystem_context_->OpenPluginPrivateFileSystem( |
| + origin, storage::kFileSystemTypePluginPrivate, fsid, plugin_name, |
| + storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, |
| + base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileSystemOpened, |
| + base::Unretained(this), await_completion.NotifyClosure())); |
| + await_completion.BlockUntilNotified(); |
| + return fsid; |
| + } |
| + |
| + void OnFileSystemOpened(const base::Closure& done_cb, |
| + base::File::Error result) { |
| + EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result); |
| + done_cb.Run(); |
| + } |
| + |
| + // Creates a file named |file_name| in the PluginPrivateFileSystem identified |
| + // by |origin| and |fsid|. The file is empty (so size = 0). Returns the URL |
| + // for the created file. The file must not already exist or the test will |
| + // fail. |
| + storage::FileSystemURL CreateFile(const GURL& origin, |
| + const std::string& fsid, |
| + const std::string& file_name) { |
| + AwaitCompletionHelper await_completion; |
| + std::string root = storage::GetIsolatedFileSystemRootURIString( |
| + origin, fsid, ppapi::kPluginPrivateRootName); |
| + storage::FileSystemURL file_url = |
| + filesystem_context_->CrackURL(GURL(root + file_name)); |
| + storage::AsyncFileUtil* file_util = filesystem_context_->GetAsyncFileUtil( |
| + storage::kFileSystemTypePluginPrivate); |
| + std::unique_ptr<storage::FileSystemOperationContext> operation_context = |
| + base::WrapUnique( |
| + new storage::FileSystemOperationContext(filesystem_context_)); |
| + operation_context->set_allowed_bytes_growth( |
| + storage::QuotaManager::kNoLimit); |
| + file_util->EnsureFileExists( |
| + std::move(operation_context), file_url, |
| + base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileCreated, |
| + base::Unretained(this), await_completion.NotifyClosure())); |
| + await_completion.BlockUntilNotified(); |
| + return file_url; |
| + } |
| + |
| + void OnFileCreated(const base::Closure& done_cb, |
| + base::File::Error result, |
| + bool created) { |
| + EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result); |
| + EXPECT_TRUE(created); |
| + done_cb.Run(); |
| + } |
| + |
| + // Sets the last_access_time and last_modified_time to |time_stamp| on the |
| + // file specified by |file_url|. The file must already exist. |
| + void SetFileTimestamp(const storage::FileSystemURL& file_url, |
| + const base::Time& time_stamp) { |
| + AwaitCompletionHelper await_completion; |
| + storage::AsyncFileUtil* file_util = filesystem_context_->GetAsyncFileUtil( |
| + storage::kFileSystemTypePluginPrivate); |
| + std::unique_ptr<storage::FileSystemOperationContext> operation_context = |
| + base::WrapUnique( |
| + new storage::FileSystemOperationContext(filesystem_context_)); |
| + file_util->Touch( |
| + std::move(operation_context), file_url, time_stamp, time_stamp, |
| + base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileTouched, |
| + base::Unretained(this), await_completion.NotifyClosure())); |
| + await_completion.BlockUntilNotified(); |
| + } |
| + |
| + void OnFileTouched(const base::Closure& done_cb, base::File::Error result) { |
| + EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result); |
| + done_cb.Run(); |
| + } |
| + |
| + const base::Time Now() { return now_; } |
| + |
| + protected: |
|
msramek
2016/09/26 18:28:05
Why is this protected? Do we inherit from this tes
jrummell
2016/09/27 18:21:15
Made private. I think I used browsing_data_file_sy
|
| + content::TestBrowserThreadBundle thread_bundle_; |
| + std::unique_ptr<TestingProfile> profile_; |
| + scoped_refptr<BrowsingDataMediaLicenseHelper> helper_; |
| + |
| + // Keep a fixed "now" so that we can compare timestamps. |
| + base::Time now_; |
| + |
| + // We don't own this pointer. |
| + storage::FileSystemContext* filesystem_context_; |
| + |
| + // Storage to pass information back from callbacks. |
| + std::unique_ptr<std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>> |
| + media_license_info_list_; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(BrowsingDataMediaLicenseHelperTest); |
| +}; |
| + |
| +// Verifies that the BrowsingDataMediaLicenseHelper correctly handles an empty |
| +// filesystem. |
| +TEST_F(BrowsingDataMediaLicenseHelperTest, Empty) { |
| + FetchMediaLicenses(); |
| + EXPECT_EQ(0u, media_license_info_list_->size()); |
| +} |
| + |
| +// Verifies that the BrowsingDataMediaLicenseHelper correctly finds the test |
| +// data, and that each media license returned contains the expected data. |
| +TEST_F(BrowsingDataMediaLicenseHelperTest, FetchData) { |
| + PopulateTestMediaLicenseData(); |
| + |
| + FetchMediaLicenses(); |
| + EXPECT_EQ(3u, media_license_info_list_->size()); |
| + |
| + // Order is arbitrary, verify both origins. |
| + bool test_hosts_found[] = {false, false, false}; |
| + for (const auto& info : *media_license_info_list_) { |
| + if (info.origin == kOrigin1) { |
| + EXPECT_FALSE(test_hosts_found[0]); |
| + test_hosts_found[0] = true; |
| + EXPECT_EQ(0u, info.size); |
| + // Single file for origin1 should be 10 days ago. |
| + EXPECT_EQ(10, (Now() - info.last_modified_time).InDays()); |
| + } else if (info.origin == kOrigin2) { |
| + EXPECT_FALSE(test_hosts_found[1]); |
| + test_hosts_found[1] = true; |
| + EXPECT_EQ(0u, info.size); |
| + // Files for origin2 are now and 60 days ago, so it should report now. |
| + EXPECT_EQ(0, (Now() - info.last_modified_time).InDays()); |
| + } else if (info.origin == kOrigin3) { |
| + EXPECT_FALSE(test_hosts_found[2]); |
| + test_hosts_found[2] = true; |
| + EXPECT_EQ(0u, info.size); |
| + // Files for origin3 are 20 and 30 days ago, so it should report 20. |
| + EXPECT_EQ(20, (Now() - info.last_modified_time).InDays()); |
| + } else { |
| + ADD_FAILURE() << info.origin.spec() << " isn't an origin we added."; |
| + } |
| + } |
| + for (size_t i = 0; i < arraysize(test_hosts_found); i++) { |
| + EXPECT_TRUE(test_hosts_found[i]); |
| + } |
| +} |
| + |
| +// Verifies that the BrowsingDataMediaLicenseHelper correctly deletes media |
| +// licenses via DeleteMediaLicenseOrigin(). |
| +TEST_F(BrowsingDataMediaLicenseHelperTest, DeleteData) { |
| + PopulateTestMediaLicenseData(); |
| + |
| + helper_->DeleteMediaLicenseOrigin(kOrigin1); |
| + helper_->DeleteMediaLicenseOrigin(kOrigin2); |
| + |
| + FetchMediaLicenses(); |
| + EXPECT_EQ(1u, media_license_info_list_->size()); |
| + |
| + BrowsingDataMediaLicenseHelper::MediaLicenseInfo info = |
| + *(media_license_info_list_->begin()); |
| + EXPECT_EQ(kOrigin3, info.origin); |
| + EXPECT_EQ(0u, info.size); |
| + EXPECT_EQ(20, (Now() - info.last_modified_time).InDays()); |
| +} |
| + |
| +} // namespace |