1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/mojo/services/mojo_cdm_file_io.h"
7 #include "base/run_loop.h"
8 #include "base/test/task_environment.h"
9 #include "media/cdm/api/content_decryption_module.h"
10 #include "mojo/public/cpp/bindings/associated_receiver.h"
11 #include "mojo/public/cpp/bindings/associated_remote.h"
12 #include "mojo/public/cpp/bindings/pending_receiver.h"
13 #include "mojo/public/cpp/bindings/receiver.h"
14 #include "mojo/public/cpp/bindings/remote.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
19 using ::testing::Unused;
20 using Status = cdm::FileIOClient::Status;
26 class MockFileIOClient : public cdm::FileIOClient {
28 MockFileIOClient() = default;
29 ~MockFileIOClient() override = default;
31 MOCK_METHOD1(OnOpenComplete, void(Status));
32 MOCK_METHOD3(OnReadComplete, void(Status, const uint8_t*, uint32_t));
33 MOCK_METHOD1(OnWriteComplete, void(Status));
36 class MockCdmFile : public mojom::CdmFile {
38 MockCdmFile() = default;
39 ~MockCdmFile() override = default;
41 MOCK_METHOD1(Read, void(ReadCallback));
42 MOCK_METHOD2(Write, void(const std::vector<uint8_t>&, WriteCallback));
45 class MockCdmStorage : public mojom::CdmStorage {
47 MockCdmStorage(mojo::PendingReceiver<mojom::CdmStorage> receiver,
48 MockCdmFile* cdm_file)
49 : receiver_(this, std::move(receiver)), client_receiver_(cdm_file) {}
50 ~MockCdmStorage() override = default;
52 // MojoCdmFileIO calls CdmStorage::Open() to open the file. Receivers always
54 void Open(const std::string& file_name, OpenCallback callback) override {
55 mojo::PendingAssociatedRemote<mojom::CdmFile> client_remote;
56 client_receiver_.Bind(client_remote.InitWithNewEndpointAndPassReceiver());
57 std::move(callback).Run(mojom::CdmStorage::Status::kSuccess,
58 std::move(client_remote));
60 base::RunLoop().RunUntilIdle();
64 mojo::Receiver<mojom::CdmStorage> receiver_;
65 mojo::AssociatedReceiver<mojom::CdmFile> client_receiver_;
70 // Note that the current browser_test ECKEncryptedMediaTest.FileIOTest
71 // does test reading and writing files with real data.
73 class MojoCdmFileIOTest : public testing::Test, public MojoCdmFileIO::Delegate {
75 MojoCdmFileIOTest() = default;
76 ~MojoCdmFileIOTest() override = default;
78 // testing::Test implementation.
79 void SetUp() override {
80 client_ = std::make_unique<MockFileIOClient>();
82 mojo::Remote<mojom::CdmStorage> cdm_storage_remote;
83 cdm_storage_ = std::make_unique<MockCdmStorage>(
84 cdm_storage_remote.BindNewPipeAndPassReceiver(), &cdm_file_);
86 file_io_ = std::make_unique<MojoCdmFileIO>(this, client_.get(),
87 std::move(cdm_storage_remote));
90 // MojoCdmFileIO::Delegate implementation.
91 void CloseCdmFileIO(MojoCdmFileIO* cdm_file_io) override {
92 DCHECK_EQ(file_io_.get(), cdm_file_io);
96 void ReportFileReadSize(int file_size_bytes) override {}
98 base::test::TaskEnvironment task_environment_;
99 std::unique_ptr<MojoCdmFileIO> file_io_;
100 std::unique_ptr<MockFileIOClient> client_;
101 std::unique_ptr<MockCdmStorage> cdm_storage_;
102 MockCdmFile cdm_file_;
105 TEST_F(MojoCdmFileIOTest, OpenFile) {
106 const std::string kFileName = "openfile";
107 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
108 file_io_->Open(kFileName.data(), kFileName.length());
110 base::RunLoop().RunUntilIdle();
113 TEST_F(MojoCdmFileIOTest, OpenFileTwice) {
114 const std::string kFileName = "openfile";
115 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
116 file_io_->Open(kFileName.data(), kFileName.length());
118 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
119 file_io_->Open(kFileName.data(), kFileName.length());
121 base::RunLoop().RunUntilIdle();
124 TEST_F(MojoCdmFileIOTest, OpenFileAfterOpen) {
125 const std::string kFileName = "openfile";
126 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
127 file_io_->Open(kFileName.data(), kFileName.length());
129 // Run now so that the file is opened.
130 base::RunLoop().RunUntilIdle();
132 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
133 file_io_->Open(kFileName.data(), kFileName.length());
135 // Run a second time so Open() tries after the file is already open.
136 base::RunLoop().RunUntilIdle();
139 TEST_F(MojoCdmFileIOTest, OpenDifferentFiles) {
140 const std::string kFileName1 = "openfile1";
141 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
142 file_io_->Open(kFileName1.data(), kFileName1.length());
144 const std::string kFileName2 = "openfile2";
145 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
146 file_io_->Open(kFileName2.data(), kFileName2.length());
148 base::RunLoop().RunUntilIdle();
151 TEST_F(MojoCdmFileIOTest, Read) {
152 const std::string kFileName = "readfile";
153 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
154 file_io_->Open(kFileName.data(), kFileName.length());
155 base::RunLoop().RunUntilIdle();
157 // Successful reads always return a 3-byte buffer.
158 EXPECT_CALL(cdm_file_, Read(_))
159 .WillOnce([](mojom::CdmFile::ReadCallback callback) {
160 std::move(callback).Run(mojom::CdmFile::Status::kSuccess, {1, 2, 3});
162 EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 3));
164 base::RunLoop().RunUntilIdle();
167 TEST_F(MojoCdmFileIOTest, ReadBeforeOpen) {
168 // File not open, so reading should fail.
169 EXPECT_CALL(*client_.get(), OnReadComplete(Status::kError, _, _));
171 base::RunLoop().RunUntilIdle();
174 TEST_F(MojoCdmFileIOTest, TwoReads) {
175 const std::string kFileName = "readfile";
176 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
177 file_io_->Open(kFileName.data(), kFileName.length());
178 base::RunLoop().RunUntilIdle();
180 EXPECT_CALL(cdm_file_, Read(_))
181 .WillOnce([](mojom::CdmFile::ReadCallback callback) {
182 std::move(callback).Run(mojom::CdmFile::Status::kSuccess, {1, 2, 3, 4});
184 EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 4));
185 EXPECT_CALL(*client_.get(), OnReadComplete(Status::kInUse, _, 0));
188 base::RunLoop().RunUntilIdle();
191 TEST_F(MojoCdmFileIOTest, Write) {
192 const std::string kFileName = "writefile";
193 std::vector<uint8_t> data{1, 2, 3, 4, 5};
195 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
196 file_io_->Open(kFileName.data(), kFileName.length());
197 base::RunLoop().RunUntilIdle();
199 // Writing always succeeds.
200 EXPECT_CALL(cdm_file_, Write(_, _))
201 .WillOnce([](Unused, mojom::CdmFile::WriteCallback callback) {
202 std::move(callback).Run(mojom::CdmFile::Status::kSuccess);
204 EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kSuccess));
205 file_io_->Write(data.data(), data.size());
206 base::RunLoop().RunUntilIdle();
209 TEST_F(MojoCdmFileIOTest, WriteBeforeOpen) {
210 std::vector<uint8_t> data{1, 2, 3, 4, 5};
212 // File not open, so writing should fail.
213 EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kError));
214 file_io_->Write(data.data(), data.size());
215 base::RunLoop().RunUntilIdle();
218 TEST_F(MojoCdmFileIOTest, TwoWrites) {
219 const std::string kFileName = "writefile";
220 std::vector<uint8_t> data{1, 2, 3, 4, 5};
222 EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
223 file_io_->Open(kFileName.data(), kFileName.length());
224 base::RunLoop().RunUntilIdle();
226 EXPECT_CALL(cdm_file_, Write(_, _))
227 .WillOnce([](Unused, mojom::CdmFile::WriteCallback callback) {
228 std::move(callback).Run(mojom::CdmFile::Status::kSuccess);
230 EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kSuccess));
231 EXPECT_CALL(*client_.get(), OnWriteComplete(Status::kInUse));
232 file_io_->Write(data.data(), data.size());
233 file_io_->Write(data.data(), data.size());
234 base::RunLoop().RunUntilIdle();