// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/back_forward_menu_model.h"

#include "base/file_util.h"
#include "base/path_service.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

const TabContentsType kHTTPTabContentsType =
    static_cast<TabContentsType>(TAB_CONTENTS_NUM_TYPES + 1);

// Since you can't just instantiate a TabContents, and some of its methods
// are protected, we subclass TabContents with our own testing dummy which
// knows how to drive the base class' NavigationController as URLs are
// loaded. Constructing one of these automatically constructs a
// NavigationController instance which we wrap our RecentPagesModel around.
class BackFwdMenuModelTestTabContents : public TabContents {
 public:
  BEGIN_MSG_MAP(BackFwdMenuModelTestTabContents)
  END_MSG_MAP()

  BackFwdMenuModelTestTabContents() : TabContents(kHTTPTabContentsType) {
  }

  // We do the same thing as the TabContents one (just commit the navigation)
  // but we *don't* want to reset the title since the test looks for this.
  virtual bool NavigateToPendingEntry(bool reload) {
    controller()->CommitPendingEntry();
    return true;
  }

  void UpdateState(const std::wstring& title) {
    NavigationEntry* entry =
      controller()->GetEntryWithPageID(type(), NULL, GetMaxPageID());
    entry->set_title(title);
  }
};

// This constructs our fake TabContents.
class BackFwdMenuModelTestTabContentsFactory : public TabContentsFactory {
 public:
  virtual TabContents* CreateInstance() {
    return new BackFwdMenuModelTestTabContents;
  }

  virtual bool CanHandleURL(const GURL& url) {
    return url.scheme() == "http";
  }
};

BackFwdMenuModelTestTabContentsFactory factory;

class BackFwdMenuModelTest : public testing::Test {
 public:

  // Overridden from testing::Test
  virtual void SetUp() {
    TabContents::RegisterFactory(kHTTPTabContentsType, &factory);

    // Name a subdirectory of the temp directory.
    ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
    file_util::AppendToPath(&test_dir_, L"BackFwdMenuModelTest");

    // Create a fresh, empty copy of this directory.
    file_util::Delete(test_dir_, true);
    CreateDirectory(test_dir_.c_str(), NULL);

    profile_path_ = test_dir_;
    file_util::AppendToPath(&profile_path_, L"New Profile");

    profile_ = ProfileManager::CreateProfile(
        FilePath::FromWStringHack(profile_path_),
        L"New Profile", L"new-profile", L"");
    ASSERT_TRUE(profile_);
    pm_.AddProfile(profile_);
  }

  virtual void TearDown() {
    TabContents::RegisterFactory(kHTTPTabContentsType, NULL);

    // Removes a profile from the set of currently-loaded profiles.
    pm_.RemoveProfileByPath(FilePath::FromWStringHack(profile_path_));

    // Clean up test directory
    ASSERT_TRUE(file_util::Delete(test_dir_, true));
    ASSERT_FALSE(file_util::PathExists(test_dir_));
  }

 protected:
  TabContents* CreateTabContents() {
    TabContents* contents = new BackFwdMenuModelTestTabContents;
    contents->CreateView();
    contents->SetupController(profile_);
    return contents;
  }

  // Forwards a URL "load" request through to our dummy TabContents
  // implementation.
  void LoadURLAndUpdateState(TabContents* contents,
                             const std::wstring& url,
                             const std::wstring& title) {
    contents->controller()->LoadURL(GURL(url), GURL(), PageTransition::LINK);
    BackFwdMenuModelTestTabContents* rsmttc =
      static_cast<BackFwdMenuModelTestTabContents*>(contents);
    rsmttc->UpdateState(title);
  }

 private:
  MessageLoopForUI message_loop_;
  std::wstring test_dir_;
  std::wstring profile_path_;
  ProfileManager pm_;
  Profile* profile_;
};

TEST_F(BackFwdMenuModelTest, BasicCase) {
  TabContents* contents = CreateTabContents();

  {
    BackForwardMenuModel back_model(
      NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE);
    back_model.set_test_tab_contents(contents);

    BackForwardMenuModel forward_model(
      NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE);
    forward_model.set_test_tab_contents(contents);

    EXPECT_EQ(0, back_model.GetItemCount());
    EXPECT_EQ(0, forward_model.GetItemCount());
    EXPECT_FALSE(back_model.SupportsCommand(1));
    EXPECT_FALSE(back_model.IsCommandEnabled(1));

    // Seed the controller with a few URLs
    LoadURLAndUpdateState(contents, L"http://www.a.com/1", L"A1");
    LoadURLAndUpdateState(contents, L"http://www.a.com/2", L"A2");
    LoadURLAndUpdateState(contents, L"http://www.a.com/3", L"A3");
    LoadURLAndUpdateState(contents, L"http://www.b.com/1", L"B1");
    LoadURLAndUpdateState(contents, L"http://www.b.com/2", L"B2");
    LoadURLAndUpdateState(contents, L"http://www.c.com/1", L"C1");
    LoadURLAndUpdateState(contents, L"http://www.c.com/2", L"C2");
    LoadURLAndUpdateState(contents, L"http://www.c.com/3", L"C3");

    // There're two more items here: a separator and a "Show Full History".
    EXPECT_EQ(9, back_model.GetItemCount());
    EXPECT_EQ(0, forward_model.GetItemCount());
    EXPECT_EQ(L"C2", back_model.GetLabel(1));
    EXPECT_EQ(L"A1", back_model.GetLabel(7));
    EXPECT_EQ(back_model.GetShowFullHistoryLabel(),
              back_model.GetLabel(9));

    EXPECT_TRUE(back_model.SupportsCommand(1));
    EXPECT_TRUE(back_model.IsCommandEnabled(1));
    EXPECT_TRUE(back_model.SupportsCommand(7));
    EXPECT_TRUE(back_model.IsCommandEnabled(7));
    EXPECT_TRUE(back_model.IsItemSeparator(8));
    EXPECT_TRUE(back_model.SupportsCommand(9));
    EXPECT_TRUE(back_model.IsCommandEnabled(9));
    EXPECT_FALSE(back_model.SupportsCommand(8));
    EXPECT_FALSE(back_model.IsCommandEnabled(8));
    EXPECT_FALSE(back_model.SupportsCommand(10));
    EXPECT_FALSE(back_model.IsCommandEnabled(10));

    contents->controller()->GoToOffset(-7);

    EXPECT_EQ(0, back_model.GetItemCount());
    EXPECT_EQ(9, forward_model.GetItemCount());
    EXPECT_EQ(L"A2", forward_model.GetLabel(1));
    EXPECT_EQ(L"C3", forward_model.GetLabel(7));
    EXPECT_EQ(forward_model.GetShowFullHistoryLabel(),
              forward_model.GetLabel(9));

    EXPECT_TRUE(forward_model.SupportsCommand(1));
    EXPECT_TRUE(forward_model.IsCommandEnabled(1));
    EXPECT_TRUE(forward_model.SupportsCommand(7));
    EXPECT_TRUE(forward_model.IsCommandEnabled(7));
    EXPECT_TRUE(forward_model.IsItemSeparator(8));
    EXPECT_TRUE(forward_model.SupportsCommand(9));
    EXPECT_TRUE(forward_model.IsCommandEnabled(9));
    EXPECT_FALSE(forward_model.SupportsCommand(8));
    EXPECT_FALSE(forward_model.IsCommandEnabled(8));
    EXPECT_FALSE(forward_model.SupportsCommand(10));
    EXPECT_FALSE(forward_model.IsCommandEnabled(10));
    contents->controller()->GoToOffset(4);

    EXPECT_EQ(6, back_model.GetItemCount());
    EXPECT_EQ(5, forward_model.GetItemCount());
    EXPECT_EQ(L"B1", back_model.GetLabel(1));
    EXPECT_EQ(L"A1", back_model.GetLabel(4));
    EXPECT_EQ(back_model.GetShowFullHistoryLabel(),
              back_model.GetLabel(6));
    EXPECT_EQ(L"C1", forward_model.GetLabel(1));
    EXPECT_EQ(L"C3", forward_model.GetLabel(3));
    EXPECT_EQ(forward_model.GetShowFullHistoryLabel(),
              forward_model.GetLabel(5));
  }
  contents->CloseContents();
}

TEST_F(BackFwdMenuModelTest, MaxItemsTest) {
  TabContents* contents = CreateTabContents();

  {
    BackForwardMenuModel back_model(
      NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE);
    back_model.set_test_tab_contents(contents);

    BackForwardMenuModel forward_model(
      NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE);
    forward_model.set_test_tab_contents(contents);

    // Seed the controller with 32 URLs
    LoadURLAndUpdateState(contents, L"http://www.a.com/1", L"A1");
    LoadURLAndUpdateState(contents, L"http://www.a.com/2", L"A2");
    LoadURLAndUpdateState(contents, L"http://www.a.com/3", L"A3");
    LoadURLAndUpdateState(contents, L"http://www.b.com/1", L"B1");
    LoadURLAndUpdateState(contents, L"http://www.b.com/2", L"B2");
    LoadURLAndUpdateState(contents, L"http://www.b.com/3", L"B3");
    LoadURLAndUpdateState(contents, L"http://www.c.com/1", L"C1");
    LoadURLAndUpdateState(contents, L"http://www.c.com/2", L"C2");
    LoadURLAndUpdateState(contents, L"http://www.c.com/3", L"C3");
    LoadURLAndUpdateState(contents, L"http://www.d.com/1", L"D1");
    LoadURLAndUpdateState(contents, L"http://www.d.com/2", L"D2");
    LoadURLAndUpdateState(contents, L"http://www.d.com/3", L"D3");
    LoadURLAndUpdateState(contents, L"http://www.e.com/1", L"E1");
    LoadURLAndUpdateState(contents, L"http://www.e.com/2", L"E2");
    LoadURLAndUpdateState(contents, L"http://www.e.com/3", L"E3");
    LoadURLAndUpdateState(contents, L"http://www.f.com/1", L"F1");
    LoadURLAndUpdateState(contents, L"http://www.f.com/2", L"F2");
    LoadURLAndUpdateState(contents, L"http://www.f.com/3", L"F3");
    LoadURLAndUpdateState(contents, L"http://www.g.com/1", L"G1");
    LoadURLAndUpdateState(contents, L"http://www.g.com/2", L"G2");
    LoadURLAndUpdateState(contents, L"http://www.g.com/3", L"G3");
    LoadURLAndUpdateState(contents, L"http://www.h.com/1", L"H1");
    LoadURLAndUpdateState(contents, L"http://www.h.com/2", L"H2");
    LoadURLAndUpdateState(contents, L"http://www.h.com/3", L"H3");
    LoadURLAndUpdateState(contents, L"http://www.i.com/1", L"I1");
    LoadURLAndUpdateState(contents, L"http://www.i.com/2", L"I2");
    LoadURLAndUpdateState(contents, L"http://www.i.com/3", L"I3");
    LoadURLAndUpdateState(contents, L"http://www.j.com/1", L"J1");
    LoadURLAndUpdateState(contents, L"http://www.j.com/2", L"J2");
    LoadURLAndUpdateState(contents, L"http://www.j.com/3", L"J3");
    LoadURLAndUpdateState(contents, L"http://www.k.com/1", L"K1");
    LoadURLAndUpdateState(contents, L"http://www.k.com/2", L"K2");

    // Also there're two more for a separator and a "Show Full History".
    int chapter_stop_offset = 6;
    EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
              back_model.GetItemCount());
    EXPECT_EQ(0, forward_model.GetItemCount());
    EXPECT_EQ(L"K1", back_model.GetLabel(1));
    EXPECT_EQ(back_model.GetShowFullHistoryLabel(),
      back_model.GetLabel(BackForwardMenuModel::kMaxHistoryItems + 2 +
                          chapter_stop_offset));

    // Test for out of bounds (beyond Show Full History).
    EXPECT_FALSE(back_model.SupportsCommand(
        BackForwardMenuModel::kMaxHistoryItems + chapter_stop_offset + 3));
    EXPECT_FALSE(back_model.IsCommandEnabled(
        BackForwardMenuModel::kMaxHistoryItems + chapter_stop_offset + 3));

    EXPECT_TRUE(back_model.SupportsCommand(
        BackForwardMenuModel::kMaxHistoryItems));
    EXPECT_TRUE(back_model.IsCommandEnabled(
        BackForwardMenuModel::kMaxHistoryItems));
    EXPECT_TRUE(back_model.IsItemSeparator(
      BackForwardMenuModel::kMaxHistoryItems + 1));

    contents->controller()->GoToIndex(0);

    EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
              forward_model.GetItemCount());
    EXPECT_EQ(0, back_model.GetItemCount());
    EXPECT_EQ(L"A2", forward_model.GetLabel(1));
    EXPECT_EQ(forward_model.GetShowFullHistoryLabel(),
      forward_model.GetLabel(BackForwardMenuModel::kMaxHistoryItems + 2 +
                             chapter_stop_offset));

    // Out of bounds
    EXPECT_FALSE(forward_model.SupportsCommand(
        BackForwardMenuModel::kMaxHistoryItems + 3 + chapter_stop_offset));
    EXPECT_FALSE(forward_model.IsCommandEnabled(
        BackForwardMenuModel::kMaxHistoryItems + 3 + chapter_stop_offset));

    EXPECT_TRUE(forward_model.SupportsCommand(
        BackForwardMenuModel::kMaxHistoryItems));
    EXPECT_TRUE(forward_model.IsCommandEnabled(
        BackForwardMenuModel::kMaxHistoryItems));
    EXPECT_TRUE(forward_model.IsItemSeparator(
        BackForwardMenuModel::kMaxHistoryItems + 1));
  }
  contents->CloseContents();
}

void ValidateModel(BackForwardMenuModel* model, int history_items,
                   int chapter_stops) {
  int h = std::min(BackForwardMenuModel::kMaxHistoryItems, history_items);
  int c = std::min(BackForwardMenuModel::kMaxChapterStops, chapter_stops);
  EXPECT_EQ(h, model->GetHistoryItemCount());
  EXPECT_EQ(c, model->GetChapterStopCount(h));
  if (h > 0)
    h += 2;  // separator and View History link
  if (c > 0)
    ++c;
  EXPECT_EQ(h + c, model->GetItemCount());
}

TEST_F(BackFwdMenuModelTest, ChapterStops) {
  TabContents* contents = CreateTabContents();

  {
    BackForwardMenuModel back_model(
      NULL, BackForwardMenuModel::BACKWARD_MENU_DELEGATE);
    back_model.set_test_tab_contents(contents);

    BackForwardMenuModel forward_model(
      NULL, BackForwardMenuModel::FORWARD_MENU_DELEGATE);
    forward_model.set_test_tab_contents(contents);

    // Seed the controller with 32 URLs.
    int i = 0;
    LoadURLAndUpdateState(contents, L"http://www.a.com/1", L"A1"); // 0
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.a.com/2", L"A2");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.a.com/3", L"A3");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.b.com/1", L"B1");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.b.com/2", L"B2");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.b.com/3", L"B3"); // 5
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.c.com/1", L"C1");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.c.com/2", L"C2");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.c.com/3", L"C3");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.d.com/1", L"D1");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.d.com/2", L"D2"); // 10
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.d.com/3", L"D3");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.e.com/1", L"E1");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.e.com/2", L"E2");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.e.com/3", L"E3");
    ValidateModel(&back_model, i++, 0);
    LoadURLAndUpdateState(contents, L"http://www.f.com/1", L"F1"); // 15
    ValidateModel(&back_model, i++, 1);
    LoadURLAndUpdateState(contents, L"http://www.f.com/2", L"F2");
    ValidateModel(&back_model, i++, 1);
    LoadURLAndUpdateState(contents, L"http://www.f.com/3", L"F3");
    ValidateModel(&back_model, i++, 1);
    LoadURLAndUpdateState(contents, L"http://www.g.com/1", L"G1");
    ValidateModel(&back_model, i++, 2);
    LoadURLAndUpdateState(contents, L"http://www.g.com/2", L"G2");
    ValidateModel(&back_model, i++, 2);
    LoadURLAndUpdateState(contents, L"http://www.g.com/3", L"G3"); // 20
    ValidateModel(&back_model, i++, 2);
    LoadURLAndUpdateState(contents, L"http://www.h.com/1", L"H1");
    ValidateModel(&back_model, i++, 3);
    LoadURLAndUpdateState(contents, L"http://www.h.com/2", L"H2");
    ValidateModel(&back_model, i++, 3);
    LoadURLAndUpdateState(contents, L"http://www.h.com/3", L"H3");
    ValidateModel(&back_model, i++, 3);
    LoadURLAndUpdateState(contents, L"http://www.i.com/1", L"I1");
    ValidateModel(&back_model, i++, 4);
    LoadURLAndUpdateState(contents, L"http://www.i.com/2", L"I2"); // 25
    ValidateModel(&back_model, i++, 4);
    LoadURLAndUpdateState(contents, L"http://www.i.com/3", L"I3");
    ValidateModel(&back_model, i++, 4);
    LoadURLAndUpdateState(contents, L"http://www.j.com/1", L"J1");
    ValidateModel(&back_model, i++, 5);
    LoadURLAndUpdateState(contents, L"http://www.j.com/2", L"J2");
    ValidateModel(&back_model, i++, 5);
    LoadURLAndUpdateState(contents, L"http://www.j.com/3", L"J3");
    ValidateModel(&back_model, i++, 5);
    LoadURLAndUpdateState(contents, L"http://www.k.com/1", L"K1"); // 30
    ValidateModel(&back_model, i++, 6);
    LoadURLAndUpdateState(contents, L"http://www.k.com/2", L"K2");
    ValidateModel(&back_model, i++, 6);
    LoadURLAndUpdateState(contents, L"http://www.k.com/3", L"K3"); // 32
    ValidateModel(&back_model, i++, 6);

    // Check to see if the chapter stops have the right labels.
    int index = BackForwardMenuModel::kMaxHistoryItems + 1;
    EXPECT_EQ(L"", back_model.GetLabel(index++));    // separator.
    EXPECT_EQ(L"F3", back_model.GetLabel(index++));
    EXPECT_EQ(L"E3", back_model.GetLabel(index++));
    EXPECT_EQ(L"D3", back_model.GetLabel(index++));
    EXPECT_EQ(L"C3", back_model.GetLabel(index++));
    EXPECT_EQ(L"B3", back_model.GetLabel(index));    // max 5 chapter stops.
    EXPECT_EQ(L"", back_model.GetLabel(index + 1));  // separator.
    EXPECT_EQ(back_model.GetShowFullHistoryLabel(),
              back_model.GetLabel(index + 2));

    // If we go back two we should still see the same chapter stop at the end.
    contents->controller()->GoBack();
    EXPECT_EQ(L"B3", back_model.GetLabel(index));
    contents->controller()->GoBack();
    EXPECT_EQ(L"B3", back_model.GetLabel(index));
    // But if we go back again, it should change.
    contents->controller()->GoBack();
    EXPECT_EQ(L"A3", back_model.GetLabel(index));
    contents->controller()->GoBack();
    EXPECT_EQ(L"A3", back_model.GetLabel(index));
    contents->controller()->GoBack();
    EXPECT_EQ(L"A3", back_model.GetLabel(index));
    contents->controller()->GoBack();
    EXPECT_EQ(L"", back_model.GetLabel(index));  // is now a separator.
    contents->controller()->GoToOffset(6);  // undo our position change.

    // Go back enough to make sure no chapter stops should appear.
    contents->controller()->GoToOffset(-BackForwardMenuModel::kMaxHistoryItems);
    ValidateModel(&forward_model, BackForwardMenuModel::kMaxHistoryItems, 0);
    // Go forward (still no chapter stop)
    contents->controller()->GoForward();
    ValidateModel(&forward_model,
                  BackForwardMenuModel::kMaxHistoryItems - 1, 0);
    // Go back two (one chapter stop should show up)
    contents->controller()->GoBack();
    contents->controller()->GoBack();
    ValidateModel(&forward_model, BackForwardMenuModel::kMaxHistoryItems, 1);

    // Go to beginning.
    contents->controller()->GoToIndex(0);

    // Check to see if the chapter stops have the right labels.
    index = BackForwardMenuModel::kMaxHistoryItems + 1;
    EXPECT_EQ(L"", forward_model.GetLabel(index++));    // separator.
    EXPECT_EQ(L"E3", forward_model.GetLabel(index++));
    EXPECT_EQ(L"F3", forward_model.GetLabel(index++));
    EXPECT_EQ(L"G3", forward_model.GetLabel(index++));
    EXPECT_EQ(L"H3", forward_model.GetLabel(index++));
    EXPECT_EQ(L"I3", forward_model.GetLabel(index));    // max 5 chapter stops.
    EXPECT_EQ(L"", forward_model.GetLabel(index + 1));  // separator.
    EXPECT_EQ(forward_model.GetShowFullHistoryLabel(),
      forward_model.GetLabel(index + 2));

    // If we advance one we should still see the same chapter stop at the end.
    contents->controller()->GoForward();
    EXPECT_EQ(L"I3", forward_model.GetLabel(index));
    // But if we advance one again, it should change.
    contents->controller()->GoForward();
    EXPECT_EQ(L"J3", forward_model.GetLabel(index));
    contents->controller()->GoForward();
    EXPECT_EQ(L"J3", forward_model.GetLabel(index));
    contents->controller()->GoForward();
    EXPECT_EQ(L"J3", forward_model.GetLabel(index));
    contents->controller()->GoForward();
    EXPECT_EQ(L"K3", forward_model.GetLabel(index));

    // Now test the boundary cases by using the chapter stop function directly.
    // Out of bounds, first too far right (incrementing), then too far left.
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(33, false));
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(-1, true));
    // Test being at end and going right, then at beginning going left.
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(32, true));
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(0, false));
    // Test success: beginning going right and end going left.
    EXPECT_EQ(2,  back_model.GetIndexOfNextChapterStop(0, true));
    EXPECT_EQ(29, back_model.GetIndexOfNextChapterStop(32, false));
    // Now see when the chapter stops begin to show up.
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(1, false));
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(2, false));
    EXPECT_EQ(2,  back_model.GetIndexOfNextChapterStop(3, false));
    // Now see when the chapter stops end.
    EXPECT_EQ(32, back_model.GetIndexOfNextChapterStop(30, true));
    EXPECT_EQ(32, back_model.GetIndexOfNextChapterStop(31, true));
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(32, true));

    // Bug found during review (two different sites, but first wasn't considered
    // a chapter-stop).
    contents->controller()->GoToIndex(0);  // Go to A1.
    LoadURLAndUpdateState(contents, L"http://www.b.com/1", L"B1");
    EXPECT_EQ(0, back_model.GetIndexOfNextChapterStop(1, false));
    EXPECT_EQ(1, back_model.GetIndexOfNextChapterStop(0, true));

    // Now see if it counts 'www.x.com' and 'mail.x.com' as same domain, which
    // it should.
    contents->controller()->GoToIndex(0);  // Go to A1.
    LoadURLAndUpdateState(contents, L"http://mail.a.com/2", L"A2-mail");
    LoadURLAndUpdateState(contents, L"http://www.b.com/1", L"B1");
    LoadURLAndUpdateState(contents, L"http://mail.b.com/2", L"B2-mail");
    LoadURLAndUpdateState(contents, L"http://new.site.com", L"new");
    EXPECT_EQ(1, back_model.GetIndexOfNextChapterStop(0, true));
    EXPECT_EQ(3, back_model.GetIndexOfNextChapterStop(1, true));
    EXPECT_EQ(3, back_model.GetIndexOfNextChapterStop(2, true));
    EXPECT_EQ(4, back_model.GetIndexOfNextChapterStop(3, true));
    // And try backwards as well.
    EXPECT_EQ(3, back_model.GetIndexOfNextChapterStop(4, false));
    EXPECT_EQ(1, back_model.GetIndexOfNextChapterStop(3, false));
    EXPECT_EQ(1, back_model.GetIndexOfNextChapterStop(2, false));
    EXPECT_EQ(-1, back_model.GetIndexOfNextChapterStop(1, false));
  }
  contents->CloseContents();
}