chromium/chromeos/ash/components/tether/keep_alive_operation_unittest.cc

// Copyright 2016 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/tether/keep_alive_operation.h"

#include <memory>
#include <optional>
#include <vector>

#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/tether/fake_host_connection.h"
#include "chromeos/ash/components/tether/message_wrapper.h"
#include "chromeos/ash/components/tether/proto_test_util.h"
#include "chromeos/ash/components/timer_factory/fake_timer_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::Invoke;
using testing::NotNull;

namespace ash::tether {

namespace {

// Used to verify the KeepAliveOperation notifies the observer when appropriate.
class MockOperationObserver : public KeepAliveOperation::Observer {
 public:
  MockOperationObserver() = default;

  MockOperationObserver(const MockOperationObserver&) = delete;
  MockOperationObserver& operator=(const MockOperationObserver&) = delete;

  ~MockOperationObserver() = default;

  MOCK_METHOD1(OnOperationFinishedRaw, void(DeviceStatus*));

  void OnOperationFinished(
      std::unique_ptr<DeviceStatus> device_status) override {
    OnOperationFinishedRaw(device_status.get());
  }
};

}  // namespace

class KeepAliveOperationTest : public testing::Test {
 public:
  KeepAliveOperationTest(const KeepAliveOperationTest&) = delete;
  KeepAliveOperationTest& operator=(const KeepAliveOperationTest&) = delete;

 protected:
  KeepAliveOperationTest()
      : tether_host_(TetherHost(multidevice::CreateRemoteDeviceRefForTest())) {}

  void SetUp() override {
    fake_host_connection_factory_ =
        std::make_unique<FakeHostConnection::Factory>();
    operation_ = base::WrapUnique(new KeepAliveOperation(
        tether_host_, fake_host_connection_factory_.get()));
    operation_->AddObserver(&mock_observer_);

    operation_->SetTimerFactoryForTest(
        std::make_unique<ash::timer_factory::FakeTimerFactory>());

    test_clock_.SetNow(base::Time::UnixEpoch());
    operation_->SetClockForTest(&test_clock_);
  }

  const TetherHost tether_host_;

  std::unique_ptr<FakeHostConnection::Factory> fake_host_connection_factory_;
  std::unique_ptr<KeepAliveOperation> operation_;

  base::SimpleTestClock test_clock_;
  MockOperationObserver mock_observer_;
  base::HistogramTester histogram_tester_;
};

// Tests that the KeepAliveTickle message is sent to the remote device once the
// communication channel is connected and authenticated.
TEST_F(KeepAliveOperationTest, KeepAliveTickleSentOnceAuthenticated) {
  // Setup the connection.
  fake_host_connection_factory_->SetupConnectionAttempt(tether_host_);

  // Start the operation.
  operation_->Initialize();

  // Verify the KeepAliveTickle message is sent.
  auto message_wrapper = std::make_unique<MessageWrapper>(KeepAliveTickle());
  std::string expected_payload = message_wrapper->ToRawMessage();
  EXPECT_EQ(1u, fake_host_connection_factory_
                    ->GetActiveConnection(tether_host_.GetDeviceId())
                    ->sent_messages()
                    .size());
  EXPECT_EQ(expected_payload,
            fake_host_connection_factory_
                ->GetActiveConnection(tether_host_.GetDeviceId())
                ->sent_messages()[0]
                .first->ToRawMessage());
}

// Tests that observers are notified when the operation has completed, signified
// by the OnMessageReceived handler being called.
TEST_F(KeepAliveOperationTest, NotifiesObserversOnResponse) {
  DeviceStatus test_status = CreateDeviceStatusWithFakeFields();

  // Setup the connection.
  fake_host_connection_factory_->SetupConnectionAttempt(tether_host_);

  // Verify that the observer is called with the correct parameters.
  EXPECT_CALL(mock_observer_, OnOperationFinishedRaw(NotNull()))
      .WillOnce(Invoke([&test_status](DeviceStatus* status) {
        EXPECT_EQ(test_status.SerializeAsString(), status->SerializeAsString());
      }));

  // Start the operation.
  operation_->Initialize();

  KeepAliveTickleResponse response;
  response.mutable_device_status()->CopyFrom(test_status);
  std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
  operation_->OnMessageReceived(std::move(message));
}

TEST_F(KeepAliveOperationTest, RecordsResponseDuration) {
  static constexpr base::TimeDelta kKeepAliveTickleResponseTime =
      base::Seconds(3);

  EXPECT_CALL(mock_observer_, OnOperationFinishedRaw(_));

  // Setup the connection.
  fake_host_connection_factory_->SetupConnectionAttempt(tether_host_);

  // Initialize the operation.
  operation_->Initialize();

  // Advance the clock in order to verify a non-zero response duration is
  // recorded and verified (below).
  test_clock_.Advance(kKeepAliveTickleResponseTime);

  std::unique_ptr<MessageWrapper> message(
      new MessageWrapper(KeepAliveTickleResponse()));
  operation_->OnMessageReceived(std::move(message));

  histogram_tester_.ExpectTimeBucketCount(
      "InstantTethering.Performance.KeepAliveTickleResponseDuration",
      kKeepAliveTickleResponseTime, 1);
}

}  // namespace ash::tether