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

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

Issue 2882513004: Remove renderer notifications of memory pressure. (Closed)
Patch Set: Merge Created 3 years, 7 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 <map> 8 #include <map>
9 #include <vector> 9 #include <vector>
10 10
(...skipping 13 matching lines...) Expand all
24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" 24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
25 #include "chrome/common/chrome_features.h" 25 #include "chrome/common/chrome_features.h"
26 #include "chrome/common/url_constants.h" 26 #include "chrome/common/url_constants.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h" 28 #include "chrome/test/base/testing_profile.h"
29 #include "components/variations/variations_associated_data.h" 29 #include "components/variations/variations_associated_data.h"
30 #include "content/public/browser/render_process_host.h" 30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/web_contents.h" 31 #include "content/public/browser/web_contents.h"
32 #include "content/public/test/mock_render_process_host.h" 32 #include "content/public/test/mock_render_process_host.h"
33 #include "content/public/test/web_contents_tester.h" 33 #include "content/public/test/web_contents_tester.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h" 34 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/gurl.h" 35 #include "url/gurl.h"
37 36
38 using content::WebContents; 37 using content::WebContents;
39 using content::WebContentsTester; 38 using content::WebContentsTester;
40 39
41 namespace memory { 40 namespace memory {
42 namespace { 41 namespace {
43 42
44 class TabStripDummyDelegate : public TestTabStripModelDelegate { 43 class TabStripDummyDelegate : public TestTabStripModelDelegate {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 } 78 }
80 79
81 private: 80 private:
82 int nb_events_; 81 int nb_events_;
83 WebContents* old_contents_; 82 WebContents* old_contents_;
84 WebContents* new_contents_; 83 WebContents* new_contents_;
85 84
86 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); 85 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
87 }; 86 };
88 87
89 // A mock task runner. This isn't directly a TaskRunner as the reference
90 // counting confuses gmock.
91 class LenientMockTaskRunner {
92 public:
93 LenientMockTaskRunner() {}
94 MOCK_METHOD2(PostDelayedTask,
95 bool(const tracked_objects::Location&, base::TimeDelta));
96
97 private:
98 DISALLOW_COPY_AND_ASSIGN(LenientMockTaskRunner);
99 };
100 using MockTaskRunner = testing::StrictMock<LenientMockTaskRunner>;
101
102 // Represents a pending task.
103 using Task = std::pair<base::TimeTicks, base::OnceClosure>;
104
105 // Comparator used for sorting Task objects. Can't use std::pair's default
106 // comparison operators because Closure's are comparable.
107 struct TaskComparator {
108 bool operator()(const Task& t1, const Task& t2) const {
109 // Reverse the sort order so this can be used with a min-heap.
110 return t1.first > t2.first;
111 }
112 };
113
114 // A TaskRunner implementation that delegates to a MockTaskRunner for asserting
115 // on task insertion order, and which stores tasks for actual later execution.
116 class TaskRunnerProxy : public base::TaskRunner {
117 public:
118 TaskRunnerProxy(MockTaskRunner* mock,
119 base::SimpleTestTickClock* clock)
120 : mock_(mock), clock_(clock) {}
121 bool RunsTasksInCurrentSequence() const override { return true; }
122 bool PostDelayedTask(const tracked_objects::Location& location,
123 base::OnceClosure closure,
124 base::TimeDelta delta) override {
125 mock_->PostDelayedTask(location, delta);
126 base::TimeTicks when = clock_->NowTicks() + delta;
127 tasks_.push_back(Task(when, std::move(closure)));
128 // Use 'greater' comparator to make this a min heap.
129 std::push_heap(tasks_.begin(), tasks_.end(), TaskComparator());
130 return true;
131 }
132
133 // Runs tasks up to the current time. Returns the number of tasks executed.
134 size_t RunTasks() {
135 base::TimeTicks now = clock_->NowTicks();
136 size_t count = 0;
137 while (!tasks_.empty() && tasks_.front().first <= now) {
138 std::move(tasks_.front().second).Run();
139 std::pop_heap(tasks_.begin(), tasks_.end(), TaskComparator());
140 tasks_.pop_back();
141 ++count;
142 }
143 return count;
144 }
145
146 // Advances the clock and runs the next task in the queue. Can run more than
147 // one task if multiple tasks have the same scheduled time.
148 size_t RunNextTask() {
149 // Get the time of the next task that can possibly run.
150 base::TimeTicks when = tasks_.front().first;
151
152 // Advance the clock to exactly that time.
153 base::TimeTicks now = clock_->NowTicks();
154 if (now < when) {
155 base::TimeDelta delta = when - now;
156 clock_->Advance(delta);
157 }
158
159 // Run whatever tasks are now eligible to run.
160 return RunTasks();
161 }
162
163 size_t size() const { return tasks_.size(); }
164
165 private:
166 ~TaskRunnerProxy() override {}
167
168 MockTaskRunner* mock_;
169 base::SimpleTestTickClock* clock_;
170
171 // A min-heap of outstanding tasks.
172 using Task = std::pair<base::TimeTicks, base::OnceClosure>;
173 std::vector<Task> tasks_;
174
175 DISALLOW_COPY_AND_ASSIGN(TaskRunnerProxy);
176 };
177
178 enum TestIndicies { 88 enum TestIndicies {
179 kSelected, 89 kSelected,
180 kAutoDiscardable, 90 kAutoDiscardable,
181 kPinned, 91 kPinned,
182 kApp, 92 kApp,
183 kPlayingAudio, 93 kPlayingAudio,
184 kFormEntry, 94 kFormEntry,
185 kRecent, 95 kRecent,
186 kOld, 96 kOld,
187 kReallyOld, 97 kReallyOld,
188 kOldButPinned, 98 kOldButPinned,
189 kInternalPage, 99 kInternalPage,
190 }; 100 };
101
191 } // namespace 102 } // namespace
192 103
193 class LenientTabManagerTest : public ChromeRenderViewHostTestHarness { 104 class TabManagerTest : public ChromeRenderViewHostTestHarness {
194 public: 105 public:
195 WebContents* CreateWebContents() { 106 WebContents* CreateWebContents() {
196 return WebContents::Create(WebContents::CreateParams(profile())); 107 return WebContents::Create(WebContents::CreateParams(profile()));
197 } 108 }
198
199 MOCK_METHOD2(NotifyRendererProcess,
200 void(const content::RenderProcessHost*,
201 base::MemoryPressureListener::MemoryPressureLevel));
202 }; 109 };
203 using TabManagerTest = testing::StrictMock<LenientTabManagerTest>;
204 110
205 // TODO(georgesak): Add tests for protection to tabs with form input and 111 // TODO(georgesak): Add tests for protection to tabs with form input and
206 // playing audio; 112 // playing audio;
207 113
208 // Tests the sorting comparator to make sure it's producing the desired order. 114 // Tests the sorting comparator to make sure it's producing the desired order.
209 TEST_F(TabManagerTest, Comparator) { 115 TEST_F(TabManagerTest, Comparator) {
210 TabStatsList test_list; 116 TabStatsList test_list;
211 const base::TimeTicks now = base::TimeTicks::Now(); 117 const base::TimeTicks now = base::TimeTicks::Now();
212 118
213 // Add kSelected last to verify that the array is being sorted. 119 // Add kSelected last to verify that the array is being sorted.
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 } 407 }
502 #else 408 #else
503 TEST_F(TabManagerTest, CanOnlyDiscardOnce) { 409 TEST_F(TabManagerTest, CanOnlyDiscardOnce) {
504 TabManager tab_manager; 410 TabManager tab_manager;
505 411
506 bool discard_once_value = tab_manager.CanOnlyDiscardOnce(); 412 bool discard_once_value = tab_manager.CanOnlyDiscardOnce();
507 EXPECT_FALSE(discard_once_value); 413 EXPECT_FALSE(discard_once_value);
508 } 414 }
509 #endif // defined(OS_WIN) || defined(OS_MACOSX) 415 #endif // defined(OS_WIN) || defined(OS_MACOSX)
510 416
511 namespace {
512
513 using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
514
515 // Function that always indicates the absence of memory pressure.
516 MemoryPressureLevel ReturnNoPressure() {
517 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
518 }
519
520 // Function that simply parrots back an externally specified memory pressure
521 // level.
522 MemoryPressureLevel ReturnSpecifiedPressure(
523 const MemoryPressureLevel* level) {
524 return *level;
525 }
526
527 } // namespace
528
529 // ChildProcessNotification is disabled on Chrome OS. crbug.com/588172.
530 #if defined(OS_CHROMEOS)
531 #define MAYBE_ChildProcessNotifications DISABLED_ChildProcessNotifications
532 #else
533 #define MAYBE_ChildProcessNotifications ChildProcessNotifications
534 #endif
535
536 // Ensure that memory pressure notifications are forwarded to child processes.
537 TEST_F(TabManagerTest, MAYBE_ChildProcessNotifications) {
538 TabManager tm;
539
540 // Set up the tab strip.
541 TabStripDummyDelegate delegate;
542 TabStripModel tabstrip(&delegate, profile());
543
544 // Create test clock, task runner.
545 base::SimpleTestTickClock test_clock;
546 MockTaskRunner mock_task_runner;
547 scoped_refptr<TaskRunnerProxy> task_runner(
548 new TaskRunnerProxy(&mock_task_runner, &test_clock));
549
550 // Configure the TabManager for testing.
551 tabstrip.AddObserver(&tm);
552 tm.test_tab_strip_models_.push_back(
553 TabManager::TestTabStripModel(&tabstrip, false /* !is_app */));
554 tm.test_tick_clock_ = &test_clock;
555 tm.task_runner_ = task_runner;
556 tm.notify_renderer_process_ = base::Bind(
557 &TabManagerTest::NotifyRendererProcess, base::Unretained(this));
558
559 // Create two dummy tabs.
560 auto* tab0 = CreateWebContents();
561 auto* tab1 = CreateWebContents();
562 auto* tab2 = CreateWebContents();
563 tabstrip.AppendWebContents(tab0, true); // Foreground tab.
564 tabstrip.AppendWebContents(tab1, false); // Background tab.
565 tabstrip.AppendWebContents(tab2, false); // Background tab.
566 const content::RenderProcessHost* renderer1 = tab1->GetRenderProcessHost();
567 const content::RenderProcessHost* renderer2 = tab2->GetRenderProcessHost();
568
569 // Make sure that tab2 has a lower priority than tab1 by its access time.
570 test_clock.Advance(base::TimeDelta::FromMilliseconds(1));
571 tab2->SetLastActiveTime(test_clock.NowTicks());
572 test_clock.Advance(base::TimeDelta::FromMilliseconds(1));
573 tab1->SetLastActiveTime(test_clock.NowTicks());
574
575 // Expect that the tab manager has not yet encountered memory pressure.
576 EXPECT_FALSE(tm.under_memory_pressure_);
577 EXPECT_EQ(0u, tm.notified_renderers_.size());
578
579 // Simulate a memory pressure situation that will immediately end. This should
580 // cause no notifications or scheduled tasks.
581 auto level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
582 tm.get_current_pressure_level_ = base::Bind(&ReturnNoPressure);
583 tm.OnMemoryPressure(level);
584 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
585 EXPECT_FALSE(tm.under_memory_pressure_);
586 EXPECT_EQ(0u, task_runner->size());
587 EXPECT_EQ(0u, tm.notified_renderers_.size());
588
589 // START OF MEMORY PRESSURE
590
591 // Simulate a memory pressure situation that persists. This should cause a
592 // task to be scheduled, and a background renderer to be notified.
593 tm.get_current_pressure_level_ = base::Bind(
594 &ReturnSpecifiedPressure, base::Unretained(&level));
595 EXPECT_CALL(mock_task_runner, PostDelayedTask(
596 testing::_,
597 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds)));
598 EXPECT_CALL(*this, NotifyRendererProcess(renderer2, level));
599 tm.OnMemoryPressure(level);
600 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
601 testing::Mock::VerifyAndClearExpectations(this);
602 EXPECT_TRUE(tm.under_memory_pressure_);
603 EXPECT_EQ(1u, task_runner->size());
604 EXPECT_EQ(1u, tm.notified_renderers_.size());
605 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer2));
606
607 // REPEATED MEMORY PRESSURE SIGNAL
608
609 // Simulate another memory pressure event. This should not cause any
610 // additional tasks to be scheduled, nor any further renderer notifications.
611 tm.OnMemoryPressure(level);
612 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
613 testing::Mock::VerifyAndClearExpectations(this);
614 EXPECT_TRUE(tm.under_memory_pressure_);
615 EXPECT_EQ(1u, task_runner->size());
616 EXPECT_EQ(1u, tm.notified_renderers_.size());
617
618 // FIRST SCHEDULED NOTIFICATION
619
620 // Run the scheduled task. This should cause another notification, but this
621 // time to the foreground tab. It should also cause another scheduled event.
622 EXPECT_CALL(mock_task_runner, PostDelayedTask(
623 testing::_,
624 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds)));
625 EXPECT_CALL(*this, NotifyRendererProcess(renderer1, level));
626 EXPECT_EQ(1u, task_runner->RunNextTask());
627 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
628 testing::Mock::VerifyAndClearExpectations(this);
629 EXPECT_TRUE(tm.under_memory_pressure_);
630 EXPECT_EQ(1u, task_runner->size());
631 EXPECT_EQ(2u, tm.notified_renderers_.size());
632 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer1));
633
634 // SECOND SCHEDULED NOTIFICATION
635
636 // Run the scheduled task. This should cause another notification, to the
637 // background tab. It should also cause another scheduled event.
638 EXPECT_CALL(mock_task_runner, PostDelayedTask(
639 testing::_,
640 base::TimeDelta::FromSeconds(tm.kRendererNotificationDelayInSeconds)));
641 EXPECT_CALL(*this, NotifyRendererProcess(renderer2, level));
642 EXPECT_EQ(1u, task_runner->RunNextTask());
643 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
644 testing::Mock::VerifyAndClearExpectations(this);
645 EXPECT_TRUE(tm.under_memory_pressure_);
646 EXPECT_EQ(1u, task_runner->size());
647 EXPECT_EQ(1u, tm.notified_renderers_.size());
648
649 // Expect that the background tab has been notified. The list of notified
650 // renderers maintained by the TabManager should also have been reset because
651 // the notification logic restarts after all renderers are notified.
652 EXPECT_EQ(1u, tm.notified_renderers_.count(renderer2));
653
654 // END OF MEMORY PRESSURE
655
656 // End the memory pressure condition and run the scheduled event. This should
657 // clean up the various state data. It should not schedule another event nor
658 // fire any notifications.
659 level = base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
660 EXPECT_EQ(1u, task_runner->RunNextTask());
661 testing::Mock::VerifyAndClearExpectations(&mock_task_runner);
662 testing::Mock::VerifyAndClearExpectations(this);
663 EXPECT_FALSE(tm.under_memory_pressure_);
664 EXPECT_EQ(0u, task_runner->size());
665 EXPECT_EQ(0u, tm.notified_renderers_.size());
666
667
668 // Clean up the tabstrip.
669 tabstrip.CloseAllTabs();
670 ASSERT_TRUE(tabstrip.empty());
671 }
672
673 TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) { 417 TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) {
674 TabManager tab_manager; 418 TabManager tab_manager;
675 base::TimeDelta time_to_purge = 419 base::TimeDelta time_to_purge =
676 tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge); 420 tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge);
677 EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30)); 421 EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30));
678 EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60)); 422 EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60));
679 } 423 }
680 424
681 TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) { 425 TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) {
682 TabManager tab_manager; 426 TabManager tab_manager;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
744 // Since tab2 is kept inactive and background for more than time-to-purge, 488 // Since tab2 is kept inactive and background for more than time-to-purge,
745 // tab2 should be purged. 489 // tab2 should be purged.
746 EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged()); 490 EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged());
747 491
748 // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED. 492 // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED.
749 tabstrip.ActivateTabAt(1, true /* user_gesture */); 493 tabstrip.ActivateTabAt(1, true /* user_gesture */);
750 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged()); 494 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged());
751 } 495 }
752 496
753 } // namespace memory 497 } // 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