#include "chrome/common/media/cdm_registration.h"
#include <optional>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "base/test/scoped_path_override.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/media/component_widevine_cdm_hint_file_linux.h"
#include "components/cdm/common/cdm_manifest.h"
#include "media/cdm/cdm_paths.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"
#if !BUILDFLAG(ENABLE_WIDEVINE)
#error "This file only applies when Widevine used."
#endif
#if !BUILDFLAG(IS_LINUX)
#error "This file only applies to desktop Linux."
#endif
namespace {
const char kLowerVersion[] = "1.0.0.0";
const char kHigherVersion[] = "10.0.0.0";
base::Version GetBundledWidevineVersion() {
base::FilePath cdm_base_path;
EXPECT_TRUE(
base::PathService::Get(chrome::DIR_BUNDLED_WIDEVINE_CDM, &cdm_base_path));
auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json"));
if (base::PathExists(manifest_path)) {
media::CdmCapability capability;
if (ParseCdmManifestFromPath(manifest_path, &capability)) {
return capability.version;
}
}
return base::Version({0, 0, 0, 0});
}
void CreateFakeComponentUpdatedWidevine(
base::Version version,
std::optional<base::Version> bundled_version) {
base::FilePath component_updated_widevine_directory;
EXPECT_TRUE(base::PathService::Get(chrome::DIR_COMPONENT_UPDATED_WIDEVINE_CDM,
&component_updated_widevine_directory));
auto new_component_directory =
component_updated_widevine_directory.Append(version.GetString());
if (!base::PathExists(new_component_directory)) {
EXPECT_TRUE(base::CreateDirectory(new_component_directory));
base::Value::Dict manifest;
manifest.Set("version", version.GetString());
manifest.Set("x-cdm-codecs", "vp8,vp09,av01");
manifest.Set("x-cdm-module-versions", "4");
manifest.Set("x-cdm-interface-versions", "10");
manifest.Set("x-cdm-host-versions", "10");
auto manifest_path =
new_component_directory.Append(FILE_PATH_LITERAL("manifest.json"));
JSONFileValueSerializer serializer(manifest_path);
EXPECT_TRUE(serializer.Serialize(manifest));
media::CdmCapability capability;
EXPECT_TRUE(ParseCdmManifestFromPath(manifest_path, &capability));
auto executable_dir =
media::GetPlatformSpecificDirectory(new_component_directory);
EXPECT_TRUE(base::CreateDirectory(executable_dir));
EXPECT_GE(base::WriteFile(executable_dir.Append(base::GetNativeLibraryName(
kWidevineCdmLibraryName)),
"random data"),
0);
}
EXPECT_TRUE(
UpdateWidevineCdmHintFile(new_component_directory, bundled_version));
}
}
TEST(CdmRegistrationTest, ChooseBundledCdm) {
const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
const base::Version bundled_version = GetBundledWidevineVersion();
auto cdms = GetSoftwareSecureWidevine();
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, bundled_version);
#else
EXPECT_EQ(cdms.size(), 0u);
#endif
}
TEST(CdmRegistrationTest, ChooseComponentUpdatedCdm) {
const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
const base::Version bundled_version = GetBundledWidevineVersion();
const base::Version component_updated_version(kHigherVersion);
EXPECT_GT(component_updated_version, bundled_version);
CreateFakeComponentUpdatedWidevine(component_updated_version, std::nullopt);
auto cdms = GetSoftwareSecureWidevine();
#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, component_updated_version);
#elif BUILDFLAG(BUNDLE_WIDEVINE_CDM)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, bundled_version);
#else
EXPECT_EQ(cdms.size(), 0u);
#endif
}
TEST(CdmRegistrationTest, ChooseDowngradedCdm) {
const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
const base::Version bundled_version = GetBundledWidevineVersion();
const base::Version component_updated_version(kLowerVersion);
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM)
EXPECT_LT(component_updated_version, bundled_version);
#endif
CreateFakeComponentUpdatedWidevine(component_updated_version,
bundled_version);
auto cdms = GetSoftwareSecureWidevine();
#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, component_updated_version);
#elif BUILDFLAG(BUNDLE_WIDEVINE_CDM)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, bundled_version);
#else
EXPECT_EQ(cdms.size(), 0u);
#endif
}
TEST(CdmRegistrationTest, ChooseCorrectCdm) {
const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
const struct test_case {
int16_t hinted;
int16_t last_bundled;
int16_t bundled;
int16_t selected;
} cases[] = {
{2, 2, 2, 2},
{1, 1, 2, 2},
{3, 2, 2, 3},
{2, 1, 2, 2},
{2, 3, 2, 2},
{1, 2, 2, 1},
{0, 1, 2, 2},
{1, 0, 2, 2},
};
const std::vector<base::Version> versions = {
base::Version(kLowerVersion), base::Version("2.0.0.0"),
GetBundledWidevineVersion(), base::Version(kHigherVersion)};
for (const auto& c : cases) {
EXPECT_EQ(c.bundled, 2);
CreateFakeComponentUpdatedWidevine(versions[c.hinted],
versions[c.last_bundled]);
auto cdms = GetSoftwareSecureWidevine();
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, versions[c.selected]);
#elif BUILDFLAG(BUNDLE_WIDEVINE_CDM)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, versions[c.bundled]);
#elif BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
EXPECT_EQ(cdms.size(), 1u);
EXPECT_EQ(cdms[0].capability->version, versions[c.hinted]);
#else
EXPECT_EQ(cdms.size(), 0u);
#endif
}
}