blob: 5b08a665373b61ef0b82a7eba23f757f6e18e3e8 [file] [log] [blame]
// 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/bind.h"
#include "base/memory/memory_pressure_listener.h"
#include "content/browser/memory/memory_message_filter.h"
#include "content/browser/memory/memory_pressure_controller.h"
#include "content/common/memory_messages.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "ipc/ipc_message.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace content {
MATCHER_P(IsSetSuppressedMessage, suppressed, "") {
// Ensure that the message is deleted upon return.
scoped_ptr<IPC::Message> message(arg);
if (message == nullptr)
return false;
MemoryMsg_SetPressureNotificationsSuppressed::Param param;
if (!MemoryMsg_SetPressureNotificationsSuppressed::Read(message.get(),
&param))
return false;
return suppressed == base::get<0>(param);
}
MATCHER_P(IsSimulateMessage, level, "") {
// Ensure that the message is deleted upon return.
scoped_ptr<IPC::Message> message(arg);
if (message == nullptr)
return false;
MemoryMsg_SimulatePressureNotification::Param param;
if (!MemoryMsg_SimulatePressureNotification::Read(message.get(), &param))
return false;
return level == base::get<0>(param);
}
MATCHER_P(IsPressureMessage, level, "") {
// Ensure that the message is deleted upon return.
scoped_ptr<IPC::Message> message(arg);
if (message == nullptr)
return false;
MemoryMsg_PressureNotification::Param param;
if (!MemoryMsg_PressureNotification::Read(message.get(), &param))
return false;
return level == base::get<0>(param);
}
class MemoryMessageFilterForTesting : public MemoryMessageFilter {
public:
// Use this object itself as a fake RenderProcessHost pointer. The address is
// only used for looking up the message filter in the controller and is never
// actually dereferenced, so this is safe.
MemoryMessageFilterForTesting()
: MemoryMessageFilter(reinterpret_cast<RenderProcessHost*>(this)) {}
MOCK_METHOD1(Send, bool(IPC::Message* message));
void Add() {
// The filter must be added on the IO thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&MemoryMessageFilterForTesting::Add,
base::Unretained(this)));
RunAllPendingInMessageLoop(BrowserThread::IO);
return;
}
OnChannelConnected(0);
OnFilterAdded(nullptr);
}
void Remove() {
// The filter must be removed on the IO thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&MemoryMessageFilterForTesting::Remove,
base::Unretained(this)));
RunAllPendingInMessageLoop(BrowserThread::IO);
return;
}
OnChannelClosing();
OnFilterRemoved();
}
protected:
~MemoryMessageFilterForTesting() override {}
};
class MemoryPressureControllerBrowserTest : public ContentBrowserTest {
public:
MOCK_METHOD1(OnMemoryPressure,
void(base::MemoryPressureListener::MemoryPressureLevel level));
protected:
void SetPressureNotificationsSuppressedInAllProcessesAndWait(
bool suppressed) {
MemoryPressureController::GetInstance()
->SetPressureNotificationsSuppressedInAllProcesses(suppressed);
RunAllPendingInMessageLoop(BrowserThread::IO);
}
void SimulatePressureNotificationInAllProcessesAndWait(
base::MemoryPressureListener::MemoryPressureLevel level) {
MemoryPressureController::GetInstance()
->SimulatePressureNotificationInAllProcesses(level);
RunAllPendingInMessageLoop(BrowserThread::IO);
}
void SendPressureNotificationAndWait(
const void* fake_process_host,
base::MemoryPressureListener::MemoryPressureLevel level) {
MemoryPressureController::GetInstance()->SendPressureNotification(
reinterpret_cast<const RenderProcessHost*>(fake_process_host), level);
RunAllPendingInMessageLoop(BrowserThread::IO);
}
};
const auto MEMORY_PRESSURE_LEVEL_MODERATE =
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
const auto MEMORY_PRESSURE_LEVEL_CRITICAL =
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
IN_PROC_BROWSER_TEST_F(MemoryPressureControllerBrowserTest,
SetPressureNotificationsSuppressedInAllProcesses) {
scoped_refptr<MemoryMessageFilterForTesting> filter1(
new MemoryMessageFilterForTesting);
scoped_refptr<MemoryMessageFilterForTesting> filter2(
new MemoryMessageFilterForTesting);
NavigateToURL(shell(), GetTestUrl("", "title.html"));
// Add the first filter. No messages should be sent because notifications are
// not suppressed.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
filter1->Add();
EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed());
testing::Mock::VerifyAndClearExpectations(this);
// Enable suppressing memory pressure notifications in all processes. The
// first filter should send a message.
EXPECT_CALL(*filter1, Send(IsSetSuppressedMessage(true))).Times(1);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
SetPressureNotificationsSuppressedInAllProcessesAndWait(true);
EXPECT_TRUE(base::MemoryPressureListener::AreNotificationsSuppressed());
testing::Mock::VerifyAndClearExpectations(this);
// Add the second filter. It should send a message because notifications are
// suppressed.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(IsSetSuppressedMessage(true))).Times(1);
filter2->Add();
testing::Mock::VerifyAndClearExpectations(this);
// Send a memory pressure event to the first child process. No messages should
// be sent as the notifications are suppressed.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
SendPressureNotificationAndWait(filter1.get(),
MEMORY_PRESSURE_LEVEL_MODERATE);
testing::Mock::VerifyAndClearExpectations(this);
// Disable suppressing memory pressure notifications in all processes. Both
// filters should send a message.
EXPECT_CALL(*filter1, Send(IsSetSuppressedMessage(false))).Times(1);
EXPECT_CALL(*filter2, Send(IsSetSuppressedMessage(false))).Times(1);
SetPressureNotificationsSuppressedInAllProcessesAndWait(false);
EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed());
testing::Mock::VerifyAndClearExpectations(this);
// Send a memory pressure event to the first child process. A message should
// be received as messages are not being suppressed.
EXPECT_CALL(*filter1, Send(IsPressureMessage(MEMORY_PRESSURE_LEVEL_MODERATE)))
.Times(1);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
SendPressureNotificationAndWait(filter1.get(),
MEMORY_PRESSURE_LEVEL_MODERATE);
testing::Mock::VerifyAndClearExpectations(this);
// Send a memory pressure event to a non-existing child process. No message
// should be sent.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
SendPressureNotificationAndWait(reinterpret_cast<const void*>(0xF005BA11),
MEMORY_PRESSURE_LEVEL_MODERATE);
testing::Mock::VerifyAndClearExpectations(this);
// Remove the first filter. No messages should be sent.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
filter1->Remove();
testing::Mock::VerifyAndClearExpectations(this);
// Enable suppressing memory pressure notifications in all processes. The
// second filter should send a message.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(IsSetSuppressedMessage(true))).Times(1);
SetPressureNotificationsSuppressedInAllProcessesAndWait(true);
EXPECT_TRUE(base::MemoryPressureListener::AreNotificationsSuppressed());
testing::Mock::VerifyAndClearExpectations(this);
// Remove the second filter and disable suppressing memory pressure
// notifications in all processes. No messages should be sent.
EXPECT_CALL(*filter1, Send(testing::_)).Times(0);
EXPECT_CALL(*filter2, Send(testing::_)).Times(0);
filter2->Remove();
SetPressureNotificationsSuppressedInAllProcessesAndWait(false);
EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed());
testing::Mock::VerifyAndClearExpectations(this);
}
IN_PROC_BROWSER_TEST_F(MemoryPressureControllerBrowserTest,
SimulatePressureNotificationInAllProcesses) {
scoped_refptr<MemoryMessageFilterForTesting> filter(
new MemoryMessageFilterForTesting);
scoped_ptr<base::MemoryPressureListener> listener(
new base::MemoryPressureListener(
base::Bind(&MemoryPressureControllerBrowserTest::OnMemoryPressure,
base::Unretained(this))));
NavigateToURL(shell(), GetTestUrl("", "title.html"));
filter->Add();
// Send a memory pressure event to the first child process. It should send a
// pressure notification message.
EXPECT_CALL(*filter, Send(IsPressureMessage(MEMORY_PRESSURE_LEVEL_MODERATE)))
.Times(1);
SendPressureNotificationAndWait(filter.get(), MEMORY_PRESSURE_LEVEL_MODERATE);
testing::Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*filter, Send(IsSimulateMessage(MEMORY_PRESSURE_LEVEL_CRITICAL)))
.Times(1);
EXPECT_CALL(*this, OnMemoryPressure(MEMORY_PRESSURE_LEVEL_CRITICAL)).Times(1);
SimulatePressureNotificationInAllProcessesAndWait(
MEMORY_PRESSURE_LEVEL_CRITICAL);
RunAllPendingInMessageLoop(); // Wait for the listener to run.
testing::Mock::VerifyAndClearExpectations(this);
// Enable suppressing memory pressure notifications in all processes. This
// should have no impact on simulating memory pressure notifications.
EXPECT_CALL(*filter, Send(IsSetSuppressedMessage(true))).Times(1);
SetPressureNotificationsSuppressedInAllProcessesAndWait(true);
testing::Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*filter, Send(IsSimulateMessage(MEMORY_PRESSURE_LEVEL_MODERATE)))
.Times(1);
EXPECT_CALL(*this, OnMemoryPressure(MEMORY_PRESSURE_LEVEL_MODERATE)).Times(1);
SimulatePressureNotificationInAllProcessesAndWait(
MEMORY_PRESSURE_LEVEL_MODERATE);
RunAllPendingInMessageLoop(); // Wait for the listener to run.
testing::Mock::VerifyAndClearExpectations(this);
// Disable suppressing memory pressure notifications in all processes. This
// should have no impact on simulating memory pressure notifications.
EXPECT_CALL(*filter, Send(IsSetSuppressedMessage(false))).Times(1);
SetPressureNotificationsSuppressedInAllProcessesAndWait(false);
testing::Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*filter, Send(IsSimulateMessage(MEMORY_PRESSURE_LEVEL_MODERATE)))
.Times(1);
EXPECT_CALL(*this, OnMemoryPressure(MEMORY_PRESSURE_LEVEL_MODERATE)).Times(1);
SimulatePressureNotificationInAllProcessesAndWait(
MEMORY_PRESSURE_LEVEL_MODERATE);
RunAllPendingInMessageLoop(); // Wait for the listener to run.
testing::Mock::VerifyAndClearExpectations(this);
filter->Remove();
}
} // namespace content