Source release 19.1.0

This commit is contained in:
Matt Feddersen
2024-03-28 19:21:54 -07:00
parent 28ec8548c6
commit b8bdfccebe
182 changed files with 10645 additions and 2040 deletions

View File

@@ -56,7 +56,7 @@ class MockCdmEngineImpl : public CdmEngine {
public:
MockCdmEngineImpl(wvutil::FileSystem* file_system,
std::shared_ptr<EngineMetrics> metrics)
: CdmEngine(file_system, metrics) {}
: CdmEngine(file_system, std::move(metrics)) {}
MOCK_METHOD(CdmResponseType, OpenSession,
(const CdmKeySystem&, CdmClientPropertySet*, const CdmSessionId&,
WvCdmEventListener*),
@@ -119,7 +119,7 @@ class WvCdmEngineMetricsImplTest : public ::testing::Test {
std::shared_ptr<EngineMetrics> engine_metrics(new EngineMetrics);
test_cdm_metrics_engine_.reset(
new CdmEngineMetricsImpl<StrictMock<MockCdmEngineImpl>>(
file_system_.get(), engine_metrics));
file_system_.get(), std::move(engine_metrics)));
}
protected:

View File

@@ -301,10 +301,6 @@ void InitVectorConstants() {
case 3:
kOverFullUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop2);
break;
default:
kOverFullUsageEntryInfoVector.push_back(
kUsageEntryInfoStorageTypeUnknown);
break;
}
}
@@ -794,8 +790,8 @@ TEST_P(CdmUsageTableInitializationTest, RestoreUsageTable_AtCapacity) {
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(usage_entries), SetArgPointee<2>(false),
Return(true)));
SetArgPointee<1>(std::move(usage_entries)),
SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(CdmResponseType(NO_ERROR)));
@@ -822,8 +818,8 @@ TEST_P(CdmUsageTableInitializationTest,
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(usage_entries), SetArgPointee<2>(false),
Return(true)));
SetArgPointee<1>(std::move(usage_entries)),
SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(CdmResponseType(NO_ERROR)));
@@ -3588,7 +3584,7 @@ TEST_F(CdmUsageTableTest, StaleHeader) {
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(entry_info_list),
SetArgPointee<1>(std::move(entry_info_list)),
SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUsageTableHeader))
@@ -4136,6 +4132,7 @@ TEST_F(CdmUsageTableTest, DetermineLicenseToRemove_BasicPriorities) {
CdmUsageEntryInfo streaming_entry_info;
streaming_entry_info.storage_type = kStorageUsageInfo;
streaming_entry_info.last_use_time = kLruBaseTime;
streaming_entry_info.offline_license_expiry_time = 0;
usage_entry_info_list.push_back(streaming_entry_info);
constexpr UsageEntryIndex streaming_entry_index = 2;
@@ -4144,6 +4141,7 @@ TEST_F(CdmUsageTableTest, DetermineLicenseToRemove_BasicPriorities) {
unknown_entry_info.storage_type = kStorageTypeUnknown;
// Should be chosen regardless of |last_use_time|.
unknown_entry_info.last_use_time = kCurrentTime;
unknown_entry_info.offline_license_expiry_time = 0;
usage_entry_info_list.push_back(unknown_entry_info);
constexpr UsageEntryIndex unknown_entry_index = 3;

View File

@@ -201,7 +201,7 @@ class MockCryptoSession : public TestCryptoSession {
MOCK_METHOD(CdmSecurityLevel, GetSecurityLevel, (), (override));
MOCK_METHOD(CdmResponseType, LoadProvisioning,
(const std::string&, const std::string&, const std::string&,
std::string*),
const std::string&, std::string*),
(override));
};
@@ -458,7 +458,7 @@ TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) {
.WillByDefault(Return(kSecurityLevelL3));
EXPECT_CALL(*crypto_session, LoadProvisioning)
.Times(1)
.WillOnce(DoAll(SetArgPointee<3>(kWrappedPrivateKey),
.WillOnce(DoAll(SetArgPointee<4>(kWrappedPrivateKey),
Return(CdmResponseType(NO_ERROR))));
MockFile* file = new MockFile();

View File

@@ -85,7 +85,7 @@ class CoreIntegrationTest : public WvCdmTestBaseWithEngine {
const std::string origin, std::string* spoid,
std::string* drm_certificate_serial_number) {
std::string name = app_package_name + origin;
return Provision(name, spoid, drm_certificate_serial_number);
return Provision(std::move(name), spoid, drm_certificate_serial_number);
}
private:

View File

@@ -5206,7 +5206,7 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
.WillOnce(Return(ByMove(std::unique_ptr<File>(file))));
EXPECT_CALL(*file, Write(_, _))
.With(AllArgs(StrAndLenContains(expected_substrings)))
.With(AllArgs(StrAndLenContains(std::move(expected_substrings))))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(*file, Read(_, _)).Times(0);
@@ -5226,7 +5226,7 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
kLicenseTestData[license_num].playback_start_time,
kLicenseTestData[license_num].last_playback_time,
kLicenseTestData[license_num].grace_period_end_time,
app_parameters,
std::move(app_parameters),
kLicenseTestData[license_num].usage_entry,
kLicenseTestData[license_num].usage_entry_index,
kLicenseTestData[license_num].drm_certificate,
@@ -5271,7 +5271,7 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
.WillOnce(Return(ByMove(std::unique_ptr<File>(file))));
EXPECT_CALL(*file, Write(_, _))
.With(AllArgs(StrAndLenContains(expected_substrings)))
.With(AllArgs(StrAndLenContains(std::move(expected_substrings))))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(*file, Read(_, _)).Times(0);
}
@@ -5295,7 +5295,7 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
kLicenseTestData[i].playback_start_time,
kLicenseTestData[i].last_playback_time,
kLicenseTestData[i].grace_period_end_time,
app_parameters,
std::move(app_parameters),
kLicenseTestData[i].usage_entry,
kLicenseTestData[i].usage_entry_index,
kLicenseTestData[i].drm_certificate,
@@ -5980,7 +5980,7 @@ TEST_P(DeviceFilesUsageInfoListTest, UsageInfoList) {
}
EXPECT_CALL(file_system, List(StrEq(device_base_path_), NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(file_list), Return(true)));
.WillOnce(DoAll(SetArgPointee<1>(std::move(file_list)), Return(true)));
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
@@ -6030,7 +6030,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
EXPECT_CALL(file_system, Open(StrEq(path), _))
.WillOnce(Return(ByMove(std::unique_ptr<File>(file))));
EXPECT_CALL(*file, Write(_, _))
.With(AllArgs(StrAndLenContains(usage_data_fields)))
.With(AllArgs(StrAndLenContains(std::move(usage_data_fields))))
.WillOnce(ReturnArg<1>());
DeviceFiles device_files(&file_system);
@@ -6336,7 +6336,7 @@ TEST_P(DeviceFilesUsageInfoTest, UpdateUsageInfo) {
.WillOnce(Return(ByMove(std::unique_ptr<File>(next_file))));
EXPECT_CALL(*next_file, Write(_, _))
.With(AllArgs(StrAndLenContains(usage_data_fields)))
.With(AllArgs(StrAndLenContains(std::move(usage_data_fields))))
.WillOnce(ReturnArg<1>());
}
@@ -6451,7 +6451,7 @@ TEST_P(DeviceFilesUsageTableTest, Store) {
EXPECT_CALL(file_system, Open(StrEq(path), _))
.WillOnce(Return(ByMove(std::unique_ptr<File>(file))));
EXPECT_CALL(*file, Write(_, _))
.With(AllArgs(StrAndLenContains(entry_data)))
.With(AllArgs(StrAndLenContains(std::move(entry_data))))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(*file, Read(_, _)).Times(0);

View File

@@ -1062,7 +1062,6 @@ class CdmUseCase_LicenseWithRenewal : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
const uint64_t next_renewal =
start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.
@@ -1268,7 +1267,6 @@ class CdmUseCase_LicenseWithRenewalPlayback : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.
SleepUntil(start_of_playback_);
@@ -1586,7 +1584,6 @@ class CdmUseCase_RenewOnLicenseLoad : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
// The Renew on License Load feature is only supported on v18+ servers.
if (config_.ServerOlderThan(18) ||
wvoec::global_features.api_version < 18) {
@@ -1734,7 +1731,6 @@ class CdmUseCase_Heartbeat : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
const uint64_t next_renewal =
start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.

View File

@@ -275,18 +275,14 @@ bool FakeProvisioningServer::MakeResponse(
// Next, we derive the keys from the keybox device key. This is Provisioning
// 2.0 specific.
// TODO(b/141438127): Add support for provisioing 3.0.
std::string mac_context;
GenerateMacContext(serialized_message, &mac_context);
std::vector<uint8_t> mac_context_v(mac_context.begin(), mac_context.end());
std::string enc_context;
GenerateEncryptContext(serialized_message, &enc_context);
std::vector<uint8_t> enc_context_v(enc_context.begin(), enc_context.end());
wvoec::KeyDeriver key_deriver;
std::vector<uint8_t> serialized_message_v(serialized_message.begin(),
serialized_message.end());
// Not only is this Prov 2.0 specific, it assumes the device is using the
// standard test keybox.
key_deriver.DeriveKeys(wvoec::kTestKeybox.device_key_,
sizeof(wvoec::kTestKeybox.device_key_), mac_context_v,
enc_context_v);
sizeof(wvoec::kTestKeybox.device_key_),
serialized_message_v);
// Create a structure to hold the RSA private key. This is used by the key
// deriver to encrypt the key.

View File

@@ -309,6 +309,7 @@ bool HttpSocket::Connect(int timeout_in_ms) {
if (socket_fd_ < 0) {
LOGE("Cannot open socket %s (port %s): errno = %d", domain_name_.c_str(),
port_.c_str(), GetError());
freeaddrinfo(addr_info);
return false;
}
@@ -318,6 +319,7 @@ bool HttpSocket::Connect(int timeout_in_ms) {
if (ioctlsocket(socket_fd_, FIONBIO, &mode) != 0) {
LOGE("ioctlsocket error %s (port %s), wsa error = %d", domain_name_.c_str(),
port_.c_str(), WSAGetLastError());
freeaddrinfo(addr_info);
CloseSocket();
return false;
}
@@ -326,12 +328,14 @@ bool HttpSocket::Connect(int timeout_in_ms) {
if (original_flags == -1) {
LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(),
port_.c_str(), errno);
freeaddrinfo(addr_info);
CloseSocket();
return false;
}
if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) {
LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(),
port_.c_str(), errno);
freeaddrinfo(addr_info);
CloseSocket();
return false;
}

View File

@@ -75,6 +75,13 @@ void LicenseHolder::ReloadLicense() {
<< "Failed to reload license for " << content_id();
}
void LicenseHolder::FailReloadLicense() {
const CdmResponseType status =
cdm_engine_->RestoreKey(session_id_, key_set_id_);
ASSERT_NE(KEY_ADDED, status)
<< "Unexpected success loading license for " << content_id();
}
void LicenseHolder::GenerateAndPostRenewalRequest(
const std::string& policy_id) {
event_listener_.set_renewal_needed(false);
@@ -86,28 +93,70 @@ void LicenseHolder::GenerateAndPostRenewalRequest(
MessageDumper::DumpRenewalRequest(request);
}
const std::string url = MakeUrl(config_.renewal_server(), policy_id);
renewal_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(renewal_in_flight_->is_connected())
request_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(request_in_flight_->is_connected())
<< "Failed for " << content_id();
renewal_in_flight_->PostRequest(request.message);
request_in_flight_->PostRequest(request.message);
}
void LicenseHolder::FetchRenewal() {
ASSERT_NE(renewal_in_flight_, nullptr) << "Failed for " << content_id();
ASSERT_NE(request_in_flight_, nullptr) << "Failed for " << content_id();
ASSERT_NO_FATAL_FAILURE(
renewal_in_flight_->AssertOkResponse(&renewal_response_))
request_in_flight_->AssertOkResponse(&request_response_))
<< "Renewal failed for " << content_id();
}
void LicenseHolder::LoadRenewal() {
LicenseRequest license_request;
license_request.GetDrmMessage(renewal_response_, renewal_message_);
license_request.GetDrmMessage(request_response_, request_message_);
if (config_.dump_golden_data()) {
MessageDumper::DumpRenewal(renewal_message_);
MessageDumper::DumpRenewal(request_message_);
}
EXPECT_EQ(KEY_ADDED, cdm_engine_->RenewKey(session_id_, renewal_message_))
EXPECT_EQ(KEY_ADDED, cdm_engine_->RenewKey(session_id_, request_message_))
<< "Failed for " << content_id();
}
void LicenseHolder::GenerateAndPostReleaseRequest(
const std::string& policy_id) {
event_listener_.set_renewal_needed(false);
CdmKeyRequest request;
CdmAppParameterMap empty_app_parameters;
video_widevine::WidevinePsshData pssh;
pssh.set_content_id(content_id_);
const std::string init_data_string = MakePSSH(pssh);
const InitializationData init_data(kCencMimeType, init_data_string);
init_data.DumpToLogs();
const CdmResponseType result = cdm_engine_->GenerateKeyRequest(
session_id_, key_set_id_, init_data, kLicenseTypeRelease,
empty_app_parameters, &request);
ASSERT_EQ(KEY_MESSAGE, result) << "Failed for " << content_id();
if (config_.dump_golden_data()) {
// TODO (b/295956275) vickymin: write DumpReleaseRequest function
// MessageDumper::DumpReleaseRequest(request);
}
const std::string url = MakeUrl(config_.renewal_server(), policy_id);
request_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(request_in_flight_->is_connected())
<< "Failed for " << content_id();
request_in_flight_->PostRequest(request.message);
}
void LicenseHolder::FetchRelease() {
ASSERT_NE(request_in_flight_, nullptr) << "Failed for " << content_id();
ASSERT_NO_FATAL_FAILURE(
request_in_flight_->AssertOkResponse(&request_response_))
<< "Renewal failed for " << content_id();
}
void LicenseHolder::LoadRelease() {
LicenseRequest license_request;
license_request.GetDrmMessage(request_response_, request_message_);
if (config_.dump_golden_data()) {
// TODO (b/295956275) vickymin: write DumpRelease function
// MessageDumper::DumpRelease(request_message_);
}
}
void LicenseHolder::RemoveLicense() {
EXPECT_EQ(NO_ERROR, cdm_engine_->RemoveLicense(session_id_))
<< "Failed for " << content_id();

View File

@@ -66,12 +66,20 @@ class LicenseHolder {
// ReloadLicense(). Also, the key_set_id must have been set previously. The
// key_set_id is set by calling LoadLicense(), or by calling set_key_set_id().
void ReloadLicense();
// Attempt to reload a license, but expect a failure.
void FailReloadLicense();
// Generate the renewal request, and send it to the server.
void GenerateAndPostRenewalRequest(const std::string& policy_id);
// Fetch the renewal response. This can add a few seconds of latency.
void FetchRenewal();
// Load the renewal response that was fetched in FetchRenewal().
void LoadRenewal();
// Generate the release request, and send it to the server.
void GenerateAndPostReleaseRequest(const std::string& policy_id);
// Fetch the release response. This can add a few seconds of latency.
void FetchRelease();
// Load the release response that was fetched in FetchRelease().
void LoadRelease();
// Releases the license and frees up entry in usage table.
void RemoveLicense();
@@ -118,9 +126,9 @@ class LicenseHolder {
CdmEngine* cdm_engine_ = nullptr;
const ConfigTestEnv& config_;
SimpleEventListener event_listener_;
std::unique_ptr<UrlRequest> renewal_in_flight_;
std::string renewal_message_;
std::string renewal_response_;
std::unique_ptr<UrlRequest> request_in_flight_;
std::string request_message_;
std::string request_response_;
// Generate the license request.
void GenerateKeyRequest(const InitializationData& init_data,

View File

@@ -136,7 +136,6 @@ const std::string kFakeKeyTooLong =
const std::string kFakeKeyTooShort = a2bs_hex("06e247e7f924208011");
const std::string kFakeIv = a2bs_hex("3d515a3ee0be1687080ac59da9e0d69a");
const std::string kFakeBuildInfo = "Mock Crypto Session - License Test";
const uint32_t kDefaultOemCryptoVersion = 18;
class MockCryptoSession : public TestCryptoSession {
public:
@@ -207,7 +206,7 @@ class CdmLicenseTestPeer : public CdmLicense {
using CdmLicense::HandleNewEntitledKeys;
void set_entitlement_keys(License license) {
void set_entitlement_keys(const License& license) {
entitlement_keys_.CopyFrom(license.key());
}
};
@@ -216,85 +215,84 @@ class CdmLicenseTest : public WvCdmTestBase {
protected:
CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh))
: pssh_(pssh) {}
void SetUp() override {
WvCdmTestBase::SetUp();
crypto_session_.reset(new MockCryptoSession(&crypto_metrics_));
clock_ = new MockClock();
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
init_data_ = new InitializationData(CENC_INIT_DATA_FORMAT, pssh_);
policy_engine_ = new MockPolicyEngine(crypto_session_);
ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()))
.WillByDefault(
DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true)));
// PolicyEngine will call GetApiVersion() on creation.
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<0>(kDefaultOemCryptoVersion), Return(true)));
policy_engine_.reset(new MockPolicyEngine(crypto_session_.get()));
init_data_ = InitializationData(CENC_INIT_DATA_FORMAT, pssh_);
clock_ = new MockClock();
cdm_license_.reset(new CdmLicenseTestPeer(kCdmSessionId, clock_));
}
void TearDown() override {
// Nullify pointers for objects owned by CdmLicense.
clock_ = nullptr;
cdm_license_.reset();
// Release mock objects used by the CdmLicense.
// Order is important.
policy_engine_.reset();
crypto_session_.reset();
delete cdm_license_;
delete policy_engine_;
delete init_data_;
delete crypto_session_;
delete clock_;
}
metrics::CryptoMetrics crypto_metrics_;
MockClock* clock_ = nullptr; // Owned by |cdm_license_|.
std::unique_ptr<CdmLicenseTestPeer> cdm_license_;
std::unique_ptr<MockPolicyEngine> policy_engine_;
std::unique_ptr<MockCryptoSession> crypto_session_;
virtual void CreateCdmLicense() {
cdm_license_ = new CdmLicenseTestPeer(kCdmSessionId, clock_);
clock_ = nullptr;
}
InitializationData init_data_;
CdmLicenseTestPeer* cdm_license_ = nullptr;
MockClock* clock_ = nullptr;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_ = nullptr;
InitializationData* init_data_ = nullptr;
MockPolicyEngine* policy_engine_ = nullptr;
std::string pssh_;
};
TEST_F(CdmLicenseTest, InitSuccess) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(false, kEmptyServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate, nullptr,
policy_engine_.get()));
policy_engine_));
}
TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate,
crypto_session_.get(), nullptr));
crypto_session_, nullptr));
}
TEST_F(CdmLicenseTest, InitWithEmptyServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kEmptyServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, InitWithInvalidServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(true, kInvalidServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, InitWithServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
@@ -337,14 +335,15 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
.WillOnce(
DoAll(SetArgPointee<0>(kWatermarkingConfigurable), Return(true)));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
std::string server_url;
EXPECT_EQ(cdm_license_->PrepareKeyRequest(
init_data_, kToken, kLicenseTypeStreaming, app_parameters,
*init_data_, kToken, kLicenseTypeStreaming, app_parameters,
&signed_request, &server_url),
KEY_MESSAGE);
@@ -428,6 +427,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
EXPECT_EQ(kLicenseStartTime, license_request.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_request.protocol_version());
EXPECT_EQ(kNonce, license_request.key_control_nonce());
EXPECT_FALSE(license_request.client_version().empty());
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) {
@@ -470,14 +470,15 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) {
.WillOnce(
DoAll(SetArgPointee<0>(kWatermarkingNotSupported), Return(true)));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
crypto_session_, policy_engine_));
CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
std::string server_url;
EXPECT_EQ(cdm_license_->PrepareKeyRequest(
init_data_, kToken, kLicenseTypeStreaming, app_parameters,
*init_data_, kToken, kLicenseTypeStreaming, app_parameters,
&signed_request, &server_url),
KEY_MESSAGE);
@@ -561,6 +562,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) {
EXPECT_EQ(kLicenseStartTime, license_request.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_request.protocol_version());
EXPECT_EQ(kNonce, license_request.key_control_nonce());
EXPECT_FALSE(license_request.client_version().empty());
}
struct EntitledKeyVariant {
@@ -614,8 +616,9 @@ TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) {
}
// Set up the CdmLicense with the mocks and fake entitlement key
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_.get(), policy_engine_.get()));
CreateCdmLicense();
ASSERT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_, policy_engine_));
cdm_license_->set_entitlement_keys(entitlement_license);
// Call the function under test and check its return value

View File

@@ -34,9 +34,10 @@ void DumpHeader(std::ofstream* out, const std::string& type) {
void DumpHex(std::ofstream* out, const std::string& name,
const std::string& value) {
const auto out_flags = out->flags();
*out << "const uint8_t " << name << "_raw[] = {\n";
*out << " ";
for (unsigned int i = 0; i < value.length(); i++) {
for (size_t i = 0; i < value.length(); i++) {
if ((i > 0) && (i % 10 == 0)) *out << "\n ";
uint8_t c = value[i];
*out << "0x" << std::hex << std::setw(2) << std::setfill('0') << int(c)
@@ -46,7 +47,7 @@ void DumpHex(std::ofstream* out, const std::string& name,
*out << name << "_ = std::string (\n"
<< " reinterpret_cast<const char *>(" << name << "_raw), \n"
<< " sizeof(" << name << "_raw));\n";
*out << std::dec; // Turn off hex when we're done.
out->flags(out_flags); // Restore flags when we're done.
}
void LogTimer(const char* field, bool set, int64_t time) {
@@ -251,7 +252,7 @@ void MessageDumper::DumpProvisioningRequest(
void MessageDumper::DumpProvisioning(const CdmProvisioningResponse& response) {
if (wvoec::global_features.derive_key_method ==
wvoec::DeviceFeatures::TEST_PROVISION_40) {
LOGD("Provisioning 4.0 does not have a v17 core message.");
LOGD("Provisioning 4.0 does not have a core message.");
} else {
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(response)) {

View File

@@ -135,7 +135,7 @@ class ParallelCdmTest : public WvCdmTestBaseWithEngine,
url.c_str());
HttpHeaderFields fields;
if (url_request.GetDebugHeaderFields(http_response, &fields)) {
for (auto field : fields) {
for (const auto& field : fields) {
LOGD(" %s: %s", field.first.c_str(), field.second.c_str());
}
}

View File

@@ -210,11 +210,12 @@ class PolicyEngineTest : public WvCdmTestBase {
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
bool expected_has_new_usable_key,
KeyId expected_keyid) {
EXPECT_CALL(mock_event_listener_,
OnSessionKeysChange(kSessionId,
UnorderedElementsAre(Pair(
expected_keyid, expected_key_status)),
expected_has_new_usable_key));
EXPECT_CALL(
mock_event_listener_,
OnSessionKeysChange(kSessionId,
UnorderedElementsAre(Pair(std::move(expected_keyid),
expected_key_status)),
expected_has_new_usable_key));
}
void ExpectSessionKeysChange(CdmKeyStatus expected_key1_status,
@@ -266,7 +267,7 @@ class PolicyEngineTestV18 : public PolicyEngineTest {
}
};
TEST_F(PolicyEngineTestV16, NoLicense) {
TEST_F(PolicyEngineTest, NoLicense) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
}
@@ -2825,10 +2826,6 @@ TEST_F(PolicyEngineTestV16, PlaybackOk_RestoreWithoutPlaybackTimes) {
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId));
}
TEST_F(PolicyEngineTestV18, NoLicense) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
}
// These tests exercise license policy when OEMCrypto supports v18.
// The following scenarios are from the duration-and-renewal doc.
// Verifies correct reporting of events, OnSessionRenewalNeeded,

View File

@@ -22,6 +22,7 @@
#include "provisioning_holder.h"
#include "test_base.h"
#include "test_printers.h"
#include "test_sleep.h"
#include "wv_cdm_types.h"
namespace wvcdm {
@@ -76,10 +77,67 @@ TEST_F(CorePIGTest, OfflineWithPST) {
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
// This test verifies that the system can download and install license with a
// key that requires secure buffers. It also verifies that we cannot decrypt to
// a non-secure buffer using this key, but that we can decrypt to a secure
// buffer, if the test harness supports secure buffers.
TEST_F(CorePIGTest, OfflineMultipleLicensesWithDefrag) {
const KeyId key_id = "0000000000000000";
// 1. Open a session, load license, close session
LicenseHolder holder1("CDM_OfflineWithPST", &cdm_engine_, config_);
holder1.set_can_persist(true);
ASSERT_NO_FATAL_FAILURE(holder1.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder1.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder1.LoadLicense());
ASSERT_NO_FATAL_FAILURE(holder1.CloseSession());
// 2. Open a session, load license, keep session open
LicenseHolder holder2("CDM_OfflineWithPST", &cdm_engine_, config_);
holder2.set_can_persist(true);
ASSERT_NO_FATAL_FAILURE(holder2.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder2.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder2.LoadLicense());
// 3. Remove first license
ASSERT_NO_FATAL_FAILURE(holder1.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder1.ReloadLicense());
ASSERT_NO_FATAL_FAILURE(holder1.RemoveLicense());
ASSERT_NO_FATAL_FAILURE(holder1.CloseSession());
// 4. Open a session, load license
LicenseHolder holder3("CDM_OfflineWithPST", &cdm_engine_, config_);
holder3.set_can_persist(true);
ASSERT_NO_FATAL_FAILURE(holder3.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder3.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder3.LoadLicense());
EXPECT_EQ(NO_ERROR, holder3.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder3.CloseSession());
ASSERT_NO_FATAL_FAILURE(holder2.CloseSession());
// Ensure first offline license can no longer be used
ASSERT_NO_FATAL_FAILURE(holder1.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder1.FailReloadLicense());
EXPECT_NE(NO_ERROR, holder1.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder1.CloseSession());
// Ensure second and third offline licenses can be used
ASSERT_NO_FATAL_FAILURE(holder2.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder2.ReloadLicense());
EXPECT_EQ(NO_ERROR, holder2.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder2.RemoveLicense());
ASSERT_NO_FATAL_FAILURE(holder2.CloseSession());
ASSERT_NO_FATAL_FAILURE(holder3.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder3.ReloadLicense());
EXPECT_EQ(NO_ERROR, holder3.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder3.RemoveLicense());
ASSERT_NO_FATAL_FAILURE(holder3.CloseSession());
}
/**
* This test verifies that the system can download and install license with a
* key that requires secure buffers. It also verifies that we cannot decrypt to
* a non-secure buffer using this key, but that we can decrypt to a secure
* buffer, if the test harness supports secure buffers.
*/
TEST_F(CorePIGTest, OfflineHWSecureRequired) {
LicenseHolder holder("CDM_OfflineHWSecureRequired", &cdm_engine_, config_);
holder.set_can_persist(true);
@@ -118,6 +176,50 @@ TEST_F(CorePIGTest, OfflineHWSecureRequired) {
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
/**
* Should be able to request license, perform playback, generate a license
* release, and receive the release response.
*/
TEST_F(CorePIGTest, LicenseRelease1) {
LicenseHolder holder("CDM_UnlimitedStreaming_can_persist", &cdm_engine_,
config_);
holder.set_can_persist(true);
const KeyId key_id = "0000000000000000";
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest(
"CDM_UnlimitedStreaming_can_persist"));
EXPECT_NE(NO_ERROR, holder.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder.FetchRelease());
ASSERT_NO_FATAL_FAILURE(holder.LoadRelease());
EXPECT_NE(NO_ERROR, holder.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
/**
* Should be able to request license, wait some time, generate a license
* release, and receive the release response.
*/
TEST_F(CorePIGTest, LicenseRelease2) {
LicenseHolder holder("CDM_UnlimitedStreaming_can_persist", &cdm_engine_,
config_);
holder.set_can_persist(true);
const KeyId key_id = "0000000000000000";
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
wvutil::TestSleep::Sleep(10);
ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest(
"CDM_UnlimitedStreaming_can_persist"));
ASSERT_NO_FATAL_FAILURE(holder.FetchRelease());
ASSERT_NO_FATAL_FAILURE(holder.LoadRelease());
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
TEST_F(CorePIGTest, CastReceiverProvisioningUsingCdm) {
const std::string digest_hex_str =
// digest info header

View File

@@ -185,6 +185,7 @@ std::string ProvisioningHolder::DumpProvAttempt(const std::string& request,
if (result != OEMCrypto_SUCCESS) {
info << "--- ERROR GETTING BCC. result=" << result;
} else {
bcc.resize(bcc_length);
info << "BCC = (len=" << bcc_length << ") "
<< wvutil::unlimited_b2a_hex(bcc) << "\n"
<< "Additional Sig = (len=" << signature_length << ") "

View File

@@ -145,14 +145,60 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
std::cout << " --dump_golden_data" << std::endl;
std::cout << " Dump the license request and response from the server."
<< std::endl
<< std::endl;
std::cout << " --skip-slow-tests" << std::endl;
std::cout << " Skips tests that are known to be slow." << std::endl;
std::cout << " --skip-sleepy-tests" << std::endl;
std::cout << " Skips tests that sleep a lot." << std::endl;
std::cout << " --run-sleepy-tests" << std::endl;
std::cout << " Allow tests that sleep a lot (used with --skip-slow-tests)"
<< std::endl;
std::cout << " --skip-decryption-stress-tests" << std::endl;
std::cout << " Skips decryption stress tests." << std::endl;
std::cout << " --run-decryption-stress-tests" << std::endl;
std::cout << " Allow decryption stress tests (used with "
<< "--skip-slow-tests)" << std::endl;
std::cout << " --skip-request-flood-tests" << std::endl;
std::cout << " Skips tests that generate a lot of requests." << std::endl;
std::cout << " --run-request-flood-tests" << std::endl;
std::cout << " Allow tests that generate a lot of requests (used with "
<< "--skip-slow-tests)" << std::endl;
std::cout << " --skip-multi-thread-stress-tests" << std::endl;
std::cout << " Skips tests that stress thread protections." << std::endl;
std::cout << " --run-multi-thread-stress-tests" << std::endl;
std::cout << " Allow tests that stress thread protections (used with "
<< "--skip-slow-tests)" << std::endl;
std::cout << " --skip-usage-table-stress-tests" << std::endl;
std::cout << " Skips tests that stess the usage table." << std::endl;
std::cout << " --run-usage-table-stress-tests" << std::endl;
std::cout << " Allow tests that stess the usage table (used with "
<< "--skip-slow-tests)" << std::endl
<< std::endl;
std::cout << extra_help_text << std::endl;
}
enum OptionalBool {
kBoolUnset,
kBoolFalse,
kBoolTrue,
};
bool UnwrapOptionalBool(OptionalBool value, bool default_value) {
return (value == kBoolUnset) ? default_value : (value == kBoolTrue);
}
} // namespace
// Static WvCdmTestBase variables.
std::unique_ptr<ConfigTestEnv> WvCdmTestBase::default_config_;
bool WvCdmTestBase::use_qa_test_keybox_ = false;
bool WvCdmTestBase::skip_sleepy_tests_ = false;
bool WvCdmTestBase::skip_decryption_stress_tests_ = false;
bool WvCdmTestBase::skip_request_flood_tests_ = false;
bool WvCdmTestBase::skip_multi_thread_stress_tests_ = false;
bool WvCdmTestBase::skip_usage_table_stress_tests_ = false;
void WvCdmTestBase::StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
uint8_t init) {
@@ -208,7 +254,6 @@ TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics,
void TestCryptoSession::MaybeInstallTestKeybox() {
if (IsTestKeyboxNeeded()) {
CryptoSession::SetAllowTestKeybox(true);
ReinitializeForTest();
WvCdmTestBase::InstallTestRootOfTrust();
}
@@ -312,8 +357,7 @@ void WvCdmTestBase::InstallTestRootOfTrust() {
}
}
WvCdmTestBase::WvCdmTestBase()
: config_(*default_config_), binary_provisioning_(false) {
WvCdmTestBase::WvCdmTestBase() : config_(*default_config_) {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
@@ -374,53 +418,29 @@ void WvCdmTestBase::EnsureProvisioned() {
ASSERT_EQ(NO_ERROR, cdm_engine.CloseSession(session_id));
}
bool WvCdmTestBase::ExtractSignedMessage(const std::string& response,
std::string* result) {
static const std::string kMessageStart = "\"signedResponse\": \"";
static const std::string kMessageEnd = "\"";
std::string response_string;
size_t start = response.find(kMessageStart);
if (start == response.npos) {
// Assume serialized protobuf message.
result->assign(response);
} else {
// Assume JSON-wrapped protobuf.
size_t end = response.find(kMessageEnd, start + kMessageStart.length());
if (end == response.npos) {
LOGE("ExtractSignedMessage cannot locate end substring");
result->clear();
return false;
}
size_t result_string_size = end - start - kMessageStart.length();
result->assign(response, start + kMessageStart.length(),
result_string_size);
}
if (result->empty()) {
LOGE("ExtractSignedMessage: Response message is empty");
return false;
}
return true;
}
bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
const std::string& extra_help_text) {
Properties::Init();
bool is_cast_receiver = false;
bool filter_tests = true;
bool show_usage = false;
int verbosity = 0;
bool skip_slow_tests = false;
OptionalBool skip_sleepy_tests = kBoolUnset;
OptionalBool skip_decryption_stress_tests = kBoolUnset;
OptionalBool skip_request_flood_tests = kBoolUnset;
OptionalBool skip_multi_thread_stress_tests = kBoolUnset;
OptionalBool skip_usage_table_stress_tests = kBoolUnset;
default_config_.reset(new ConfigTestEnv(kContentProtectionUatServer));
// Skip the first element, which is the program name.
const std::vector<std::string> args(argv + 1, argv + argc);
for (const std::string& arg : args) {
if (arg == "--verbose" || arg == "-v") {
if (arg == "--help" || arg == "-h") {
show_usage = true;
} else if (arg == "--verbose" || arg == "-v") {
++verbosity;
} else if (arg == "--no_filter") {
filter_tests = false;
} else if (arg == "--cast") {
is_cast_receiver = true;
} else if (arg == "--fake_sleep") {
@@ -435,6 +455,28 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
} else if (arg == "--dump_golden_data") {
default_config_->set_dump_golden_data(true);
testing::AddGlobalTestEnvironment(new MessageDumper);
} else if (arg == "--skip-slow-tests") {
skip_slow_tests = true;
} else if (arg == "--skip-sleepy-tests") {
skip_sleepy_tests = kBoolTrue;
} else if (arg == "--run-sleepy-tests") {
skip_sleepy_tests = kBoolFalse;
} else if (arg == "--skip-decryption-stress-tests") {
skip_decryption_stress_tests = kBoolTrue;
} else if (arg == "--run-decryption-stress-tests") {
skip_decryption_stress_tests = kBoolFalse;
} else if (arg == "--skip-request-flood-tests") {
skip_request_flood_tests = kBoolTrue;
} else if (arg == "--run-request-flood-tests") {
skip_request_flood_tests = kBoolFalse;
} else if (arg == "--skip-multi-thread-stress-tests") {
skip_multi_thread_stress_tests = kBoolTrue;
} else if (arg == "--run-multi-thread-stress-tests") {
skip_multi_thread_stress_tests = kBoolFalse;
} else if (arg == "--skip-usage-table-stress-tests") {
skip_usage_table_stress_tests = kBoolTrue;
} else if (arg == "--run-usage-table-stress-tests") {
skip_usage_table_stress_tests = kBoolFalse;
} else {
const auto index = arg.find('=');
if (index == std::string::npos) {
@@ -529,12 +571,16 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
// support being a cast receiver.
wvoec::global_features.set_cast_receiver(is_cast_receiver);
}
// If the user requests --no_filter, we don't change the filter, otherwise, we
// filter out features that are not supported.
if (filter_tests) {
::testing::GTEST_FLAG(filter) =
wvoec::global_features.RestrictFilter(::testing::GTEST_FLAG(filter));
}
skip_sleepy_tests_ = UnwrapOptionalBool(skip_sleepy_tests, skip_slow_tests);
skip_decryption_stress_tests_ =
UnwrapOptionalBool(skip_decryption_stress_tests, skip_slow_tests);
skip_request_flood_tests_ =
UnwrapOptionalBool(skip_request_flood_tests, skip_slow_tests);
skip_multi_thread_stress_tests_ =
UnwrapOptionalBool(skip_multi_thread_stress_tests, skip_slow_tests);
skip_usage_table_stress_tests_ =
UnwrapOptionalBool(skip_usage_table_stress_tests, skip_slow_tests);
return true;
}

View File

@@ -48,16 +48,22 @@ class WvCdmTestBase : public ::testing::Test {
// Calls Provision() if not already provisioned.
virtual void EnsureProvisioned();
// Locate the portion of the server's provisioning response message that is
// between the strings jason_start_substr and json_end_substr. Returns the
// string through *result. If the start substring match fails, assume the
// entire string represents a serialized protobuf mesaage and return true with
// the entire string. If the end_substring match fails, return false with an
// empty *result.
bool ExtractSignedMessage(const std::string& response, std::string* result);
virtual bool skip_sleepy_tests() const { return skip_sleepy_tests_; }
virtual bool skip_decryption_stress_tests() const {
return skip_decryption_stress_tests_;
}
virtual bool skip_request_flood_tests() const {
return skip_request_flood_tests_;
}
virtual bool skip_multi_thread_stress_tests() const {
return skip_multi_thread_stress_tests_;
}
virtual bool skip_usage_table_stress_tests() const {
return skip_usage_table_stress_tests_;
}
// Fill a buffer with some nonconstant data of the given size. The first
// byte will be set to <init> to help you find the buffer when debugging.
// Fill a buffer with some nonconstant data of the given size. The first byte
// will be set to <init> to help you find the buffer when debugging.
static void StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
uint8_t init);
@@ -86,7 +92,15 @@ class WvCdmTestBase : public ::testing::Test {
// This should be set by test subclasses BEFORE calling SetUp -- i.e. in the
// tests's constructor.
bool binary_provisioning_;
bool binary_provisioning_ = false;
private:
// Skip flags for long running tests.
static bool skip_sleepy_tests_;
static bool skip_decryption_stress_tests_;
static bool skip_request_flood_tests_;
static bool skip_multi_thread_stress_tests_;
static bool skip_usage_table_stress_tests_;
};
// This just makes the constructor public so that we can create one with dummy
@@ -95,7 +109,7 @@ class TestCdmEngine : public CdmEngine {
public:
TestCdmEngine(wvutil::FileSystem* file_system,
std::shared_ptr<metrics::EngineMetrics> metrics)
: CdmEngine(file_system, metrics) {}
: CdmEngine(file_system, std::move(metrics)) {}
const CdmSession* GetCdmSession(std::string sessionId) const;
};

View File

@@ -79,3 +79,20 @@ void PrintTo(const SystemState& state, std::ostream* os) {
}
} // namespace okp
} // namespace wvcdm
namespace std {
void PrintTo(future_status status, ostream* os) {
switch (status) {
case future_status::ready:
*os << "future_status::ready";
return;
case future_status::timeout:
*os << "future_status::timeout";
return;
case future_status::deferred:
*os << "future_status::deferred";
return;
}
*os << "<unknown(" << static_cast<int>(status) << ")>";
}
} // namespace std

View File

@@ -7,6 +7,7 @@
#ifndef CDM_TEST_PRINTERS_H_
#define CDM_TEST_PRINTERS_H_
#include <future>
#include <iostream>
#include "OEMCryptoCENC.h"
@@ -24,4 +25,9 @@ namespace okp {
void PrintTo(const SystemState& state, std::ostream* os);
} // namespace okp
} // namespace wvcdm
namespace std {
void PrintTo(future_status status, ostream* os);
} // namespace std
#endif // CDM_TEST_PRINTERS_H_

View File

@@ -120,7 +120,7 @@ bool UrlRequest::GetResponse(std::string* message) {
}
}
ConcatenateChunkedResponse(response, message);
ConcatenateChunkedResponse(std::move(response), message);
LOGV("HTTP response from %s: (%zu): %s", socket_.url().c_str(),
message->size(), message->c_str());
return true;