Chromium Code Reviews| Index: base/memory/memory_pressure_monitor_mac_unittest.cc |
| diff --git a/base/memory/memory_pressure_monitor_mac_unittest.cc b/base/memory/memory_pressure_monitor_mac_unittest.cc |
| index b7c29cd13cb59025ae230c7007a937f81badc456..9d251f011e9959d1ea5f52482546e382155e993e 100644 |
| --- a/base/memory/memory_pressure_monitor_mac_unittest.cc |
| +++ b/base/memory/memory_pressure_monitor_mac_unittest.cc |
| @@ -4,7 +4,11 @@ |
| #include "base/memory/memory_pressure_monitor_mac.h" |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/mac/scoped_cftyperef.h" |
| #include "base/macros.h" |
| +#include "base/test/histogram_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| namespace base { |
| @@ -12,43 +16,87 @@ namespace mac { |
| class TestMemoryPressureMonitor : public MemoryPressureMonitor { |
| public: |
| - using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure; |
| + using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel; |
| + |
| + // A HistogramTester for verifying correct UMA stat generation. |
| + base::HistogramTester tester; |
| TestMemoryPressureMonitor() { } |
| + // Clears the next run loop update time so that the next pass of the run |
| + // loop checks the memory pressure level immediately. Normally there's a |
| + // 5 second delay between pressure readings. |
| + void ResetRunLoopUpdateTime() { next_run_loop_update_time_ = 0; } |
| + |
| + // Access to the last-recorded memory pressure level. |
| + MemoryPressureListener::MemoryPressureLevel LastPressureLevel() { |
| + return last_pressure_level_; |
| + } |
| + |
| + // Sets the last UMA stat report time. Time spent in memory pressure is |
| + // recorded in 5-second "ticks" from the last time statistics were recorded. |
| + void SetLastStatisticReportTime(CFTimeInterval time) { |
| + last_statistic_report_time_ = time; |
| + } |
| + |
| + // Sets the raw macOS memory pressure level read by the memory pressure |
| + // monitor. |
| + int macos_pressure_level_for_testing_; |
| + |
| + // Exposes the UpdatePressureLevel() method for testing. |
| + void UpdatePressureLevel() { MemoryPressureMonitor::UpdatePressureLevel(); } |
| + |
| + // Returns the number of seconds left over from the last UMA tick |
| + // calculation. |
| + int SubTickSeconds() { return subtick_seconds_; } |
| + |
| + // Returns the number of seconds per UMA tick. |
| + static int GetSecondsPerUMATick() { |
| + return MemoryPressureMonitor::GetSecondsPerUMATick(); |
| + } |
| + |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); |
| + |
| + int GetMacMemoryPressureLevel() override { |
| + return macos_pressure_level_for_testing_; |
| + } |
| }; |
| TEST(MacMemoryPressureMonitorTest, MemoryPressureFromMacMemoryPressure) { |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure( |
| - DISPATCH_MEMORYPRESSURE_NORMAL)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure( |
| - DISPATCH_MEMORYPRESSURE_WARN)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure( |
| - DISPATCH_MEMORYPRESSURE_CRITICAL)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure(0)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure(3)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure(5)); |
| - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| - TestMemoryPressureMonitor:: |
| - MemoryPressureLevelForMacMemoryPressure(-1)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + DISPATCH_MEMORYPRESSURE_NORMAL)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + DISPATCH_MEMORYPRESSURE_WARN)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + DISPATCH_MEMORYPRESSURE_CRITICAL)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + 0)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + 3)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + 5)); |
| + EXPECT_EQ( |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, |
| + TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| + -1)); |
| } |
| TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { |
| TestMemoryPressureMonitor monitor; |
| + |
| MemoryPressureListener::MemoryPressureLevel memory_pressure = |
| monitor.GetCurrentPressureLevel(); |
| EXPECT_TRUE(memory_pressure == |
| @@ -59,5 +107,124 @@ TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { |
| MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| } |
| +TEST(MacMemoryPressureMonitorTest, MemoryPressureConversion) { |
| + TestMemoryPressureMonitor monitor; |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; |
| + MemoryPressureListener::MemoryPressureLevel memory_pressure = |
| + monitor.GetCurrentPressureLevel(); |
| + EXPECT_EQ(memory_pressure, |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; |
| + memory_pressure = monitor.GetCurrentPressureLevel(); |
| + EXPECT_EQ(memory_pressure, |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; |
| + memory_pressure = monitor.GetCurrentPressureLevel(); |
| + EXPECT_EQ(memory_pressure, |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| +} |
| + |
| +TEST(MacMemoryPressureMonitorTest, MemoryPressureRunLoopChecking) { |
| + TestMemoryPressureMonitor monitor; |
| + |
| + // To test grabbing the memory presure at the end of the run loop, we have to |
| + // run the run loop, but to do that the run loop needs a run loop source. Add |
| + // a timer as the source. We know that the exit observer is attached to |
| + // the kMessageLoopExclusiveRunLoopMode mode, so use that mode. |
| + ScopedCFTypeRef<CFRunLoopTimerRef> timer_ref(CFRunLoopTimerCreate( |
| + NULL, CFAbsoluteTimeGetCurrent() + 10, 0, 0, 0, nullptr, nullptr)); |
| + CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer_ref, |
| + kMessageLoopExclusiveRunLoopMode); |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; |
| + monitor.ResetRunLoopUpdateTime(); |
| + CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); |
| + EXPECT_EQ(monitor.LastPressureLevel(), |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; |
| + monitor.ResetRunLoopUpdateTime(); |
| + CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); |
| + EXPECT_EQ(monitor.LastPressureLevel(), |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| + |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; |
| + monitor.ResetRunLoopUpdateTime(); |
| + CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); |
| + EXPECT_EQ(monitor.LastPressureLevel(), |
| + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); |
| + |
| + CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer_ref, |
| + kMessageLoopExclusiveRunLoopMode); |
| +} |
| + |
| +TEST(MacMemoryPressureMonitorTest, RecordMemoryPressureStats) { |
| + TestMemoryPressureMonitor monitor; |
| + const char* kHistogram = "Memory.PressureLevel"; |
| + CFTimeInterval now = CFAbsoluteTimeGetCurrent(); |
| + const int seconds_per_tick = |
| + TestMemoryPressureMonitor::GetSecondsPerUMATick(); |
| + |
| + // Set the initial pressure level. |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; |
| + // Incur one UMA tick of time (and include one extra second of elapsed time). |
| + monitor.SetLastStatisticReportTime(now - (seconds_per_tick + 1)); |
| + monitor.UpdatePressureLevel(); |
| + monitor.tester.ExpectTotalCount(kHistogram, 1); |
| + monitor.tester.ExpectBucketCount(kHistogram, 0, 1); |
| + // The report time above included an extra second so there should be 1 |
| + // sub-tick second left over. |
| + EXPECT_EQ(1, monitor.SubTickSeconds()); |
| + |
| + // Simulate sitting in normal pressure for 1 second less than 6 UMA tick |
| + // seconds and then elevating to warning. With the left over sub-tick second |
| + // from above, the total elapsed ticks should be an even 6 UMA ticks. |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; |
| + monitor.SetLastStatisticReportTime(now - (seconds_per_tick * 6 - 1)); |
| + monitor.UpdatePressureLevel(); |
| + monitor.tester.ExpectTotalCount(kHistogram, 7); |
| + monitor.tester.ExpectBucketCount(kHistogram, 0, 7); |
| + monitor.tester.ExpectBucketCount(kHistogram, 1, 0); |
| + EXPECT_EQ(0, monitor.SubTickSeconds()); |
| + |
| + // Simulate sitting in warning pressure for 20 UMA ticks and 2 seconds, and |
| + // then elevating to critical. |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; |
| + monitor.SetLastStatisticReportTime(now - (20 * seconds_per_tick + 2)); |
| + monitor.UpdatePressureLevel(); |
| + monitor.tester.ExpectTotalCount(kHistogram, 27); |
| + monitor.tester.ExpectBucketCount(kHistogram, 0, 7); |
| + monitor.tester.ExpectBucketCount(kHistogram, 1, 20); |
| + monitor.tester.ExpectBucketCount(kHistogram, 2, 0); |
| + EXPECT_EQ(2, monitor.SubTickSeconds()); |
|
lgrey
2017/02/22 21:33:40
I wonder if we should always be rounding up for co
shrike
2017/02/22 23:56:25
I think if we do, and if we get a lot of subtick_s
lgrey
2017/02/23 15:49:29
Nah, no problem. I guess the inaccuracy is baked i
|
| + |
| + // A quick update while critical - the stats should not budge because less |
| + // than 1 tick of time has elapsed. |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; |
| + monitor.SetLastStatisticReportTime(now - 1); |
| + monitor.UpdatePressureLevel(); |
| + monitor.tester.ExpectTotalCount(kHistogram, 27); |
| + monitor.tester.ExpectBucketCount(kHistogram, 0, 7); |
| + monitor.tester.ExpectBucketCount(kHistogram, 1, 20); |
| + monitor.tester.ExpectBucketCount(kHistogram, 2, 0); |
| + EXPECT_EQ(3, monitor.SubTickSeconds()); |
| + |
| + // A quick change back to normal. Less than 1 tick of time has elapsed, but |
| + // in this case the pressure level changed, so the critical bucket should |
| + // get another sample (otherwise we could miss quick level changes). |
| + monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; |
| + monitor.SetLastStatisticReportTime(now - 1); |
| + monitor.UpdatePressureLevel(); |
| + monitor.tester.ExpectTotalCount(kHistogram, 28); |
| + monitor.tester.ExpectBucketCount(kHistogram, 0, 7); |
| + monitor.tester.ExpectBucketCount(kHistogram, 1, 20); |
| + monitor.tester.ExpectBucketCount(kHistogram, 2, 1); |
| + // When less than 1 tick of time has elapsed but the pressure level changed, |
| + // the subtick remainder gets zeroed out. |
| + EXPECT_EQ(0, monitor.SubTickSeconds()); |
| +} |
| } // namespace mac |
| } // namespace base |