Create custom gtest matcher for similar URLs.
[ Cherry-pick of http://ag/19893913 ]
[ Merge of http://go/wvgerrit/157098 ]
Several of the Android integration tests perform direct URL comparisons
between fixed URLs and the server URL returned by the CDM. With
provisioning 4.0, the CDM will append additional query parameters to
the server URL. This updated URL still contains all of the original
expected information, but with additional parameters. So long as the
URL contains the required fields, any additional parameter should be
considered valid.
The gtest framework used by the integration tests allow for the
creation of custom "matchers", rules that can be used to validate data
and create informative failure logs. The CL creates a new matcher for
checking that a tested URL is a superset of content of the expected
URL.
Bug: 244319313
Test: request_license_test on prov 4 device
Change-Id: Ie721058fa628b3a4a74dc56f4172a3dfcb1f1ef3
(cherry picked from commit fa8c0a9a62)
This commit is contained in:
@@ -1548,6 +1548,118 @@ FourSampleDecryptionInfo kCenc30SwitchCipherData[8] = {
|
||||
}},
|
||||
};
|
||||
|
||||
// Extracts the scheme, hostname and path from the provided URL.
|
||||
// Example:
|
||||
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
||||
// Output: "https://www.widevine.com/service"
|
||||
std::string GetUrlHostAndPath(const std::string& full_url) {
|
||||
const auto start_of_params = full_url.find('?');
|
||||
if (start_of_params == std::string::npos) return full_url;
|
||||
return full_url.substr(0, start_of_params);
|
||||
}
|
||||
|
||||
// Extracts and splits the query parameters from the provided URL.
|
||||
// Returns an empty list if no parameters are found.
|
||||
//
|
||||
// Example:
|
||||
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
||||
// Output: {"id=1234", "device=pixel", "flag"}
|
||||
std::vector<std::string> GetUrlQueryParamPairs(const std::string& full_url) {
|
||||
const auto start_of_params = full_url.find('?');
|
||||
if (start_of_params == std::string::npos) return {}; // No params.
|
||||
// Remove host and path.
|
||||
const std::string all_params = full_url.substr(start_of_params + 1);
|
||||
if (all_params.empty()) return {};
|
||||
// Check if there are more than 1 parameters.
|
||||
auto param_separator = all_params.find('&');
|
||||
if (param_separator == std::string::npos) {
|
||||
return {all_params}; // Only 1 parameter.
|
||||
}
|
||||
// Split parameters by '&'.
|
||||
std::vector<std::string> params;
|
||||
params.push_back(all_params.substr(0, param_separator));
|
||||
while (param_separator != std::string::npos) {
|
||||
auto next_param_separator = all_params.find('&', param_separator + 1);
|
||||
if (next_param_separator == std::string::npos) {
|
||||
params.push_back(all_params.substr(param_separator + 1));
|
||||
} else {
|
||||
params.push_back(all_params.substr(
|
||||
param_separator + 1, next_param_separator - param_separator - 1));
|
||||
}
|
||||
param_separator = next_param_separator;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
// Extracts and maps the query parameters from the provided URL.
|
||||
// Creates a map between the parameter keys and values. Parameters
|
||||
// that are only keys have an empty string value.
|
||||
//
|
||||
// Example:
|
||||
// Input: "https://www.widevine.com/service?id=1234&device=pixel&flag"
|
||||
// Output: {"id": "1234", "device": "pixel", "flag": ""}
|
||||
std::map<std::string, std::string> GetUrlQueryParams(
|
||||
const std::string& full_url) {
|
||||
const std::vector<std::string> param_pairs = GetUrlQueryParamPairs(full_url);
|
||||
std::map<std::string, std::string> params;
|
||||
if (param_pairs.empty()) return params;
|
||||
for (const auto& pair : param_pairs) {
|
||||
const auto value_separator = pair.find('=');
|
||||
if (value_separator == std::string::npos) {
|
||||
// Just the key.
|
||||
params.emplace(pair, "");
|
||||
} else {
|
||||
params.emplace(pair.substr(0, value_separator),
|
||||
pair.substr(value_separator + 1));
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
// Checks that the |actual_url| is a super set of information compared
|
||||
// to the |expected_url|. The scheme, hostname and path must be the
|
||||
// same. The |actual_url| must contain at all the query parameters of
|
||||
// the |expected_url|. Order of the query parameters do not matter.
|
||||
//
|
||||
// Example A:
|
||||
// Expected: "https://www.widevine.com/service?key=1234"
|
||||
// Actual: "https://www.widevine.com/service?retry=true&key=1234"
|
||||
// Result: true
|
||||
//
|
||||
// Example B:
|
||||
// Expected: "https://www.widevine.com/service?key=1234&retry=true"
|
||||
// Actual: "https://www.widevine.com/service?key=1234"
|
||||
// Result: false
|
||||
//
|
||||
// Example C:
|
||||
// Expected: "https://www.widevine.com/service?key=1234"
|
||||
// Actual: "https://www.widevine.org/service?key=1234"
|
||||
// Result: false
|
||||
bool IsUrlSimilar(const std::string& expected_url,
|
||||
const std::string& actual_url) {
|
||||
// First check the host and path.
|
||||
const std::string expected_host_and_path = GetUrlHostAndPath(expected_url);
|
||||
const std::string actual_host_and_path = GetUrlHostAndPath(actual_url);
|
||||
if (expected_host_and_path != actual_host_and_path) {
|
||||
return false; // Bad host and/or path.
|
||||
}
|
||||
// Compare query parameters.
|
||||
const std::map<std::string, std::string> expected_params =
|
||||
GetUrlQueryParams(expected_url);
|
||||
const std::map<std::string, std::string> actual_params =
|
||||
GetUrlQueryParams(actual_url);
|
||||
if (actual_params.size() < expected_params.size()) {
|
||||
return false; // Missing params.
|
||||
}
|
||||
for (const auto& expected_param : expected_params) {
|
||||
const auto actual_param = actual_params.find(expected_param.first);
|
||||
if (actual_param == actual_params.end())
|
||||
return false; // Missing particular param.
|
||||
if (actual_param->second != expected_param.second)
|
||||
return false; // Incorrect param value.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -1556,6 +1668,10 @@ using video_widevine::ClientIdentification;
|
||||
using video_widevine::ClientIdentification_NameValue;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
MATCHER_P(IsSimilarUrlTo, expected_url, "") {
|
||||
return IsUrlSimilar(expected_url, arg);
|
||||
}
|
||||
|
||||
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
TestWvCdmClientPropertySet()
|
||||
@@ -2058,7 +2174,8 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
&provisioning_server);
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, status);
|
||||
if (NO_ERROR != status) return;
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
CdmProvisioningResponse response =
|
||||
GetCertRequestResponse(config_.provisioning_server());
|
||||
@@ -2177,7 +2294,8 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg_,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -2202,7 +2320,8 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTestWithServiceCertificate) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
config_.provisioning_service_certificate(), kLevelDefault,
|
||||
&key_msg_, &provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -2227,7 +2346,8 @@ TEST_F(WvCdmRequestLicenseTest, L3ProvisioningTest) {
|
||||
decryptor_->GetProvisioningRequest(
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevel3, &key_msg_, &provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -2330,14 +2450,16 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg1,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||
decryptor_->GetProvisioningRequest(
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg2,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
// Second message should succeed.
|
||||
key_msg_ = key_msg2;
|
||||
@@ -2375,14 +2497,16 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterspersedRetryTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg1,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR,
|
||||
decryptor_->GetProvisioningRequest(
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg2,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
// First request expected to fail, because only one message may be active.
|
||||
key_msg_ = key_msg1;
|
||||
@@ -2421,7 +2545,8 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_X509ProvisioningTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &key_msg_,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -2512,7 +2637,8 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) {
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
||||
if (NO_ERROR != result) return;
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
std::vector<uint8_t> request =
|
||||
@@ -2550,7 +2676,8 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) {
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
||||
if (NO_ERROR != result) return;
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
|
||||
ProvisioningResponse provisioning_response;
|
||||
provisioning_response.set_status(status);
|
||||
@@ -2663,7 +2790,8 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
std::string response =
|
||||
GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -2740,7 +2868,8 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
std::string response = GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
@@ -3150,7 +3279,8 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseRetryL3OfflineKeyTest) {
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
||||
&provisioning_server));
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
std::string response =
|
||||
GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
@@ -3220,7 +3350,8 @@ TEST_F(WvCdmRequestLicenseTest,
|
||||
cert_type, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevel3, &key_msg_,
|
||||
&provisioning_server_url));
|
||||
EXPECT_EQ(provisioning_server_url, kDefaultProvisioningServerUrl);
|
||||
EXPECT_THAT(provisioning_server_url,
|
||||
IsSimilarUrlTo(kDefaultProvisioningServerUrl));
|
||||
std::string response =
|
||||
GetCertRequestResponse(config_.provisioning_server());
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
|
||||
Reference in New Issue
Block a user