blob: 553c1eff163f7e7beb18a5a49b658f0d51f501e8 [file] [log] [blame]
// Copyright 2017 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 "media/mojo/services/mojo_cdm_file_io.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "media/cdm/api/content_decryption_module.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Unused;
using Status = cdm::FileIOClient::Status;
namespace media {
namespace {
class MockFileIOClient : public cdm::FileIOClient {
public:
MockFileIOClient() = default;
~MockFileIOClient() override = default;
MOCK_METHOD1(OnOpenComplete, void(Status));
MOCK_METHOD3(OnReadComplete, void(Status, const uint8_t*, uint32_t));
MOCK_METHOD1(OnWriteComplete, void(Status));
};
class MockCdmFile : public mojom::CdmFile {
public:
MockCdmFile() = default;
~MockCdmFile() override = default;
MOCK_METHOD1(Read, void(ReadCallback));
MOCK_METHOD2(Write, void(const std::vector<uint8_t>&, WriteCallback));
};
class MockCdmStorage : public mojom::CdmStorage {
public:
MockCdmStorage(mojo::PendingReceiver<mojom::CdmStorage> receiver,
MockCdmFile* cdm_file)
: receiver_(this, std::move(receiver)), client_receiver_(cdm_file) {}
~MockCdmStorage() override = default;
// MojoCdmFileIO calls CdmStorage::Open() to open the file. Receivers always
// succeed.
void Open(const std::string& file_name, OpenCallback callback) override {
mojo::PendingAssociatedRemote<mojom::CdmFile> client_remote;
client_receiver_.Bind(client_remote.InitWithNewEndpointAndPassReceiver());
std::move(callback).Run(mojom::CdmStorage::Status::kSuccess,
std::move(client_remote));
base::RunLoop().RunUntilIdle();
}
private:
mojo::Receiver<mojom::CdmStorage> receiver_;
mojo::AssociatedReceiver<mojom::CdmFile> client_receiver_;
};
} // namespace
// Note that the current browser_test ECKEncryptedMediaTest.FileIOTest
// does test reading and writing files with real data.
class MojoCdmFileIOTest : public testing::Test, public MojoCdmFileIO::Delegate {
protected:
MojoCdmFileIOTest() = default;
~MojoCdmFileIOTest() override = default;
// testing::Test implementation.
void SetUp() override {
client_ = std::make_unique<MockFileIOClient>();
mojo::Remote<mojom::CdmStorage> cdm_storage_remote;
cdm_storage_ = std::make_unique<MockCdmStorage>(
cdm_storage_remote.BindNewPipeAndPassReceiver(), &cdm_file_);
file_io_ = std::make_unique<MojoCdmFileIO>(this, client_.get(),
std::move(cdm_storage_remote));
}
// MojoCdmFileIO::Delegate implementation.
void CloseCdmFileIO(MojoCdmFileIO* cdm_file_io) override {
DCHECK_EQ(file_io_.get(), cdm_file_io);
file_io_.reset();
}
void ReportFileReadSize(int file_size_bytes) override {}
base::test::TaskEnvironment task_environment_;
std::unique_ptr<MojoCdmFileIO> file_io_;
std::unique_ptr<MockFileIOClient> client_;
std::unique_ptr<MockCdmStorage> cdm_storage_;
MockCdmFile cdm_file_;
};
TEST_F(MojoCdmFileIOTest, OpenFile) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenFileTwice) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenFileAfterOpen) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
// Run now so that the file is opened.
base::RunLoop().RunUntilIdle();
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
// Run a second time so Open() tries after the file is already open.
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenDifferentFiles) {
const std::string kFileName1 = "openfile1";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName1.data(), kFileName1.length());
const std::string kFileName2 = "openfile2";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName2.data(), kFileName2.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, Read) {
const std::string kFileName = "readfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
// Successful reads always return a 3-byte buffer.
EXPECT_CALL(cdm_file_, Read(_))
.WillOnce([](mojom::CdmFile::ReadCallback callback) {
std::move(callback).Run(mojom::CdmFile::Status::kSuccess, {1, 2, 3});
});
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 3));
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, ReadBeforeOpen) {
// File not open, so reading should fail.
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kError, _, _));
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, TwoReads) {
const std::string kFileName = "readfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
EXPECT_CALL(cdm_file_, Read(_))
.WillOnce([](mojom::CdmFile::ReadCallback callback) {
std::move(callback).Run(mojom::CdmFile::Status::kSuccess, {1, 2, 3, 4});
});
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 4));
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kInUse, _, 0));
file_io_->Read();
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, Write) {
const std::string kFileName = "writefile";
std::vector<uint8_t> data{1, 2, 3, 4, 5};
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
// Writing always succeeds.
EXPECT_CALL(cdm_file_, Write(_, _))
.WillOnce([](Unused, mojom::CdmFile::WriteCallback callback) {
std::move(callback).Run(mojom::CdmFile::Status::kSuccess);
});
EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kSuccess));
file_io_->Write(data.data(), data.size());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, WriteBeforeOpen) {
std::vector<uint8_t> data{1, 2, 3, 4, 5};
// File not open, so writing should fail.
EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kError));
file_io_->Write(data.data(), data.size());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, TwoWrites) {
const std::string kFileName = "writefile";
std::vector<uint8_t> data{1, 2, 3, 4, 5};
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
EXPECT_CALL(cdm_file_, Write(_, _))
.WillOnce([](Unused, mojom::CdmFile::WriteCallback callback) {
std::move(callback).Run(mojom::CdmFile::Status::kSuccess);
});
EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kSuccess));
EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kInUse));
file_io_->Write(data.data(), data.size());
file_io_->Write(data.data(), data.size());
base::RunLoop().RunUntilIdle();
}
} // namespace media