chromium/ios/chrome/browser/shared/model/web_state_list/all_web_state_observation_forwarder_unittest.mm

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/shared/model/web_state_list/all_web_state_observation_forwarder.h"

#import <memory>
#import <vector>

#import "base/containers/contains.h"
#import "ios/chrome/browser/shared/model/web_state_list/test/fake_web_state_list_delegate.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/web_state_observer.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"

namespace {

class TestObserver : public web::WebStateObserver {
 public:
  TestObserver() {}
  ~TestObserver() override {}

  bool WasInvokedFor(web::WebState* web_state) {
    return base::Contains(invoker_web_states_, web_state);
  }

  void Reset() { invoker_web_states_.clear(); }

  // web::WebStateObserver.
  void RenderProcessGone(web::WebState* web_state) override {
    invoker_web_states_.push_back(web_state);
  }

 private:
  std::vector<web::WebState*> invoker_web_states_;
};

class AllWebStateObservationForwarderTest : public PlatformTest {
 public:
  AllWebStateObservationForwarderTest()
      : web_state_list_(&web_state_list_delegate_) {
    forwarder_ = std::make_unique<AllWebStateObservationForwarder>(
        &web_state_list_, &observer_);
  }

  web::FakeWebState* AddWebStateToList(bool activate) {
    auto web_state = std::make_unique<web::FakeWebState>();
    web::FakeWebState* web_state_ptr = web_state.get();
    web_state_list_.InsertWebState(
        std::move(web_state),
        WebStateList::InsertionParams::Automatic().Activate(activate));
    return web_state_ptr;
  }

 protected:
  FakeWebStateListDelegate web_state_list_delegate_;
  WebStateList web_state_list_;
  TestObserver observer_;
  std::unique_ptr<AllWebStateObservationForwarder> forwarder_;
};

}  // namespace

TEST_F(AllWebStateObservationForwarderTest, TestInsertActiveWebState) {
  // Insert two webstates into the list and mark the second one active.  Send
  // observer notifications for both and verify the result.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  ASSERT_EQ(web_state_b, web_state_list_.GetActiveWebState());

  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();

  // The observer should get notifications for both web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
}

TEST_F(AllWebStateObservationForwarderTest, TestInsertNonActiveWebState) {
  // Insert two webstates into the list, but do not mark the second one active.
  // Send observer notifications for both and verify the result.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(false);
  ASSERT_EQ(web_state_a, web_state_list_.GetActiveWebState());

  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();

  // The observer should get notifications for both web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
}

TEST_F(AllWebStateObservationForwarderTest, TestDetachActiveWebState) {
  // Insert three webstates into the list.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  web::FakeWebState* web_state_c = AddWebStateToList(true);
  ASSERT_EQ(web_state_c, web_state_list_.GetActiveWebState());

  // Remove the active web state and send observer notifications.
  std::unique_ptr<web::WebState> detached_web_state =
      web_state_list_.DetachWebStateAt(web_state_list_.active_index());

  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();
  web_state_c->OnRenderProcessGone();

  // The observer should get notifications for the two remaining web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
  EXPECT_FALSE(observer_.WasInvokedFor(web_state_c));
}

TEST_F(AllWebStateObservationForwarderTest, TestDetachNonActiveWebState) {
  // Insert three webstates into the list.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  web::FakeWebState* web_state_c = AddWebStateToList(true);
  ASSERT_EQ(web_state_c, web_state_list_.GetActiveWebState());

  // Remove a non-active web state and send observer notifications.
  std::unique_ptr<web::WebState> detached_web_state =
      web_state_list_.DetachWebStateAt(
          web_state_list_.GetIndexOfWebState(web_state_a));
  ASSERT_EQ(web_state_c, web_state_list_.GetActiveWebState());

  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();
  web_state_c->OnRenderProcessGone();

  // The observer should get notifications for the two remaining web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_c));
  EXPECT_FALSE(observer_.WasInvokedFor(web_state_a));
}

TEST_F(AllWebStateObservationForwarderTest, TestReplaceActiveWebState) {
  // Insert two webstates into the list and mark the second one active.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  ASSERT_EQ(web_state_b, web_state_list_.GetActiveWebState());

  // Replace the active web state.  Send notifications and verify the result.
  auto replacement_web_state(std::make_unique<web::FakeWebState>());
  web::FakeWebState* web_state_c = replacement_web_state.get();

  std::unique_ptr<web::WebState> detached_web_state =
      web_state_list_.ReplaceWebStateAt(
          web_state_list_.GetIndexOfWebState(web_state_b),
          std::move(replacement_web_state));

  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();
  web_state_c->OnRenderProcessGone();

  // The observer should get notifications for the two remaining web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_c));
  EXPECT_FALSE(observer_.WasInvokedFor(web_state_b));
}

TEST_F(AllWebStateObservationForwarderTest, TestChangeActiveWebState) {
  // Insert two webstates into the list and mark the second one active.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  ASSERT_EQ(web_state_b, web_state_list_.GetActiveWebState());

  // Make web state A active and send notifications.
  web_state_list_.ActivateWebStateAt(
      web_state_list_.GetIndexOfWebState(web_state_a));
  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();

  // The observer should get notifications for both web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));

  // Make web state B active and send notifications.
  observer_.Reset();
  web_state_list_.ActivateWebStateAt(
      web_state_list_.GetIndexOfWebState(web_state_b));
  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();

  // The observer should get notifications for both web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
}

TEST_F(AllWebStateObservationForwarderTest, TestNonEmptyInitialWebStateList) {
  // Insert two webstates into the list.
  web::FakeWebState* web_state_a = AddWebStateToList(true);
  web::FakeWebState* web_state_b = AddWebStateToList(true);
  ASSERT_EQ(web_state_b, web_state_list_.GetActiveWebState());

  // Recreate the multi observer to simulate creation with an already-populated
  // WebStateList.
  forwarder_.reset();
  forwarder_ = std::make_unique<AllWebStateObservationForwarder>(
      &web_state_list_, &observer_);

  // Send notifications and verify the result.
  web_state_a->OnRenderProcessGone();
  web_state_b->OnRenderProcessGone();

  // The observer should get notifications for both web states.
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_a));
  EXPECT_TRUE(observer_.WasInvokedFor(web_state_b));
}