// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. // // This file contains unit tests for the WvContentDecryptionModule class // that pertain to collecting and reporting metrics. #include "cdm_identifier.h" #include "gmock/gmock.h" #include "log.h" #include "string_conversions.h" #include "test_base.h" #include "test_printers.h" #include "wv_cdm_types.h" #include "wv_content_decryption_module.h" #include "wv_metrics.pb.h" using ::testing::Eq; using ::testing::Ge; using ::testing::Gt; using ::testing::Lt; using ::testing::StrEq; using ::testing::Test; using wvcdm::CdmResponseType; namespace { const std::string kEmptyServiceCertificate; } // unnamed namespace namespace wvcdm { // This class is used to test the metrics-related feaures of the // WvContentDecryptionModule class. class WvContentDecryptionModuleMetricsTest : public WvCdmTestBase { protected: void Unprovision(CdmIdentifier identifier) { EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL1, identifier)); EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3, identifier)); } wvcdm::WvContentDecryptionModule decryptor_; }; TEST_F(WvContentDecryptionModuleMetricsTest, IdentifierNotFound) { drm_metrics::WvCdmMetrics metrics; ASSERT_EQ(wvcdm::UNKNOWN_ERROR, decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics)); } TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) { std::string request; std::string provisioning_server_url; CdmCertificateType cert_type = kCertificateWidevine; std::string cert_authority, cert, wrapped_key; // This call will create a CdmEngine instance with an EngineMetrics instance. EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest( cert_type, cert_authority, kDefaultCdmIdentifier, kEmptyServiceCertificate, kLevelDefault, &request, &provisioning_server_url)); drm_metrics::WvCdmMetrics metrics; ASSERT_EQ(wvcdm::NO_ERROR, decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics)); // 100 is an arbitrary high value that shouldn't ever occur. EXPECT_THAT(metrics.engine_metrics() .level3_oemcrypto_initialization_error() .int_value(), Lt(100)); EXPECT_THAT(metrics.engine_metrics() .level3_oemcrypto_initialization_error() .int_value(), Ge(0)); EXPECT_THAT( metrics.engine_metrics().oemcrypto_initialization_mode().int_value(), Lt(100)); EXPECT_THAT( metrics.engine_metrics().oemcrypto_initialization_mode().int_value(), Ge(0)); EXPECT_THAT(metrics.engine_metrics() .previous_oemcrypto_initialization_failure() .int_value(), Lt(100)); EXPECT_THAT(metrics.engine_metrics() .previous_oemcrypto_initialization_failure() .int_value(), Ge(0)); ASSERT_THAT(metrics.engine_metrics() .cdm_engine_get_provisioning_request_time_us() .size(), Eq(1)); EXPECT_THAT(metrics.engine_metrics() .cdm_engine_get_provisioning_request_time_us(0) .operation_count(), Eq(1u)); } TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) { CdmSessionId session_id; wvcdm::CdmKeySystem key_system("com.widevine"); Unprovision(kDefaultCdmIdentifier); // Opening the session will fail with NEEDS_PROVISIONING error. But it will // still create some session-level stats. EXPECT_EQ(wvcdm::NEED_PROVISIONING, decryptor_.OpenSession(key_system, nullptr, kDefaultCdmIdentifier, nullptr, &session_id)); drm_metrics::WvCdmMetrics metrics; ASSERT_EQ(wvcdm::NO_ERROR, decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics)); std::string serialized_metrics; ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics)); // Spot check some metric values. // Validate engine-level metrics. EXPECT_TRUE( metrics.engine_metrics().has_level3_oemcrypto_initialization_error()); EXPECT_TRUE(metrics.engine_metrics().has_oemcrypto_initialization_mode()); ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(), Eq(1)); EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(), Eq(1)); EXPECT_THAT(metrics.engine_metrics() .cdm_engine_open_session(0) .attributes() .error_code(), Eq(NEED_PROVISIONING)); // Validate a session-level metric. ASSERT_THAT(metrics.session_metrics().size(), Eq(1)); } TEST_F(WvContentDecryptionModuleMetricsTest, DifferentCdmIdentifiersHaveDifferentMetrics) { CdmSessionId session_id; wvcdm::CdmKeySystem key_system("com.widevine"); CdmIdentifier identifiers[] = {kDefaultCdmIdentifier, {"foo", "bar", "baz", 7, 10}, // Note that this has all the same parameters // as the one above except for the unique_id. {"foo", "bar", "baz", 8, 11}}; const int cdm_engine_count = 3; // Force Unprovision. for (int i = 0; i < cdm_engine_count; i++) { Unprovision(identifiers[i]); } for (int i = 0; i < cdm_engine_count; i++) { // To make sure we can detect different engine metrics, // make the open session call a different number of times for // each identifier. for (int j = 0; j <= i; j++) { EXPECT_EQ(NEED_PROVISIONING, decryptor_.OpenSession(key_system, nullptr, identifiers[i], nullptr, &session_id)); } } for (int i = 0; i < cdm_engine_count; i++) { drm_metrics::WvCdmMetrics metrics; metrics.Clear(); ASSERT_EQ(wvcdm::NO_ERROR, decryptor_.GetCurrentMetrics(identifiers[i], &metrics)); std::string serialized_metrics; ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics)); ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(), Eq(1)); // The number of times open session was called should match the index // of the identifier EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(), Eq(i + 1)); EXPECT_THAT(metrics.engine_metrics() .cdm_engine_open_session(0) .attributes() .error_code(), Eq(NEED_PROVISIONING)); // Spot check a session-level metric. ASSERT_THAT(metrics.session_metrics().size(), Eq(i + 1)) << "Unexpected failure with session_metrics: " << wvutil::b2a_hex(serialized_metrics); } } } // namespace wvcdm