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