// 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(); }