persistent_tab_restore_service_browsertest.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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/sessions/persistent_tab_restore_service.h"
6
7#include "base/compiler_specific.h"
8#include "base/run_loop.h"
9#include "base/strings/stringprintf.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "chrome/browser/chrome_notification_types.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/sessions/session_service.h"
15#include "chrome/browser/sessions/session_service_factory.h"
16#include "chrome/browser/sessions/session_types.h"
17#include "chrome/browser/sessions/tab_restore_service_factory.h"
18#include "chrome/browser/sessions/tab_restore_service_observer.h"
19#include "chrome/browser/ui/browser_window.h"
20#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_test.h"
23#include "chrome/test/base/in_process_browser_test.h"
24#include "chrome/test/base/testing_profile.h"
25#include "components/sessions/serialized_navigation_entry_test_helper.h"
26#include "content/public/browser/browser_thread.h"
27#include "content/public/browser/navigation_controller.h"
28#include "content/public/browser/navigation_entry.h"
29#include "content/public/browser/notification_service.h"
30#include "content/public/browser/notification_types.h"
31#include "content/public/browser/web_contents.h"
32#include "content/public/test/render_view_test.h"
33#include "content/public/test/test_utils.h"
34#include "content/public/test/web_contents_tester.h"
35#include "testing/gtest/include/gtest/gtest.h"
36
37typedef TabRestoreService::Tab Tab;
38typedef TabRestoreService::Window Window;
39
40using content::NavigationEntry;
41using content::WebContentsTester;
42using sessions::SerializedNavigationEntry;
43using sessions::SerializedNavigationEntryTestHelper;
44
45// Create subclass that overrides TimeNow so that we can control the time used
46// for closed tabs and windows.
47class PersistentTabRestoreTimeFactory : public TabRestoreService::TimeFactory {
48 public:
49  PersistentTabRestoreTimeFactory() : time_(base::Time::Now()) {}
50
51  virtual ~PersistentTabRestoreTimeFactory() {}
52
53  virtual base::Time TimeNow() OVERRIDE {
54    return time_;
55  }
56
57 private:
58  base::Time time_;
59};
60
61class PersistentTabRestoreServiceTest : public ChromeRenderViewHostTestHarness {
62 public:
63  PersistentTabRestoreServiceTest()
64    : url1_("http://1"),
65      url2_("http://2"),
66      url3_("http://3"),
67      user_agent_override_(
68          "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19"
69          " (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19") {
70  }
71
72  virtual ~PersistentTabRestoreServiceTest() {
73  }
74
75 protected:
76  enum {
77    kMaxEntries = TabRestoreServiceHelper::kMaxEntries,
78  };
79
80  // testing::Test:
81  virtual void SetUp() OVERRIDE {
82    ChromeRenderViewHostTestHarness::SetUp();
83    time_factory_ = new PersistentTabRestoreTimeFactory();
84    service_.reset(new PersistentTabRestoreService(profile(), time_factory_));
85  }
86
87  virtual void TearDown() OVERRIDE {
88    service_->Shutdown();
89    service_.reset();
90    delete time_factory_;
91    ChromeRenderViewHostTestHarness::TearDown();
92  }
93
94  TabRestoreService::Entries* mutable_entries() {
95    return service_->mutable_entries();
96  }
97
98  void PruneEntries() {
99    service_->PruneEntries();
100  }
101
102  void AddThreeNavigations() {
103    // Navigate to three URLs.
104    NavigateAndCommit(url1_);
105    NavigateAndCommit(url2_);
106    NavigateAndCommit(url3_);
107  }
108
109  void NavigateToIndex(int index) {
110    // Navigate back. We have to do this song and dance as NavigationController
111    // isn't happy if you navigate immediately while going back.
112    controller().GoToIndex(index);
113    WebContentsTester::For(web_contents())->CommitPendingNavigation();
114  }
115
116  void RecreateService() {
117    // Must set service to null first so that it is destroyed before the new
118    // one is created.
119    service_->Shutdown();
120    content::BrowserThread::GetBlockingPool()->FlushForTesting();
121    service_.reset();
122    service_.reset(new PersistentTabRestoreService(profile(), time_factory_));
123    SynchronousLoadTabsFromLastSession();
124  }
125
126  // Adds a window with one tab and url to the profile's session service.
127  // If |pinned| is true, the tab is marked as pinned in the session service.
128  void AddWindowWithOneTabToSessionService(bool pinned) {
129    SessionService* session_service =
130        SessionServiceFactory::GetForProfile(profile());
131    SessionID tab_id;
132    SessionID window_id;
133    session_service->SetWindowType(
134        window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
135    session_service->SetTabWindow(window_id, tab_id);
136    session_service->SetTabIndexInWindow(window_id, tab_id, 0);
137    session_service->SetSelectedTabInWindow(window_id, 0);
138    if (pinned)
139      session_service->SetPinnedState(window_id, tab_id, true);
140    session_service->UpdateTabNavigation(
141        window_id, tab_id,
142        SerializedNavigationEntryTestHelper::CreateNavigation(
143            url1_.spec(), "title"));
144  }
145
146  // Creates a SessionService and assigns it to the Profile. The SessionService
147  // is configured with a single window with a single tab pointing at url1_ by
148  // way of AddWindowWithOneTabToSessionService. If |pinned| is true, the
149  // tab is marked as pinned in the session service.
150  void CreateSessionServiceWithOneWindow(bool pinned) {
151    // The profile takes ownership of this.
152    SessionService* session_service = new SessionService(profile());
153    SessionServiceFactory::SetForTestProfile(profile(), session_service);
154
155    AddWindowWithOneTabToSessionService(pinned);
156
157    // Set this, otherwise previous session won't be loaded.
158    profile()->set_last_session_exited_cleanly(false);
159  }
160
161  void SynchronousLoadTabsFromLastSession() {
162    // Ensures that the load is complete before continuing.
163    service_->LoadTabsFromLastSession();
164    content::BrowserThread::GetBlockingPool()->FlushForTesting();
165    base::RunLoop().RunUntilIdle();
166    content::BrowserThread::GetBlockingPool()->FlushForTesting();
167  }
168
169  GURL url1_;
170  GURL url2_;
171  GURL url3_;
172  std::string user_agent_override_;
173  scoped_ptr<PersistentTabRestoreService> service_;
174  PersistentTabRestoreTimeFactory* time_factory_;
175};
176
177namespace {
178
179class TestTabRestoreServiceObserver : public TabRestoreServiceObserver {
180 public:
181  TestTabRestoreServiceObserver() : got_loaded_(false) {}
182
183  void clear_got_loaded() { got_loaded_ = false; }
184  bool got_loaded() const { return got_loaded_; }
185
186  // TabRestoreServiceObserver:
187  virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE {
188  }
189  virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE {
190  }
191  virtual void TabRestoreServiceLoaded(TabRestoreService* service) OVERRIDE {
192    got_loaded_ = true;
193  }
194
195 private:
196  // Was TabRestoreServiceLoaded() invoked?
197  bool got_loaded_;
198
199  DISALLOW_COPY_AND_ASSIGN(TestTabRestoreServiceObserver);
200};
201
202}  // namespace
203
204TEST_F(PersistentTabRestoreServiceTest, Basic) {
205  AddThreeNavigations();
206
207  // Have the service record the tab.
208  service_->CreateHistoricalTab(web_contents(), -1);
209
210  // Make sure an entry was created.
211  ASSERT_EQ(1U, service_->entries().size());
212
213  // Make sure the entry matches.
214  TabRestoreService::Entry* entry = service_->entries().front();
215  ASSERT_EQ(TabRestoreService::TAB, entry->type);
216  Tab* tab = static_cast<Tab*>(entry);
217  EXPECT_FALSE(tab->pinned);
218  EXPECT_TRUE(tab->extension_app_id.empty());
219  ASSERT_EQ(3U, tab->navigations.size());
220  EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
221  EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
222  EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
223  EXPECT_EQ("", tab->user_agent_override);
224  EXPECT_EQ(2, tab->current_navigation_index);
225  EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
226            tab->timestamp.ToInternalValue());
227
228  NavigateToIndex(1);
229
230  // And check again, but set the user agent override this time.
231  web_contents()->SetUserAgentOverride(user_agent_override_);
232  service_->CreateHistoricalTab(web_contents(), -1);
233
234  // There should be two entries now.
235  ASSERT_EQ(2U, service_->entries().size());
236
237  // Make sure the entry matches.
238  entry = service_->entries().front();
239  ASSERT_EQ(TabRestoreService::TAB, entry->type);
240  tab = static_cast<Tab*>(entry);
241  EXPECT_FALSE(tab->pinned);
242  ASSERT_EQ(3U, tab->navigations.size());
243  EXPECT_EQ(url1_, tab->navigations[0].virtual_url());
244  EXPECT_EQ(url2_, tab->navigations[1].virtual_url());
245  EXPECT_EQ(url3_, tab->navigations[2].virtual_url());
246  EXPECT_EQ(user_agent_override_, tab->user_agent_override);
247  EXPECT_EQ(1, tab->current_navigation_index);
248  EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
249            tab->timestamp.ToInternalValue());
250}
251
252// Make sure TabRestoreService doesn't create an entry for a tab with no
253// navigations.
254TEST_F(PersistentTabRestoreServiceTest, DontCreateEmptyTab) {
255  service_->CreateHistoricalTab(web_contents(), -1);
256  EXPECT_TRUE(service_->entries().empty());
257}
258
259// Tests restoring a single tab.
260TEST_F(PersistentTabRestoreServiceTest, Restore) {
261  AddThreeNavigations();
262
263  // Have the service record the tab.
264  service_->CreateHistoricalTab(web_contents(), -1);
265
266  // Recreate the service and have it load the tabs.
267  RecreateService();
268
269  // One entry should be created.
270  ASSERT_EQ(1U, service_->entries().size());
271
272  // And verify the entry.
273  PersistentTabRestoreService::Entry* entry = service_->entries().front();
274  ASSERT_EQ(TabRestoreService::TAB, entry->type);
275  Tab* tab = static_cast<Tab*>(entry);
276  EXPECT_FALSE(tab->pinned);
277  ASSERT_EQ(3U, tab->navigations.size());
278  EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
279  EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
280  EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
281  EXPECT_EQ(2, tab->current_navigation_index);
282  EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
283            tab->timestamp.ToInternalValue());
284}
285
286// Tests restoring a single pinned tab.
287TEST_F(PersistentTabRestoreServiceTest, RestorePinnedAndApp) {
288  AddThreeNavigations();
289
290  // Have the service record the tab.
291  service_->CreateHistoricalTab(web_contents(), -1);
292
293  // One entry should be created.
294  ASSERT_EQ(1U, service_->entries().size());
295
296  // We have to explicitly mark the tab as pinned as there is no browser for
297  // these tests.
298  TabRestoreService::Entry* entry = service_->entries().front();
299  ASSERT_EQ(TabRestoreService::TAB, entry->type);
300  Tab* tab = static_cast<Tab*>(entry);
301  tab->pinned = true;
302  const std::string extension_app_id("test");
303  tab->extension_app_id = extension_app_id;
304
305  // Recreate the service and have it load the tabs.
306  RecreateService();
307
308  // One entry should be created.
309  ASSERT_EQ(1U, service_->entries().size());
310
311  // And verify the entry.
312  entry = service_->entries().front();
313  ASSERT_EQ(TabRestoreService::TAB, entry->type);
314  tab = static_cast<Tab*>(entry);
315  EXPECT_TRUE(tab->pinned);
316  ASSERT_EQ(3U, tab->navigations.size());
317  EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
318  EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
319  EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
320  EXPECT_EQ(2, tab->current_navigation_index);
321  EXPECT_TRUE(extension_app_id == tab->extension_app_id);
322}
323
324// We only restore apps on chromeos.
325#if defined(USE_AURA)
326
327typedef InProcessBrowserTest PersistentTabRestoreServiceBrowserTest;
328
329IN_PROC_BROWSER_TEST_F(PersistentTabRestoreServiceBrowserTest, RestoreApp) {
330  Profile* profile = browser()->profile();
331  TabRestoreService* trs = TabRestoreServiceFactory::GetForProfile(profile);
332  const char* app_name = "TestApp";
333
334  Browser* app_browser = CreateBrowserForApp(app_name, profile);
335  app_browser->window()->Close();
336  content::WindowedNotificationObserver observer(
337      chrome::NOTIFICATION_BROWSER_CLOSED,
338      content::Source<Browser>(app_browser));
339  observer.Wait();
340
341  // One entry should be created.
342  ASSERT_EQ(1U, trs->entries().size());
343  const TabRestoreService::Entry* restored_entry = trs->entries().front();
344
345  // It should be a window with an app.
346  ASSERT_EQ(TabRestoreService::WINDOW, restored_entry->type);
347  const Window* restored_window =
348      static_cast<const Window*>(restored_entry);
349  EXPECT_EQ(app_name, restored_window->app_name);
350}
351#endif  // defined(USE_AURA)
352
353// Make sure we persist entries to disk that have post data.
354TEST_F(PersistentTabRestoreServiceTest, DontPersistPostData) {
355  AddThreeNavigations();
356  controller().GetEntryAtIndex(0)->SetHasPostData(true);
357  controller().GetEntryAtIndex(1)->SetHasPostData(true);
358  controller().GetEntryAtIndex(2)->SetHasPostData(true);
359
360  // Have the service record the tab.
361  service_->CreateHistoricalTab(web_contents(), -1);
362  ASSERT_EQ(1U, service_->entries().size());
363
364  // Recreate the service and have it load the tabs.
365  RecreateService();
366
367  // One entry should be created.
368  ASSERT_EQ(1U, service_->entries().size());
369
370  const TabRestoreService::Entry* restored_entry = service_->entries().front();
371  ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
372
373  const Tab* restored_tab =
374      static_cast<const Tab*>(restored_entry);
375  // There should be 3 navs.
376  ASSERT_EQ(3U, restored_tab->navigations.size());
377  EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
378            restored_tab->timestamp.ToInternalValue());
379}
380
381// Make sure we don't persist entries to disk that have post data. This
382// differs from DontPersistPostData1 in that all the navigations have post
383// data, so that nothing should be persisted.
384TEST_F(PersistentTabRestoreServiceTest, DontLoadTwice) {
385  AddThreeNavigations();
386
387  // Have the service record the tab.
388  service_->CreateHistoricalTab(web_contents(), -1);
389  ASSERT_EQ(1U, service_->entries().size());
390
391  // Recreate the service and have it load the tabs.
392  RecreateService();
393
394  SynchronousLoadTabsFromLastSession();
395
396  // There should only be one entry.
397  ASSERT_EQ(1U, service_->entries().size());
398}
399
400// Makes sure we load the previous session as necessary.
401TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSession) {
402  CreateSessionServiceWithOneWindow(false);
403
404  SessionServiceFactory::GetForProfile(profile())->
405      MoveCurrentSessionToLastSession();
406
407  EXPECT_FALSE(service_->IsLoaded());
408
409  TestTabRestoreServiceObserver observer;
410  service_->AddObserver(&observer);
411  SynchronousLoadTabsFromLastSession();
412  EXPECT_TRUE(observer.got_loaded());
413  service_->RemoveObserver(&observer);
414
415  // Make sure we get back one entry with one tab whose url is url1.
416  ASSERT_EQ(1U, service_->entries().size());
417  TabRestoreService::Entry* entry2 = service_->entries().front();
418  ASSERT_EQ(TabRestoreService::WINDOW, entry2->type);
419  TabRestoreService::Window* window =
420      static_cast<TabRestoreService::Window*>(entry2);
421  ASSERT_EQ(1U, window->tabs.size());
422  EXPECT_EQ(0, window->timestamp.ToInternalValue());
423  EXPECT_EQ(0, window->selected_tab_index);
424  ASSERT_EQ(1U, window->tabs[0].navigations.size());
425  EXPECT_EQ(0, window->tabs[0].current_navigation_index);
426  EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
427  EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
428}
429
430// Makes sure we don't attempt to load previous sessions after a restore.
431TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterRestore) {
432  CreateSessionServiceWithOneWindow(false);
433
434  SessionServiceFactory::GetForProfile(profile())->
435      MoveCurrentSessionToLastSession();
436
437  profile()->set_restored_last_session(true);
438
439  SynchronousLoadTabsFromLastSession();
440
441  // Because we restored a session PersistentTabRestoreService shouldn't load
442  // the tabs.
443  ASSERT_EQ(0U, service_->entries().size());
444}
445
446// Makes sure we don't attempt to load previous sessions after a clean exit.
447TEST_F(PersistentTabRestoreServiceTest, DontLoadAfterCleanExit) {
448  CreateSessionServiceWithOneWindow(false);
449
450  SessionServiceFactory::GetForProfile(profile())->
451      MoveCurrentSessionToLastSession();
452
453  profile()->set_last_session_exited_cleanly(true);
454
455  SynchronousLoadTabsFromLastSession();
456
457  ASSERT_EQ(0U, service_->entries().size());
458}
459
460TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabs) {
461  CreateSessionServiceWithOneWindow(false);
462
463  SessionServiceFactory::GetForProfile(profile())->
464      MoveCurrentSessionToLastSession();
465
466  AddThreeNavigations();
467
468  service_->CreateHistoricalTab(web_contents(), -1);
469
470  RecreateService();
471
472  // We should get back two entries, one from the previous session and one from
473  // the tab restore service. The previous session entry should be first.
474  ASSERT_EQ(2U, service_->entries().size());
475  // The first entry should come from the session service.
476  TabRestoreService::Entry* entry = service_->entries().front();
477  ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
478  TabRestoreService::Window* window =
479      static_cast<TabRestoreService::Window*>(entry);
480  ASSERT_EQ(1U, window->tabs.size());
481  EXPECT_EQ(0, window->selected_tab_index);
482  EXPECT_EQ(0, window->timestamp.ToInternalValue());
483  ASSERT_EQ(1U, window->tabs[0].navigations.size());
484  EXPECT_EQ(0, window->tabs[0].current_navigation_index);
485  EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
486  EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
487
488  // Then the closed tab.
489  entry = *(++service_->entries().begin());
490  ASSERT_EQ(TabRestoreService::TAB, entry->type);
491  Tab* tab = static_cast<Tab*>(entry);
492  ASSERT_FALSE(tab->pinned);
493  ASSERT_EQ(3U, tab->navigations.size());
494  EXPECT_EQ(2, tab->current_navigation_index);
495  EXPECT_EQ(time_factory_->TimeNow().ToInternalValue(),
496            tab->timestamp.ToInternalValue());
497  EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
498  EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
499  EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
500}
501
502// Make sure pinned state is correctly loaded from session service.
503TEST_F(PersistentTabRestoreServiceTest, LoadPreviousSessionAndTabsPinned) {
504  CreateSessionServiceWithOneWindow(true);
505
506  SessionServiceFactory::GetForProfile(profile())->
507      MoveCurrentSessionToLastSession();
508
509  AddThreeNavigations();
510
511  service_->CreateHistoricalTab(web_contents(), -1);
512
513  RecreateService();
514
515  // We should get back two entries, one from the previous session and one from
516  // the tab restore service. The previous session entry should be first.
517  ASSERT_EQ(2U, service_->entries().size());
518  // The first entry should come from the session service.
519  TabRestoreService::Entry* entry = service_->entries().front();
520  ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
521  TabRestoreService::Window* window =
522      static_cast<TabRestoreService::Window*>(entry);
523  ASSERT_EQ(1U, window->tabs.size());
524  EXPECT_EQ(0, window->selected_tab_index);
525  EXPECT_TRUE(window->tabs[0].pinned);
526  ASSERT_EQ(1U, window->tabs[0].navigations.size());
527  EXPECT_EQ(0, window->tabs[0].current_navigation_index);
528  EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
529
530  // Then the closed tab.
531  entry = *(++service_->entries().begin());
532  ASSERT_EQ(TabRestoreService::TAB, entry->type);
533  Tab* tab = static_cast<Tab*>(entry);
534  ASSERT_FALSE(tab->pinned);
535  ASSERT_EQ(3U, tab->navigations.size());
536  EXPECT_EQ(2, tab->current_navigation_index);
537  EXPECT_TRUE(url1_ == tab->navigations[0].virtual_url());
538  EXPECT_TRUE(url2_ == tab->navigations[1].virtual_url());
539  EXPECT_TRUE(url3_ == tab->navigations[2].virtual_url());
540}
541
542// Creates kMaxEntries + 1 windows in the session service and makes sure we only
543// get back kMaxEntries on restore.
544TEST_F(PersistentTabRestoreServiceTest, ManyWindowsInSessionService) {
545  CreateSessionServiceWithOneWindow(false);
546
547  for (size_t i = 0; i < kMaxEntries; ++i)
548    AddWindowWithOneTabToSessionService(false);
549
550  SessionServiceFactory::GetForProfile(profile())->
551      MoveCurrentSessionToLastSession();
552
553  AddThreeNavigations();
554
555  service_->CreateHistoricalTab(web_contents(), -1);
556
557  RecreateService();
558
559  // We should get back kMaxEntries entries. We added more, but
560  // TabRestoreService only allows up to kMaxEntries.
561  ASSERT_EQ(kMaxEntries, service_->entries().size());
562
563  // The first entry should come from the session service.
564  TabRestoreService::Entry* entry = service_->entries().front();
565  ASSERT_EQ(TabRestoreService::WINDOW, entry->type);
566  TabRestoreService::Window* window =
567      static_cast<TabRestoreService::Window*>(entry);
568  ASSERT_EQ(1U, window->tabs.size());
569  EXPECT_EQ(0, window->selected_tab_index);
570  EXPECT_EQ(0, window->timestamp.ToInternalValue());
571  ASSERT_EQ(1U, window->tabs[0].navigations.size());
572  EXPECT_EQ(0, window->tabs[0].current_navigation_index);
573  EXPECT_EQ(0, window->tabs[0].timestamp.ToInternalValue());
574  EXPECT_TRUE(url1_ == window->tabs[0].navigations[0].virtual_url());
575}
576
577// Makes sure we restore timestamps correctly.
578TEST_F(PersistentTabRestoreServiceTest, TimestampSurvivesRestore) {
579  base::Time tab_timestamp(base::Time::FromInternalValue(123456789));
580
581  AddThreeNavigations();
582
583  // Have the service record the tab.
584  service_->CreateHistoricalTab(web_contents(), -1);
585
586  // Make sure an entry was created.
587  ASSERT_EQ(1U, service_->entries().size());
588
589  // Make sure the entry matches.
590  std::vector<SerializedNavigationEntry> old_navigations;
591  {
592    // |entry|/|tab| doesn't survive after RecreateService().
593    TabRestoreService::Entry* entry = service_->entries().front();
594    ASSERT_EQ(TabRestoreService::TAB, entry->type);
595    Tab* tab = static_cast<Tab*>(entry);
596    tab->timestamp = tab_timestamp;
597    old_navigations = tab->navigations;
598  }
599
600  EXPECT_EQ(3U, old_navigations.size());
601  for (size_t i = 0; i < old_navigations.size(); ++i) {
602    EXPECT_FALSE(old_navigations[i].timestamp().is_null());
603  }
604
605  // Set this, otherwise previous session won't be loaded.
606  profile()->set_last_session_exited_cleanly(false);
607
608  RecreateService();
609
610  // One entry should be created.
611  ASSERT_EQ(1U, service_->entries().size());
612
613  // And verify the entry.
614  TabRestoreService::Entry* restored_entry = service_->entries().front();
615  ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
616  Tab* restored_tab =
617      static_cast<Tab*>(restored_entry);
618  EXPECT_EQ(tab_timestamp.ToInternalValue(),
619            restored_tab->timestamp.ToInternalValue());
620  ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size());
621  for (size_t i = 0; i < restored_tab->navigations.size(); ++i) {
622    EXPECT_EQ(old_navigations[i].timestamp(),
623              restored_tab->navigations[i].timestamp());
624  }
625}
626
627TEST_F(PersistentTabRestoreServiceTest, PruneEntries) {
628  service_->ClearEntries();
629  ASSERT_TRUE(service_->entries().empty());
630
631  const size_t max_entries = kMaxEntries;
632  for (size_t i = 0; i < max_entries + 5; i++) {
633    SerializedNavigationEntry navigation =
634        SerializedNavigationEntryTestHelper::CreateNavigation(
635            base::StringPrintf("http://%d", static_cast<int>(i)),
636            base::StringPrintf("%d", static_cast<int>(i)));
637
638    Tab* tab = new Tab();
639    tab->navigations.push_back(navigation);
640    tab->current_navigation_index = 0;
641
642    mutable_entries()->push_back(tab);
643  }
644
645  // Only keep kMaxEntries around.
646  EXPECT_EQ(max_entries + 5, service_->entries().size());
647  PruneEntries();
648  EXPECT_EQ(max_entries, service_->entries().size());
649  // Pruning again does nothing.
650  PruneEntries();
651  EXPECT_EQ(max_entries, service_->entries().size());
652
653  // Prune older first.
654  const char kRecentUrl[] = "http://recent";
655  SerializedNavigationEntry navigation =
656      SerializedNavigationEntryTestHelper::CreateNavigation(kRecentUrl,
657                                                            "Most recent");
658  Tab* tab = new Tab();
659  tab->navigations.push_back(navigation);
660  tab->current_navigation_index = 0;
661  mutable_entries()->push_front(tab);
662  EXPECT_EQ(max_entries + 1, service_->entries().size());
663  PruneEntries();
664  EXPECT_EQ(max_entries, service_->entries().size());
665  EXPECT_EQ(GURL(kRecentUrl),
666      static_cast<Tab*>(service_->entries().front())->
667          navigations[0].virtual_url());
668
669  // Ignore NTPs.
670  navigation = SerializedNavigationEntryTestHelper::CreateNavigation(
671      chrome::kChromeUINewTabURL, "New tab");
672
673  tab = new Tab();
674  tab->navigations.push_back(navigation);
675  tab->current_navigation_index = 0;
676  mutable_entries()->push_front(tab);
677
678  EXPECT_EQ(max_entries + 1, service_->entries().size());
679  PruneEntries();
680  EXPECT_EQ(max_entries, service_->entries().size());
681  EXPECT_EQ(GURL(kRecentUrl),
682      static_cast<Tab*>(service_->entries().front())->
683          navigations[0].virtual_url());
684
685  // Don't prune pinned NTPs.
686  tab = new Tab();
687  tab->pinned = true;
688  tab->current_navigation_index = 0;
689  tab->navigations.push_back(navigation);
690  mutable_entries()->push_front(tab);
691  EXPECT_EQ(max_entries + 1, service_->entries().size());
692  PruneEntries();
693  EXPECT_EQ(max_entries, service_->entries().size());
694  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
695      static_cast<Tab*>(service_->entries().front())->
696          navigations[0].virtual_url());
697
698  // Don't prune NTPs that have multiple navigations.
699  // (Erase the last NTP first.)
700  delete service_->entries().front();
701  mutable_entries()->erase(mutable_entries()->begin());
702  tab = new Tab();
703  tab->current_navigation_index = 1;
704  tab->navigations.push_back(navigation);
705  tab->navigations.push_back(navigation);
706  mutable_entries()->push_front(tab);
707  EXPECT_EQ(max_entries, service_->entries().size());
708  PruneEntries();
709  EXPECT_EQ(max_entries, service_->entries().size());
710  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
711      static_cast<Tab*>(service_->entries().front())->
712          navigations[1].virtual_url());
713}
714
715// Regression test for crbug.com/106082
716TEST_F(PersistentTabRestoreServiceTest, PruneIsCalled) {
717  CreateSessionServiceWithOneWindow(false);
718
719  SessionServiceFactory::GetForProfile(profile())->
720      MoveCurrentSessionToLastSession();
721
722  profile()->set_restored_last_session(true);
723
724  const size_t max_entries = kMaxEntries;
725  for (size_t i = 0; i < max_entries + 5; i++) {
726    NavigateAndCommit(
727        GURL(base::StringPrintf("http://%d", static_cast<int>(i))));
728    service_->CreateHistoricalTab(web_contents(), -1);
729  }
730
731  EXPECT_EQ(max_entries, service_->entries().size());
732  // This should not crash.
733  SynchronousLoadTabsFromLastSession();
734  EXPECT_EQ(max_entries, service_->entries().size());
735}
736
737// Makes sure invoking LoadTabsFromLastSession() when the max number of entries
738// have been added results in IsLoaded() returning true and notifies observers.
739TEST_F(PersistentTabRestoreServiceTest, GoToLoadedWhenHaveMaxEntries) {
740  const size_t max_entries = kMaxEntries;
741  for (size_t i = 0; i < max_entries + 5; i++) {
742    NavigateAndCommit(
743        GURL(base::StringPrintf("http://%d", static_cast<int>(i))));
744    service_->CreateHistoricalTab(web_contents(), -1);
745  }
746
747  EXPECT_FALSE(service_->IsLoaded());
748  TestTabRestoreServiceObserver observer;
749  service_->AddObserver(&observer);
750  EXPECT_EQ(max_entries, service_->entries().size());
751  SynchronousLoadTabsFromLastSession();
752  EXPECT_TRUE(observer.got_loaded());
753  EXPECT_TRUE(service_->IsLoaded());
754  service_->RemoveObserver(&observer);
755}
756