chromium/ui/display/manager/content_protection_key_manager_unittest.cc

// 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 "ui/display/manager/content_protection_key_manager.h"

#include <memory>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display_features.h"
#include "ui/display/manager/test/fake_display_snapshot.h"
#include "ui/display/manager/test/test_native_display_delegate.h"
#include "ui/display/types/display_constants.h"

namespace display::test {

namespace {
constexpr int64_t kDisplayId = 123;
const DisplayMode kDisplayMode({1366, 768}, false, 60.0f);
const std::string kFakeKey285 =
    "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv"
    "wxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr"
    "stuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn"
    "opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy";

}  // namespace

class ContentProtectionKeyManagerTest : public testing::Test {
 protected:
  void SetProvisionedKeyRequest(bool use_valid_key) {
    if (use_valid_key) {
      key_manager_.set_provisioned_key_request(base::BindRepeating(
          [](base::OnceCallback<void(const std::string&)> cb) {
            std::move(cb).Run(kFakeKey285);
          }));
    } else {
      key_manager_.set_provisioned_key_request(base::BindRepeating(
          [](base::OnceCallback<void(const std::string&)> cb) {
            std::move(cb).Run("");
          }));
    }
  }

  void SetKeyIfRequiredForDisplay(auto on_key_set) {
    key_manager_.SetKeyIfRequired(
        std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{
            display_.get()},
        kDisplayId, std::move(on_key_set));
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  ContentProtectionKeyManager key_manager_;
  std::unique_ptr<DisplaySnapshot> display_;

  ActionLogger log_;
  TestNativeDisplayDelegate native_display_delegate_{&log_};

 private:
  void SetUp() override {
    scoped_feature_list_.InitAndEnableFeature(
        features::kRequireHdcpKeyProvisioning);

    key_manager_.set_native_display_delegate(&native_display_delegate_);

    display_ = FakeDisplaySnapshot::Builder()
                   .SetId(kDisplayId)
                   .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
                   .SetHasContentProtectionKey(true)
                   .SetCurrentMode(kDisplayMode.Clone())
                   .Build();
  }
};

TEST_F(ContentProtectionKeyManagerTest, TestIfKeySetWhenServerKeyIsValid) {
  SetProvisionedKeyRequest(true);

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));

  EXPECT_EQ(GetSetHdcpKeyPropAction(kDisplayId, true),
            log_.GetActionsAndClear());
}

TEST_F(ContentProtectionKeyManagerTest, TestIfKeyNotSetWhenServerKeyIsEmpty) {
  SetProvisionedKeyRequest(false);

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest, TestIfKeyNotSetIfFeatureIsDisabled) {
  scoped_feature_list_.Reset();

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest, TestIfKeyNotSetWhenDisplayIdMismatch) {
  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  key_manager_.SetKeyIfRequired(
      std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{display_.get()},
      kDisplayId + 1, std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest,
       TestIfKeySetWithMultipleDisplaysOneIsValid) {
  auto other_display = FakeDisplaySnapshot::Builder()
                           .SetId(kDisplayId + 1)
                           .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
                           .SetHasContentProtectionKey(true)
                           .SetCurrentMode(kDisplayMode.Clone())
                           .Build();

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(result); });

  SetProvisionedKeyRequest(true);

  auto displays = std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{
      display_.get(), other_display.get()};
  key_manager_.SetKeyIfRequired(displays, kDisplayId, std::move(on_key_set));

  EXPECT_EQ(GetSetHdcpKeyPropAction(kDisplayId, true),
            log_.GetActionsAndClear());
}

TEST_F(ContentProtectionKeyManagerTest, TestIfKeyNotSetWhenDisplayIsEdp) {
  display_ = FakeDisplaySnapshot::Builder()
                 .SetId(kDisplayId)
                 .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
                 .SetHasContentProtectionKey(true)
                 .SetCurrentMode(kDisplayMode.Clone())
                 .Build();

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest,
       TestIfKeyNotSetWhenDisplayHasNoContentProtectionKeyProp) {
  display_ = FakeDisplaySnapshot::Builder()
                 .SetId(kDisplayId)
                 .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
                 .SetHasContentProtectionKey(false)
                 .SetCurrentMode(kDisplayMode.Clone())
                 .Build();

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest, TestThatKeyIsSetForMultipleDisplays) {
  SetProvisionedKeyRequest(true);

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(result); });
  key_manager_.SetKeyIfRequired(
      std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{display_.get()},
      kDisplayId, std::move(on_key_set));

  auto other_display = FakeDisplaySnapshot::Builder()
                           .SetId(kDisplayId + 1)
                           .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
                           .SetHasContentProtectionKey(true)
                           .SetCurrentMode(kDisplayMode.Clone())
                           .Build();
  on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(result); });
  key_manager_.SetKeyIfRequired(
      std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{
          other_display.get()},
      kDisplayId + 1, std::move(on_key_set));

  EXPECT_EQ(GetSetHdcpKeyPropAction(kDisplayId, true) + "," +
                GetSetHdcpKeyPropAction(kDisplayId + 1, true),
            log_.GetActionsAndClear());
}

TEST_F(ContentProtectionKeyManagerTest,
       TestThatKeyIsFetchedOnlyOnceIfKeyValid) {
  key_manager_.set_provisioned_key_request(
      base::BindRepeating([](base::OnceCallback<void(const std::string&)> cb) {
        static int fetch_key_call_count = 0;

        fetch_key_call_count++;
        std::move(cb).Run(kFakeKey285);

        EXPECT_EQ(1, fetch_key_call_count);
      }));

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));

  on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(true); });

  key_manager_.SetKeyIfRequired(
      std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{display_.get()},
      kDisplayId + 1, std::move(on_key_set));
}

TEST_F(ContentProtectionKeyManagerTest, TestThatKeyIsFetchedAgainIfKeyInvalid) {
  key_manager_.set_provisioned_key_request(
      base::BindRepeating([](base::OnceCallback<void(const std::string&)> cb) {
        static int fetch_key_call_count = 0;

        fetch_key_call_count++;
        if (fetch_key_call_count == 1) {
          std::move(cb).Run("");
        } else {
          std::move(cb).Run(kFakeKey285);

          EXPECT_EQ(2, fetch_key_call_count);
        }
      }));

  auto on_key_set = base::BindOnce([](bool result) { EXPECT_FALSE(result); });

  SetKeyIfRequiredForDisplay(std::move(on_key_set));

  on_key_set = base::BindOnce([](bool result) { EXPECT_TRUE(true); });

  key_manager_.SetKeyIfRequired(
      std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>{display_.get()},
      kDisplayId + 1, std::move(on_key_set));
}

}  // namespace display::test