blob: 970029b1a6e803c0e26d6d4b6ded909aed7cedf2 [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/memory/memory_pressure_monitor_utils.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory {
namespace {
constexpr base::TimeDelta kDefaultWindowLength =
base::TimeDelta::FromMinutes(1);
} // namespace
// Test class used to expose some protected members from
// internal::ObservationWindow and to simplify the tests by removing the
// template argument.
class TestObservationWindow : public internal::ObservationWindow<int> {
public:
TestObservationWindow()
: internal::ObservationWindow<int>(kDefaultWindowLength) {}
~TestObservationWindow() override = default;
using internal::ObservationWindow<int>::observations_for_testing;
using internal::ObservationWindow<int>::set_clock_for_testing;
MOCK_METHOD1(OnSampleAdded, void(const int& sample));
MOCK_METHOD1(OnSampleRemoved, void(const int& sample));
private:
DISALLOW_COPY_AND_ASSIGN(TestObservationWindow);
};
class ObservationWindowTest : public testing::Test {
public:
ObservationWindowTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
tick_clock_(scoped_task_environment_.GetMockTickClock()) {}
~ObservationWindowTest() override = default;
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
const base::TickClock* tick_clock_;
private:
DISALLOW_COPY_AND_ASSIGN(ObservationWindowTest);
};
TEST_F(ObservationWindowTest, OnSample) {
::testing::StrictMock<TestObservationWindow> window;
typedef decltype(window.observations_for_testing()) ObservationContainerType;
window.set_clock_for_testing(tick_clock_);
// The window is expected to be empty by default.
EXPECT_TRUE(window.observations_for_testing().empty());
// Add a first sample.
int t0_sample = 1;
base::TimeTicks t0_timestamp = tick_clock_->NowTicks();
EXPECT_CALL(window, OnSampleAdded(t0_sample)).Times(1);
window.OnSample(t0_sample);
::testing::Mock::VerifyAndClear(&window);
// decltype is used here to avoid having to expose the internal datatype used
// to represent this window.
EXPECT_THAT(window.observations_for_testing(),
::testing::ContainerEq(ObservationContainerType{
{t0_timestamp, t0_sample},
}));
// Fast forward by the length of the observation window, no sample should be
// removed as all samples have an age that doesn't exceed the window length.
scoped_task_environment_.FastForwardBy(kDefaultWindowLength);
int t1_sample = 2;
base::TimeTicks t1_timestamp = tick_clock_->NowTicks();
EXPECT_CALL(window, OnSampleAdded(t1_sample)).Times(1);
window.OnSample(t1_sample);
::testing::Mock::VerifyAndClear(&window);
EXPECT_THAT(window.observations_for_testing(),
::testing::ContainerEq(ObservationContainerType{
{t0_timestamp, t0_sample},
{t1_timestamp, t1_sample},
}));
// Fast forward by one second, the first sample should be removed the next
// time a sample gets added as its age exceed the length of the observation
// window.
scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
int t2_sample = 3;
base::TimeTicks t2_timestamp = tick_clock_->NowTicks();
EXPECT_CALL(window, OnSampleAdded(t2_sample)).Times(1);
EXPECT_CALL(window, OnSampleRemoved(t0_sample)).Times(1);
window.OnSample(t2_sample);
::testing::Mock::VerifyAndClear(&window);
EXPECT_THAT(window.observations_for_testing(),
::testing::ContainerEq(ObservationContainerType{
{t1_timestamp, t1_sample},
{t2_timestamp, t2_sample},
}));
}
TEST_F(ObservationWindowTest, FreeMemoryObservationWindow) {
FreeMemoryObservationWindow::Config window_config = {};
FreeMemoryObservationWindow window(kDefaultWindowLength, window_config);
window.set_clock_for_testing(tick_clock_);
DCHECK_GT(window_config.low_memory_early_limit_mb,
window_config.low_memory_critical_limit_mb);
// Fill the window with samples with a value higher than the higher limit.
for (size_t i = 0; i < window_config.min_sample_count; ++i) {
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
window.OnSample(window_config.low_memory_early_limit_mb + 1);
}
// The window has enough samples to be evaluated but they're all above the
// thresholds.
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
// Test the detection that the system has reached the early limit.
// Remove all the observations from the window.
scoped_task_environment_.FastForwardBy(kDefaultWindowLength +
base::TimeDelta::FromSeconds(1));
const size_t min_sample_count_to_be_positive =
static_cast<size_t>(window_config.sample_ratio_to_be_positive *
window_config.min_sample_count);
DCHECK_GE(window_config.min_sample_count, min_sample_count_to_be_positive);
for (size_t i = 0;
i < window_config.min_sample_count - min_sample_count_to_be_positive;
++i) {
window.OnSample(window_config.low_memory_early_limit_mb + 1);
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
}
for (size_t i = 0; i < min_sample_count_to_be_positive; ++i) {
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
window.OnSample(window_config.low_memory_early_limit_mb - 1);
}
EXPECT_TRUE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
// Test the detection that the system has reached the critical limit.
// Remove all the observations from the window.
scoped_task_environment_.FastForwardBy(kDefaultWindowLength +
base::TimeDelta::FromSeconds(1));
for (size_t i = 0;
i < window_config.min_sample_count - min_sample_count_to_be_positive;
++i) {
window.OnSample(window_config.low_memory_critical_limit_mb + 1);
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
}
for (size_t i = 0; i < min_sample_count_to_be_positive; ++i) {
EXPECT_FALSE(window.MemoryIsUnderEarlyLimit());
EXPECT_FALSE(window.MemoryIsUnderCriticalLimit());
window.OnSample(window_config.low_memory_critical_limit_mb - 1);
}
EXPECT_TRUE(window.MemoryIsUnderEarlyLimit());
EXPECT_TRUE(window.MemoryIsUnderCriticalLimit());
}
TEST_F(ObservationWindowTest, DiskIdleTimeObservationWindow) {
const float kDiskIdleTimeThreshold = 0.5;
DiskIdleTimeObservationWindow window(kDefaultWindowLength,
kDiskIdleTimeThreshold);
window.set_clock_for_testing(tick_clock_);
window.OnSample(1.0);
// The average disk idle time is equal to 1, it's not under the threshold.
EXPECT_FALSE(window.DiskIdleTimeIsLow());
window.OnSample(0.1);
// The average disk idle time is equal to (1 + 0.1) / 2 = 0.55, it's still not
// under the threshold.
EXPECT_FALSE(window.DiskIdleTimeIsLow());
window.OnSample(0.1);
// The average disk idle time is now equal to (1 + 0.1 + 0.1) / 3 = 0.4, it's
// now under the threshold.
EXPECT_TRUE(window.DiskIdleTimeIsLow());
// Remove all the observations from the window.
scoped_task_environment_.FastForwardBy(kDefaultWindowLength +
base::TimeDelta::FromSeconds(1));
// Add a sample under the threshold, the disk idle should be considered as low
// as it's under the threshold.
window.OnSample(0.1);
EXPECT_TRUE(window.DiskIdleTimeIsLow());
window.OnSample(1.0);
// The average disk idle time is equal to (1 + 0.1) / 2 = 0.55, it's not under
// the threshold anymore.
EXPECT_FALSE(window.DiskIdleTimeIsLow());
}
} // namespace memory