chromium/chromeos/ash/components/phonehub/ping_manager_impl_unittest.cc

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

#include "chromeos/ash/components/phonehub/ping_manager_impl.h"

#include "ash/constants/ash_features.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/phonehub/fake_feature_status_provider.h"
#include "chromeos/ash/components/phonehub/fake_message_receiver.h"
#include "chromeos/ash/components/phonehub/fake_message_sender.h"
#include "chromeos/ash/components/phonehub/proto/phonehub_api.pb.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/fake_connection_manager.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::phonehub {

namespace {

constexpr auto kLatencyDelta = base::Milliseconds(123u);
constexpr char kPingManagerLatencyHistogramName[] =
    "PhoneHub.PhoneAvailabilityCheck.Latency";
constexpr char kPingManagerPingResultHistogramName[] =
    "PhoneHub.PhoneAvailabilityCheck.Result";

}  // namespace

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

  void SetUp() override {
    feature_list_.InitWithFeatures(
        /*enabled_features=*/{features::kPhoneHubPingOnBubbleOpen},
        /*disabled_features=*/{});

    fake_connection_manager_ =
        std::make_unique<secure_channel::FakeConnectionManager>();
    ping_manager_ = std::make_unique<PingManagerImpl>(
        fake_connection_manager_.get(), &fake_feature_status_provider_,
        &fake_message_receiver_, &fake_message_sender_);

    SetIsPingSupportedByPhone(true);
  }

  void TearDown() override {
    ping_manager_.reset();
    fake_connection_manager_.reset();
  }

  PingManagerImpl* ping_manager() { return ping_manager_.get(); }

  bool IsPingSupportedByPhone() {
    return ping_manager_->is_ping_supported_by_phone_for_test();
  }

  bool IsWaitingForResponse() {
    return ping_manager_->is_waiting_for_response_for_test();
  }

  void SetIsPingSupportedByPhone(bool is_supported) {
    ping_manager_->set_is_ping_supported_by_phone_for_test(is_supported);
  }

  void SetIsWaitingForResponse(bool is_waiting) {
    ping_manager_->set_is_waiting_for_response_for_test(is_waiting);
  }

  void SetFeatureStatus(FeatureStatus feature_status) {
    fake_feature_status_provider_.SetStatus(feature_status);
  }

  void NotifyPhoneStatusSnapshotReceived(
      proto::PhoneStatusSnapshot status_snapshot) {
    fake_message_receiver_.NotifyPhoneStatusSnapshotReceived(status_snapshot);
  }

  void NotifyPhoneStatusUpdateReceived(proto::PhoneStatusUpdate status_update) {
    fake_message_receiver_.NotifyPhoneStatusUpdateReceived(status_update);
  }

  void NotifyPingResponseReceived() {
    fake_message_receiver_.NotifyPingResponseReceived();
  }

  size_t GetPingRequestCallCount() {
    return fake_message_sender_.GetPingRequestCallCount();
  }

  size_t GetNumDisconnectCalls() {
    return fake_connection_manager_->num_disconnect_calls();
  }

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

 private:
  std::unique_ptr<secure_channel::FakeConnectionManager>
      fake_connection_manager_;
  std::unique_ptr<PingManagerImpl> ping_manager_;
  FakeFeatureStatusProvider fake_feature_status_provider_;
  FakeMessageReceiver fake_message_receiver_;
  FakeMessageSender fake_message_sender_;
};

TEST_F(PingManagerImplTest, OnPhoneStatusSnapshotReceivedPingSupported) {
  SetIsPingSupportedByPhone(false);
  EXPECT_FALSE(IsPingSupportedByPhone());

  auto config = std::make_unique<proto::FeatureSetupConfig>();
  config->set_ping_capability_supported(true);

  auto expected_phone_properties = std::make_unique<proto::PhoneProperties>();
  expected_phone_properties->set_allocated_feature_setup_config(
      config.release());

  proto::PhoneStatusSnapshot expected_snapshot;
  expected_snapshot.set_allocated_properties(
      expected_phone_properties.release());

  // Simulate receiving a message
  NotifyPhoneStatusSnapshotReceived(expected_snapshot);

  EXPECT_TRUE(IsPingSupportedByPhone());
}

TEST_F(PingManagerImplTest, OnPhoneStatusSnapshotReceivedPingNotSupported) {
  auto config = std::make_unique<proto::FeatureSetupConfig>();
  config->set_ping_capability_supported(false);

  auto expected_phone_properties = std::make_unique<proto::PhoneProperties>();
  expected_phone_properties->set_allocated_feature_setup_config(
      config.release());

  proto::PhoneStatusSnapshot expected_snapshot;
  expected_snapshot.set_allocated_properties(
      expected_phone_properties.release());

  // Simulate receiving a message
  NotifyPhoneStatusSnapshotReceived(expected_snapshot);

  EXPECT_FALSE(IsPingSupportedByPhone());
}

TEST_F(PingManagerImplTest, OnPhoneStatusUpdateReceivedPingSupported) {
  SetIsPingSupportedByPhone(false);
  EXPECT_FALSE(IsPingSupportedByPhone());

  auto config = std::make_unique<proto::FeatureSetupConfig>();
  config->set_ping_capability_supported(true);

  auto expected_phone_properties = std::make_unique<proto::PhoneProperties>();
  expected_phone_properties->set_allocated_feature_setup_config(
      config.release());

  proto::PhoneStatusUpdate expected_update;
  expected_update.set_allocated_properties(expected_phone_properties.release());

  // Simulate receiving a message
  NotifyPhoneStatusUpdateReceived(expected_update);

  EXPECT_TRUE(IsPingSupportedByPhone());
}

TEST_F(PingManagerImplTest, OnPhoneStatusUpdateReceivedPingNotSupported) {
  auto config = std::make_unique<proto::FeatureSetupConfig>();
  config->set_ping_capability_supported(false);

  auto expected_phone_properties = std::make_unique<proto::PhoneProperties>();
  expected_phone_properties->set_allocated_feature_setup_config(
      config.release());

  proto::PhoneStatusUpdate expected_update;
  expected_update.set_allocated_properties(expected_phone_properties.release());

  // Simulate receiving a message
  NotifyPhoneStatusUpdateReceived(expected_update);

  EXPECT_FALSE(IsPingSupportedByPhone());
}

TEST_F(PingManagerImplTest, OnPhoneStatusUpdateReceivedNoFeatureSetupConfig) {
  auto expected_phone_properties = std::make_unique<proto::PhoneProperties>();

  proto::PhoneStatusUpdate expected_update;
  expected_update.set_allocated_properties(expected_phone_properties.release());

  // Simulate receiving a message
  NotifyPhoneStatusUpdateReceived(expected_update);

  EXPECT_FALSE(IsPingSupportedByPhone());
}

TEST_F(PingManagerImplTest, OnFeatureStatusChanged) {
  ping_manager()->SendPingRequest();
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);

  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

  SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);

  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());

  ping_manager()->SendPingRequest();
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

  SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);

  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());

  ping_manager()->SendPingRequest();
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

  SetFeatureStatus(FeatureStatus::kEnabledAndConnected);

  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, OnPingResponseReceived) {
  base::HistogramTester histogram_tester;

  SetIsWaitingForResponse(true);
  EXPECT_TRUE(IsWaitingForResponse());

  // Simulate receiving a message
  NotifyPingResponseReceived();

  histogram_tester.ExpectBucketCount(kPingManagerPingResultHistogramName, true,
                                     1);

  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, SendPingRequest) {
  proto::PingRequest request;

  ping_manager()->SendPingRequest();

  EXPECT_EQ(1u, GetPingRequestCallCount());
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, SendPingRequestNotSupported) {
  SetIsPingSupportedByPhone(false);

  ping_manager()->SendPingRequest();

  EXPECT_EQ(0u, GetPingRequestCallCount());
  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, SendPingRequestWaitingForResponse) {
  SetIsWaitingForResponse(true);

  ping_manager()->SendPingRequest();

  EXPECT_EQ(0u, GetPingRequestCallCount());
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, OnPingTimerFired) {
  base::HistogramTester histogram_tester;

  EXPECT_EQ(0u, GetNumDisconnectCalls());
  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());

  ping_manager()->SendPingRequest();

  EXPECT_EQ(0u, GetNumDisconnectCalls());
  EXPECT_EQ(1u, GetPingRequestCallCount());
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

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

  histogram_tester.ExpectBucketCount(kPingManagerPingResultHistogramName, false,
                                     1);

  EXPECT_EQ(1u, GetNumDisconnectCalls());
  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());
}

TEST_F(PingManagerImplTest, SendAndReceivePing) {
  base::HistogramTester histogram_tester;

  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());

  ping_manager()->SendPingRequest();

  EXPECT_EQ(0u, GetNumDisconnectCalls());
  EXPECT_EQ(1u, GetPingRequestCallCount());
  EXPECT_TRUE(IsWaitingForResponse());
  EXPECT_TRUE(ping_manager()->IsPingTimeoutTimerRunning());

  task_environment_.FastForwardBy(kLatencyDelta);

  // Simulate receiving a message.
  NotifyPingResponseReceived();

  histogram_tester.ExpectTimeBucketCount(kPingManagerLatencyHistogramName,
                                         kLatencyDelta, 1);
  histogram_tester.ExpectBucketCount(kPingManagerPingResultHistogramName, true,
                                     1);

  EXPECT_FALSE(IsWaitingForResponse());
  EXPECT_FALSE(ping_manager()->IsPingTimeoutTimerRunning());
  EXPECT_EQ(0u, GetNumDisconnectCalls());
}

}  // namespace ash::phonehub