OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/memory/tab_manager.h" | 5 #include "chrome/browser/memory/tab_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/macros.h" | 11 #include "base/macros.h" |
12 #include "base/strings/string16.h" | 12 #include "base/strings/string16.h" |
| 13 #include "base/test/simple_test_tick_clock.h" |
13 #include "base/time/time.h" | 14 #include "base/time/time.h" |
14 #include "build/build_config.h" | 15 #include "build/build_config.h" |
15 #include "chrome/browser/memory/tab_manager_web_contents_data.h" | 16 #include "chrome/browser/memory/tab_manager_web_contents_data.h" |
16 #include "chrome/browser/memory/tab_stats.h" | 17 #include "chrome/browser/memory/tab_stats.h" |
17 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
18 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 19 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
19 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" | 20 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" |
20 #include "chrome/common/url_constants.h" | 21 #include "chrome/common/url_constants.h" |
21 #include "chrome/test/base/chrome_render_view_host_test_harness.h" | 22 #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
22 #include "chrome/test/base/testing_profile.h" | 23 #include "chrome/test/base/testing_profile.h" |
23 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
24 #include "content/public/test/web_contents_tester.h" | 25 #include "content/public/test/web_contents_tester.h" |
| 26 #include "testing/gmock/include/gmock/gmock.h" |
25 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
26 #include "url/gurl.h" | 28 #include "url/gurl.h" |
27 | 29 |
28 using content::WebContents; | 30 using content::WebContents; |
29 using content::WebContentsTester; | 31 using content::WebContentsTester; |
30 | 32 |
31 namespace memory { | 33 namespace memory { |
32 namespace { | 34 namespace { |
33 | 35 |
34 class TabStripDummyDelegate : public TestTabStripModelDelegate { | 36 class TabStripDummyDelegate : public TestTabStripModelDelegate { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 } | 71 } |
70 | 72 |
71 private: | 73 private: |
72 int nb_events_; | 74 int nb_events_; |
73 WebContents* old_contents_; | 75 WebContents* old_contents_; |
74 WebContents* new_contents_; | 76 WebContents* new_contents_; |
75 | 77 |
76 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); | 78 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); |
77 }; | 79 }; |
78 | 80 |
| 81 // A mock task runner. This isn't directly a TaskRunner as the reference |
| 82 // counting confuses gmock. |
| 83 class LenientMockTaskRunner { |
| 84 public: |
| 85 LenientMockTaskRunner() {} |
| 86 MOCK_METHOD3(PostDelayedTask, |
| 87 bool(const tracked_objects::Location&, |
| 88 const base::Closure&, |
| 89 base::TimeDelta)); |
| 90 private: |
| 91 DISALLOW_COPY_AND_ASSIGN(LenientMockTaskRunner); |
| 92 }; |
| 93 using MockTaskRunner = testing::StrictMock<LenientMockTaskRunner>; |
| 94 |
| 95 // Represents a pending task. |
| 96 using Task = std::pair<base::TimeTicks, base::Closure>; |
| 97 |
| 98 // Comparator used for sorting Task objects. Can't use std::pair's default |
| 99 // comparison operators because Closure's are comparable. |
| 100 struct TaskComparator { |
| 101 bool operator()(const Task& t1, const Task& t2) const { |
| 102 // Reverse the sort order so this can be used with a min-heap. |
| 103 return t1.first > t2.first; |
| 104 } |
| 105 }; |
| 106 |
| 107 // A TaskRunner implementation that delegates to a MockTaskRunner for asserting |
| 108 // on task insertion order, and which stores tasks for actual later execution. |
| 109 class TaskRunnerProxy : public base::TaskRunner { |
| 110 public: |
| 111 TaskRunnerProxy(MockTaskRunner* mock, |
| 112 base::SimpleTestTickClock* clock) |
| 113 : mock_(mock), clock_(clock) {} |
| 114 bool RunsTasksOnCurrentThread() const override { return true; } |
| 115 bool PostDelayedTask(const tracked_objects::Location& location, |
| 116 const base::Closure& closure, |
| 117 base::TimeDelta delta) override { |
| 118 mock_->PostDelayedTask(location, closure, delta); |
| 119 base::TimeTicks when = clock_->NowTicks() + delta; |
| 120 tasks_.push_back(Task(when, closure)); |
| 121 // Use 'greater' comparator to make this a min heap. |
| 122 std::push_heap(tasks_.begin(), tasks_.end(), TaskComparator()); |
| 123 return true; |
| 124 } |
| 125 |
| 126 // Runs tasks up to the current time. Returns the number of tasks executed. |
| 127 size_t RunTasks() { |
| 128 base::TimeTicks now = clock_->NowTicks(); |
| 129 size_t count = 0; |
| 130 while (!tasks_.empty() && tasks_.front().first <= now) { |
| 131 tasks_.front().second.Run(); |
| 132 std::pop_heap(tasks_.begin(), tasks_.end(), TaskComparator()); |
| 133 tasks_.pop_back(); |
| 134 ++count; |
| 135 } |
| 136 return count; |
| 137 } |
| 138 |
| 139 // Advances the clock and runs the next task in the queue. Can run more than |
| 140 // one task if multiple tasks have the same scheduled time. |
| 141 size_t RunNextTask() { |
| 142 // Get the time of the next task that can possibly run. |
| 143 base::TimeTicks when = tasks_.front().first; |
| 144 |
| 145 // Advance the clock to exactly that time. |
| 146 base::TimeTicks now = clock_->NowTicks(); |
| 147 if (now < when) { |
| 148 base::TimeDelta delta = when - now; |
| 149 clock_->Advance(delta); |
| 150 } |
| 151 |
| 152 // Run whatever tasks are now eligible to run. |
| 153 return RunTasks(); |
| 154 } |
| 155 |
| 156 size_t size() const { return tasks_.size(); } |
| 157 |
| 158 private: |
| 159 ~TaskRunnerProxy() override {} |
| 160 |
| 161 MockTaskRunner* mock_; |
| 162 base::SimpleTestTickClock* clock_; |
| 163 |
| 164 // A min-heap of outstanding tasks. |
| 165 using Task = std::pair<base::TimeTicks, base::Closure>; |
| 166 std::vector<Task> tasks_; |
| 167 |
| 168 DISALLOW_COPY_AND_ASSIGN(TaskRunnerProxy); |
| 169 }; |
| 170 |
79 enum TestIndicies { | 171 enum TestIndicies { |
80 kSelected, | 172 kSelected, |
81 kPinned, | 173 kPinned, |
82 kApp, | 174 kApp, |
83 kPlayingAudio, | 175 kPlayingAudio, |
84 kFormEntry, | 176 kFormEntry, |
85 kRecent, | 177 kRecent, |
86 kOld, | 178 kOld, |
87 kReallyOld, | 179 kReallyOld, |
88 kOldButPinned, | 180 kOldButPinned, |
89 kInternalPage, | 181 kInternalPage, |
90 }; | 182 }; |
91 } // namespace | 183 } // namespace |
92 | 184 |
93 class TabManagerTest : public ChromeRenderViewHostTestHarness { | 185 class LenientTabManagerTest : public ChromeRenderViewHostTestHarness { |
94 public: | 186 public: |
95 WebContents* CreateWebContents() { | 187 WebContents* CreateWebContents() { |
96 return WebContents::Create(WebContents::CreateParams(profile())); | 188 return WebContents::Create(WebContents::CreateParams(profile())); |
97 } | 189 } |
| 190 |
| 191 MOCK_METHOD2(NotifyRendererProcess, |
| 192 void(const content::RenderProcessHost*, |
| 193 base::MemoryPressureListener::MemoryPressureLevel)); |
98 }; | 194 }; |
| 195 using TabManagerTest = testing::StrictMock<LenientTabManagerTest>; |
99 | 196 |
100 // TODO(georgesak): Add tests for protection to tabs with form input and | 197 // TODO(georgesak): Add tests for protection to tabs with form input and |
101 // playing audio; | 198 // playing audio; |
102 | 199 |
103 // Tests the sorting comparator to make sure it's producing the desired order. | 200 // Tests the sorting comparator to make sure it's producing the desired order. |
104 TEST_F(TabManagerTest, Comparator) { | 201 TEST_F(TabManagerTest, Comparator) { |
105 TabStatsList test_list; | 202 TabStatsList test_list; |
106 const base::TimeTicks now = base::TimeTicks::Now(); | 203 const base::TimeTicks now = base::TimeTicks::Now(); |
107 | 204 |
108 // Add kSelected last to verify that the array is being sorted. | 205 // Add kSelected last to verify that the array is being sorted. |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 test_contents->SetLastActiveTime(new_last_active_time); | 426 test_contents->SetLastActiveTime(new_last_active_time); |
330 EXPECT_EQ(new_last_active_time, test_contents->GetLastActiveTime()); | 427 EXPECT_EQ(new_last_active_time, test_contents->GetLastActiveTime()); |
331 | 428 |
332 WebContents* null_contents = tab_manager.DiscardWebContentsAt(1, &tabstrip); | 429 WebContents* null_contents = tab_manager.DiscardWebContentsAt(1, &tabstrip); |
333 EXPECT_EQ(new_last_active_time, null_contents->GetLastActiveTime()); | 430 EXPECT_EQ(new_last_active_time, null_contents->GetLastActiveTime()); |
334 | 431 |
335 tabstrip.CloseAllTabs(); | 432 tabstrip.CloseAllTabs(); |
336 EXPECT_TRUE(tabstrip.empty()); | 433 EXPECT_TRUE(tabstrip.empty()); |
337 } | 434 } |
338 | 435 |
| 436 namespace { |
| 437 |
| 438 using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel; |
| 439 |
| 440 // Function that always indicates the absence of memory pressure. |
| 441 MemoryPressureLevel ReturnNoPressure() { |
| 442 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
| 443 } |
| 444 |
| 445 // Function that simply parrots back an externally specified memory pressure |
| 446 // level. |
| 447 MemoryPressureLevel ReturnSpecifiedPressure( |
| 448 const MemoryPressureLevel* level) { |
| 449 return *level; |
| 450 } |
| 451 |
| 452 } // namespace |
| 453 |
| 454 // Ensure that memory pressure notifications are forwarded to child processes. |
| 455 TEST_F(TabManagerTest, ChildProcessNotifications) { |
| 456 TabManager tm; |
| 457 |
| 458 // Set up the tab strip. |
| 459 TabStripDummyDelegate delegate; |
| 460 TabStripModel tabstrip(&delegate, profile()); |
| 461 |
| 462 // Create test clock, task runner. |
| 463 base::SimpleTestTickClock test_clock; |
| 464 MockTaskRunner mock_task_runner; |
| 465 scoped_refptr<TaskRunnerProxy> task_runner( |
| 466 new TaskRunnerProxy(&mock_task_runner, &test_clock)); |
| 467 |
| 468 // Configure the TabManager for testing. |
| 469 tabstrip.AddObserver(&tm); |
| 470 tm.test_tab_strip_models_.push_back( |
| 471 TabManager::TestTabStripModel(&tabstrip, false /* !is_app */)); |
| 472 tm.test_tick_clock_ = &test_clock; |
| 473 tm.task_runner_ = task_runner; |
| 474 tm.notify_renderer_process_ = base::Bind( |
| 475 &TabManagerTest::NotifyRendererProcess, base::Unretained(this)); |
| 476 |
| 477 // Create two dummy tabs. |
| 478 auto tab1 = CreateWebContents(); |
| 479 auto tab2 = CreateWebContents(); |
| 480 tabstrip.AppendWebContents(tab1, true); // Foreground tab. |
| 481 tabstrip.AppendWebContents(tab2, false); // Opened in background. |
| 482 const content::RenderProcessHost* renderer1 = tab1->GetRenderProcessHost(); |
| 483 const content::RenderProcessHost* renderer2 = tab2->GetRenderProcessHost(); |
| 484 |
| 485 // Expect that the tab manager has not yet encountered memory pressure. |
| 486 EXPECT_FALSE(tm.under_memory_pressure_); |
| 487 EXPECT_EQ(0u, tm.notified_renderers_.size()); |
| 488 |
| 489 // Simulate a memory pressure situation that will immediately end. This should |
| 490 // cause no notifications or scheduled tasks. |
| 491 auto level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; |
| 492 tm.get_current_pressure_level_ = base::Bind(&ReturnNoPressure); |
| 493 tm.OnMemoryPressure(level); |
| 494 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 495 EXPECT_FALSE(tm.under_memory_pressure_); |
| 496 EXPECT_EQ(0u, task_runner->size()); |
| 497 EXPECT_EQ(0u, tm.notified_renderers_.size()); |
| 498 |
| 499 // START OF MEMORY PRESSURE |
| 500 |
| 501 // Simulate a memory pressure situation that persists. This should cause a |
| 502 // task to be scheduled. |
| 503 tm.get_current_pressure_level_ = base::Bind( |
| 504 &ReturnSpecifiedPressure, base::Unretained(&level)); |
| 505 EXPECT_CALL(mock_task_runner, PostDelayedTask( |
| 506 testing::_, |
| 507 testing::_, |
| 508 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds))); |
| 509 EXPECT_CALL(*this, NotifyRendererProcess(renderer2, level)); |
| 510 tm.OnMemoryPressure(level); |
| 511 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 512 testing::Mock::VerifyAndClearExpectations(this); |
| 513 EXPECT_TRUE(tm.under_memory_pressure_); |
| 514 EXPECT_EQ(1u, task_runner->size()); |
| 515 EXPECT_EQ(1u, tm.notified_renderers_.size()); |
| 516 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer2)); |
| 517 |
| 518 // REPEATED MEMORY PRESSURE SIGNAL |
| 519 |
| 520 // Simulate another memory pressure event. This should not cause any |
| 521 // additional tasks to be scheduled, nor any further renderer notifications. |
| 522 tm.OnMemoryPressure(level); |
| 523 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 524 testing::Mock::VerifyAndClearExpectations(this); |
| 525 EXPECT_TRUE(tm.under_memory_pressure_); |
| 526 EXPECT_EQ(1u, task_runner->size()); |
| 527 EXPECT_EQ(1u, tm.notified_renderers_.size()); |
| 528 |
| 529 // FIRST SCHEDULED NOTIFICATION |
| 530 |
| 531 // Run the scheduled task. This should cause another notification, but this |
| 532 // time to the foreground tab. It should also cause another scheduled event. |
| 533 EXPECT_CALL(mock_task_runner, PostDelayedTask( |
| 534 testing::_, |
| 535 testing::_, |
| 536 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds))); |
| 537 EXPECT_CALL(*this, NotifyRendererProcess(renderer1, level)); |
| 538 EXPECT_EQ(1u, task_runner->RunNextTask()); |
| 539 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 540 testing::Mock::VerifyAndClearExpectations(this); |
| 541 EXPECT_TRUE(tm.under_memory_pressure_); |
| 542 EXPECT_EQ(1u, task_runner->size()); |
| 543 EXPECT_EQ(2u, tm.notified_renderers_.size()); |
| 544 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer1)); |
| 545 |
| 546 // SECOND SCHEDULED NOTIFICATION |
| 547 |
| 548 // Run the scheduled task. This should cause another notification, to the |
| 549 // background tab. It should also cause another scheduled event. |
| 550 EXPECT_CALL(mock_task_runner, PostDelayedTask( |
| 551 testing::_, |
| 552 testing::_, |
| 553 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds))); |
| 554 EXPECT_CALL(*this, NotifyRendererProcess(renderer2, level)); |
| 555 EXPECT_EQ(1u, task_runner->RunNextTask()); |
| 556 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 557 testing::Mock::VerifyAndClearExpectations(this); |
| 558 EXPECT_TRUE(tm.under_memory_pressure_); |
| 559 EXPECT_EQ(1u, task_runner->size()); |
| 560 EXPECT_EQ(1u, tm.notified_renderers_.size()); |
| 561 |
| 562 // Expect that the background tab has been notified. The list of notified |
| 563 // renderers maintained by the TabManager should also have been reset because |
| 564 // the notification logic restarts after all renderers are notified. |
| 565 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer2)); |
| 566 |
| 567 // END OF MEMORY PRESSURE |
| 568 |
| 569 // End the memory pressure condition and run the scheduled event. This should |
| 570 // clean up the various state data. It should not schedule another event nor |
| 571 // fire any notifications. |
| 572 level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
| 573 EXPECT_EQ(1u, task_runner->RunNextTask()); |
| 574 testing::Mock::VerifyAndClearExpectations(&mock_task_runner); |
| 575 testing::Mock::VerifyAndClearExpectations(this); |
| 576 EXPECT_FALSE(tm.under_memory_pressure_); |
| 577 EXPECT_EQ(0u, task_runner->size()); |
| 578 EXPECT_EQ(0u, tm.notified_renderers_.size()); |
| 579 |
| 580 |
| 581 // Clean up the tabstrip. |
| 582 tabstrip.CloseAllTabs(); |
| 583 ASSERT_TRUE(tabstrip.empty()); |
| 584 } |
| 585 |
339 } // namespace memory | 586 } // namespace memory |
OLD | NEW |