Index: content/browser/memory/memory_coordinator_impl_unittest.cc |
diff --git a/content/browser/memory/memory_coordinator_impl_unittest.cc b/content/browser/memory/memory_coordinator_impl_unittest.cc |
index b2d8530fc58154982eaad874ee37260de3901670..0aa49d7a02d342a882fed3584cb5ae911ebd7e38 100644 |
--- a/content/browser/memory/memory_coordinator_impl_unittest.cc |
+++ b/content/browser/memory/memory_coordinator_impl_unittest.cc |
@@ -4,19 +4,52 @@ |
#include "content/browser/memory/memory_coordinator_impl.h" |
+#include "base/memory/memory_coordinator_client_registry.h" |
#include "base/memory/memory_coordinator_proxy.h" |
+#include "base/memory/memory_pressure_monitor.h" |
+#include "base/message_loop/message_loop.h" |
#include "base/run_loop.h" |
#include "base/test/scoped_feature_list.h" |
#include "base/test/test_mock_time_task_runner.h" |
#include "content/browser/memory/memory_monitor.h" |
#include "content/browser/memory/memory_state_updater.h" |
#include "content/public/common/content_features.h" |
+#include "mojo/public/cpp/bindings/binding.h" |
#include "testing/gtest/include/gtest/gtest.h" |
namespace content { |
namespace { |
+void RunUntilIdle() { |
+ base::RunLoop loop; |
+ loop.RunUntilIdle(); |
+} |
+ |
+// A mock ChildMemoryCoordinator, for testing interaction between MC and CMC. |
+class MockChildMemoryCoordinator : public mojom::ChildMemoryCoordinator { |
+ public: |
+ MockChildMemoryCoordinator() |
+ : state_(mojom::MemoryState::NORMAL), |
+ on_state_change_calls_(0) {} |
+ |
+ ~MockChildMemoryCoordinator() override {} |
+ |
+ void OnStateChange(mojom::MemoryState state) override { |
+ state_ = state; |
+ ++on_state_change_calls_; |
+ } |
+ |
+ mojom::MemoryState state() const { return state_; } |
+ int on_state_change_calls() const { return on_state_change_calls_; } |
+ |
+ private: |
+ mojom::MemoryState state_; |
+ int on_state_change_calls_; |
+}; |
+ |
+// A mock MemoryCoordinatorClient, for testing interaction between MC and |
+// clients. |
class MockMemoryCoordinatorClient : public base::MemoryCoordinatorClient { |
public: |
void OnMemoryStateChange(base::MemoryState state) override { |
@@ -32,8 +65,6 @@ class MockMemoryCoordinatorClient : public base::MemoryCoordinatorClient { |
base::MemoryState state_ = base::MemoryState::NORMAL; |
}; |
-} // namespace |
- |
class MockMemoryMonitor : public MemoryMonitor { |
public: |
MockMemoryMonitor() {} |
@@ -52,22 +83,66 @@ class MockMemoryMonitor : public MemoryMonitor { |
DISALLOW_COPY_AND_ASSIGN(MockMemoryMonitor); |
}; |
+// A MemoryCoordinatorImpl that can be directly constructed. |
+class TestMemoryCoordinatorImpl : public MemoryCoordinatorImpl { |
+ public: |
+ // Mojo machinery for wrapping a mock ChildMemoryCoordinator. |
+ struct Child { |
+ Child(mojom::ChildMemoryCoordinatorPtr* cmc_ptr) : cmc_binding(&cmc) { |
+ cmc_binding.Bind(mojo::GetProxy(cmc_ptr)); |
+ RunUntilIdle(); |
+ } |
+ |
+ MockChildMemoryCoordinator cmc; |
+ mojo::Binding<mojom::ChildMemoryCoordinator> cmc_binding; |
+ }; |
+ |
+ TestMemoryCoordinatorImpl( |
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner) |
+ : MemoryCoordinatorImpl(task_runner, |
+ base::MakeUnique<MockMemoryMonitor>()) {} |
+ ~TestMemoryCoordinatorImpl() override {} |
+ |
+ using MemoryCoordinatorImpl::OnConnectionError; |
+ using MemoryCoordinatorImpl::children; |
+ |
+ MockChildMemoryCoordinator* CreateChildMemoryCoordinator( |
+ int process_id) { |
+ mojom::ChildMemoryCoordinatorPtr cmc_ptr; |
+ children_.push_back(std::unique_ptr<Child>(new Child(&cmc_ptr))); |
+ AddChildForTesting(process_id, std::move(cmc_ptr)); |
+ return &children_.back()->cmc; |
+ } |
+ |
+ // Wrapper of MemoryCoordinator::SetMemoryState that also calls RunUntilIdle. |
+ bool SetChildMemoryState( |
+ int render_process_id, mojom::MemoryState memory_state) { |
+ bool result = MemoryCoordinatorImpl::SetChildMemoryState( |
+ render_process_id, memory_state); |
+ RunUntilIdle(); |
+ return result; |
+ } |
+ |
+ std::vector<std::unique_ptr<Child>> children_; |
+}; |
+ |
+} // namespace |
+ |
class MemoryCoordinatorImplTest : public testing::Test { |
public: |
void SetUp() override { |
scoped_feature_list_.InitAndEnableFeature(features::kMemoryCoordinator); |
task_runner_ = new base::TestMockTimeTaskRunner(); |
- coordinator_.reset(new MemoryCoordinatorImpl( |
- task_runner_, base::WrapUnique(new MockMemoryMonitor))); |
+ coordinator_.reset(new TestMemoryCoordinatorImpl(task_runner_)); |
base::MemoryCoordinatorProxy::GetInstance()-> |
SetGetCurrentMemoryStateCallback(base::Bind( |
- &MemoryCoordinator::GetCurrentMemoryState, |
+ &MemoryCoordinatorImpl::GetCurrentMemoryState, |
base::Unretained(coordinator_.get()))); |
base::MemoryCoordinatorProxy::GetInstance()-> |
SetSetCurrentMemoryStateForTestingCallback(base::Bind( |
- &MemoryCoordinator::SetCurrentMemoryStateForTesting, |
+ &MemoryCoordinatorImpl::SetCurrentMemoryStateForTesting, |
base::Unretained(coordinator_.get()))); |
} |
@@ -76,12 +151,92 @@ class MemoryCoordinatorImplTest : public testing::Test { |
} |
protected: |
- std::unique_ptr<MemoryCoordinatorImpl> coordinator_; |
+ std::unique_ptr<TestMemoryCoordinatorImpl> coordinator_; |
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
base::MessageLoop message_loop_; |
base::test::ScopedFeatureList scoped_feature_list_; |
}; |
+TEST_F(MemoryCoordinatorImplTest, ChildRemovedOnConnectionError) { |
+ coordinator_->CreateChildMemoryCoordinator(1); |
+ ASSERT_EQ(1u, coordinator_->children().size()); |
+ coordinator_->OnConnectionError(1); |
+ EXPECT_EQ(0u, coordinator_->children().size()); |
+} |
+ |
+TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidState) { |
+ auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
+ |
+ EXPECT_FALSE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::UNKNOWN)); |
+ EXPECT_EQ(0, cmc1->on_state_change_calls()); |
+} |
+ |
+TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidRenderer) { |
+ auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
+ |
+ EXPECT_FALSE( |
+ coordinator_->SetChildMemoryState(2, mojom::MemoryState::THROTTLED)); |
+ EXPECT_EQ(0, cmc1->on_state_change_calls()); |
+} |
+ |
+TEST_F(MemoryCoordinatorImplTest, SetMemoryStateNotDeliveredNop) { |
+ auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
+ |
+ EXPECT_FALSE( |
+ coordinator_->SetChildMemoryState(2, mojom::MemoryState::NORMAL)); |
+ EXPECT_EQ(0, cmc1->on_state_change_calls()); |
+} |
+ |
+TEST_F(MemoryCoordinatorImplTest, SetMemoryStateDelivered) { |
+ auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
+ auto cmc2 = coordinator_->CreateChildMemoryCoordinator(2); |
+ |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
+ EXPECT_EQ(1, cmc1->on_state_change_calls()); |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc1->state()); |
+ |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(2, mojom::MemoryState::SUSPENDED)); |
+ EXPECT_EQ(1, cmc2->on_state_change_calls()); |
+ // Child processes are considered as visible (foreground) by default, |
+ // and visible ones won't be suspended but throttled. |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc2->state()); |
+} |
+ |
+TEST_F(MemoryCoordinatorImplTest, SetChildMemoryState) { |
+ auto cmc = coordinator_->CreateChildMemoryCoordinator(1); |
+ auto iter = coordinator_->children().find(1); |
+ ASSERT_TRUE(iter != coordinator_->children().end()); |
+ |
+ // Foreground |
+ iter->second.is_visible = true; |
+ EXPECT_TRUE(coordinator_->SetChildMemoryState(1, mojom::MemoryState::NORMAL)); |
+ EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
+ |
+ // Background |
+ iter->second.is_visible = false; |
+ EXPECT_TRUE(coordinator_->SetChildMemoryState(1, mojom::MemoryState::NORMAL)); |
+#if defined(OS_ANDROID) |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
+#else |
+ EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); |
+#endif |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
+ EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
+ EXPECT_TRUE( |
+ coordinator_->SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); |
+ EXPECT_EQ(mojom::MemoryState::SUSPENDED, cmc->state()); |
+} |
+ |
TEST_F(MemoryCoordinatorImplTest, CalculateNextState) { |
auto* state_updater = coordinator_->state_updater_.get(); |
state_updater->expected_renderer_size_ = 10; |
@@ -160,8 +315,7 @@ TEST_F(MemoryCoordinatorImplTest, UpdateState) { |
coordinator_->current_state_ = base::MemoryState::NORMAL; |
GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40); |
state_updater->UpdateState(); |
- base::RunLoop loop; |
- loop.RunUntilIdle(); |
+ RunUntilIdle(); |
EXPECT_TRUE(client.is_called()); |
EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); |
base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |
@@ -174,8 +328,7 @@ TEST_F(MemoryCoordinatorImplTest, UpdateState) { |
coordinator_->current_state_ = base::MemoryState::NORMAL; |
GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50); |
state_updater->UpdateState(); |
- base::RunLoop loop; |
- loop.RunUntilIdle(); |
+ RunUntilIdle(); |
EXPECT_FALSE(client.is_called()); |
EXPECT_EQ(base::MemoryState::NORMAL, client.state()); |
base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |
@@ -215,9 +368,8 @@ TEST_F(MemoryCoordinatorImplTest, SetMemoryStateForTesting) { |
coordinator_->GetCurrentMemoryState()); |
EXPECT_EQ(base::MemoryState::THROTTLED, |
base::MemoryCoordinatorProxy::GetInstance()-> |
- GetCurrentMemoryState()); |
- base::RunLoop loop; |
- loop.RunUntilIdle(); |
+ GetCurrentMemoryState()); |
+ RunUntilIdle(); |
EXPECT_TRUE(client.is_called()); |
EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); |
base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |