// Copyright 2020 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 "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/filename_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/navigation.h"
#include "weblayer/public/navigation_controller.h"
#include "weblayer/public/tab.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/test_navigation_observer.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"

namespace weblayer {

class MinimalBrowserPersisterTest : public WebLayerBrowserTest {
 public:
  MinimalBrowserPersisterTest() = default;
  ~MinimalBrowserPersisterTest() override = default;

  // WebLayerBrowserTest:
  void SetUpOnMainThread() override {
    WebLayerBrowserTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
    browser_ = Browser::Create(GetProfile(), nullptr);
    tab_ = static_cast<TabImpl*>(browser_->CreateTab());
    browser_->SetActiveTab(tab_);
  }
  void PostRunTestOnMainThread() override {
    tab_ = nullptr;
    browser_.reset();
    WebLayerBrowserTest::PostRunTestOnMainThread();
  }

  GURL url1() { return embedded_test_server()->GetURL("/simple_page.html"); }

  GURL url2() { return embedded_test_server()->GetURL("/simple_page2.html"); }

  // Persists the current state, then recreates the browser. See
  // BrowserImpl::GetMinimalPersistenceState() for details on
  // |max_size_in_bytes|, 0 means use the default value.
  void RecreateBrowserFromCurrentState(int max_size_in_bytes = 0) {
    Browser::PersistenceInfo persistence_info;
    persistence_info.minimal_state =
        browser_impl()->GetMinimalPersistenceState(max_size_in_bytes);
    tab_ = nullptr;
    browser_ = Browser::Create(GetProfile(), &persistence_info);
    // There is always at least one tab created (even if restore fails).
    ASSERT_GE(browser_->GetTabs().size(), 1u);
    tab_ = static_cast<TabImpl*>(browser_->GetTabs()[0]);
  }

 protected:
  BrowserImpl* browser_impl() {
    return static_cast<BrowserImpl*>(browser_.get());
  }

  std::unique_ptr<Browser> browser_;
  TabImpl* tab_ = nullptr;
};

IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, SingleTab) {
  NavigateAndWaitForCompletion(url1(), tab_);

  ASSERT_NO_FATAL_FAILURE(RecreateBrowserFromCurrentState());

  EXPECT_EQ(tab_, browser_->GetActiveTab());
  TestNavigationObserver observer(
      url1(), TestNavigationObserver::NavigationEvent::kCompletion,
      browser_->GetActiveTab());
  observer.Wait();
  EXPECT_EQ(1, tab_->GetNavigationController()->GetNavigationListSize());
}

IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, TwoTabs) {
  NavigateAndWaitForCompletion(url1(), tab_);

  Tab* tab2 = browser_->CreateTab();
  NavigateAndWaitForCompletion(url2(), tab2);
  browser_->SetActiveTab(tab2);

  // Shutdown the service and run the assertions twice to ensure we handle
  // correctly storing state of tabs that need to be reloaded.
  for (int i = 0; i < 2; ++i) {
    ASSERT_NO_FATAL_FAILURE(RecreateBrowserFromCurrentState());
    tab2 = nullptr;

    ASSERT_EQ(2u, browser_->GetTabs().size()) << "iteration " << i;
    tab2 = browser_->GetTabs()[1];
    EXPECT_EQ(tab2, browser_->GetActiveTab()) << "iteration " << i;
    // The first tab shouldn't have loaded yet, as it's not active.
    EXPECT_TRUE(tab_->web_contents()->GetController().NeedsReload())
        << "iteration " << i;
    EXPECT_EQ(1, tab2->GetNavigationController()->GetNavigationListSize())
        << "iteration " << i;
    TestNavigationObserver observer(
        url2(), TestNavigationObserver::NavigationEvent::kCompletion, tab2);
  }
}

IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, PendingSkipped) {
  NavigateAndWaitForCompletion(url1(), tab_);

  tab_->GetNavigationController()->Navigate(url2());

  ASSERT_NO_FATAL_FAILURE(RecreateBrowserFromCurrentState());

  EXPECT_EQ(tab_, browser_->GetActiveTab());
  TestNavigationObserver observer(
      url1(), TestNavigationObserver::NavigationEvent::kCompletion,
      browser_->GetActiveTab());
  observer.Wait();
  ASSERT_EQ(1, tab_->GetNavigationController()->GetNavigationListSize());
}

IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, TwoNavs) {
  NavigateAndWaitForCompletion(url1(), tab_);

  NavigateAndWaitForCompletion(url2(), tab_);

  ASSERT_NO_FATAL_FAILURE(RecreateBrowserFromCurrentState());

  TabImpl* restored_tab = tab_;
  EXPECT_EQ(restored_tab, browser_->GetActiveTab());
  TestNavigationObserver observer(
      url2(), TestNavigationObserver::NavigationEvent::kCompletion,
      restored_tab);
  observer.Wait();
  ASSERT_EQ(2,
            restored_tab->GetNavigationController()->GetNavigationListSize());
  content::NavigationController& nav_controller =
      restored_tab->web_contents()->GetController();
  EXPECT_EQ(1, nav_controller.GetCurrentEntryIndex());
  EXPECT_EQ(url1(), nav_controller.GetEntryAtIndex(0)->GetURL());
  EXPECT_EQ(url2(), nav_controller.GetEntryAtIndex(1)->GetURL());
}

IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, Overflow) {
  std::string url_string(2048, 'a');
  const std::string data = "data:,";
  url_string.replace(0, data.size(), data);
  NavigateAndWaitForCompletion(GURL(url_string), tab_);

  ASSERT_NO_FATAL_FAILURE(RecreateBrowserFromCurrentState(2048));

  TabImpl* restored_tab = tab_;
  EXPECT_EQ(restored_tab, browser_->GetActiveTab());
  EXPECT_EQ(0, restored_tab->web_contents()->GetController().GetEntryCount());
  EXPECT_TRUE(restored_tab->web_contents()->GetController().GetPendingEntry() ==
              nullptr);
}

}  // namespace weblayer
