Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2040)

Unified Diff: base/win/memory_pressure_monitor_win_unittest.cc

Issue 1122863005: Create base::win::MemoryPressureMonitor class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/win/memory_pressure_monitor_win_unittest.cc
diff --git a/base/win/memory_pressure_monitor_win_unittest.cc b/base/win/memory_pressure_monitor_win_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..48fb80af45a8db94dd82df9efe216ff53079d2f0
--- /dev/null
+++ b/base/win/memory_pressure_monitor_win_unittest.cc
@@ -0,0 +1,261 @@
+// Copyright 2015 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 "base/basictypes.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/message_loop/message_loop.h"
+#include "base/win/memory_pressure_monitor_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// True if the memory notifier got called.
grt (UTC plus 2) 2015/05/05 15:34:00 you could use Google Mock here rather than rolling
chrisha 2015/05/05 19:46:13 Yeah, I use gmock all over the place in Syzygy lan
grt (UTC plus 2) 2015/05/06 13:26:36 My personal belief is that gmock is okay to use wh
+// Do not read/modify value directly.
+bool on_memory_pressure_called = false;
+
+// If the memory notifier got called, this is the memory pressure reported.
+MemoryPressureListener::MemoryPressureLevel on_memory_pressure_level =
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+
+// Processes OnMemoryPressure calls.
+void OnMemoryPressure(MemoryPressureListener::MemoryPressureLevel level) {
+ on_memory_pressure_called = true;
+ on_memory_pressure_level = level;
+}
+
+// Resets the indicator for memory pressure.
+void ResetOnMemoryPressureCalled() {
+ on_memory_pressure_called = false;
+}
+
+// Returns true when OnMemoryPressure was called (and resets it).
+bool WasOnMemoryPressureCalled() {
+ bool b = on_memory_pressure_called;
+ ResetOnMemoryPressureCalled();
+ return b;
+}
+
+struct PressureSettings {
+ int phys;
+ int virt;
+ MemoryPressureListener::MemoryPressureLevel level;
+};
+
+} // namespace
+
+// This is outside of the anonymous namespace so that it can be seen as a friend
+// to the monitor class.
+class TestMemoryPressureMonitor : public MemoryPressureMonitorWin {
+ public:
+ using MemoryPressureMonitorWin::CalculateCurrentPressureLevel;
+ using MemoryPressureMonitorWin::CheckMemoryPressure;
+
+ TestMemoryPressureMonitor() {
+ // Disable any timers which are going on and set a special memory reporting
+ // function.
+ StopObserving();
+ }
+
+ virtual ~TestMemoryPressureMonitor() {}
+
+ // Sets up the memory status to reflect the provided loads.
+ void SetMemoryLoad(int phys_load_pct, int virt_load_pct) {
+ static const DWORDLONG k4GB = 4ull * 1024 * 1024 * 1024;
+
+ mem_status_.dwMemoryLoad = static_cast<DWORD>(phys_load_pct);
+ mem_status_.ullTotalPhys = k4GB;
+ mem_status_.ullAvailPhys = (k4GB * (100 - phys_load_pct)) / 100;
+ mem_status_.ullTotalPageFile = k4GB;
+ mem_status_.ullAvailPageFile = k4GB;
+ mem_status_.ullTotalVirtual = k4GB;
+ mem_status_.ullAvailVirtual = (k4GB * (100 - virt_load_pct)) / 100;
+ }
+
+ // Sets up the memory status to reflect the provided absolute memory left.
+ // This uses quite small total memory quantities so that the absolute memory
+ // limits have a chance to kick in.
+ void SetMemoryFree(int phys_left_mb, int virt_left_mb) {
+ // Figure out an amount of memory that makes sense for the desired
+ // quantities of memory to be left.
+ DWORDLONG total = 64; // Minimum of 64MB.
+ while (total < phys_left_mb || total < virt_left_mb)
+ total *= 2;
+ total *= 1024 * 1024;
+
+ mem_status_.ullTotalPhys = total;
+ mem_status_.ullAvailPhys =
+ static_cast<DWORDLONG>(phys_left_mb) * 1024 * 1024;
+ mem_status_.ullTotalPageFile = total;
+ mem_status_.ullAvailPageFile = total;
+ mem_status_.ullTotalVirtual = total;
+ mem_status_.ullAvailVirtual =
+ static_cast<DWORDLONG>(virt_left_mb) * 1024 * 1024;
+ mem_status_.dwMemoryLoad = static_cast<DWORD>(
+ 100 * (mem_status_.ullTotalPhys - mem_status_.ullAvailPhys) /
+ mem_status_.ullTotalPhys);
+ }
+
+ private:
+ virtual bool GetSystemMemoryStatus(_MEMORYSTATUSEX* mem_status) override {
+ // Simply copy the memory status set by the test fixture.
+ *mem_status = mem_status_;
+ return true;
+ }
+
+ _MEMORYSTATUSEX mem_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor);
+};
+
+// Tests the fundamental direct calculation of memory pressure.
+TEST(MemoryPressureMonitorWinTest, CalculateCurrentMemoryPressureLevel) {
+ base::MessageLoopForUI message_loop;
+ scoped_ptr<TestMemoryPressureMonitor> monitor(
grt (UTC plus 2) 2015/05/05 15:34:00 why not put the instance on the stack?
chrisha 2015/05/05 19:46:13 No real reason, other than cut and paste from Memo
+ new TestMemoryPressureMonitor);
+
+ // A bunch of memory load settings and the expected memory pressure.
+ const PressureSettings kLoadSettings[] = {
+ { 10, 10, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE },
+ { 40, 40, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE },
+ { 65, 10, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 10, 65, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 65, 65, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 98, 10, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 98, 65, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 10, 98, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 65, 98, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 98, 98, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ };
+
+ for (size_t i = 0; i < arraysize(kLoadSettings); ++i) {
grt (UTC plus 2) 2015/05/05 15:34:00 for (const PressureSettings& setting : kLoadSettin
chrisha 2015/05/05 19:46:13 Done.
+ monitor->SetMemoryLoad(kLoadSettings[i].phys, kLoadSettings[i].virt);
+ EXPECT_EQ(kLoadSettings[i].level,
+ monitor->CalculateCurrentPressureLevel());
+ }
+
+ // A bunch of free memory settings and the expected memory pressure.
+ const PressureSettings kFreeSettings[] = {
+ { 500, 500, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE },
+ { 250, 500, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 500, 250, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 250, 250, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE },
+ { 50, 500, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 50, 250, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 500, 50, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 250, 50, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ { 50, 50, MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL },
+ };
+
+ for (size_t i = 0; i < arraysize(kFreeSettings); ++i) {
+ monitor->SetMemoryFree(kFreeSettings[i].phys, kFreeSettings[i].virt);
+ EXPECT_EQ(kFreeSettings[i].level,
+ monitor->CalculateCurrentPressureLevel());
+ }
+}
+
+// This test tests the various transition states from memory pressure, looking
+// for the correct behavior on event reposting as well as state updates.
+TEST(MemoryPressureMonitorWinTest, CheckMemoryPressure) {
+ base::MessageLoopForUI message_loop;
grt (UTC plus 2) 2015/05/05 15:34:00 does this test use the timer? if so, it'll be a sl
chrisha 2015/05/05 19:46:13 No, the test bypasses the timer entirely by direct
+ scoped_ptr<TestMemoryPressureMonitor> monitor(
+ new TestMemoryPressureMonitor);
+ scoped_ptr<MemoryPressureListener> listener(
+ new MemoryPressureListener(base::Bind(&OnMemoryPressure)));
+
+ // Checking the memory pressure while 0% are used should not produce any
+ // events.
+ ResetOnMemoryPressureCalled();
+ monitor->SetMemoryLoad(0, 0);
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_FALSE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ monitor->GetCurrentPressureLevel());
+
+ // Setting the memory level to 80% should produce a moderate pressure level.
+ monitor->SetMemoryLoad(80, 80);
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_TRUE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ monitor->GetCurrentPressureLevel());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ on_memory_pressure_level);
+
+ // We need to check that the event gets reposted after a while.
+ int i = 0;
+ for (; i < 100; i++) {
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ monitor->GetCurrentPressureLevel());
+ if (WasOnMemoryPressureCalled()) {
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ on_memory_pressure_level);
+ break;
+ }
+ }
+ // Should be more then 5 and less then 100.
+ EXPECT_LE(5, i);
+ EXPECT_GE(99, i);
+
+ // Setting the memory usage to 99% should produce critical levels.
+ monitor->SetMemoryLoad(99, 99);
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_TRUE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ on_memory_pressure_level);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ monitor->GetCurrentPressureLevel());
+
+ // Calling it again should immediately produce a second call.
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_TRUE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ on_memory_pressure_level);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ monitor->GetCurrentPressureLevel());
+
+ // When lowering the pressure again we should get an event and the
+ // pressure should go back to moderate.
+ monitor->SetMemoryLoad(80, 80);
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_TRUE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ on_memory_pressure_level);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ monitor->GetCurrentPressureLevel());
+
+ // We should need exactly the same amount of calls as before, before the next
+ // call comes in.
+ int j = 0;
+ for (; j < 100; j++) {
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ monitor->GetCurrentPressureLevel());
+ if (WasOnMemoryPressureCalled()) {
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ on_memory_pressure_level);
+ break;
+ }
+ }
+ // We should have needed exactly the same amount of checks as before.
+ EXPECT_EQ(j, i);
+
+ // Going down to no pressure should not produce an event.
+ monitor->SetMemoryLoad(0, 0);
+ monitor->CheckMemoryPressure();
+ message_loop.RunUntilIdle();
+ EXPECT_FALSE(WasOnMemoryPressureCalled());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ monitor->GetCurrentPressureLevel());
+}
+
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698