blob: 17685f97a482a7b746d33f321c5e656c5f711698 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/cdm_storage_manager.h"
#include <string>
#include "base/test/task_environment.h"
#include "base/test/test_file_util.h"
#include "base/test/test_future.h"
#include "media/mojo/mojom/cdm_storage.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
using media::mojom::CdmFile;
using media::mojom::CdmStorage;
namespace content {
namespace {
const media::CdmType kCdmType{1234, 5678};
const char kFileName[] = "file.txt";
const char kFileNameTwo[] = "file2.txt";
const std::vector<uint8_t> kPopulatedFileValue = {1, 2, 3};
const std::vector<uint8_t> kPopulatedFileValueTwo = {1, 2, 3, 4};
std::vector<uint8_t> kEmptyFileValue;
} // namespace
// TODO(crbug.com/40272342): Add a DeleteFile test once there is a way to check
// that the db is actually deleted.
class CdmStorageManagerTest : public testing::Test {
public:
CdmStorageManagerTest() = default;
~CdmStorageManagerTest() override = default;
protected:
void SetUpManager(const base::FilePath file_path) {
cdm_storage_manager_ = std::make_unique<CdmStorageManager>(file_path);
}
void SetUpManagerTwo(const base::FilePath file_path) {
cdm_storage_manager_two_ = std::make_unique<CdmStorageManager>(file_path);
}
mojo::AssociatedRemote<media::mojom::CdmFile> OpenCdmFile(
const mojo::Remote<media::mojom::CdmStorage>& storage,
const std::string& file_name) {
base::test::TestFuture<media::mojom::CdmStorage::Status,
mojo::PendingAssociatedRemote<media::mojom::CdmFile>>
open_future;
storage->Open(file_name, open_future.GetCallback());
auto [status, pending_remote] = open_future.Take();
EXPECT_EQ(status, media::mojom::CdmStorage::Status::kSuccess);
mojo::AssociatedRemote<media::mojom::CdmFile> cdm_file;
cdm_file.Bind(std::move(pending_remote));
return cdm_file;
}
void Write(const mojo::AssociatedRemote<media::mojom::CdmFile>& cdm_file,
const std::vector<uint8_t> data) {
base::test::TestFuture<media::mojom::CdmFile::Status> write_future;
cdm_file->Write(data, write_future.GetCallback());
EXPECT_EQ(write_future.Get(), media::mojom::CdmFile::Status::kSuccess);
}
// Reads the previously opened `cdm_file` and check that its contents match
// `expected_data`.
void ExpectFileContents(
const mojo::AssociatedRemote<media::mojom::CdmFile>& cdm_file,
const std::vector<uint8_t> expected_data) {
base::test::TestFuture<media::mojom::CdmFile::Status, std::vector<uint8_t>>
future;
cdm_file->Read(future.GetCallback<media::mojom::CdmFile::Status,
const std::vector<uint8_t>&>());
auto [status, data] = future.Take();
EXPECT_EQ(status, media::mojom::CdmFile::Status::kSuccess);
EXPECT_EQ(data, expected_data);
}
const blink::StorageKey kTestStorageKey =
blink::StorageKey::CreateFromStringForTesting("https://example.com/");
const blink::StorageKey kTestStorageKeyTwo =
blink::StorageKey::CreateFromStringForTesting("https://exampletwo.com/");
std::unique_ptr<CdmStorageManager> cdm_storage_manager_;
std::unique_ptr<CdmStorageManager> cdm_storage_manager_two_;
mojo::Remote<CdmStorage> cdm_storage_;
mojo::Remote<CdmStorage> cdm_storage_two_;
base::test::TaskEnvironment task_environment_;
};
class CdmStorageManagerSingularTest : public CdmStorageManagerTest,
public testing::WithParamInterface<bool> {
public:
void SetUp() override {
bool is_incognito = GetParam();
if (is_incognito) {
SetUpManager(base::FilePath());
} else {
SetUpManager(base::CreateUniqueTempDirectoryScopedToTest().Append(
kCdmStorageDatabaseFileName));
}
}
void TearDown() override {
cdm_storage_manager_->DeleteData(base::NullCallback(), blink::StorageKey(),
base::Time::Min(), base::Time::Max(),
base::DoNothing());
// To prevent a memory leak, reset the manager. This may post
// destruction of other objects, so RunUntilIdle().
cdm_storage_manager_.reset();
task_environment_.RunUntilIdle();
}
};
class CdmStorageManagerMultipleTest
: public CdmStorageManagerTest,
public testing::WithParamInterface<std::tuple<bool, bool>> {
public:
void SetUp() override {
if (std::get<0>(GetParam())) {
SetUpManager(base::FilePath());
} else {
SetUpManager(base::CreateUniqueTempDirectoryScopedToTest().Append(
kCdmStorageDatabaseFileName));
}
if (std::get<1>(GetParam())) {
SetUpManagerTwo(base::FilePath());
} else {
SetUpManagerTwo(base::CreateUniqueTempDirectoryScopedToTest().Append(
kCdmStorageDatabaseFileName));
}
}
void TearDown() override {
cdm_storage_manager_->DeleteData(base::NullCallback(), blink::StorageKey(),
base::Time::Min(), base::Time::Max(),
base::DoNothing());
cdm_storage_manager_two_->DeleteData(base::NullCallback(),
blink::StorageKey(), base::Time::Min(),
base::Time::Max(), base::DoNothing());
// To prevent a memory leak, reset the manager. This may post
// destruction of other objects, so RunUntilIdle().
cdm_storage_manager_.reset();
cdm_storage_manager_two_.reset();
task_environment_.RunUntilIdle();
}
};
// This test case tests deletion of a file.
TEST_P(CdmStorageManagerSingularTest, DeleteFile) {
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file = OpenCdmFile(cdm_storage_, kFileName);
ASSERT_TRUE(cdm_file.is_bound());
Write(cdm_file, kPopulatedFileValue);
ExpectFileContents(cdm_file, kPopulatedFileValue);
cdm_storage_manager_->DeleteFile(kTestStorageKey, kCdmType, kFileName,
base::DoNothing());
ExpectFileContents(cdm_file, kEmptyFileValue);
}
// This test case tests deletion of a non existent file to make sure it doesn't
// affect any other files in the database.
TEST_P(CdmStorageManagerSingularTest, DeleteNonExistentFile) {
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file = OpenCdmFile(cdm_storage_, kFileName);
ASSERT_TRUE(cdm_file.is_bound());
Write(cdm_file, kPopulatedFileValue);
ExpectFileContents(cdm_file, kPopulatedFileValue);
cdm_storage_manager_->DeleteFile(kTestStorageKey, kCdmType, kFileNameTwo,
base::DoNothing());
ExpectFileContents(cdm_file, kPopulatedFileValue);
}
// This test cases tests that we accurately delete data for a storage key
// without touching other data in the database.
TEST_P(CdmStorageManagerSingularTest, DeleteDataForStorageKey) {
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_one = OpenCdmFile(cdm_storage_, kFileName);
Write(cdm_file_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_one.is_bound());
auto cdm_file_two_for_remote_one = OpenCdmFile(cdm_storage_, kFileNameTwo);
Write(cdm_file_two_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_two_for_remote_one.is_bound());
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKeyTwo, kCdmType),
cdm_storage_two_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_two = OpenCdmFile(cdm_storage_two_, kFileNameTwo);
Write(cdm_file_for_remote_two, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_two.is_bound());
cdm_storage_manager_->DeleteData(base::NullCallback(), kTestStorageKey,
base::Time::Min(), base::Time::Max(),
base::DoNothing());
ExpectFileContents(cdm_file_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_two_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_for_remote_two, kPopulatedFileValue);
}
TEST_P(CdmStorageManagerSingularTest, DeleteFileNoDatabase) {
cdm_storage_manager_->DeleteFile(kTestStorageKey, kCdmType, kFileNameTwo,
base::DoNothing());
}
// This test case tests when there is a file written to the CdmStorageDatabase
// from two different profiles with the same file name, cdm type, and storage
// key. We want to make sure that although they share the same qualities,
// deleting the file from one of the profiles does not affect the other profile.
TEST_P(CdmStorageManagerMultipleTest, DeleteFile) {
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file = OpenCdmFile(cdm_storage_, kFileName);
ASSERT_TRUE(cdm_file.is_bound());
Write(cdm_file, kPopulatedFileValue);
ExpectFileContents(cdm_file, kPopulatedFileValue);
cdm_storage_manager_two_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_two_.BindNewPipeAndPassReceiver());
auto cdm_file_two = OpenCdmFile(cdm_storage_two_, kFileName);
ASSERT_TRUE(cdm_file_two.is_bound());
Write(cdm_file_two, kPopulatedFileValue);
ExpectFileContents(cdm_file_two, kPopulatedFileValue);
cdm_storage_manager_->DeleteFile(kTestStorageKey, kCdmType, kFileName,
base::DoNothing());
ExpectFileContents(cdm_file, kEmptyFileValue);
ExpectFileContents(cdm_file_two, kPopulatedFileValue);
}
// This tests deletion of multiple files under the same storage key. Since the
// files are written from multiple profiles with the same storage key, we verify
// that deletion from one profile does not affect the files written to the same
// storage key from another profile.
TEST_P(CdmStorageManagerMultipleTest, DeleteDataForStorageKey) {
auto time_test_started = base::Time::Now();
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_one = OpenCdmFile(cdm_storage_, kFileName);
Write(cdm_file_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_one.is_bound());
auto cdm_file_two_for_remote_one = OpenCdmFile(cdm_storage_, kFileNameTwo);
Write(cdm_file_two_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_two_for_remote_one.is_bound());
cdm_storage_manager_two_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_two_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_two = OpenCdmFile(cdm_storage_two_, kFileNameTwo);
Write(cdm_file_for_remote_two, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_two.is_bound());
cdm_storage_manager_->DeleteData(base::NullCallback(), kTestStorageKey,
base::Time::Min(), time_test_started,
base::DoNothing());
ExpectFileContents(cdm_file_for_remote_one, kPopulatedFileValue);
ExpectFileContents(cdm_file_two_for_remote_one, kPopulatedFileValue);
ExpectFileContents(cdm_file_for_remote_two, kPopulatedFileValue);
cdm_storage_manager_->DeleteData(base::NullCallback(), kTestStorageKey,
base::Time::Min(), base::Time::Max(),
base::DoNothing());
ExpectFileContents(cdm_file_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_two_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_for_remote_two, kPopulatedFileValue);
}
TEST_P(CdmStorageManagerMultipleTest, DeleteDataForStorageKeyTimeSpecified) {
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_one = OpenCdmFile(cdm_storage_, kFileName);
Write(cdm_file_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_one.is_bound());
auto cdm_file_two_for_remote_one = OpenCdmFile(cdm_storage_, kFileNameTwo);
Write(cdm_file_two_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_two_for_remote_one.is_bound());
cdm_storage_manager_two_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_two_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_two = OpenCdmFile(cdm_storage_two_, kFileNameTwo);
Write(cdm_file_for_remote_two, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_two.is_bound());
cdm_storage_manager_->DeleteData(base::NullCallback(), kTestStorageKey,
base::Time::Min(), base::Time::Max(),
base::DoNothing());
ExpectFileContents(cdm_file_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_two_for_remote_one, kEmptyFileValue);
ExpectFileContents(cdm_file_for_remote_two, kPopulatedFileValue);
}
TEST_P(CdmStorageManagerMultipleTest, DeleteFileNoDatabase) {
cdm_storage_manager_->DeleteFile(kTestStorageKey, kCdmType, kFileNameTwo,
base::DoNothing());
cdm_storage_manager_two_->DeleteFile(kTestStorageKey, kCdmType, kFileNameTwo,
base::DoNothing());
}
TEST_P(CdmStorageManagerMultipleTest, GetSizeForFileNoDatabase) {
base::test::TestFuture<uint64_t> get_size_future;
cdm_storage_manager_->GetSizeForFile(kTestStorageKey, kCdmType, kFileNameTwo,
get_size_future.GetCallback());
EXPECT_EQ(get_size_future.Get(), 0u);
base::test::TestFuture<uint64_t> get_size_future_two;
cdm_storage_manager_two_->GetSizeForFile(kTestStorageKey, kCdmType,
kFileNameTwo,
get_size_future_two.GetCallback());
EXPECT_EQ(get_size_future_two.Get(), 0u);
}
TEST_P(CdmStorageManagerMultipleTest, GetSizeForTests) {
auto time_test_started = base::Time::Now();
cdm_storage_manager_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_one = OpenCdmFile(cdm_storage_, kFileName);
Write(cdm_file_for_remote_one, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_one.is_bound());
auto cdm_file_two_for_remote_one = OpenCdmFile(cdm_storage_, kFileNameTwo);
Write(cdm_file_two_for_remote_one, kPopulatedFileValueTwo);
ASSERT_TRUE(cdm_file_two_for_remote_one.is_bound());
cdm_storage_manager_two_->OpenCdmStorage(
CdmStorageBindingContext(kTestStorageKey, kCdmType),
cdm_storage_two_.BindNewPipeAndPassReceiver());
auto cdm_file_for_remote_two = OpenCdmFile(cdm_storage_two_, kFileNameTwo);
Write(cdm_file_for_remote_two, kPopulatedFileValue);
ASSERT_TRUE(cdm_file_for_remote_two.is_bound());
base::test::TestFuture<uint64_t> get_size_time_frame_future;
cdm_storage_manager_->GetSizeForTimeFrame(
base::Time::Min(), base::Time::Max(),
get_size_time_frame_future.GetCallback());
EXPECT_EQ(get_size_time_frame_future.Get(),
kPopulatedFileValue.size() + kPopulatedFileValueTwo.size());
base::test::TestFuture<uint64_t> get_size_time_frame_future_two;
cdm_storage_manager_two_->GetSizeForTimeFrame(
time_test_started, base::Time::Max(),
get_size_time_frame_future_two.GetCallback());
EXPECT_EQ(get_size_time_frame_future_two.Get(), kPopulatedFileValue.size());
base::test::TestFuture<uint64_t> get_size_storage_key;
cdm_storage_manager_->GetSizeForStorageKey(
kTestStorageKey, time_test_started, base::Time::Max(),
get_size_storage_key.GetCallback());
EXPECT_EQ(get_size_storage_key.Get(),
kPopulatedFileValue.size() + kPopulatedFileValueTwo.size());
base::test::TestFuture<uint64_t> get_size_file;
cdm_storage_manager_->GetSizeForFile(kTestStorageKey, kCdmType, kFileName,
get_size_file.GetCallback());
EXPECT_EQ(get_size_file.Get(), kPopulatedFileValue.size());
}
// `testing::Bool()` represents either incognito, or a non incognito profile, so
// that we test on both.
INSTANTIATE_TEST_SUITE_P(,
CdmStorageManagerSingularTest,
/*is_profile_incognito=*/testing::Bool());
// The pairs represent different combinations of the types of profiles that
// can exist on Chrome together.
INSTANTIATE_TEST_SUITE_P(
,
CdmStorageManagerMultipleTest,
testing::Combine(/*is_profile_one_incognito=*/testing::Bool(),
/*is_profile_two_incognito=*/testing::Bool()));
} // namespace content