| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/memory/memory_coordinator.h" | |
| 6 | |
| 7 #include "base/memory/memory_pressure_monitor.h" | |
| 8 #include "base/message_loop/message_loop.h" | |
| 9 #include "base/run_loop.h" | |
| 10 #include "mojo/public/cpp/bindings/binding.h" | |
| 11 | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 | |
| 14 namespace content { | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 void RunUntilIdle() { | |
| 19 base::RunLoop loop; | |
| 20 loop.RunUntilIdle(); | |
| 21 } | |
| 22 | |
| 23 } // namespace | |
| 24 | |
| 25 // A mock ChildMemoryCoordinator, for testing interaction between MC and CMC. | |
| 26 class MockChildMemoryCoordinator : public mojom::ChildMemoryCoordinator { | |
| 27 public: | |
| 28 MockChildMemoryCoordinator() | |
| 29 : state_(mojom::MemoryState::NORMAL), | |
| 30 on_state_change_calls_(0) {} | |
| 31 | |
| 32 ~MockChildMemoryCoordinator() override {} | |
| 33 | |
| 34 void OnStateChange(mojom::MemoryState state) override { | |
| 35 state_ = state; | |
| 36 ++on_state_change_calls_; | |
| 37 } | |
| 38 | |
| 39 mojom::MemoryState state() const { return state_; } | |
| 40 int on_state_change_calls() const { return on_state_change_calls_; } | |
| 41 | |
| 42 private: | |
| 43 mojom::MemoryState state_; | |
| 44 int on_state_change_calls_; | |
| 45 }; | |
| 46 | |
| 47 // A MemoryCoordinator that can be directly constructed. | |
| 48 class TestMemoryCoordinator : public MemoryCoordinator { | |
| 49 public: | |
| 50 // Mojo machinery for wrapping a mock ChildMemoryCoordinator. | |
| 51 struct Child { | |
| 52 Child(mojom::ChildMemoryCoordinatorPtr* cmc_ptr) : cmc_binding(&cmc) { | |
| 53 cmc_binding.Bind(mojo::GetProxy(cmc_ptr)); | |
| 54 RunUntilIdle(); | |
| 55 } | |
| 56 | |
| 57 MockChildMemoryCoordinator cmc; | |
| 58 mojo::Binding<mojom::ChildMemoryCoordinator> cmc_binding; | |
| 59 }; | |
| 60 | |
| 61 TestMemoryCoordinator() {} | |
| 62 ~TestMemoryCoordinator() override {} | |
| 63 | |
| 64 using MemoryCoordinator::OnConnectionError; | |
| 65 | |
| 66 MockChildMemoryCoordinator* CreateChildMemoryCoordinator( | |
| 67 int process_id) { | |
| 68 mojom::ChildMemoryCoordinatorPtr cmc_ptr; | |
| 69 children_.push_back(std::unique_ptr<Child>(new Child(&cmc_ptr))); | |
| 70 AddChildForTesting(process_id, std::move(cmc_ptr)); | |
| 71 return &children_.back()->cmc; | |
| 72 } | |
| 73 | |
| 74 // Wrapper of MemoryCoordinator::SetMemoryState that also calls RunUntilIdle. | |
| 75 bool SetChildMemoryState( | |
| 76 int render_process_id, mojom::MemoryState memory_state) { | |
| 77 bool result = MemoryCoordinator::SetChildMemoryState( | |
| 78 render_process_id, memory_state); | |
| 79 RunUntilIdle(); | |
| 80 return result; | |
| 81 } | |
| 82 | |
| 83 std::vector<std::unique_ptr<Child>> children_; | |
| 84 }; | |
| 85 | |
| 86 // Test fixture. | |
| 87 class MemoryCoordinatorTest : public testing::Test { | |
| 88 private: | |
| 89 // Needed for mojo. | |
| 90 base::MessageLoop message_loop_; | |
| 91 }; | |
| 92 | |
| 93 TEST_F(MemoryCoordinatorTest, ChildRemovedOnConnectionError) { | |
| 94 TestMemoryCoordinator mc; | |
| 95 mc.CreateChildMemoryCoordinator(1); | |
| 96 ASSERT_EQ(1u, mc.children().size()); | |
| 97 mc.OnConnectionError(1); | |
| 98 EXPECT_EQ(0u, mc.children().size()); | |
| 99 } | |
| 100 | |
| 101 TEST_F(MemoryCoordinatorTest, SetMemoryStateFailsInvalidState) { | |
| 102 TestMemoryCoordinator mc; | |
| 103 auto cmc1 = mc.CreateChildMemoryCoordinator(1); | |
| 104 | |
| 105 EXPECT_FALSE(mc.SetChildMemoryState(1, mojom::MemoryState::UNKNOWN)); | |
| 106 EXPECT_EQ(0, cmc1->on_state_change_calls()); | |
| 107 } | |
| 108 | |
| 109 TEST_F(MemoryCoordinatorTest, SetMemoryStateFailsInvalidRenderer) { | |
| 110 TestMemoryCoordinator mc; | |
| 111 auto cmc1 = mc.CreateChildMemoryCoordinator(1); | |
| 112 | |
| 113 EXPECT_FALSE(mc.SetChildMemoryState(2, mojom::MemoryState::THROTTLED)); | |
| 114 EXPECT_EQ(0, cmc1->on_state_change_calls()); | |
| 115 } | |
| 116 | |
| 117 TEST_F(MemoryCoordinatorTest, SetMemoryStateNotDeliveredNop) { | |
| 118 TestMemoryCoordinator mc; | |
| 119 auto cmc1 = mc.CreateChildMemoryCoordinator(1); | |
| 120 | |
| 121 EXPECT_FALSE(mc.SetChildMemoryState(2, mojom::MemoryState::NORMAL)); | |
| 122 EXPECT_EQ(0, cmc1->on_state_change_calls()); | |
| 123 } | |
| 124 | |
| 125 TEST_F(MemoryCoordinatorTest, SetMemoryStateDelivered) { | |
| 126 TestMemoryCoordinator mc; | |
| 127 auto cmc1 = mc.CreateChildMemoryCoordinator(1); | |
| 128 auto cmc2 = mc.CreateChildMemoryCoordinator(2); | |
| 129 | |
| 130 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); | |
| 131 EXPECT_EQ(1, cmc1->on_state_change_calls()); | |
| 132 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc1->state()); | |
| 133 | |
| 134 EXPECT_TRUE(mc.SetChildMemoryState(2, mojom::MemoryState::SUSPENDED)); | |
| 135 EXPECT_EQ(1, cmc2->on_state_change_calls()); | |
| 136 // Child processes are considered as visible (foreground) by default, | |
| 137 // and visible ones won't be suspended but throttled. | |
| 138 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc2->state()); | |
| 139 } | |
| 140 | |
| 141 TEST_F(MemoryCoordinatorTest, SetChildMemoryState) { | |
| 142 TestMemoryCoordinator mc; | |
| 143 auto cmc = mc.CreateChildMemoryCoordinator(1); | |
| 144 auto iter = mc.children().find(1); | |
| 145 ASSERT_TRUE(iter != mc.children().end()); | |
| 146 | |
| 147 // Foreground | |
| 148 iter->second.is_visible = true; | |
| 149 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::NORMAL)); | |
| 150 EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); | |
| 151 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); | |
| 152 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); | |
| 153 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); | |
| 154 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); | |
| 155 | |
| 156 // Background | |
| 157 iter->second.is_visible = false; | |
| 158 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::NORMAL)); | |
| 159 #if defined(OS_ANDROID) | |
| 160 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); | |
| 161 #else | |
| 162 EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); | |
| 163 #endif | |
| 164 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); | |
| 165 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); | |
| 166 EXPECT_TRUE(mc.SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); | |
| 167 EXPECT_EQ(mojom::MemoryState::SUSPENDED, cmc->state()); | |
| 168 } | |
| 169 | |
| 170 } // namespace content | |
| OLD | NEW |