Update AddEntry() for usage table changes.

[ Merge of http://go/wvgerrit/96071 ]

Changes to how the usage table method InvalidateEntry() behaves
required additional changes to CDM code that uses this method.

This involved some refactoring to AddEntry(), moving the LRU
related code to its own function.

A few unittests had to be changed / removed as the moving
multiple entries changes expectations of several existing tests.

Several additional helper methods have been created to improve
readability.  These include getters for information about the
usage table, a method for releasing stale entries, and a method of
recording LRU metrics.

Bug: 150890014
Bug: 150887808
Bug: 154269671
Test: Linux unit tests and Android unit tests
Change-Id: I11a98f9a2dea9b2ae57b37d7d4483a37be721763
This commit is contained in:
Alex Dale
2020-03-18 17:17:03 -07:00
parent 1e5e221909
commit 66e3d69300
3 changed files with 130 additions and 302 deletions

View File

@@ -93,6 +93,13 @@ class UsageTableHeader {
size_t potential_table_capacity() const { return potential_table_capacity_; }
// Returns the number of entries currently tracked by the CDM that
// are related to usage info (streaming licenses).
size_t UsageInfoCount() const;
// Returns the number of entries currently tracked by the CDM that
// are related to offline licenses.
size_t OfflineEntryCount() const;
const std::vector<CdmUsageEntryInfo>& usage_entry_info() const {
return usage_entry_info_;
}
@@ -139,6 +146,10 @@ class UsageTableHeader {
CdmResponseType DefragTable(DeviceFiles* device_files,
metrics::CryptoMetrics* metrics);
// This will use the LRU algorithm to decide which entry is to be
// evicted.
CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics);
virtual bool is_inited() { return is_inited_; }
// Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a
@@ -149,6 +160,12 @@ class UsageTableHeader {
int64_t GetCurrentTime() { return clock_ref_->GetCurrentTime(); }
// Sets LRU related metrics based on the provided |staleness| (in
// seconds) and |storage_type| of the entry removed.
void RecordLruEventMetrics(metrics::CryptoMetrics* metrics,
uint64_t staleness,
CdmUsageEntryStorageType storage_type);
// Uses an LRU-base algorithm to determine which licenses should be
// removed. This is intended to be used if the usage table is full
// and a new entry needs to be added.
@@ -212,7 +229,7 @@ class UsageTableHeader {
// Synchonizes access to the Usage Table Header and bookkeeping
// data-structures
std::mutex usage_table_header_lock_;
mutable std::mutex usage_table_header_lock_;
metrics::CryptoMetrics alternate_crypto_metrics_;

View File

@@ -16,7 +16,6 @@ namespace wvcdm {
namespace {
std::string kEmptyString;
size_t kMaxCryptoRetries = 3;
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
@@ -24,6 +23,7 @@ std::string kOldUsageEntryPoviderSessionToken =
"nahZ6achSheiqua3TohQuei0ahwohv";
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
// Number of elements to be considered for removal using the LRU algorithm.
// TODO(b/155230578): Remove this constant.
constexpr size_t kLruRemovalSetSize = 3;
// Fraction of table capacity of number of unexpired offline licenses
// before they are considered to be removed. This could occur if
@@ -132,6 +132,16 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files,
}
return true;
}
bool EntryIsUsageInfo(const CdmUsageEntryInfo& info) {
// Used for stl filters.
return info.storage_type == kStorageUsageInfo;
}
bool EntryIsOfflineLicense(const CdmUsageEntryInfo& info) {
// Used for stl filters.
return info.storage_type == kStorageLicense;
}
} // namespace
UsageTableHeader::UsageTableHeader()
@@ -237,6 +247,13 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
result = InvalidateEntry(temporary_usage_entry_number,
/* defrag_table = */ true,
device_files_.get(), metrics);
if (usage_entry_info_.size() > temporary_usage_entry_number) {
// The entry should have been deleted from the usage table,
// not just marked as type unknown. Failure to call
// Shrink() may be an indicator of other issues.
LOGE("Temporary entry was not deleted");
result = UNKNOWN_ERROR;
}
}
if (result != NO_ERROR) {
LOGE(
@@ -284,66 +301,11 @@ CdmResponseType UsageTableHeader::AddEntry(
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3) {
// If usage entry creation fails due to insufficient resources, release an
// entry based on LRU.
std::vector<uint32_t> removal_candidates;
if (!GetRemovalCandidates(&removal_candidates)) {
LOGE("Could not determine which license to remove");
return status;
}
// Variables for metrics.
const size_t usage_info_count =
std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
[](const CdmUsageEntryInfo& usage_entry) {
return usage_entry.storage_type == kStorageUsageInfo;
});
const size_t license_count =
std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
[](const CdmUsageEntryInfo& usage_entry) {
return usage_entry.storage_type == kStorageLicense;
});
int64_t staleness_of_removed;
CdmUsageEntryStorageType storage_type_of_removed;
const int64_t current_time = GetCurrentTime();
for (size_t i = 0; i < removal_candidates.size() &&
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
++i) {
const uint32_t entry_number_to_invalidate = removal_candidates[i];
// Calculate metric values.
staleness_of_removed =
current_time -
usage_entry_info_[entry_number_to_invalidate].last_use_time;
storage_type_of_removed =
usage_entry_info_[entry_number_to_invalidate].storage_type;
if (InvalidateEntry(entry_number_to_invalidate,
/* defrag_table = */ true, device_files_.get(),
metrics) == NO_ERROR) {
// If the entry was deleted, it is still possible for the create new
// entry to fail. If so, we must ensure that the previously last
// entry was not in the |removal_candidates| as it has now been swapped
// with the deleted entry.
for (uint32_t& entry_number : removal_candidates) {
if (entry_number == usage_entry_info_.size()) {
entry_number = entry_number_to_invalidate;
}
}
}
LOGW("Usage table may be full, releasing oldest entry: size = %zu",
usage_entry_info_.size());
status = ReleaseOldestEntry(metrics);
if (status == NO_ERROR) {
status = crypto_session->CreateUsageEntry(usage_entry_number);
// Record metrics on success.
if (status == NO_ERROR) {
metrics->usage_table_header_lru_usage_info_count_.Record(
usage_info_count);
metrics->usage_table_header_lru_offline_license_count_.Record(
license_count);
metrics->usage_table_header_lru_evicted_entry_staleness_.Record(
staleness_of_removed);
metrics->usage_table_header_lru_evicted_entry_type_.Record(
static_cast<int>(storage_type_of_removed));
}
}
}
@@ -422,28 +384,9 @@ CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
CdmResponseType status =
const CdmResponseType status =
crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
// If loading a usage entry fails due to insufficient resources, release a
// random entry different from |usage_entry_number| and try again. If there
// are no more entries to release, we fail.
for (uint32_t retry_count = 0; retry_count < kMaxCryptoRetries &&
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
++retry_count) {
if (usage_entry_info_.size() <= 1) break;
// Get a random entry from the other entries.
uint32_t entry_number_to_invalidate =
CdmRandom::RandomInRange(usage_entry_info_.size() - 2);
if (entry_number_to_invalidate >= usage_entry_number) {
// Exclude |usage_entry_number|.
++entry_number_to_invalidate;
}
InvalidateEntry(entry_number_to_invalidate, /* defrag_table = */ true,
device_files_.get(), metrics);
status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
}
if (status == NO_ERROR) {
usage_entry_info_[usage_entry_number].last_use_time = GetCurrentTime();
}
@@ -518,6 +461,20 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
return NO_ERROR;
}
size_t UsageTableHeader::UsageInfoCount() const {
LOGI("Locking to count usage info (streaming license) entries");
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
EntryIsUsageInfo);
}
size_t UsageTableHeader::OfflineEntryCount() const {
LOGI("Locking to count offline license entries");
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
EntryIsOfflineLicense);
}
CdmResponseType UsageTableHeader::MoveEntry(
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
uint32_t to_usage_entry_number, DeviceFiles* device_files,
@@ -986,6 +943,40 @@ CdmResponseType UsageTableHeader::DefragTable(DeviceFiles* device_files,
return Shrink(metrics, to_remove);
} // End Defrag().
CdmResponseType UsageTableHeader::ReleaseOldestEntry(
metrics::CryptoMetrics* metrics) {
LOGV("Releasing oldest entry");
std::vector<uint32_t> removal_candidates;
if (!GetRemovalCandidates(&removal_candidates)) {
LOGE("Could not determine which license to remove");
return UNKNOWN_ERROR;
}
// Only release one entry.
const uint32_t entry_number_to_delete = removal_candidates.front();
const CdmUsageEntryInfo& usage_entry_info =
usage_entry_info_[entry_number_to_delete];
const int64_t current_time = GetCurrentTime();
// Capture metric values now, as the |usage_entry_info| reference will
// change after the call to invalidate.
const int64_t staleness = current_time - usage_entry_info.last_use_time;
const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type;
const CdmResponseType status =
InvalidateEntry(entry_number_to_delete, /* defrag_table = */ true,
device_files_.get(), metrics);
if (status != NO_ERROR) {
LOGE("Failed to invalidate oldest entry: status = %d",
static_cast<int>(status));
return status;
}
// Record metrics on success.
RecordLruEventMetrics(metrics, staleness, storage_type);
return NO_ERROR;
}
// Test only method.
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number);
@@ -1135,7 +1126,20 @@ bool UsageTableHeader::GetRemovalCandidates(
removal_candidates);
}
void UsageTableHeader::RecordLruEventMetrics(
metrics::CryptoMetrics* metrics, uint64_t staleness,
CdmUsageEntryStorageType storage_type) {
if (metrics == nullptr) return;
metrics->usage_table_header_lru_usage_info_count_.Record(UsageInfoCount());
metrics->usage_table_header_lru_offline_license_count_.Record(
OfflineEntryCount());
metrics->usage_table_header_lru_evicted_entry_staleness_.Record(staleness);
metrics->usage_table_header_lru_evicted_entry_type_.Record(
static_cast<int>(storage_type));
}
// Static.
// TODO(b/155230578): Change this function to only return 1 entry.
bool UsageTableHeader::DetermineLicenseToRemove(
const std::vector<CdmUsageEntryInfo>& usage_entry_info_list,
int64_t current_time, size_t unexpired_threshold, size_t removal_count,

View File

@@ -350,28 +350,6 @@ void ToVector(std::vector<CdmUsageEntryInfo>& vec,
}
}
// Used to quickly populate a vector of CdmUsageEntryInfo structs with LRU
// information. This is intended to allow tests which are not concerned with
// the LRU replacement policy of the UsageTableHeader, but are affected by its
// presents.
void GenericLruUpgrade(std::vector<CdmUsageEntryInfo>* usage_entry_info_list,
int64_t last_use_time = kLruBaseTime,
int64_t offline_license_expiry_time =
kLruBaseTime + kDefaultExpireDuration) {
if (usage_entry_info_list == nullptr) {
return;
}
for (auto& usage_entry_info : *usage_entry_info_list) {
usage_entry_info.last_use_time = last_use_time;
if (usage_entry_info.storage_type == kStorageLicense) {
usage_entry_info.offline_license_expiry_time =
offline_license_expiry_time;
} else {
usage_entry_info.offline_license_expiry_time = 0;
}
}
}
}; // namespace
class MockDeviceFiles : public DeviceFiles {
@@ -484,6 +462,7 @@ using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Ge;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Lt;
using ::testing::NotNull;
using ::testing::Return;
@@ -751,7 +730,7 @@ TEST_P(UsageTableHeaderInitializationTest,
usage_entries_202))
.WillOnce(Return(true));
// Expectations for InvalidateEntry
// Expectations for InvalidateEntry, assumes no entry other entry is invalid.
EXPECT_CALL(*crypto_session_, Open(security_level))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_,
@@ -914,7 +893,8 @@ TEST_F(UsageTableHeaderTest,
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
std::vector<CdmUsageEntryInfo> final_usage_entries;
uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 1;
const uint32_t expected_usage_entry_number =
k10UsageEntryInfoVector.size() - 1;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
@@ -959,123 +939,50 @@ TEST_F(UsageTableHeaderTest,
EXPECT_EQ(expected_usage_entries, final_usage_entries);
}
TEST_F(UsageTableHeaderTest,
AddEntry_CreateUsageEntryFailsTwice_SucceedsThirdTime) {
// Initialize and setup
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
k10UsageEntryInfoVector;
GenericLruUpgrade(&usage_entry_info_vector_at_start);
Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector_at_start);
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen
std::vector<CdmUsageEntryInfo> final_usage_entries;
uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 2;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
.WillOnce(
DoAll(SetArgPointee<0>(expected_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, _))
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
// Now invoke the method under test
uint32_t usage_entry_number;
EXPECT_EQ(NO_ERROR,
mock_usage_table_header->AddEntry(
crypto_session_,
kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense,
kUsageEntryInfoOfflineLicense6.key_set_id,
kUsageEntryInfoOfflineLicense6.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
// Verify added/deleted usage entry number and entries
EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
EXPECT_LE(0u, usage_entry_number_first_to_be_deleted);
EXPECT_LE(usage_entry_number_first_to_be_deleted,
usage_entry_info_vector_at_start.size() - 1);
EXPECT_LE(0u, usage_entry_number_second_to_be_deleted);
EXPECT_LE(usage_entry_number_second_to_be_deleted,
usage_entry_info_vector_at_start.size() - 1);
std::vector<CdmUsageEntryInfo> expected_usage_entries =
usage_entry_info_vector_at_start;
expected_usage_entries[usage_entry_number_first_to_be_deleted] =
expected_usage_entries[expected_usage_entries.size() - 1];
expected_usage_entries.resize(expected_usage_entries.size() - 1);
expected_usage_entries[usage_entry_number_second_to_be_deleted] =
expected_usage_entries[expected_usage_entries.size() - 1];
expected_usage_entries.resize(expected_usage_entries.size() - 1);
expected_usage_entries.push_back(kUsageEntryInfoOfflineLicense6);
EXPECT_EQ(expected_usage_entries, final_usage_entries);
}
TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsThrice) {
// The usage table should only delete/invalidate a single entry.
// After which, it should fail.
TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
// Initialize and setup
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
k10UsageEntryInfoVector;
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen
uint32_t usage_entry_number_third_to_be_deleted; // randomly chosen
std::vector<CdmUsageEntryInfo> final_usage_entries;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_third_to_be_deleted),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.Times(4)
.Times(2)
.WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3));
// Now invoke the method under test
uint32_t usage_entry_number;
EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3,
mock_usage_table_header->AddEntry(
crypto_session_,
kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense,
crypto_session_, true /* persistent */,
kUsageEntryInfoOfflineLicense6.key_set_id,
kUsageEntryInfoOfflineLicense6.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
// Verify deleted usage entry number and entries
EXPECT_LE(0u, usage_entry_number_first_to_be_deleted);
EXPECT_LE(usage_entry_number_first_to_be_deleted,
usage_entry_info_vector_at_start.size() - 1);
EXPECT_LE(0u, usage_entry_number_second_to_be_deleted);
EXPECT_LE(usage_entry_number_second_to_be_deleted,
usage_entry_info_vector_at_start.size() - 1);
EXPECT_LE(0u, usage_entry_number_third_to_be_deleted);
EXPECT_LE(usage_entry_number_third_to_be_deleted,
usage_entry_info_vector_at_start.size() - 1);
// Verify the number of entries deleted.
constexpr uint32_t kExpectedEntriesDeleted = 1;
const std::vector<CdmUsageEntryInfo>& final_usage_entries =
mock_usage_table_header->usage_entry_info();
uint32_t invalid_entries = 0;
for (const CdmUsageEntryInfo& usage_entry_info : final_usage_entries) {
if (usage_entry_info.storage_type == kStorageTypeUnknown) {
++invalid_entries;
}
}
// Number of entries deleted is equal to the number of entries
// marked as invalid plus the number of fewer entries in the table
// at the end of the call.
const uint32_t entries_deleted =
invalid_entries +
(k10UsageEntryInfoVector.size() - final_usage_entries.size());
EXPECT_EQ(kExpectedEntriesDeleted, entries_deleted);
}
TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) {
@@ -1139,106 +1046,6 @@ TEST_F(UsageTableHeaderTest, UpdateEntry) {
usage_table_header_->UpdateEntry(0, crypto_session_, &usage_entry));
}
TEST_F(UsageTableHeaderTest,
LoadEntry_LoadUsageEntryFailsOnce_SucceedsSecondTime) {
// Initialize and setup
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
// We try to load a usage entry from the first 9 entries, since
// InvalidateEntry can't delete an entry if the last one is in use.
uint32_t usage_entry_number_to_load =
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 2);
CdmUsageEntry usage_entry_to_load = kUsageEntry;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.Times(1)
.WillRepeatedly(
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_,
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
.WillOnce(Return(NO_ERROR));
// Now invoke the method under test
EXPECT_EQ(NO_ERROR,
mock_usage_table_header->LoadEntry(
crypto_session_,
usage_entry_to_load,
usage_entry_number_to_load));
}
TEST_F(UsageTableHeaderTest,
LoadEntry_LoadUsageEntryFailsTwice_SucceedsThirdTime) {
// Initialize and setup
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
// We try to load a usage entry from the first 8 entries, since
// InvalidateEntry can't delete an entry if the last one is in use.
uint32_t usage_entry_number_to_load =
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 3);
CdmUsageEntry usage_entry_to_load = kUsageEntry;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.Times(2)
.WillRepeatedly(
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_,
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
.WillOnce(Return(NO_ERROR));
// Now invoke the method under test
EXPECT_EQ(NO_ERROR,
mock_usage_table_header->LoadEntry(
crypto_session_,
usage_entry_to_load,
usage_entry_number_to_load));
}
TEST_F(UsageTableHeaderTest, LoadEntry_LoadUsageEntryFailsThrice) {
// Initialize and setup
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
// We try to load a usage entry from the first 7 entries, since
// InvalidateEntry can't delete an entry if the last one is in use.
uint32_t usage_entry_number_to_load =
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 4);
CdmUsageEntry usage_entry_to_load = kUsageEntry;
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.Times(3)
.WillRepeatedly(
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_,
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
.Times(4)
.WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3));
// Now invoke the method under test
EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3,
mock_usage_table_header->LoadEntry(
crypto_session_,
usage_entry_to_load,
usage_entry_number_to_load));
}
TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number = kUsageEntryInfoVector.size();