[M120 Migration][MM][CAPI] Fix the logic for media using capi player.
[platform/framework/web/chromium-efl.git] / media / mojo / services / fuchsia_cdm_manager_unittest.cc
1 // Copyright 2020 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.
4
5 #include "media/mojo/services/fuchsia_cdm_manager.h"
6
7 #include <fuchsia/media/drm/cpp/fidl.h>
8 #include <fuchsia/media/drm/cpp/fidl_test_base.h>
9 #include <lib/fidl/cpp/binding_set.h>
10 #include <lib/fidl/cpp/interface_request.h>
11 #include <lib/fpromise/promise.h>
12 #include <map>
13
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/run_loop.h"
19 #include "base/test/bind.h"
20 #include "base/test/task_environment.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "url/gurl.h"
24 #include "url/origin.h"
25
26 namespace media {
27 namespace {
28
29 namespace drm = ::fuchsia::media::drm;
30
31 using ::testing::_;
32 using ::testing::Eq;
33 using ::testing::Invoke;
34 using ::testing::SaveArg;
35 using ::testing::WithArgs;
36
37 // This is a mock for the Chromium media::ProvisionFetcher (and not Fuchsia's
38 // similarly named ProvisioningFetcher protocol).
39 class MockProvisionFetcher : public ProvisionFetcher {
40  public:
41   MockProvisionFetcher() = default;
42   ~MockProvisionFetcher() override = default;
43
44   MOCK_METHOD(void,
45               Retrieve,
46               (const GURL& default_url,
47                const std::string& request_data,
48                ResponseCB response_cb),
49               (override));
50 };
51
52 std::unique_ptr<ProvisionFetcher> CreateMockProvisionFetcher() {
53   auto mock_provision_fetcher = std::make_unique<MockProvisionFetcher>();
54   ON_CALL(*mock_provision_fetcher, Retrieve(_, _, _))
55       .WillByDefault(WithArgs<2>(
56           Invoke([](ProvisionFetcher::ResponseCB response_callback) {
57             std::move(response_callback).Run(true, "response");
58           })));
59   return mock_provision_fetcher;
60 }
61
62 class MockKeySystem : public drm::testing::KeySystem_TestBase {
63  public:
64   MockKeySystem() = default;
65   ~MockKeySystem() override = default;
66
67   drm::KeySystemHandle AddBinding() { return bindings_.AddBinding(this); }
68   fidl::BindingSet<drm::KeySystem>& bindings() { return bindings_; }
69
70   void NotImplemented_(const std::string& name) override { FAIL() << name; }
71
72   MOCK_METHOD(void,
73               AddDataStore,
74               (uint32_t data_store_id,
75                drm::DataStoreParams params,
76                AddDataStoreCallback callback),
77               (override));
78   MOCK_METHOD(
79       void,
80       CreateContentDecryptionModule2,
81       (uint32_t data_store_id,
82        fidl::InterfaceRequest<drm::ContentDecryptionModule> cdm_request),
83       (override));
84
85  private:
86   fidl::BindingSet<drm::KeySystem> bindings_;
87 };
88
89 class FuchsiaCdmManagerTest : public ::testing::Test {
90  public:
91   FuchsiaCdmManagerTest() { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); }
92
93   std::unique_ptr<FuchsiaCdmManager> CreateFuchsiaCdmManager(
94       std::vector<base::StringPiece> key_systems,
95       absl::optional<uint64_t> cdm_data_quota_bytes = absl::nullopt) {
96     FuchsiaCdmManager::CreateKeySystemCallbackMap create_key_system_callbacks;
97
98     for (const base::StringPiece& name : key_systems) {
99       MockKeySystem& key_system = mock_key_systems_[name];
100       create_key_system_callbacks.emplace(
101           name, base::BindRepeating(&MockKeySystem::AddBinding,
102                                     base::Unretained(&key_system)));
103     }
104     return std::make_unique<FuchsiaCdmManager>(
105         std::move(create_key_system_callbacks), temp_dir_.GetPath(),
106         cdm_data_quota_bytes);
107   }
108
109  protected:
110   using MockKeySystemMap = std::map<base::StringPiece, MockKeySystem>;
111
112   MockKeySystem& mock_key_system(const base::StringPiece& key_system_name) {
113     return mock_key_systems_[key_system_name];
114   }
115
116   base::test::TaskEnvironment task_environment_{
117       base::test::TaskEnvironment::MainThreadType::IO};
118
119   MockKeySystemMap mock_key_systems_;
120   base::ScopedTempDir temp_dir_;
121 };
122
123 TEST_F(FuchsiaCdmManagerTest, NoKeySystems) {
124   std::unique_ptr<FuchsiaCdmManager> cdm_manager = CreateFuchsiaCdmManager({});
125
126   base::RunLoop run_loop;
127   drm::ContentDecryptionModulePtr cdm_ptr;
128   cdm_ptr.set_error_handler([&](zx_status_t status) {
129     EXPECT_EQ(status, ZX_ERR_NOT_FOUND);
130     run_loop.Quit();
131   });
132
133   cdm_manager->CreateAndProvision(
134       "com.key_system", url::Origin(),
135       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
136   run_loop.Run();
137 }
138
139 TEST_F(FuchsiaCdmManagerTest, CreateAndProvision) {
140   constexpr char kKeySystem[] = "com.key_system.a";
141   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
142       CreateFuchsiaCdmManager({kKeySystem});
143
144   base::RunLoop run_loop;
145   drm::ContentDecryptionModulePtr cdm_ptr;
146   cdm_ptr.set_error_handler([&](zx_status_t status) { run_loop.Quit(); });
147
148   uint32_t added_data_store_id = 0;
149   uint32_t cdm_data_store_id = 0;
150   EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(_, _, _))
151       .WillOnce(WithArgs<0, 2>(
152           Invoke([&](uint32_t data_store_id,
153                      drm::KeySystem::AddDataStoreCallback callback) {
154             added_data_store_id = data_store_id;
155             callback(fpromise::ok());
156           })));
157
158   EXPECT_CALL(mock_key_system(kKeySystem), CreateContentDecryptionModule2(_, _))
159       .WillOnce(SaveArg<0>(&cdm_data_store_id));
160
161   cdm_manager->CreateAndProvision(
162       kKeySystem, url::Origin(),
163       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
164   run_loop.Run();
165
166   EXPECT_NE(added_data_store_id, 0u);
167   EXPECT_EQ(added_data_store_id, cdm_data_store_id);
168 }
169
170 TEST_F(FuchsiaCdmManagerTest, RecreateAfterDisconnect) {
171   constexpr char kKeySystem[] = "com.key_system.a";
172   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
173       CreateFuchsiaCdmManager({kKeySystem});
174
175   uint32_t added_data_store_id = 0;
176   EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(_, _, _))
177       .WillOnce(WithArgs<0, 2>(
178           Invoke([&](uint32_t data_store_id,
179                      drm::KeySystem::AddDataStoreCallback callback) {
180             added_data_store_id = data_store_id;
181             callback(fpromise::ok());
182           })));
183
184   // Create a CDM to force a KeySystem binding
185   base::RunLoop create_run_loop;
186   drm::ContentDecryptionModulePtr cdm_ptr;
187   cdm_ptr.set_error_handler(
188       [&](zx_status_t status) { create_run_loop.Quit(); });
189   cdm_manager->CreateAndProvision(
190       kKeySystem, url::Origin(),
191       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
192   create_run_loop.Run();
193   ASSERT_EQ(mock_key_system(kKeySystem).bindings().size(), 1u);
194
195   // Close the KeySystem's bindings and wait until empty
196   base::RunLoop disconnect_run_loop;
197   cdm_manager->set_on_key_system_disconnect_for_test_callback(
198       base::BindLambdaForTesting([&](const std::string& key_system_name) {
199         if (key_system_name == kKeySystem) {
200           disconnect_run_loop.Quit();
201         }
202       }));
203   mock_key_system(kKeySystem).bindings().CloseAll();
204   disconnect_run_loop.Run();
205   ASSERT_EQ(mock_key_system(kKeySystem).bindings().size(), 0u);
206
207   EXPECT_CALL(mock_key_system(kKeySystem),
208               AddDataStore(Eq(added_data_store_id), _, _))
209       .WillOnce(
210           WithArgs<2>(Invoke([](drm::KeySystem::AddDataStoreCallback callback) {
211             callback(fpromise::ok());
212           })));
213
214   base::RunLoop recreate_run_loop;
215   cdm_ptr.set_error_handler(
216       [&](zx_status_t status) { recreate_run_loop.Quit(); });
217   cdm_manager->CreateAndProvision(
218       kKeySystem, url::Origin(),
219       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
220   recreate_run_loop.Run();
221   EXPECT_EQ(mock_key_system(kKeySystem).bindings().size(), 1u);
222 }
223
224 TEST_F(FuchsiaCdmManagerTest, SameOriginShareDataStore) {
225   constexpr char kKeySystem[] = "com.key_system.a";
226   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
227       CreateFuchsiaCdmManager({kKeySystem});
228
229   base::RunLoop run_loop;
230   drm::ContentDecryptionModulePtr cdm1, cdm2;
231   auto error_handler = [&](zx_status_t status) {
232     EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
233     if (!cdm1.is_bound() && !cdm2.is_bound()) {
234       run_loop.Quit();
235     }
236   };
237   cdm1.set_error_handler(error_handler);
238   cdm2.set_error_handler(error_handler);
239
240   EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(Eq(1u), _, _))
241       .WillOnce(
242           WithArgs<2>(Invoke([](drm::KeySystem::AddDataStoreCallback callback) {
243             callback(fpromise::ok());
244           })));
245   EXPECT_CALL(mock_key_system(kKeySystem),
246               CreateContentDecryptionModule2(Eq(1u), _))
247       .Times(2);
248
249   url::Origin origin = url::Origin::Create(GURL("http://origin_a.com"));
250   cdm_manager->CreateAndProvision(
251       kKeySystem, origin, base::BindRepeating(&CreateMockProvisionFetcher),
252       cdm1.NewRequest());
253   cdm_manager->CreateAndProvision(
254       kKeySystem, origin, base::BindRepeating(&CreateMockProvisionFetcher),
255       cdm2.NewRequest());
256
257   run_loop.Run();
258 }
259
260 TEST_F(FuchsiaCdmManagerTest, DifferentOriginDoNotShareDataStore) {
261   constexpr char kKeySystem[] = "com.key_system.a";
262   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
263       CreateFuchsiaCdmManager({kKeySystem});
264
265   base::RunLoop run_loop;
266   drm::ContentDecryptionModulePtr cdm1, cdm2;
267   auto error_handler = [&](zx_status_t status) {
268     EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
269     if (!cdm1.is_bound() && !cdm2.is_bound()) {
270       run_loop.Quit();
271     }
272   };
273   cdm1.set_error_handler(error_handler);
274   cdm2.set_error_handler(error_handler);
275
276   EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(Eq(1u), _, _))
277       .WillOnce(
278           WithArgs<2>(Invoke([](drm::KeySystem::AddDataStoreCallback callback) {
279             callback(fpromise::ok());
280           })));
281   EXPECT_CALL(mock_key_system(kKeySystem), AddDataStore(Eq(2u), _, _))
282       .WillOnce(
283           WithArgs<2>(Invoke([](drm::KeySystem::AddDataStoreCallback callback) {
284             callback(fpromise::ok());
285           })));
286   EXPECT_CALL(mock_key_system(kKeySystem),
287               CreateContentDecryptionModule2(Eq(1u), _))
288       .Times(1);
289   EXPECT_CALL(mock_key_system(kKeySystem),
290               CreateContentDecryptionModule2(Eq(2u), _))
291       .Times(1);
292
293   url::Origin origin_a = url::Origin::Create(GURL("http://origin_a.com"));
294   url::Origin origin_b = url::Origin::Create(GURL("http://origin_b.com"));
295   cdm_manager->CreateAndProvision(
296       kKeySystem, origin_a, base::BindRepeating(&CreateMockProvisionFetcher),
297       cdm1.NewRequest());
298   cdm_manager->CreateAndProvision(
299       kKeySystem, origin_b, base::BindRepeating(&CreateMockProvisionFetcher),
300       cdm2.NewRequest());
301
302   run_loop.Run();
303 }
304
305 void CreateDummyCdmDirectory(const base::FilePath& cdm_data_path,
306                              base::StringPiece origin,
307                              base::StringPiece key_system,
308                              uint64_t size) {
309   const base::FilePath path = cdm_data_path.Append(origin).Append(key_system);
310   CHECK(base::CreateDirectory(path));
311   if (size) {
312     std::vector<uint8_t> zeroes(size);
313     CHECK(base::WriteFile(path.Append("zeroes"), zeroes));
314   }
315 }
316
317 // Verify that the least recently used CDM data directories are removed, until
318 // the quota is met.  Also verify that old directories are removed regardless
319 // of whether they are empty or not.
320 TEST_F(FuchsiaCdmManagerTest, CdmDataQuotaBytes) {
321   constexpr uint64_t kTestQuotaBytes = 1024;
322   constexpr char kOriginDirectory1[] = "origin1";
323   constexpr char kOriginDirectory2[] = "origin2";
324   constexpr char kKeySystemDirectory1[] = "key_system1";
325   constexpr char kKeySystemDirectory2[] = "key_system2";
326   constexpr char kEmptyKeySystemDirectory[] = "empty_key_system";
327
328   // Create fake CDM data directories for two origins, each with two key
329   // systems, with each directory consuming 50% of the total quota, so that
330   // two directories must be removed to meet quota.
331
332   // Create least-recently-used directories & their contents.
333   const base::FilePath temp_path = temp_dir_.GetPath();
334   CreateDummyCdmDirectory(temp_path, kOriginDirectory1, kKeySystemDirectory1,
335                           kTestQuotaBytes / 2);
336   CreateDummyCdmDirectory(temp_path, kOriginDirectory2, kKeySystemDirectory2,
337                           kTestQuotaBytes / 2);
338   CreateDummyCdmDirectory(temp_path, kOriginDirectory1,
339                           kEmptyKeySystemDirectory, 0);
340
341   // Sleep to account for coarse-grained filesystem timestamps.
342   base::PlatformThread::Sleep(base::Seconds(1));
343
344   // Create the recently-used directories.
345   CreateDummyCdmDirectory(temp_path, kOriginDirectory1, kKeySystemDirectory2,
346                           kTestQuotaBytes / 2);
347   CreateDummyCdmDirectory(temp_path, kOriginDirectory2, kKeySystemDirectory1,
348                           kTestQuotaBytes / 2);
349   CreateDummyCdmDirectory(temp_path, kOriginDirectory2,
350                           kEmptyKeySystemDirectory, 0);
351
352   // Create the CDM manager, to run the data directory quota enforcement.
353   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
354       CreateFuchsiaCdmManager({}, kTestQuotaBytes);
355
356   // Use a CreateAndProvision() request as a proxy to wait for quota enforcement
357   // to finish being applied.
358   base::RunLoop run_loop;
359   drm::ContentDecryptionModulePtr cdm_ptr;
360   cdm_ptr.set_error_handler([&](zx_status_t status) {
361     EXPECT_EQ(status, ZX_ERR_NOT_FOUND);
362     run_loop.Quit();
363   });
364
365   cdm_manager->CreateAndProvision(
366       "com.key_system", url::Origin(),
367       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
368   run_loop.Run();
369
370   EXPECT_FALSE(base::PathExists(
371       temp_path.Append(kOriginDirectory1).Append(kKeySystemDirectory1)));
372   EXPECT_FALSE(base::PathExists(
373       temp_path.Append(kOriginDirectory2).Append(kKeySystemDirectory2)));
374
375   EXPECT_TRUE(base::PathExists(
376       temp_path.Append(kOriginDirectory1).Append(kKeySystemDirectory2)));
377   EXPECT_TRUE(base::PathExists(
378       temp_path.Append(kOriginDirectory2).Append(kKeySystemDirectory1)));
379
380   // Empty directories are currently always treated as old, causing them all to
381   // be deleted if the CDM data directory exceeds its quota.
382   EXPECT_FALSE(base::PathExists(
383       temp_path.Append(kOriginDirectory1).Append(kEmptyKeySystemDirectory)));
384   EXPECT_FALSE(base::PathExists(
385       temp_path.Append(kOriginDirectory2).Append(kEmptyKeySystemDirectory)));
386 }
387
388 // Verify that if all key-system sub-directories for a given origin have been
389 // deleted then the origin's directory is also deleted.
390 TEST_F(FuchsiaCdmManagerTest, EmptyOriginDirectory) {
391   constexpr uint64_t kTestQuotaBytes = 1024;
392   constexpr char kInactiveOriginDirectory[] = "origin1";
393   constexpr char kActiveOriginDirectory[] = "origin2";
394   constexpr char kKeySystemDirectory1[] = "key_system1";
395   constexpr char kKeySystemDirectory2[] = "key_system2";
396
397   // Create dummy data for an inactive origin.
398   const base::FilePath temp_path = temp_dir_.GetPath();
399   CreateDummyCdmDirectory(temp_path, kInactiveOriginDirectory,
400                           kKeySystemDirectory1, kTestQuotaBytes / 2);
401   CreateDummyCdmDirectory(temp_path, kInactiveOriginDirectory,
402                           kKeySystemDirectory2, kTestQuotaBytes / 2);
403
404   // Sleep to account for coarse-grained filesystem timestamps.
405   base::PlatformThread::Sleep(base::Seconds(1));
406
407   // Create dummy data for a recently-used, active origin.
408   CreateDummyCdmDirectory(temp_path, kActiveOriginDirectory,
409                           kKeySystemDirectory2, kTestQuotaBytes);
410
411   // Create the CDM manager, to run the data directory quota enforcement.
412   std::unique_ptr<FuchsiaCdmManager> cdm_manager =
413       CreateFuchsiaCdmManager({}, kTestQuotaBytes);
414
415   // Use a CreateAndProvision() request as a proxy to wait for quota enforcement
416   // to finish being applied.
417   base::RunLoop run_loop;
418   drm::ContentDecryptionModulePtr cdm_ptr;
419   cdm_ptr.set_error_handler([&](zx_status_t status) {
420     EXPECT_EQ(status, ZX_ERR_NOT_FOUND);
421     run_loop.Quit();
422   });
423
424   cdm_manager->CreateAndProvision(
425       "com.key_system", url::Origin(),
426       base::BindRepeating(&CreateMockProvisionFetcher), cdm_ptr.NewRequest());
427   run_loop.Run();
428
429   EXPECT_FALSE(base::PathExists(temp_path.Append(kInactiveOriginDirectory)));
430   EXPECT_TRUE(base::PathExists(
431       temp_path.Append(kActiveOriginDirectory).Append(kKeySystemDirectory2)));
432 }
433
434 }  // namespace
435 }  // namespace media