chromium/ash/webui/eche_app_ui/eche_connection_status_handler_unittest.cc

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

#include "ash/webui/eche_app_ui/eche_connection_status_handler.h"

#include "ash/constants/ash_features.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::eche_app {

namespace {

class FakeObserver : public EcheConnectionStatusHandler::Observer {
 public:
  FakeObserver() = default;
  ~FakeObserver() override = default;

  size_t num_connection_status_changed_calls() const {
    return num_connection_status_changed_calls_;
  }

  size_t num_connection_status_for_ui_changed_calls() const {
    return num_connection_status_for_ui_changed_calls_;
  }

  size_t num_request_background_connection_attempt_calls() const {
    return num_request_background_connection_attempt_calls_;
  }

  size_t num_request_close_connection_calls() const {
    return num_request_close_connection_calls_;
  }

  mojom::ConnectionStatus last_connection_changed_status() const {
    return last_connection_changed_status_;
  }

  mojom::ConnectionStatus last_connection_for_ui_changed_status() const {
    return last_connection_for_ui_changed_status_;
  }

  // EcheConnectionStatusHandler::Observer:
  void OnConnectionStatusChanged(
      mojom::ConnectionStatus connection_status) override {
    ++num_connection_status_changed_calls_;
    last_connection_changed_status_ = connection_status;
  }

  void OnConnectionStatusForUiChanged(
      mojom::ConnectionStatus connection_status) override {
    ++num_connection_status_for_ui_changed_calls_;
    last_connection_for_ui_changed_status_ = connection_status;
  }

  void OnRequestBackgroundConnectionAttempt() override {
    ++num_request_background_connection_attempt_calls_;
  }

  void OnRequestCloseConnection() override {
    ++num_request_close_connection_calls_;
  }

 private:
  size_t num_connection_status_changed_calls_ = 0;
  size_t num_connection_status_for_ui_changed_calls_ = 0;
  size_t num_request_background_connection_attempt_calls_ = 0;
  size_t num_request_close_connection_calls_ = 0;
  mojom::ConnectionStatus last_connection_changed_status_ =
      mojom::ConnectionStatus::kConnectionStatusDisconnected;
  mojom::ConnectionStatus last_connection_for_ui_changed_status_ =
      mojom::ConnectionStatus::kConnectionStatusDisconnected;
};

}  // namespace

class EcheConnectionStatusHandlerTest : public testing::Test {
 public:
  EcheConnectionStatusHandlerTest() = default;
  EcheConnectionStatusHandlerTest(const EcheConnectionStatusHandlerTest&) =
      delete;
  EcheConnectionStatusHandlerTest& operator=(
      const EcheConnectionStatusHandlerTest&) = delete;
  ~EcheConnectionStatusHandlerTest() override = default;

  // testing::Test:
  void SetUp() override {
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{features::kEcheSWA,
                              features::kEcheNetworkConnectionState},
        /*disabled_features=*/{});

    handler_ = std::make_unique<EcheConnectionStatusHandler>();
    handler_->AddObserver(&fake_observer_);
  }

  void TearDown() override {
    handler_->RemoveObserver(&fake_observer_);
    handler_.reset();
  }

  EcheConnectionStatusHandler& handler() { return *handler_; }

  void NotifyConnectionStatusChanged(
      mojom::ConnectionStatus connection_status) {
    handler_->OnConnectionStatusChanged(connection_status);
  }

  void SetFeatureStatus(FeatureStatus feature_status) {
    handler_->set_feature_status_for_test(feature_status);
  }

  bool GetIsConnectingOrConnectedStatus() const {
    return handler_->is_connecting_or_connected_for_test();
  }

  size_t GetNumConnectionStatusChangedCalls() const {
    return fake_observer_.num_connection_status_changed_calls();
  }

  size_t GetNumConnectionStatusForUiChangedCalls() const {
    return fake_observer_.num_connection_status_for_ui_changed_calls();
  }

  size_t GetNumRequestBackgroundConnectionAttemptCalls() const {
    return fake_observer_.num_request_background_connection_attempt_calls();
  }

  size_t GetNumRequestCloceConnectionCalls() const {
    return fake_observer_.num_request_close_connection_calls();
  }

  mojom::ConnectionStatus GetLastConnectionChangedStatus() const {
    return fake_observer_.last_connection_changed_status();
  }

  mojom::ConnectionStatus GetLastConnectionForUiChangedStatus() const {
    return fake_observer_.last_connection_for_ui_changed_status();
  }

  mojom::ConnectionStatus GetConnectionStatusForUi() const {
    return handler_->connection_status_for_ui();
  }

  base::test::ScopedFeatureList scoped_feature_list_;

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

 private:
  FakeObserver fake_observer_;
  std::unique_ptr<EcheConnectionStatusHandler> handler_;
};

TEST_F(EcheConnectionStatusHandlerTest, OnConnectionStatusChanged) {
  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnecting);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnecting);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 1u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnected);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 2u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusFailed);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusFailed);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 3u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 4u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());
}

TEST_F(EcheConnectionStatusHandlerTest, OnConnectionStatusChangedFlagDisabled) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.InitWithFeatures(
      /*enabled_features=*/{features::kEcheSWA},
      /*disabled_features=*/{features::kEcheNetworkConnectionState});

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnecting);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnected);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusFailed);

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());
}

TEST_F(EcheConnectionStatusHandlerTest, CheckConnectionStatusForUi) {
  SetFeatureStatus(FeatureStatus::kDisconnected);

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnecting);

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 0u);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 1u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 0u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 1u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  SetFeatureStatus(FeatureStatus::kConnected);
  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusChangedCalls(), 2u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());
}

TEST_F(EcheConnectionStatusHandlerTest,
       CheckConnectionStatusForUi_TimeSinceLastCheckIncreases) {
  SetFeatureStatus(FeatureStatus::kDisconnected);
  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 0u);
  EXPECT_EQ(GetNumRequestBackgroundConnectionAttemptCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 1u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  // After more than 10 seconds pass, extra calls should happen when there is no
  // active stream.
  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);
  SetFeatureStatus(FeatureStatus::kConnected);
  task_environment_.FastForwardBy(base::Seconds(11));
  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 2u);
  EXPECT_EQ(GetNumRequestBackgroundConnectionAttemptCalls(), 1u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  // After more than 10 seconds pass, no extra calls should happen if there's an
  // active stream.
  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusConnected);
  task_environment_.FastForwardBy(base::Seconds(11));
  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 2u);        // no change
  EXPECT_EQ(GetNumRequestBackgroundConnectionAttemptCalls(), 1u);  // no change
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());                 // no change

  // Reset to Disconnected
  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 3u);
  EXPECT_EQ(GetNumRequestBackgroundConnectionAttemptCalls(), 1u);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  // After more than 10 minutes pass, state should go back to Connecting.
  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 4u);
  EXPECT_TRUE(GetIsConnectingOrConnectedStatus());

  NotifyConnectionStatusChanged(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);
  task_environment_.FastForwardBy(base::Minutes(11));
  handler().CheckConnectionStatusForUi();

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnecting);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 5u);
  EXPECT_EQ(GetNumRequestBackgroundConnectionAttemptCalls(), 2u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());
}

TEST_F(EcheConnectionStatusHandlerTest, SetConnectionStatusForUi) {
  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 0u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusConnecting);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnecting);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 1u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 2u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusFailed);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusFailed);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 3u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusDisconnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 4u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());
}

TEST_F(EcheConnectionStatusHandlerTest, OnFeatureStatusChanged) {
  handler().OnFeatureStatusChanged(FeatureStatus::kDisconnected);
  // always resets to "loading" on disconnections.
  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnecting);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 1u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().SetConnectionStatusForUi(
      mojom::ConnectionStatus::kConnectionStatusConnected);

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 2u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());

  handler().OnFeatureStatusChanged(FeatureStatus::kConnected);

  task_environment_.FastForwardBy(base::Seconds(2));

  EXPECT_EQ(GetLastConnectionForUiChangedStatus(),
            mojom::ConnectionStatus::kConnectionStatusConnected);
  EXPECT_EQ(GetNumConnectionStatusForUiChangedCalls(), 3u);
  EXPECT_FALSE(GetIsConnectingOrConnectedStatus());
}

}  // namespace ash::eche_app