New usage entries are moved lower after creation.

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

When the CDM creates a new usage entry for an offline or streaming
license, the new entry is immediately moved to the lowest available
entry index that has been marked as vacant (kStorageTypeUnknown).

When a license is released, its meta data that is managed by the CDM
is cleared; however, the usage entry's index is marked vacant, but it
is not released.  This creates wasted entry space in the usage table.
Unfortunately, defragging the table is computationally expensive and
may not be able to actually free up much space depending on when it
is performed.

For a typical user, this will likely not be an issue as the table
can get quite large compared to the number of licenses an app uses
and the table is partially cleaned on each boot.

GTS tests, however, have reached a point where they fill the usage
table before all tests are complete.  This is causing many unexpected
failures for devices.  Most of these tests release their license, but
the CDM never reaches a state where it can clean up the table.

By moving newly created entries to the lowest available index directly
after creating the entries, the table never needs to grow unless all
entries are in use.  Clean up is now almost never required.

Bug: 180639135
Bug: 180638990
Bug: 180638530
Test: MediaDrmTest#testWidevineApi28
Change-Id: I1a68d90d51384094298b27037778747ce7435374
This commit is contained in:
Alex Dale
2021-04-29 11:00:51 -07:00
parent 023b06eded
commit 884550333d
3 changed files with 504 additions and 141 deletions

View File

@@ -202,8 +202,16 @@ const DeviceFiles::CdmUsageData kCdmUsageData3 = {
const std::vector<DeviceFiles::CdmUsageData> kEmptyUsageInfoUsageDataList;
const std::vector<CdmUsageEntryInfo> kEmptyUsageEntryInfoVector;
std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector;
std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector;
const std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector = {
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
kUsageEntryInfoStorageTypeUnknown};
const std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector = {
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
kUsageEntryInfoOfflineLicense2, kUsageEntryInfoSecureStop2,
kUsageEntryInfoOfflineLicense3, kUsageEntryInfoSecureStop3,
kUsageEntryInfoOfflineLicense4, kUsageEntryInfoSecureStop4,
kUsageEntryInfoOfflineLicense5, kUsageEntryInfoSecureStop5,
};
std::vector<CdmUsageEntryInfo> kOverFullUsageEntryInfoVector;
const CdmOfflineLicenseState kActiveLicenseState = kLicenseStateActive;
@@ -289,23 +297,6 @@ std::vector<CdmUsageEntryInfo> kUpgradedUsageEntryInfoList;
namespace {
void InitVectorConstants() {
kUsageEntryInfoVector.clear();
kUsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1);
kUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1);
kUsageEntryInfoVector.push_back(kUsageEntryInfoStorageTypeUnknown);
k10UsageEntryInfoVector.clear();
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense2);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop2);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense3);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop3);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense4);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop4);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense5);
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop5);
kOverFullUsageEntryInfoVector.clear();
for (size_t i = 0; i < (kDefaultTableCapacity + 1); ++i) {
switch (i % 4) {
@@ -479,8 +470,9 @@ class MockCryptoSession : public TestCryptoSession {
class MockUsageTableHeader : public UsageTableHeader {
public:
MockUsageTableHeader() : UsageTableHeader() {}
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
metrics::CryptoMetrics*));
MOCK_METHOD4(InvalidateEntryInternal,
CdmResponseType(uint32_t, bool, DeviceFiles*,
metrics::CryptoMetrics*));
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
const CdmKeySetId&, const std::string&,
const CdmKeyResponse&, uint32_t*));
@@ -1041,25 +1033,53 @@ TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) {
kEmptyString /* license */, &usage_entry_number));
}
// Initial Test state:
// 1. Table has a few entries, one of which is unoccupied.
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. A successful call to OEMCrypto to create an entry
// at the end of the current table
// b. Moving the new entry to the unoccupied entry index
// c. Shrink table to remove the now empty entry slot created in (a)
// d. Storing the new updated usage table
//
// Storage type Usage entries
// at start at end
// ============= ======== ======
// Offline License 1 0 0
// Secure Stop 1 1 1
// Storage Type Unknown 2 Replaced
// Offline License 2 DNE 2
//
// DNE = Does Not Exist
//
// # of usage entries 3 3
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector;
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
expect_usage_entry_info_vector[expect_usage_entry_number] =
expect_usage_entry_info_vector[final_usage_entry_number] =
kUsageEntryInfoOfflineLicense2;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(
*crypto_session_,
ShrinkUsageTableHeader(kLevelDefault,
expect_usage_entry_info_vector.size(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
StoreUsageTableInfo(
kAnotherUsageTableHeader,
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
expect_usage_entry_info_vector))
.WillOnce(Return(true));
uint32_t usage_entry_number = 0;
@@ -1070,29 +1090,182 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
kUsageEntryInfoOfflineLicense2.key_set_id,
kUsageEntryInfoOfflineLicense2.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
}
// Initial Test state:
// 1. Table has a few entries, one of which is unoccupied.
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. A successful call to OEMCrypto to create an entry
// at the end of the current table
// b. Moving the new entry to the unoccupied entry index
// c. Shrink table to remove the now empty entry slot created in (a)
// d. Storing the new updated usage table
//
// Storage type Usage entries
// at start at end
// ============= ======== ======
// Offline License 1 0 0
// Secure Stop 1 1 1
// Storage Type Unknown 2 Replaced
// Secure Stop 2 DNE 2
//
// DNE = Does Not Exist
//
// # of usage entries 3 3
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector;
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
expect_usage_entry_info_vector[expect_usage_entry_number] =
expect_usage_entry_info_vector[final_usage_entry_number] =
kUsageEntryInfoSecureStop2;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(
*crypto_session_,
ShrinkUsageTableHeader(kLevelDefault,
expect_usage_entry_info_vector.size(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
expect_usage_entry_info_vector))
.WillOnce(Return(true));
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR,
usage_table_header_->AddEntry(
crypto_session_,
kUsageEntryInfoSecureStop2.storage_type == kStorageLicense,
kUsageEntryInfoSecureStop2.key_set_id,
kUsageEntryInfoSecureStop2.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
}
// Initial Test state:
// 1. Table has a few entries, one of which is unoccupied.
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. An odd but successful call to OEMCrypto to create an entry
// beyond the end of the current table
// b. Empty entries will fill the gap between the original table
// and the new entry
// c. Move the new entry to the *lowest* unoccupied entry index
// d. Shrink table to remove the now empty entry slot created in (a)
// and the filler gap entries created in (b)
// e. Storing the new updated usage table
//
// Storage type Usage entries
// at start at end
// ============= ======== ======
// Offline License 1 0 0
// Secure Stop 1 1 1
// Storage Type Unknown 2 Replaced
// Storage Type Unknown DNE Removed (never stored)
// Storage Type Unknown DNE Removed (never stored)
// Storage Type Unknown DNE Removed (never stored)
// Secure Stop 2 DNE 2
//
// DNE = Does Not Exist
//
// # of usage entries 3 3
TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
const size_t skip_usage_entries = 3;
const uint32_t initial_usage_entry_number =
next_usage_entry_number + skip_usage_entries;
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector;
expect_usage_entry_info_vector[final_usage_entry_number] =
kUsageEntryInfoSecureStop2;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(
*crypto_session_,
ShrinkUsageTableHeader(kLevelDefault,
expect_usage_entry_info_vector.size(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
expect_usage_entry_info_vector))
.WillOnce(Return(true));
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR,
usage_table_header_->AddEntry(
crypto_session_,
kUsageEntryInfoSecureStop2.storage_type == kStorageLicense,
kUsageEntryInfoSecureStop2.key_set_id,
kUsageEntryInfoSecureStop2.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
}
// Initial Test state:
// 1. Table has a few entries, one of which is unoccupied.
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. A successful call to OEMCrypto to create an entry
// at the end of the current table
// b. Cannot move the new entry to the unoccupied entry index
// due to entry being in use (according to OEMCrypto)
// c. Storing the new updated usage table
//
// Storage type Usage entries
// at start at end
// ============= ======== ======
// Offline License 1 0 0
// Secure Stop 1 1 1
// Storage Type Unknown 2 2
// Secure Stop 2 DNE 3
//
// DNE = Does Not Exist
//
// # of usage entries 3 4
TEST_F(UsageTableHeaderTest, AddEntry_CannotMoveNewEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t attempted_usage_entry_number =
kUsageEntryInfoVector.size() - 1;
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector;
expect_usage_entry_info_vector.push_back(kUsageEntryInfoSecureStop2);
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(final_usage_entry_number), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, MoveUsageEntry(attempted_usage_entry_number))
.WillOnce(Return(MOVE_USAGE_ENTRY_DESTINATION_IN_USE));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
StoreUsageTableInfo(
kAnotherUsageTableHeader,
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
StoreUsageTableInfo(kAnotherUsageTableHeader,
expect_usage_entry_info_vector))
.WillOnce(Return(true));
uint32_t usage_entry_number = 0;
@@ -1103,32 +1276,58 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
kUsageEntryInfoSecureStop2.key_set_id,
kUsageEntryInfoSecureStop2.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
}
TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
// Initial Test state:
// 1. Table has a few entries, one of which is unoccupied.
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. A successful call to OEMCrypto to create an entry
// at the end of the current table
// b. Moving the new entry to the unoccupied entry index
// c. Fail to shrink table due to occupied entry
// d. Storing the new updated usage table
//
// Storage type Usage entries
// at start at end
// ============= ======== ======
// Offline License 1 0 0
// Secure Stop 1 1 1
// Storage Type Unknown 2 Replaced
// Secure Stop 2 DNE 2
// Storage Type Unknown DNE 3 (created when new entry moved)
//
// DNE = Does Not Exist
//
// # of usage entries 3 4
TEST_F(UsageTableHeaderTest, AddEntry_CannotShinkAfterMove) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
size_t skip_usage_entries = 3;
uint32_t expect_usage_entry_number =
next_usage_entry_number + skip_usage_entries;
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector;
expect_usage_entry_info_vector[final_usage_entry_number] =
kUsageEntryInfoSecureStop2;
expect_usage_entry_info_vector.push_back(kUsageEntryInfoStorageTypeUnknown);
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(
*crypto_session_,
ShrinkUsageTableHeader(
kLevelDefault, expect_usage_entry_info_vector.size() - 1, NotNull()))
.WillOnce(Return(SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(
*device_files_,
StoreUsageTableInfo(
kAnotherUsageTableHeader,
UnorderedElementsAre(
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
kUsageEntryInfoStorageTypeUnknown,
kUsageEntryInfoStorageTypeUnknown,
kUsageEntryInfoStorageTypeUnknown,
kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2)))
EXPECT_CALL(*device_files_,
StoreUsageTableInfo(kAnotherUsageTableHeader,
expect_usage_entry_info_vector))
.WillOnce(Return(true));
uint32_t usage_entry_number = 0;
@@ -1139,9 +1338,21 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
kUsageEntryInfoSecureStop2.key_set_id,
kUsageEntryInfoSecureStop2.usage_info_file_name,
kEmptyString /* license */, &usage_entry_number));
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
}
// Initial Test state:
// 1. Table is full with entries
// 2. An entry-less session requires a new entry.
//
// Attempting to add an entry will result in:
// a. First call to OEMCrypto to create an entry fails due to
// table being full
// b. One of the existing entries will be removed, shrinking table
// by one
// c. Table will be stored
// d. Second call to OEMCrypto to create an entry will succeed
// e. Storing the new updated usage table
TEST_F(UsageTableHeaderTest,
AddEntry_CreateUsageEntryFailsOnce_SucceedsSecondTime) {
// Initialize and setup
@@ -1150,32 +1361,33 @@ TEST_F(UsageTableHeaderTest,
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
k10UsageEntryInfoVector;
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
std::vector<CdmUsageEntryInfo> final_usage_entries;
uint32_t invalidated_entry = 0; // Randomly chosen by UsageTableHeader
const uint32_t expected_usage_entry_number =
k10UsageEntryInfoVector.size() - 1;
// Setup expectations
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
// First call fails
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES))
// Second call succeeds
.WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number),
Return(NO_ERROR)));
// Covers all other expectations.
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
InvalidateEntryInternal(_, true, device_files_, NotNull()))
.WillOnce(DoAll(SaveArg<0>(&invalidated_entry),
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES))
.WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number),
Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
std::vector<CdmUsageEntryInfo> final_usage_entries;
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _))
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
// Now invoke the method under test
uint32_t usage_entry_number;
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR,
mock_usage_table_header->SuperAddEntry(
crypto_session_,
@@ -1187,17 +1399,15 @@ TEST_F(UsageTableHeaderTest,
// 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, invalidated_entry);
EXPECT_LE(invalidated_entry, k10UsageEntryInfoVector.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.push_back(kUsageEntryInfoOfflineLicense6);
k10UsageEntryInfoVector;
expected_usage_entries[invalidated_entry] = expected_usage_entries.back();
expected_usage_entries.pop_back();
expected_usage_entries.push_back(kUsageEntryInfoOfflineLicense6);
EXPECT_EQ(expected_usage_entries, final_usage_entries);
}
@@ -1210,7 +1420,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
// Setup expectations
EXPECT_CALL(*mock_usage_table_header,
InvalidateEntry(_, true, device_files_, NotNull()))
InvalidateEntryInternal(_, true, device_files_, NotNull()))
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
Return(NO_ERROR)));