// Copyright (c) 2011 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 <map>

#include "base/basictypes.h"
#include "chrome/browser/automation/automation_tab_helper.h"
#include "chrome/browser/automation/mock_tab_event_observer.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/tab_contents/test_tab_contents.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

class AutomationTabHelperTest : public TabContentsWrapperTestHarness {
 public:
  virtual void SetUp() {
    TabContentsWrapperTestHarness::SetUp();
    mock_observer_.StartObserving(tab_helper());
  }

 protected:
  // These are here so that we don't have to add each test as a
  // |AutomationTabHelper| friend.
  void StartLoading() {
    tab_helper()->DidStartLoading();
  }

  void StopLoading() {
    tab_helper()->DidStopLoading();
  }

  void TabContentsDestroyed() {
    tab_helper()->TabContentsDestroyed(contents_wrapper()->tab_contents());
  }

  void WillPerformClientRedirect(int64 frame_id) {
    tab_helper()->OnWillPerformClientRedirect(frame_id, 0);
  }

  void CompleteOrCancelClientRedirect(int64 frame_id) {
    tab_helper()->OnDidCompleteOrCancelClientRedirect(frame_id);
  }

  AutomationTabHelper* tab_helper() {
    return contents_wrapper()->automation_tab_helper();
  }

  MockTabEventObserver mock_observer_;
};

ACTION_P2(CheckHasPendingLoads, tab_helper, has_pending_loads) {
  EXPECT_EQ(has_pending_loads, tab_helper->has_pending_loads());
}

TEST_F(AutomationTabHelperTest, AddAndRemoveObserver) {
  testing::MockFunction<void()> check;

  {
    testing::InSequence expect_in_sequence;
    EXPECT_CALL(mock_observer_, OnFirstPendingLoad(_));
    EXPECT_CALL(check, Call());
  }

  StartLoading();
  check.Call();
  tab_helper()->RemoveObserver(&mock_observer_);
  StartLoading();
}

TEST_F(AutomationTabHelperTest, StartStopLoading) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()))
      .WillOnce(CheckHasPendingLoads(tab_helper(), true));
  EXPECT_CALL(check, Call());
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()))
      .WillOnce(CheckHasPendingLoads(tab_helper(), false));

  EXPECT_FALSE(tab_helper()->has_pending_loads());
  StartLoading();
  EXPECT_TRUE(tab_helper()->has_pending_loads());
  check.Call();
  StopLoading();
  EXPECT_FALSE(tab_helper()->has_pending_loads());
}

TEST_F(AutomationTabHelperTest, DuplicateLoads) {
  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  StartLoading();
  StartLoading();
  StopLoading();
  StopLoading();
}

TEST_F(AutomationTabHelperTest, ClientRedirect) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()))
      .WillOnce(CheckHasPendingLoads(tab_helper(), true));
  EXPECT_CALL(check, Call());
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()))
      .WillOnce(CheckHasPendingLoads(tab_helper(), false));

  WillPerformClientRedirect(1);
  EXPECT_TRUE(tab_helper()->has_pending_loads());
  check.Call();
  CompleteOrCancelClientRedirect(1);
  EXPECT_FALSE(tab_helper()->has_pending_loads());
}

TEST_F(AutomationTabHelperTest, DiscardExtraClientRedirects) {
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  WillPerformClientRedirect(1);
  WillPerformClientRedirect(1);
  CompleteOrCancelClientRedirect(1);
  EXPECT_FALSE(tab_helper()->has_pending_loads());
  CompleteOrCancelClientRedirect(1);
  CompleteOrCancelClientRedirect(2);
}

TEST_F(AutomationTabHelperTest, StartStopLoadingWithClientRedirect) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  StartLoading();
  WillPerformClientRedirect(1);
  CompleteOrCancelClientRedirect(1);
  StopLoading();
}

TEST_F(AutomationTabHelperTest, ClientRedirectBeforeLoad) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(check, Call());
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  StartLoading();
  WillPerformClientRedirect(1);
  CompleteOrCancelClientRedirect(1);
  EXPECT_TRUE(tab_helper()->has_pending_loads());
  check.Call();
  StopLoading();
}

TEST_F(AutomationTabHelperTest, ClientRedirectAfterLoad) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(check, Call());
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  StartLoading();
  WillPerformClientRedirect(1);
  StopLoading();
  EXPECT_TRUE(tab_helper()->has_pending_loads());
  check.Call();
  CompleteOrCancelClientRedirect(1);
  EXPECT_FALSE(tab_helper()->has_pending_loads());
}

TEST_F(AutomationTabHelperTest, AllFramesMustFinishRedirects) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(check, Call());
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  WillPerformClientRedirect(1);
  WillPerformClientRedirect(2);
  CompleteOrCancelClientRedirect(1);
  check.Call();
  EXPECT_TRUE(tab_helper()->has_pending_loads());
  CompleteOrCancelClientRedirect(2);
  EXPECT_FALSE(tab_helper()->has_pending_loads());
}

TEST_F(AutomationTabHelperTest, DestroyedTabStopsLoading) {
  testing::MockFunction<void()> check;

  testing::InSequence expect_in_sequence;
  EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents()));
  EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents()));

  StartLoading();
  WillPerformClientRedirect(1);
  TabContentsDestroyed();
  EXPECT_FALSE(tab_helper()->has_pending_loads());
}