chromium/chromecast/device/bluetooth/le/le_scan_manager_impl_test.cc

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

#include "chromecast/device/bluetooth/le/le_scan_manager_impl.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
#include "chromecast/device/bluetooth/bluetooth_util.h"
#include "chromecast/device/bluetooth/le/remote_characteristic.h"
#include "chromecast/device/bluetooth/le/remote_descriptor.h"
#include "chromecast/device/bluetooth/le/remote_device.h"
#include "chromecast/device/bluetooth/le/remote_service.h"
#include "chromecast/device/bluetooth/shlib/mock_le_scanner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;

namespace chromecast {
namespace bluetooth {

namespace {

const bluetooth_v2_shlib::Addr kTestAddr1 = {
    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
const bluetooth_v2_shlib::Addr kTestAddr2 = {
    {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B}};

// This hack is needed because base::BindOnce does not support capture lambdas.
template <typename T>
void CopyResult(T* out, T in) {
  *out = std::move(in);
}

class MockLeScanManagerObserver : public LeScanManager::Observer {
 public:
  MOCK_METHOD1(OnScanEnableChanged, void(bool enabled));
  MOCK_METHOD1(OnNewScanResult, void(LeScanResult result));
};

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

 protected:
  LeScanManagerTest()
      : io_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
            {base::TaskPriority::BEST_EFFORT, base::MayBlock()})),
        le_scan_manager_(&le_scanner_) {
    le_scan_manager_.Initialize(io_task_runner_);
    le_scan_manager_.AddObserver(&mock_observer_);
    task_environment_.RunUntilIdle();
  }
  ~LeScanManagerTest() override {
    le_scan_manager_.RemoveObserver(&mock_observer_);
    le_scan_manager_.Finalize();
  }

  bluetooth_v2_shlib::LeScanner::Delegate* delegate() {
    return &le_scan_manager_;
  }

  base::test::TaskEnvironment task_environment_;
  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
  bluetooth_v2_shlib::MockLeScanner le_scanner_;
  LeScanManagerImpl le_scan_manager_;
  MockLeScanManagerObserver mock_observer_;
};

}  // namespace

TEST_F(LeScanManagerTest, TestEnableDisableScan) {
  std::unique_ptr<LeScanManager::ScanHandle> scan_handle;

  // Enable the LE scan. We expect the observer to be updated and to get a scan
  // handle.
  EXPECT_CALL(le_scanner_, StartScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(true));
  le_scan_manager_.RequestScan(base::BindOnce(
      &CopyResult<std::unique_ptr<LeScanManager::ScanHandle>>, &scan_handle));
  task_environment_.RunUntilIdle();
  ASSERT_TRUE(scan_handle);

  // After deleting the last handle, we expect scan to be disabled.
  EXPECT_CALL(le_scanner_, StopScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(false));
  scan_handle.reset();
  task_environment_.RunUntilIdle();
}

TEST_F(LeScanManagerTest, TestPauseResumeScan) {
  std::unique_ptr<LeScanManager::ScanHandle> scan_handle;

  // Don't call StartScan or StopScan if there is no handle.
  EXPECT_CALL(le_scanner_, StopScan()).Times(0);
  le_scan_manager_.PauseScan();
  EXPECT_CALL(le_scanner_, StartScan()).Times(0);
  le_scan_manager_.ResumeScan();
  task_environment_.RunUntilIdle();

  // Create a handle.
  EXPECT_CALL(le_scanner_, StartScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(true));
  le_scan_manager_.RequestScan(base::BindOnce(
      &CopyResult<std::unique_ptr<LeScanManager::ScanHandle>>, &scan_handle));
  task_environment_.RunUntilIdle();
  ASSERT_TRUE(scan_handle);

  // Pause scan, we shouldn't declare scan is disabled.
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(_)).Times(0);
  EXPECT_CALL(le_scanner_, StopScan()).WillOnce(Return(true));
  le_scan_manager_.PauseScan();
  task_environment_.RunUntilIdle();

  // Resume scan.
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(_)).Times(0);
  EXPECT_CALL(le_scanner_, StartScan()).WillOnce(Return(true));
  le_scan_manager_.ResumeScan();
  task_environment_.RunUntilIdle();

  // Delete the handle.
  EXPECT_CALL(le_scanner_, StopScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(false));
  scan_handle.reset();
  task_environment_.RunUntilIdle();
}

TEST_F(LeScanManagerTest, TestMultipleHandles) {
  static constexpr int kNumHandles = 20;

  std::vector<std::unique_ptr<LeScanManager::ScanHandle>> scan_handles;

  EXPECT_CALL(le_scanner_, StartScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(true));
  for (int i = 0; i < kNumHandles; ++i) {
    std::unique_ptr<LeScanManager::ScanHandle> handle;
    le_scan_manager_.RequestScan(base::BindOnce(
        &CopyResult<std::unique_ptr<LeScanManager::ScanHandle>>, &handle));
    task_environment_.RunUntilIdle();
    ASSERT_TRUE(handle);
    scan_handles.push_back(std::move(handle));
  }

  EXPECT_CALL(le_scanner_, StopScan()).Times(0);
  for (int i = 0; i < kNumHandles - 1; ++i) {
    scan_handles.pop_back();
    task_environment_.RunUntilIdle();
  }

  // After deleting the last handle, we expect scan to be disabled.
  EXPECT_CALL(le_scanner_, StopScan()).WillOnce(Return(true));
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(false));
  scan_handles.pop_back();
  task_environment_.RunUntilIdle();
}

TEST_F(LeScanManagerTest, TestGetScanResultsEmpty) {
  std::vector<LeScanResult> results;

  // Get asynchronous scan results. The result should be empty.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results));

  task_environment_.RunUntilIdle();
  ASSERT_EQ(0u, results.size());
}

TEST_F(LeScanManagerTest, TestEnableScanFails) {
  std::unique_ptr<LeScanManager::ScanHandle> scan_handle;

  // Observer should not be notified.
  EXPECT_CALL(mock_observer_, OnScanEnableChanged(true)).Times(0);

  EXPECT_CALL(le_scanner_, StartScan()).WillOnce(Return(false));
  le_scan_manager_.RequestScan(base::BindOnce(
      &CopyResult<std::unique_ptr<LeScanManager::ScanHandle>>, &scan_handle));
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(scan_handle);
}

TEST_F(LeScanManagerTest, TestGetScanResults) {
  // Simulate some scan results.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result(kTestAddr1, {}, 1234);

  EXPECT_CALL(mock_observer_, OnNewScanResult(_));
  delegate()->OnScanResult(raw_scan_result);
  task_environment_.RunUntilIdle();

  std::vector<LeScanResult> results;
  // Get asynchronous scan results.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results));

  task_environment_.RunUntilIdle();

  ASSERT_EQ(1u, results.size());
  ASSERT_EQ(kTestAddr1, results[0].addr);
  ASSERT_EQ(1234, results[0].rssi);
}

TEST_F(LeScanManagerTest, TestGetScanResultsWithService) {
  EXPECT_CALL(mock_observer_, OnNewScanResult(_)).Times(2);

  // Add a scan result with service 0x4444.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result(
    kTestAddr1, {0x03, 0x02, 0x44, 0x44}, 1234);
  delegate()->OnScanResult(raw_scan_result);

  // Add a scan result with service 0x5555.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result_two(
    kTestAddr2, {0x03, 0x02, 0x55, 0x55}, 1234);
  delegate()->OnScanResult(raw_scan_result_two);

  task_environment_.RunUntilIdle();

  // Get asynchronous scan results for results with service 0x4444.
  std::vector<LeScanResult> results;
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results),
      ScanFilter::From16bitUuid(0x4444));
  task_environment_.RunUntilIdle();

  ASSERT_EQ(1u, results.size());
  ASSERT_EQ(kTestAddr1, results[0].addr);
  ASSERT_EQ(std::vector<uint8_t>({0x44, 0x44}),
            results[0].type_to_data[0x02][0]);
  ASSERT_EQ(1234, results[0].rssi);

  // Get asynchronous scan results for results with service 0x5555.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results),
      ScanFilter::From16bitUuid(0x5555));
  task_environment_.RunUntilIdle();

  ASSERT_EQ(1u, results.size());
  ASSERT_EQ(kTestAddr2, results[0].addr);
  ASSERT_EQ(std::vector<uint8_t>({0x55, 0x55}),
            results[0].type_to_data[0x02][0]);
  ASSERT_EQ(1234, results[0].rssi);

  // Get asynchronous scan results for results with service 0x6666.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results),
      ScanFilter::From16bitUuid(0x6666));
  task_environment_.RunUntilIdle();

  ASSERT_EQ(0u, results.size());
}

TEST_F(LeScanManagerTest, TestGetScanResultsSortedByRssi) {
  EXPECT_CALL(mock_observer_, OnNewScanResult(_)).Times(3);

  // Add a scan result with service 0x4444.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result(
    kTestAddr1, {0x03, 0x02, 0x44, 0x44}, 1);
  delegate()->OnScanResult(raw_scan_result);

  // Add a scan result with service 0x5555.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result_two(
    kTestAddr2, {0x03, 0x02, 0x55, 0x55}, 3);
  delegate()->OnScanResult(raw_scan_result_two);

  // Add a scan result with service 0x5555.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result_three(
    kTestAddr1, {0x03, 0x02, 0x55, 0x55}, 2);
  delegate()->OnScanResult(raw_scan_result_three);

  task_environment_.RunUntilIdle();

  std::vector<LeScanResult> results;
  // Get asynchronous scan results.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results));

  task_environment_.RunUntilIdle();

  ASSERT_EQ(3u, results.size());
  EXPECT_EQ(kTestAddr2, results[0].addr);
  EXPECT_EQ(3, results[0].rssi);
  EXPECT_EQ(kTestAddr1, results[1].addr);
  EXPECT_EQ(2, results[1].rssi);
  EXPECT_EQ(kTestAddr1, results[2].addr);
  EXPECT_EQ(1, results[2].rssi);
}

TEST_F(LeScanManagerTest, TestOnNewScanResult) {
  LeScanResult result;
  ON_CALL(mock_observer_, OnNewScanResult(_))
      .WillByDefault(
          Invoke([&result](LeScanResult result_in) { result = result_in; }));

  // Add a scan result with service 0x4444.
  bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result(
    kTestAddr1, {0x03, 0x02, 0x44, 0x44}, 1);
  delegate()->OnScanResult(raw_scan_result);
  task_environment_.RunUntilIdle();

  // Ensure that the observer was notified.
  ASSERT_EQ(kTestAddr1, result.addr);
  ASSERT_EQ(std::vector<uint8_t>({0x44, 0x44}), result.type_to_data[0x02][0]);
  ASSERT_EQ(1, result.rssi);
}

TEST_F(LeScanManagerTest, TestMaxScanResultEntries) {
  EXPECT_CALL(mock_observer_, OnNewScanResult(_))
      .Times(LeScanManagerImpl::kMaxScanResultEntries + 5);

  // Add scan results with different addrs.
  for (int i = 0; i < LeScanManagerImpl::kMaxScanResultEntries + 5; ++i) {
    uint8_t addr_bit0 = i & 0xFF;
    uint8_t addr_bit1 = (i & 0xFF00) >> 8;
    bluetooth_v2_shlib::LeScanner::ScanResult raw_scan_result(
      {{addr_bit0, addr_bit1, 0xFF, 0xFF, 0xFF, 0xFF}}, {0x03, 0x02, 0x44, 0x44}, -i);
    delegate()->OnScanResult(raw_scan_result);
  }

  task_environment_.RunUntilIdle();

  std::vector<LeScanResult> results;
  // Get asynchronous scan results.
  le_scan_manager_.GetScanResults(
      base::BindOnce(&CopyResult<std::vector<LeScanResult>>, &results));

  task_environment_.RunUntilIdle();

  // First 5 addresses should have been kicked out.
  ASSERT_EQ(1024u, results.size());
  bluetooth_v2_shlib::Addr test_addr;
  for (int i = 0; i < LeScanManagerImpl::kMaxScanResultEntries; ++i) {
    uint8_t addr_bit0 = (i + 5) & 0xFF;
    uint8_t addr_bit1 = ((i + 5) & 0xFF00) >> 8;
    test_addr = {{addr_bit0, addr_bit1, 0xFF, 0xFF, 0xFF, 0xFF}};
    EXPECT_EQ(test_addr, results[i].addr);
    EXPECT_EQ(-(i + 5), results[i].rssi);
  }
}

}  // namespace bluetooth
}  // namespace chromecast