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

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

Issue 2898033002: [TabManager] Move TabManager into chrome/browser/resource_coordinator. (Closed)
Patch Set: rebase Created 3 years, 6 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/memory/tab_manager.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/strings/string16.h"
16 #include "base/test/mock_entropy_provider.h"
17 #include "base/test/simple_test_tick_clock.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/memory/tab_manager_web_contents_data.h"
21 #include "chrome/browser/memory/tab_stats.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
25 #include "chrome/common/chrome_features.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/variations/variations_associated_data.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/test/mock_render_process_host.h"
33 #include "content/public/test/web_contents_tester.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/gurl.h"
36
37 using content::WebContents;
38 using content::WebContentsTester;
39
40 namespace memory {
41 namespace {
42
43 class TabStripDummyDelegate : public TestTabStripModelDelegate {
44 public:
45 TabStripDummyDelegate() {}
46
47 bool RunUnloadListenerBeforeClosing(WebContents* contents) override {
48 return false;
49 }
50
51 private:
52 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
53 };
54
55 class MockTabStripModelObserver : public TabStripModelObserver {
56 public:
57 MockTabStripModelObserver()
58 : nb_events_(0), old_contents_(nullptr), new_contents_(nullptr) {}
59
60 int NbEvents() const { return nb_events_; }
61 WebContents* OldContents() const { return old_contents_; }
62 WebContents* NewContents() const { return new_contents_; }
63
64 void Reset() {
65 nb_events_ = 0;
66 old_contents_ = nullptr;
67 new_contents_ = nullptr;
68 }
69
70 // TabStripModelObserver implementation:
71 void TabReplacedAt(TabStripModel* tab_strip_model,
72 WebContents* old_contents,
73 WebContents* new_contents,
74 int index) override {
75 nb_events_++;
76 old_contents_ = old_contents;
77 new_contents_ = new_contents;
78 }
79
80 private:
81 int nb_events_;
82 WebContents* old_contents_;
83 WebContents* new_contents_;
84
85 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
86 };
87
88 enum TestIndicies {
89 kSelected,
90 kAutoDiscardable,
91 kPinned,
92 kApp,
93 kPlayingAudio,
94 kFormEntry,
95 kRecent,
96 kOld,
97 kReallyOld,
98 kOldButPinned,
99 kInternalPage,
100 };
101
102 } // namespace
103
104 class TabManagerTest : public ChromeRenderViewHostTestHarness {
105 public:
106 WebContents* CreateWebContents() {
107 return WebContents::Create(WebContents::CreateParams(profile()));
108 }
109 };
110
111 // TODO(georgesak): Add tests for protection to tabs with form input and
112 // playing audio;
113
114 // Tests the sorting comparator to make sure it's producing the desired order.
115 TEST_F(TabManagerTest, Comparator) {
116 TabStatsList test_list;
117 const base::TimeTicks now = base::TimeTicks::Now();
118
119 // Add kSelected last to verify that the array is being sorted.
120
121 {
122 TabStats stats;
123 stats.last_active = now;
124 stats.is_pinned = true;
125 stats.child_process_host_id = kPinned;
126 test_list.push_back(stats);
127 }
128
129 {
130 TabStats stats;
131 stats.last_active = now;
132 stats.is_app = true;
133 stats.child_process_host_id = kApp;
134 test_list.push_back(stats);
135 }
136
137 {
138 TabStats stats;
139 stats.last_active = now;
140 stats.is_media = true;
141 stats.child_process_host_id = kPlayingAudio;
142 test_list.push_back(stats);
143 }
144
145 {
146 TabStats stats;
147 stats.last_active = now;
148 stats.has_form_entry = true;
149 stats.child_process_host_id = kFormEntry;
150 test_list.push_back(stats);
151 }
152
153 {
154 TabStats stats;
155 stats.last_active = now - base::TimeDelta::FromSeconds(10);
156 stats.child_process_host_id = kRecent;
157 test_list.push_back(stats);
158 }
159
160 {
161 TabStats stats;
162 stats.last_active = now - base::TimeDelta::FromMinutes(15);
163 stats.child_process_host_id = kOld;
164 test_list.push_back(stats);
165 }
166
167 {
168 TabStats stats;
169 stats.last_active = now - base::TimeDelta::FromDays(365);
170 stats.child_process_host_id = kReallyOld;
171 test_list.push_back(stats);
172 }
173
174 {
175 TabStats stats;
176 stats.is_pinned = true;
177 stats.last_active = now - base::TimeDelta::FromDays(365);
178 stats.child_process_host_id = kOldButPinned;
179 test_list.push_back(stats);
180 }
181
182 {
183 TabStats stats;
184 stats.last_active = now;
185 stats.is_internal_page = true;
186 stats.child_process_host_id = kInternalPage;
187 test_list.push_back(stats);
188 }
189
190 {
191 TabStats stats;
192 stats.last_active = now;
193 stats.is_auto_discardable = false;
194 stats.child_process_host_id = kAutoDiscardable;
195 test_list.push_back(stats);
196 }
197
198 // This entry sorts to the front, so by adding it last, it verifies that the
199 // array is being sorted.
200 {
201 TabStats stats;
202 stats.last_active = now;
203 stats.is_selected = true;
204 stats.child_process_host_id = kSelected;
205 test_list.push_back(stats);
206 }
207
208 std::sort(test_list.begin(), test_list.end(), TabManager::CompareTabStats);
209
210 int index = 0;
211 EXPECT_EQ(kSelected, test_list[index++].child_process_host_id);
212 EXPECT_EQ(kAutoDiscardable, test_list[index++].child_process_host_id);
213 EXPECT_EQ(kFormEntry, test_list[index++].child_process_host_id);
214 EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id);
215 EXPECT_EQ(kPinned, test_list[index++].child_process_host_id);
216 EXPECT_EQ(kOldButPinned, test_list[index++].child_process_host_id);
217 EXPECT_EQ(kApp, test_list[index++].child_process_host_id);
218 EXPECT_EQ(kRecent, test_list[index++].child_process_host_id);
219 EXPECT_EQ(kOld, test_list[index++].child_process_host_id);
220 EXPECT_EQ(kReallyOld, test_list[index++].child_process_host_id);
221 EXPECT_EQ(kInternalPage, test_list[index++].child_process_host_id);
222 }
223
224 TEST_F(TabManagerTest, IsInternalPage) {
225 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDownloadsURL)));
226 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIHistoryURL)));
227 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUINewTabURL)));
228 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUISettingsURL)));
229
230 // Debugging URLs are not included.
231 #if defined(OS_CHROMEOS)
232 EXPECT_FALSE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDiscardsURL)));
233 #endif
234 EXPECT_FALSE(
235 TabManager::IsInternalPage(GURL(chrome::kChromeUINetInternalsURL)));
236
237 // Prefix matches are included.
238 EXPECT_TRUE(
239 TabManager::IsInternalPage(GURL("chrome://settings/fakeSetting")));
240 }
241
242 // Ensures discarding tabs leaves TabStripModel in a good state.
243 TEST_F(TabManagerTest, DiscardWebContentsAt) {
244 TabManager tab_manager;
245
246 TabStripDummyDelegate delegate;
247 TabStripModel tabstrip(&delegate, profile());
248 tabstrip.AddObserver(&tab_manager);
249
250 // Fill it with some tabs.
251 WebContents* contents1 = CreateWebContents();
252 tabstrip.AppendWebContents(contents1, true);
253 WebContents* contents2 = CreateWebContents();
254 tabstrip.AppendWebContents(contents2, true);
255
256 // Start watching for events after the appends to avoid observing state
257 // transitions that aren't relevant to this test.
258 MockTabStripModelObserver tabstrip_observer;
259 tabstrip.AddObserver(&tabstrip_observer);
260
261 // Discard one of the tabs.
262 WebContents* null_contents1 = tab_manager.DiscardWebContentsAt(0, &tabstrip);
263 ASSERT_EQ(2, tabstrip.count());
264 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0)));
265 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
266 ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0));
267 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
268 ASSERT_EQ(1, tabstrip_observer.NbEvents());
269 EXPECT_EQ(contents1, tabstrip_observer.OldContents());
270 EXPECT_EQ(null_contents1, tabstrip_observer.NewContents());
271 tabstrip_observer.Reset();
272
273 // Discard the same tab again, after resetting its discard state.
274 tab_manager.GetWebContentsData(tabstrip.GetWebContentsAt(0))
275 ->SetDiscardState(false);
276 WebContents* null_contents2 = tab_manager.DiscardWebContentsAt(0, &tabstrip);
277 ASSERT_EQ(2, tabstrip.count());
278 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0)));
279 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
280 ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0));
281 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
282 ASSERT_EQ(1, tabstrip_observer.NbEvents());
283 EXPECT_EQ(null_contents1, tabstrip_observer.OldContents());
284 EXPECT_EQ(null_contents2, tabstrip_observer.NewContents());
285
286 // Activating the tab should clear its discard state.
287 tabstrip.ActivateTabAt(0, true /* user_gesture */);
288 ASSERT_EQ(2, tabstrip.count());
289 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0)));
290 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
291
292 // Don't discard active tab.
293 tab_manager.DiscardWebContentsAt(0, &tabstrip);
294 ASSERT_EQ(2, tabstrip.count());
295 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0)));
296 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
297
298 tabstrip.CloseAllTabs();
299 EXPECT_TRUE(tabstrip.empty());
300 }
301
302 // Makes sure that reloading a discarded tab without activating it unmarks the
303 // tab as discarded so it won't reload on activation.
304 TEST_F(TabManagerTest, ReloadDiscardedTabContextMenu) {
305 // Note that we do not add |tab_manager| as an observer to |tabstrip| here as
306 // the event we are trying to test for is not related to the tab strip, but
307 // the web content instead and therefore should be handled by WebContentsData
308 // (which observes the web content).
309 TabManager tab_manager;
310 TabStripDummyDelegate delegate;
311 TabStripModel tabstrip(&delegate, profile());
312
313 // Create 2 tabs because the active tab cannot be discarded.
314 tabstrip.AppendWebContents(CreateWebContents(), true);
315 content::WebContents* test_contents =
316 WebContentsTester::CreateTestWebContents(browser_context(), nullptr);
317 tabstrip.AppendWebContents(test_contents, false); // Opened in background.
318
319 // Navigate to a web page. This is necessary to set a current entry in memory
320 // so the reload can happen.
321 WebContentsTester::For(test_contents)
322 ->NavigateAndCommit(GURL("chrome://newtab"));
323 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
324
325 tab_manager.DiscardWebContentsAt(1, &tabstrip);
326 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
327
328 tabstrip.GetWebContentsAt(1)->GetController().Reload(
329 content::ReloadType::NORMAL, false);
330 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1)));
331 tabstrip.CloseAllTabs();
332 EXPECT_TRUE(tabstrip.empty());
333 }
334
335 // Makes sure that the last active time property is saved even though the tab is
336 // discarded.
337 TEST_F(TabManagerTest, DiscardedTabKeepsLastActiveTime) {
338 TabManager tab_manager;
339 TabStripDummyDelegate delegate;
340 TabStripModel tabstrip(&delegate, profile());
341 tabstrip.AddObserver(&tab_manager);
342
343 tabstrip.AppendWebContents(CreateWebContents(), true);
344 WebContents* test_contents = CreateWebContents();
345 tabstrip.AppendWebContents(test_contents, false);
346
347 // Simulate an old inactive tab about to get discarded.
348 base::TimeTicks new_last_active_time =
349 base::TimeTicks::Now() - base::TimeDelta::FromMinutes(35);
350 test_contents->SetLastActiveTime(new_last_active_time);
351 EXPECT_EQ(new_last_active_time, test_contents->GetLastActiveTime());
352
353 WebContents* null_contents = tab_manager.DiscardWebContentsAt(1, &tabstrip);
354 EXPECT_EQ(new_last_active_time, null_contents->GetLastActiveTime());
355
356 tabstrip.CloseAllTabs();
357 EXPECT_TRUE(tabstrip.empty());
358 }
359
360 // Test to see if a tab can only be discarded once. On Windows and Mac, this
361 // defaults to true unless overridden through a variation parameter. On other
362 // platforms, it's always false.
363 #if defined(OS_WIN) || defined(OS_MACOSX)
364 TEST_F(TabManagerTest, CanOnlyDiscardOnce) {
365 TabManager tab_manager;
366 const std::string kTrialName = features::kAutomaticTabDiscarding.name;
367
368 // Not setting the variation parameter.
369 {
370 bool discard_once_value = tab_manager.CanOnlyDiscardOnce();
371 EXPECT_TRUE(discard_once_value);
372 }
373
374 // Setting the variation parameter to true.
375 {
376 std::unique_ptr<base::FieldTrialList> field_trial_list_;
377 field_trial_list_.reset(
378 new base::FieldTrialList(
379 base::MakeUnique<base::MockEntropyProvider>()));
380 variations::testing::ClearAllVariationParams();
381
382 std::map<std::string, std::string> params;
383 params["AllowMultipleDiscards"] = "true";
384 ASSERT_TRUE(variations::AssociateVariationParams(kTrialName, "A", params));
385 base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
386
387 bool discard_once_value = tab_manager.CanOnlyDiscardOnce();
388 EXPECT_FALSE(discard_once_value);
389 }
390
391 // Setting the variation parameter to something else.
392 {
393 std::unique_ptr<base::FieldTrialList> field_trial_list_;
394 field_trial_list_.reset(
395 new base::FieldTrialList(
396 base::MakeUnique<base::MockEntropyProvider>()));
397 variations::testing::ClearAllVariationParams();
398
399 std::map<std::string, std::string> params;
400 params["AllowMultipleDiscards"] = "somethingElse";
401 ASSERT_TRUE(variations::AssociateVariationParams(kTrialName, "B", params));
402 base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
403
404 bool discard_once_value = tab_manager.CanOnlyDiscardOnce();
405 EXPECT_TRUE(discard_once_value);
406 }
407 }
408 #else
409 TEST_F(TabManagerTest, CanOnlyDiscardOnce) {
410 TabManager tab_manager;
411
412 bool discard_once_value = tab_manager.CanOnlyDiscardOnce();
413 EXPECT_FALSE(discard_once_value);
414 }
415 #endif // defined(OS_WIN) || defined(OS_MACOSX)
416
417 TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) {
418 TabManager tab_manager;
419 base::TimeDelta time_to_purge =
420 tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge);
421 EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30));
422 EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60));
423 }
424
425 TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) {
426 TabManager tab_manager;
427 TabStripDummyDelegate delegate;
428 TabStripModel tabstrip(&delegate, profile());
429 tabstrip.AddObserver(&tab_manager);
430
431 WebContents* test_contents = CreateWebContents();
432 tabstrip.AppendWebContents(test_contents, false);
433
434 base::SimpleTestTickClock test_clock;
435 tab_manager.set_test_tick_clock(&test_clock);
436
437 tab_manager.GetWebContentsData(test_contents)->set_is_purged(false);
438 tab_manager.GetWebContentsData(test_contents)
439 ->SetLastInactiveTime(test_clock.NowTicks());
440 tab_manager.GetWebContentsData(test_contents)
441 ->set_time_to_purge(base::TimeDelta::FromMinutes(1));
442
443 // Wait 1 minute and verify that the tab is still not to be purged.
444 test_clock.Advance(base::TimeDelta::FromMinutes(1));
445 EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents));
446
447 // Wait another 1 second and verify that it should be purged now .
448 test_clock.Advance(base::TimeDelta::FromSeconds(1));
449 EXPECT_TRUE(tab_manager.ShouldPurgeNow(test_contents));
450
451 tab_manager.GetWebContentsData(test_contents)->set_is_purged(true);
452 tab_manager.GetWebContentsData(test_contents)
453 ->SetLastInactiveTime(test_clock.NowTicks());
454
455 // Wait 1 day and verify that the tab is still be purged.
456 test_clock.Advance(base::TimeDelta::FromHours(24));
457 EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents));
458 }
459
460 TEST_F(TabManagerTest, ActivateTabResetPurgeState) {
461 TabManager tab_manager;
462 TabStripDummyDelegate delegate;
463 TabStripModel tabstrip(&delegate, profile());
464 tabstrip.AddObserver(&tab_manager);
465 tab_manager.test_tab_strip_models_.push_back(
466 TabManager::TestTabStripModel(&tabstrip, false /* !is_app */));
467
468 base::SimpleTestTickClock test_clock;
469 tab_manager.set_test_tick_clock(&test_clock);
470
471 WebContents* tab1 = CreateWebContents();
472 WebContents* tab2 = CreateWebContents();
473 tabstrip.AppendWebContents(tab1, true);
474 tabstrip.AppendWebContents(tab2, false);
475
476 tab_manager.GetWebContentsData(tab2)->SetLastInactiveTime(
477 test_clock.NowTicks());
478 static_cast<content::MockRenderProcessHost*>(tab2->GetRenderProcessHost())
479 ->set_is_process_backgrounded(true);
480 EXPECT_TRUE(tab2->GetRenderProcessHost()->IsProcessBackgrounded());
481
482 // Initially PurgeAndSuspend state should be NOT_PURGED.
483 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged());
484 tab_manager.GetWebContentsData(tab2)->set_time_to_purge(
485 base::TimeDelta::FromMinutes(1));
486 test_clock.Advance(base::TimeDelta::FromMinutes(2));
487 tab_manager.PurgeBackgroundedTabsIfNeeded();
488 // Since tab2 is kept inactive and background for more than time-to-purge,
489 // tab2 should be purged.
490 EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged());
491
492 // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED.
493 tabstrip.ActivateTabAt(1, true /* user_gesture */);
494 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged());
495 }
496
497 } // namespace memory
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698