blob: b8f5025349972e3034f69ec48cc848fdc7795605 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <string>
#include "components/web_cache/browser/web_cache_manager.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Time;
using base::TimeDelta;
namespace web_cache {
class WebCacheManagerTest : public testing::Test {
protected:
typedef WebCacheManager::StatsMap StatsMap;
typedef WebCacheManager::Allocation Allocation;
typedef WebCacheManager::AllocationStrategy AllocationStrategy;
static const int kRendererID;
static const int kRendererID2;
static const WebCacheManager::RendererInfo kStats;
static const WebCacheManager::RendererInfo kStats2;
WebCacheManagerTest() = default;
// Thunks to access protected members of WebCacheManager
static std::map<int, WebCacheManager::RendererInfo>& stats(
WebCacheManager* h) {
return h->stats_;
}
static void SimulateInactivity(WebCacheManager* h, int renderer_id) {
stats(h)[renderer_id].access = Time::Now() - TimeDelta::FromMinutes(
WebCacheManager::kRendererInactiveThresholdMinutes);
h->FindInactiveRenderers();
}
static std::set<int>& active_renderers(WebCacheManager* h) {
return h->active_renderers_;
}
static std::set<int>& inactive_renderers(WebCacheManager* h) {
return h->inactive_renderers_;
}
static void GatherStats(WebCacheManager* h,
std::set<int> renderers,
WebCacheManager::RendererInfo* stats) {
memset(stats, 0, sizeof(WebCacheManager::RendererInfo));
h->GatherStats(renderers, &stats->capacity, &stats->size);
}
static uint64_t GetSize(int tactic,
const WebCacheManager::RendererInfo& stats) {
return WebCacheManager::GetSize(
static_cast<WebCacheManager::AllocationTactic>(tactic), stats.size);
}
static bool AttemptTactic(WebCacheManager* h,
int active_tactic,
const WebCacheManager::RendererInfo& active_stats,
int inactive_tactic,
const WebCacheManager::RendererInfo& inactive_stats,
std::list<std::pair<int, uint64_t>>* strategy) {
return h->AttemptTactic(
static_cast<WebCacheManager::AllocationTactic>(active_tactic),
active_stats.size,
static_cast<WebCacheManager::AllocationTactic>(inactive_tactic),
inactive_stats.size, strategy);
}
static void AddToStrategy(WebCacheManager* h,
std::set<int> renderers,
int tactic,
uint64_t extra_bytes_to_allocate,
std::list<std::pair<int, uint64_t>>* strategy) {
h->AddToStrategy(renderers,
static_cast<WebCacheManager::AllocationTactic>(tactic),
extra_bytes_to_allocate,
strategy);
}
static bool RendererInfoEqual(const WebCacheManager::RendererInfo& lhs,
const WebCacheManager::RendererInfo& rhs) {
return lhs.capacity == rhs.capacity && lhs.size == rhs.size;
}
enum {
DIVIDE_EVENLY = WebCacheManager::DIVIDE_EVENLY,
KEEP_CURRENT_WITH_HEADROOM = WebCacheManager::KEEP_CURRENT_WITH_HEADROOM,
KEEP_CURRENT = WebCacheManager::KEEP_CURRENT,
};
WebCacheManager* manager() { return &manager_; }
private:
WebCacheManager manager_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
};
// static
const int WebCacheManagerTest::kRendererID = 146;
// static
const int WebCacheManagerTest::kRendererID2 = 245;
// static
const WebCacheManager::RendererInfo WebCacheManagerTest::kStats = {
base::Time(), 1024 * 1024, 256 * 1024 + 512,
};
// static
const WebCacheManager::RendererInfo WebCacheManagerTest::kStats2 = {
base::Time(), 2 * 1024 * 1024, 2 * 256 * 1024 + 2 * 512,
};
TEST_F(WebCacheManagerTest, AddRemoveRendererTest) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
manager()->Add(kRendererID);
EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID));
EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID));
manager()->Remove(kRendererID);
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
}
TEST_F(WebCacheManagerTest, ActiveInactiveTest) {
manager()->Add(kRendererID);
manager()->ObserveActivity(kRendererID);
EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID));
EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID));
SimulateInactivity(manager(), kRendererID);
EXPECT_EQ(0U, active_renderers(manager()).count(kRendererID));
EXPECT_EQ(1U, inactive_renderers(manager()).count(kRendererID));
manager()->ObserveActivity(kRendererID);
EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID));
EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID));
manager()->Remove(kRendererID);
}
TEST_F(WebCacheManagerTest, ObserveStatsTest) {
manager()->Add(kRendererID);
EXPECT_EQ(1U, stats(manager()).size());
manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
EXPECT_EQ(1U, stats(manager()).size());
EXPECT_TRUE(RendererInfoEqual(kStats, stats(manager())[kRendererID]));
manager()->Remove(kRendererID);
}
TEST_F(WebCacheManagerTest, SetGlobalSizeLimitTest) {
uint64_t limit = manager()->GetDefaultGlobalSizeLimit();
manager()->SetGlobalSizeLimit(limit);
EXPECT_EQ(limit, manager()->global_size_limit());
manager()->SetGlobalSizeLimit(0);
EXPECT_EQ(0U, manager()->global_size_limit());
}
TEST_F(WebCacheManagerTest, GatherStatsTest) {
manager()->Add(kRendererID);
manager()->Add(kRendererID2);
manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
std::set<int> renderer_set;
renderer_set.insert(kRendererID);
WebCacheManager::RendererInfo stats;
GatherStats(manager(), renderer_set, &stats);
EXPECT_TRUE(RendererInfoEqual(kStats, stats));
renderer_set.insert(kRendererID2);
GatherStats(manager(), renderer_set, &stats);
WebCacheManager::RendererInfo expected_stats = kStats;
expected_stats.capacity += kStats2.capacity;
expected_stats.size += kStats2.size;
EXPECT_TRUE(RendererInfoEqual(expected_stats, stats));
manager()->Remove(kRendererID);
manager()->Remove(kRendererID2);
}
TEST_F(WebCacheManagerTest, GetSizeTest) {
EXPECT_EQ(0U, GetSize(DIVIDE_EVENLY, kStats));
EXPECT_LT(256 * 1024u + 512, GetSize(KEEP_CURRENT_WITH_HEADROOM, kStats));
EXPECT_EQ(256 * 1024u + 512, GetSize(KEEP_CURRENT, kStats));
}
TEST_F(WebCacheManagerTest, AttemptTacticTest) {
manager()->Add(kRendererID);
manager()->Add(kRendererID2);
manager()->ObserveActivity(kRendererID);
SimulateInactivity(manager(), kRendererID2);
manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
AllocationStrategy strategy;
manager()->SetGlobalSizeLimit(kStats.size + kStats2.size - 1);
EXPECT_FALSE(AttemptTactic(manager(),
KEEP_CURRENT,
kStats,
KEEP_CURRENT,
kStats2,
&strategy));
EXPECT_TRUE(strategy.empty());
manager()->SetGlobalSizeLimit(kStats.size + kStats2.size);
EXPECT_TRUE(AttemptTactic(manager(), KEEP_CURRENT, kStats, KEEP_CURRENT,
kStats2, &strategy));
EXPECT_EQ(2U, strategy.size());
AllocationStrategy::iterator iter = strategy.begin();
while (iter != strategy.end()) {
if (iter->first == kRendererID)
EXPECT_LE(kStats.size, iter->second);
else if (iter->first == kRendererID2)
EXPECT_LE(kStats2.size, iter->second);
else
ADD_FAILURE(); // Unexpected entry in strategy.
++iter;
}
manager()->Remove(kRendererID);
manager()->Remove(kRendererID2);
}
TEST_F(WebCacheManagerTest, AddToStrategyTest) {
manager()->Add(kRendererID);
manager()->Add(kRendererID2);
std::set<int> renderer_set;
renderer_set.insert(kRendererID);
renderer_set.insert(kRendererID2);
manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
const uint64_t kExtraBytesToAllocate = 10 * 1024;
AllocationStrategy strategy;
AddToStrategy(manager(),
renderer_set,
KEEP_CURRENT,
kExtraBytesToAllocate,
&strategy);
EXPECT_EQ(2U, strategy.size());
uint64_t total_bytes = 0;
AllocationStrategy::iterator iter = strategy.begin();
while (iter != strategy.end()) {
total_bytes += iter->second;
if (iter->first == kRendererID)
EXPECT_LE(kStats.size, iter->second);
else if (iter->first == kRendererID2)
EXPECT_LE(kStats2.size, iter->second);
else
ADD_FAILURE(); // Unexpected entry in strategy.
++iter;
}
uint64_t expected_total_bytes =
kExtraBytesToAllocate + kStats.size + kStats2.size;
EXPECT_GE(expected_total_bytes, total_bytes);
manager()->Remove(kRendererID);
manager()->Remove(kRendererID2);
}
// Regression test for http://crbug.com/12362.
// There are three operations in the following order will cause the crash:
// Remove(kRendererID) -> ObserveActivity(kRendererID) -> Remove(kRendererID2)
// To prevent similar failures in the future, 6 tests are added in total to
// cover all the possible orderings of these three operations.
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_1) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
// The following order will cause a crash in http://crbug.com/12362.
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
}
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_2) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
manager()->ReviseAllocationStrategy();
}
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_3) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
EXPECT_EQ(0U, inactive_renderers(manager()).size());
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
}
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_4) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
EXPECT_EQ(0U, inactive_renderers(manager()).size());
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
}
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_5) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
}
TEST_F(WebCacheManagerTest,
CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_6) {
EXPECT_EQ(0U, active_renderers(manager()).size());
EXPECT_EQ(0U, inactive_renderers(manager()).size());
// Add, Remove, and ObserveActivity trigger deferred
// calls to ReviseAllocationStrategy and that we call it directly after each
// operation to sidestep the need to wait for an unobservable background
// operation.
manager()->Add(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->Add(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID2);
manager()->ReviseAllocationStrategy();
manager()->Remove(kRendererID);
manager()->ReviseAllocationStrategy();
manager()->ObserveActivity(kRendererID);
manager()->ReviseAllocationStrategy();
}
} // namespace web_cache