Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: chrome/browser/memory/tab_manager_unittest.cc

Issue 1641813002: Provide renderers with memory pressure signals. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@contentapi
Patch Set: Addressed creis' comments. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/memory/tab_manager.cc ('k') | chrome/browser/memory/tab_stats.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/memory/tab_manager.cc ('k') | chrome/browser/memory/tab_stats.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698