// Copyright (c) 2012 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/google/google_url_tracker.h"

#include <set>
#include <string>

#include "base/message_loop.h"
#include "chrome/browser/infobars/infobar_delegate.h"
#include "chrome/browser/google/google_url_tracker_factory.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/url_fetcher.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

class TabContents;

// TestNotificationObserver ---------------------------------------------------

namespace {

class TestNotificationObserver : public content::NotificationObserver {
 public:
  TestNotificationObserver();
  virtual ~TestNotificationObserver();

  virtual void Observe(int type,
                       const content::NotificationSource& source,
                       const content::NotificationDetails& details);
  bool notified() const { return notified_; }
  void clear_notified() { notified_ = false; }

 private:
  bool notified_;
};

TestNotificationObserver::TestNotificationObserver() : notified_(false) {
}

TestNotificationObserver::~TestNotificationObserver() {
}

void TestNotificationObserver::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  notified_ = true;
}


// TestInfoBarDelegate --------------------------------------------------------

class TestInfoBarDelegate : public GoogleURLTrackerInfoBarDelegate {
 public:
  TestInfoBarDelegate(InfoBarTabHelper* infobar_helper,
                      const GURL& search_url,
                      GoogleURLTracker* google_url_tracker,
                      const GURL& new_google_url);
  virtual ~TestInfoBarDelegate();

  GURL search_url() const { return search_url_; }
  GURL new_google_url() const { return new_google_url_; }
  bool showing() const { return showing_; }

 private:
  // GoogleURLTrackerInfoBarDelegate:
  virtual void Show() OVERRIDE;
  virtual void Close(bool redo_search) OVERRIDE;
};

TestInfoBarDelegate::TestInfoBarDelegate(InfoBarTabHelper* infobar_helper,
                                         const GURL& search_url,
                                         GoogleURLTracker* google_url_tracker,
                                         const GURL& new_google_url)
  : GoogleURLTrackerInfoBarDelegate(NULL, search_url, google_url_tracker,
                                    new_google_url) {
  // We set |map_key_| here instead of in the superclass constructor so that the
  // InfoBarDelegate base class will not try to dereference it, which would fail
  // since this is really a magic number and not an actual pointer.
  map_key_ = infobar_helper;
}

void TestInfoBarDelegate::Show() {
  showing_ = true;
}

void TestInfoBarDelegate::Close(bool redo_search) {
  InfoBarClosed();
}

TestInfoBarDelegate::~TestInfoBarDelegate() {
}

GoogleURLTrackerInfoBarDelegate* CreateTestInfobar(
    InfoBarTabHelper* infobar_helper,
    const GURL& search_url,
    GoogleURLTracker* google_url_tracker,
    const GURL& new_google_url) {
  return new TestInfoBarDelegate(infobar_helper, search_url, google_url_tracker,
                                 new_google_url);
}

}  // namespace


// GoogleURLTrackerTest -------------------------------------------------------

class GoogleURLTrackerTest : public testing::Test {
 protected:
  GoogleURLTrackerTest();
  virtual ~GoogleURLTrackerTest();

  // testing::Test
  virtual void SetUp();
  virtual void TearDown();

  TestURLFetcher* GetFetcher();
  void MockSearchDomainCheckResponse(const std::string& domain);
  void RequestServerCheck();
  void FinishSleep();
  void NotifyIPAddressChanged();
  GURL fetched_google_url() const {
    return google_url_tracker_->fetched_google_url_;
  }
  void set_google_url(const GURL& url) {
    google_url_tracker_->google_url_ = url;
  }
  GURL google_url() const { return google_url_tracker_->google_url_; }
  void SetLastPromptedGoogleURL(const GURL& url);
  GURL GetLastPromptedGoogleURL();
  void SetSearchPending(const GURL& search_url, int unique_id);
  void CommitSearch(int unique_id);
  void CloseTab(int unique_id);
  TestInfoBarDelegate* GetInfoBar(int unique_id);
  void ExpectDefaultURLs();

  scoped_ptr<TestNotificationObserver> observer_;

 private:
  // These are required by the TestURLFetchers GoogleURLTracker will create (see
  // test_url_fetcher_factory.h).
  MessageLoop message_loop_;
  content::TestBrowserThread io_thread_;
  // Creating this allows us to call
  // net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests().
  scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
  TestURLFetcherFactory fetcher_factory_;
  content::NotificationRegistrar registrar_;
  TestingProfile profile_;
  scoped_ptr<GoogleURLTracker> google_url_tracker_;
  // This tracks the different "tabs" a test has "opened", so we can close them
  // properly before shutting down |google_url_tracker_|, which expects that.
  std::set<int> unique_ids_seen_;
};

GoogleURLTrackerTest::GoogleURLTrackerTest()
    : observer_(new TestNotificationObserver),
      message_loop_(MessageLoop::TYPE_IO),
      io_thread_(content::BrowserThread::IO, &message_loop_) {
  GoogleURLTrackerFactory::GetInstance()->RegisterUserPrefsOnProfile(&profile_);
}

GoogleURLTrackerTest::~GoogleURLTrackerTest() {
}

void GoogleURLTrackerTest::SetUp() {
  network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
  google_url_tracker_.reset(
      new GoogleURLTracker(&profile_, GoogleURLTracker::UNIT_TEST_MODE));
  google_url_tracker_->infobar_creator_ = &CreateTestInfobar;
}

void GoogleURLTrackerTest::TearDown() {
  while (!unique_ids_seen_.empty())
    CloseTab(*unique_ids_seen_.begin());

  google_url_tracker_.reset();
  network_change_notifier_.reset();
}

TestURLFetcher* GoogleURLTrackerTest::GetFetcher() {
  // This will return the last fetcher created.  If no fetchers have been
  // created, we'll pass GetFetcherByID() "-1", and it will return NULL.
  return fetcher_factory_.GetFetcherByID(google_url_tracker_->fetcher_id_ - 1);
}

void GoogleURLTrackerTest::MockSearchDomainCheckResponse(
    const std::string& domain) {
  TestURLFetcher* fetcher = GetFetcher();
  if (!fetcher)
    return;
  fetcher_factory_.RemoveFetcherFromMap(fetcher->id());
  fetcher->set_url(GURL(GoogleURLTracker::kSearchDomainCheckURL));
  fetcher->set_response_code(200);
  fetcher->SetResponseString(domain);
  fetcher->delegate()->OnURLFetchComplete(fetcher);
  // At this point, |fetcher| is deleted.
}

void GoogleURLTrackerTest::RequestServerCheck() {
  if (!registrar_.IsRegistered(observer_.get(),
      chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
      content::Source<Profile>(&profile_))) {
    registrar_.Add(observer_.get(), chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
                   content::Source<Profile>(&profile_));
  }
  google_url_tracker_->SetNeedToFetch();
}

void GoogleURLTrackerTest::FinishSleep() {
  google_url_tracker_->FinishSleep();
}

void GoogleURLTrackerTest::NotifyIPAddressChanged() {
  net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
  // For thread safety, the NCN queues tasks to do the actual notifications, so
  // we need to spin the message loop so the tracker will actually be notified.
  MessageLoop::current()->RunAllPending();
}

void GoogleURLTrackerTest::SetLastPromptedGoogleURL(const GURL& url) {
  profile_.GetPrefs()->SetString(prefs::kLastPromptedGoogleURL, url.spec());
}

GURL GoogleURLTrackerTest::GetLastPromptedGoogleURL() {
  return GURL(profile_.GetPrefs()->GetString(prefs::kLastPromptedGoogleURL));
}

void GoogleURLTrackerTest::SetSearchPending(const GURL& search_url,
                                            int unique_id) {
  unique_ids_seen_.insert(unique_id);
  google_url_tracker_->SearchCommitted();
  if (google_url_tracker_->registrar_.IsRegistered(google_url_tracker_.get(),
      content::NOTIFICATION_NAV_ENTRY_PENDING,
      content::NotificationService::AllBrowserContextsAndSources())) {
    google_url_tracker_->OnNavigationPending(
        content::Source<content::NavigationController>(
            reinterpret_cast<content::NavigationController*>(unique_id)),
        content::Source<TabContents>(reinterpret_cast<TabContents*>(unique_id)),
        reinterpret_cast<InfoBarTabHelper*>(unique_id), search_url);
  }
}

void GoogleURLTrackerTest::CommitSearch(int unique_id) {
  content::Source<content::NavigationController> source(
      reinterpret_cast<content::NavigationController*>(unique_id));
  if (google_url_tracker_->registrar_.IsRegistered(google_url_tracker_.get(),
      content::NOTIFICATION_NAV_ENTRY_COMMITTED, source)) {
    google_url_tracker_->OnNavigationCommittedOrTabClosed(source,
        content::Source<TabContents>(reinterpret_cast<TabContents*>(unique_id)),
        reinterpret_cast<InfoBarTabHelper*>(unique_id), true);
  }
}

void GoogleURLTrackerTest::CloseTab(int unique_id) {
  unique_ids_seen_.erase(unique_id);
  content::Source<TabContents> source(
      reinterpret_cast<TabContents*>(unique_id));
  InfoBarTabHelper* infobar_helper =
      reinterpret_cast<InfoBarTabHelper*>(unique_id);
  if (google_url_tracker_->registrar_.IsRegistered(google_url_tracker_.get(),
      chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, source)) {
    google_url_tracker_->OnNavigationCommittedOrTabClosed(
        content::Source<content::NavigationController>(
            reinterpret_cast<content::NavigationController*>(unique_id)),
        source, infobar_helper, false);
  } else {
    // Normally, closing a tab with an infobar showing will close the infobar.
    // Since we don't have real tabs and are just faking things with magic
    // numbers, we have to manually close the infobar, if any.
    GoogleURLTracker::InfoBarMap::iterator i =
        google_url_tracker_->infobar_map_.find(infobar_helper);
    if (i != google_url_tracker_->infobar_map_.end()) {
      TestInfoBarDelegate* infobar =
          static_cast<TestInfoBarDelegate*>(i->second);
      DCHECK(infobar->showing());
      infobar->InfoBarClosed();
    }
  }
}

TestInfoBarDelegate* GoogleURLTrackerTest::GetInfoBar(int unique_id) {
  GoogleURLTracker::InfoBarMap::const_iterator i =
      google_url_tracker_->infobar_map_.find(
          reinterpret_cast<InfoBarTabHelper*>(unique_id));
  return (i == google_url_tracker_->infobar_map_.end()) ?
      NULL : static_cast<TestInfoBarDelegate*>(i->second);
}

void GoogleURLTrackerTest::ExpectDefaultURLs() {
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL(), fetched_google_url());
}


// Tests ----------------------------------------------------------------------

TEST_F(GoogleURLTrackerTest, DontFetchWhenNoOneRequestsCheck) {
  ExpectDefaultURLs();
  FinishSleep();
  // No one called RequestServerCheck() so nothing should have happened.
  EXPECT_FALSE(GetFetcher());
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  ExpectDefaultURLs();
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, UpdateOnFirstRun) {
  RequestServerCheck();
  EXPECT_FALSE(GetFetcher());
  ExpectDefaultURLs();
  EXPECT_FALSE(observer_->notified());

  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  // GoogleURL should be updated, becase there was no last prompted URL.
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_TRUE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, DontUpdateWhenUnchanged) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));

  RequestServerCheck();
  EXPECT_FALSE(GetFetcher());
  ExpectDefaultURLs();
  EXPECT_FALSE(observer_->notified());

  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  // GoogleURL should not be updated, because the fetched and prompted URLs
  // match.
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, DontPromptOnBadReplies) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));

  RequestServerCheck();
  EXPECT_FALSE(GetFetcher());
  ExpectDefaultURLs();
  EXPECT_FALSE(observer_->notified());

  // Old-style domain string.
  FinishSleep();
  MockSearchDomainCheckResponse(".google.co.in");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // Bad subdomain.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://mail.google.com/");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // Non-empty path.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.com/search");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // Non-empty query.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.com/?q=foo");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // Non-empty ref.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.com/#anchor");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // Complete garbage.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("HJ)*qF)_*&@f1");
  EXPECT_EQ(GURL(), fetched_google_url());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_FALSE(observer_->notified());
  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);
}

TEST_F(GoogleURLTrackerTest, UpdatePromptedURLOnReturnToPreviousLocation) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.jp/"));
  set_google_url(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, SilentlyAcceptSchemeChange) {
  // We should auto-accept changes to the current Google URL that merely change
  // the scheme, regardless of what the last prompted URL was.
  SetLastPromptedGoogleURL(GURL("http://www.google.co.jp/"));
  set_google_url(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("https://www.google.co.uk/");
  EXPECT_EQ(GURL("https://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("https://www.google.co.uk/"), google_url());
  EXPECT_EQ(GURL("https://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(observer_->notified());

  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, RefetchOnIPAddressChange) {
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_TRUE(observer_->notified());
  observer_->clear_notified();

  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.in/");
  EXPECT_EQ(GURL("http://www.google.co.in/"), fetched_google_url());
  // Just fetching a new URL shouldn't reset things without a prompt.
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, DontRefetchWhenNoOneRequestsCheck) {
  FinishSleep();
  NotifyIPAddressChanged();
  // No one called RequestServerCheck() so nothing should have happened.
  EXPECT_FALSE(GetFetcher());
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  ExpectDefaultURLs();
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, FetchOnLateRequest) {
  FinishSleep();
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  RequestServerCheck();
  // The first request for a check should trigger a fetch if it hasn't happened
  // already.
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_TRUE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, DontFetchTwiceOnLateRequests) {
  FinishSleep();
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  RequestServerCheck();
  // The first request for a check should trigger a fetch if it hasn't happened
  // already.
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_TRUE(observer_->notified());
  observer_->clear_notified();

  RequestServerCheck();
  // The second request should be ignored.
  EXPECT_FALSE(GetFetcher());
  MockSearchDomainCheckResponse("http://www.google.co.in/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, SearchingDoesNothingIfNoNeedToPrompt) {
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(observer_->notified());
  observer_->clear_notified();

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  EXPECT_TRUE(infobar == NULL);
  EXPECT_EQ(GURL("http://www.google.co.uk/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, TabClosedOnPendingSearch) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.jp/"), fetched_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_FALSE(infobar->showing());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test"),
            infobar->search_url());
  EXPECT_EQ(GURL("http://www.google.co.jp/"), infobar->new_google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());

  CloseTab(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, TabClosedOnCommittedSearch) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_TRUE(infobar->showing());

  CloseTab(1);
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, InfobarClosed) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);

  infobar->InfoBarClosed();
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, InfobarRefused) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);

  infobar->Cancel();
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, InfobarAccepted) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);

  infobar->Accept();
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_EQ(GURL("http://www.google.co.jp/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, FetchesCanAutomaticallyCloseInfobars) {
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse(google_url().spec());

  // Re-fetching the accepted URL after showing an infobar for another URL
  // should close the infobar.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_FALSE(GetInfoBar(1) == NULL);
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse(google_url().spec());
  EXPECT_EQ(google_url(), GetLastPromptedGoogleURL());
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // As should fetching a URL that differs from the accepted only by the scheme.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_FALSE(GetInfoBar(1) == NULL);
  NotifyIPAddressChanged();
  url_canon::Replacements<char> replacements;
  const std::string& scheme("https");
  replacements.SetScheme(scheme.data(),
                         url_parse::Component(0, scheme.length()));
  GURL new_google_url(google_url().ReplaceComponents(replacements));
  MockSearchDomainCheckResponse(new_google_url.spec());
  EXPECT_EQ(new_google_url, GetLastPromptedGoogleURL());
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // As should re-fetching the last prompted URL.
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_FALSE(GetInfoBar(1) == NULL);
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  EXPECT_EQ(new_google_url, google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // And one that differs from the last prompted URL only by the scheme.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_FALSE(GetInfoBar(1) == NULL);
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("https://www.google.co.uk/");
  EXPECT_EQ(new_google_url, google_url());
  EXPECT_EQ(GURL("https://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(GetInfoBar(1) == NULL);

  // And fetching a different URL entirely.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  EXPECT_FALSE(GetInfoBar(1) == NULL);
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("https://www.google.co.in/");
  EXPECT_EQ(new_google_url, google_url());
  EXPECT_EQ(GURL("https://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(GetInfoBar(1) == NULL);
}

TEST_F(GoogleURLTrackerTest, ResetInfobarGoogleURLs) {
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse(google_url().spec());

  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("http://www.google.co.uk/");
  SetSearchPending(GURL("http://www.google.com/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_EQ(GURL("http://www.google.co.uk/"), infobar->new_google_url());

  // If while an infobar is showing we fetch a new URL that differs from the
  // infobar's only by scheme, the infobar should stay open but have its Google
  // URL reset.
  NotifyIPAddressChanged();
  MockSearchDomainCheckResponse("https://www.google.co.uk/");
  TestInfoBarDelegate* new_infobar = GetInfoBar(1);
  ASSERT_FALSE(new_infobar == NULL);
  EXPECT_EQ(infobar, new_infobar);
  EXPECT_EQ(GURL("https://www.google.co.uk/"), new_infobar->new_google_url());
}

TEST_F(GoogleURLTrackerTest, InfobarShownAgainOnSearchAfterPendingSearch) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_FALSE(infobar->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test"),
            infobar->search_url());

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test2"), 1);
  TestInfoBarDelegate* infobar2 = GetInfoBar(1);
  ASSERT_FALSE(infobar2 == NULL);
  EXPECT_FALSE(infobar2->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test2"),
            infobar2->search_url());

  CommitSearch(1);
  EXPECT_TRUE(infobar2->showing());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, InfobarShownAgainOnSearchAfterCommittedSearch) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  CommitSearch(1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_TRUE(infobar->showing());

  // In real usage, the upcoming pending navigation for "test2" would close
  // |infobar|.  Since we're not actually doing navigations, we need to clean it
  // up manually to prevent leaks.
  delete infobar;

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test2"), 1);
  TestInfoBarDelegate* infobar2 = GetInfoBar(1);
  ASSERT_FALSE(infobar2 == NULL);
  EXPECT_FALSE(infobar2->showing());

  CommitSearch(1);
  EXPECT_TRUE(infobar2->showing());
  EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage), google_url());
  EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
  EXPECT_FALSE(observer_->notified());
}

TEST_F(GoogleURLTrackerTest, MultipleInfobars) {
  SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
  RequestServerCheck();
  FinishSleep();
  MockSearchDomainCheckResponse("http://www.google.co.jp/");

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test"), 1);
  TestInfoBarDelegate* infobar = GetInfoBar(1);
  ASSERT_FALSE(infobar == NULL);
  EXPECT_FALSE(infobar->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test"),
            infobar->search_url());

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test2"), 2);
  CommitSearch(2);
  TestInfoBarDelegate* infobar2 = GetInfoBar(2);
  ASSERT_FALSE(infobar2 == NULL);
  EXPECT_TRUE(infobar2->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test2"),
            infobar2->search_url());

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test3"), 3);
  TestInfoBarDelegate* infobar3 = GetInfoBar(3);
  ASSERT_FALSE(infobar3 == NULL);
  EXPECT_FALSE(infobar3->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test3"),
            infobar3->search_url());

  SetSearchPending(GURL("http://www.google.co.uk/search?q=test4"), 4);
  CommitSearch(4);
  TestInfoBarDelegate* infobar4 = GetInfoBar(4);
  ASSERT_FALSE(infobar4 == NULL);
  EXPECT_TRUE(infobar4->showing());
  EXPECT_EQ(GURL("http://www.google.co.uk/search?q=test4"),
            infobar4->search_url());

  CommitSearch(1);
  EXPECT_TRUE(infobar->showing());

  infobar2->InfoBarClosed();
  EXPECT_TRUE(GetInfoBar(2) == NULL);
  EXPECT_FALSE(observer_->notified());

  infobar4->Accept();
  EXPECT_TRUE(GetInfoBar(1) == NULL);
  EXPECT_TRUE(GetInfoBar(3) == NULL);
  EXPECT_TRUE(GetInfoBar(4) == NULL);
  EXPECT_EQ(GURL("http://www.google.co.jp/"), google_url());
  EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
  EXPECT_TRUE(observer_->notified());
}