chromium/ash/quick_pair/keyed_service/battery_update_message_handler_unittest.cc

// Copyright 2021 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/quick_pair/keyed_service/battery_update_message_handler.h"

#include <memory>
#include <optional>

#include "ash/quick_pair/common/constants.h"
#include "ash/quick_pair/common/device.h"
#include "ash/quick_pair/common/fake_bluetooth_adapter.h"
#include "ash/quick_pair/common/logging.h"
#include "ash/quick_pair/common/protocol.h"
#include "ash/quick_pair/message_stream/fake_bluetooth_socket.h"
#include "ash/quick_pair/message_stream/fake_message_stream_lookup.h"
#include "ash/quick_pair/message_stream/message_stream.h"
#include "ash/quick_pair/message_stream/message_stream_lookup.h"
#include "ash/quick_pair/pairing/mock_pairer_broker.h"
#include "ash/quick_pair/pairing/pairer_broker.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/services/quick_pair/fast_pair_data_parser.h"
#include "chromeos/ash/services/quick_pair/mock_quick_pair_process_manager.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_manager.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_manager_impl.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

constexpr char kTestDeviceAddress[] = "11:12:13:14:15:16";
constexpr char kTestBleDeviceName[] = "Test Device Name";

std::vector<uint8_t> kBatteryUpdateBytes1 = {/*mesage_group=*/0x03,
                                             /*mesage_code=*/0x03,
                                             /*additional_data_length=*/0x00,
                                             0x03,
                                             /*additional_data=*/0x57,
                                             0x41,
                                             0x7F};
std::vector<uint8_t> kBatteryUpdateBytes2 = {/*mesage_group=*/0x03,
                                             /*mesage_code=*/0x03,
                                             /*additional_data_length=*/0x00,
                                             0x03,
                                             /*additional_data=*/0x51,
                                             0x38,
                                             0x38};

const std::vector<uint8_t> kModelIdBytes = {
    /*message_group=*/0x03,
    /*message_code=*/0x01,
    /*additional_data_length=*/0x00, 0x03,
    /*additional_data=*/0xAA,        0xBB, 0xCC};

std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>>
CreateTestBluetoothDevice(std::string address,
                          device::MockBluetoothAdapter* adapter) {
  return std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
      /*adapter=*/adapter, /*bluetooth_class=*/0, kTestBleDeviceName, address,
      /*paired=*/true, /*connected=*/false);
}

}  // namespace

namespace ash {
namespace quick_pair {

class BatteryUpdateMessageHandlerTest : public testing::Test {
 public:
  void SetUp() override {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>>
        bluetooth_device =
            CreateTestBluetoothDevice(kTestDeviceAddress, adapter_.get());
    device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
    bluetooth_device_ = bluetooth_device.get();
    adapter_->AddMockDevice(std::move(bluetooth_device));

    message_stream_lookup_ = std::make_unique<FakeMessageStreamLookup>();
    fake_message_stream_lookup_ =
        static_cast<FakeMessageStreamLookup*>(message_stream_lookup_.get());
    message_stream_ =
        std::make_unique<MessageStream>(kTestDeviceAddress, fake_socket_.get());

    process_manager_ = std::make_unique<MockQuickPairProcessManager>();
    quick_pair_process::SetProcessManager(process_manager_.get());
    data_parser_ = std::make_unique<FastPairDataParser>(
        fast_pair_data_parser_.InitWithNewPipeAndPassReceiver());
    data_parser_remote_.Bind(std::move(fast_pair_data_parser_),
                             task_environment_.GetMainThreadTaskRunner());
    EXPECT_CALL(*mock_process_manager(), GetProcessReference)
        .WillRepeatedly([&](QuickPairProcessManager::ProcessStoppedCallback) {
          return std::make_unique<
              QuickPairProcessManagerImpl::ProcessReferenceImpl>(
              data_parser_remote_, base::DoNothing());
        });

    battery_update_message_handler_ =
        std::make_unique<BatteryUpdateMessageHandler>(
            message_stream_lookup_.get());
  }

  void TearDown() override {
    fake_message_stream_lookup_->RemoveMessageStream(kTestDeviceAddress);
    battery_update_message_handler_.reset();
  }

  MockQuickPairProcessManager* mock_process_manager() {
    return static_cast<MockQuickPairProcessManager*>(process_manager_.get());
  }

  void SetMessageStream(const std::vector<uint8_t>& message_bytes) {
    fake_socket_->SetIOBufferFromBytes(message_bytes);
    message_stream_ =
        std::make_unique<MessageStream>(kTestDeviceAddress, fake_socket_.get());
  }

  void AddMessageStream(const std::vector<uint8_t>& message_bytes) {
    fake_socket_->SetIOBufferFromBytes(message_bytes);
    message_stream_ =
        std::make_unique<MessageStream>(kTestDeviceAddress, fake_socket_.get());
    fake_message_stream_lookup_->AddMessageStream(kTestDeviceAddress,
                                                  message_stream_.get());
  }

  void NotifyMessageStreamConnected(std::string device_address) {
    fake_message_stream_lookup_->NotifyMessageStreamConnected(
        device_address, message_stream_.get());
  }

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

  scoped_refptr<FakeBluetoothAdapter> adapter_;

  scoped_refptr<FakeBluetoothSocket> fake_socket_ =
      base::MakeRefCounted<FakeBluetoothSocket>();
  std::unique_ptr<MessageStream> message_stream_;
  std::unique_ptr<MessageStreamLookup> message_stream_lookup_;
  raw_ptr<FakeMessageStreamLookup> fake_message_stream_lookup_ = nullptr;

  mojo::SharedRemote<mojom::FastPairDataParser> data_parser_remote_;
  mojo::PendingRemote<mojom::FastPairDataParser> fast_pair_data_parser_;
  std::unique_ptr<FastPairDataParser> data_parser_;
  std::unique_ptr<QuickPairProcessManager> process_manager_;

  raw_ptr<device::BluetoothDevice, DanglingUntriaged> bluetooth_device_ =
      nullptr;
  std::unique_ptr<BatteryUpdateMessageHandler> battery_update_message_handler_;
};

TEST_F(BatteryUpdateMessageHandlerTest, BatteryUpdate_GetMessages) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kBatteryUpdateBytes1);
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, BatteryUpdate_Observation) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  fake_socket_->SetIOBufferFromBytes(kBatteryUpdateBytes1);
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_NE(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, BatteryUpdate_MultipleMessages) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kBatteryUpdateBytes1);
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(
      bluetooth_device_
          ->GetBatteryInfo(
              device::BluetoothDevice::BatteryType::kLeftBudTrueWireless)
          ->percentage);
  EXPECT_EQ(87,
            bluetooth_device_
                ->GetBatteryInfo(
                    device::BluetoothDevice::BatteryType::kLeftBudTrueWireless)
                ->percentage.value());
  EXPECT_TRUE(
      bluetooth_device_
          ->GetBatteryInfo(
              device::BluetoothDevice::BatteryType::kRightBudTrueWireless)
          ->percentage);
  EXPECT_EQ(65,
            bluetooth_device_
                ->GetBatteryInfo(
                    device::BluetoothDevice::BatteryType::kRightBudTrueWireless)
                ->percentage.value());

  EXPECT_FALSE(bluetooth_device_
                   ->GetBatteryInfo(
                       device::BluetoothDevice::BatteryType::kCaseTrueWireless)
                   ->percentage);

  fake_socket_->SetIOBufferFromBytes(kBatteryUpdateBytes2);
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(
      bluetooth_device_
          ->GetBatteryInfo(
              device::BluetoothDevice::BatteryType::kLeftBudTrueWireless)
          ->percentage);
  EXPECT_EQ(81,
            bluetooth_device_
                ->GetBatteryInfo(
                    device::BluetoothDevice::BatteryType::kLeftBudTrueWireless)
                ->percentage.value());
  EXPECT_TRUE(
      bluetooth_device_
          ->GetBatteryInfo(
              device::BluetoothDevice::BatteryType::kRightBudTrueWireless)
          ->percentage);
  EXPECT_EQ(56,
            bluetooth_device_
                ->GetBatteryInfo(
                    device::BluetoothDevice::BatteryType::kRightBudTrueWireless)
                ->percentage.value());

  EXPECT_TRUE(bluetooth_device_
                  ->GetBatteryInfo(
                      device::BluetoothDevice::BatteryType::kCaseTrueWireless)
                  ->percentage);
  EXPECT_EQ(56, bluetooth_device_
                    ->GetBatteryInfo(
                        device::BluetoothDevice::BatteryType::kCaseTrueWireless)
                    ->percentage.value());
}

TEST_F(BatteryUpdateMessageHandlerTest, NoBatteryUpdate_GetMessages) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kModelIdBytes);
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, NoBatteryUpdate_Observation) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  fake_socket_->SetIOBufferFromBytes(kModelIdBytes);
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, MessageStreamRemovedOnDestroyed) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kBatteryUpdateBytes1);
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  message_stream_.reset();
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, MessageStreamRemovedOnDisconnect) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  fake_socket_->SetErrorReason(
      device::BluetoothSocket::ErrorReason::kDisconnected);
  message_stream_ =
      std::make_unique<MessageStream>(kTestDeviceAddress, fake_socket_.get());
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest,
       MessageStreamRemovedOnDisconnect_MessageStreamDestroted) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  fake_socket_->SetErrorReason(
      device::BluetoothSocket::ErrorReason::kDisconnected);
  message_stream_ =
      std::make_unique<MessageStream>(kTestDeviceAddress, fake_socket_.get());
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kBatteryUpdateBytes1);
  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  message_stream_.reset();
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

TEST_F(BatteryUpdateMessageHandlerTest, DeviceLost) {
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device_->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));

  SetMessageStream(kBatteryUpdateBytes1);
  fake_socket_->TriggerReceiveCallback();
  base::RunLoop().RunUntilIdle();
  auto bluetooth_device = adapter_->RemoveMockDevice(kTestDeviceAddress);

  NotifyMessageStreamConnected(kTestDeviceAddress);
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(std::nullopt,
            bluetooth_device->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kLeftBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kRightBudTrueWireless));
  EXPECT_EQ(std::nullopt,
            bluetooth_device->GetBatteryInfo(
                device::BluetoothDevice::BatteryType::kCaseTrueWireless));
}

}  // namespace quick_pair
}  // namespace ash