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