blob: 7ce055420df34b037ff73aa91f987a5967b85cb9 [file] [log] [blame]
// Copyright 2018 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/ui/user_education/active_tab_tracker.h"
#include <memory>
#include <utility>
#include "base/test/mock_callback.h"
#include "base/test/simple_test_tick_clock.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/list_selection_model.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::Mock;
namespace {
class TestTabStripModelDelegateNoUnloadListener
: public TestTabStripModelDelegate {
public:
TestTabStripModelDelegateNoUnloadListener() = default;
~TestTabStripModelDelegateNoUnloadListener() override = default;
bool RunUnloadListenerBeforeClosing(content::WebContents* contents) override {
return false;
}
};
constexpr base::TimeDelta kTimeStep = base::TimeDelta::FromSeconds(1);
} // namespace
class ActiveTabTrackerTest : public ::testing::Test {
protected:
void SetUp() override {
TestingProfile::Builder profile_builder;
profile_ = profile_builder.Build();
}
void AddTab(TabStripModel* model) {
std::unique_ptr<content::WebContents> contents =
content::WebContents::Create(
content::WebContents::CreateParams(profile_.get()));
model->AppendWebContents(std::move(contents), true);
}
void CloseTabAt(TabStripModel* model, int index) {
model->CloseWebContentsAt(index,
TabStripModel::CloseTypes::CLOSE_USER_GESTURE);
}
Profile* profile() { return profile_.get(); }
base::SimpleTestTickClock* clock() { return &clock_; }
private:
// A |BrowserTaskEnvironment| is needed for creating and using
// |WebContents|es in a unit test.
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> profile_;
base::SimpleTestTickClock clock_;
};
TEST_F(ActiveTabTrackerTest, NotifiesOnActiveTabClosed) {
TestTabStripModelDelegateNoUnloadListener delegate;
TabStripModel model(&delegate, profile());
base::MockCallback<ActiveTabTracker::ActiveTabClosedCallback> cb;
ActiveTabTracker tracker(clock(), cb.Get());
tracker.AddTabStripModel(&model);
AddTab(&model);
AddTab(&model);
clock()->Advance(kTimeStep);
model.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
clock()->Advance(kTimeStep);
EXPECT_CALL(cb, Run(&model, kTimeStep)).Times(1);
CloseTabAt(&model, 0);
tracker.RemoveTabStripModel(&model);
model.DetachWebContentsAt(0);
}
TEST_F(ActiveTabTrackerTest, UpdatesTimes) {
TestTabStripModelDelegateNoUnloadListener delegate;
TabStripModel model(&delegate, profile());
base::MockCallback<ActiveTabTracker::ActiveTabClosedCallback> cb;
ActiveTabTracker tracker(clock(), cb.Get());
tracker.AddTabStripModel(&model);
AddTab(&model);
AddTab(&model);
model.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
clock()->Advance(kTimeStep);
model.ActivateTabAt(1, {TabStripModel::GestureType::kOther});
model.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
EXPECT_CALL(cb, Run(&model, base::TimeDelta())).Times(1);
CloseTabAt(&model, 0);
tracker.RemoveTabStripModel(&model);
model.DetachWebContentsAt(0);
}
TEST_F(ActiveTabTrackerTest, IgnoresInactiveTabs) {
TestTabStripModelDelegateNoUnloadListener delegate;
TabStripModel model(&delegate, profile());
base::MockCallback<ActiveTabTracker::ActiveTabClosedCallback> cb;
ActiveTabTracker tracker(clock(), cb.Get());
tracker.AddTabStripModel(&model);
AddTab(&model);
AddTab(&model);
model.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
EXPECT_CALL(cb, Run(_, _)).Times(0);
CloseTabAt(&model, 1);
tracker.RemoveTabStripModel(&model);
model.DetachWebContentsAt(0);
}
TEST_F(ActiveTabTrackerTest, TracksMultipleTabStripModels) {
TestTabStripModelDelegateNoUnloadListener delegate;
TabStripModel model_1(&delegate, profile());
TabStripModel model_2(&delegate, profile());
base::MockCallback<ActiveTabTracker::ActiveTabClosedCallback> cb;
ActiveTabTracker tracker(clock(), cb.Get());
tracker.AddTabStripModel(&model_1);
tracker.AddTabStripModel(&model_2);
AddTab(&model_1);
AddTab(&model_1);
AddTab(&model_2);
AddTab(&model_2);
clock()->Advance(kTimeStep);
model_1.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
clock()->Advance(kTimeStep);
model_2.ActivateTabAt(0, {TabStripModel::GestureType::kOther});
{
InSequence seq;
EXPECT_CALL(cb, Run(&model_1, kTimeStep)).Times(1);
EXPECT_CALL(cb, Run(&model_2, base::TimeDelta())).Times(1);
}
CloseTabAt(&model_1, 0);
CloseTabAt(&model_2, 0);
tracker.RemoveTabStripModel(&model_1);
tracker.RemoveTabStripModel(&model_2);
model_1.DetachWebContentsAt(0);
model_2.DetachWebContentsAt(0);
}
TEST_F(ActiveTabTrackerTest, StopsObservingUponRemove) {
TestTabStripModelDelegateNoUnloadListener delegate;
TabStripModel model(&delegate, profile());
base::MockCallback<ActiveTabTracker::ActiveTabClosedCallback> cb;
ActiveTabTracker tracker(clock(), cb.Get());
tracker.AddTabStripModel(&model);
AddTab(&model);
AddTab(&model);
tracker.RemoveTabStripModel(&model);
EXPECT_CALL(cb, Run(_, _)).Times(0);
CloseTabAt(&model, 0);
model.DetachWebContentsAt(0);
}