From d90ca489f929bfe378187d0851fb2c7e13ca9a3d Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Thu, 20 Feb 2014 14:38:28 -0800 Subject: [PATCH] Widevine encryptor source unittest Change-Id: I1b56d0a019480472eb462d8a39f81c4eb3c81a0e --- .../base/{httpfetcher.cc => http_fetcher.cc} | 34 +-- media/base/{httpfetcher.h => http_fetcher.h} | 41 +++- ...r_unittest.cc => http_fetcher_unittest.cc} | 16 +- media/base/media_base.gyp | 15 +- media/base/widevine_encryptor_source.cc | 18 +- media/base/widevine_encryptor_source.h | 8 + .../widevine_encryptor_source_unittest.cc | 232 ++++++++++++++++++ 7 files changed, 315 insertions(+), 49 deletions(-) rename media/base/{httpfetcher.cc => http_fetcher.cc} (84%) rename media/base/{httpfetcher.h => http_fetcher.h} (51%) rename media/base/{httpfetcher_unittest.cc => http_fetcher_unittest.cc} (88%) create mode 100644 media/base/widevine_encryptor_source_unittest.cc diff --git a/media/base/httpfetcher.cc b/media/base/http_fetcher.cc similarity index 84% rename from media/base/httpfetcher.cc rename to media/base/http_fetcher.cc index e35ac91718..818b61d671 100644 --- a/media/base/httpfetcher.cc +++ b/media/base/http_fetcher.cc @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "media/base/httpfetcher.h" +#include "media/base/http_fetcher.h" #ifdef WIN32 #include @@ -16,7 +16,7 @@ namespace { -struct HTTPResult { +struct HttpResult { int status_code; std::string status_message; std::string response; @@ -74,7 +74,7 @@ void OnBegin(const happyhttp::Response* response, void* userdata) { DLOG(INFO) << "BEGIN (" << response->getstatus() << ", " << response->getreason() << ")."; - HTTPResult* result = static_cast(userdata); + HttpResult* result = static_cast(userdata); result->status_code = response->getstatus(); result->status_message = response->getreason(); result->response.clear(); @@ -85,13 +85,13 @@ void OnData(const happyhttp::Response* response, const unsigned char* data, int num_bytes) { DCHECK(response && userdata && data); - HTTPResult* result = static_cast(userdata); + HttpResult* result = static_cast(userdata); result->response.append(reinterpret_cast(data), num_bytes); } void OnComplete(const happyhttp::Response* response, void* userdata) { DCHECK(response && userdata); - HTTPResult* result = static_cast(userdata); + HttpResult* result = static_cast(userdata); DLOG(INFO) << "COMPLETE (" << result->response.size() << " bytes)."; } @@ -101,7 +101,10 @@ const int kHttpOK = 200; namespace media { -HTTPFetcher::HTTPFetcher() { +HttpFetcher::HttpFetcher() {} +HttpFetcher::~HttpFetcher() {} + +SimpleHttpFetcher::SimpleHttpFetcher() { #ifdef WIN32 WSAData wsa_data; int code = WSAStartup(MAKEWORD(1, 1), &wsa_data); @@ -111,26 +114,27 @@ HTTPFetcher::HTTPFetcher() { #endif // WIN32 } -HTTPFetcher::~HTTPFetcher() { +SimpleHttpFetcher::~SimpleHttpFetcher() { #ifdef WIN32 if (wsa_startup_succeeded_) WSACleanup(); #endif // WIN32 } -Status HTTPFetcher::Get(const std::string& path, std::string* response) { +Status SimpleHttpFetcher::Get(const std::string& path, std::string* response) { return FetchInternal("GET", path, "", response); } -Status HTTPFetcher::Post(const std::string& path, const std::string& data, - std::string* response) { +Status SimpleHttpFetcher::Post(const std::string& path, + const std::string& data, + std::string* response) { return FetchInternal("POST", path, data, response); } -Status HTTPFetcher::FetchInternal(const std::string& method, - const std::string& url, - const std::string& data, - std::string* response) { +Status SimpleHttpFetcher::FetchInternal(const std::string& method, + const std::string& url, + const std::string& data, + std::string* response) { DCHECK(response); int status_code = 0; @@ -145,7 +149,7 @@ Status HTTPFetcher::FetchInternal(const std::string& method, } try { - HTTPResult result; + HttpResult result; happyhttp::Connection connection(host.data(), port); connection.setcallbacks(OnBegin, OnData, OnComplete, &result); diff --git a/media/base/httpfetcher.h b/media/base/http_fetcher.h similarity index 51% rename from media/base/httpfetcher.h rename to media/base/http_fetcher.h index 7715ef74d3..8c1dff63f0 100644 --- a/media/base/httpfetcher.h +++ b/media/base/http_fetcher.h @@ -4,32 +4,49 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#ifndef MEDIA_BASE_HTTPFETCHER_H_ -#define MEDIA_BASE_HTTPFETCHER_H_ +#ifndef MEDIA_BASE_HTTP_FETCHER_H_ +#define MEDIA_BASE_HTTP_FETCHER_H_ +#include "base/compiler_specific.h" #include "media/base/status.h" namespace media { -// A simple HTTP fetcher implementation using happyhttp. -class HTTPFetcher { +class HttpFetcher { public: - // TODO(kqyang): Add timeout support. - HTTPFetcher(); - ~HTTPFetcher(); + HttpFetcher(); + virtual ~HttpFetcher(); // Fetch |response| from |url| using HTTP GET. // |response| should not be NULL, will contain the body of the http response // on success. // Return OK on success. - Status Get(const std::string& url, std::string* response); + virtual Status Get(const std::string& url, std::string* response) = 0; // Fetch |response| from |url| using HTTP POST. // |response| should not be NULL, will contain the body of the http response // on success. // Return OK on success. - Status Post(const std::string& url, const std::string& data, - std::string* response); + virtual Status Post(const std::string& url, + const std::string& data, + std::string* response) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(HttpFetcher); +}; + +// A simple HttpFetcher implementation using happyhttp. +class SimpleHttpFetcher : public HttpFetcher { + public: + // TODO(kqyang): Add timeout support. + SimpleHttpFetcher(); + virtual ~SimpleHttpFetcher(); + + // HttpFetcher implementation overrides. + virtual Status Get(const std::string& url, std::string* response) OVERRIDE; + virtual Status Post(const std::string& url, + const std::string& data, + std::string* response) OVERRIDE; private: // Internal implementation of HTTP functions, e.g. Get and Post. @@ -41,10 +58,10 @@ class HTTPFetcher { bool wsa_startup_succeeded_; #endif - DISALLOW_COPY_AND_ASSIGN(HTTPFetcher); + DISALLOW_COPY_AND_ASSIGN(SimpleHttpFetcher); }; } // namespace media -#endif // MEDIA_BASE_HTTPFETCHER_H_ +#endif // MEDIA_BASE_HTTP_FETCHER_H_ diff --git a/media/base/httpfetcher_unittest.cc b/media/base/http_fetcher_unittest.cc similarity index 88% rename from media/base/httpfetcher_unittest.cc rename to media/base/http_fetcher_unittest.cc index c259bfe4cb..aaf1ed2269 100644 --- a/media/base/httpfetcher_unittest.cc +++ b/media/base/http_fetcher_unittest.cc @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "media/base/httpfetcher.h" +#include "media/base/http_fetcher.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" @@ -30,7 +30,7 @@ namespace media { static void CheckHttpGet(const std::string& url, const std::string& expected_response) { - HTTPFetcher fetcher; + SimpleHttpFetcher fetcher; std::string response; ASSERT_OK(fetcher.Get(url, &response)); base::RemoveChars(response, "\r\n\t ", &response); @@ -39,7 +39,7 @@ static void CheckHttpGet(const std::string& url, static void CheckHttpPost(const std::string& url, const std::string& data, const std::string& expected_response) { - HTTPFetcher fetcher; + SimpleHttpFetcher fetcher; std::string response; ASSERT_OK(fetcher.Post(url, data, &response)); base::RemoveChars(response, "\r\n\t ", &response); @@ -47,16 +47,16 @@ static void CheckHttpPost(const std::string& url, const std::string& data, } -TEST(HTTPFetcherTest, HttpGet) { +TEST(HttpFetcherTest, HttpGet) { CheckHttpGet(kTestUrl, kExpectedGetResponse); } -TEST(HTTPFetcherTest, HttpPost) { +TEST(HttpFetcherTest, HttpPost) { CheckHttpPost(kTestUrl, kPostData, kExpectedPostResponse); } -TEST(HTTPFetcherTest, InvalidUrl) { - HTTPFetcher fetcher; +TEST(HttpFetcherTest, InvalidUrl) { + SimpleHttpFetcher fetcher; std::string response; const std::string invalid_url(kTestUrl, sizeof(kTestUrl) - 2); Status status = fetcher.Get(invalid_url, &response); @@ -65,7 +65,7 @@ TEST(HTTPFetcherTest, InvalidUrl) { EndsWith(status.error_message(), base::IntToString(kHttpNotFound), true)); } -TEST(HTTPFetcherTest, UrlWithPort) { +TEST(HttpFetcherTest, UrlWithPort) { CheckHttpGet(kTestUrlWithPort, kExpectedGetResponse); } diff --git a/media/base/media_base.gyp b/media/base/media_base.gyp index 320e7eb86e..1119e343ae 100644 --- a/media/base/media_base.gyp +++ b/media/base/media_base.gyp @@ -23,11 +23,11 @@ ], }, { - 'target_name': 'httpfetcher', + 'target_name': 'http_fetcher', 'type': '<(component)', 'sources': [ - 'httpfetcher.cc', - 'httpfetcher.h', + 'http_fetcher.cc', + 'http_fetcher.h', ], 'cflags!': [ '-fno-exceptions' ], 'cflags_cc!': [ '-fno-exceptions' ], @@ -45,16 +45,16 @@ }, { # Note that this test performs real http requests to a http server. - 'target_name': 'httpfetcher_unittest', + 'target_name': 'http_fetcher_unittest', 'type': '<(gtest_target_type)', 'sources': [ - 'httpfetcher_unittest.cc', + 'http_fetcher_unittest.cc', ], 'dependencies': [ '../../base/base.gyp:base', '../../testing/gtest.gyp:gtest', '../../testing/gtest.gyp:gtest_main', - 'httpfetcher', + 'http_fetcher', ], }, { @@ -110,7 +110,7 @@ 'dependencies': [ '../../base/base.gyp:base', '../../third_party/openssl/openssl.gyp:openssl', - 'httpfetcher', + 'http_fetcher', 'status', ], }, @@ -130,6 +130,7 @@ 'status_test_util.h', 'status_test_util_unittest.cc', 'status_unittest.cc', + 'widevine_encryptor_source_unittest.cc', ], 'dependencies': [ '../../testing/gtest.gyp:gtest', diff --git a/media/base/widevine_encryptor_source.cc b/media/base/widevine_encryptor_source.cc index b0b3edc512..f5bf0c33eb 100644 --- a/media/base/widevine_encryptor_source.cc +++ b/media/base/widevine_encryptor_source.cc @@ -12,7 +12,7 @@ #include "base/time/time.h" #include "base/threading/platform_thread.h" #include "base/values.h" -#include "media/base/httpfetcher.h" +#include "media/base/http_fetcher.h" #include "media/base/request_signer.h" // TODO(kqyang): Move media/mp4/rcheck.h to media/base/. @@ -102,7 +102,8 @@ WidevineEncryptorSource::WidevineEncryptorSource( const std::string& content_id, TrackType track_type, scoped_ptr signer) - : server_url_(server_url), + : http_fetcher_(new SimpleHttpFetcher()), + server_url_(server_url), content_id_(content_id), track_type_(track_type), signer_(signer.Pass()) { @@ -120,14 +121,13 @@ Status WidevineEncryptorSource::Initialize() { return status; VLOG(1) << "Message: " << message; - HTTPFetcher fetcher; std::string raw_response; int64 sleep_duration = kFirstRetryDelayMilliseconds; // Perform client side retries if seeing server transient error to workaround // server limitation. for (int i = 0; i < kNumTransientErrorRetries; ++i) { - status = fetcher.Post(server_url_, message, &raw_response); + status = http_fetcher_->Post(server_url_, message, &raw_response); if (!status.ok()) return status; VLOG(1) << "Retry [" << i << "] Response:" << raw_response; @@ -172,6 +172,11 @@ WidevineEncryptorSource::GetTrackTypeFromString( return TRACK_TYPE_UNKNOWN; } +void WidevineEncryptorSource::set_http_fetcher( + scoped_ptr http_fetcher) { + http_fetcher_ = http_fetcher.Pass(); +} + void WidevineEncryptorSource::FillRequest(const std::string& content_id, std::string* request) { DCHECK(request); @@ -256,9 +261,8 @@ bool WidevineEncryptorSource::IsExpectedTrackType( return track_type_ == GetTrackTypeFromString(track_type_string); } -bool WidevineEncryptorSource::ExtractEncryptionKey( - const std::string& response, - bool* transient_error) { +bool WidevineEncryptorSource::ExtractEncryptionKey(const std::string& response, + bool* transient_error) { DCHECK(transient_error); *transient_error = false; diff --git a/media/base/widevine_encryptor_source.h b/media/base/widevine_encryptor_source.h index a2f1c1cf18..4c478193df 100644 --- a/media/base/widevine_encryptor_source.h +++ b/media/base/widevine_encryptor_source.h @@ -8,10 +8,12 @@ #define MEDIA_BASE_WIDEVINE_ENCRYPTOR_SOURCE_H_ #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "media/base/encryptor_source.h" namespace media { +class HttpFetcher; class RequestSigner; // Defines an encryptor source which talks to Widevine encryption server. @@ -37,6 +39,8 @@ class WidevineEncryptorSource : public EncryptorSource { static WidevineEncryptorSource::TrackType GetTrackTypeFromString( const std::string& track_type_string); + void set_http_fetcher(scoped_ptr http_fetcher); + private: // Fill |request| with necessary fields for Widevine encryption request. // |request| should not be NULL. @@ -55,6 +59,10 @@ class WidevineEncryptorSource : public EncryptorSource { bool ExtractEncryptionKey(const std::string& response, bool* transient_error); + // The fetcher object used to fetch HTTP response from server. + // It is initialized to a default fetcher on class initialization. + // Can be overridden using set_http_fetcher for testing or other purposes. + scoped_ptr http_fetcher_; std::string server_url_; std::string content_id_; TrackType track_type_; diff --git a/media/base/widevine_encryptor_source_unittest.cc b/media/base/widevine_encryptor_source_unittest.cc new file mode 100644 index 0000000000..cb40cd30c7 --- /dev/null +++ b/media/base/widevine_encryptor_source_unittest.cc @@ -0,0 +1,232 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "media/base/widevine_encryptor_source.h" + +#include "base/base64.h" +#include "base/strings/stringprintf.h" +#include "media/base/http_fetcher.h" +#include "media/base/request_signer.h" +#include "media/base/status_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kServerUrl[] = "http://www.foo.com/getcontentkey"; +const char kContentId[] = "ContentFoo"; +const char kTrackType[] = "SD"; +const char kSignerName[] = "SignerFoo"; + +const char kMockSignature[] = "MockSignature"; + +const char kMockKeyId[] = "MockKeyId"; +const char kMockKey[] = "MockKey"; +const char kMockPsshData[] = "MockPsshData"; + +// The lisence service may return an error indicating a transient error has +// just happened in the server, or other types of errors. +// WidevineEncryptorSource will perform a number of retries on transient errors; +// WidevineEncryptorSource does not know about other errors and retries are not +// performed. +const char kLicenseStatusTransientError[] = "INTERNAL_ERROR"; +const char kLicenseStatusUnknownError[] = "UNKNOWN_ERROR"; + +const char kExpectedRequestMessageFormat[] = + "{\"content_id\":\"%s\",\"drm_types\":[\"WIDEVINE\"],\"policy\":\"\"," + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; +const char kExpectedSignedMessageFormat[] = + "{\"request\":\"%s\",\"signature\":\"%s\",\"signer\":\"%s\"}"; +const char kLicenseOkResponseFormat[] = + "{\"status\":\"OK\",\"tracks\":[{\"type\":\"%s\",\"key_id\":\"%s\",\"key\":" + "\"%s\",\"pssh\":[{\"drm_type\":\"WIDEVINE\",\"data\":\"%s\"}]}]}"; +const char kLicenseErrorResponseFormat[] = + "{\"status\":\"%s\",\"drm\":[],\"tracks\":[]}"; +const char kHttpResponseFormat[] = "{\"response\":\"%s\"}"; + +std::string Base64Encode(const std::string& input) { + std::string output; + base::Base64Encode(input, &output); + return output; +} + +std::string ToString(const std::vector v) { + return std::string(v.begin(), v.end()); +} +} // namespace + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; + +namespace media { + +class MockRequestSigner : public RequestSigner { + public: + explicit MockRequestSigner(const std::string& signer_name) + : RequestSigner(signer_name) {} + virtual ~MockRequestSigner() {} + + MOCK_METHOD2(GenerateSignature, + bool(const std::string& message, std::string* signature)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockRequestSigner); +}; + +class MockHttpFetcher : public HttpFetcher { + public: + MockHttpFetcher() : HttpFetcher() {} + virtual ~MockHttpFetcher() {} + + MOCK_METHOD2(Get, Status(const std::string& url, std::string* response)); + MOCK_METHOD3(Post, + Status(const std::string& url, + const std::string& data, + std::string* response)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockHttpFetcher); +}; + +class WidevineEncryptorSourceTest : public ::testing::Test { + public: + WidevineEncryptorSourceTest() + : mock_request_signer_(new MockRequestSigner(kSignerName)), + mock_http_fetcher_(new MockHttpFetcher()) {} + + protected: + void CreateWidevineEncryptorSource() { + widevine_encryptor_source_.reset(new WidevineEncryptorSource( + kServerUrl, + kContentId, + WidevineEncryptorSource::GetTrackTypeFromString(kTrackType), + mock_request_signer_.PassAs())); + widevine_encryptor_source_->set_http_fetcher( + mock_http_fetcher_.PassAs()); + } + + scoped_ptr mock_request_signer_; + scoped_ptr mock_http_fetcher_; + scoped_ptr widevine_encryptor_source_; + + private: + DISALLOW_COPY_AND_ASSIGN(WidevineEncryptorSourceTest); +}; + +TEST_F(WidevineEncryptorSourceTest, GetTrackTypeFromString) { + EXPECT_EQ(WidevineEncryptorSource::TRACK_TYPE_SD, + WidevineEncryptorSource::GetTrackTypeFromString("SD")); + EXPECT_EQ(WidevineEncryptorSource::TRACK_TYPE_HD, + WidevineEncryptorSource::GetTrackTypeFromString("HD")); + EXPECT_EQ(WidevineEncryptorSource::TRACK_TYPE_AUDIO, + WidevineEncryptorSource::GetTrackTypeFromString("AUDIO")); + EXPECT_EQ(WidevineEncryptorSource::TRACK_TYPE_UNKNOWN, + WidevineEncryptorSource::GetTrackTypeFromString("FOO")); +} + +TEST_F(WidevineEncryptorSourceTest, GeneratureSignatureFailure) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(false)); + + CreateWidevineEncryptorSource(); + ASSERT_EQ(Status(error::INTERNAL_ERROR, "Signature generation failed."), + widevine_encryptor_source_->Initialize()); +} + +// Check whether expected request message and post data was generated and +// verify the correct behavior on http failure. +TEST_F(WidevineEncryptorSourceTest, HttpPostFailure) { + std::string expected_message = base::StringPrintf( + kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str()); + EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _)) + .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); + + std::string expected_post_data = + base::StringPrintf(kExpectedSignedMessageFormat, + Base64Encode(expected_message).c_str(), + Base64Encode(kMockSignature).c_str(), + kSignerName); + const Status kMockStatus = Status::UNKNOWN; + EXPECT_CALL(*mock_http_fetcher_, Post(kServerUrl, expected_post_data, _)) + .WillOnce(Return(kMockStatus)); + + CreateWidevineEncryptorSource(); + ASSERT_EQ(kMockStatus, widevine_encryptor_source_->Initialize()); +} + +TEST_F(WidevineEncryptorSourceTest, LicenseStatusOK) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_license_status = + base::StringPrintf(kLicenseOkResponseFormat, + kTrackType, + Base64Encode(kMockKeyId).c_str(), + Base64Encode(kMockKey).c_str(), + Base64Encode(kMockPsshData).c_str()); + std::string expected_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(mock_license_status).c_str()); + + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(expected_response), Return(Status::OK))); + + CreateWidevineEncryptorSource(); + ASSERT_OK(widevine_encryptor_source_->Initialize()); + EXPECT_EQ(kMockKeyId, ToString(widevine_encryptor_source_->key_id())); + EXPECT_EQ(kMockKey, ToString(widevine_encryptor_source_->key())); + EXPECT_EQ(kMockPsshData, ToString(widevine_encryptor_source_->pssh())); +} + +TEST_F(WidevineEncryptorSourceTest, RetryOnTransientError) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_license_status = base::StringPrintf( + kLicenseErrorResponseFormat, kLicenseStatusTransientError); + std::string expected_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(mock_license_status).c_str()); + + std::string mock_retried_license_status = + base::StringPrintf(kLicenseOkResponseFormat, + kTrackType, + Base64Encode(kMockKeyId).c_str(), + Base64Encode(kMockKey).c_str(), + Base64Encode(kMockPsshData).c_str()); + std::string expected_retried_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(mock_retried_license_status).c_str()); + + // Retry is expected on transient error. + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(expected_response), Return(Status::OK))) + .WillOnce(DoAll(SetArgPointee<2>(expected_retried_response), + Return(Status::OK))); + + CreateWidevineEncryptorSource(); + ASSERT_OK(widevine_encryptor_source_->Initialize()); + EXPECT_EQ(kMockKeyId, ToString(widevine_encryptor_source_->key_id())); + EXPECT_EQ(kMockKey, ToString(widevine_encryptor_source_->key())); + EXPECT_EQ(kMockPsshData, ToString(widevine_encryptor_source_->pssh())); +} + +TEST_F(WidevineEncryptorSourceTest, NoRetryOnUnknownError) { + EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) + .WillOnce(Return(true)); + + std::string mock_license_status = base::StringPrintf( + kLicenseErrorResponseFormat, kLicenseStatusUnknownError); + std::string mock_response = base::StringPrintf( + kHttpResponseFormat, Base64Encode(mock_license_status).c_str()); + + EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); + + CreateWidevineEncryptorSource(); + ASSERT_EQ(error::SERVER_ERROR, + widevine_encryptor_source_->Initialize().error_code()); +} + +} // namespace media