chromium/chromeos/components/cdm_factory_daemon/content_decryption_module_adapter_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chromeos/components/cdm_factory_daemon/content_decryption_module_adapter.h"

#include <algorithm>

#include "base/logging.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "media/base/decoder_buffer.h"
#include "media/base/mock_filters.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using DaemonCdm = chromeos::cdm::mojom::ContentDecryptionModule;
using testing::_;
using testing::IsNull;

namespace chromeos {

namespace {

constexpr char kFakeEmeInitData[] = "fake_init_data";
const std::vector<uint8_t> kFakeEncryptedData = {42, 22, 26, 13, 7, 16, 8, 2};
constexpr char kFakeKeyId[] = "fake_key_id";
constexpr char kFakeIv[] = "fake_iv_16_bytes";
constexpr char kFakeServiceCertificate[] = "fake_service_cert";
constexpr char kFakeSessionId1[] = "fakeSid1";
constexpr char kFakeSessionId2[] = "fakeSid2";
constexpr char kFakeSessionUpdate[] = "fake_session_update";

constexpr int64_t kFakeTimestampSec = 42;
constexpr int64_t kFakeDurationSec = 64;
constexpr uint64_t kFakeSecureHandle = 75;

template <size_t size>
std::vector<uint8_t> ToVector(const char (&array)[size]) {
  return std::vector<uint8_t>(array, array + size - 1);
}

MATCHER_P(MatchesDecoderBuffer, buffer, "") {
  DCHECK(arg);
  return arg->MatchesForTesting(*buffer);
}

MATCHER_P(MatchesDecryptConfig, config, "") {
  if (!arg && !*config)
    return true;
  DCHECK(arg);
  return arg->Matches(**config);
}

// Mock of the mojo implementation on the Chrome OS side.
class MockDaemonCdm : public cdm::mojom::ContentDecryptionModule {
 public:
  MockDaemonCdm(mojo::PendingAssociatedReceiver<ContentDecryptionModule>
                    pending_receiver) {
    receiver_.Bind(std::move(pending_receiver));
  }
  ~MockDaemonCdm() override = default;

  MOCK_METHOD(void,
              SetServerCertificate,
              (const std::vector<uint8_t>&, SetServerCertificateCallback));
  MOCK_METHOD(void,
              GetStatusForPolicy,
              (media::HdcpVersion, GetStatusForPolicyCallback));
  MOCK_METHOD(void,
              CreateSessionAndGenerateRequest,
              (media::CdmSessionType,
               media::EmeInitDataType,
               const std::vector<uint8_t>&,
               CreateSessionAndGenerateRequestCallback));
  MOCK_METHOD(void,
              LoadSession,
              (media::CdmSessionType, const std::string&, LoadSessionCallback));
  MOCK_METHOD(void,
              UpdateSession,
              (const std::string&,
               const std::vector<uint8_t>&,
               UpdateSessionCallback));
  MOCK_METHOD(void, CloseSession, (const std::string&, CloseSessionCallback));
  MOCK_METHOD(void, RemoveSession, (const std::string&, RemoveSessionCallback));
  MOCK_METHOD(void,
              DecryptDeprecated,
              (const std::vector<uint8_t>&,
               std::unique_ptr<media::DecryptConfig>,
               DecryptDeprecatedCallback));
  MOCK_METHOD(void,
              Decrypt,
              (const std::vector<uint8_t>&,
               std::unique_ptr<media::DecryptConfig>,
               bool,
               uint64_t,
               DecryptCallback));
  MOCK_METHOD(void,
              GetHwKeyData,
              (std::unique_ptr<media::DecryptConfig>,
               const std::vector<uint8_t>&,
               GetHwKeyDataCallback));

 private:
  mojo::AssociatedReceiver<ContentDecryptionModule> receiver_{this};
};

cdm::mojom::CdmPromiseResultPtr CreatePromise(bool success) {
  cdm::mojom::CdmPromiseResultPtr promise = cdm::mojom::CdmPromiseResult::New();
  promise->success = success;
  if (!success) {
    promise->error_message = "error";
  }
  return promise;
}

scoped_refptr<media::DecoderBuffer> CreateDecoderBuffer(
    base::span<const uint8_t> data) {
  scoped_refptr<media::DecoderBuffer> buffer =
      media::DecoderBuffer::CopyFrom(data);
  buffer->set_timestamp(base::Seconds(kFakeTimestampSec));
  buffer->set_duration(base::Seconds(kFakeDurationSec));
  return buffer;
}

}  // namespace

class ContentDecryptionModuleAdapterTest : public testing::Test {
 protected:
  ContentDecryptionModuleAdapterTest() {
    mojo::AssociatedRemote<cdm::mojom::ContentDecryptionModule> daemon_cdm_mojo;
    mock_daemon_cdm_ = std::make_unique<MockDaemonCdm>(
        daemon_cdm_mojo.BindNewEndpointAndPassDedicatedReceiver());
    cdm_adapter_ = base::WrapRefCounted<ContentDecryptionModuleAdapter>(
        new ContentDecryptionModuleAdapter(
            nullptr /* storage */, std::move(daemon_cdm_mojo),
            mock_session_message_cb_.Get(), mock_session_closed_cb_.Get(),
            mock_session_keys_change_cb_.Get(),
            mock_session_expiration_update_cb_.Get()));
  }

  ~ContentDecryptionModuleAdapterTest() override {
    // Destroy the CdmAdapter first so it can invoke any session closed
    // callbacks on destruction before we destroy the callback mockers.
    cdm_adapter_.reset();
  }

  void LoadSession() {
    EXPECT_CALL(*mock_daemon_cdm_,
                LoadSession(media::CdmSessionType::kPersistentLicense,
                            kFakeSessionId1, _))
        .WillOnce([](media::CdmSessionType session_type,
                     const std::string& session_id,
                     MockDaemonCdm::LoadSessionCallback callback) {
          std::move(callback).Run(CreatePromise(true), kFakeSessionId2);
        });
    std::string session_id;
    std::unique_ptr<media::MockCdmSessionPromise> promise =
        std::make_unique<media::MockCdmSessionPromise>(true, &session_id);
    cdm_adapter_->LoadSession(media::CdmSessionType::kPersistentLicense,
                              kFakeSessionId1, std::move(promise));
    base::RunLoop().RunUntilIdle();
    EXPECT_EQ(session_id, kFakeSessionId2);
  }

  scoped_refptr<ContentDecryptionModuleAdapter> cdm_adapter_;
  std::unique_ptr<MockDaemonCdm> mock_daemon_cdm_;

  base::MockCallback<media::SessionMessageCB> mock_session_message_cb_;
  base::MockCallback<media::SessionClosedCB> mock_session_closed_cb_;
  base::MockCallback<media::SessionKeysChangeCB> mock_session_keys_change_cb_;
  base::MockCallback<media::SessionExpirationUpdateCB>
      mock_session_expiration_update_cb_;

 private:
  base::test::TaskEnvironment task_environment_;
};

TEST_F(ContentDecryptionModuleAdapterTest, GetClientInterface) {
  auto pending_remote = cdm_adapter_->GetClientInterface();
  EXPECT_TRUE(pending_remote.is_valid());
  // If we try to get it again, it should fail.
  ASSERT_DEATH(cdm_adapter_->GetClientInterface(), "");
}

TEST_F(ContentDecryptionModuleAdapterTest, SetServerCertificate_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_,
              SetServerCertificate(ToVector(kFakeServiceCertificate), _))
      .WillOnce([](const std::vector<uint8_t>& cert,
                   MockDaemonCdm::SetServerCertificateCallback callback) {
        std::move(callback).Run(CreatePromise(false));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(false);
  cdm_adapter_->SetServerCertificate(ToVector(kFakeServiceCertificate),
                                     std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, SetServerCertificate_Success) {
  EXPECT_CALL(*mock_daemon_cdm_,
              SetServerCertificate(ToVector(kFakeServiceCertificate), _))
      .WillOnce([](const std::vector<uint8_t>& cert,
                   MockDaemonCdm::SetServerCertificateCallback callback) {
        std::move(callback).Run(CreatePromise(true));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(true);
  cdm_adapter_->SetServerCertificate(ToVector(kFakeServiceCertificate),
                                     std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, GetStatusForPolicy_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_,
              GetStatusForPolicy(media::HdcpVersion::kHdcpVersion1_4, _))
      .WillOnce([](media::HdcpVersion hdcp_version,
                   MockDaemonCdm::GetStatusForPolicyCallback callback) {
        std::move(callback).Run(
            CreatePromise(false),
            media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
      });
  media::CdmKeyInformation::KeyStatus key_status;
  std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
      std::make_unique<media::MockCdmKeyStatusPromise>(false, &key_status);
  cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion1_4,
                                   std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest,
       GetStatusForPolicy_SuccessRestricted) {
  EXPECT_CALL(*mock_daemon_cdm_,
              GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_2, _))
      .WillOnce([](media::HdcpVersion hdcp_version,
                   MockDaemonCdm::GetStatusForPolicyCallback callback) {
        std::move(callback).Run(
            CreatePromise(true),
            media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
      });
  media::CdmKeyInformation::KeyStatus key_status;
  std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
      std::make_unique<media::MockCdmKeyStatusPromise>(true, &key_status);
  cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_2,
                                   std::move(promise));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(key_status, media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
}

TEST_F(ContentDecryptionModuleAdapterTest, GetStatusForPolicy_SuccessUsable) {
  EXPECT_CALL(*mock_daemon_cdm_,
              GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_0, _))
      .WillOnce([](media::HdcpVersion hdcp_version,
                   MockDaemonCdm::GetStatusForPolicyCallback callback) {
        std::move(callback).Run(CreatePromise(true),
                                media::CdmKeyInformation::KeyStatus::USABLE);
      });
  media::CdmKeyInformation::KeyStatus key_status;
  std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
      std::make_unique<media::MockCdmKeyStatusPromise>(true, &key_status);
  cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_0,
                                   std::move(promise));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(key_status, media::CdmKeyInformation::KeyStatus::USABLE);
}

TEST_F(ContentDecryptionModuleAdapterTest,
       CreateSessionAndGenerateRequest_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_,
              CreateSessionAndGenerateRequest(media::CdmSessionType::kTemporary,
                                              media::EmeInitDataType::CENC,
                                              ToVector(kFakeEmeInitData), _))
      .WillOnce(
          [](media::CdmSessionType session_type,
             media::EmeInitDataType init_type,
             const std::vector<uint8_t>& init_data,
             MockDaemonCdm::CreateSessionAndGenerateRequestCallback callback) {
            std::move(callback).Run(CreatePromise(false), "");
          });
  std::string session_id;
  std::unique_ptr<media::MockCdmSessionPromise> promise =
      std::make_unique<media::MockCdmSessionPromise>(false, &session_id);
  cdm_adapter_->CreateSessionAndGenerateRequest(
      media::CdmSessionType::kTemporary, media::EmeInitDataType::CENC,
      ToVector(kFakeEmeInitData), std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest,
       CreateSessionAndGenerateRequest_Success) {
  EXPECT_CALL(*mock_daemon_cdm_,
              CreateSessionAndGenerateRequest(media::CdmSessionType::kTemporary,
                                              media::EmeInitDataType::CENC,
                                              ToVector(kFakeEmeInitData), _))
      .WillOnce(
          [](media::CdmSessionType session_type,
             media::EmeInitDataType init_type,
             const std::vector<uint8_t>& init_data,
             MockDaemonCdm::CreateSessionAndGenerateRequestCallback callback) {
            std::move(callback).Run(CreatePromise(true), kFakeSessionId1);
          });
  std::string session_id;
  std::unique_ptr<media::MockCdmSessionPromise> promise =
      std::make_unique<media::MockCdmSessionPromise>(true, &session_id);
  cdm_adapter_->CreateSessionAndGenerateRequest(
      media::CdmSessionType::kTemporary, media::EmeInitDataType::CENC,
      ToVector(kFakeEmeInitData), std::move(promise));
  // We should also be getting a session closed callback for any open sessions.
  EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId1, _));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(session_id, kFakeSessionId1);
}

TEST_F(ContentDecryptionModuleAdapterTest, LoadSession_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_,
              LoadSession(media::CdmSessionType::kPersistentLicense,
                          kFakeSessionId1, _))
      .WillOnce([](media::CdmSessionType session_type,
                   const std::string& session_id,
                   MockDaemonCdm::LoadSessionCallback callback) {
        std::move(callback).Run(CreatePromise(false), "");
      });
  std::string session_id;
  std::unique_ptr<media::MockCdmSessionPromise> promise =
      std::make_unique<media::MockCdmSessionPromise>(false, &session_id);
  cdm_adapter_->LoadSession(media::CdmSessionType::kPersistentLicense,
                            kFakeSessionId1, std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, LoadSession_Success) {
  LoadSession();
  // We should also be getting a session closed callback for any open sessions.
  EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId2, _));
}

TEST_F(ContentDecryptionModuleAdapterTest, UpdateSession_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_,
              UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate), _))
      .WillOnce([](const std::string& session_id,
                   const std::vector<uint8_t>& response,
                   MockDaemonCdm::UpdateSessionCallback callback) {
        std::move(callback).Run(CreatePromise(false));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(false);
  cdm_adapter_->UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate),
                              std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, UpdateSession_Success) {
  EXPECT_CALL(*mock_daemon_cdm_,
              UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate), _))
      .WillOnce([](const std::string& session_id,
                   const std::vector<uint8_t>& response,
                   MockDaemonCdm::UpdateSessionCallback callback) {
        std::move(callback).Run(CreatePromise(true));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(true);
  cdm_adapter_->UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate),
                              std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, CloseSession_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_, CloseSession(kFakeSessionId1, _))
      .WillOnce([](const std::string& session_id,
                   MockDaemonCdm::CloseSessionCallback callback) {
        std::move(callback).Run(CreatePromise(false));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(false);
  cdm_adapter_->CloseSession(kFakeSessionId1, std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, CloseSession_Success) {
  EXPECT_CALL(*mock_daemon_cdm_, CloseSession(kFakeSessionId1, _))
      .WillOnce([](const std::string& session_id,
                   MockDaemonCdm::CloseSessionCallback callback) {
        std::move(callback).Run(CreatePromise(true));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(true);
  cdm_adapter_->CloseSession(kFakeSessionId1, std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, RemoveSession_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_, RemoveSession(kFakeSessionId1, _))
      .WillOnce([](const std::string& session_id,
                   MockDaemonCdm::RemoveSessionCallback callback) {
        std::move(callback).Run(CreatePromise(false));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(false);
  cdm_adapter_->RemoveSession(kFakeSessionId1, std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, RemoveSession_Success) {
  EXPECT_CALL(*mock_daemon_cdm_, RemoveSession(kFakeSessionId1, _))
      .WillOnce([](const std::string& session_id,
                   MockDaemonCdm::RemoveSessionCallback callback) {
        std::move(callback).Run(CreatePromise(true));
      });
  std::unique_ptr<media::MockCdmPromise> promise =
      std::make_unique<media::MockCdmPromise>(true);
  cdm_adapter_->RemoveSession(kFakeSessionId1, std::move(promise));
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, OnSessionMessage) {
  EXPECT_CALL(mock_session_message_cb_,
              Run(kFakeSessionId1, media::CdmMessageType::LICENSE_REQUEST,
                  ToVector(kFakeSessionUpdate)));
  cdm_adapter_->OnSessionMessage(kFakeSessionId1,
                                 media::CdmMessageType::LICENSE_REQUEST,
                                 ToVector(kFakeSessionUpdate));
}

TEST_F(ContentDecryptionModuleAdapterTest, OnSessionClosed) {
  LoadSession();
  EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId2, _));
  cdm_adapter_->OnSessionClosed(kFakeSessionId2);
}

TEST_F(ContentDecryptionModuleAdapterTest, OnSessionKeysChange) {
  EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, true, _));
  cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, true, {});
}

TEST_F(ContentDecryptionModuleAdapterTest, OnSessionExpirationUpdate) {
  constexpr double kFakeExpiration = 123456;
  EXPECT_CALL(mock_session_expiration_update_cb_,
              Run(kFakeSessionId2,
                  base::Time::FromSecondsSinceUnixEpoch(kFakeExpiration)));
  cdm_adapter_->OnSessionExpirationUpdate(kFakeSessionId2, kFakeExpiration);
}

TEST_F(ContentDecryptionModuleAdapterTest, RegisterNewKeyCB) {
  base::MockCallback<media::CdmContext::EventCB> event_cb_1;
  base::MockCallback<media::CdmContext::EventCB> event_cb_2;
  auto cb_registration_1 = cdm_adapter_->RegisterEventCB(event_cb_1.Get());
  auto cb_registration_2 = cdm_adapter_->RegisterEventCB(event_cb_2.Get());

  // All registered event callbacks should be invoked.
  EXPECT_CALL(event_cb_1,
              Run(media::CdmContext::Event::kHasAdditionalUsableKey));
  EXPECT_CALL(event_cb_2,
              Run(media::CdmContext::Event::kHasAdditionalUsableKey));
  EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, true, _));
  cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, true, {});
  base::RunLoop().RunUntilIdle();

  // If no keys change, no registered event callbacks should be invoked.
  EXPECT_CALL(event_cb_1, Run(_)).Times(0);
  EXPECT_CALL(event_cb_2, Run(_)).Times(0);
  EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, false, _));
  cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, false, {});
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_TranscryptUnencrypted) {
  std::unique_ptr<media::DecryptConfig> expected_decrypt_config;
  EXPECT_CALL(
      *mock_daemon_cdm_,
      Decrypt(kFakeEncryptedData,
              MatchesDecryptConfig(&expected_decrypt_config), true, 0, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        // Simulate transcryption (that's the only reason this is called with
        // clear data), by just reversing the data.
        std::vector<uint8_t> decrypted = data;
        std::reverse(std::begin(decrypted), std::end(decrypted));
        std::unique_ptr<media::DecryptConfig> transcrypt_config =
            media::DecryptConfig::CreateCencConfig(
                kFakeKeyId, std::string(16, '0'),
                {media::SubsampleEntry(3, 5)});
        std::move(callback).Run(media::Decryptor::kSuccess,
                                std::move(decrypted),
                                std::move(transcrypt_config));
      });
  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_is_key_frame(true);
  std::vector<uint8_t> transcrypted_data = kFakeEncryptedData;
  std::reverse(std::begin(transcrypted_data), std::end(transcrypted_data));
  scoped_refptr<media::DecoderBuffer> transcrypted_buffer =
      CreateDecoderBuffer(transcrypted_data);
  transcrypted_buffer->set_decrypt_config(
      media::DecryptConfig::CreateCencConfig(kFakeKeyId, std::string(16, '0'),
                                             {media::SubsampleEntry(3, 5)}));
  transcrypted_buffer->set_is_key_frame(true);
  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
                            MatchesDecoderBuffer(transcrypted_buffer)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_Failure) {
  EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _, _, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        std::move(callback).Run(media::Decryptor::kError, {}, nullptr);
      });
  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kError, IsNull()));
  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
      kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_NoKey) {
  EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _, _, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        std::move(callback).Run(media::Decryptor::kNoKey, {}, nullptr);
      });
  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kNoKey, IsNull()));
  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
      kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_MismatchedSubsamples) {
  EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _, _, _)).Times(0);
  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kError, IsNull()));
  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCencConfig(
      kFakeKeyId, kFakeIv, {media::SubsampleEntry(1, 1)}));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_Success) {
  std::unique_ptr<media::DecryptConfig> expected_decrypt_config =
      media::DecryptConfig::CreateCbcsConfig(kFakeKeyId, kFakeIv, {},
                                             media::EncryptionPattern(6, 9));
  EXPECT_CALL(
      *mock_daemon_cdm_,
      Decrypt(kFakeEncryptedData,
              MatchesDecryptConfig(&expected_decrypt_config), true, 0, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        // For decryption, just reverse the data.
        std::vector<uint8_t> decrypted = data;
        std::reverse(std::begin(decrypted), std::end(decrypted));
        std::move(callback).Run(media::Decryptor::kSuccess,
                                std::move(decrypted), nullptr);
      });
  std::vector<uint8_t> decrypted_data = kFakeEncryptedData;
  std::reverse(std::begin(decrypted_data), std::end(decrypted_data));
  scoped_refptr<media::DecoderBuffer> decrypted_buffer =
      CreateDecoderBuffer(decrypted_data);
  decrypted_buffer->set_is_key_frame(true);

  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
                            MatchesDecoderBuffer(decrypted_buffer)));
  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_is_key_frame(true);
  encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
      kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_SecureHandleUnencrypted) {
  std::unique_ptr<media::DecryptConfig> no_config;
  EXPECT_CALL(*mock_daemon_cdm_,
              Decrypt(kFakeEncryptedData, MatchesDecryptConfig(&no_config),
                      true, kFakeSecureHandle, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        // For secure handles, there is no decrypted data returned.
        std::move(callback).Run(media::Decryptor::kSuccess, {}, nullptr);
      });

  base::MockCallback<media::Decryptor::DecryptCB> callback;
  scoped_refptr<media::DecoderBuffer> clear_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  clear_buffer->set_is_key_frame(true);
  clear_buffer->WritableSideData().secure_handle = kFakeSecureHandle;
  EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
                            MatchesDecoderBuffer(clear_buffer)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, clear_buffer, callback.Get());
  base::RunLoop().RunUntilIdle();
}

TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_SecureHandleEncrypted) {
  std::unique_ptr<media::DecryptConfig> expected_decrypt_config =
      media::DecryptConfig::CreateCbcsConfig(kFakeKeyId, kFakeIv, {},
                                             media::EncryptionPattern(6, 9));
  EXPECT_CALL(*mock_daemon_cdm_,
              Decrypt(kFakeEncryptedData,
                      MatchesDecryptConfig(&expected_decrypt_config), true,
                      kFakeSecureHandle, _))
      .WillOnce([](const std::vector<uint8_t>& data,
                   std::unique_ptr<media::DecryptConfig> decrypt_config,
                   bool is_video, uint64_t secure_handle,
                   MockDaemonCdm::DecryptCallback callback) {
        // For secure handles, there is no decrypted data returned.
        std::move(callback).Run(media::Decryptor::kSuccess, {}, nullptr);
      });

  scoped_refptr<media::DecoderBuffer> encrypted_buffer =
      CreateDecoderBuffer(kFakeEncryptedData);
  encrypted_buffer->set_is_key_frame(true);
  encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
      kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
  encrypted_buffer->WritableSideData().secure_handle = kFakeSecureHandle;
  base::MockCallback<media::Decryptor::DecryptCB> callback;
  EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
                            MatchesDecoderBuffer(encrypted_buffer)));
  cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
                        callback.Get());
  base::RunLoop().RunUntilIdle();
}

}  // namespace chromeos